X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/d09e800a0e7028c35b811a73c388b65321150784..bec617b934dc2ef90b7acd1c7ef4b5db74821e91:/exclude.c diff --git a/exclude.c b/exclude.c index eb6c49f1..59b4ef52 100644 --- a/exclude.c +++ b/exclude.c @@ -33,6 +33,7 @@ extern int eol_nulls; extern int list_only; extern int recurse; extern int io_error; +extern int local_server; extern int delete_mode; extern int delete_excluded; extern int cvs_exclude; @@ -52,7 +53,8 @@ struct filter_list_struct server_filter_list = { 0, 0, " [server]" }; #define MAX_RULE_PREFIX (16) #define MODIFIERS_MERGE_FILE "-+Cenw" -#define MODIFIERS_INCL_EXCL "/!C" +#define MODIFIERS_INCL_EXCL "/!Crs" +#define MODIFIERS_HIDE_PROTECT "/!" /* The dirbuf is set by push_local_filters() to the current subdirectory * relative to curr_dir that is being processed. The path always has a @@ -131,6 +133,19 @@ 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 + * needs to be filtered now, not post-filtered later. */ + if (xflags & XFLG_ANCHORED2ABS) { + uint32 mf = mflags & (MATCHFLG_RECEIVER_SIDE|MATCHFLG_SENDER_SIDE); + if (am_sender) { + if (mf == MATCHFLG_RECEIVER_SIDE) + return; + } else { + if (mf == MATCHFLG_SENDER_SIDE) + return; + } + } + if (!(ret = new(struct filter_struct))) out_of_memory("add_rule"); memset(ret, 0, sizeof ret[0]); @@ -277,7 +292,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", - merge_file); + safe_fname(merge_file)); return NULL; } } else { @@ -290,7 +305,8 @@ 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", fn); + rprintf(FERROR, "merge-file name overflows: %s\n", + safe_fname(fn)); return NULL; } memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip); @@ -603,6 +619,18 @@ int check_filter(struct filter_list_struct *listp, char *name, int name_is_dir) return 0; } +#define RULE_STRCMP(s,r) rule_strcmp((s), (r), sizeof (r) - 1) + +static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len) +{ + if (strncmp((char*)str, rule, rule_len) != 0) + return NULL; + if (isspace(str[rule_len]) || str[rule_len] == '_' || !str[rule_len]) + return str + rule_len - 1; + if (str[rule_len] == ',') + return str + rule_len; + return NULL; +} /* Get the next include/exclude arg from the string. The token will not * be '\0' terminated, so use the returned length to limit the string. @@ -637,7 +665,7 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, * for old include/exclude patterns where just "+ " and "- " are * allowed as optional prefixes. */ if (mflags & MATCHFLG_NO_PREFIXES) { - if (*s == '!') + if (*s == '!' && mflags & MATCHFLG_CVS_IGNORE) new_mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */ } else if (xflags & XFLG_OLD_PREFIXES) { if (*s == '-' && s[1] == ' ') { @@ -646,12 +674,54 @@ 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 *mods = ""; + char ch = 0, *mods = ""; switch (*s) { + case 'c': + if ((s = RULE_STRCMP(s, "clear")) != NULL) + ch = '!'; + break; + case 'd': + if ((s = RULE_STRCMP(s, "dir-merge")) != NULL) + ch = ':'; + break; + case 'e': + if ((s = RULE_STRCMP(s, "exclude")) != NULL) + ch = '-'; + break; + case 'h': + if ((s = RULE_STRCMP(s, "hide")) != NULL) + ch = 'H'; + break; + case 'i': + if ((s = RULE_STRCMP(s, "include")) != NULL) + ch = '+'; + break; + case 'm': + if ((s = RULE_STRCMP(s, "merge")) != NULL) + ch = '.'; + break; + case 'p': + if ((s = RULE_STRCMP(s, "protect")) != NULL) + ch = 'P'; + break; + case 'r': + if ((s = RULE_STRCMP(s, "risk")) != NULL) + ch = 'R'; + break; + case 's': + if ((s = RULE_STRCMP(s, "show")) != NULL) + ch = 'S'; + break; + default: + ch = *s; + if (s[1] == ',') + s++; + break; + } + switch (ch) { case ':': new_mflags |= MATCHFLG_PERDIR_MERGE | MATCHFLG_FINISH_SETUP; @@ -666,12 +736,26 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, case '-': mods = MODIFIERS_INCL_EXCL; break; + case 'S': + new_mflags |= MATCHFLG_INCLUDE; + /* FALL THROUGH */ + case 'H': + new_mflags |= MATCHFLG_SENDER_SIDE; + mods = MODIFIERS_HIDE_PROTECT; + break; + case 'R': + new_mflags |= MATCHFLG_INCLUDE; + /* FALL THROUGH */ + case 'P': + new_mflags |= MATCHFLG_RECEIVER_SIDE; + mods = MODIFIERS_HIDE_PROTECT; + break; case '!': new_mflags |= MATCHFLG_CLEAR_LIST; 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 != '_') { @@ -718,6 +802,12 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, case 'n': new_mflags |= MATCHFLG_NO_INHERIT; break; + case 'r': + new_mflags |= MATCHFLG_RECEIVER_SIDE; + break; + case 's': + new_mflags |= MATCHFLG_SENDER_SIDE; + break; case 'w': new_mflags |= MATCHFLG_WORD_SPLIT; break; @@ -755,6 +845,35 @@ static const char *parse_rule_tok(const char *p, uint32 mflags, int xflags, } +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 *~ #* .#* ,* _$* *$" + " *.old *.bak *.BAK *.orig *.rej .del-*" + " *.a *.olb *.o *.obj *.so *.exe" + " *.Z *.elc *.ln core" + /* The rest we added to suit ourself. */ + " .svn/"; + +static void get_cvs_excludes(uint32 mflags) +{ + char *p, fname[MAXPATHLEN]; + static int initialized = 0; + + if (initialized) + return; + initialized = 1; + + parse_rule(&cvs_filter_list, default_cvsignore, mflags, 0); + + p = module_id >= 0 && lp_use_chroot(module_id) ? "/" : getenv("HOME"); + if (p && pathjoin(fname, MAXPATHLEN, p, ".cvsignore") < MAXPATHLEN) + parse_filter_file(&cvs_filter_list, fname, mflags, 0); + + parse_rule(&cvs_filter_list, getenv("CVSIGNORE"), mflags, 0); +} + + void parse_rule(struct filter_list_struct *listp, const char *pattern, uint32 mflags, int xflags) { @@ -826,7 +945,7 @@ void parse_rule(struct filter_list_struct *listp, const char *pattern, if (new_mflags & MATCHFLG_CVS_IGNORE && !(new_mflags & MATCHFLG_MERGE_FILE)) - get_cvs_excludes(); + get_cvs_excludes(new_mflags); } } @@ -905,15 +1024,15 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname, fclose(fp); } -/* If the "sending" flag is > 0, the prefix is made compatible with the +/* If the "for_xfer" flag is set, the prefix is made compatible with the * current protocol_version (if possible) or a NULL is returned (if not * possible). */ -char *get_rule_prefix(int match_flags, const char *pat, int sending, +char *get_rule_prefix(int match_flags, const char *pat, int for_xfer, unsigned int *plen_ptr) { static char buf[MAX_RULE_PREFIX+1]; char *op = buf; - int legal_len = sending && 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) @@ -927,8 +1046,6 @@ char *get_rule_prefix(int match_flags, const char *pat, int sending, else legal_len = 0; - if (match_flags & MATCHFLG_EXCLUDE_SELF) - *op++ = 'e'; if (match_flags & MATCHFLG_CVS_IGNORE) *op++ = 'C'; else { @@ -943,6 +1060,15 @@ char *get_rule_prefix(int match_flags, const char *pat, int sending, *op++ = '-'; } } + if (match_flags & MATCHFLG_EXCLUDE_SELF) + *op++ = 'e'; + if (match_flags & MATCHFLG_SENDER_SIDE + && (!for_xfer || protocol_version >= 29)) + *op++ = 's'; + if (match_flags & MATCHFLG_RECEIVER_SIDE + && (!for_xfer || protocol_version >= 29 + || (delete_excluded && am_sender))) + *op++ = 'r'; if (op - buf > legal_len) return NULL; if (legal_len) @@ -950,25 +1076,39 @@ char *get_rule_prefix(int match_flags, const char *pat, int sending, *op = '\0'; if (plen_ptr) *plen_ptr = op - buf; - if (op - buf > MAX_RULE_PREFIX) - overflow("get_rule_prefix"); return buf; } static void send_rules(int f_out, struct filter_list_struct *flp) { - struct filter_struct *ent; + struct filter_struct *ent, *prev = NULL; for (ent = flp->head; ent; ent = ent->next) { unsigned int len, plen, dlen; + int elide = 0; char *p; + if (ent->match_flags & MATCHFLG_SENDER_SIDE) + elide = am_sender ? 1 : -1; + if (ent->match_flags & MATCHFLG_RECEIVER_SIDE) + elide = elide ? 0 : am_sender ? -1 : 1; + else if (delete_excluded && !elide) + elide = am_sender ? 1 : -1; + if (elide < 0) { + if (prev) + prev->next = ent->next; + else + flp->head = ent->next; + } else + prev = ent; + if (elide > 0) + continue; if (ent->match_flags & MATCHFLG_CVS_IGNORE && !(ent->match_flags & MATCHFLG_MERGE_FILE)) { - if (am_sender || protocol_version < 29) { - send_rules(f_out, &cvs_filter_list); + int f = am_sender || protocol_version < 29 ? f_out : -2; + send_rules(f, &cvs_filter_list); + if (f == f_out) continue; - } } p = get_rule_prefix(ent->match_flags, ent->pattern, 1, &plen); if (!p) { @@ -976,6 +1116,8 @@ static void send_rules(int f_out, struct filter_list_struct *flp) "filter rules are too modern for remote rsync.\n"); exit_cleanup(RERR_SYNTAX); } + if (f_out < 0) + continue; len = strlen(ent->pattern); dlen = ent->match_flags & MATCHFLG_DIRECTORY ? 1 : 0; if (!(plen + len + dlen)) @@ -987,12 +1129,16 @@ static void send_rules(int f_out, struct filter_list_struct *flp) if (dlen) write_byte(f_out, '/'); } + flp->tail = prev; } /* This is only called by the client. */ void send_filter_list(int f_out) { - if (am_sender && (!delete_mode || delete_excluded)) + int receiver_wants_list = delete_mode + && (!delete_excluded || protocol_version >= 29); + + if (local_server || (am_sender && !receiver_wants_list)) f_out = -1; if (cvs_exclude && am_sender) { if (protocol_version >= 29) @@ -1005,10 +1151,10 @@ void send_filter_list(int f_out) if (list_only == 1 && !recurse) parse_rule(&filter_list, "/*/*", MATCHFLG_NO_PREFIXES, 0); - if (f_out >= 0) { - send_rules(f_out, &filter_list); + send_rules(f_out, &filter_list); + + if (f_out >= 0) write_int(f_out, 0); - } if (cvs_exclude) { if (!am_sender || protocol_version < 29) @@ -1023,51 +1169,26 @@ void recv_filter_list(int f_in) { char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */ int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES; + int receiver_wants_list = delete_mode + && (!delete_excluded || protocol_version >= 29); unsigned int len; - if (am_sender || (delete_mode && !delete_excluded)) { + 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); } } if (cvs_exclude) { - if (am_sender || protocol_version < 29) + if (local_server || am_sender || protocol_version < 29) parse_rule(&filter_list, ":C", 0, 0); - if (am_sender) + if (local_server || am_sender) parse_rule(&filter_list, "-C", 0, 0); } -} - - -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 *~ #* .#* ,* _$* *$" - " *.old *.bak *.BAK *.orig *.rej .del-*" - " *.a *.olb *.o *.obj *.so *.exe" - " *.Z *.elc *.ln core" - /* The rest we added to suit ourself. */ - " .svn/"; - -void get_cvs_excludes(void) -{ - static unsigned cvs_mflags = MATCHFLG_WORD_SPLIT|MATCHFLG_NO_PREFIXES; - char *p, fname[MAXPATHLEN]; - static int initialized = 0; - - if (initialized) - return; - initialized = 1; - - parse_rule(&cvs_filter_list, default_cvsignore, cvs_mflags, 0); - - p = module_id >= 0 && lp_use_chroot(module_id) ? "/" : getenv("HOME"); - if (p && pathjoin(fname, MAXPATHLEN, p, ".cvsignore") < MAXPATHLEN) - parse_filter_file(&cvs_filter_list, fname, cvs_mflags, 0); - parse_rule(&cvs_filter_list, getenv("CVSIGNORE"), cvs_mflags, 0); + if (local_server) /* filter out any rules that aren't for us. */ + send_rules(-1, &filter_list); }