Path: blob/master/Utilities/cmlibarchive/libarchive/archive_match.c
5090 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#endif37#ifdef HAVE_LIMITS_H38#include <limits.h>39#endif4041#include "archive.h"42#include "archive_private.h"43#include "archive_entry.h"44#include "archive_pathmatch.h"45#include "archive_rb.h"46#include "archive_string.h"47#include "archive_time_private.h"4849struct match {50struct match *next;51int matched;52struct archive_mstring pattern;53};5455struct match_list {56struct match *first;57struct match **last;58size_t unmatched_count;59struct match *unmatched_next;60int unmatched_eof;61};6263struct match_file {64struct archive_rb_node node;65struct match_file *next;66struct archive_mstring pathname;67int flag;68time_t mtime_sec;69long mtime_nsec;70time_t ctime_sec;71long ctime_nsec;72};7374struct entry_list {75struct match_file *first;76struct match_file **last;77};7879struct id_array {80size_t size;/* Allocated size */81size_t count;82int64_t *ids;83};8485#define PATTERN_IS_SET 186#define TIME_IS_SET 287#define ID_IS_SET 48889struct archive_match {90struct archive archive;9192/* exclusion/inclusion set flag. */93int setflag;9495/* Recursively include directory content? */96int recursive_include;9798/*99* Matching filename patterns.100*/101struct match_list exclusions;102struct match_list inclusions;103104/*105* Matching time stamps.106*/107time_t now;108int newer_mtime_filter;109time_t newer_mtime_sec;110long newer_mtime_nsec;111int newer_ctime_filter;112time_t newer_ctime_sec;113long newer_ctime_nsec;114int older_mtime_filter;115time_t older_mtime_sec;116long older_mtime_nsec;117int older_ctime_filter;118time_t older_ctime_sec;119long older_ctime_nsec;120/*121* Matching time stamps with its filename.122*/123struct archive_rb_tree exclusion_tree;124struct entry_list exclusion_entry_list;125126/*127* Matching file owners.128*/129struct id_array inclusion_uids;130struct id_array inclusion_gids;131struct match_list inclusion_unames;132struct match_list inclusion_gnames;133};134135static int add_pattern_from_file(struct archive_match *,136struct match_list *, int, const void *, int);137static int add_entry(struct archive_match *, int,138struct archive_entry *);139static int add_owner_id(struct archive_match *, struct id_array *,140int64_t);141static int add_owner_name(struct archive_match *, struct match_list *,142int, const void *);143static int add_pattern_mbs(struct archive_match *, struct match_list *,144const char *);145static int add_pattern_wcs(struct archive_match *, struct match_list *,146const wchar_t *);147#if !defined(_WIN32) || defined(__CYGWIN__)148static int cmp_key_mbs(const struct archive_rb_node *, const void *);149static int cmp_node_mbs(const struct archive_rb_node *,150const struct archive_rb_node *);151#else152static int cmp_key_wcs(const struct archive_rb_node *, const void *);153static int cmp_node_wcs(const struct archive_rb_node *,154const struct archive_rb_node *);155#endif156static void entry_list_add(struct entry_list *, struct match_file *);157static void entry_list_free(struct entry_list *);158static void entry_list_init(struct entry_list *);159static int error_nomem(struct archive_match *);160static void match_list_add(struct match_list *, struct match *);161static void match_list_free(struct match_list *);162static void match_list_init(struct match_list *);163static int match_list_unmatched_inclusions_next(struct archive_match *,164struct match_list *, int, const void **);165static int match_owner_id(struct id_array *, int64_t);166#if !defined(_WIN32) || defined(__CYGWIN__)167static int match_owner_name_mbs(struct archive_match *,168struct match_list *, const char *);169#else170static int match_owner_name_wcs(struct archive_match *,171struct match_list *, const wchar_t *);172#endif173static int match_path_exclusion(struct archive_match *,174struct match *, int, const void *);175static int match_path_inclusion(struct archive_match *,176struct match *, int, const void *);177static int owner_excluded(struct archive_match *,178struct archive_entry *);179static int path_excluded(struct archive_match *, int, const void *);180static int set_timefilter(struct archive_match *, int, time_t, long,181time_t, long);182static int set_timefilter_pathname_mbs(struct archive_match *,183int, const char *);184static int set_timefilter_pathname_wcs(struct archive_match *,185int, const wchar_t *);186static int set_timefilter_date(struct archive_match *, int, const char *);187static int set_timefilter_date_w(struct archive_match *, int,188const wchar_t *);189static int time_excluded(struct archive_match *,190struct archive_entry *);191static int validate_time_flag(struct archive *, int, const char *);192193#define get_date archive_parse_date194195static const struct archive_rb_tree_ops rb_ops = {196#if !defined(_WIN32) || defined(__CYGWIN__)197cmp_node_mbs, cmp_key_mbs198#else199cmp_node_wcs, cmp_key_wcs200#endif201};202203/*204* The matching logic here needs to be re-thought. I started out to205* try to mimic gtar's matching logic, but it's not entirely206* consistent. In particular 'tar -t' and 'tar -x' interpret patterns207* on the command line as anchored, but --exclude doesn't.208*/209210static int211error_nomem(struct archive_match *a)212{213archive_set_error(&(a->archive), ENOMEM, "No memory");214a->archive.state = ARCHIVE_STATE_FATAL;215return (ARCHIVE_FATAL);216}217218/*219* Create an ARCHIVE_MATCH object.220*/221struct archive *222archive_match_new(void)223{224struct archive_match *a;225226a = calloc(1, sizeof(*a));227if (a == NULL)228return (NULL);229a->archive.magic = ARCHIVE_MATCH_MAGIC;230a->archive.state = ARCHIVE_STATE_NEW;231a->recursive_include = 1;232match_list_init(&(a->inclusions));233match_list_init(&(a->exclusions));234__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops);235entry_list_init(&(a->exclusion_entry_list));236match_list_init(&(a->inclusion_unames));237match_list_init(&(a->inclusion_gnames));238time(&a->now);239return (&(a->archive));240}241242/*243* Free an ARCHIVE_MATCH object.244*/245int246archive_match_free(struct archive *_a)247{248struct archive_match *a;249250if (_a == NULL)251return (ARCHIVE_OK);252archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,253ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");254a = (struct archive_match *)_a;255match_list_free(&(a->inclusions));256match_list_free(&(a->exclusions));257entry_list_free(&(a->exclusion_entry_list));258free(a->inclusion_uids.ids);259free(a->inclusion_gids.ids);260match_list_free(&(a->inclusion_unames));261match_list_free(&(a->inclusion_gnames));262free(a);263return (ARCHIVE_OK);264}265266/*267* Convenience function to perform all exclusion tests.268*269* Returns 1 if archive entry is excluded.270* Returns 0 if archive entry is not excluded.271* Returns <0 if something error happened.272*/273int274archive_match_excluded(struct archive *_a, struct archive_entry *entry)275{276struct archive_match *a;277int r;278279archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,280ARCHIVE_STATE_NEW, "archive_match_excluded_ae");281282a = (struct archive_match *)_a;283if (entry == NULL) {284archive_set_error(&(a->archive), EINVAL, "entry is NULL");285return (ARCHIVE_FAILED);286}287288r = 0;289if (a->setflag & PATTERN_IS_SET) {290#if defined(_WIN32) && !defined(__CYGWIN__)291r = path_excluded(a, 0, archive_entry_pathname_w(entry));292#else293r = path_excluded(a, 1, archive_entry_pathname(entry));294#endif295if (r != 0)296return (r);297}298299if (a->setflag & TIME_IS_SET) {300r = time_excluded(a, entry);301if (r != 0)302return (r);303}304305if (a->setflag & ID_IS_SET)306r = owner_excluded(a, entry);307return (r);308}309310/*311* Utility functions to manage exclusion/inclusion patterns312*/313314int315archive_match_exclude_pattern(struct archive *_a, const char *pattern)316{317struct archive_match *a;318int r;319320archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,321ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");322a = (struct archive_match *)_a;323324if (pattern == NULL || *pattern == '\0') {325archive_set_error(&(a->archive), EINVAL, "pattern is empty");326return (ARCHIVE_FAILED);327}328if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)329return (r);330return (ARCHIVE_OK);331}332333int334archive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)335{336struct archive_match *a;337int r;338339archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,340ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");341a = (struct archive_match *)_a;342343if (pattern == NULL || *pattern == L'\0') {344archive_set_error(&(a->archive), EINVAL, "pattern is empty");345return (ARCHIVE_FAILED);346}347if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)348return (r);349return (ARCHIVE_OK);350}351352int353archive_match_exclude_pattern_from_file(struct archive *_a,354const char *pathname, int nullSeparator)355{356struct archive_match *a;357358archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,359ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");360a = (struct archive_match *)_a;361362return add_pattern_from_file(a, &(a->exclusions), 1, pathname,363nullSeparator);364}365366int367archive_match_exclude_pattern_from_file_w(struct archive *_a,368const wchar_t *pathname, int nullSeparator)369{370struct archive_match *a;371372archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,373ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");374a = (struct archive_match *)_a;375376return add_pattern_from_file(a, &(a->exclusions), 0, pathname,377nullSeparator);378}379380int381archive_match_include_pattern(struct archive *_a, const char *pattern)382{383struct archive_match *a;384int r;385386archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,387ARCHIVE_STATE_NEW, "archive_match_include_pattern");388a = (struct archive_match *)_a;389390if (pattern == NULL || *pattern == '\0') {391archive_set_error(&(a->archive), EINVAL, "pattern is empty");392return (ARCHIVE_FAILED);393}394if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)395return (r);396return (ARCHIVE_OK);397}398399int400archive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)401{402struct archive_match *a;403int r;404405archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,406ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");407a = (struct archive_match *)_a;408409if (pattern == NULL || *pattern == L'\0') {410archive_set_error(&(a->archive), EINVAL, "pattern is empty");411return (ARCHIVE_FAILED);412}413if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)414return (r);415return (ARCHIVE_OK);416}417418int419archive_match_include_pattern_from_file(struct archive *_a,420const char *pathname, int nullSeparator)421{422struct archive_match *a;423424archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,425ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");426a = (struct archive_match *)_a;427428return add_pattern_from_file(a, &(a->inclusions), 1, pathname,429nullSeparator);430}431432int433archive_match_include_pattern_from_file_w(struct archive *_a,434const wchar_t *pathname, int nullSeparator)435{436struct archive_match *a;437438archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,439ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");440a = (struct archive_match *)_a;441442return add_pattern_from_file(a, &(a->inclusions), 0, pathname,443nullSeparator);444}445446/*447* Test functions for pathname patterns.448*449* Returns 1 if archive entry is excluded.450* Returns 0 if archive entry is not excluded.451* Returns <0 if something error happened.452*/453int454archive_match_path_excluded(struct archive *_a,455struct archive_entry *entry)456{457struct archive_match *a;458459archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,460ARCHIVE_STATE_NEW, "archive_match_path_excluded");461462a = (struct archive_match *)_a;463if (entry == NULL) {464archive_set_error(&(a->archive), EINVAL, "entry is NULL");465return (ARCHIVE_FAILED);466}467468/* If we don't have exclusion/inclusion pattern set at all,469* the entry is always not excluded. */470if ((a->setflag & PATTERN_IS_SET) == 0)471return (0);472#if defined(_WIN32) && !defined(__CYGWIN__)473return (path_excluded(a, 0, archive_entry_pathname_w(entry)));474#else475return (path_excluded(a, 1, archive_entry_pathname(entry)));476#endif477}478479/*480* When recursive inclusion of directory content is enabled,481* an inclusion pattern that matches a directory will also482* include everything beneath that directory. Enabled by default.483*484* For compatibility with GNU tar, exclusion patterns always485* match if a subset of the full patch matches (i.e., they are486* are not rooted at the beginning of the path) and thus there487* is no corresponding non-recursive exclusion mode.488*/489int490archive_match_set_inclusion_recursion(struct archive *_a, int enabled)491{492struct archive_match *a;493494archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,495ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");496a = (struct archive_match *)_a;497a->recursive_include = enabled;498return (ARCHIVE_OK);499}500501/*502* Utility functions to get statistic information for inclusion patterns.503*/504int505archive_match_path_unmatched_inclusions(struct archive *_a)506{507struct archive_match *a;508509archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,510ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");511a = (struct archive_match *)_a;512513if (a->inclusions.unmatched_count > (size_t)INT_MAX)514return INT_MAX;515return (int)(a->inclusions.unmatched_count);516}517518int519archive_match_path_unmatched_inclusions_next(struct archive *_a,520const char **_p)521{522struct archive_match *a;523const void *v;524int r;525526archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,527ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");528a = (struct archive_match *)_a;529530r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);531*_p = (const char *)v;532return (r);533}534535int536archive_match_path_unmatched_inclusions_next_w(struct archive *_a,537const wchar_t **_p)538{539struct archive_match *a;540const void *v;541int r;542543archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,544ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");545a = (struct archive_match *)_a;546547r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);548*_p = (const wchar_t *)v;549return (r);550}551552/*553* Add inclusion/exclusion patterns.554*/555static int556add_pattern_mbs(struct archive_match *a, struct match_list *list,557const char *pattern)558{559struct match *match;560size_t len;561562match = calloc(1, sizeof(*match));563if (match == NULL)564return (error_nomem(a));565/* Both "foo/" and "foo" should match "foo/bar". */566len = strlen(pattern);567if (len && pattern[len - 1] == '/')568--len;569archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);570match_list_add(list, match);571a->setflag |= PATTERN_IS_SET;572return (ARCHIVE_OK);573}574575static int576add_pattern_wcs(struct archive_match *a, struct match_list *list,577const wchar_t *pattern)578{579struct match *match;580size_t len;581582match = calloc(1, sizeof(*match));583if (match == NULL)584return (error_nomem(a));585/* Both "foo/" and "foo" should match "foo/bar". */586len = wcslen(pattern);587if (len && pattern[len - 1] == L'/')588--len;589archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);590match_list_add(list, match);591a->setflag |= PATTERN_IS_SET;592return (ARCHIVE_OK);593}594595static int596add_pattern_from_file(struct archive_match *a, struct match_list *mlist,597int mbs, const void *pathname, int nullSeparator)598{599struct archive *ar;600struct archive_entry *ae;601struct archive_string as;602const void *buff;603size_t size;604int64_t offset;605int r;606607ar = archive_read_new();608if (ar == NULL) {609archive_set_error(&(a->archive), ENOMEM, "No memory");610return (ARCHIVE_FATAL);611}612r = archive_read_support_format_raw(ar);613if (r == ARCHIVE_OK)614r = archive_read_support_format_empty(ar);615if (r != ARCHIVE_OK) {616archive_copy_error(&(a->archive), ar);617archive_read_free(ar);618return (r);619}620if (mbs)621r = archive_read_open_filename(ar, pathname, 512*20);622else623r = archive_read_open_filename_w(ar, pathname, 512*20);624if (r != ARCHIVE_OK) {625archive_copy_error(&(a->archive), ar);626archive_read_free(ar);627return (r);628}629r = archive_read_next_header(ar, &ae);630if (r != ARCHIVE_OK) {631archive_read_free(ar);632if (r == ARCHIVE_EOF) {633return (ARCHIVE_OK);634} else {635archive_copy_error(&(a->archive), ar);636return (r);637}638}639640archive_string_init(&as);641642while ((r = archive_read_data_block(ar, &buff, &size, &offset))643== ARCHIVE_OK) {644const char *b = (const char *)buff;645646while (size) {647const char *s = (const char *)b;648size_t length = 0;649int found_separator = 0;650651while (length < size) {652if (nullSeparator) {653if (*b == '\0') {654found_separator = 1;655break;656}657} else {658if (*b == 0x0d || *b == 0x0a) {659found_separator = 1;660break;661}662}663b++;664length++;665}666if (!found_separator) {667archive_strncat(&as, s, length);668/* Read next data block. */669break;670}671b++;672size -= length + 1;673archive_strncat(&as, s, length);674675/* If the line is not empty, add the pattern. */676if (archive_strlen(&as) > 0) {677/* Add pattern. */678r = add_pattern_mbs(a, mlist, as.s);679if (r != ARCHIVE_OK) {680archive_read_free(ar);681archive_string_free(&as);682return (r);683}684archive_string_empty(&as);685}686}687}688689/* If an error occurred, report it immediately. */690if (r < ARCHIVE_OK) {691archive_copy_error(&(a->archive), ar);692archive_read_free(ar);693archive_string_free(&as);694return (r);695}696697/* If the line is not empty, add the pattern. */698if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {699/* Add pattern. */700r = add_pattern_mbs(a, mlist, as.s);701if (r != ARCHIVE_OK) {702archive_read_free(ar);703archive_string_free(&as);704return (r);705}706}707archive_read_free(ar);708archive_string_free(&as);709return (ARCHIVE_OK);710}711712/*713* Test if pathname is excluded by inclusion/exclusion patterns.714*/715static int716path_excluded(struct archive_match *a, int mbs, const void *pathname)717{718struct match *match;719struct match *matched;720int r;721722if (a == NULL)723return (0);724725/* Mark off any unmatched inclusions. */726/* In particular, if a filename does appear in the archive and727* is explicitly included and excluded, then we don't report728* it as missing even though we don't extract it.729*/730matched = NULL;731for (match = a->inclusions.first; match != NULL;732match = match->next){733if (!match->matched &&734(r = match_path_inclusion(a, match, mbs, pathname)) != 0) {735if (r < 0)736return (r);737a->inclusions.unmatched_count--;738match->matched = 1;739matched = match;740}741}742743/* Exclusions take priority. */744for (match = a->exclusions.first; match != NULL;745match = match->next){746r = match_path_exclusion(a, match, mbs, pathname);747if (r)748return (r);749}750751/* It's not excluded and we found an inclusion above, so it's752* included. */753if (matched != NULL)754return (0);755756757/* We didn't find an unmatched inclusion, check the remaining ones. */758for (match = a->inclusions.first; match != NULL;759match = match->next){760/* We looked at previously-unmatched inclusions already. */761if (match->matched &&762(r = match_path_inclusion(a, match, mbs, pathname)) != 0) {763if (r < 0)764return (r);765return (0);766}767}768769/* If there were inclusions, default is to exclude. */770if (a->inclusions.first != NULL)771return (1);772773/* No explicit inclusions, default is to match. */774return (0);775}776777/*778* This is a little odd, but it matches the default behavior of779* gtar. In particular, 'a*b' will match 'foo/a1111/222b/bar'780*781*/782static int783match_path_exclusion(struct archive_match *a, struct match *m,784int mbs, const void *pn)785{786int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;787int r;788789if (mbs) {790const char *p;791r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);792if (r == 0)793return (archive_pathmatch(p, (const char *)pn, flag));794} else {795const wchar_t *p;796r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);797if (r == 0)798return (archive_pathmatch_w(p, (const wchar_t *)pn,799flag));800}801if (errno == ENOMEM)802return (error_nomem(a));803return (0);804}805806/*807* Again, mimic gtar: inclusions are always anchored (have to match808* the beginning of the path) even though exclusions are not anchored.809*/810static int811match_path_inclusion(struct archive_match *a, struct match *m,812int mbs, const void *pn)813{814/* Recursive operation requires only a prefix match. */815int flag = a->recursive_include ?816PATHMATCH_NO_ANCHOR_END :8170;818int r;819820if (mbs) {821const char *p;822r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);823if (r == 0)824return (archive_pathmatch(p, (const char *)pn, flag));825} else {826const wchar_t *p;827r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);828if (r == 0)829return (archive_pathmatch_w(p, (const wchar_t *)pn,830flag));831}832if (errno == ENOMEM)833return (error_nomem(a));834return (0);835}836837static void838match_list_init(struct match_list *list)839{840list->first = NULL;841list->last = &(list->first);842}843844static void845match_list_free(struct match_list *list)846{847struct match *p, *q;848849for (p = list->first; p != NULL; ) {850q = p;851p = p->next;852archive_mstring_clean(&(q->pattern));853free(q);854}855}856857static void858match_list_add(struct match_list *list, struct match *m)859{860*list->last = m;861list->last = &(m->next);862list->unmatched_count++;863}864865static int866match_list_unmatched_inclusions_next(struct archive_match *a,867struct match_list *list, int mbs, const void **vp)868{869struct match *m;870871*vp = NULL;872if (list->unmatched_eof) {873list->unmatched_eof = 0;874return (ARCHIVE_EOF);875}876if (list->unmatched_next == NULL) {877if (list->unmatched_count == 0)878return (ARCHIVE_EOF);879list->unmatched_next = list->first;880}881882for (m = list->unmatched_next; m != NULL; m = m->next) {883int r;884885if (m->matched)886continue;887if (mbs) {888const char *p;889r = archive_mstring_get_mbs(&(a->archive),890&(m->pattern), &p);891if (r < 0 && errno == ENOMEM)892return (error_nomem(a));893if (p == NULL)894p = "";895*vp = p;896} else {897const wchar_t *p;898r = archive_mstring_get_wcs(&(a->archive),899&(m->pattern), &p);900if (r < 0 && errno == ENOMEM)901return (error_nomem(a));902if (p == NULL)903p = L"";904*vp = p;905}906list->unmatched_next = m->next;907if (list->unmatched_next == NULL)908/* To return EOF next time. */909list->unmatched_eof = 1;910return (ARCHIVE_OK);911}912list->unmatched_next = NULL;913return (ARCHIVE_EOF);914}915916/*917* Utility functions to manage inclusion timestamps.918*/919int920archive_match_include_time(struct archive *_a, int flag, time_t sec,921long nsec)922{923int r;924925r = validate_time_flag(_a, flag, "archive_match_include_time");926if (r != ARCHIVE_OK)927return (r);928return set_timefilter((struct archive_match *)_a, flag,929sec, nsec, sec, nsec);930}931932int933archive_match_include_date(struct archive *_a, int flag,934const char *datestr)935{936int r;937938r = validate_time_flag(_a, flag, "archive_match_include_date");939if (r != ARCHIVE_OK)940return (r);941return set_timefilter_date((struct archive_match *)_a, flag, datestr);942}943944int945archive_match_include_date_w(struct archive *_a, int flag,946const wchar_t *datestr)947{948int r;949950r = validate_time_flag(_a, flag, "archive_match_include_date_w");951if (r != ARCHIVE_OK)952return (r);953954return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);955}956957int958archive_match_include_file_time(struct archive *_a, int flag,959const char *pathname)960{961int r;962963r = validate_time_flag(_a, flag, "archive_match_include_file_time");964if (r != ARCHIVE_OK)965return (r);966return set_timefilter_pathname_mbs((struct archive_match *)_a,967flag, pathname);968}969970int971archive_match_include_file_time_w(struct archive *_a, int flag,972const wchar_t *pathname)973{974int r;975976r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");977if (r != ARCHIVE_OK)978return (r);979return set_timefilter_pathname_wcs((struct archive_match *)_a,980flag, pathname);981}982983int984archive_match_exclude_entry(struct archive *_a, int flag,985struct archive_entry *entry)986{987struct archive_match *a;988int r;989990archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,991ARCHIVE_STATE_NEW, "archive_match_time_include_entry");992a = (struct archive_match *)_a;993994if (entry == NULL) {995archive_set_error(&(a->archive), EINVAL, "entry is NULL");996return (ARCHIVE_FAILED);997}998r = validate_time_flag(_a, flag, "archive_match_exclude_entry");999if (r != ARCHIVE_OK)1000return (r);1001return (add_entry(a, flag, entry));1002}10031004/*1005* Test function for time stamps.1006*1007* Returns 1 if archive entry is excluded.1008* Returns 0 if archive entry is not excluded.1009* Returns <0 if something error happened.1010*/1011int1012archive_match_time_excluded(struct archive *_a,1013struct archive_entry *entry)1014{1015struct archive_match *a;10161017archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1018ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");10191020a = (struct archive_match *)_a;1021if (entry == NULL) {1022archive_set_error(&(a->archive), EINVAL, "entry is NULL");1023return (ARCHIVE_FAILED);1024}10251026/* If we don't have inclusion time set at all, the entry is always1027* not excluded. */1028if ((a->setflag & TIME_IS_SET) == 0)1029return (0);1030return (time_excluded(a, entry));1031}10321033static int1034validate_time_flag(struct archive *_a, int flag, const char *_fn)1035{1036archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1037ARCHIVE_STATE_NEW, _fn);10381039/* Check a type of time. */1040if (flag &1041((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {1042archive_set_error(_a, EINVAL, "Invalid time flag");1043return (ARCHIVE_FAILED);1044}1045if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {1046archive_set_error(_a, EINVAL, "No time flag");1047return (ARCHIVE_FAILED);1048}10491050/* Check a type of comparison. */1051if (flag &1052((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER1053| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {1054archive_set_error(_a, EINVAL, "Invalid comparison flag");1055return (ARCHIVE_FAILED);1056}1057if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER1058| ARCHIVE_MATCH_EQUAL)) == 0) {1059archive_set_error(_a, EINVAL, "No comparison flag");1060return (ARCHIVE_FAILED);1061}10621063return (ARCHIVE_OK);1064}10651066#define JUST_EQUAL(t) (((t) & (ARCHIVE_MATCH_EQUAL |\1067ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)1068static int1069set_timefilter(struct archive_match *a, int timetype,1070time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)1071{1072if (timetype & ARCHIVE_MATCH_MTIME) {1073if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {1074a->newer_mtime_filter = timetype;1075a->newer_mtime_sec = mtime_sec;1076a->newer_mtime_nsec = mtime_nsec;1077a->setflag |= TIME_IS_SET;1078}1079if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {1080a->older_mtime_filter = timetype;1081a->older_mtime_sec = mtime_sec;1082a->older_mtime_nsec = mtime_nsec;1083a->setflag |= TIME_IS_SET;1084}1085}1086if (timetype & ARCHIVE_MATCH_CTIME) {1087if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {1088a->newer_ctime_filter = timetype;1089a->newer_ctime_sec = ctime_sec;1090a->newer_ctime_nsec = ctime_nsec;1091a->setflag |= TIME_IS_SET;1092}1093if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {1094a->older_ctime_filter = timetype;1095a->older_ctime_sec = ctime_sec;1096a->older_ctime_nsec = ctime_nsec;1097a->setflag |= TIME_IS_SET;1098}1099}1100return (ARCHIVE_OK);1101}11021103static int1104set_timefilter_date(struct archive_match *a, int timetype, const char *datestr)1105{1106time_t t;11071108if (datestr == NULL || *datestr == '\0') {1109archive_set_error(&(a->archive), EINVAL, "date is empty");1110return (ARCHIVE_FAILED);1111}1112t = get_date(a->now, datestr);1113if (t == (time_t)-1) {1114archive_set_error(&(a->archive), EINVAL, "invalid date string");1115return (ARCHIVE_FAILED);1116}1117return set_timefilter(a, timetype, t, 0, t, 0);1118}11191120static int1121set_timefilter_date_w(struct archive_match *a, int timetype,1122const wchar_t *datestr)1123{1124struct archive_string as;1125time_t t;11261127if (datestr == NULL || *datestr == L'\0') {1128archive_set_error(&(a->archive), EINVAL, "date is empty");1129return (ARCHIVE_FAILED);1130}11311132archive_string_init(&as);1133if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {1134archive_string_free(&as);1135if (errno == ENOMEM)1136return (error_nomem(a));1137archive_set_error(&(a->archive), -1,1138"Failed to convert WCS to MBS");1139return (ARCHIVE_FAILED);1140}1141t = get_date(a->now, as.s);1142archive_string_free(&as);1143if (t == (time_t)-1) {1144archive_set_error(&(a->archive), EINVAL, "invalid date string");1145return (ARCHIVE_FAILED);1146}1147return set_timefilter(a, timetype, t, 0, t, 0);1148}11491150#if defined(_WIN32) && !defined(__CYGWIN__)1151static int1152set_timefilter_find_data(struct archive_match *a, int timetype,1153const FILETIME* ftLastWriteTime, const FILETIME* ftCreationTime)1154{1155time_t ctime_sec, mtime_sec;1156uint32_t ctime_ns, mtime_ns;11571158ntfs_to_unix(FILETIME_to_ntfs(ftLastWriteTime), &mtime_sec, &mtime_ns);1159ntfs_to_unix(FILETIME_to_ntfs(ftCreationTime), &ctime_sec, &ctime_ns);1160return set_timefilter(a, timetype,1161mtime_sec, mtime_ns, ctime_sec, ctime_ns);1162}11631164static int1165set_timefilter_pathname_mbs(struct archive_match *a, int timetype,1166const char *path)1167{1168/* NOTE: stat() on Windows cannot handle nano seconds. */1169HANDLE h;1170WIN32_FIND_DATAA d;11711172if (path == NULL || *path == '\0') {1173archive_set_error(&(a->archive), EINVAL, "pathname is empty");1174return (ARCHIVE_FAILED);1175}1176h = FindFirstFileA(path, &d);1177if (h == INVALID_HANDLE_VALUE) {1178la_dosmaperr(GetLastError());1179archive_set_error(&(a->archive), errno,1180"Failed to FindFirstFileA");1181return (ARCHIVE_FAILED);1182}1183FindClose(h);1184return set_timefilter_find_data(a, timetype, &d.ftLastWriteTime, &d.ftCreationTime);1185}11861187static int1188set_timefilter_pathname_wcs(struct archive_match *a, int timetype,1189const wchar_t *path)1190{1191HANDLE h;1192WIN32_FIND_DATAW d;11931194if (path == NULL || *path == L'\0') {1195archive_set_error(&(a->archive), EINVAL, "pathname is empty");1196return (ARCHIVE_FAILED);1197}1198h = FindFirstFileW(path, &d);1199if (h == INVALID_HANDLE_VALUE) {1200la_dosmaperr(GetLastError());1201archive_set_error(&(a->archive), errno,1202"Failed to FindFirstFile");1203return (ARCHIVE_FAILED);1204}1205FindClose(h);1206return set_timefilter_find_data(a, timetype, &d.ftLastWriteTime, &d.ftCreationTime);1207}12081209#else /* _WIN32 && !__CYGWIN__ */12101211static int1212set_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)1213{1214struct archive_entry *ae;1215time_t ctime_sec, mtime_sec;1216long ctime_ns, mtime_ns;12171218ae = archive_entry_new();1219if (ae == NULL)1220return (error_nomem(a));1221archive_entry_copy_stat(ae, st);1222ctime_sec = archive_entry_ctime(ae);1223ctime_ns = archive_entry_ctime_nsec(ae);1224mtime_sec = archive_entry_mtime(ae);1225mtime_ns = archive_entry_mtime_nsec(ae);1226archive_entry_free(ae);1227return set_timefilter(a, timetype, mtime_sec, mtime_ns,1228ctime_sec, ctime_ns);1229}12301231static int1232set_timefilter_pathname_mbs(struct archive_match *a, int timetype,1233const char *path)1234{1235struct stat st;12361237if (path == NULL || *path == '\0') {1238archive_set_error(&(a->archive), EINVAL, "pathname is empty");1239return (ARCHIVE_FAILED);1240}1241if (la_stat(path, &st) != 0) {1242archive_set_error(&(a->archive), errno, "Failed to stat()");1243return (ARCHIVE_FAILED);1244}1245return (set_timefilter_stat(a, timetype, &st));1246}12471248static int1249set_timefilter_pathname_wcs(struct archive_match *a, int timetype,1250const wchar_t *path)1251{1252struct archive_string as;1253int r;12541255if (path == NULL || *path == L'\0') {1256archive_set_error(&(a->archive), EINVAL, "pathname is empty");1257return (ARCHIVE_FAILED);1258}12591260/* Convert WCS filename to MBS filename. */1261archive_string_init(&as);1262if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {1263archive_string_free(&as);1264if (errno == ENOMEM)1265return (error_nomem(a));1266archive_set_error(&(a->archive), -1,1267"Failed to convert WCS to MBS");1268return (ARCHIVE_FAILED);1269}12701271r = set_timefilter_pathname_mbs(a, timetype, as.s);1272archive_string_free(&as);12731274return (r);1275}1276#endif /* _WIN32 && !__CYGWIN__ */12771278/*1279* Call back functions for archive_rb.1280*/1281#if !defined(_WIN32) || defined(__CYGWIN__)1282static int1283cmp_node_mbs(const struct archive_rb_node *n1,1284const struct archive_rb_node *n2)1285{1286struct match_file *f1 = (struct match_file *)(uintptr_t)n1;1287struct match_file *f2 = (struct match_file *)(uintptr_t)n2;1288const char *p1, *p2;12891290archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);1291archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);1292if (p1 == NULL)1293return (1);1294if (p2 == NULL)1295return (-1);1296return (strcmp(p1, p2));1297}12981299static int1300cmp_key_mbs(const struct archive_rb_node *n, const void *key)1301{1302struct match_file *f = (struct match_file *)(uintptr_t)n;1303const char *p;13041305archive_mstring_get_mbs(NULL, &(f->pathname), &p);1306if (p == NULL)1307return (-1);1308return (strcmp(p, (const char *)key));1309}1310#else1311static int1312cmp_node_wcs(const struct archive_rb_node *n1,1313const struct archive_rb_node *n2)1314{1315struct match_file *f1 = (struct match_file *)(uintptr_t)n1;1316struct match_file *f2 = (struct match_file *)(uintptr_t)n2;1317const wchar_t *p1, *p2;13181319archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);1320archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);1321if (p1 == NULL)1322return (1);1323if (p2 == NULL)1324return (-1);1325return (wcscmp(p1, p2));1326}13271328static int1329cmp_key_wcs(const struct archive_rb_node *n, const void *key)1330{1331struct match_file *f = (struct match_file *)(uintptr_t)n;1332const wchar_t *p;13331334archive_mstring_get_wcs(NULL, &(f->pathname), &p);1335if (p == NULL)1336return (-1);1337return (wcscmp(p, (const wchar_t *)key));1338}1339#endif13401341static void1342entry_list_init(struct entry_list *list)1343{1344list->first = NULL;1345list->last = &(list->first);1346}13471348static void1349entry_list_free(struct entry_list *list)1350{1351struct match_file *p, *q;13521353for (p = list->first; p != NULL; ) {1354q = p;1355p = p->next;1356archive_mstring_clean(&(q->pathname));1357free(q);1358}1359}13601361static void1362entry_list_add(struct entry_list *list, struct match_file *file)1363{1364*list->last = file;1365list->last = &(file->next);1366}13671368static int1369add_entry(struct archive_match *a, int flag,1370struct archive_entry *entry)1371{1372struct match_file *f;1373const void *pathname;1374int r;13751376f = calloc(1, sizeof(*f));1377if (f == NULL)1378return (error_nomem(a));13791380#if defined(_WIN32) && !defined(__CYGWIN__)1381pathname = archive_entry_pathname_w(entry);1382if (pathname == NULL) {1383free(f);1384archive_set_error(&(a->archive), EINVAL, "pathname is NULL");1385return (ARCHIVE_FAILED);1386}1387archive_mstring_copy_wcs(&(f->pathname), pathname);1388#else1389pathname = archive_entry_pathname(entry);1390if (pathname == NULL) {1391free(f);1392archive_set_error(&(a->archive), EINVAL, "pathname is NULL");1393return (ARCHIVE_FAILED);1394}1395archive_mstring_copy_mbs(&(f->pathname), pathname);1396#endif1397f->flag = flag;1398f->mtime_sec = archive_entry_mtime(entry);1399f->mtime_nsec = archive_entry_mtime_nsec(entry);1400f->ctime_sec = archive_entry_ctime(entry);1401f->ctime_nsec = archive_entry_ctime_nsec(entry);1402r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));1403if (!r) {1404struct match_file *f2;14051406/* Get the duplicated file. */1407f2 = (struct match_file *)__archive_rb_tree_find_node(1408&(a->exclusion_tree), pathname);14091410/*1411* We always overwrite comparison condition.1412* If you do not want to overwrite it, you should not1413* call archive_match_exclude_entry(). We cannot know1414* what behavior you really expect since overwriting1415* condition might be different with the flag.1416*/1417if (f2 != NULL) {1418f2->flag = f->flag;1419f2->mtime_sec = f->mtime_sec;1420f2->mtime_nsec = f->mtime_nsec;1421f2->ctime_sec = f->ctime_sec;1422f2->ctime_nsec = f->ctime_nsec;1423}1424/* Release the duplicated file. */1425archive_mstring_clean(&(f->pathname));1426free(f);1427return (ARCHIVE_OK);1428}1429entry_list_add(&(a->exclusion_entry_list), f);1430a->setflag |= TIME_IS_SET;1431return (ARCHIVE_OK);1432}14331434/*1435* Test if entry is excluded by its timestamp.1436*/1437static int1438time_excluded(struct archive_match *a, struct archive_entry *entry)1439{1440struct match_file *f;1441const void *pathname;1442time_t sec;1443long nsec;14441445/*1446* If this file/dir is excluded by a time comparison, skip it.1447*/1448if (a->newer_ctime_filter) {1449/* If ctime is not set, use mtime instead. */1450if (archive_entry_ctime_is_set(entry))1451sec = archive_entry_ctime(entry);1452else1453sec = archive_entry_mtime(entry);1454if (sec < a->newer_ctime_sec)1455return (1); /* Too old, skip it. */1456if (sec == a->newer_ctime_sec) {1457if (archive_entry_ctime_is_set(entry))1458nsec = archive_entry_ctime_nsec(entry);1459else1460nsec = archive_entry_mtime_nsec(entry);1461if (nsec < a->newer_ctime_nsec)1462return (1); /* Too old, skip it. */1463if (nsec == a->newer_ctime_nsec &&1464(a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)1465== 0)1466return (1); /* Equal, skip it. */1467}1468}1469if (a->older_ctime_filter) {1470/* If ctime is not set, use mtime instead. */1471if (archive_entry_ctime_is_set(entry))1472sec = archive_entry_ctime(entry);1473else1474sec = archive_entry_mtime(entry);1475if (sec > a->older_ctime_sec)1476return (1); /* Too new, skip it. */1477if (sec == a->older_ctime_sec) {1478if (archive_entry_ctime_is_set(entry))1479nsec = archive_entry_ctime_nsec(entry);1480else1481nsec = archive_entry_mtime_nsec(entry);1482if (nsec > a->older_ctime_nsec)1483return (1); /* Too new, skip it. */1484if (nsec == a->older_ctime_nsec &&1485(a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)1486== 0)1487return (1); /* Equal, skip it. */1488}1489}1490if (a->newer_mtime_filter) {1491sec = archive_entry_mtime(entry);1492if (sec < a->newer_mtime_sec)1493return (1); /* Too old, skip it. */1494if (sec == a->newer_mtime_sec) {1495nsec = archive_entry_mtime_nsec(entry);1496if (nsec < a->newer_mtime_nsec)1497return (1); /* Too old, skip it. */1498if (nsec == a->newer_mtime_nsec &&1499(a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)1500== 0)1501return (1); /* Equal, skip it. */1502}1503}1504if (a->older_mtime_filter) {1505sec = archive_entry_mtime(entry);1506if (sec > a->older_mtime_sec)1507return (1); /* Too new, skip it. */1508nsec = archive_entry_mtime_nsec(entry);1509if (sec == a->older_mtime_sec) {1510if (nsec > a->older_mtime_nsec)1511return (1); /* Too new, skip it. */1512if (nsec == a->older_mtime_nsec &&1513(a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)1514== 0)1515return (1); /* Equal, skip it. */1516}1517}15181519/* If there is no exclusion list, include the file. */1520if (a->exclusion_entry_list.first == NULL)1521return (0);15221523#if defined(_WIN32) && !defined(__CYGWIN__)1524pathname = archive_entry_pathname_w(entry);1525#else1526pathname = archive_entry_pathname(entry);1527#endif1528if (pathname == NULL)1529return (0);15301531f = (struct match_file *)__archive_rb_tree_find_node(1532&(a->exclusion_tree), pathname);1533/* If the file wasn't rejected, include it. */1534if (f == NULL)1535return (0);15361537if (f->flag & ARCHIVE_MATCH_CTIME) {1538sec = archive_entry_ctime(entry);1539if (f->ctime_sec > sec) {1540if (f->flag & ARCHIVE_MATCH_OLDER)1541return (1);1542} else if (f->ctime_sec < sec) {1543if (f->flag & ARCHIVE_MATCH_NEWER)1544return (1);1545} else {1546nsec = archive_entry_ctime_nsec(entry);1547if (f->ctime_nsec > nsec) {1548if (f->flag & ARCHIVE_MATCH_OLDER)1549return (1);1550} else if (f->ctime_nsec < nsec) {1551if (f->flag & ARCHIVE_MATCH_NEWER)1552return (1);1553} else if (f->flag & ARCHIVE_MATCH_EQUAL)1554return (1);1555}1556}1557if (f->flag & ARCHIVE_MATCH_MTIME) {1558sec = archive_entry_mtime(entry);1559if (f->mtime_sec > sec) {1560if (f->flag & ARCHIVE_MATCH_OLDER)1561return (1);1562} else if (f->mtime_sec < sec) {1563if (f->flag & ARCHIVE_MATCH_NEWER)1564return (1);1565} else {1566nsec = archive_entry_mtime_nsec(entry);1567if (f->mtime_nsec > nsec) {1568if (f->flag & ARCHIVE_MATCH_OLDER)1569return (1);1570} else if (f->mtime_nsec < nsec) {1571if (f->flag & ARCHIVE_MATCH_NEWER)1572return (1);1573} else if (f->flag & ARCHIVE_MATCH_EQUAL)1574return (1);1575}1576}1577return (0);1578}15791580/*1581* Utility functions to manage inclusion owners1582*/15831584int1585archive_match_include_uid(struct archive *_a, la_int64_t uid)1586{1587struct archive_match *a;15881589archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1590ARCHIVE_STATE_NEW, "archive_match_include_uid");1591a = (struct archive_match *)_a;1592return (add_owner_id(a, &(a->inclusion_uids), uid));1593}15941595int1596archive_match_include_gid(struct archive *_a, la_int64_t gid)1597{1598struct archive_match *a;15991600archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1601ARCHIVE_STATE_NEW, "archive_match_include_gid");1602a = (struct archive_match *)_a;1603return (add_owner_id(a, &(a->inclusion_gids), gid));1604}16051606int1607archive_match_include_uname(struct archive *_a, const char *uname)1608{1609struct archive_match *a;16101611archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1612ARCHIVE_STATE_NEW, "archive_match_include_uname");1613a = (struct archive_match *)_a;1614return (add_owner_name(a, &(a->inclusion_unames), 1, uname));1615}16161617int1618archive_match_include_uname_w(struct archive *_a, const wchar_t *uname)1619{1620struct archive_match *a;16211622archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1623ARCHIVE_STATE_NEW, "archive_match_include_uname_w");1624a = (struct archive_match *)_a;1625return (add_owner_name(a, &(a->inclusion_unames), 0, uname));1626}16271628int1629archive_match_include_gname(struct archive *_a, const char *gname)1630{1631struct archive_match *a;16321633archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1634ARCHIVE_STATE_NEW, "archive_match_include_gname");1635a = (struct archive_match *)_a;1636return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));1637}16381639int1640archive_match_include_gname_w(struct archive *_a, const wchar_t *gname)1641{1642struct archive_match *a;16431644archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1645ARCHIVE_STATE_NEW, "archive_match_include_gname_w");1646a = (struct archive_match *)_a;1647return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));1648}16491650/*1651* Test function for owner(uid, gid, uname, gname).1652*1653* Returns 1 if archive entry is excluded.1654* Returns 0 if archive entry is not excluded.1655* Returns <0 if something error happened.1656*/1657int1658archive_match_owner_excluded(struct archive *_a,1659struct archive_entry *entry)1660{1661struct archive_match *a;16621663archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,1664ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");16651666a = (struct archive_match *)_a;1667if (entry == NULL) {1668archive_set_error(&(a->archive), EINVAL, "entry is NULL");1669return (ARCHIVE_FAILED);1670}16711672/* If we don't have inclusion id set at all, the entry is always1673* not excluded. */1674if ((a->setflag & ID_IS_SET) == 0)1675return (0);1676return (owner_excluded(a, entry));1677}16781679static int1680add_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)1681{1682size_t i;16831684if (ids->count + 1 >= ids->size) {1685void *p;16861687if (ids->size == 0)1688ids->size = 8;1689else1690ids->size *= 2;1691p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);1692if (p == NULL)1693return (error_nomem(a));1694ids->ids = (int64_t *)p;1695}16961697/* Find an insert point. */1698for (i = 0; i < ids->count; i++) {1699if (ids->ids[i] >= id)1700break;1701}17021703/* Add owner id. */1704if (i == ids->count)1705ids->ids[ids->count++] = id;1706else if (ids->ids[i] != id) {1707memmove(&(ids->ids[i+1]), &(ids->ids[i]),1708(ids->count - i) * sizeof(ids->ids[0]));1709ids->ids[i] = id;1710ids->count++;1711}1712a->setflag |= ID_IS_SET;1713return (ARCHIVE_OK);1714}17151716static int1717match_owner_id(struct id_array *ids, int64_t id)1718{1719size_t b, m, t;17201721t = 0;1722b = ids->count;1723while (t < b) {1724m = (t + b)>>1;1725if (ids->ids[m] == id)1726return (1);1727if (ids->ids[m] < id)1728t = m + 1;1729else1730b = m;1731}1732return (0);1733}17341735static int1736add_owner_name(struct archive_match *a, struct match_list *list,1737int mbs, const void *name)1738{1739struct match *match;17401741match = calloc(1, sizeof(*match));1742if (match == NULL)1743return (error_nomem(a));1744if (mbs)1745archive_mstring_copy_mbs(&(match->pattern), name);1746else1747archive_mstring_copy_wcs(&(match->pattern), name);1748match_list_add(list, match);1749a->setflag |= ID_IS_SET;1750return (ARCHIVE_OK);1751}17521753#if !defined(_WIN32) || defined(__CYGWIN__)1754static int1755match_owner_name_mbs(struct archive_match *a, struct match_list *list,1756const char *name)1757{1758struct match *m;1759const char *p;17601761if (name == NULL || *name == '\0')1762return (0);1763for (m = list->first; m; m = m->next) {1764if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)1765< 0 && errno == ENOMEM)1766return (error_nomem(a));1767if (p != NULL && strcmp(p, name) == 0) {1768m->matched = 1;1769return (1);1770}1771}1772return (0);1773}1774#else1775static int1776match_owner_name_wcs(struct archive_match *a, struct match_list *list,1777const wchar_t *name)1778{1779struct match *m;1780const wchar_t *p;17811782if (name == NULL || *name == L'\0')1783return (0);1784for (m = list->first; m; m = m->next) {1785if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)1786< 0 && errno == ENOMEM)1787return (error_nomem(a));1788if (p != NULL && wcscmp(p, name) == 0) {1789m->matched = 1;1790return (1);1791}1792}1793return (0);1794}1795#endif17961797/*1798* Test if entry is excluded by uid, gid, uname or gname.1799*/1800static int1801owner_excluded(struct archive_match *a, struct archive_entry *entry)1802{1803int r;18041805if (a->inclusion_uids.count) {1806if (!match_owner_id(&(a->inclusion_uids),1807archive_entry_uid(entry)))1808return (1);1809}18101811if (a->inclusion_gids.count) {1812if (!match_owner_id(&(a->inclusion_gids),1813archive_entry_gid(entry)))1814return (1);1815}18161817if (a->inclusion_unames.first != NULL) {1818#if defined(_WIN32) && !defined(__CYGWIN__)1819r = match_owner_name_wcs(a, &(a->inclusion_unames),1820archive_entry_uname_w(entry));1821#else1822r = match_owner_name_mbs(a, &(a->inclusion_unames),1823archive_entry_uname(entry));1824#endif1825if (!r)1826return (1);1827else if (r < 0)1828return (r);1829}18301831if (a->inclusion_gnames.first != NULL) {1832#if defined(_WIN32) && !defined(__CYGWIN__)1833r = match_owner_name_wcs(a, &(a->inclusion_gnames),1834archive_entry_gname_w(entry));1835#else1836r = match_owner_name_mbs(a, &(a->inclusion_gnames),1837archive_entry_gname(entry));1838#endif1839if (!r)1840return (1);1841else if (r < 0)1842return (r);1843}1844return (0);1845}184618471848