X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/b39f79296a4cea240c659cb7d264035198aa6d44..6694ead8b8bc8ce69fbe98fd68659eaff4aa8bfd:/exclude.c diff --git a/exclude.c b/exclude.c index 9a7e0daa..19de82dc 100644 --- a/exclude.c +++ b/exclude.c @@ -44,12 +44,11 @@ struct filter_list_struct filter_list = { .debug_type = "" }; struct filter_list_struct cvs_filter_list = { .debug_type = " [global CVS]" }; struct filter_list_struct daemon_filter_list = { .debug_type = " [daemon]" }; -/* Need room enough for ":MODS " prefix plus some room to grow. */ -#define MAX_RULE_PREFIX (16) +struct filter_struct *last_hit_filter; -#define MODIFIERS_MERGE_FILE "-+Cenw" -#define MODIFIERS_INCL_EXCL "/!Crsp" -#define MODIFIERS_HIDE_PROTECT "/!p" +/* Need room enough for ":MODS " prefix, which can now include + * chmod/user/group values. */ +#define MAX_RULE_PREFIX (256) #define SLASH_WILD3_SUFFIX "/***" @@ -122,8 +121,27 @@ static void teardown_mergelist(struct filter_struct *ex) mergelist_cnt--; } +static struct filter_chmod_struct *ref_filter_chmod(struct filter_chmod_struct *chmod) +{ + chmod->ref_cnt++; + assert(chmod->ref_cnt != 0); /* Catch overflow. */ + return chmod; +} + +static void unref_filter_chmod(struct filter_chmod_struct *chmod) +{ + chmod->ref_cnt--; + if (chmod->ref_cnt == 0) { + free(chmod->modestr); + free_chmod_mode(chmod->modes); + free(chmod); + } +} + static void free_filter(struct filter_struct *ex) { + if (ex->flags & MATCHFLG_CHMOD) + unref_filter_chmod(ex->chmod); free(ex->pattern); free(ex); } @@ -732,6 +750,7 @@ static void report_filter_result(enum logcode code, char const *name, /* * Return -1 if file "name" is defined to be excluded by the specified * exclude list, 1 if it is included, and 0 if it was not matched. + * Sets last_hit_filter to the filter that was hit, or NULL if none. */ int check_filter(struct filter_list_struct *listp, enum logcode code, const char *name, int name_is_dir) @@ -758,10 +777,12 @@ int check_filter(struct filter_list_struct *listp, enum logcode code, if (rule_matches(name, ent, name_is_dir)) { report_filter_result(code, name, ent, name_is_dir, listp->debug_type); + last_hit_filter = ent; return ent->flags & MATCHFLG_INCLUDE ? 1 : -1; } } + last_hit_filter = NULL; return 0; } @@ -778,9 +799,46 @@ static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len return NULL; } +static char *grab_paren_value(const uchar **s_ptr) +{ + const uchar *start, *end; + int val_sz; + char *val; + + if ((*s_ptr)[1] != '(') + return NULL; + start = (*s_ptr) + 2; + + for (end = start; *end != ')'; end++) + if (!*end || *end == ' ' || *end == '_') + return NULL; + + val_sz = end - start + 1; + val = new_array(char, val_sz); + strlcpy(val, (const char *)start, val_sz); + *s_ptr = end; /* remember ++s in parse_rule_tok */ + return val; +} + +static struct filter_chmod_struct *make_chmod_struct(char *modestr) +{ + struct filter_chmod_struct *chmod; + struct chmod_mode_struct *modes = NULL; + + if (!parse_chmod(modestr, &modes)) + return NULL; + + if (!(chmod = new(struct filter_chmod_struct))) + out_of_memory("make_chmod_struct"); + chmod->ref_cnt = 1; + chmod->modestr = modestr; + chmod->modes = modes; + return chmod; +} + #define MATCHFLGS_FROM_CONTAINER (MATCHFLG_ABS_PATH | MATCHFLG_INCLUDE \ | MATCHFLG_DIRECTORY | MATCHFLG_NEGATE \ - | MATCHFLG_PERISHABLE) + | MATCHFLG_PERISHABLE | MATCHFLGS_ATTRS) /* Gets the next include/exclude rule from *rulestr_ptr and advances * *rulestr_ptr to point beyond it. Stores the pattern's start (within @@ -795,6 +853,7 @@ static struct filter_struct *parse_rule_tok(const char **rulestr_ptr, const char **pat_ptr, unsigned int *pat_len_ptr) { const uchar *s = (const uchar *)*rulestr_ptr; + char *val; struct filter_struct *filter; unsigned int len; @@ -814,6 +873,12 @@ static struct filter_struct *parse_rule_tok(const char **rulestr_ptr, /* Inherit from the template. Don't inherit MATCHFLGS_SIDES; we check * that later. */ filter->flags = template->flags & MATCHFLGS_FROM_CONTAINER; + if (template->flags & MATCHFLG_CHMOD) + filter->chmod = ref_filter_chmod(template->chmod); + if (template->flags & MATCHFLG_FORCE_OWNER) + filter->force_uid = template->force_uid; + if (template->flags & MATCHFLG_FORCE_GROUP) + filter->force_gid = template->force_gid; /* Figure out what kind of a filter rule "s" is pointing at. Note * that if MATCHFLG_NO_PREFIXES is set, the rule is either an include @@ -959,11 +1024,63 @@ static struct filter_struct *parse_rule_tok(const char **rulestr_ptr, goto invalid; filter->flags |= MATCHFLG_EXCLUDE_SELF; break; + case 'g': { + gid_t gid; + + if (!(val = grab_paren_value(&s))) + goto invalid; + if (name_to_gid(val, &gid, True)) { + filter->flags |= MATCHFLG_FORCE_GROUP; + filter->force_gid = gid; + } else { + rprintf(FERROR, + "unknown group '%s' in filter rule: %s\n", + val, *rulestr_ptr); + exit_cleanup(RERR_SYNTAX); + } + free(val); + break; + } + case 'm': { + struct filter_chmod_struct *chmod; + + if (!(val = grab_paren_value(&s))) + goto invalid; + if ((chmod = make_chmod_struct(val))) { + if (filter->flags & MATCHFLG_CHMOD) + unref_filter_chmod(filter->chmod); + filter->flags |= MATCHFLG_CHMOD; + filter->chmod = chmod; + } else { + rprintf(FERROR, + "unparseable chmod string '%s' in filter rule: %s\n", + val, *rulestr_ptr); + exit_cleanup(RERR_SYNTAX); + } + break; + } case 'n': if (!(filter->flags & MATCHFLG_MERGE_FILE)) goto invalid; filter->flags |= MATCHFLG_NO_INHERIT; break; + case 'o': { + uid_t uid; + + if (!(val = grab_paren_value(&s))) + goto invalid; + if (name_to_uid(val, &uid, True)) { + filter->flags |= MATCHFLG_FORCE_OWNER; + filter->force_uid = uid; + } else { + rprintf(FERROR, + "unknown user '%s' in filter rule: %s\n", + val, *rulestr_ptr); + exit_cleanup(RERR_SYNTAX); + } + free(val); + break; + } case 'p': filter->flags |= MATCHFLG_PERISHABLE; break; @@ -1296,6 +1413,23 @@ char *get_rule_prefix(struct filter_struct *filter, const char *pat, int for_xfe else if (am_sender) return NULL; } + if (filter->flags & MATCHFLGS_ATTRS) { + if (!for_xfer || protocol_version >= 31) { + if (filter->flags & MATCHFLG_CHMOD) + if (!snappendf(&op, (buf + sizeof buf) - op, + "m(%s)", filter->chmod->modestr)) + return NULL; + if (filter->flags & MATCHFLG_FORCE_OWNER) + if (!snappendf(&op, (buf + sizeof buf) - op, + "o(%u)", (unsigned)filter->force_uid)) + return NULL; + if (filter->flags & MATCHFLG_FORCE_GROUP) + if (!snappendf(&op, (buf + sizeof buf) - op, + "g(%u)", (unsigned)filter->force_gid)) + return NULL; + } else if (!am_sender) + return NULL; + } if (op - buf > legal_len) return NULL; if (legal_len)