The branch, master has been updated via 87bc224 Add a way to specify xattr name filtering. via a4e8b55 Join some lines. via 6265220 Get rid of some superfluous double-quotes in error messages. from 001adf5 Fix extern of preallocated_len w/o SUPPORT_PREALLOCATION.
https://git.samba.org/?p=rsync.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 87bc2240115a2a1beadd098518a17719908da2cb Author: Wayne Davison <way...@samba.org> Date: Sun Jan 22 16:01:45 2017 -0800 Add a way to specify xattr name filtering. commit a4e8b552d6a99373ef1c8cdb075568e755b5f402 Author: Wayne Davison <way...@samba.org> Date: Sun Jan 22 15:44:18 2017 -0800 Join some lines. commit 62652202c46897b8fbce205834c626260e6b9855 Author: Wayne Davison <way...@samba.org> Date: Sun Jan 22 15:42:36 2017 -0800 Get rid of some superfluous double-quotes in error messages. ----------------------------------------------------------------------- Summary of changes: exclude.c | 53 ++++++++++++++++++++++++++++++--------- flist.c | 33 +++--------------------- rsync.h | 5 ++++ rsync.yo | 28 ++++++++++++++++++--- testsuite/xattrs.test | 14 ++++++----- xattrs.c | 69 ++++++++++++++++++++++++++++++--------------------- 6 files changed, 124 insertions(+), 78 deletions(-) Changeset truncated at 500 lines: diff --git a/exclude.c b/exclude.c index 0aaa19b..00988a7 100644 --- a/exclude.c +++ b/exclude.c @@ -44,6 +44,8 @@ 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]" }; +int saw_xattr_filter = 0; + /* Need room enough for ":MODS " prefix plus some room to grow. */ #define MAX_RULE_PREFIX (16) @@ -622,7 +624,7 @@ void change_local_filter_dir(const char *dname, int dlen, int dir_depth) 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 +635,9 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_is_dir) 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 +655,7 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_is_dir) 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++] = "/"; @@ -702,7 +707,7 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_is_dir) 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 +717,40 @@ static void report_filter_result(enum logcode code, char const *name, 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. */ +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; + } + + 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. */ 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,22 +758,19 @@ int check_filter(filter_rule_list *listp, enum logcode code, 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); return ent->rflags & FILTRULE_INCLUDE ? 1 : -1; } } @@ -970,6 +995,10 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr, goto invalid; rule->rflags |= FILTRULE_WORD_SPLIT; break; + case 'x': + rule->rflags |= FILTRULE_XATTR; + saw_xattr_filter = 1; + break; } } if (*s) diff --git a/flist.c b/flist.c index 54ced36..28553fc 100644 --- a/flist.c +++ b/flist.c @@ -243,16 +243,6 @@ int link_stat(const char *path, STRUCT_STAT *stp, int follow_dirlinks) #endif } -static inline int is_daemon_excluded(const char *fname, int is_dir) -{ - if (daemon_filter_list.head - && check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) { - errno = ENOENT; - return 1; - } - return 0; -} - static inline int path_is_daemon_excluded(char *path, int ignore_filename) { if (daemon_filter_list.head) { @@ -279,23 +269,9 @@ static inline int path_is_daemon_excluded(char *path, int ignore_filename) return 0; } -/* 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. */ -static int is_excluded(const char *fname, int is_dir, int filter_level) +static inline int is_excluded(const char *fname, int is_dir, int filter_level) { -#if 0 /* This currently never happens, so avoid a useless compare. */ - if (filter_level == NO_FILTERS) - return 0; -#endif - if (is_daemon_excluded(fname, is_dir)) - return 1; - if (filter_level != ALL_FILTERS) - return 0; - if (filter_list.head - && check_filter(&filter_list, FINFO, fname, is_dir) < 0) - return 1; - return 0; + return name_is_excluded(fname, is_dir ? NAME_IS_DIR : NAME_IS_FILE, filter_level); } static void send_directory(int f, struct file_list *flist, @@ -2268,7 +2244,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[]) memmove(fbuf, fn, len + 1); if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0 - || (name_type != DOTDIR_NAME && is_daemon_excluded(fbuf, S_ISDIR(st.st_mode))) + || (name_type != DOTDIR_NAME && is_excluded(fbuf, S_ISDIR(st.st_mode) != 0, SERVER_FILTERS)) || (relative_paths && path_is_daemon_excluded(fbuf, 1))) { if (errno != ENOENT || missing_args == 0) { /* This is a transfer error, but inhibit deletion @@ -2967,8 +2943,7 @@ static void flist_sort_and_clean(struct file_list *flist, int strip_root) clear_file(fp); } prev_depth = F_DEPTH(file); - if (is_excluded(f_name(file, fbuf), 1, - ALL_FILTERS)) { + if (is_excluded(f_name(file, fbuf), 1, ALL_FILTERS)) { /* Keep dirs through this dir. */ for (j = prev_depth-1; ; j--) { fp = flist->sorted[prev_i]; diff --git a/rsync.h b/rsync.h index 6e1e1dd..1720293 100644 --- a/rsync.h +++ b/rsync.h @@ -856,6 +856,10 @@ struct map_struct { int status; /* first errno from read errors */ }; +#define NAME_IS_FILE (0) /* filter name as a file */ +#define NAME_IS_DIR (1<<0) /* filter name as a dir */ +#define NAME_IS_XATTR (1<<2) /* filter name as an xattr */ + #define FILTRULE_WILD (1<<0) /* pattern has '*', '[', and/or '?' */ #define FILTRULE_WILD2 (1<<1) /* pattern has '**' */ #define FILTRULE_WILD2_PREFIX (1<<2) /* pattern starts with "**" */ @@ -876,6 +880,7 @@ struct map_struct { #define FILTRULE_RECEIVER_SIDE (1<<17)/* rule applies to the receiving side */ #define FILTRULE_CLEAR_LIST (1<<18)/* this item is the "!" token */ #define FILTRULE_PERISHABLE (1<<19)/* perishable if parent dir goes away */ +#define FILTRULE_XATTR (1<<20)/* rule only applies to xattr names */ #define FILTRULES_SIDES (FILTRULE_SENDER_SIDE | FILTRULE_RECEIVER_SIDE) diff --git a/rsync.yo b/rsync.yo index d1e6fdf..d56db43 100644 --- a/rsync.yo +++ b/rsync.yo @@ -1118,9 +1118,27 @@ super-user copies all namespaces except system.*. A normal user only copies the user.* namespace. To be able to backup and restore non-user namespaces as a normal user, see the bf(--fake-super) option. -Note that this option does not copy rsyncs special xattr values (e.g. those -used by bf(--fake-super)) unless you repeat the option (e.g. -XX). This -"copy all xattrs" mode cannot be used with bf(--fake-super). +The above name filtering can be overridden by using one or more filter options +with the bf(x) modifier. When you specify an xattr-affecting filter rule, rsync +requires that you do your own system/user filtering, as well as any additional +filtering for what xattr names are copied and what names are allowed to be +deleted. For example, to skip the system namespace, you could specify: + +quote(--filter='-x system.*') + +To skip all namespaces except the user namespace, you could specify a +negated-user match: + +quote(--filter='-x! user.*') + +To prevent any attributes from being deleted, you could specify a receiver-only +rule that excludes all names: + +quote(--filter='-xr *') + +Note that the bf(-X) option does not copy rsync's special xattr values (e.g. +those used by bf(--fake-super)) unless you repeat the option (e.g. -XX). +This "copy all xattrs" mode cannot be used with bf(--fake-super). dit(bf(--chmod)) This option tells rsync to apply one or more comma-separated "chmod" modes to the permission of the files in the @@ -2926,6 +2944,10 @@ itemization( option's default rules that exclude things like "CVS" and "*.o" are marked as perishable, and will not prevent a directory that was removed on the source from being deleted on the destination. + it() An bf(x) indicates that a rule affects xattr names in xattr copy/delete + operations (and is thus ignored when matching file/dir names). If no + xattr-matching rules are specified, a default xattr filtering rule is + used (see the bf(--xattrs) option). ) manpagesection(MERGE-FILE FILTER RULES) diff --git a/testsuite/xattrs.test b/testsuite/xattrs.test index 06afcba..f7d9a6d 100644 --- a/testsuite/xattrs.test +++ b/testsuite/xattrs.test @@ -127,8 +127,10 @@ esac xls $dirs $files >"$scratchdir/xattrs.txt" +XFILT='-f-x_system.* -f-x_security.*' + # OK, let's try a simple xattr copy. -checkit "$RSYNC -avX $dashH --super . '$chkdir/'" "$fromdir" "$chkdir" +checkit "$RSYNC -avX $XFILT $dashH --super . '$chkdir/'" "$fromdir" "$chkdir" cd "$chkdir" xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" - @@ -142,7 +144,7 @@ if [ "$dashH" ]; then done fi -checkit "$RSYNC -aiX $dashH --super $altDest=../chk . ../to" "$fromdir" "$todir" +checkit "$RSYNC -aiX $XFILT $dashH --super $altDest=../chk . ../to" "$fromdir" "$todir" cd "$todir" xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" - @@ -156,7 +158,7 @@ xset user.nice 'this is nice, but different' file1 xls $dirs $files >"$scratchdir/xattrs.txt" -checkit "$RSYNC -aiX $dashH --fake-super --link-dest=../chk . ../to" "$chkdir" "$todir" +checkit "$RSYNC -aiX $XFILT $dashH --fake-super --link-dest=../chk . ../to" "$chkdir" "$todir" cd "$todir" xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" - @@ -186,7 +188,7 @@ cd "$fromdir" rm -rf "$todir" # When run by a non-root tester, this checks if no-user-perm files/dirs can be copied. -checkit "$RSYNC -aiX $dashH --fake-super --chmod=a= . ../to" "$chkdir" "$todir" # 2>"$scratchdir/errors.txt" +checkit "$RSYNC -aiX $XFILT $dashH --fake-super --chmod=a= . ../to" "$chkdir" "$todir" # 2>"$scratchdir/errors.txt" cd "$todir" xls $dirs $files | diff $diffopt "$scratchdir/xattrs.txt" - @@ -202,7 +204,7 @@ $RSYNC -aX file1 ../lnk/ xls file1 file2 >"$scratchdir/xattrs.txt" -checkit "$RSYNC -aiiX $dashH $altDest=../lnk . ../to" "$chkdir" "$todir" +checkit "$RSYNC -aiiX $XFILT $dashH $altDest=../lnk . ../to" "$chkdir" "$todir" [ "$dashH" ] && rm ../lnk/extra-link @@ -215,7 +217,7 @@ rm "$todir/file2" echo extra >file1 $RSYNC -aX . ../chk/ -checkit "$RSYNC -aiiX . ../to" "$chkdir" "$todir" +checkit "$RSYNC -aiiX $XFILT . ../to" "$chkdir" "$todir" cd "$todir" xls file1 file2 | diff $diffopt "$scratchdir/xattrs.txt" - diff --git a/xattrs.c b/xattrs.c index b105392..75b1c20 100644 --- a/xattrs.c +++ b/xattrs.c @@ -37,6 +37,7 @@ extern int preserve_links; extern int preserve_devices; extern int preserve_specials; extern int checksum_seed; +extern int saw_xattr_filter; #define RSYNC_XAL_INITIAL 5 #define RSYNC_XAL_LIST_INITIAL 100 @@ -161,7 +162,7 @@ static ssize_t get_xattr_names(const char *fname) arg = namebuf_len; got_error: rsyserr(FERROR_XFER, errno, - "get_xattr_names: llistxattr(\"%s\",%s) failed", + "get_xattr_names: llistxattr(%s,%s) failed", full_fname(fname), big_num(arg)); return -1; } @@ -197,7 +198,7 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr if (errno == ENOTSUP || no_missing_error) return NULL; rsyserr(FERROR_XFER, errno, - "get_xattr_data: lgetxattr(\"%s\",\"%s\",0) failed", + "get_xattr_data: lgetxattr(%s,\"%s\",0) failed", full_fname(fname), name); return NULL; } @@ -214,12 +215,12 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr if (len != datum_len) { if (len == (size_t)-1) { rsyserr(FERROR_XFER, errno, - "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)" - " failed", full_fname(fname), name, (long)datum_len); + "get_xattr_data: lgetxattr(%s,\"%s\",%ld) failed", + full_fname(fname), name, (long)datum_len); } else { rprintf(FERROR_XFER, - "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)" - " returned %ld\n", full_fname(fname), name, + "get_xattr_data: lgetxattr(%s,\"%s\",%ld) returned %ld\n", + full_fname(fname), name, (long)datum_len, (long)len); } free(ptr); @@ -249,17 +250,18 @@ static int rsync_xal_get(const char *fname, item_list *xalp) name_len = strlen(name) + 1; list_len -= name_len; + if (saw_xattr_filter) { + if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS)) + continue; + } #ifdef HAVE_LINUX_XATTRS - /* We always ignore the system namespace, and non-root - * ignores everything but the user namespace. */ - if (user_only ? !HAS_PREFIX(name, USER_PREFIX) - : HAS_PREFIX(name, SYSTEM_PREFIX)) + /* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */ + else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX)) continue; #endif /* No rsync.%FOO attributes are copied w/o 2 -X options. */ - if (name_len > RPRE_LEN && name[RPRE_LEN] == '%' - && HAS_PREFIX(name, RSYNC_PREFIX)) { + if (name_len > RPRE_LEN && name[RPRE_LEN] == '%' && HAS_PREFIX(name, RSYNC_PREFIX)) { if ((am_sender && preserve_xattrs < 2) || (am_root < 0 && (strcmp(name+RPRE_LEN+1, XSTAT_SUFFIX) == 0 @@ -352,11 +354,13 @@ int copy_xattrs(const char *source, const char *dest) name_len = strlen(name) + 1; list_len -= name_len; + if (saw_xattr_filter) { + if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS)) + continue; + } #ifdef HAVE_LINUX_XATTRS - /* We always ignore the system namespace, and non-root - * ignores everything but the user namespace. */ - if (user_only ? !HAS_PREFIX(name, USER_PREFIX) - : HAS_PREFIX(name, SYSTEM_PREFIX)) + /* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */ + else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX)) continue; #endif @@ -366,7 +370,7 @@ int copy_xattrs(const char *source, const char *dest) if (sys_lsetxattr(dest, name, ptr, datum_len) < 0) { int save_errno = errno ? errno : EINVAL; rsyserr(FERROR_XFER, errno, - "copy_xattrs: lsetxattr(\"%s\",\"%s\") failed", + "copy_xattrs: lsetxattr(%s,\"%s\") failed", full_fname(dest), name); errno = save_errno; return -1; @@ -826,10 +830,17 @@ void receive_xattr(int f, struct file_struct *file) *ptr = XSTATE_ABBREV; read_buf(f, ptr + 1, MAX_DIGEST_LEN); } + + if (saw_xattr_filter) { + if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS)) { + free(ptr); + continue; + } + } #ifdef HAVE_LINUX_XATTRS /* Non-root can only save the user namespace. */ if (am_root <= 0 && !HAS_PREFIX(name, USER_PREFIX)) { - if (!am_root) { + if (!am_root && !saw_xattr_filter) { free(ptr); continue; } @@ -860,6 +871,7 @@ void receive_xattr(int f, struct file_struct *file) free(ptr); continue; } + rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, 1); rxa->name = name; rxa->datum = ptr; @@ -991,7 +1003,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp, ; /* Value is already set when identical */ else if (sys_lsetxattr(fname, name, ptr, len) < 0) { rsyserr(FERROR_XFER, errno, - "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed", + "rsync_xal_set: lsetxattr(%s,\"%s\") failed", full_fname(fname), name); ret = -1; } else /* make sure caller sets mtime */ @@ -1012,7 +1024,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp, if (sys_lsetxattr(fname, name, rxas[i].datum, rxas[i].datum_len) < 0) { rsyserr(FERROR_XFER, errno, - "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed", + "rsync_xal_set: lsetxattr(%s,\"%s\") failed", full_fname(fname), name); ret = -1; } else /* make sure caller sets mtime */ @@ -1024,15 +1036,16 @@ static int rsync_xal_set(const char *fname, item_list *xalp, name_len = strlen(name) + 1; list_len -= name_len; + if (saw_xattr_filter) { + if (name_is_excluded(name, NAME_IS_XATTR, ALL_FILTERS)) + continue; + } #ifdef HAVE_LINUX_XATTRS - /* We always ignore the system namespace, and non-root - * ignores everything but the user namespace. */ - if (user_only ? !HAS_PREFIX(name, USER_PREFIX) - : HAS_PREFIX(name, SYSTEM_PREFIX)) + /* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */ + else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX)) continue; #endif - if (am_root < 0 && name_len > RPRE_LEN - && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0) + if (am_root < 0 && name_len > RPRE_LEN && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0) continue; for (i = 0; i < xalp->count; i++) { @@ -1042,7 +1055,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp, if (i == xalp->count) { if (sys_lremovexattr(fname, name) < 0) { rsyserr(FERROR_XFER, errno, - "rsync_xal_set: lremovexattr(\"%s\",\"%s\") failed", + "rsync_xal_set: lremovexattr(%s,\"%s\") failed", -- The rsync repository. _______________________________________________ rsync-cvs mailing list rsync-cvs@lists.samba.org https://lists.samba.org/mailman/listinfo/rsync-cvs