Add a way to specify xattr name filtering.

This commit is contained in:
Wayne Davison committed 2017-01-22 16:01:45 -08:00
1 parent a4e8b552d6
commit 87bc224011
6 files changed
+106 -49

No files matched your search

+41 -9
View File
@@ -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,19 +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;
}
}
@@ -967,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)
+3 -27
View File
@@ -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
+5
View File
@@ -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)
+25 -3
View File
@@ -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)
+8 -6
View File
@@ -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" -
+24 -4
View File
@@ -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
@@ -249,9 +250,13 @@ 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
/* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */
if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
continue;
#endif
@@ -349,9 +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
/* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */
if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
else if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
continue;
#endif
@@ -821,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;
}
@@ -1020,9 +1036,13 @@ 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
/* Choose between ignoring the system namespace or (non-root) ignoring any non-user namespace. */
if (user_only ? !HAS_PREFIX(name, USER_PREFIX) : HAS_PREFIX(name, SYSTEM_PREFIX))
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)