--- embedaddon/rsync/exclude.c 2016/11/01 09:54:32 1.1.1.3 +++ embedaddon/rsync/exclude.c 2021/03/17 00:32:36 1.1.1.4 @@ -4,7 +4,7 @@ * Copyright (C) 1996-2001 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2002 Martin Pool - * Copyright (C) 2003-2015 Wayne Davison + * Copyright (C) 2003-2020 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 @@ -21,6 +21,7 @@ */ #include "rsync.h" +#include "ifuncs.h" extern int am_server; extern int am_sender; @@ -44,9 +45,14 @@ filter_rule_list filter_list = { .debug_type = "" }; filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" }; filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" }; -/* Need room enough for ":MODS " prefix plus some room to grow. */ -#define MAX_RULE_PREFIX (16) +filter_rule *last_hit_filter_rule; +int saw_xattr_filter = 0; + +/* Need room enough for ":MODS " prefix, which can now include + * chmod/user/group values. */ +#define MAX_RULE_PREFIX (256) + #define SLASH_WILD3_SUFFIX "/***" /* The dirbuf is set by push_local_filters() to the current subdirectory @@ -124,8 +130,27 @@ static void teardown_mergelist(filter_rule *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(filter_rule *ex) { + if (ex->rflags & FILTRULE_CHMOD) + unref_filter_chmod(ex->chmod); if (ex->rflags & FILTRULE_PERDIR_MERGE) teardown_mergelist(ex); free(ex->pattern); @@ -197,8 +222,7 @@ static void add_rule(filter_rule_list *listp, const ch } else suf_len = 0; - if (!(rule->pattern = new_array(char, pre_len + pat_len + suf_len + 1))) - out_of_memory("add_rule"); + rule->pattern = new_array(char, pre_len + pat_len + suf_len + 1); if (pre_len) { memcpy(rule->pattern, dirbuf + module_dirlen, pre_len); for (cp = rule->pattern; cp < rule->pattern + pre_len; cp++) { @@ -259,19 +283,14 @@ static void add_rule(filter_rule_list *listp, const ch } } - if (!(lp = new_array0(filter_rule_list, 1))) - out_of_memory("add_rule"); + lp = new_array0(filter_rule_list, 1); if (asprintf(&lp->debug_type, " [per-dir %s]", cp) < 0) out_of_memory("add_rule"); rule->u.mergelist = lp; if (mergelist_cnt == mergelist_size) { mergelist_size += 5; - mergelist_parents = realloc_array(mergelist_parents, - filter_rule *, - mergelist_size); - if (!mergelist_parents) - out_of_memory("add_rule"); + mergelist_parents = realloc_array(mergelist_parents, filter_rule *, mergelist_size); } if (DEBUG_GTE(FILTER, 2)) { rprintf(FINFO, "[%s] activating mergelist #%d%s\n", @@ -495,8 +514,6 @@ void *push_local_filters(const char *dir, unsigned int push = (struct local_filter_state *)new_array(char, sizeof (struct local_filter_state) + (mergelist_cnt-1) * sizeof (filter_rule_list)); - if (!push) - out_of_memory("push_local_filters"); push->mergelist_cnt = mergelist_cnt; for (i = 0; i < mergelist_cnt; i++) { @@ -622,7 +639,7 @@ void change_local_filter_dir(const char *dname, int dl filt_array[cur_depth] = push_local_filters(dname, dlen); } -static int rule_matches(const char *fname, filter_rule *ex, int name_is_dir) +static int rule_matches(const char *fname, filter_rule *ex, int name_flags) { int slash_handling, str_cnt = 0, anchored_match = 0; int ret_match = ex->rflags & FILTRULE_NEGATE ? 0 : 1; @@ -633,6 +650,9 @@ static int rule_matches(const char *fname, filter_rule if (!*name) return 0; + if (!(name_flags & NAME_IS_XATTR) ^ !(ex->rflags & FILTRULE_XATTR)) + return 0; + if (!ex->u.slash_cnt && !(ex->rflags & FILTRULE_WILD2)) { /* If the pattern does not have any slashes AND it does * not have a "**" (which could match a slash), then we @@ -650,7 +670,7 @@ static int rule_matches(const char *fname, filter_rule strings[str_cnt++] = "/"; } strings[str_cnt++] = name; - if (name_is_dir) { + if (name_flags & NAME_IS_DIR) { /* Allow a trailing "/"+"***" to match the directory. */ if (ex->rflags & FILTRULE_WILD3_SUFFIX) strings[str_cnt++] = "/"; @@ -685,16 +705,15 @@ static int rule_matches(const char *fname, filter_rule if (litmatch_array(pattern, strings, slash_handling)) return ret_match; } else if (anchored_match) { - if (strcmp(name, pattern) == 0) + if (ic_strEQ(name, pattern)) return ret_match; } else { int l1 = strlen(name); int l2 = strlen(pattern); - if (l2 <= l1 && - strcmp(name+(l1-l2),pattern) == 0 && - (l1==l2 || name[l1-(l2+1)] == '/')) { + if (l2 <= l1 + && ic_strEQ(name + (l1-l2), pattern) + && (l1 == l2 || name[l1 - (l2+1)] == '/')) return ret_match; - } } return !ret_match; @@ -702,7 +721,7 @@ static int rule_matches(const char *fname, filter_rule static void report_filter_result(enum logcode code, char const *name, filter_rule const *ent, - int name_is_dir, const char *type) + int name_flags, const char *type) { /* If a trailing slash is present to match only directories, * then it is stripped out by add_rule(). So as a special @@ -712,17 +731,46 @@ static void report_filter_result(enum logcode code, ch static char *actions[2][2] = { {"show", "hid"}, {"risk", "protect"} }; const char *w = who_am_i(); + const char *t = name_flags & NAME_IS_XATTR ? "xattr" + : name_flags & NAME_IS_DIR ? "directory" + : "file"; rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n", w, actions[*w!='s'][!(ent->rflags & FILTRULE_INCLUDE)], - name_is_dir ? "directory" : "file", name, ent->pattern, + t, name, ent->pattern, ent->rflags & FILTRULE_DIRECTORY ? "/" : "", type); } } +/* This function is used to check if a file should be included/excluded + * from the list of files based on its name and type etc. The value of + * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. + * "last_hit_filter_rule" will be set to the operative filter, or NULL if none. */ + +int name_is_excluded(const char *fname, int name_flags, int filter_level) +{ + if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, name_flags) < 0) { + if (!(name_flags & NAME_IS_XATTR)) + errno = ENOENT; + return 1; + } + + /* Don't leave a daemon include in last_hit_filter_rule. */ + last_hit_filter_rule = NULL; + + if (filter_level != ALL_FILTERS) + return 0; + + if (filter_list.head && check_filter(&filter_list, FINFO, fname, name_flags) < 0) + return 1; + + return 0; +} + /* 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. */ + * exclude list, 1 if it is included, and 0 if it was not matched. + * Sets last_hit_filter_rule to the filter that was hit, or NULL if none. */ int check_filter(filter_rule_list *listp, enum logcode code, - const char *name, int name_is_dir) + const char *name, int name_flags) { filter_rule *ent; @@ -730,26 +778,25 @@ int check_filter(filter_rule_list *listp, enum logcode if (ignore_perishable && ent->rflags & FILTRULE_PERISHABLE) continue; if (ent->rflags & FILTRULE_PERDIR_MERGE) { - int rc = check_filter(ent->u.mergelist, code, name, - name_is_dir); + int rc = check_filter(ent->u.mergelist, code, name, name_flags); if (rc) return rc; continue; } if (ent->rflags & FILTRULE_CVS_IGNORE) { - int rc = check_filter(&cvs_filter_list, code, name, - name_is_dir); + int rc = check_filter(&cvs_filter_list, code, name, name_flags); if (rc) return rc; continue; } - if (rule_matches(name, ent, name_is_dir)) { - report_filter_result(code, name, ent, name_is_dir, - listp->debug_type); + if (rule_matches(name, ent, name_flags)) { + report_filter_result(code, name, ent, name_flags, listp->debug_type); + last_hit_filter_rule = ent; return ent->rflags & FILTRULE_INCLUDE ? 1 : -1; } } + last_hit_filter_rule = NULL; return 0; } @@ -766,9 +813,45 @@ static const uchar *rule_strcmp(const uchar *str, cons 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; + + chmod = new(struct filter_chmod_struct); + chmod->ref_cnt = 1; + chmod->modestr = modestr; + chmod->modes = modes; + return chmod; +} + #define FILTRULES_FROM_CONTAINER (FILTRULE_ABS_PATH | FILTRULE_INCLUDE \ | FILTRULE_DIRECTORY | FILTRULE_NEGATE \ - | FILTRULE_PERISHABLE) + | FILTRULE_PERISHABLE | FILTRULES_ATTRS) /* Gets the next include/exclude rule from *rulestr_ptr and advances * *rulestr_ptr to point beyond it. Stores the pattern's start (within @@ -783,6 +866,7 @@ static filter_rule *parse_rule_tok(const char **rulest const char **pat_ptr, unsigned int *pat_len_ptr) { const uchar *s = (const uchar *)*rulestr_ptr; + char *val; filter_rule *rule; unsigned int len; @@ -796,12 +880,17 @@ static filter_rule *parse_rule_tok(const char **rulest if (!*s) return NULL; - if (!(rule = new0(filter_rule))) - out_of_memory("parse_rule_tok"); + rule = new0(filter_rule); /* Inherit from the template. Don't inherit FILTRULES_SIDES; we check * that later. */ rule->rflags = template->rflags & FILTRULES_FROM_CONTAINER; + if (template->rflags & FILTRULE_CHMOD) + rule->chmod = ref_filter_chmod(template->chmod); + if (template->rflags & FILTRULE_FORCE_OWNER) + rule->force_uid = template->force_uid; + if (template->rflags & FILTRULE_FORCE_GROUP) + rule->force_gid = template->force_gid; /* Figure out what kind of a filter rule "s" is pointing at. Note * that if FILTRULE_NO_PREFIXES is set, the rule is either an include @@ -947,11 +1036,63 @@ static filter_rule *parse_rule_tok(const char **rulest goto invalid; rule->rflags |= FILTRULE_EXCLUDE_SELF; break; + case 'g': { + gid_t gid; + + if (!(val = grab_paren_value(&s))) + goto invalid; + if (group_to_gid(val, &gid, True)) { + rule->rflags |= FILTRULE_FORCE_GROUP; + rule->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 (rule->rflags & FILTRULE_CHMOD) + unref_filter_chmod(rule->chmod); + rule->rflags |= FILTRULE_CHMOD; + rule->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 (!(rule->rflags & FILTRULE_MERGE_FILE)) goto invalid; rule->rflags |= FILTRULE_NO_INHERIT; break; + case 'o': { + uid_t uid; + + if (!(val = grab_paren_value(&s))) + goto invalid; + if (user_to_uid(val, &uid, True)) { + rule->rflags |= FILTRULE_FORCE_OWNER; + rule->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': rule->rflags |= FILTRULE_PERISHABLE; break; @@ -970,6 +1111,10 @@ static filter_rule *parse_rule_tok(const char **rulest goto invalid; rule->rflags |= FILTRULE_WORD_SPLIT; break; + case 'x': + rule->rflags |= FILTRULE_XATTR; + saw_xattr_filter = 1; + break; } } if (*s) @@ -1022,16 +1167,6 @@ static filter_rule *parse_rule_tok(const char **rulest return rule; } -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/ .git/ .hg/ .bzr/"; - static void get_cvs_excludes(uint32 rflags) { static int initialized = 0; @@ -1041,7 +1176,7 @@ static void get_cvs_excludes(uint32 rflags) return; initialized = 1; - parse_filter_str(&cvs_filter_list, default_cvsignore, + parse_filter_str(&cvs_filter_list, default_cvsignore(), rule_template(rflags | (protocol_version >= 30 ? FILTRULE_PERISHABLE : 0)), 0); @@ -1105,8 +1240,7 @@ void parse_filter_str(filter_rule_list *listp, const c const char *name; filter_rule *excl_self; - if (!(excl_self = new0(filter_rule))) - out_of_memory("parse_filter_str"); + excl_self = new0(filter_rule); /* Find the beginning of the basename and add an exclude for it. */ for (name = pat + pat_len; name > pat && name[-1] != '/'; name--) {} add_rule(listp, name, (pat + pat_len) - name, excl_self, 0); @@ -1257,6 +1391,8 @@ char *get_rule_prefix(filter_rule *rule, const char *p } if (rule->rflags & FILTRULE_EXCLUDE_SELF) *op++ = 'e'; + if (rule->rflags & FILTRULE_XATTR) + *op++ = 'x'; if (rule->rflags & FILTRULE_SENDER_SIDE && (!for_xfer || protocol_version >= 29)) *op++ = 's'; @@ -1270,6 +1406,23 @@ char *get_rule_prefix(filter_rule *rule, const char *p else if (am_sender) return NULL; } + if (rule->rflags & FILTRULES_ATTRS) { + if (!for_xfer || protocol_version >= 31) { + if (rule->rflags & FILTRULE_CHMOD) + if (!snappendf(&op, (buf + sizeof buf) - op, + "m(%s)", rule->chmod->modestr)) + return NULL; + if (rule->rflags & FILTRULE_FORCE_OWNER) + if (!snappendf(&op, (buf + sizeof buf) - op, + "o(%u)", (unsigned)rule->force_uid)) + return NULL; + if (rule->rflags & FILTRULE_FORCE_GROUP) + if (!snappendf(&op, (buf + sizeof buf) - op, + "g(%u)", (unsigned)rule->force_gid)) + return NULL; + } else if (!am_sender) + return NULL; + } if (op - buf > legal_len) return NULL; if (legal_len) @@ -1375,8 +1528,7 @@ void recv_filter_list(int f_in) char line[BIGPATHBUFLEN]; int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES; int receiver_wants_list = prune_empty_dirs - || (delete_mode - && (!delete_excluded || protocol_version >= 29)); + || (delete_mode && (!delete_excluded || protocol_version >= 29)); unsigned int len; if (!local_server && (am_sender || receiver_wants_list)) {