Path: blob/master/Utilities/cmlibarchive/libarchive/archive_match.c
3153 views
/*-1* Copyright (c) 2003-2007 Tim Kientzle2* Copyright (c) 2012 Michihiro NAKAJIMA3* All rights reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526#include "archive_platform.h"2728#ifdef HAVE_ERRNO_H29#include <errno.h>30#endif31#ifdef HAVE_STDLIB_H32#include <stdlib.h>33#endif34#ifdef HAVE_STRING_H35#include <string.h>36#endif3738#include "archive.h"39#include "archive_private.h"40#include "archive_entry.h"41#include "archive_getdate.h"42#include "archive_pathmatch.h"43#include "archive_rb.h"44#include "archive_string.h"4546struct match {47struct match *next;48int matched;49struct archive_mstring pattern;50};5152struct match_list {53struct match *first;54struct match **last;55int count;56int unmatched_count;57struct match *unmatched_next;58int unmatched_eof;59};6061struct match_file {62struct archive_rb_node node;63struct match_file *next;64struct archive_mstring pathname;65int flag;66time_t mtime_sec;67long mtime_nsec;68time_t ctime_sec;69long ctime_nsec;70};7172struct entry_list {73struct match_file *first;74struct match_file **last;75int count;76};7778struct id_array {79size_t size;/* Allocated size */80size_t count;81int64_t *ids;82};8384#define PATTERN_IS_SET 185#define TIME_IS_SET 286#define ID_IS_SET 48788struct archive_match {89struct archive archive;9091/* exclusion/inclusion set flag. */92int setflag;9394/* Recursively include directory content? */95int recursive_include;9697/*98* Matching filename patterns.99*/100struct match_list exclusions;101struct match_list inclusions;102103/*104* Matching time stamps.105*/106time_t now;107int newer_mtime_filter;108time_t newer_mtime_sec;109long newer_mtime_nsec;110int newer_ctime_filter;111time_t newer_ctime_sec;112long newer_ctime_nsec;113int older_mtime_filter;114time_t older_mtime_sec;115long older_mtime_nsec;116int older_ctime_filter;117time_t older_ctime_sec;118long older_ctime_nsec;119/*120* Matching time stamps with its filename.121*/122struct archive_rb_tree exclusion_tree;123struct entry_list exclusion_entry_list;124125/*126* Matching file owners.127*/128struct id_array inclusion_uids;129struct id_array inclusion_gids;130struct match_list inclusion_unames;131struct match_list inclusion_gnames;132};133134static int add_pattern_from_file(struct archive_match *,135struct match_list *, int, const void *, int);136static int add_entry(struct archive_match *, int,137struct archive_entry *);138static int add_owner_id(struct archive_match *, struct id_array *,139int64_t);140static int add_owner_name(struct archive_match *, struct match_list *,141int, const void *);142static int add_pattern_mbs(struct archive_match *, struct match_list *,143const char *);144static int add_pattern_wcs(struct archive_match *, struct match_list *,145const wchar_t *);146static int cmp_key_mbs(const struct archive_rb_node *, const void *);147static int cmp_key_wcs(const struct archive_rb_node *, const void *);148static int cmp_node_mbs(const struct archive_rb_node *,149const struct archive_rb_node *);150static int cmp_node_wcs(const struct archive_rb_node *,151const struct archive_rb_node *);152static void entry_list_add(struct entry_list *, struct match_file *);153static void entry_list_free(struct entry_list *);154static void entry_list_init(struct entry_list *);155static int error_nomem(struct archive_match *);156static void match_list_add(struct match_list *, struct match *);157static void match_list_free(struct match_list *);158static void match_list_init(struct match_list *);159static int match_list_unmatched_inclusions_next(struct archive_match *,160struct match_list *, int, const void **);161static int match_owner_id(struct id_array *, int64_t);162#if !defined(_WIN32) || defined(__CYGWIN__)163static int match_owner_name_mbs(struct archive_match *,164struct match_list *, const char *);165#else166static int match_owner_name_wcs(struct archive_match *,167struct match_list *, const wchar_t *);168#endif169static int match_path_exclusion(struct archive_match *,170struct match *, int, const void *);171static int match_path_inclusion(struct archive_match *,172struct match *, int, const void *);173static int owner_excluded(struct archive_match *,174struct archive_entry *);175static int path_excluded(struct archive_match *, int, const void *);176static int set_timefilter(struct archive_match *, int, time_t, long,177time_t, long);178static int set_timefilter_pathname_mbs(struct archive_match *,179int, const char *);180static int set_timefilter_pathname_wcs(struct archive_match *,181int, const wchar_t *);182static int set_timefilter_date(struct archive_match *, int, const char *);183static int set_timefilter_date_w(struct archive_match *, int,184const wchar_t *);185static int time_excluded(struct archive_match *,186struct archive_entry *);187static int validate_time_flag(struct archive *, int, const char *);188189#define get_date __archive_get_date190191static const struct archive_rb_tree_ops rb_ops_mbs = {192cmp_node_mbs, cmp_key_mbs193};194195static const struct archive_rb_tree_ops rb_ops_wcs = {196cmp_node_wcs, cmp_key_wcs197};198199/*200* The matching logic here needs to be re-thought. I started out to201* try to mimic gtar's matching logic, but it's not entirely202* consistent. In particular 'tar -t' and 'tar -x' interpret patterns203* on the command line as anchored, but --exclude doesn't.204*/205206static int207error_nomem(struct archive_match *a)208{209archive_set_error(&(a->archive), ENOMEM, "No memory");210a->archive.state = ARCHIVE_STATE_FATAL;211return (ARCHIVE_FATAL);212}213214/*215* Create an ARCHIVE_MATCH object.216*/217struct archive *218archive_match_new(void)219{220struct archive_match *a;221222a = calloc(1, sizeof(*a));223if (a == NULL)224return (NULL);225a->archive.magic = ARCHIVE_MATCH_MAGIC;226a->archive.state = ARCHIVE_STATE_NEW;227a->recursive_include = 1;228match_list_init(&(a->inclusions));229match_list_init(&(a->exclusions));230__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);231entry_list_init(&(a->exclusion_entry_list));232match_list_init(&(a->inclusion_unames));233match_list_init(&(a->inclusion_gnames));234time(&a->now);235return (&(a->archive));236}237238/*239* Free an ARCHIVE_MATCH object.240*/241int242archive_match_free(struct archive *_a)243{244struct archive_match *a;245246if (_a == NULL)247return (ARCHIVE_OK);248archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,249ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");250a = (struct archive_match *)_a;251match_list_free(&(a->inclusions));252match_list_free(&(a->exclusions));253entry_list_free(&(a->exclusion_entry_list));254free(a->inclusion_uids.ids);255free(a->inclusion_gids.ids);256match_list_free(&(a->inclusion_unames));257match_list_free(&(a->inclusion_gnames));258free(a);259return (ARCHIVE_OK);260}261262/*263* Convenience function to perform all exclusion tests.264*265* Returns 1 if archive entry is excluded.266* Returns 0 if archive entry is not excluded.267* Returns <0 if something error happened.268*/269int270archive_match_excluded(struct archive *_a, struct archive_entry *entry)271{272struct archive_match *a;273int r;274275archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,276ARCHIVE_STATE_NEW, "archive_match_excluded_ae");277278a = (struct archive_match *)_a;279if (entry == NULL) {280archive_set_error(&(a->archive), EINVAL, "entry is NULL");281return (ARCHIVE_FAILED);282}283284r = 0;285if (a->setflag & PATTERN_IS_SET) {286#if defined(_WIN32) && !defined(__CYGWIN__)287r = path_excluded(a, 0, archive_entry_pathname_w(entry));288#else289r = path_excluded(a, 1, archive_entry_pathname(entry));290#endif291if (r != 0)292return (r);293}294295if (a->setflag & TIME_IS_SET) {296r = time_excluded(a, entry);297if (r != 0)298return (r);299}300301if (a->setflag & ID_IS_SET)302r = owner_excluded(a, entry);303return (r);304}305306/*307* Utility functions to manage exclusion/inclusion patterns308*/309310int311archive_match_exclude_pattern(struct archive *_a, const char *pattern)312{313struct archive_match *a;314int r;315316archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,317ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");318a = (struct archive_match *)_a;319320if (pattern == NULL || *pattern == '\0') {321archive_set_error(&(a->archive), EINVAL, "pattern is empty");322return (ARCHIVE_FAILED);323}324if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)325return (r);326return (ARCHIVE_OK);327}328329int330archive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)331{332struct archive_match *a;333int r;334335archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,336ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");337a = (struct archive_match *)_a;338339if (pattern == NULL || *pattern == L'\0') {340archive_set_error(&(a->archive), EINVAL, "pattern is empty");341return (ARCHIVE_FAILED);342}343if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)344return (r);345return (ARCHIVE_OK);346}347348int349archive_match_exclude_pattern_from_file(struct archive *_a,350const char *pathname, int nullSeparator)351{352struct archive_match *a;353354archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,355ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");356a = (struct archive_match *)_a;357358return add_pattern_from_file(a, &(a->exclusions), 1, pathname,359nullSeparator);360}361362int363archive_match_exclude_pattern_from_file_w(struct archive *_a,364const wchar_t *pathname, int nullSeparator)365{366struct archive_match *a;367368archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,369ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");370a = (struct archive_match *)_a;371372return add_pattern_from_file(a, &(a->exclusions), 0, pathname,373nullSeparator);374}375376int377archive_match_include_pattern(struct archive *_a, const char *pattern)378{379struct archive_match *a;380int r;381382archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,383ARCHIVE_STATE_NEW, "archive_match_include_pattern");384a = (struct archive_match *)_a;385386if (pattern == NULL || *pattern == '\0') {387archive_set_error(&(a->archive), EINVAL, "pattern is empty");388return (ARCHIVE_FAILED);389}390if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)391return (r);392return (ARCHIVE_OK);393}394395int396archive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)397{398struct archive_match *a;399int r;400401archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,402ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");403a = (struct archive_match *)_a;404405if (pattern == NULL || *pattern == L'\0') {406archive_set_error(&(a->archive), EINVAL, "pattern is empty");407return (ARCHIVE_FAILED);408}409if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)410return (r);411return (ARCHIVE_OK);412}413414int415archive_match_include_pattern_from_file(struct archive *_a,416const char *pathname, int nullSeparator)417{418struct archive_match *a;419420archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,421ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");422a = (struct archive_match *)_a;423424return add_pattern_from_file(a, &(a->inclusions), 1, pathname,425nullSeparator);426}427428int429archive_match_include_pattern_from_file_w(struct archive *_a,430const wchar_t *pathname, int nullSeparator)431{432struct archive_match *a;433434archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,435ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");436a = (struct archive_match *)_a;437438return add_pattern_from_file(a, &(a->inclusions), 0, pathname,439nullSeparator);440}441442/*443* Test functions for pathname patterns.444*445* Returns 1 if archive entry is excluded.446* Returns 0 if archive entry is not excluded.447* Returns <0 if something error happened.448*/449int450archive_match_path_excluded(struct archive *_a,451struct archive_entry *entry)452{453struct archive_match *a;454455archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,456ARCHIVE_STATE_NEW, "archive_match_path_excluded");457458a = (struct archive_match *)_a;459if (entry == NULL) {460archive_set_error(&(a->archive), EINVAL, "entry is NULL");461return (ARCHIVE_FAILED);462}463464/* If we don't have exclusion/inclusion pattern set at all,465* the entry is always not excluded. */466if ((a->setflag & PATTERN_IS_SET) == 0)467return (0);468#if defined(_WIN32) && !defined(__CYGWIN__)469return (path_excluded(a, 0, archive_entry_pathname_w(entry)));470#else471return (path_excluded(a, 1, archive_entry_pathname(entry)));472#endif473}474475/*476* When recursive inclusion of directory content is enabled,477* an inclusion pattern that matches a directory will also478* include everything beneath that directory. Enabled by default.479*480* For compatibility with GNU tar, exclusion patterns always481* match if a subset of the full patch matches (i.e., they are482* are not rooted at the beginning of the path) and thus there483* is no corresponding non-recursive exclusion mode.484*/485int486archive_match_set_inclusion_recursion(struct archive *_a, int enabled)487{488struct archive_match *a;489490archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,491ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");492a = (struct archive_match *)_a;493a->recursive_include = enabled;494return (ARCHIVE_OK);495}496497/*498* Utility functions to get statistic information for inclusion patterns.499*/500int501archive_match_path_unmatched_inclusions(struct archive *_a)502{503struct archive_match *a;504505archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,506ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");507a = (struct archive_match *)_a;508509return (a->inclusions.unmatched_count);510}511512int513archive_match_path_unmatched_inclusions_next(struct archive *_a,514const char **_p)515{516struct archive_match *a;517const void *v;518int r;519520archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,521ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");522a = (struct archive_match *)_a;523524r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);525*_p = (const char *)v;526return (r);527}528529int530archive_match_path_unmatched_inclusions_next_w(struct archive *_a,531const wchar_t **_p)532{533struct archive_match *a;534const void *v;535int r;536537archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,538ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");539a = (struct archive_match *)_a;540541r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);542*_p = (const wchar_t *)v;543return (r);544}545546/*547* Add inclusion/exclusion patterns.548*/549static int550add_pattern_mbs(struct archive_match *a, struct match_list *list,551const char *pattern)552{553struct match *match;554size_t len;555556match = calloc(1, sizeof(*match));557if (match == NULL)558return (error_nomem(a));559/* Both "foo/" and "foo" should match "foo/bar". */560len = strlen(pattern);561if (len && pattern[len - 1] == '/')562--len;563archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);564match_list_add(list, match);565a->setflag |= PATTERN_IS_SET;566return (ARCHIVE_OK);567}568569static int570add_pattern_wcs(struct archive_match *a, struct match_list *list,571const wchar_t *pattern)572{573struct match *match;574size_t len;575576match = calloc(1, sizeof(*match));577if (match == NULL)578return (error_nomem(a));579/* Both "foo/" and "foo" should match "foo/bar". */580len = wcslen(pattern);581if (len && pattern[len - 1] == L'/')582--len;583archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);584match_list_add(list, match);585a->setflag |= PATTERN_IS_SET;586return (ARCHIVE_OK);587}588589static int590add_pattern_from_file(struct archive_match *a, struct match_list *mlist,591int mbs, const void *pathname, int nullSeparator)592{593struct archive *ar;594struct archive_entry *ae;595struct archive_string as;596const void *buff;597size_t size;598int64_t offset;599int r;600601ar = archive_read_new();602if (ar == NULL) {603archive_set_error(&(a->archive), ENOMEM, "No memory");604return (ARCHIVE_FATAL);605}606r = archive_read_support_format_raw(ar);607if (r == ARCHIVE_OK)608r = archive_read_support_format_empty(ar);609if (r != ARCHIVE_OK) {610archive_copy_error(&(a->archive), ar);611archive_read_free(ar);612return (r);613}614if (mbs)615r = archive_read_open_filename(ar, pathname, 512*20);616else617r = archive_read_open_filename_w(ar, pathname, 512*20);618if (r != ARCHIVE_OK) {619archive_copy_error(&(a->archive), ar);620archive_read_free(ar);621return (r);622}623r = archive_read_next_header(ar, &ae);624if (r != ARCHIVE_OK) {625archive_read_free(ar);626if (r == ARCHIVE_EOF) {627return (ARCHIVE_OK);628} else {629archive_copy_error(&(a->archive), ar);630return (r);631}632}633634archive_string_init(&as);635636while ((r = archive_read_data_block(ar, &buff, &size, &offset))637== ARCHIVE_OK) {638const char *b = (const char *)buff;639640while (size) {641const char *s = (const char *)b;642size_t length = 0;643int found_separator = 0;644645while (length < size) {646if (nullSeparator) {647if (*b == '\0') {648found_separator = 1;649break;650}651} else {652if (*b == 0x0d || *b == 0x0a) {653found_separator = 1;654break;655}656}657b++;658length++;659}660if (!found_separator) {661archive_strncat(&as, s, length);662/* Read next data block. */663break;664}665b++;666size -= length + 1;667archive_strncat(&as, s, length);668669/* If the line is not empty, add the pattern. */670if (archive_strlen(&as) > 0) {671/* Add pattern. */672r = add_pattern_mbs(a, mlist, as.s);673if (r != ARCHIVE_OK) {674archive_read_free(ar);675archive_string_free(&as);676return (r);677}678archive_string_empty(&as);679}680}681}682683/* If an error occurred, report it immediately. */684if (r < ARCHIVE_OK) {685archive_copy_error(&(a->archive), ar);686archive_read_free(ar);687archive_string_free(&as);688return (r);689}690691/* If the line is not empty, add the pattern. */692if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {693/* Add pattern. */694r = add_pattern_mbs(a, mlist, as.s);695if (r != ARCHIVE_OK) {696archive_read_free(ar);697archive_string_free(&as);698return (r);699}700}701archive_read_free(ar);702archive_string_free(&as);703return (ARCHIVE_OK);704}705706/*707* Test if pathname is excluded by inclusion/exclusion patterns.708*/709static int710path_excluded(struct archive_match *a, int mbs, const void *pathname)711{712struct match *match;713struct match *matched;714int r;715716if (a == NULL)717return (0);718719/* Mark off any unmatched inclusions. */720/* In particular, if a filename does appear in the archive and721* is explicitly included and excluded, then we don't report722* it as missing even though we don't extract it.723*/724matched = NULL;725for (match = a->inclusions.first; match != NULL;726match = match->next){727if (!match->matched &&728(r = match_path_inclusion(a, match, mbs, pathname)) != 0) {729if (r < 0)730return (r);731a->inclusions.unmatched_count--;732match->matched = 1;733matched = match;734}735}736737/* Exclusions take priority */738for (match = a->exclusions.first; match != NULL;739match = match->next){740r = match_path_exclusion(a, match, mbs, pathname);741if (r)742return (r);743}744745/* It's not excluded and we found an inclusion above, so it's746* included. */747if (matched != NULL)748return (0);749750751/* We didn't find an unmatched inclusion, check the remaining ones. */752for (match = a->inclusions.first; match != NULL;753match = match->next){754/* We looked at previously-unmatched inclusions already. */755if (match->matched &&756(r = match_path_inclusion(a, match, mbs, pathname)) != 0) {757if (r < 0)758return (r);759return (0);760}761}762763/* If there were inclusions, default is to exclude. */764if (a->inclusions.first != NULL)765return (1);766767/* No explicit inclusions, default is to match. */768return (0);769}770771/*772* This is a little odd, but it matches the default behavior of773* gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'774*775*/776static int777match_path_exclusion(struct archive_match *a, struct match *m,778int mbs, const void *pn)779{780int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;781int r;782783if (mbs) {784const char *p;785r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);786if (r == 0)787return (archive_pathmatch(p, (const char *)pn, flag));788} else {789const wchar_t *p;790r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);791if (r == 0)792return (archive_pathmatch_w(p, (const wchar_t *)pn,793flag));794}795if (errno == ENOMEM)796return (error_nomem(a));797return (0);798}799800/*801* Again, mimic gtar: inclusions are always anchored (have to match802* the beginning of the path) even though exclusions are not anchored.803*/804static int805match_path_inclusion(struct archive_match *a, struct match *m,806int mbs, const void *pn)807{808/* Recursive operation requires only a prefix match. */809int flag = a->recursive_include ?810PATHMATCH_NO_ANCHOR_END :8110;812int r;813814if (mbs) {815const char *p;816r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);817if (r == 0)818return (archive_pathmatch(p, (const char *)pn, flag));819} else {820const wchar_t *p;821r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);822if (r == 0)823return (archive_pathmatch_w(p, (const wchar_t *)pn,824flag));825}826if (errno == ENOMEM)827return (error_nomem(a));828return (0);829}830831static void832match_list_init(struct match_list *list)833{834list->first = NULL;835list->last = &(list->first);836list->count = 0;837}838839static void840match_list_free(struct match_list *list)841{842struct match *p, *q;843844for (p = list->first; p != NULL; ) {845q = p;846p = p->next;847archive_mstring_clean(&(q->pattern));848free(q);849}850}851852static void853match_list_add(struct match_list *list, struct match *m)854{855*list->last = m;856list->last = &(m->next);857list->count++;858list->unmatched_count++;859}860861static int862match_list_unmatched_inclusions_next(struct archive_match *a,863struct match_list *list, int mbs, const void **vp)864{865struct match *m;866867*vp = NULL;868if (list->unmatched_eof) {869list->unmatched_eof = 0;870return (ARCHIVE_EOF);871}872if (list->unmatched_next == NULL) {873if (list->unmatched_count == 0)874return (ARCHIVE_EOF);875list->unmatched_next = list->first;876}877878for (m = list->unmatched_next; m != NULL; m = m->next) {879int r;880881if (m->matched)882continue;883if (mbs) {884const char *p;885r = archive_mstring_get_mbs(&(a->archive),886&(m->pattern), &p);887if (r < 0 && errno == ENOMEM)888return (error_nomem(a));889if (p == NULL)890p = "";891*vp = p;892} else {893const wchar_t *p;894r = archive_mstring_get_wcs(&(a->archive),895&(m->pattern), &p);896if (r < 0 && errno == ENOMEM)897return (error_nomem(a));898if (p == NULL)899p = L"";900*vp = p;901}902list->unmatched_next = m->next;903if (list->unmatched_next == NULL)904/* To return EOF next time. */905list->unmatched_eof = 1;906return (ARCHIVE_OK);907}908list->unmatched_next = NULL;909return (ARCHIVE_EOF);910}911912/*913* Utility functions to manage inclusion timestamps.914*/915int916archive_match_include_time(struct archive *_a, int flag, time_t sec,917long nsec)918{919int r;920921r = validate_time_flag(_a, flag, "archive_match_include_time");922if (r != ARCHIVE_OK)923return (r);924return set_timefilter((struct archive_match *)_a, flag,925sec, nsec, sec, nsec);926}927928int929archive_match_include_date(struct archive *_a, int flag,930const char *datestr)931{932int r;933934r = validate_time_flag(_a, flag, "archive_match_include_date");935if (r != ARCHIVE_OK)936return (r);937return set_timefilter_date((struct archive_match *)_a, flag, datestr);938}939940int941archive_match_include_date_w(struct archive *_a, int flag,942const wchar_t *datestr)943{944int r;945946r = validate_time_flag(_a, flag, "archive_match_include_date_w");947if (r != ARCHIVE_OK)948return (r);949950return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);951}952953int954archive_match_include_file_time(struct archive *_a, int flag,955const char *pathname)956{957int r;958959r = validate_time_flag(_a, flag, "archive_match_include_file_time");960if (r != ARCHIVE_OK)961return (r);962return set_timefilter_pathname_mbs((struct archive_match *)_a,963flag, pathname);964}965966int967archive_match_include_file_time_w(struct archive *_a, int flag,968const wchar_t *pathname)969{970int r;971972r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");973if (r != ARCHIVE_OK)974return (r);975return set_timefilter_pathname_wcs((struct archive_match *)_a,976flag, pathname);977}978979int980archive_match_exclude_entry(struct archive *_a, int flag,981struct archive_entry *entry)982{983struct archive_match *a;984int r;985986archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,987ARCHIVE_STATE_NEW, "archive_match_time_include_entry");988a = (struct archive_match *)_a;989990if (entry == NULL) {991archive_set_error(&(a->archive), EINVAL, "entry is NULL");992return (ARCHIVE_FAILED);993}994r = validate_time_flag(_a, flag, "archive_match_exclude_entry");995if (r != ARCHIVE_OK)996return (r);997return (add_entry(a, flag, entry));998}9991000/*1001* Test function for time stamps.1002*1003* Returns 1 if archive entry is excluded.1004* Returns 0 if archive entry is not excluded.1005* Returns <0 if something error happened.1006*/1007int1008archive_match_time_excluded(struct archive *_a,1009struct archive_entry *entry)1010{1011struct archive_match *a;10121013archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1014ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");10151016a = (struct archive_match *)_a;1017if (entry == NULL) {1018archive_set_error(&(a->archive), EINVAL, "entry is NULL");1019return (ARCHIVE_FAILED);1020}10211022/* If we don't have inclusion time set at all, the entry is always1023* not excluded. */1024if ((a->setflag & TIME_IS_SET) == 0)1025return (0);1026return (time_excluded(a, entry));1027}10281029static int1030validate_time_flag(struct archive *_a, int flag, const char *_fn)1031{1032archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1033ARCHIVE_STATE_NEW, _fn);10341035/* Check a type of time. */1036if (flag &1037((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {1038archive_set_error(_a, EINVAL, "Invalid time flag");1039return (ARCHIVE_FAILED);1040}1041if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {1042archive_set_error(_a, EINVAL, "No time flag");1043return (ARCHIVE_FAILED);1044}10451046/* Check a type of comparison. */1047if (flag &1048((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER1049| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {1050archive_set_error(_a, EINVAL, "Invalid comparison flag");1051return (ARCHIVE_FAILED);1052}1053if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER1054| ARCHIVE_MATCH_EQUAL)) == 0) {1055archive_set_error(_a, EINVAL, "No comparison flag");1056return (ARCHIVE_FAILED);1057}10581059return (ARCHIVE_OK);1060}10611062#define JUST_EQUAL(t) (((t) & (ARCHIVE_MATCH_EQUAL |\1063ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)1064static int1065set_timefilter(struct archive_match *a, int timetype,1066time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)1067{1068if (timetype & ARCHIVE_MATCH_MTIME) {1069if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {1070a->newer_mtime_filter = timetype;1071a->newer_mtime_sec = mtime_sec;1072a->newer_mtime_nsec = mtime_nsec;1073a->setflag |= TIME_IS_SET;1074}1075if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {1076a->older_mtime_filter = timetype;1077a->older_mtime_sec = mtime_sec;1078a->older_mtime_nsec = mtime_nsec;1079a->setflag |= TIME_IS_SET;1080}1081}1082if (timetype & ARCHIVE_MATCH_CTIME) {1083if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {1084a->newer_ctime_filter = timetype;1085a->newer_ctime_sec = ctime_sec;1086a->newer_ctime_nsec = ctime_nsec;1087a->setflag |= TIME_IS_SET;1088}1089if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {1090a->older_ctime_filter = timetype;1091a->older_ctime_sec = ctime_sec;1092a->older_ctime_nsec = ctime_nsec;1093a->setflag |= TIME_IS_SET;1094}1095}1096return (ARCHIVE_OK);1097}10981099static int1100set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)1101{1102time_t t;11031104if (datestr == NULL || *datestr == '\0') {1105archive_set_error(&(a->archive), EINVAL, "date is empty");1106return (ARCHIVE_FAILED);1107}1108t = get_date(a->now, datestr);1109if (t == (time_t)-1) {1110archive_set_error(&(a->archive), EINVAL, "invalid date string");1111return (ARCHIVE_FAILED);1112}1113return set_timefilter(a, timetype, t, 0, t, 0);1114}11151116static int1117set_timefilter_date_w(struct archive_match *a, int timetype,1118const wchar_t *datestr)1119{1120struct archive_string as;1121time_t t;11221123if (datestr == NULL || *datestr == L'\0') {1124archive_set_error(&(a->archive), EINVAL, "date is empty");1125return (ARCHIVE_FAILED);1126}11271128archive_string_init(&as);1129if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {1130archive_string_free(&as);1131if (errno == ENOMEM)1132return (error_nomem(a));1133archive_set_error(&(a->archive), -1,1134"Failed to convert WCS to MBS");1135return (ARCHIVE_FAILED);1136}1137t = get_date(a->now, as.s);1138archive_string_free(&as);1139if (t == (time_t)-1) {1140archive_set_error(&(a->archive), EINVAL, "invalid date string");1141return (ARCHIVE_FAILED);1142}1143return set_timefilter(a, timetype, t, 0, t, 0);1144}11451146#if defined(_WIN32) && !defined(__CYGWIN__)1147#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)1148static int1149set_timefilter_find_data(struct archive_match *a, int timetype,1150DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,1151DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)1152{1153ULARGE_INTEGER utc;1154time_t ctime_sec, mtime_sec;1155long ctime_ns, mtime_ns;11561157utc.HighPart = ftCreationTime_dwHighDateTime;1158utc.LowPart = ftCreationTime_dwLowDateTime;1159if (utc.QuadPart >= EPOC_TIME) {1160utc.QuadPart -= EPOC_TIME;1161ctime_sec = (time_t)(utc.QuadPart / 10000000);1162ctime_ns = (long)(utc.QuadPart % 10000000) * 100;1163} else {1164ctime_sec = 0;1165ctime_ns = 0;1166}1167utc.HighPart = ftLastWriteTime_dwHighDateTime;1168utc.LowPart = ftLastWriteTime_dwLowDateTime;1169if (utc.QuadPart >= EPOC_TIME) {1170utc.QuadPart -= EPOC_TIME;1171mtime_sec = (time_t)(utc.QuadPart / 10000000);1172mtime_ns = (long)(utc.QuadPart % 10000000) * 100;1173} else {1174mtime_sec = 0;1175mtime_ns = 0;1176}1177return set_timefilter(a, timetype,1178mtime_sec, mtime_ns, ctime_sec, ctime_ns);1179}11801181static int1182set_timefilter_pathname_mbs(struct archive_match *a, int timetype,1183const char *path)1184{1185/* NOTE: stat() on Windows cannot handle nano seconds. */1186HANDLE h;1187WIN32_FIND_DATAA d;11881189if (path == NULL || *path == '\0') {1190archive_set_error(&(a->archive), EINVAL, "pathname is empty");1191return (ARCHIVE_FAILED);1192}1193h = FindFirstFileA(path, &d);1194if (h == INVALID_HANDLE_VALUE) {1195la_dosmaperr(GetLastError());1196archive_set_error(&(a->archive), errno,1197"Failed to FindFirstFileA");1198return (ARCHIVE_FAILED);1199}1200FindClose(h);1201return set_timefilter_find_data(a, timetype,1202d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,1203d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);1204}12051206static int1207set_timefilter_pathname_wcs(struct archive_match *a, int timetype,1208const wchar_t *path)1209{1210HANDLE h;1211WIN32_FIND_DATAW d;12121213if (path == NULL || *path == L'\0') {1214archive_set_error(&(a->archive), EINVAL, "pathname is empty");1215return (ARCHIVE_FAILED);1216}1217h = FindFirstFileW(path, &d);1218if (h == INVALID_HANDLE_VALUE) {1219la_dosmaperr(GetLastError());1220archive_set_error(&(a->archive), errno,1221"Failed to FindFirstFile");1222return (ARCHIVE_FAILED);1223}1224FindClose(h);1225return set_timefilter_find_data(a, timetype,1226d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,1227d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);1228}12291230#else /* _WIN32 && !__CYGWIN__ */12311232static int1233set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)1234{1235struct archive_entry *ae;1236time_t ctime_sec, mtime_sec;1237long ctime_ns, mtime_ns;12381239ae = archive_entry_new();1240if (ae == NULL)1241return (error_nomem(a));1242archive_entry_copy_stat(ae, st);1243ctime_sec = archive_entry_ctime(ae);1244ctime_ns = archive_entry_ctime_nsec(ae);1245mtime_sec = archive_entry_mtime(ae);1246mtime_ns = archive_entry_mtime_nsec(ae);1247archive_entry_free(ae);1248return set_timefilter(a, timetype, mtime_sec, mtime_ns,1249ctime_sec, ctime_ns);1250}12511252static int1253set_timefilter_pathname_mbs(struct archive_match *a, int timetype,1254const char *path)1255{1256struct stat st;12571258if (path == NULL || *path == '\0') {1259archive_set_error(&(a->archive), EINVAL, "pathname is empty");1260return (ARCHIVE_FAILED);1261}1262if (la_stat(path, &st) != 0) {1263archive_set_error(&(a->archive), errno, "Failed to stat()");1264return (ARCHIVE_FAILED);1265}1266return (set_timefilter_stat(a, timetype, &st));1267}12681269static int1270set_timefilter_pathname_wcs(struct archive_match *a, int timetype,1271const wchar_t *path)1272{1273struct archive_string as;1274int r;12751276if (path == NULL || *path == L'\0') {1277archive_set_error(&(a->archive), EINVAL, "pathname is empty");1278return (ARCHIVE_FAILED);1279}12801281/* Convert WCS filename to MBS filename. */1282archive_string_init(&as);1283if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {1284archive_string_free(&as);1285if (errno == ENOMEM)1286return (error_nomem(a));1287archive_set_error(&(a->archive), -1,1288"Failed to convert WCS to MBS");1289return (ARCHIVE_FAILED);1290}12911292r = set_timefilter_pathname_mbs(a, timetype, as.s);1293archive_string_free(&as);12941295return (r);1296}1297#endif /* _WIN32 && !__CYGWIN__ */12981299/*1300* Call back functions for archive_rb.1301*/1302static int1303cmp_node_mbs(const struct archive_rb_node *n1,1304const struct archive_rb_node *n2)1305{1306struct match_file *f1 = (struct match_file *)(uintptr_t)n1;1307struct match_file *f2 = (struct match_file *)(uintptr_t)n2;1308const char *p1, *p2;13091310archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);1311archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);1312if (p1 == NULL)1313return (1);1314if (p2 == NULL)1315return (-1);1316return (strcmp(p1, p2));1317}13181319static int1320cmp_key_mbs(const struct archive_rb_node *n, const void *key)1321{1322struct match_file *f = (struct match_file *)(uintptr_t)n;1323const char *p;13241325archive_mstring_get_mbs(NULL, &(f->pathname), &p);1326if (p == NULL)1327return (-1);1328return (strcmp(p, (const char *)key));1329}13301331static int1332cmp_node_wcs(const struct archive_rb_node *n1,1333const struct archive_rb_node *n2)1334{1335struct match_file *f1 = (struct match_file *)(uintptr_t)n1;1336struct match_file *f2 = (struct match_file *)(uintptr_t)n2;1337const wchar_t *p1, *p2;13381339archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);1340archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);1341if (p1 == NULL)1342return (1);1343if (p2 == NULL)1344return (-1);1345return (wcscmp(p1, p2));1346}13471348static int1349cmp_key_wcs(const struct archive_rb_node *n, const void *key)1350{1351struct match_file *f = (struct match_file *)(uintptr_t)n;1352const wchar_t *p;13531354archive_mstring_get_wcs(NULL, &(f->pathname), &p);1355if (p == NULL)1356return (-1);1357return (wcscmp(p, (const wchar_t *)key));1358}13591360static void1361entry_list_init(struct entry_list *list)1362{1363list->first = NULL;1364list->last = &(list->first);1365list->count = 0;1366}13671368static void1369entry_list_free(struct entry_list *list)1370{1371struct match_file *p, *q;13721373for (p = list->first; p != NULL; ) {1374q = p;1375p = p->next;1376archive_mstring_clean(&(q->pathname));1377free(q);1378}1379}13801381static void1382entry_list_add(struct entry_list *list, struct match_file *file)1383{1384*list->last = file;1385list->last = &(file->next);1386list->count++;1387}13881389static int1390add_entry(struct archive_match *a, int flag,1391struct archive_entry *entry)1392{1393struct match_file *f;1394const void *pathname;1395int r;13961397f = calloc(1, sizeof(*f));1398if (f == NULL)1399return (error_nomem(a));14001401#if defined(_WIN32) && !defined(__CYGWIN__)1402pathname = archive_entry_pathname_w(entry);1403if (pathname == NULL) {1404free(f);1405archive_set_error(&(a->archive), EINVAL, "pathname is NULL");1406return (ARCHIVE_FAILED);1407}1408archive_mstring_copy_wcs(&(f->pathname), pathname);1409a->exclusion_tree.rbt_ops = &rb_ops_wcs;1410#else1411(void)rb_ops_wcs;1412pathname = archive_entry_pathname(entry);1413if (pathname == NULL) {1414free(f);1415archive_set_error(&(a->archive), EINVAL, "pathname is NULL");1416return (ARCHIVE_FAILED);1417}1418archive_mstring_copy_mbs(&(f->pathname), pathname);1419a->exclusion_tree.rbt_ops = &rb_ops_mbs;1420#endif1421f->flag = flag;1422f->mtime_sec = archive_entry_mtime(entry);1423f->mtime_nsec = archive_entry_mtime_nsec(entry);1424f->ctime_sec = archive_entry_ctime(entry);1425f->ctime_nsec = archive_entry_ctime_nsec(entry);1426r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));1427if (!r) {1428struct match_file *f2;14291430/* Get the duplicated file. */1431f2 = (struct match_file *)__archive_rb_tree_find_node(1432&(a->exclusion_tree), pathname);14331434/*1435* We always overwrite comparison condition.1436* If you do not want to overwrite it, you should not1437* call archive_match_exclude_entry(). We cannot know1438* what behavior you really expect since overwriting1439* condition might be different with the flag.1440*/1441if (f2 != NULL) {1442f2->flag = f->flag;1443f2->mtime_sec = f->mtime_sec;1444f2->mtime_nsec = f->mtime_nsec;1445f2->ctime_sec = f->ctime_sec;1446f2->ctime_nsec = f->ctime_nsec;1447}1448/* Release the duplicated file. */1449archive_mstring_clean(&(f->pathname));1450free(f);1451return (ARCHIVE_OK);1452}1453entry_list_add(&(a->exclusion_entry_list), f);1454a->setflag |= TIME_IS_SET;1455return (ARCHIVE_OK);1456}14571458/*1459* Test if entry is excluded by its timestamp.1460*/1461static int1462time_excluded(struct archive_match *a, struct archive_entry *entry)1463{1464struct match_file *f;1465const void *pathname;1466time_t sec;1467long nsec;14681469/*1470* If this file/dir is excluded by a time comparison, skip it.1471*/1472if (a->newer_ctime_filter) {1473/* If ctime is not set, use mtime instead. */1474if (archive_entry_ctime_is_set(entry))1475sec = archive_entry_ctime(entry);1476else1477sec = archive_entry_mtime(entry);1478if (sec < a->newer_ctime_sec)1479return (1); /* Too old, skip it. */1480if (sec == a->newer_ctime_sec) {1481if (archive_entry_ctime_is_set(entry))1482nsec = archive_entry_ctime_nsec(entry);1483else1484nsec = archive_entry_mtime_nsec(entry);1485if (nsec < a->newer_ctime_nsec)1486return (1); /* Too old, skip it. */1487if (nsec == a->newer_ctime_nsec &&1488(a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)1489== 0)1490return (1); /* Equal, skip it. */1491}1492}1493if (a->older_ctime_filter) {1494/* If ctime is not set, use mtime instead. */1495if (archive_entry_ctime_is_set(entry))1496sec = archive_entry_ctime(entry);1497else1498sec = archive_entry_mtime(entry);1499if (sec > a->older_ctime_sec)1500return (1); /* Too new, skip it. */1501if (sec == a->older_ctime_sec) {1502if (archive_entry_ctime_is_set(entry))1503nsec = archive_entry_ctime_nsec(entry);1504else1505nsec = archive_entry_mtime_nsec(entry);1506if (nsec > a->older_ctime_nsec)1507return (1); /* Too new, skip it. */1508if (nsec == a->older_ctime_nsec &&1509(a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)1510== 0)1511return (1); /* Equal, skip it. */1512}1513}1514if (a->newer_mtime_filter) {1515sec = archive_entry_mtime(entry);1516if (sec < a->newer_mtime_sec)1517return (1); /* Too old, skip it. */1518if (sec == a->newer_mtime_sec) {1519nsec = archive_entry_mtime_nsec(entry);1520if (nsec < a->newer_mtime_nsec)1521return (1); /* Too old, skip it. */1522if (nsec == a->newer_mtime_nsec &&1523(a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)1524== 0)1525return (1); /* Equal, skip it. */1526}1527}1528if (a->older_mtime_filter) {1529sec = archive_entry_mtime(entry);1530if (sec > a->older_mtime_sec)1531return (1); /* Too new, skip it. */1532nsec = archive_entry_mtime_nsec(entry);1533if (sec == a->older_mtime_sec) {1534if (nsec > a->older_mtime_nsec)1535return (1); /* Too new, skip it. */1536if (nsec == a->older_mtime_nsec &&1537(a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)1538== 0)1539return (1); /* Equal, skip it. */1540}1541}15421543/* If there is no exclusion list, include the file. */1544if (a->exclusion_entry_list.count == 0)1545return (0);15461547#if defined(_WIN32) && !defined(__CYGWIN__)1548pathname = archive_entry_pathname_w(entry);1549a->exclusion_tree.rbt_ops = &rb_ops_wcs;1550#else1551(void)rb_ops_wcs;1552pathname = archive_entry_pathname(entry);1553a->exclusion_tree.rbt_ops = &rb_ops_mbs;1554#endif1555if (pathname == NULL)1556return (0);15571558f = (struct match_file *)__archive_rb_tree_find_node(1559&(a->exclusion_tree), pathname);1560/* If the file wasn't rejected, include it. */1561if (f == NULL)1562return (0);15631564if (f->flag & ARCHIVE_MATCH_CTIME) {1565sec = archive_entry_ctime(entry);1566if (f->ctime_sec > sec) {1567if (f->flag & ARCHIVE_MATCH_OLDER)1568return (1);1569} else if (f->ctime_sec < sec) {1570if (f->flag & ARCHIVE_MATCH_NEWER)1571return (1);1572} else {1573nsec = archive_entry_ctime_nsec(entry);1574if (f->ctime_nsec > nsec) {1575if (f->flag & ARCHIVE_MATCH_OLDER)1576return (1);1577} else if (f->ctime_nsec < nsec) {1578if (f->flag & ARCHIVE_MATCH_NEWER)1579return (1);1580} else if (f->flag & ARCHIVE_MATCH_EQUAL)1581return (1);1582}1583}1584if (f->flag & ARCHIVE_MATCH_MTIME) {1585sec = archive_entry_mtime(entry);1586if (f->mtime_sec > sec) {1587if (f->flag & ARCHIVE_MATCH_OLDER)1588return (1);1589} else if (f->mtime_sec < sec) {1590if (f->flag & ARCHIVE_MATCH_NEWER)1591return (1);1592} else {1593nsec = archive_entry_mtime_nsec(entry);1594if (f->mtime_nsec > nsec) {1595if (f->flag & ARCHIVE_MATCH_OLDER)1596return (1);1597} else if (f->mtime_nsec < nsec) {1598if (f->flag & ARCHIVE_MATCH_NEWER)1599return (1);1600} else if (f->flag & ARCHIVE_MATCH_EQUAL)1601return (1);1602}1603}1604return (0);1605}16061607/*1608* Utility functions to manage inclusion owners1609*/16101611int1612archive_match_include_uid(struct archive *_a, la_int64_t uid)1613{1614struct archive_match *a;16151616archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1617ARCHIVE_STATE_NEW, "archive_match_include_uid");1618a = (struct archive_match *)_a;1619return (add_owner_id(a, &(a->inclusion_uids), uid));1620}16211622int1623archive_match_include_gid(struct archive *_a, la_int64_t gid)1624{1625struct archive_match *a;16261627archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1628ARCHIVE_STATE_NEW, "archive_match_include_gid");1629a = (struct archive_match *)_a;1630return (add_owner_id(a, &(a->inclusion_gids), gid));1631}16321633int1634archive_match_include_uname(struct archive *_a, const char *uname)1635{1636struct archive_match *a;16371638archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1639ARCHIVE_STATE_NEW, "archive_match_include_uname");1640a = (struct archive_match *)_a;1641return (add_owner_name(a, &(a->inclusion_unames), 1, uname));1642}16431644int1645archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)1646{1647struct archive_match *a;16481649archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1650ARCHIVE_STATE_NEW, "archive_match_include_uname_w");1651a = (struct archive_match *)_a;1652return (add_owner_name(a, &(a->inclusion_unames), 0, uname));1653}16541655int1656archive_match_include_gname(struct archive *_a, const char *gname)1657{1658struct archive_match *a;16591660archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1661ARCHIVE_STATE_NEW, "archive_match_include_gname");1662a = (struct archive_match *)_a;1663return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));1664}16651666int1667archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)1668{1669struct archive_match *a;16701671archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1672ARCHIVE_STATE_NEW, "archive_match_include_gname_w");1673a = (struct archive_match *)_a;1674return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));1675}16761677/*1678* Test function for owner(uid, gid, uname, gname).1679*1680* Returns 1 if archive entry is excluded.1681* Returns 0 if archive entry is not excluded.1682* Returns <0 if something error happened.1683*/1684int1685archive_match_owner_excluded(struct archive *_a,1686struct archive_entry *entry)1687{1688struct archive_match *a;16891690archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1691ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");16921693a = (struct archive_match *)_a;1694if (entry == NULL) {1695archive_set_error(&(a->archive), EINVAL, "entry is NULL");1696return (ARCHIVE_FAILED);1697}16981699/* If we don't have inclusion id set at all, the entry is always1700* not excluded. */1701if ((a->setflag & ID_IS_SET) == 0)1702return (0);1703return (owner_excluded(a, entry));1704}17051706static int1707add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)1708{1709unsigned i;17101711if (ids->count + 1 >= ids->size) {1712void *p;17131714if (ids->size == 0)1715ids->size = 8;1716else1717ids->size *= 2;1718p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);1719if (p == NULL)1720return (error_nomem(a));1721ids->ids = (int64_t *)p;1722}17231724/* Find an insert point. */1725for (i = 0; i < ids->count; i++) {1726if (ids->ids[i] >= id)1727break;1728}17291730/* Add owner id. */1731if (i == ids->count)1732ids->ids[ids->count++] = id;1733else if (ids->ids[i] != id) {1734memmove(&(ids->ids[i+1]), &(ids->ids[i]),1735(ids->count - i) * sizeof(ids->ids[0]));1736ids->ids[i] = id;1737ids->count++;1738}1739a->setflag |= ID_IS_SET;1740return (ARCHIVE_OK);1741}17421743static int1744match_owner_id(struct id_array *ids, int64_t id)1745{1746unsigned b, m, t;17471748t = 0;1749b = (unsigned)ids->count;1750while (t < b) {1751m = (t + b)>>1;1752if (ids->ids[m] == id)1753return (1);1754if (ids->ids[m] < id)1755t = m + 1;1756else1757b = m;1758}1759return (0);1760}17611762static int1763add_owner_name(struct archive_match *a, struct match_list *list,1764int mbs, const void *name)1765{1766struct match *match;17671768match = calloc(1, sizeof(*match));1769if (match == NULL)1770return (error_nomem(a));1771if (mbs)1772archive_mstring_copy_mbs(&(match->pattern), name);1773else1774archive_mstring_copy_wcs(&(match->pattern), name);1775match_list_add(list, match);1776a->setflag |= ID_IS_SET;1777return (ARCHIVE_OK);1778}17791780#if !defined(_WIN32) || defined(__CYGWIN__)1781static int1782match_owner_name_mbs(struct archive_match *a, struct match_list *list,1783const char *name)1784{1785struct match *m;1786const char *p;17871788if (name == NULL || *name == '\0')1789return (0);1790for (m = list->first; m; m = m->next) {1791if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)1792< 0 && errno == ENOMEM)1793return (error_nomem(a));1794if (p != NULL && strcmp(p, name) == 0) {1795m->matched = 1;1796return (1);1797}1798}1799return (0);1800}1801#else1802static int1803match_owner_name_wcs(struct archive_match *a, struct match_list *list,1804const wchar_t *name)1805{1806struct match *m;1807const wchar_t *p;18081809if (name == NULL || *name == L'\0')1810return (0);1811for (m = list->first; m; m = m->next) {1812if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)1813< 0 && errno == ENOMEM)1814return (error_nomem(a));1815if (p != NULL && wcscmp(p, name) == 0) {1816m->matched = 1;1817return (1);1818}1819}1820return (0);1821}1822#endif18231824/*1825* Test if entry is excluded by uid, gid, uname or gname.1826*/1827static int1828owner_excluded(struct archive_match *a, struct archive_entry *entry)1829{1830int r;18311832if (a->inclusion_uids.count) {1833if (!match_owner_id(&(a->inclusion_uids),1834archive_entry_uid(entry)))1835return (1);1836}18371838if (a->inclusion_gids.count) {1839if (!match_owner_id(&(a->inclusion_gids),1840archive_entry_gid(entry)))1841return (1);1842}18431844if (a->inclusion_unames.count) {1845#if defined(_WIN32) && !defined(__CYGWIN__)1846r = match_owner_name_wcs(a, &(a->inclusion_unames),1847archive_entry_uname_w(entry));1848#else1849r = match_owner_name_mbs(a, &(a->inclusion_unames),1850archive_entry_uname(entry));1851#endif1852if (!r)1853return (1);1854else if (r < 0)1855return (r);1856}18571858if (a->inclusion_gnames.count) {1859#if defined(_WIN32) && !defined(__CYGWIN__)1860r = match_owner_name_wcs(a, &(a->inclusion_gnames),1861archive_entry_gname_w(entry));1862#else1863r = match_owner_name_mbs(a, &(a->inclusion_gnames),1864archive_entry_gname(entry));1865#endif1866if (!r)1867return (1);1868else if (r < 0)1869return (r);1870}1871return (0);1872}1873187418751876