X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/2765f2e4a7293a72869d97bad96ef4ed032ee5c5..d051056f921f465c487a85f9fa29a2324332bc4b:/exclude.c diff --git a/exclude.c b/exclude.c index f2be6a9b..c6d9ec89 100644 --- a/exclude.c +++ b/exclude.c @@ -34,6 +34,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 +47,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 +134,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 +151,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 +175,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; } } @@ -292,7 +303,7 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr, } if (!sanitize_path(fn, merge_file, r, dirbuf_depth)) { rprintf(FERROR, "merge-file name overflows: %s\n", - safe_fname(merge_file)); + merge_file); return NULL; } } else { @@ -305,8 +316,7 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr, 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 +498,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 +585,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); } } @@ -827,7 +837,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); @@ -954,7 +965,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; @@ -976,7 +987,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]"); } @@ -985,7 +996,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; @@ -997,8 +1008,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)) @@ -1105,9 +1118,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 || f_out < 0) + if (f == f_out) continue; } p = get_rule_prefix(ent->match_flags, ent->pattern, 1, &plen); @@ -1135,8 +1148,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; @@ -1167,16 +1180,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); }