1238825Smm/*-
2238825Smm * Copyright (c) 2003-2007 Tim Kientzle
3238825Smm * Copyright (c) 2012 Michihiro NAKAJIMA
4238825Smm * All rights reserved.
5238825Smm *
6238825Smm * Redistribution and use in source and binary forms, with or without
7238825Smm * modification, are permitted provided that the following conditions
8238825Smm * are met:
9238825Smm * 1. Redistributions of source code must retain the above copyright
10238825Smm *    notice, this list of conditions and the following disclaimer.
11238825Smm * 2. Redistributions in binary form must reproduce the above copyright
12238825Smm *    notice, this list of conditions and the following disclaimer in the
13238825Smm *    documentation and/or other materials provided with the distribution.
14238825Smm *
15238825Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16238825Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17238825Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18238825Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19238825Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20238825Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21238825Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22238825Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23238825Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24238825Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25238825Smm */
26238825Smm
27238825Smm#include "archive_platform.h"
28238825Smm__FBSDID("$FreeBSD$");
29238825Smm
30238825Smm#ifdef HAVE_ERRNO_H
31238825Smm#include <errno.h>
32238825Smm#endif
33238825Smm#ifdef HAVE_STDLIB_H
34238825Smm#include <stdlib.h>
35238825Smm#endif
36238825Smm#ifdef HAVE_STRING_H
37238825Smm#include <string.h>
38238825Smm#endif
39238825Smm
40238825Smm#include "archive.h"
41238825Smm#include "archive_private.h"
42238825Smm#include "archive_entry.h"
43299529Smm#include "archive_getdate.h"
44238825Smm#include "archive_pathmatch.h"
45238825Smm#include "archive_rb.h"
46238825Smm#include "archive_string.h"
47238825Smm
48238825Smmstruct match {
49238825Smm	struct match		*next;
50238825Smm	int			 matches;
51238825Smm	struct archive_mstring	 pattern;
52238825Smm};
53238825Smm
54238825Smmstruct match_list {
55238825Smm	struct match		*first;
56238825Smm	struct match		**last;
57238825Smm	int			 count;
58238825Smm	int			 unmatched_count;
59238825Smm	struct match		*unmatched_next;
60238825Smm	int			 unmatched_eof;
61238825Smm};
62238825Smm
63238825Smmstruct match_file {
64238825Smm	struct archive_rb_node	 node;
65238825Smm	struct match_file	*next;
66238825Smm	struct archive_mstring	 pathname;
67238825Smm	int			 flag;
68238825Smm	time_t			 mtime_sec;
69238825Smm	long			 mtime_nsec;
70238825Smm	time_t			 ctime_sec;
71238825Smm	long			 ctime_nsec;
72238825Smm};
73238825Smm
74238825Smmstruct entry_list {
75238825Smm	struct match_file	*first;
76238825Smm	struct match_file	**last;
77238825Smm	int			 count;
78238825Smm};
79238825Smm
80238825Smmstruct id_array {
81238825Smm	size_t			 size;/* Allocated size */
82238825Smm	size_t			 count;
83238825Smm	int64_t			*ids;
84238825Smm};
85238825Smm
86238825Smm#define PATTERN_IS_SET		1
87238825Smm#define TIME_IS_SET		2
88238825Smm#define ID_IS_SET		4
89238825Smm
90238825Smmstruct archive_match {
91238825Smm	struct archive		 archive;
92238825Smm
93238825Smm	/* exclusion/inclusion set flag. */
94238825Smm	int			 setflag;
95238825Smm
96348607Smm	/* Recursively include directory content? */
97348607Smm	int			 recursive_include;
98348607Smm
99238825Smm	/*
100238825Smm	 * Matching filename patterns.
101238825Smm	 */
102238825Smm	struct match_list	 exclusions;
103238825Smm	struct match_list	 inclusions;
104238825Smm
105238825Smm	/*
106238825Smm	 * Matching time stamps.
107238825Smm	 */
108238825Smm	time_t			 now;
109238825Smm	int			 newer_mtime_filter;
110238825Smm	time_t			 newer_mtime_sec;
111238825Smm	long			 newer_mtime_nsec;
112238825Smm	int			 newer_ctime_filter;
113238825Smm	time_t			 newer_ctime_sec;
114238825Smm	long			 newer_ctime_nsec;
115238825Smm	int			 older_mtime_filter;
116238825Smm	time_t			 older_mtime_sec;
117238825Smm	long			 older_mtime_nsec;
118238825Smm	int			 older_ctime_filter;
119238825Smm	time_t			 older_ctime_sec;
120238825Smm	long			 older_ctime_nsec;
121238825Smm	/*
122238825Smm	 * Matching time stamps with its filename.
123238825Smm	 */
124238825Smm	struct archive_rb_tree	 exclusion_tree;
125238825Smm	struct entry_list 	 exclusion_entry_list;
126238825Smm
127238825Smm	/*
128238825Smm	 * Matching file owners.
129238825Smm	 */
130238825Smm	struct id_array 	 inclusion_uids;
131238825Smm	struct id_array 	 inclusion_gids;
132238825Smm	struct match_list	 inclusion_unames;
133238825Smm	struct match_list	 inclusion_gnames;
134238825Smm};
135238825Smm
136238825Smmstatic int	add_pattern_from_file(struct archive_match *,
137238825Smm		    struct match_list *, int, const void *, int);
138238825Smmstatic int	add_entry(struct archive_match *, int,
139238825Smm		    struct archive_entry *);
140238825Smmstatic int	add_owner_id(struct archive_match *, struct id_array *,
141238825Smm		    int64_t);
142238825Smmstatic int	add_owner_name(struct archive_match *, struct match_list *,
143238825Smm		    int, const void *);
144238825Smmstatic int	add_pattern_mbs(struct archive_match *, struct match_list *,
145238825Smm		    const char *);
146238825Smmstatic int	add_pattern_wcs(struct archive_match *, struct match_list *,
147238825Smm		    const wchar_t *);
148238825Smmstatic int	cmp_key_mbs(const struct archive_rb_node *, const void *);
149238825Smmstatic int	cmp_key_wcs(const struct archive_rb_node *, const void *);
150238825Smmstatic int	cmp_node_mbs(const struct archive_rb_node *,
151238825Smm		    const struct archive_rb_node *);
152238825Smmstatic int	cmp_node_wcs(const struct archive_rb_node *,
153238825Smm		    const struct archive_rb_node *);
154238825Smmstatic void	entry_list_add(struct entry_list *, struct match_file *);
155238825Smmstatic void	entry_list_free(struct entry_list *);
156238825Smmstatic void	entry_list_init(struct entry_list *);
157238825Smmstatic int	error_nomem(struct archive_match *);
158238825Smmstatic void	match_list_add(struct match_list *, struct match *);
159238825Smmstatic void	match_list_free(struct match_list *);
160238825Smmstatic void	match_list_init(struct match_list *);
161238825Smmstatic int	match_list_unmatched_inclusions_next(struct archive_match *,
162238825Smm		    struct match_list *, int, const void **);
163238825Smmstatic int	match_owner_id(struct id_array *, int64_t);
164238825Smm#if !defined(_WIN32) || defined(__CYGWIN__)
165238825Smmstatic int	match_owner_name_mbs(struct archive_match *,
166238825Smm		    struct match_list *, const char *);
167238825Smm#else
168238825Smmstatic int	match_owner_name_wcs(struct archive_match *,
169238825Smm		    struct match_list *, const wchar_t *);
170238825Smm#endif
171238825Smmstatic int	match_path_exclusion(struct archive_match *,
172238825Smm		    struct match *, int, const void *);
173238825Smmstatic int	match_path_inclusion(struct archive_match *,
174238825Smm		    struct match *, int, const void *);
175238825Smmstatic int	owner_excluded(struct archive_match *,
176238825Smm		    struct archive_entry *);
177238825Smmstatic int	path_excluded(struct archive_match *, int, const void *);
178238825Smmstatic int	set_timefilter(struct archive_match *, int, time_t, long,
179238825Smm		    time_t, long);
180238825Smmstatic int	set_timefilter_pathname_mbs(struct archive_match *,
181238825Smm		    int, const char *);
182238825Smmstatic int	set_timefilter_pathname_wcs(struct archive_match *,
183238825Smm		    int, const wchar_t *);
184238825Smmstatic int	set_timefilter_date(struct archive_match *, int, const char *);
185238825Smmstatic int	set_timefilter_date_w(struct archive_match *, int,
186238825Smm		    const wchar_t *);
187238825Smmstatic int	time_excluded(struct archive_match *,
188238825Smm		    struct archive_entry *);
189238825Smmstatic int	validate_time_flag(struct archive *, int, const char *);
190238825Smm
191238825Smm#define get_date __archive_get_date
192238825Smm
193238825Smmstatic const struct archive_rb_tree_ops rb_ops_mbs = {
194238825Smm	cmp_node_mbs, cmp_key_mbs
195238825Smm};
196238825Smm
197238825Smmstatic const struct archive_rb_tree_ops rb_ops_wcs = {
198238825Smm	cmp_node_wcs, cmp_key_wcs
199238825Smm};
200238825Smm
201238825Smm/*
202238825Smm * The matching logic here needs to be re-thought.  I started out to
203238825Smm * try to mimic gtar's matching logic, but it's not entirely
204238825Smm * consistent.  In particular 'tar -t' and 'tar -x' interpret patterns
205238825Smm * on the command line as anchored, but --exclude doesn't.
206238825Smm */
207238825Smm
208238825Smmstatic int
209238825Smmerror_nomem(struct archive_match *a)
210238825Smm{
211238825Smm	archive_set_error(&(a->archive), ENOMEM, "No memory");
212238825Smm	a->archive.state = ARCHIVE_STATE_FATAL;
213238825Smm	return (ARCHIVE_FATAL);
214238825Smm}
215238825Smm
216238825Smm/*
217238825Smm * Create an ARCHIVE_MATCH object.
218238825Smm */
219238825Smmstruct archive *
220238825Smmarchive_match_new(void)
221238825Smm{
222238825Smm	struct archive_match *a;
223238825Smm
224238825Smm	a = (struct archive_match *)calloc(1, sizeof(*a));
225238825Smm	if (a == NULL)
226238825Smm		return (NULL);
227238825Smm	a->archive.magic = ARCHIVE_MATCH_MAGIC;
228238825Smm	a->archive.state = ARCHIVE_STATE_NEW;
229348607Smm	a->recursive_include = 1;
230238825Smm	match_list_init(&(a->inclusions));
231238825Smm	match_list_init(&(a->exclusions));
232238825Smm	__archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs);
233238825Smm	entry_list_init(&(a->exclusion_entry_list));
234238825Smm	match_list_init(&(a->inclusion_unames));
235238825Smm	match_list_init(&(a->inclusion_gnames));
236238825Smm	time(&a->now);
237238825Smm	return (&(a->archive));
238238825Smm}
239238825Smm
240238825Smm/*
241238825Smm * Free an ARCHIVE_MATCH object.
242238825Smm */
243238825Smmint
244238825Smmarchive_match_free(struct archive *_a)
245238825Smm{
246238825Smm	struct archive_match *a;
247238825Smm
248238825Smm	if (_a == NULL)
249238825Smm		return (ARCHIVE_OK);
250238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
251238825Smm	    ARCHIVE_STATE_ANY | ARCHIVE_STATE_FATAL, "archive_match_free");
252238825Smm	a = (struct archive_match *)_a;
253238825Smm	match_list_free(&(a->inclusions));
254238825Smm	match_list_free(&(a->exclusions));
255238825Smm	entry_list_free(&(a->exclusion_entry_list));
256238825Smm	free(a->inclusion_uids.ids);
257238825Smm	free(a->inclusion_gids.ids);
258238825Smm	match_list_free(&(a->inclusion_unames));
259238825Smm	match_list_free(&(a->inclusion_gnames));
260238825Smm	free(a);
261238825Smm	return (ARCHIVE_OK);
262238825Smm}
263238825Smm
264238825Smm/*
265238825Smm * Convenience function to perform all exclusion tests.
266238825Smm *
267238825Smm * Returns 1 if archive entry is excluded.
268238825Smm * Returns 0 if archive entry is not excluded.
269238825Smm * Returns <0 if something error happened.
270238825Smm */
271238825Smmint
272238825Smmarchive_match_excluded(struct archive *_a, struct archive_entry *entry)
273238825Smm{
274238825Smm	struct archive_match *a;
275238825Smm	int r;
276238825Smm
277238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
278238825Smm	    ARCHIVE_STATE_NEW, "archive_match_excluded_ae");
279238825Smm
280238825Smm	a = (struct archive_match *)_a;
281238825Smm	if (entry == NULL) {
282238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
283238825Smm		return (ARCHIVE_FAILED);
284238825Smm	}
285238825Smm
286238825Smm	r = 0;
287238825Smm	if (a->setflag & PATTERN_IS_SET) {
288238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
289238825Smm		r = path_excluded(a, 0, archive_entry_pathname_w(entry));
290238825Smm#else
291238825Smm		r = path_excluded(a, 1, archive_entry_pathname(entry));
292238825Smm#endif
293238825Smm		if (r != 0)
294238825Smm			return (r);
295238825Smm	}
296238825Smm
297238825Smm	if (a->setflag & TIME_IS_SET) {
298238825Smm		r = time_excluded(a, entry);
299238825Smm		if (r != 0)
300238825Smm			return (r);
301238825Smm	}
302238825Smm
303238825Smm	if (a->setflag & ID_IS_SET)
304238825Smm		r = owner_excluded(a, entry);
305238825Smm	return (r);
306238825Smm}
307238825Smm
308238825Smm/*
309238825Smm * Utility functions to manage exclusion/inclusion patterns
310238825Smm */
311238825Smm
312238825Smmint
313238825Smmarchive_match_exclude_pattern(struct archive *_a, const char *pattern)
314238825Smm{
315238825Smm	struct archive_match *a;
316238825Smm	int r;
317238825Smm
318238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
319238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern");
320238825Smm	a = (struct archive_match *)_a;
321238825Smm
322238825Smm	if (pattern == NULL || *pattern == '\0') {
323238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
324238825Smm		return (ARCHIVE_FAILED);
325238825Smm	}
326238825Smm	if ((r = add_pattern_mbs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
327238825Smm		return (r);
328238825Smm	return (ARCHIVE_OK);
329238825Smm}
330238825Smm
331238825Smmint
332238825Smmarchive_match_exclude_pattern_w(struct archive *_a, const wchar_t *pattern)
333238825Smm{
334238825Smm	struct archive_match *a;
335238825Smm	int r;
336238825Smm
337238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
338238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_w");
339238825Smm	a = (struct archive_match *)_a;
340238825Smm
341238825Smm	if (pattern == NULL || *pattern == L'\0') {
342238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
343238825Smm		return (ARCHIVE_FAILED);
344238825Smm	}
345238825Smm	if ((r = add_pattern_wcs(a, &(a->exclusions), pattern)) != ARCHIVE_OK)
346238825Smm		return (r);
347238825Smm	return (ARCHIVE_OK);
348238825Smm}
349238825Smm
350238825Smmint
351238825Smmarchive_match_exclude_pattern_from_file(struct archive *_a,
352238825Smm    const char *pathname, int nullSeparator)
353238825Smm{
354238825Smm	struct archive_match *a;
355238825Smm
356238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
357238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file");
358238825Smm	a = (struct archive_match *)_a;
359238825Smm
360238825Smm	return add_pattern_from_file(a, &(a->exclusions), 1, pathname,
361238825Smm		nullSeparator);
362238825Smm}
363238825Smm
364238825Smmint
365238825Smmarchive_match_exclude_pattern_from_file_w(struct archive *_a,
366238825Smm    const wchar_t *pathname, int nullSeparator)
367238825Smm{
368238825Smm	struct archive_match *a;
369238825Smm
370238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
371238825Smm	    ARCHIVE_STATE_NEW, "archive_match_exclude_pattern_from_file_w");
372238825Smm	a = (struct archive_match *)_a;
373238825Smm
374238825Smm	return add_pattern_from_file(a, &(a->exclusions), 0, pathname,
375238825Smm		nullSeparator);
376238825Smm}
377238825Smm
378238825Smmint
379238825Smmarchive_match_include_pattern(struct archive *_a, const char *pattern)
380238825Smm{
381238825Smm	struct archive_match *a;
382238825Smm	int r;
383238825Smm
384238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
385238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern");
386238825Smm	a = (struct archive_match *)_a;
387238825Smm
388238825Smm	if (pattern == NULL || *pattern == '\0') {
389238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
390238825Smm		return (ARCHIVE_FAILED);
391238825Smm	}
392238825Smm	if ((r = add_pattern_mbs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
393238825Smm		return (r);
394238825Smm	return (ARCHIVE_OK);
395238825Smm}
396238825Smm
397238825Smmint
398238825Smmarchive_match_include_pattern_w(struct archive *_a, const wchar_t *pattern)
399238825Smm{
400238825Smm	struct archive_match *a;
401238825Smm	int r;
402238825Smm
403238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
404238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_w");
405238825Smm	a = (struct archive_match *)_a;
406238825Smm
407238825Smm	if (pattern == NULL || *pattern == L'\0') {
408238825Smm		archive_set_error(&(a->archive), EINVAL, "pattern is empty");
409238825Smm		return (ARCHIVE_FAILED);
410238825Smm	}
411238825Smm	if ((r = add_pattern_wcs(a, &(a->inclusions), pattern)) != ARCHIVE_OK)
412238825Smm		return (r);
413238825Smm	return (ARCHIVE_OK);
414238825Smm}
415238825Smm
416238825Smmint
417238825Smmarchive_match_include_pattern_from_file(struct archive *_a,
418238825Smm    const char *pathname, int nullSeparator)
419238825Smm{
420238825Smm	struct archive_match *a;
421238825Smm
422238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
423238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file");
424238825Smm	a = (struct archive_match *)_a;
425238825Smm
426238825Smm	return add_pattern_from_file(a, &(a->inclusions), 1, pathname,
427238825Smm		nullSeparator);
428238825Smm}
429238825Smm
430238825Smmint
431238825Smmarchive_match_include_pattern_from_file_w(struct archive *_a,
432238825Smm    const wchar_t *pathname, int nullSeparator)
433238825Smm{
434238825Smm	struct archive_match *a;
435238825Smm
436238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
437238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_pattern_from_file_w");
438238825Smm	a = (struct archive_match *)_a;
439238825Smm
440238825Smm	return add_pattern_from_file(a, &(a->inclusions), 0, pathname,
441238825Smm		nullSeparator);
442238825Smm}
443238825Smm
444238825Smm/*
445238825Smm * Test functions for pathname patterns.
446238825Smm *
447238825Smm * Returns 1 if archive entry is excluded.
448238825Smm * Returns 0 if archive entry is not excluded.
449238825Smm * Returns <0 if something error happened.
450238825Smm */
451238825Smmint
452238825Smmarchive_match_path_excluded(struct archive *_a,
453238825Smm    struct archive_entry *entry)
454238825Smm{
455238825Smm	struct archive_match *a;
456238825Smm
457238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
458238825Smm	    ARCHIVE_STATE_NEW, "archive_match_path_excluded");
459238825Smm
460238825Smm	a = (struct archive_match *)_a;
461238825Smm	if (entry == NULL) {
462238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
463238825Smm		return (ARCHIVE_FAILED);
464238825Smm	}
465238825Smm
466238825Smm	/* If we don't have exclusion/inclusion pattern set at all,
467238825Smm	 * the entry is always not excluded. */
468238825Smm	if ((a->setflag & PATTERN_IS_SET) == 0)
469238825Smm		return (0);
470238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
471238825Smm	return (path_excluded(a, 0, archive_entry_pathname_w(entry)));
472238825Smm#else
473238825Smm	return (path_excluded(a, 1, archive_entry_pathname(entry)));
474238825Smm#endif
475238825Smm}
476238825Smm
477238825Smm/*
478348607Smm * When recursive inclusion of directory content is enabled,
479348607Smm * an inclusion pattern that matches a directory will also
480348607Smm * include everything beneath that directory. Enabled by default.
481348607Smm *
482348607Smm * For compatibility with GNU tar, exclusion patterns always
483348607Smm * match if a subset of the full patch matches (i.e., they are
484348607Smm * are not rooted at the beginning of the path) and thus there
485348607Smm * is no corresponding non-recursive exclusion mode.
486348607Smm */
487348607Smmint
488348607Smmarchive_match_set_inclusion_recursion(struct archive *_a, int enabled)
489348607Smm{
490348607Smm	struct archive_match *a;
491348607Smm
492348607Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
493348607Smm	    ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion");
494348607Smm	a = (struct archive_match *)_a;
495348607Smm	a->recursive_include = enabled;
496348607Smm	return (ARCHIVE_OK);
497348607Smm}
498348607Smm
499348607Smm/*
500313570Smm * Utility functions to get statistic information for inclusion patterns.
501238825Smm */
502238825Smmint
503238825Smmarchive_match_path_unmatched_inclusions(struct archive *_a)
504238825Smm{
505238825Smm	struct archive_match *a;
506238825Smm
507238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
508238825Smm	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions");
509238825Smm	a = (struct archive_match *)_a;
510238825Smm
511238825Smm	return (a->inclusions.unmatched_count);
512238825Smm}
513238825Smm
514238825Smmint
515238825Smmarchive_match_path_unmatched_inclusions_next(struct archive *_a,
516238825Smm    const char **_p)
517238825Smm{
518238825Smm	struct archive_match *a;
519238825Smm	const void *v;
520238825Smm	int r;
521238825Smm
522238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
523238825Smm	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next");
524238825Smm	a = (struct archive_match *)_a;
525238825Smm
526238825Smm	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 1, &v);
527238825Smm	*_p = (const char *)v;
528238825Smm	return (r);
529238825Smm}
530238825Smm
531238825Smmint
532238825Smmarchive_match_path_unmatched_inclusions_next_w(struct archive *_a,
533238825Smm    const wchar_t **_p)
534238825Smm{
535238825Smm	struct archive_match *a;
536238825Smm	const void *v;
537238825Smm	int r;
538238825Smm
539238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
540238825Smm	    ARCHIVE_STATE_NEW, "archive_match_unmatched_inclusions_next_w");
541238825Smm	a = (struct archive_match *)_a;
542238825Smm
543238825Smm	r = match_list_unmatched_inclusions_next(a, &(a->inclusions), 0, &v);
544238825Smm	*_p = (const wchar_t *)v;
545238825Smm	return (r);
546238825Smm}
547238825Smm
548238825Smm/*
549238825Smm * Add inclusion/exclusion patterns.
550238825Smm */
551238825Smmstatic int
552238825Smmadd_pattern_mbs(struct archive_match *a, struct match_list *list,
553238825Smm    const char *pattern)
554238825Smm{
555238825Smm	struct match *match;
556238825Smm	size_t len;
557238825Smm
558238825Smm	match = calloc(1, sizeof(*match));
559238825Smm	if (match == NULL)
560238825Smm		return (error_nomem(a));
561238825Smm	/* Both "foo/" and "foo" should match "foo/bar". */
562238825Smm	len = strlen(pattern);
563238825Smm	if (len && pattern[len - 1] == '/')
564238825Smm		--len;
565238825Smm	archive_mstring_copy_mbs_len(&(match->pattern), pattern, len);
566238825Smm	match_list_add(list, match);
567238825Smm	a->setflag |= PATTERN_IS_SET;
568238825Smm	return (ARCHIVE_OK);
569238825Smm}
570238825Smm
571238825Smmstatic int
572238825Smmadd_pattern_wcs(struct archive_match *a, struct match_list *list,
573238825Smm    const wchar_t *pattern)
574238825Smm{
575238825Smm	struct match *match;
576238825Smm	size_t len;
577238825Smm
578238825Smm	match = calloc(1, sizeof(*match));
579238825Smm	if (match == NULL)
580238825Smm		return (error_nomem(a));
581238825Smm	/* Both "foo/" and "foo" should match "foo/bar". */
582238825Smm	len = wcslen(pattern);
583238825Smm	if (len && pattern[len - 1] == L'/')
584238825Smm		--len;
585238825Smm	archive_mstring_copy_wcs_len(&(match->pattern), pattern, len);
586238825Smm	match_list_add(list, match);
587238825Smm	a->setflag |= PATTERN_IS_SET;
588238825Smm	return (ARCHIVE_OK);
589238825Smm}
590238825Smm
591238825Smmstatic int
592238825Smmadd_pattern_from_file(struct archive_match *a, struct match_list *mlist,
593238825Smm    int mbs, const void *pathname, int nullSeparator)
594238825Smm{
595238825Smm	struct archive *ar;
596238825Smm	struct archive_entry *ae;
597238825Smm	struct archive_string as;
598238825Smm	const void *buff;
599238825Smm	size_t size;
600238825Smm	int64_t offset;
601238825Smm	int r;
602238825Smm
603238825Smm	ar = archive_read_new();
604238825Smm	if (ar == NULL) {
605238825Smm		archive_set_error(&(a->archive), ENOMEM, "No memory");
606238825Smm		return (ARCHIVE_FATAL);
607238825Smm	}
608238825Smm	r = archive_read_support_format_raw(ar);
609299529Smm	r = archive_read_support_format_empty(ar);
610238825Smm	if (r != ARCHIVE_OK) {
611238825Smm		archive_copy_error(&(a->archive), ar);
612238825Smm		archive_read_free(ar);
613238825Smm		return (r);
614238825Smm	}
615238825Smm	if (mbs)
616238825Smm		r = archive_read_open_filename(ar, pathname, 512*20);
617238825Smm	else
618238825Smm		r = archive_read_open_filename_w(ar, pathname, 512*20);
619238825Smm	if (r != ARCHIVE_OK) {
620238825Smm		archive_copy_error(&(a->archive), ar);
621238825Smm		archive_read_free(ar);
622238825Smm		return (r);
623238825Smm	}
624238825Smm	r = archive_read_next_header(ar, &ae);
625238825Smm	if (r != ARCHIVE_OK) {
626238825Smm		archive_read_free(ar);
627299529Smm		if (r == ARCHIVE_EOF) {
628299529Smm			return (ARCHIVE_OK);
629299529Smm		} else {
630299529Smm			archive_copy_error(&(a->archive), ar);
631299529Smm			return (r);
632299529Smm		}
633238825Smm	}
634238825Smm
635238825Smm	archive_string_init(&as);
636238825Smm
637238825Smm	while ((r = archive_read_data_block(ar, &buff, &size, &offset))
638238825Smm	    == ARCHIVE_OK) {
639238825Smm		const char *b = (const char *)buff;
640238825Smm
641238825Smm		while (size) {
642238825Smm			const char *s = (const char *)b;
643238825Smm			size_t length = 0;
644238825Smm			int found_separator = 0;
645238825Smm
646238825Smm			while (length < size) {
647238825Smm				if (nullSeparator) {
648238825Smm					if (*b == '\0') {
649238825Smm						found_separator = 1;
650238825Smm						break;
651238825Smm					}
652238825Smm				} else {
653238825Smm			            	if (*b == 0x0d || *b == 0x0a) {
654238825Smm						found_separator = 1;
655238825Smm						break;
656238825Smm					}
657238825Smm				}
658238825Smm				b++;
659238825Smm				length++;
660238825Smm			}
661238825Smm			if (!found_separator) {
662238825Smm				archive_strncat(&as, s, length);
663238825Smm				/* Read next data block. */
664238825Smm				break;
665238825Smm			}
666238825Smm			b++;
667238825Smm			size -= length + 1;
668238825Smm			archive_strncat(&as, s, length);
669238825Smm
670238825Smm			/* If the line is not empty, add the pattern. */
671238825Smm			if (archive_strlen(&as) > 0) {
672238825Smm				/* Add pattern. */
673238825Smm				r = add_pattern_mbs(a, mlist, as.s);
674238825Smm				if (r != ARCHIVE_OK) {
675238825Smm					archive_read_free(ar);
676238825Smm					archive_string_free(&as);
677238825Smm					return (r);
678238825Smm				}
679238825Smm				archive_string_empty(&as);
680238825Smm			}
681238825Smm		}
682238825Smm	}
683238825Smm
684305188Smm	/* If an error occurred, report it immediately. */
685238825Smm	if (r < ARCHIVE_OK) {
686238825Smm		archive_copy_error(&(a->archive), ar);
687238825Smm		archive_read_free(ar);
688238825Smm		archive_string_free(&as);
689238825Smm		return (r);
690238825Smm	}
691238825Smm
692238825Smm	/* If the line is not empty, add the pattern. */
693238825Smm	if (r == ARCHIVE_EOF && archive_strlen(&as) > 0) {
694238825Smm		/* Add pattern. */
695238825Smm		r = add_pattern_mbs(a, mlist, as.s);
696238825Smm		if (r != ARCHIVE_OK) {
697238825Smm			archive_read_free(ar);
698238825Smm			archive_string_free(&as);
699238825Smm			return (r);
700238825Smm		}
701238825Smm	}
702238825Smm	archive_read_free(ar);
703238825Smm	archive_string_free(&as);
704238825Smm	return (ARCHIVE_OK);
705238825Smm}
706238825Smm
707238825Smm/*
708238825Smm * Test if pathname is excluded by inclusion/exclusion patterns.
709238825Smm */
710238825Smmstatic int
711238825Smmpath_excluded(struct archive_match *a, int mbs, const void *pathname)
712238825Smm{
713238825Smm	struct match *match;
714238825Smm	struct match *matched;
715238825Smm	int r;
716238825Smm
717238825Smm	if (a == NULL)
718238825Smm		return (0);
719238825Smm
720238825Smm	/* Mark off any unmatched inclusions. */
721238825Smm	/* In particular, if a filename does appear in the archive and
722238825Smm	 * is explicitly included and excluded, then we don't report
723238825Smm	 * it as missing even though we don't extract it.
724238825Smm	 */
725238825Smm	matched = NULL;
726238825Smm	for (match = a->inclusions.first; match != NULL;
727238825Smm	    match = match->next){
728238825Smm		if (match->matches == 0 &&
729238825Smm		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
730238825Smm			if (r < 0)
731238825Smm				return (r);
732238825Smm			a->inclusions.unmatched_count--;
733238825Smm			match->matches++;
734238825Smm			matched = match;
735238825Smm		}
736238825Smm	}
737238825Smm
738238825Smm	/* Exclusions take priority */
739238825Smm	for (match = a->exclusions.first; match != NULL;
740238825Smm	    match = match->next){
741238825Smm		r = match_path_exclusion(a, match, mbs, pathname);
742238825Smm		if (r)
743238825Smm			return (r);
744238825Smm	}
745238825Smm
746238825Smm	/* It's not excluded and we found an inclusion above, so it's
747238825Smm	 * included. */
748238825Smm	if (matched != NULL)
749238825Smm		return (0);
750238825Smm
751238825Smm
752238825Smm	/* We didn't find an unmatched inclusion, check the remaining ones. */
753238825Smm	for (match = a->inclusions.first; match != NULL;
754238825Smm	    match = match->next){
755238825Smm		/* We looked at previously-unmatched inclusions already. */
756238825Smm		if (match->matches > 0 &&
757238825Smm		    (r = match_path_inclusion(a, match, mbs, pathname)) != 0) {
758238825Smm			if (r < 0)
759238825Smm				return (r);
760238825Smm			match->matches++;
761238825Smm			return (0);
762238825Smm		}
763238825Smm	}
764238825Smm
765238825Smm	/* If there were inclusions, default is to exclude. */
766238825Smm	if (a->inclusions.first != NULL)
767238825Smm	    return (1);
768238825Smm
769238825Smm	/* No explicit inclusions, default is to match. */
770238825Smm	return (0);
771238825Smm}
772238825Smm
773238825Smm/*
774238825Smm * This is a little odd, but it matches the default behavior of
775238825Smm * gtar.  In particular, 'a*b' will match 'foo/a1111/222b/bar'
776238825Smm *
777238825Smm */
778238825Smmstatic int
779238825Smmmatch_path_exclusion(struct archive_match *a, struct match *m,
780238825Smm    int mbs, const void *pn)
781238825Smm{
782238825Smm	int flag = PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END;
783238825Smm	int r;
784238825Smm
785238825Smm	if (mbs) {
786238825Smm		const char *p;
787238825Smm		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
788238825Smm		if (r == 0)
789238825Smm			return (archive_pathmatch(p, (const char *)pn, flag));
790238825Smm	} else {
791238825Smm		const wchar_t *p;
792238825Smm		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
793238825Smm		if (r == 0)
794238825Smm			return (archive_pathmatch_w(p, (const wchar_t *)pn,
795238825Smm				flag));
796238825Smm	}
797238825Smm	if (errno == ENOMEM)
798238825Smm		return (error_nomem(a));
799238825Smm	return (0);
800238825Smm}
801238825Smm
802238825Smm/*
803238825Smm * Again, mimic gtar:  inclusions are always anchored (have to match
804238825Smm * the beginning of the path) even though exclusions are not anchored.
805238825Smm */
806238825Smmstatic int
807238825Smmmatch_path_inclusion(struct archive_match *a, struct match *m,
808238825Smm    int mbs, const void *pn)
809238825Smm{
810348607Smm	/* Recursive operation requires only a prefix match. */
811348607Smm	int flag = a->recursive_include ?
812348607Smm		PATHMATCH_NO_ANCHOR_END :
813348607Smm		0;
814238825Smm	int r;
815238825Smm
816238825Smm	if (mbs) {
817238825Smm		const char *p;
818238825Smm		r = archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p);
819238825Smm		if (r == 0)
820238825Smm			return (archive_pathmatch(p, (const char *)pn, flag));
821238825Smm	} else {
822238825Smm		const wchar_t *p;
823238825Smm		r = archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p);
824238825Smm		if (r == 0)
825238825Smm			return (archive_pathmatch_w(p, (const wchar_t *)pn,
826238825Smm				flag));
827238825Smm	}
828238825Smm	if (errno == ENOMEM)
829238825Smm		return (error_nomem(a));
830238825Smm	return (0);
831238825Smm}
832238825Smm
833238825Smmstatic void
834238825Smmmatch_list_init(struct match_list *list)
835238825Smm{
836238825Smm	list->first = NULL;
837238825Smm	list->last = &(list->first);
838238825Smm	list->count = 0;
839238825Smm}
840238825Smm
841238825Smmstatic void
842238825Smmmatch_list_free(struct match_list *list)
843238825Smm{
844238825Smm	struct match *p, *q;
845238825Smm
846238825Smm	for (p = list->first; p != NULL; ) {
847238825Smm		q = p;
848238825Smm		p = p->next;
849238825Smm		archive_mstring_clean(&(q->pattern));
850238825Smm		free(q);
851238825Smm	}
852238825Smm}
853238825Smm
854238825Smmstatic void
855238825Smmmatch_list_add(struct match_list *list, struct match *m)
856238825Smm{
857238825Smm	*list->last = m;
858238825Smm	list->last = &(m->next);
859238825Smm	list->count++;
860238825Smm	list->unmatched_count++;
861238825Smm}
862238825Smm
863238825Smmstatic int
864238825Smmmatch_list_unmatched_inclusions_next(struct archive_match *a,
865238825Smm    struct match_list *list, int mbs, const void **vp)
866238825Smm{
867238825Smm	struct match *m;
868238825Smm
869238825Smm	*vp = NULL;
870238825Smm	if (list->unmatched_eof) {
871238825Smm		list->unmatched_eof = 0;
872238825Smm		return (ARCHIVE_EOF);
873238825Smm	}
874238825Smm	if (list->unmatched_next == NULL) {
875238825Smm		if (list->unmatched_count == 0)
876238825Smm			return (ARCHIVE_EOF);
877238825Smm		list->unmatched_next = list->first;
878238825Smm	}
879238825Smm
880238825Smm	for (m = list->unmatched_next; m != NULL; m = m->next) {
881238825Smm		int r;
882238825Smm
883238825Smm		if (m->matches)
884238825Smm			continue;
885238825Smm		if (mbs) {
886238825Smm			const char *p;
887238825Smm			r = archive_mstring_get_mbs(&(a->archive),
888238825Smm				&(m->pattern), &p);
889238825Smm			if (r < 0 && errno == ENOMEM)
890238825Smm				return (error_nomem(a));
891238825Smm			if (p == NULL)
892238825Smm				p = "";
893238825Smm			*vp = p;
894238825Smm		} else {
895238825Smm			const wchar_t *p;
896238825Smm			r = archive_mstring_get_wcs(&(a->archive),
897238825Smm				&(m->pattern), &p);
898238825Smm			if (r < 0 && errno == ENOMEM)
899238825Smm				return (error_nomem(a));
900238825Smm			if (p == NULL)
901238825Smm				p = L"";
902238825Smm			*vp = p;
903238825Smm		}
904238825Smm		list->unmatched_next = m->next;
905238825Smm		if (list->unmatched_next == NULL)
906238825Smm			/* To return EOF next time. */
907238825Smm			list->unmatched_eof = 1;
908238825Smm		return (ARCHIVE_OK);
909238825Smm	}
910238825Smm	list->unmatched_next = NULL;
911238825Smm	return (ARCHIVE_EOF);
912238825Smm}
913238825Smm
914238825Smm/*
915238825Smm * Utility functions to manage inclusion timestamps.
916238825Smm */
917238825Smmint
918238825Smmarchive_match_include_time(struct archive *_a, int flag, time_t sec,
919238825Smm    long nsec)
920238825Smm{
921238825Smm	int r;
922238825Smm
923238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_time");
924238825Smm	if (r != ARCHIVE_OK)
925238825Smm		return (r);
926238825Smm	return set_timefilter((struct archive_match *)_a, flag,
927238825Smm			sec, nsec, sec, nsec);
928238825Smm}
929238825Smm
930238825Smmint
931238825Smmarchive_match_include_date(struct archive *_a, int flag,
932238825Smm    const char *datestr)
933238825Smm{
934238825Smm	int r;
935238825Smm
936238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_date");
937238825Smm	if (r != ARCHIVE_OK)
938238825Smm		return (r);
939238825Smm	return set_timefilter_date((struct archive_match *)_a, flag, datestr);
940238825Smm}
941238825Smm
942238825Smmint
943238825Smmarchive_match_include_date_w(struct archive *_a, int flag,
944238825Smm    const wchar_t *datestr)
945238825Smm{
946238825Smm	int r;
947238825Smm
948238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_date_w");
949238825Smm	if (r != ARCHIVE_OK)
950238825Smm		return (r);
951238825Smm
952238825Smm	return set_timefilter_date_w((struct archive_match *)_a, flag, datestr);
953238825Smm}
954238825Smm
955238825Smmint
956238825Smmarchive_match_include_file_time(struct archive *_a, int flag,
957238825Smm    const char *pathname)
958238825Smm{
959238825Smm	int r;
960238825Smm
961238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_file_time");
962238825Smm	if (r != ARCHIVE_OK)
963238825Smm		return (r);
964238825Smm	return set_timefilter_pathname_mbs((struct archive_match *)_a,
965238825Smm			flag, pathname);
966238825Smm}
967238825Smm
968238825Smmint
969238825Smmarchive_match_include_file_time_w(struct archive *_a, int flag,
970238825Smm    const wchar_t *pathname)
971238825Smm{
972238825Smm	int r;
973238825Smm
974238825Smm	r = validate_time_flag(_a, flag, "archive_match_include_file_time_w");
975238825Smm	if (r != ARCHIVE_OK)
976238825Smm		return (r);
977238825Smm	return set_timefilter_pathname_wcs((struct archive_match *)_a,
978238825Smm			flag, pathname);
979238825Smm}
980238825Smm
981238825Smmint
982238825Smmarchive_match_exclude_entry(struct archive *_a, int flag,
983238825Smm    struct archive_entry *entry)
984238825Smm{
985238825Smm	struct archive_match *a;
986238825Smm	int r;
987238825Smm
988238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
989238825Smm	    ARCHIVE_STATE_NEW, "archive_match_time_include_entry");
990238825Smm	a = (struct archive_match *)_a;
991238825Smm
992238825Smm	if (entry == NULL) {
993238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
994238825Smm		return (ARCHIVE_FAILED);
995238825Smm	}
996238825Smm	r = validate_time_flag(_a, flag, "archive_match_exclude_entry");
997238825Smm	if (r != ARCHIVE_OK)
998238825Smm		return (r);
999238825Smm	return (add_entry(a, flag, entry));
1000238825Smm}
1001238825Smm
1002238825Smm/*
1003238825Smm * Test function for time stamps.
1004238825Smm *
1005238825Smm * Returns 1 if archive entry is excluded.
1006238825Smm * Returns 0 if archive entry is not excluded.
1007238825Smm * Returns <0 if something error happened.
1008238825Smm */
1009238825Smmint
1010238825Smmarchive_match_time_excluded(struct archive *_a,
1011238825Smm    struct archive_entry *entry)
1012238825Smm{
1013238825Smm	struct archive_match *a;
1014238825Smm
1015238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1016238825Smm	    ARCHIVE_STATE_NEW, "archive_match_time_excluded_ae");
1017238825Smm
1018238825Smm	a = (struct archive_match *)_a;
1019238825Smm	if (entry == NULL) {
1020238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
1021238825Smm		return (ARCHIVE_FAILED);
1022238825Smm	}
1023238825Smm
1024238825Smm	/* If we don't have inclusion time set at all, the entry is always
1025238825Smm	 * not excluded. */
1026238825Smm	if ((a->setflag & TIME_IS_SET) == 0)
1027238825Smm		return (0);
1028238825Smm	return (time_excluded(a, entry));
1029238825Smm}
1030238825Smm
1031238825Smmstatic int
1032238825Smmvalidate_time_flag(struct archive *_a, int flag, const char *_fn)
1033238825Smm{
1034238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1035238825Smm	    ARCHIVE_STATE_NEW, _fn);
1036238825Smm
1037238825Smm	/* Check a type of time. */
1038238825Smm	if (flag &
1039238825Smm	   ((~(ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) & 0xff00)) {
1040238825Smm		archive_set_error(_a, EINVAL, "Invalid time flag");
1041238825Smm		return (ARCHIVE_FAILED);
1042238825Smm	}
1043238825Smm	if ((flag & (ARCHIVE_MATCH_MTIME | ARCHIVE_MATCH_CTIME)) == 0) {
1044238825Smm		archive_set_error(_a, EINVAL, "No time flag");
1045238825Smm		return (ARCHIVE_FAILED);
1046238825Smm	}
1047238825Smm
1048238825Smm	/* Check a type of comparison. */
1049238825Smm	if (flag &
1050238825Smm	   ((~(ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1051238825Smm			| ARCHIVE_MATCH_EQUAL)) & 0x00ff)) {
1052238825Smm		archive_set_error(_a, EINVAL, "Invalid comparison flag");
1053238825Smm		return (ARCHIVE_FAILED);
1054238825Smm	}
1055238825Smm	if ((flag & (ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER
1056238825Smm	    | ARCHIVE_MATCH_EQUAL)) == 0) {
1057238825Smm		archive_set_error(_a, EINVAL, "No comparison flag");
1058238825Smm		return (ARCHIVE_FAILED);
1059238825Smm	}
1060238825Smm
1061238825Smm	return (ARCHIVE_OK);
1062238825Smm}
1063238825Smm
1064238825Smm#define JUST_EQUAL(t) (((t) &  (ARCHIVE_MATCH_EQUAL |\
1065238825Smm	ARCHIVE_MATCH_NEWER | ARCHIVE_MATCH_OLDER)) == ARCHIVE_MATCH_EQUAL)
1066238825Smmstatic int
1067238825Smmset_timefilter(struct archive_match *a, int timetype,
1068238825Smm    time_t mtime_sec, long mtime_nsec, time_t ctime_sec, long ctime_nsec)
1069238825Smm{
1070238825Smm	if (timetype & ARCHIVE_MATCH_MTIME) {
1071238825Smm		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1072238825Smm			a->newer_mtime_filter = timetype;
1073238825Smm			a->newer_mtime_sec = mtime_sec;
1074238825Smm			a->newer_mtime_nsec = mtime_nsec;
1075238825Smm			a->setflag |= TIME_IS_SET;
1076238825Smm		}
1077238825Smm		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1078238825Smm			a->older_mtime_filter = timetype;
1079238825Smm			a->older_mtime_sec = mtime_sec;
1080238825Smm			a->older_mtime_nsec = mtime_nsec;
1081238825Smm			a->setflag |= TIME_IS_SET;
1082238825Smm		}
1083238825Smm	}
1084238825Smm	if (timetype & ARCHIVE_MATCH_CTIME) {
1085238825Smm		if ((timetype & ARCHIVE_MATCH_NEWER) || JUST_EQUAL(timetype)) {
1086238825Smm			a->newer_ctime_filter = timetype;
1087238825Smm			a->newer_ctime_sec = ctime_sec;
1088238825Smm			a->newer_ctime_nsec = ctime_nsec;
1089238825Smm			a->setflag |= TIME_IS_SET;
1090238825Smm		}
1091238825Smm		if ((timetype & ARCHIVE_MATCH_OLDER) || JUST_EQUAL(timetype)) {
1092238825Smm			a->older_ctime_filter = timetype;
1093238825Smm			a->older_ctime_sec = ctime_sec;
1094238825Smm			a->older_ctime_nsec = ctime_nsec;
1095238825Smm			a->setflag |= TIME_IS_SET;
1096238825Smm		}
1097238825Smm	}
1098238825Smm	return (ARCHIVE_OK);
1099238825Smm}
1100238825Smm
1101238825Smmstatic int
1102238825Smmset_timefilter_date(struct archive_match *a, int timetype, const char *datestr)
1103238825Smm{
1104238825Smm	time_t t;
1105238825Smm
1106238825Smm	if (datestr == NULL || *datestr == '\0') {
1107238825Smm		archive_set_error(&(a->archive), EINVAL, "date is empty");
1108238825Smm		return (ARCHIVE_FAILED);
1109238825Smm	}
1110238825Smm	t = get_date(a->now, datestr);
1111238825Smm	if (t == (time_t)-1) {
1112238825Smm		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1113238825Smm		return (ARCHIVE_FAILED);
1114238825Smm	}
1115238825Smm	return set_timefilter(a, timetype, t, 0, t, 0);
1116238825Smm}
1117238825Smm
1118238825Smmstatic int
1119238825Smmset_timefilter_date_w(struct archive_match *a, int timetype,
1120238825Smm    const wchar_t *datestr)
1121238825Smm{
1122238825Smm	struct archive_string as;
1123238825Smm	time_t t;
1124238825Smm
1125238825Smm	if (datestr == NULL || *datestr == L'\0') {
1126238825Smm		archive_set_error(&(a->archive), EINVAL, "date is empty");
1127238825Smm		return (ARCHIVE_FAILED);
1128238825Smm	}
1129238825Smm
1130238825Smm	archive_string_init(&as);
1131238825Smm	if (archive_string_append_from_wcs(&as, datestr, wcslen(datestr)) < 0) {
1132238825Smm		archive_string_free(&as);
1133238825Smm		if (errno == ENOMEM)
1134238825Smm			return (error_nomem(a));
1135238825Smm		archive_set_error(&(a->archive), -1,
1136238825Smm		    "Failed to convert WCS to MBS");
1137238825Smm		return (ARCHIVE_FAILED);
1138238825Smm	}
1139238825Smm	t = get_date(a->now, as.s);
1140238825Smm	archive_string_free(&as);
1141238825Smm	if (t == (time_t)-1) {
1142238825Smm		archive_set_error(&(a->archive), EINVAL, "invalid date string");
1143238825Smm		return (ARCHIVE_FAILED);
1144238825Smm	}
1145238825Smm	return set_timefilter(a, timetype, t, 0, t, 0);
1146238825Smm}
1147238825Smm
1148238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1149238825Smm#define EPOC_TIME ARCHIVE_LITERAL_ULL(116444736000000000)
1150238825Smmstatic int
1151238825Smmset_timefilter_find_data(struct archive_match *a, int timetype,
1152238825Smm    DWORD ftLastWriteTime_dwHighDateTime, DWORD ftLastWriteTime_dwLowDateTime,
1153238825Smm    DWORD ftCreationTime_dwHighDateTime, DWORD ftCreationTime_dwLowDateTime)
1154238825Smm{
1155238825Smm	ULARGE_INTEGER utc;
1156238825Smm	time_t ctime_sec, mtime_sec;
1157238825Smm	long ctime_ns, mtime_ns;
1158238825Smm
1159238825Smm	utc.HighPart = ftCreationTime_dwHighDateTime;
1160238825Smm	utc.LowPart = ftCreationTime_dwLowDateTime;
1161238825Smm	if (utc.QuadPart >= EPOC_TIME) {
1162238825Smm		utc.QuadPart -= EPOC_TIME;
1163238825Smm		ctime_sec = (time_t)(utc.QuadPart / 10000000);
1164238825Smm		ctime_ns = (long)(utc.QuadPart % 10000000) * 100;
1165238825Smm	} else {
1166238825Smm		ctime_sec = 0;
1167238825Smm		ctime_ns = 0;
1168238825Smm	}
1169238825Smm	utc.HighPart = ftLastWriteTime_dwHighDateTime;
1170238825Smm	utc.LowPart = ftLastWriteTime_dwLowDateTime;
1171238825Smm	if (utc.QuadPart >= EPOC_TIME) {
1172238825Smm		utc.QuadPart -= EPOC_TIME;
1173238825Smm		mtime_sec = (time_t)(utc.QuadPart / 10000000);
1174238825Smm		mtime_ns = (long)(utc.QuadPart % 10000000) * 100;
1175238825Smm	} else {
1176238825Smm		mtime_sec = 0;
1177238825Smm		mtime_ns = 0;
1178238825Smm	}
1179238825Smm	return set_timefilter(a, timetype,
1180238825Smm			mtime_sec, mtime_ns, ctime_sec, ctime_ns);
1181238825Smm}
1182238825Smm
1183238825Smmstatic int
1184238825Smmset_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1185238825Smm    const char *path)
1186238825Smm{
1187238825Smm	/* NOTE: stat() on Windows cannot handle nano seconds. */
1188238825Smm	HANDLE h;
1189299529Smm	WIN32_FIND_DATAA d;
1190238825Smm
1191238825Smm	if (path == NULL || *path == '\0') {
1192238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1193238825Smm		return (ARCHIVE_FAILED);
1194238825Smm	}
1195238825Smm	h = FindFirstFileA(path, &d);
1196238825Smm	if (h == INVALID_HANDLE_VALUE) {
1197238825Smm		la_dosmaperr(GetLastError());
1198238825Smm		archive_set_error(&(a->archive), errno,
1199238825Smm		    "Failed to FindFirstFileA");
1200238825Smm		return (ARCHIVE_FAILED);
1201238825Smm	}
1202238825Smm	FindClose(h);
1203238825Smm	return set_timefilter_find_data(a, timetype,
1204238825Smm	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1205238825Smm	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1206238825Smm}
1207238825Smm
1208238825Smmstatic int
1209238825Smmset_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1210238825Smm    const wchar_t *path)
1211238825Smm{
1212238825Smm	HANDLE h;
1213238825Smm	WIN32_FIND_DATAW d;
1214238825Smm
1215238825Smm	if (path == NULL || *path == L'\0') {
1216238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1217238825Smm		return (ARCHIVE_FAILED);
1218238825Smm	}
1219238825Smm	h = FindFirstFileW(path, &d);
1220238825Smm	if (h == INVALID_HANDLE_VALUE) {
1221238825Smm		la_dosmaperr(GetLastError());
1222238825Smm		archive_set_error(&(a->archive), errno,
1223238825Smm		    "Failed to FindFirstFile");
1224238825Smm		return (ARCHIVE_FAILED);
1225238825Smm	}
1226238825Smm	FindClose(h);
1227238825Smm	return set_timefilter_find_data(a, timetype,
1228238825Smm	    d.ftLastWriteTime.dwHighDateTime, d.ftLastWriteTime.dwLowDateTime,
1229238825Smm	    d.ftCreationTime.dwHighDateTime, d.ftCreationTime.dwLowDateTime);
1230238825Smm}
1231238825Smm
1232238825Smm#else /* _WIN32 && !__CYGWIN__ */
1233238825Smm
1234238825Smmstatic int
1235238825Smmset_timefilter_stat(struct archive_match *a, int timetype, struct stat *st)
1236238825Smm{
1237238825Smm	struct archive_entry *ae;
1238238825Smm	time_t ctime_sec, mtime_sec;
1239238825Smm	long ctime_ns, mtime_ns;
1240238825Smm
1241238825Smm	ae = archive_entry_new();
1242238825Smm	if (ae == NULL)
1243238825Smm		return (error_nomem(a));
1244238825Smm	archive_entry_copy_stat(ae, st);
1245238825Smm	ctime_sec = archive_entry_ctime(ae);
1246238825Smm	ctime_ns = archive_entry_ctime_nsec(ae);
1247238825Smm	mtime_sec = archive_entry_mtime(ae);
1248238825Smm	mtime_ns = archive_entry_mtime_nsec(ae);
1249238825Smm	archive_entry_free(ae);
1250238825Smm	return set_timefilter(a, timetype, mtime_sec, mtime_ns,
1251238825Smm			ctime_sec, ctime_ns);
1252238825Smm}
1253238825Smm
1254238825Smmstatic int
1255238825Smmset_timefilter_pathname_mbs(struct archive_match *a, int timetype,
1256238825Smm    const char *path)
1257238825Smm{
1258238825Smm	struct stat st;
1259238825Smm
1260238825Smm	if (path == NULL || *path == '\0') {
1261238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1262238825Smm		return (ARCHIVE_FAILED);
1263238825Smm	}
1264348607Smm	if (la_stat(path, &st) != 0) {
1265238825Smm		archive_set_error(&(a->archive), errno, "Failed to stat()");
1266238825Smm		return (ARCHIVE_FAILED);
1267238825Smm	}
1268238825Smm	return (set_timefilter_stat(a, timetype, &st));
1269238825Smm}
1270238825Smm
1271238825Smmstatic int
1272238825Smmset_timefilter_pathname_wcs(struct archive_match *a, int timetype,
1273238825Smm    const wchar_t *path)
1274238825Smm{
1275238825Smm	struct archive_string as;
1276238825Smm	int r;
1277238825Smm
1278238825Smm	if (path == NULL || *path == L'\0') {
1279238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is empty");
1280238825Smm		return (ARCHIVE_FAILED);
1281238825Smm	}
1282238825Smm
1283238825Smm	/* Convert WCS filename to MBS filename. */
1284238825Smm	archive_string_init(&as);
1285238825Smm	if (archive_string_append_from_wcs(&as, path, wcslen(path)) < 0) {
1286238825Smm		archive_string_free(&as);
1287238825Smm		if (errno == ENOMEM)
1288238825Smm			return (error_nomem(a));
1289238825Smm		archive_set_error(&(a->archive), -1,
1290238825Smm		    "Failed to convert WCS to MBS");
1291238825Smm		return (ARCHIVE_FAILED);
1292238825Smm	}
1293238825Smm
1294238825Smm	r = set_timefilter_pathname_mbs(a, timetype, as.s);
1295238825Smm	archive_string_free(&as);
1296238825Smm
1297238825Smm	return (r);
1298238825Smm}
1299238825Smm#endif /* _WIN32 && !__CYGWIN__ */
1300238825Smm
1301238825Smm/*
1302313570Smm * Call back functions for archive_rb.
1303238825Smm */
1304238825Smmstatic int
1305238825Smmcmp_node_mbs(const struct archive_rb_node *n1,
1306238825Smm    const struct archive_rb_node *n2)
1307238825Smm{
1308238825Smm	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1309238825Smm	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1310238825Smm	const char *p1, *p2;
1311238825Smm
1312238825Smm	archive_mstring_get_mbs(NULL, &(f1->pathname), &p1);
1313238825Smm	archive_mstring_get_mbs(NULL, &(f2->pathname), &p2);
1314238825Smm	if (p1 == NULL)
1315238825Smm		return (1);
1316238825Smm	if (p2 == NULL)
1317238825Smm		return (-1);
1318238825Smm	return (strcmp(p1, p2));
1319238825Smm}
1320238825Smm
1321238825Smmstatic int
1322238825Smmcmp_key_mbs(const struct archive_rb_node *n, const void *key)
1323238825Smm{
1324238825Smm	struct match_file *f = (struct match_file *)(uintptr_t)n;
1325238825Smm	const char *p;
1326238825Smm
1327238825Smm	archive_mstring_get_mbs(NULL, &(f->pathname), &p);
1328238825Smm	if (p == NULL)
1329238825Smm		return (-1);
1330238825Smm	return (strcmp(p, (const char *)key));
1331238825Smm}
1332238825Smm
1333238825Smmstatic int
1334238825Smmcmp_node_wcs(const struct archive_rb_node *n1,
1335238825Smm    const struct archive_rb_node *n2)
1336238825Smm{
1337238825Smm	struct match_file *f1 = (struct match_file *)(uintptr_t)n1;
1338238825Smm	struct match_file *f2 = (struct match_file *)(uintptr_t)n2;
1339238825Smm	const wchar_t *p1, *p2;
1340238825Smm
1341238825Smm	archive_mstring_get_wcs(NULL, &(f1->pathname), &p1);
1342238825Smm	archive_mstring_get_wcs(NULL, &(f2->pathname), &p2);
1343238825Smm	if (p1 == NULL)
1344238825Smm		return (1);
1345238825Smm	if (p2 == NULL)
1346238825Smm		return (-1);
1347238825Smm	return (wcscmp(p1, p2));
1348238825Smm}
1349238825Smm
1350238825Smmstatic int
1351238825Smmcmp_key_wcs(const struct archive_rb_node *n, const void *key)
1352238825Smm{
1353238825Smm	struct match_file *f = (struct match_file *)(uintptr_t)n;
1354238825Smm	const wchar_t *p;
1355238825Smm
1356238825Smm	archive_mstring_get_wcs(NULL, &(f->pathname), &p);
1357238825Smm	if (p == NULL)
1358238825Smm		return (-1);
1359238825Smm	return (wcscmp(p, (const wchar_t *)key));
1360238825Smm}
1361238825Smm
1362238825Smmstatic void
1363238825Smmentry_list_init(struct entry_list *list)
1364238825Smm{
1365238825Smm	list->first = NULL;
1366238825Smm	list->last = &(list->first);
1367238825Smm	list->count = 0;
1368238825Smm}
1369238825Smm
1370238825Smmstatic void
1371238825Smmentry_list_free(struct entry_list *list)
1372238825Smm{
1373238825Smm	struct match_file *p, *q;
1374238825Smm
1375238825Smm	for (p = list->first; p != NULL; ) {
1376238825Smm		q = p;
1377238825Smm		p = p->next;
1378238825Smm		archive_mstring_clean(&(q->pathname));
1379238825Smm		free(q);
1380238825Smm	}
1381238825Smm}
1382238825Smm
1383238825Smmstatic void
1384238825Smmentry_list_add(struct entry_list *list, struct match_file *file)
1385238825Smm{
1386238825Smm	*list->last = file;
1387238825Smm	list->last = &(file->next);
1388238825Smm	list->count++;
1389238825Smm}
1390238825Smm
1391238825Smmstatic int
1392238825Smmadd_entry(struct archive_match *a, int flag,
1393238825Smm    struct archive_entry *entry)
1394238825Smm{
1395238825Smm	struct match_file *f;
1396238825Smm	const void *pathname;
1397238825Smm	int r;
1398238825Smm
1399238825Smm	f = calloc(1, sizeof(*f));
1400238825Smm	if (f == NULL)
1401238825Smm		return (error_nomem(a));
1402238825Smm
1403238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1404238825Smm	pathname = archive_entry_pathname_w(entry);
1405238825Smm	if (pathname == NULL) {
1406238825Smm		free(f);
1407238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1408238825Smm		return (ARCHIVE_FAILED);
1409238825Smm	}
1410238825Smm	archive_mstring_copy_wcs(&(f->pathname), pathname);
1411238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1412238825Smm#else
1413248616Smm	(void)rb_ops_wcs;
1414238825Smm	pathname = archive_entry_pathname(entry);
1415238825Smm	if (pathname == NULL) {
1416238825Smm		free(f);
1417238825Smm		archive_set_error(&(a->archive), EINVAL, "pathname is NULL");
1418238825Smm		return (ARCHIVE_FAILED);
1419238825Smm	}
1420238825Smm	archive_mstring_copy_mbs(&(f->pathname), pathname);
1421238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1422238825Smm#endif
1423238825Smm	f->flag = flag;
1424238825Smm	f->mtime_sec = archive_entry_mtime(entry);
1425238825Smm	f->mtime_nsec = archive_entry_mtime_nsec(entry);
1426238825Smm	f->ctime_sec = archive_entry_ctime(entry);
1427238825Smm	f->ctime_nsec = archive_entry_ctime_nsec(entry);
1428238825Smm	r = __archive_rb_tree_insert_node(&(a->exclusion_tree), &(f->node));
1429238825Smm	if (!r) {
1430238825Smm		struct match_file *f2;
1431238825Smm
1432238825Smm		/* Get the duplicated file. */
1433238825Smm		f2 = (struct match_file *)__archive_rb_tree_find_node(
1434238825Smm			&(a->exclusion_tree), pathname);
1435238825Smm
1436238825Smm		/*
1437313570Smm		 * We always overwrite comparison condition.
1438238825Smm		 * If you do not want to overwrite it, you should not
1439238825Smm		 * call archive_match_exclude_entry(). We cannot know
1440238825Smm		 * what behavior you really expect since overwriting
1441238825Smm		 * condition might be different with the flag.
1442238825Smm		 */
1443238825Smm		if (f2 != NULL) {
1444238825Smm			f2->flag = f->flag;
1445238825Smm			f2->mtime_sec = f->mtime_sec;
1446238825Smm			f2->mtime_nsec = f->mtime_nsec;
1447238825Smm			f2->ctime_sec = f->ctime_sec;
1448238825Smm			f2->ctime_nsec = f->ctime_nsec;
1449238825Smm		}
1450238825Smm		/* Release the duplicated file. */
1451238825Smm		archive_mstring_clean(&(f->pathname));
1452238825Smm		free(f);
1453238825Smm		return (ARCHIVE_OK);
1454238825Smm	}
1455238825Smm	entry_list_add(&(a->exclusion_entry_list), f);
1456238825Smm	a->setflag |= TIME_IS_SET;
1457238825Smm	return (ARCHIVE_OK);
1458238825Smm}
1459238825Smm
1460238825Smm/*
1461238825Smm * Test if entry is excluded by its timestamp.
1462238825Smm */
1463238825Smmstatic int
1464238825Smmtime_excluded(struct archive_match *a, struct archive_entry *entry)
1465238825Smm{
1466238825Smm	struct match_file *f;
1467238825Smm	const void *pathname;
1468238825Smm	time_t sec;
1469238825Smm	long nsec;
1470238825Smm
1471238825Smm	/*
1472238825Smm	 * If this file/dir is excluded by a time comparison, skip it.
1473238825Smm	 */
1474238825Smm	if (a->newer_ctime_filter) {
1475238825Smm		/* If ctime is not set, use mtime instead. */
1476238825Smm		if (archive_entry_ctime_is_set(entry))
1477238825Smm			sec = archive_entry_ctime(entry);
1478238825Smm		else
1479238825Smm			sec = archive_entry_mtime(entry);
1480238825Smm		if (sec < a->newer_ctime_sec)
1481238825Smm			return (1); /* Too old, skip it. */
1482238825Smm		if (sec == a->newer_ctime_sec) {
1483238825Smm			if (archive_entry_ctime_is_set(entry))
1484238825Smm				nsec = archive_entry_ctime_nsec(entry);
1485238825Smm			else
1486238825Smm				nsec = archive_entry_mtime_nsec(entry);
1487238825Smm			if (nsec < a->newer_ctime_nsec)
1488238825Smm				return (1); /* Too old, skip it. */
1489238825Smm			if (nsec == a->newer_ctime_nsec &&
1490238825Smm			    (a->newer_ctime_filter & ARCHIVE_MATCH_EQUAL)
1491238825Smm			      == 0)
1492238825Smm				return (1); /* Equal, skip it. */
1493238825Smm		}
1494238825Smm	}
1495238825Smm	if (a->older_ctime_filter) {
1496238825Smm		/* If ctime is not set, use mtime instead. */
1497238825Smm		if (archive_entry_ctime_is_set(entry))
1498238825Smm			sec = archive_entry_ctime(entry);
1499238825Smm		else
1500238825Smm			sec = archive_entry_mtime(entry);
1501238825Smm		if (sec > a->older_ctime_sec)
1502238825Smm			return (1); /* Too new, skip it. */
1503238825Smm		if (sec == a->older_ctime_sec) {
1504238825Smm			if (archive_entry_ctime_is_set(entry))
1505238825Smm				nsec = archive_entry_ctime_nsec(entry);
1506238825Smm			else
1507238825Smm				nsec = archive_entry_mtime_nsec(entry);
1508238825Smm			if (nsec > a->older_ctime_nsec)
1509238825Smm				return (1); /* Too new, skip it. */
1510238825Smm			if (nsec == a->older_ctime_nsec &&
1511238825Smm			    (a->older_ctime_filter & ARCHIVE_MATCH_EQUAL)
1512238825Smm			      == 0)
1513313570Smm				return (1); /* Equal, skip it. */
1514238825Smm		}
1515238825Smm	}
1516238825Smm	if (a->newer_mtime_filter) {
1517238825Smm		sec = archive_entry_mtime(entry);
1518238825Smm		if (sec < a->newer_mtime_sec)
1519238825Smm			return (1); /* Too old, skip it. */
1520238825Smm		if (sec == a->newer_mtime_sec) {
1521238825Smm			nsec = archive_entry_mtime_nsec(entry);
1522238825Smm			if (nsec < a->newer_mtime_nsec)
1523238825Smm				return (1); /* Too old, skip it. */
1524238825Smm			if (nsec == a->newer_mtime_nsec &&
1525238825Smm			    (a->newer_mtime_filter & ARCHIVE_MATCH_EQUAL)
1526238825Smm			       == 0)
1527238825Smm				return (1); /* Equal, skip it. */
1528238825Smm		}
1529238825Smm	}
1530238825Smm	if (a->older_mtime_filter) {
1531238825Smm		sec = archive_entry_mtime(entry);
1532238825Smm		if (sec > a->older_mtime_sec)
1533238825Smm			return (1); /* Too new, skip it. */
1534238825Smm		nsec = archive_entry_mtime_nsec(entry);
1535238825Smm		if (sec == a->older_mtime_sec) {
1536238825Smm			if (nsec > a->older_mtime_nsec)
1537238825Smm				return (1); /* Too new, skip it. */
1538238825Smm			if (nsec == a->older_mtime_nsec &&
1539238825Smm			    (a->older_mtime_filter & ARCHIVE_MATCH_EQUAL)
1540238825Smm			       == 0)
1541238825Smm				return (1); /* Equal, skip it. */
1542238825Smm		}
1543238825Smm	}
1544238825Smm
1545313570Smm	/* If there is no exclusion list, include the file. */
1546238825Smm	if (a->exclusion_entry_list.count == 0)
1547238825Smm		return (0);
1548238825Smm
1549238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1550238825Smm	pathname = archive_entry_pathname_w(entry);
1551238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_wcs;
1552238825Smm#else
1553248616Smm	(void)rb_ops_wcs;
1554238825Smm	pathname = archive_entry_pathname(entry);
1555238825Smm	a->exclusion_tree.rbt_ops = &rb_ops_mbs;
1556238825Smm#endif
1557238825Smm	if (pathname == NULL)
1558238825Smm		return (0);
1559238825Smm
1560238825Smm	f = (struct match_file *)__archive_rb_tree_find_node(
1561238825Smm		&(a->exclusion_tree), pathname);
1562238825Smm	/* If the file wasn't rejected, include it. */
1563238825Smm	if (f == NULL)
1564238825Smm		return (0);
1565238825Smm
1566238825Smm	if (f->flag & ARCHIVE_MATCH_CTIME) {
1567238825Smm		sec = archive_entry_ctime(entry);
1568238825Smm		if (f->ctime_sec > sec) {
1569238825Smm			if (f->flag & ARCHIVE_MATCH_OLDER)
1570238825Smm				return (1);
1571238825Smm		} else if (f->ctime_sec < sec) {
1572238825Smm			if (f->flag & ARCHIVE_MATCH_NEWER)
1573238825Smm				return (1);
1574238825Smm		} else {
1575238825Smm			nsec = archive_entry_ctime_nsec(entry);
1576238825Smm			if (f->ctime_nsec > nsec) {
1577238825Smm				if (f->flag & ARCHIVE_MATCH_OLDER)
1578238825Smm					return (1);
1579238825Smm			} else if (f->ctime_nsec < nsec) {
1580238825Smm				if (f->flag & ARCHIVE_MATCH_NEWER)
1581238825Smm					return (1);
1582238825Smm			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1583238825Smm				return (1);
1584238825Smm		}
1585238825Smm	}
1586238825Smm	if (f->flag & ARCHIVE_MATCH_MTIME) {
1587238825Smm		sec = archive_entry_mtime(entry);
1588238825Smm		if (f->mtime_sec > sec) {
1589238825Smm			if (f->flag & ARCHIVE_MATCH_OLDER)
1590238825Smm				return (1);
1591238825Smm		} else if (f->mtime_sec < sec) {
1592238825Smm			if (f->flag & ARCHIVE_MATCH_NEWER)
1593238825Smm				return (1);
1594238825Smm		} else {
1595238825Smm			nsec = archive_entry_mtime_nsec(entry);
1596238825Smm			if (f->mtime_nsec > nsec) {
1597238825Smm				if (f->flag & ARCHIVE_MATCH_OLDER)
1598238825Smm					return (1);
1599238825Smm			} else if (f->mtime_nsec < nsec) {
1600238825Smm				if (f->flag & ARCHIVE_MATCH_NEWER)
1601238825Smm					return (1);
1602238825Smm			} else if (f->flag & ARCHIVE_MATCH_EQUAL)
1603238825Smm				return (1);
1604238825Smm		}
1605238825Smm	}
1606238825Smm	return (0);
1607238825Smm}
1608238825Smm
1609238825Smm/*
1610238825Smm * Utility functions to manage inclusion owners
1611238825Smm */
1612238825Smm
1613238825Smmint
1614328827Smmarchive_match_include_uid(struct archive *_a, la_int64_t uid)
1615238825Smm{
1616238825Smm	struct archive_match *a;
1617238825Smm
1618238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1619238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uid");
1620238825Smm	a = (struct archive_match *)_a;
1621238825Smm	return (add_owner_id(a, &(a->inclusion_uids), uid));
1622238825Smm}
1623238825Smm
1624238825Smmint
1625328827Smmarchive_match_include_gid(struct archive *_a, la_int64_t gid)
1626238825Smm{
1627238825Smm	struct archive_match *a;
1628238825Smm
1629238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1630238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gid");
1631238825Smm	a = (struct archive_match *)_a;
1632238825Smm	return (add_owner_id(a, &(a->inclusion_gids), gid));
1633238825Smm}
1634238825Smm
1635238825Smmint
1636238825Smmarchive_match_include_uname(struct archive *_a, const char *uname)
1637238825Smm{
1638238825Smm	struct archive_match *a;
1639238825Smm
1640238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1641238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uname");
1642238825Smm	a = (struct archive_match *)_a;
1643238825Smm	return (add_owner_name(a, &(a->inclusion_unames), 1, uname));
1644238825Smm}
1645238825Smm
1646238825Smmint
1647238825Smmarchive_match_include_uname_w(struct archive *_a, const wchar_t *uname)
1648238825Smm{
1649238825Smm	struct archive_match *a;
1650238825Smm
1651238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1652238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_uname_w");
1653238825Smm	a = (struct archive_match *)_a;
1654238825Smm	return (add_owner_name(a, &(a->inclusion_unames), 0, uname));
1655238825Smm}
1656238825Smm
1657238825Smmint
1658238825Smmarchive_match_include_gname(struct archive *_a, const char *gname)
1659238825Smm{
1660238825Smm	struct archive_match *a;
1661238825Smm
1662238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1663238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gname");
1664238825Smm	a = (struct archive_match *)_a;
1665238825Smm	return (add_owner_name(a, &(a->inclusion_gnames), 1, gname));
1666238825Smm}
1667238825Smm
1668238825Smmint
1669238825Smmarchive_match_include_gname_w(struct archive *_a, const wchar_t *gname)
1670238825Smm{
1671238825Smm	struct archive_match *a;
1672238825Smm
1673238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1674238825Smm	    ARCHIVE_STATE_NEW, "archive_match_include_gname_w");
1675238825Smm	a = (struct archive_match *)_a;
1676238825Smm	return (add_owner_name(a, &(a->inclusion_gnames), 0, gname));
1677238825Smm}
1678238825Smm
1679238825Smm/*
1680238825Smm * Test function for owner(uid, gid, uname, gname).
1681238825Smm *
1682238825Smm * Returns 1 if archive entry is excluded.
1683238825Smm * Returns 0 if archive entry is not excluded.
1684238825Smm * Returns <0 if something error happened.
1685238825Smm */
1686238825Smmint
1687238825Smmarchive_match_owner_excluded(struct archive *_a,
1688238825Smm    struct archive_entry *entry)
1689238825Smm{
1690238825Smm	struct archive_match *a;
1691238825Smm
1692238825Smm	archive_check_magic(_a, ARCHIVE_MATCH_MAGIC,
1693238825Smm	    ARCHIVE_STATE_NEW, "archive_match_id_excluded_ae");
1694238825Smm
1695238825Smm	a = (struct archive_match *)_a;
1696238825Smm	if (entry == NULL) {
1697238825Smm		archive_set_error(&(a->archive), EINVAL, "entry is NULL");
1698238825Smm		return (ARCHIVE_FAILED);
1699238825Smm	}
1700238825Smm
1701238825Smm	/* If we don't have inclusion id set at all, the entry is always
1702238825Smm	 * not excluded. */
1703238825Smm	if ((a->setflag & ID_IS_SET) == 0)
1704238825Smm		return (0);
1705238825Smm	return (owner_excluded(a, entry));
1706238825Smm}
1707238825Smm
1708238825Smmstatic int
1709238825Smmadd_owner_id(struct archive_match *a, struct id_array *ids, int64_t id)
1710238825Smm{
1711238825Smm	unsigned i;
1712238825Smm
1713238825Smm	if (ids->count + 1 >= ids->size) {
1714248616Smm		void *p;
1715248616Smm
1716238825Smm		if (ids->size == 0)
1717238825Smm			ids->size = 8;
1718238825Smm		else
1719238825Smm			ids->size *= 2;
1720248616Smm		p = realloc(ids->ids, sizeof(*ids->ids) * ids->size);
1721248616Smm		if (p == NULL)
1722238825Smm			return (error_nomem(a));
1723248616Smm		ids->ids = (int64_t *)p;
1724238825Smm	}
1725238825Smm
1726238825Smm	/* Find an insert point. */
1727238825Smm	for (i = 0; i < ids->count; i++) {
1728238825Smm		if (ids->ids[i] >= id)
1729238825Smm			break;
1730238825Smm	}
1731238825Smm
1732313570Smm	/* Add owner id. */
1733238825Smm	if (i == ids->count)
1734238825Smm		ids->ids[ids->count++] = id;
1735238825Smm	else if (ids->ids[i] != id) {
1736238825Smm		memmove(&(ids->ids[i+1]), &(ids->ids[i]),
1737238825Smm		    (ids->count - i) * sizeof(ids->ids[0]));
1738238825Smm		ids->ids[i] = id;
1739238825Smm		ids->count++;
1740238825Smm	}
1741238825Smm	a->setflag |= ID_IS_SET;
1742238825Smm	return (ARCHIVE_OK);
1743238825Smm}
1744238825Smm
1745238825Smmstatic int
1746238825Smmmatch_owner_id(struct id_array *ids, int64_t id)
1747238825Smm{
1748238825Smm	unsigned b, m, t;
1749238825Smm
1750238825Smm	t = 0;
1751248616Smm	b = (unsigned)ids->count;
1752238825Smm	while (t < b) {
1753238825Smm		m = (t + b)>>1;
1754238825Smm		if (ids->ids[m] == id)
1755238825Smm			return (1);
1756238825Smm		if (ids->ids[m] < id)
1757238825Smm			t = m + 1;
1758238825Smm		else
1759238825Smm			b = m;
1760238825Smm	}
1761238825Smm	return (0);
1762238825Smm}
1763238825Smm
1764238825Smmstatic int
1765238825Smmadd_owner_name(struct archive_match *a, struct match_list *list,
1766238825Smm    int mbs, const void *name)
1767238825Smm{
1768238825Smm	struct match *match;
1769238825Smm
1770238825Smm	match = calloc(1, sizeof(*match));
1771238825Smm	if (match == NULL)
1772238825Smm		return (error_nomem(a));
1773238825Smm	if (mbs)
1774238825Smm		archive_mstring_copy_mbs(&(match->pattern), name);
1775238825Smm	else
1776238825Smm		archive_mstring_copy_wcs(&(match->pattern), name);
1777238825Smm	match_list_add(list, match);
1778238825Smm	a->setflag |= ID_IS_SET;
1779238825Smm	return (ARCHIVE_OK);
1780238825Smm}
1781238825Smm
1782238825Smm#if !defined(_WIN32) || defined(__CYGWIN__)
1783238825Smmstatic int
1784238825Smmmatch_owner_name_mbs(struct archive_match *a, struct match_list *list,
1785238825Smm    const char *name)
1786238825Smm{
1787238825Smm	struct match *m;
1788238825Smm	const char *p;
1789238825Smm
1790238825Smm	if (name == NULL || *name == '\0')
1791238825Smm		return (0);
1792238825Smm	for (m = list->first; m; m = m->next) {
1793238825Smm		if (archive_mstring_get_mbs(&(a->archive), &(m->pattern), &p)
1794238825Smm		    < 0 && errno == ENOMEM)
1795238825Smm			return (error_nomem(a));
1796238825Smm		if (p != NULL && strcmp(p, name) == 0) {
1797238825Smm			m->matches++;
1798238825Smm			return (1);
1799238825Smm		}
1800238825Smm	}
1801238825Smm	return (0);
1802238825Smm}
1803238825Smm#else
1804238825Smmstatic int
1805238825Smmmatch_owner_name_wcs(struct archive_match *a, struct match_list *list,
1806238825Smm    const wchar_t *name)
1807238825Smm{
1808238825Smm	struct match *m;
1809238825Smm	const wchar_t *p;
1810238825Smm
1811238825Smm	if (name == NULL || *name == L'\0')
1812238825Smm		return (0);
1813238825Smm	for (m = list->first; m; m = m->next) {
1814238825Smm		if (archive_mstring_get_wcs(&(a->archive), &(m->pattern), &p)
1815238825Smm		    < 0 && errno == ENOMEM)
1816238825Smm			return (error_nomem(a));
1817238825Smm		if (p != NULL && wcscmp(p, name) == 0) {
1818238825Smm			m->matches++;
1819238825Smm			return (1);
1820238825Smm		}
1821238825Smm	}
1822238825Smm	return (0);
1823238825Smm}
1824238825Smm#endif
1825238825Smm
1826238825Smm/*
1827238825Smm * Test if entry is excluded by uid, gid, uname or gname.
1828238825Smm */
1829238825Smmstatic int
1830238825Smmowner_excluded(struct archive_match *a, struct archive_entry *entry)
1831238825Smm{
1832238825Smm	int r;
1833238825Smm
1834238825Smm	if (a->inclusion_uids.count) {
1835238825Smm		if (!match_owner_id(&(a->inclusion_uids),
1836238825Smm		    archive_entry_uid(entry)))
1837238825Smm			return (1);
1838238825Smm	}
1839238825Smm
1840238825Smm	if (a->inclusion_gids.count) {
1841238825Smm		if (!match_owner_id(&(a->inclusion_gids),
1842238825Smm		    archive_entry_gid(entry)))
1843238825Smm			return (1);
1844238825Smm	}
1845238825Smm
1846238825Smm	if (a->inclusion_unames.count) {
1847238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1848238825Smm		r = match_owner_name_wcs(a, &(a->inclusion_unames),
1849238825Smm			archive_entry_uname_w(entry));
1850238825Smm#else
1851238825Smm		r = match_owner_name_mbs(a, &(a->inclusion_unames),
1852238825Smm			archive_entry_uname(entry));
1853238825Smm#endif
1854238825Smm		if (!r)
1855238825Smm			return (1);
1856238825Smm		else if (r < 0)
1857238825Smm			return (r);
1858238825Smm	}
1859238825Smm
1860238825Smm	if (a->inclusion_gnames.count) {
1861238825Smm#if defined(_WIN32) && !defined(__CYGWIN__)
1862238825Smm		r = match_owner_name_wcs(a, &(a->inclusion_gnames),
1863238825Smm			archive_entry_gname_w(entry));
1864238825Smm#else
1865238825Smm		r = match_owner_name_mbs(a, &(a->inclusion_gnames),
1866238825Smm			archive_entry_gname(entry));
1867238825Smm#endif
1868238825Smm		if (!r)
1869238825Smm			return (1);
1870238825Smm		else if (r < 0)
1871238825Smm			return (r);
1872238825Smm	}
1873238825Smm	return (0);
1874238825Smm}
1875238825Smm
1876