From 42a7a591a61dec356ce453f6c2e483c4a56fc9f4 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Fri, 31 Aug 2001 06:33:42 +0000 Subject: [PATCH] Merge 'exclude' changes from tar 1.13.22. This fixes one or two unlikely storage allocation overflow bugs, but doesn't change user-visible behavior otherwise. (bool): Declare, perhaps by including stdbool.h. (): Include only if HAVE_SYS_TYPES_H. (, , , , ): Include if available. (): Include (SIZE_MAX): Define if or doesn't. (verify): New macro. Use it to verify that EXCLUDE macros do not collide with FNM macros. (struct patopts): New struct. (struct exclude): Use it, as exclude patterns now come with options. (new_exclude): Support above changes. (new_exclude, add_exclude_file): Initial size must now be a power of two to simplify overflow checking. (free_exclude, fnmatch_no_wildcards): New function. (excluded_filename): No longer requires options arg, as the options are determined by add_exclude. Now returns bool, not int. (excluded_filename, add_exclude): Add support for the fancy new exclusion options. (add_exclude, add_exclude_file): Now takes int options arg. Check for arithmetic overflow when computing sizes. (add_exclude_file): xrealloc might modify errno, so don't realloc until after errno might be used. --- lib/exclude.c | 204 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 170 insertions(+), 34 deletions(-) diff --git a/lib/exclude.c b/lib/exclude.c index 1925a398a0..5a1e1ee6f5 100644 --- a/lib/exclude.c +++ b/lib/exclude.c @@ -1,5 +1,7 @@ /* exclude.c -- exclude file names - Copyright 1992, 1993, 1994, 1997, 1999, 2000 Free Software Foundation, Inc. + + Copyright 1992, 1993, 1994, 1997, 1999, 2000, 2001 Free Software + Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,73 +24,202 @@ # include #endif +#if HAVE_STDBOOL_H +# include +#else +typedef enum {false = 0, true = 1} bool; +#endif + #include #ifndef errno extern int errno; #endif +#include +#if HAVE_SYS_TYPES_H +# include +#endif +#if HAVE_STDLIB_H +# include +#endif +#if HAVE_STRING_H +# include +#endif +#if HAVE_STRINGS_H +# include +#endif +#if HAVE_INTTYPES_H +# include +#else +# if HAVE_STDINT_H +# include +# endif +#endif + #include #include -#include -#include +#include + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +/* Verify a requirement at compile-time (unlike assert, which is runtime). */ +#define verify(name, assertion) struct name { char a[(assertion) ? 1 : -1]; } + +verify (EXCLUDE_macros_do_not_collide_with_FNM_macros, + (((EXCLUDE_ANCHORED | EXCLUDE_INCLUDE | EXCLUDE_WILDCARDS) + & (FNM_FILE_NAME | FNM_NOESCAPE | FNM_PERIOD | FNM_LEADING_DIR + | FNM_CASEFOLD)) + == 0)); -void *xmalloc PARAMS ((size_t)); -void *xrealloc PARAMS ((void *, size_t)); +/* An exclude pattern-options pair. The options are fnmatch options + ORed with EXCLUDE_* options. */ + +struct patopts + { + char const *pattern; + int options; + }; -/* Keep track of excluded file name patterns. */ +/* An exclude list, of pattern-options pairs. */ struct exclude { - char const **exclude; - int exclude_alloc; - int exclude_count; + struct patopts *exclude; + size_t exclude_alloc; + size_t exclude_count; }; +/* Return a newly allocated and empty exclude list. */ + struct exclude * new_exclude (void) { - struct exclude *ex = (struct exclude *) xmalloc (sizeof (struct exclude)); + struct exclude *ex = (struct exclude *) xmalloc (sizeof *ex); ex->exclude_count = 0; - ex->exclude_alloc = 64; - ex->exclude = (char const **) xmalloc (ex->exclude_alloc * sizeof (char *)); + ex->exclude_alloc = (1 << 6); /* This must be a power of 2. */ + ex->exclude = (struct patopts *) xmalloc (ex->exclude_alloc + * sizeof ex->exclude[0]); return ex; } -int -excluded_filename (struct exclude const *ex, char const *f, int options) +/* Free the storage associated with an exclude list. */ + +void +free_exclude (struct exclude *ex) { - char const * const *exclude = ex->exclude; - int exclude_count = ex->exclude_count; - int i; + free (ex->exclude); + free (ex); +} - for (i = 0; i < exclude_count; i++) - if (fnmatch (exclude[i], f, options) == 0) - return 1; +/* Return zero if PATTERN matches F, obeying OPTIONS, except that + (unlike fnmatch) wildcards are disabled in PATTERN. */ - return 0; +static int +fnmatch_no_wildcards (char const *pattern, char const *f, int options) +{ + if (! (options & FNM_CASEFOLD)) + return (options & FNM_LEADING_DIR ? strcasecmp : strcmp) (pattern, f); + else + { + size_t patlen = strlen (pattern); + int r = ((options & FNM_LEADING_DIR ? strncasecmp : strncmp) + (pattern, f, patlen)); + if (! r) + { + r = f[patlen]; + if (r == '/' && (options & FNM_LEADING_DIR)) + r = 0; + } + return r; + } } +/* Return true if EX excludes F. */ + +bool +excluded_filename (struct exclude const *ex, char const *f) +{ + size_t exclude_count = ex->exclude_count; + + /* If no options are given, the default is to include. */ + if (exclude_count == 0) + return 0; + else + { + struct patopts const *exclude = ex->exclude; + size_t i; + + /* Otherwise, the default is the opposite of the first option. */ + bool excluded = !! (exclude[0].options & EXCLUDE_INCLUDE); + + /* Scan through the options, seeing whether they change F from + excluded to included or vice versa. */ + for (i = 0; i < exclude_count; i++) + { + char const *pattern = exclude[i].pattern; + int options = exclude[i].options; + if (excluded == !! (options & EXCLUDE_INCLUDE)) + { + int (*matcher) PARAMS ((char const *, char const *, int)) = + (options & EXCLUDE_WILDCARDS + ? fnmatch + : fnmatch_no_wildcards); + bool matched = ((*matcher) (pattern, f, options) == 0); + char const *p; + + if (! (options & EXCLUDE_ANCHORED)) + for (p = f; *p && ! matched; p++) + if (*p == '/' && p[1] != '/') + matched = ((*matcher) (pattern, p + 1, options) == 0); + + excluded ^= matched; + } + } + + return excluded; + } +} + +/* Append to EX the exclusion PATTERN with OPTIONS. */ + void -add_exclude (struct exclude *ex, char const *pattern) +add_exclude (struct exclude *ex, char const *pattern, int options) { + struct patopts *patopts; + if (ex->exclude_alloc <= ex->exclude_count) - ex->exclude = (char const **) xrealloc (ex->exclude, - ((ex->exclude_alloc *= 2) - * sizeof (char *))); + { + size_t s = 2 * ex->exclude_alloc; + if (! (0 < s && s <= SIZE_MAX / sizeof ex->exclude[0])) + xalloc_die (); + ex->exclude_alloc = s; + ex->exclude = (struct patopts *) xrealloc (ex->exclude, + s * sizeof ex->exclude[0]); + } - ex->exclude[ex->exclude_count++] = pattern; + patopts = &ex->exclude[ex->exclude_count++]; + patopts->pattern = pattern; + patopts->options = options; } +/* Use ADD_FUNC to append to EX the patterns in FILENAME, each with + OPTIONS. LINE_END terminates each pattern in the file. Return -1 + on failure, 0 on success. */ + int -add_exclude_file (void (*add_func) PARAMS ((struct exclude *, char const *)), - struct exclude *ex, char const *filename, char line_end) +add_exclude_file (void (*add_func) PARAMS ((struct exclude *, + char const *, int)), + struct exclude *ex, char const *filename, int options, + char line_end) { - int use_stdin = filename[0] == '-' && !filename[1]; + bool use_stdin = filename[0] == '-' && !filename[1]; FILE *in; char *buf; char *p; char const *pattern; char const *lim; - size_t buf_alloc = 1024; + size_t buf_alloc = (1 << 10); /* This must be a power of two. */ size_t buf_count = 0; int c; int e = 0; @@ -104,22 +235,27 @@ add_exclude_file (void (*add_func) PARAMS ((struct exclude *, char const *)), { buf[buf_count++] = c; if (buf_count == buf_alloc) - buf = xrealloc (buf, buf_alloc *= 2); + { + buf_alloc *= 2; + if (! buf_alloc) + xalloc_die (); + buf = xrealloc (buf, buf_alloc); + } } - buf = xrealloc (buf, buf_count + 1); - if (ferror (in)) e = errno; if (!use_stdin && fclose (in) != 0) e = errno; + buf = xrealloc (buf, buf_count + 1); + for (pattern = p = buf, lim = buf + buf_count; p <= lim; p++) if (p < lim ? *p == line_end : buf < p && p[-1]) { *p = '\0'; - (*add_func) (ex, pattern); + (*add_func) (ex, pattern, options); pattern = p + 1; } -- 2.30.2