X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/4adbb5f24683417a90a1cc00be54e58d2f7a901a..c98ad3df964103e23f7f502b8e610a0155f19eb6:/exclude.c diff --git a/exclude.c b/exclude.c index fa11e449..bc180f20 100644 --- a/exclude.c +++ b/exclude.c @@ -1,8 +1,10 @@ -/* -*- c-file-style: "linux" -*- +/* + * The filter include/exclude routines. * - * Copyright (C) 1996-2001 by Andrew Tridgell - * Copyright (C) 1996 by Paul Mackerras - * Copyright (C) 2002 by Martin Pool + * Copyright (C) 1996-2001 Andrew Tridgell + * Copyright (C) 1996 Paul Mackerras + * Copyright (C) 2002 Martin Pool + * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison * * 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 @@ -14,16 +16,11 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ -/* a lot of this stuff was originally derived from GNU tar, although - it has now changed so much that it is hard to tell :) */ - -/* include/exclude cluestick added by Martin Pool */ - #include "rsync.h" extern int verbose; @@ -34,6 +31,7 @@ extern int list_only; extern int recurse; extern int io_error; extern int local_server; +extern int prune_empty_dirs; extern int delete_mode; extern int delete_excluded; extern int cvs_exclude; @@ -46,8 +44,8 @@ extern unsigned int curr_dir_len; extern unsigned int module_dirlen; struct filter_list_struct filter_list = { 0, 0, "" }; -struct filter_list_struct cvs_filter_list = { 0, 0, " [cvsignore]" }; -struct filter_list_struct server_filter_list = { 0, 0, " [server]" }; +struct filter_list_struct cvs_filter_list = { 0, 0, " [global CVS]" }; +struct filter_list_struct server_filter_list = { 0, 0, " [daemon]" }; /* Need room enough for ":MODS " prefix plus some room to grow. */ #define MAX_RULE_PREFIX (16) @@ -133,9 +131,9 @@ static void add_rule(struct filter_list_struct *listp, const char *pat, listp->debug_type); } - /* This flag also indicates that we're reading a list that + /* These flags also indicate that we're reading a list that * needs to be filtered now, not post-filtered later. */ - if (xflags & XFLG_ANCHORED2ABS) { + if (xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH)) { uint32 mf = mflags & (MATCHFLG_RECEIVER_SIDE|MATCHFLG_SENDER_SIDE); if (am_sender) { if (mf == MATCHFLG_RECEIVER_SIDE) @@ -150,10 +148,14 @@ static void add_rule(struct filter_list_struct *listp, const char *pat, out_of_memory("add_rule"); memset(ret, 0, sizeof ret[0]); - if (xflags & XFLG_ANCHORED2ABS && *pat == '/' - && !(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE))) { + if (!(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE)) + && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/') + || (xflags & XFLG_ABS_IF_SLASH && strchr(pat, '/') != NULL))) { mflags |= MATCHFLG_ABS_PATH; - ex_len = dirbuf_len - module_dirlen - 1; + if (*pat == '/') + ex_len = dirbuf_len - module_dirlen - 1; + else + ex_len = 0; } else ex_len = 0; if (!(ret->pattern = new_array(char, ex_len + pat_len + 1))) @@ -170,6 +172,12 @@ static void add_rule(struct filter_list_struct *listp, const char *pat, /* If the pattern starts with **, note that. */ if (cp == ret->pattern) mflags |= MATCHFLG_WILD2_PREFIX; + /* If the pattern ends with ***, note that. */ + if (pat_len >= 3 + && ret->pattern[pat_len-3] == '*' + && ret->pattern[pat_len-2] == '*' + && ret->pattern[pat_len-1] == '*') + mflags |= MATCHFLG_WILD3_SUFFIX; } } @@ -290,23 +298,22 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr, strlcpy(to, merge_file, *len_ptr + 1); merge_file = to; } - if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) { + if (!sanitize_path(fn, merge_file, r, dirbuf_depth, NULL)) { rprintf(FERROR, "merge-file name overflows: %s\n", - safe_fname(merge_file)); + merge_file); return NULL; } } else { strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN); clean_fname(fn, 1); } - + fn_len = strlen(fn); if (fn == buf) goto done; if (dirbuf_len + fn_len >= MAXPATHLEN) { - rprintf(FERROR, "merge-file name overflows: %s\n", - safe_fname(fn)); + rprintf(FERROR, "merge-file name overflows: %s\n", fn); return NULL; } memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip); @@ -488,71 +495,69 @@ void pop_local_filters(void *mem) static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir) { - char *p, full_name[MAXPATHLEN]; - int match_start = 0; + int slash_handling, str_cnt = 0, anchored_match = 0; int ret_match = ex->match_flags & MATCHFLG_NEGATE ? 0 : 1; - char *pattern = ex->pattern; + char *p, *pattern = ex->pattern; + const char *strings[16]; /* more than enough */ if (!*name) return 0; - /* If the pattern does not have any slashes AND it does not have - * a "**" (which could match a slash), then we just match the - * name portion of the path. */ if (!ex->u.slash_cnt && !(ex->match_flags & MATCHFLG_WILD2)) { + /* If the pattern does not have any slashes AND it does + * not have a "**" (which could match a slash), then we + * just match the name portion of the path. */ if ((p = strrchr(name,'/')) != NULL) name = p+1; - } - else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/' + } else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/' && curr_dir_len > module_dirlen + 1) { - pathjoin(full_name, sizeof full_name, - curr_dir + module_dirlen + 1, name); - name = full_name; + /* If we're matching against an absolute-path pattern, + * we need to prepend our full path info. */ + strings[str_cnt++] = curr_dir + module_dirlen + 1; + strings[str_cnt++] = "/"; + } else if (ex->match_flags & MATCHFLG_WILD2_PREFIX && *name != '/') { + /* Allow "**"+"/" to match at the start of the string. */ + strings[str_cnt++] = "/"; } - - if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir) + strings[str_cnt++] = name; + if (name_is_dir) { + /* Allow a trailing "/"+"***" to match the directory. */ + if (ex->match_flags & MATCHFLG_WILD3_SUFFIX) + strings[str_cnt++] = "/"; + } else if (ex->match_flags & MATCHFLG_DIRECTORY) return !ret_match; + strings[str_cnt] = NULL; if (*pattern == '/') { - match_start = 1; + anchored_match = 1; pattern++; - if (*name == '/') - name++; + if (strings[0][0] == '/') + strings[0]++; } - if (ex->match_flags & MATCHFLG_WILD) { + if (!anchored_match && ex->u.slash_cnt + && !(ex->match_flags & MATCHFLG_WILD2)) { /* A non-anchored match with an infix slash and no "**" * needs to match the last slash_cnt+1 name elements. */ - if (!match_start && ex->u.slash_cnt - && !(ex->match_flags & MATCHFLG_WILD2)) { - int cnt = ex->u.slash_cnt + 1; - for (p = name + strlen(name) - 1; p >= name; p--) { - if (*p == '/' && !--cnt) - break; - } - name = p+1; - } - if (wildmatch(pattern, name)) + slash_handling = ex->u.slash_cnt + 1; + } else if (!anchored_match && !(ex->match_flags & MATCHFLG_WILD2_PREFIX) + && ex->match_flags & MATCHFLG_WILD2) { + /* A non-anchored match with an infix or trailing "**" (but not + * a prefixed "**") needs to try matching after every slash. */ + slash_handling = -1; + } else { + /* The pattern matches only at the start of the path or name. */ + slash_handling = 0; + } + + if (ex->match_flags & MATCHFLG_WILD) { + if (wildmatch_array(pattern, strings, slash_handling)) return ret_match; - if (ex->match_flags & MATCHFLG_WILD2_PREFIX) { - /* If the **-prefixed pattern has a '/' as the next - * character, then try to match the rest of the - * pattern at the root. */ - if (pattern[2] == '/' && wildmatch(pattern+3, name)) - return ret_match; - } - else if (!match_start && ex->match_flags & MATCHFLG_WILD2) { - /* A non-anchored match with an infix or trailing "**" - * (but not a prefixed "**") needs to try matching - * after every slash. */ - while ((name = strchr(name, '/')) != NULL) { - name++; - if (wildmatch(pattern, name)) - return ret_match; - } - } - } else if (match_start) { - if (strcmp(name,pattern) == 0) + } else if (str_cnt > 1) { + if (litmatch_array(pattern, strings, slash_handling)) + return ret_match; + } else if (anchored_match) { + if (strcmp(strings[0], pattern) == 0) return ret_match; } else { int l1 = strlen(name); @@ -577,11 +582,13 @@ static void report_filter_result(char const *name, * case we add it back in here. */ if (verbose >= 2) { - rprintf(FINFO, "[%s] %scluding %s %s because of pattern %s%s%s\n", - who_am_i(), - ent->match_flags & MATCHFLG_INCLUDE ? "in" : "ex", - name_is_dir ? "directory" : "file", name, ent->pattern, - ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type); + static char *actions[2][2] + = { {"show", "hid"}, {"risk", "protect"} }; + const char *w = who_am_i(); + rprintf(FINFO, "[%s] %sing %s %s because of pattern %s%s%s\n", + w, actions[*w!='s'][!(ent->match_flags&MATCHFLG_INCLUDE)], + name_is_dir ? "directory" : "file", name, ent->pattern, + ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type); } } @@ -674,8 +681,7 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, } else if (*s == '+' && s[1] == ' ') { new_mflags |= MATCHFLG_INCLUDE; s += 2; - } - if (*s == '!') + } else if (*s == '!') new_mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */ } else { char ch = 0, *mods = ""; @@ -716,7 +722,6 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, if ((s = RULE_STRCMP(s, "show")) != NULL) ch = 'S'; break; - default: ch = *s; if (s[1] == ',') @@ -757,7 +762,7 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, mods = NULL; break; default: - rprintf(FERROR, "Unknown filter rule: %s\n", p); + rprintf(FERROR, "Unknown filter rule: `%s'\n", p); exit_cleanup(RERR_SYNTAX); } while (mods && *++s && *s != ' ' && *s != '_') { @@ -829,7 +834,8 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, len = strlen((char*)s); if (new_mflags & MATCHFLG_CLEAR_LIST) { - if (!(xflags & XFLG_OLD_PREFIXES) && len) { + if (!(mflags & MATCHFLG_NO_PREFIXES) + && !(xflags & XFLG_OLD_PREFIXES) && len) { rprintf(FERROR, "'!' rule has trailing characters: %s\n", p); exit_cleanup(RERR_SYNTAX); @@ -847,7 +853,7 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, } -static char default_cvsignore[] = +static char default_cvsignore[] = /* These default ignored items come from the CVS manual. */ "RCS SCCS CVS CVS.adm RCSLOG cvslog.* tags TAGS" " .make.state .nse_depinfo *~ #* .#* ,* _$* *$" @@ -855,7 +861,7 @@ static char default_cvsignore[] = " *.a *.olb *.o *.obj *.so *.exe" " *.Z *.elc *.ln core" /* The rest we added to suit ourself. */ - " .svn/"; + " .svn/ .bzr/"; static void get_cvs_excludes(uint32 mflags) { @@ -956,7 +962,7 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname, uint32 mflags, int xflags) { FILE *fp; - char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */ + char line[BIGPATHBUFLEN]; char *eob = line + sizeof line - 1; int word_split = mflags & MATCHFLG_WORD_SPLIT; @@ -978,7 +984,7 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname, if (verbose > 2) { rprintf(FINFO, "[%s] parse_filter_file(%s,%x,%x)%s\n", - who_am_i(), safe_fname(fname), mflags, xflags, + who_am_i(), fname, mflags, xflags, fp ? "" : " [not found]"); } @@ -987,7 +993,7 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname, rsyserr(FERROR, errno, "failed to open %sclude file %s", mflags & MATCHFLG_INCLUDE ? "in" : "ex", - safe_fname(fname)); + fname); exit_cleanup(RERR_FILEIO); } return; @@ -999,8 +1005,10 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname, int ch, overflow = 0; while (1) { if ((ch = getc(fp)) == EOF) { - if (ferror(fp) && errno == EINTR) + if (ferror(fp) && errno == EINTR) { + clearerr(fp); continue; + } break; } if (word_split && isspace(ch)) @@ -1034,7 +1042,7 @@ char *get_rule_prefix(int match_flags, const char *pat, int for_xfer, { static char buf[MAX_RULE_PREFIX+1]; char *op = buf; - int legal_len = for_xfer && protocol_version < 29 ? 1 : MAX_RULE_PREFIX; + int legal_len = for_xfer && protocol_version < 29 ? 1 : MAX_RULE_PREFIX-1; if (match_flags & MATCHFLG_PERDIR_MERGE) { if (legal_len == 1) @@ -1071,10 +1079,10 @@ char *get_rule_prefix(int match_flags, const char *pat, int for_xfer, && (!for_xfer || protocol_version >= 29 || (delete_excluded && am_sender))) *op++ = 'r'; - if (legal_len) - *op++ = ' '; if (op - buf > legal_len) return NULL; + if (legal_len) + *op++ = ' '; *op = '\0'; if (plen_ptr) *plen_ptr = op - buf; @@ -1107,9 +1115,9 @@ static void send_rules(int f_out, struct filter_list_struct *flp) continue; if (ent->match_flags & MATCHFLG_CVS_IGNORE && !(ent->match_flags & MATCHFLG_MERGE_FILE)) { - int f = am_sender || protocol_version < 29 ? f_out : -1; + int f = am_sender || protocol_version < 29 ? f_out : -2; send_rules(f, &cvs_filter_list); - if (f >= 0) + if (f == f_out) continue; } p = get_rule_prefix(ent->match_flags, ent->pattern, 1, &plen); @@ -1137,8 +1145,8 @@ static void send_rules(int f_out, struct filter_list_struct *flp) /* This is only called by the client. */ void send_filter_list(int f_out) { - int receiver_wants_list = delete_mode - && (!delete_excluded || protocol_version >= 29); + int receiver_wants_list = prune_empty_dirs + || (delete_mode && (!delete_excluded || protocol_version >= 29)); if (local_server || (am_sender && !receiver_wants_list)) f_out = -1; @@ -1169,16 +1177,17 @@ void send_filter_list(int f_out) /* This is only called by the server. */ void recv_filter_list(int f_in) { - char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */ + char line[BIGPATHBUFLEN]; int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES; - int receiver_wants_list = delete_mode - && (!delete_excluded || protocol_version >= 29); + int receiver_wants_list = prune_empty_dirs + || (delete_mode + && (!delete_excluded || protocol_version >= 29)); unsigned int len; if (!local_server && (am_sender || receiver_wants_list)) { while ((len = read_int(f_in)) != 0) { if (len >= sizeof line) - overflow("recv_rules"); + overflow_exit("recv_rules"); read_sbuf(f_in, line, len); parse_rule(&filter_list, line, 0, xflags); }