X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/d3d07a5e860f1cde0e234ec7a1aff7111a2c514f..4c3e9c09eb0fe3c56afe0c8e8e8d435a0448d626:/xattrs.c diff --git a/xattrs.c b/xattrs.c index a436572b..79313255 100644 --- a/xattrs.c +++ b/xattrs.c @@ -3,7 +3,7 @@ * Written by Jay Fenlason, vaguely based on the ACLs patch. * * Copyright (C) 2004 Red Hat, Inc. - * Copyright (C) 2006-2008 Wayne Davison + * Copyright (C) 2006-2009 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" +#include "inums.h" #include "lib/sysxattrs.h" #ifdef SUPPORT_XATTRS @@ -44,9 +45,9 @@ extern int checksum_seed; #define XATTR_ABBREV(x) ((size_t)((x).name - (x).datum) < (x).datum_len) -#define XSTATE_ABBREV 0 -#define XSTATE_DONE 1 -#define XSTATE_TODO 2 +#define XSTATE_ABBREV 1 +#define XSTATE_DONE 2 +#define XSTATE_TODO 3 #define USER_PREFIX "user." #define UPRE_LEN ((int)sizeof USER_PREFIX - 1) @@ -62,9 +63,12 @@ extern int checksum_seed; #endif #define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1) -#define XSTAT_ATTR RSYNC_PREFIX "%stat" -#define XACC_ACL_ATTR RSYNC_PREFIX "%aacl" -#define XDEF_ACL_ATTR RSYNC_PREFIX "%dacl" +#define XSTAT_SUFFIX "stat" +#define XSTAT_ATTR RSYNC_PREFIX "%" XSTAT_SUFFIX +#define XACC_ACL_SUFFIX "aacl" +#define XACC_ACL_ATTR RSYNC_PREFIX "%" XACC_ACL_SUFFIX +#define XDEF_ACL_SUFFIX "dacl" +#define XDEF_ACL_ATTR RSYNC_PREFIX "%" XDEF_ACL_SUFFIX typedef struct { char *datum, *name; @@ -78,6 +82,8 @@ static char *namebuf = NULL; static item_list empty_xattr = EMPTY_ITEM_LIST; static item_list rsync_xal_l = EMPTY_ITEM_LIST; +static size_t prior_xattr_count = (size_t)-1; + /* ------------------------------------------------------------------------- */ static void rsync_xal_free(item_list *xalp) @@ -111,7 +117,7 @@ static int rsync_xal_compare_names(const void *x1, const void *x2) static ssize_t get_xattr_names(const char *fname) { ssize_t list_len; - double arg; + int64 arg; if (!namebuf) { namebuf_len = 1024; @@ -129,11 +135,11 @@ static ssize_t get_xattr_names(const char *fname) } else if (errno == ENOTSUP) return 0; else if (errno != ERANGE) { - arg = (double)namebuf_len; + arg = namebuf_len; got_error: rsyserr(FERROR_XFER, errno, - "get_xattr_names: llistxattr(\"%s\",%.0f) failed", - fname, arg); + "get_xattr_names: llistxattr(\"%s\",%s) failed", + fname, big_num(arg)); return -1; } list_len = sys_llistxattr(fname, NULL, 0); @@ -175,8 +181,9 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr if (!datum_len && !extra_len) extra_len = 1; /* request non-zero amount of memory */ - if (datum_len + extra_len < datum_len /* checks for overflow */ - || !(ptr = new_array(char, datum_len + extra_len))) + if (datum_len + extra_len < datum_len) + overflow_exit("get_xattr_data"); + if (!(ptr = new_array(char, datum_len + extra_len))) out_of_memory("get_xattr_data"); if (datum_len) { @@ -231,7 +238,10 @@ static int rsync_xal_get(const char *fname, item_list *xalp) if (name_len > RPRE_LEN && name[RPRE_LEN] == '%' && HAS_PREFIX(name, RSYNC_PREFIX)) { if ((am_sender && preserve_xattrs < 2) - || (am_root < 0 && strcmp(name, XSTAT_ATTR) == 0)) + || (am_root < 0 + && (strcmp(name+RPRE_LEN+1, XSTAT_SUFFIX) == 0 + || strcmp(name+RPRE_LEN+1, XACC_ACL_SUFFIX) == 0 + || strcmp(name+RPRE_LEN+1, XDEF_ACL_SUFFIX) == 0))) continue; } @@ -253,14 +263,6 @@ static int rsync_xal_get(const char *fname, item_list *xalp) } else name_offset = datum_len; -#ifdef HAVE_LINUX_XATTRS - if (am_root < 0 && name_len > RPRE_LEN && name[RPRE_LEN] != '%' - && HAS_PREFIX(name, RSYNC_PREFIX)) { - name += RPRE_LEN; - name_len -= RPRE_LEN; - } -#endif - rxa = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); rxa->name = ptr + name_offset; memcpy(rxa->name, name, name_len); @@ -282,6 +284,20 @@ int get_xattr(const char *fname, stat_x *sxp) { sxp->xattr = new(item_list); *sxp->xattr = empty_xattr; + +#ifdef NO_SPECIAL_XATTRS + if (IS_SPECIAL(sxp->st.st_mode)) + return 0; +#endif +#ifdef NO_DEVICE_XATTRS + if (IS_DEVICE(sxp->st.st_mode)) + return 0; +#endif +#ifdef NO_SYMLINK_XATTRS + if (S_ISLNK(sxp->st.st_mode)) + return 0; +#endif + if (rsync_xal_get(fname, sxp->xattr) < 0) { free_xattr(sxp); return -1; @@ -289,6 +305,48 @@ int get_xattr(const char *fname, stat_x *sxp) return 0; } +int copy_xattrs(const char *source, const char *dest) +{ + ssize_t list_len, name_len; + size_t datum_len; + char *name, *ptr; +#ifdef HAVE_LINUX_XATTRS + int user_only = am_sender ? 0 : am_root <= 0; +#endif + + /* This puts the name list into the "namebuf" buffer. */ + if ((list_len = get_xattr_names(source)) < 0) + return -1; + + for (name = namebuf; list_len > 0; name += name_len) { + name_len = strlen(name) + 1; + list_len -= name_len; + +#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)) + continue; +#endif + + datum_len = 0; + if (!(ptr = get_xattr_data(source, name, &datum_len, 0))) + return -1; + if (sys_lsetxattr(dest, name, ptr, datum_len) < 0) { + int save_errno = errno ? errno : EINVAL; + rsyserr(FERROR_XFER, errno, + "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed", + dest, name); + errno = save_errno; + return -1; + } + free(ptr); + } + + return 0; +} + static int find_matching_xattr(item_list *xalp) { size_t i, j; @@ -352,25 +410,32 @@ int send_xattr(stat_x *sxp, int f) int count = sxp->xattr->count; write_varint(f, count); for (rxa = sxp->xattr->items; count--; rxa++) { + size_t name_len = rxa->name_len; + const char *name = rxa->name; + /* Strip the rsync prefix from disguised namespaces. */ + if (name_len > RPRE_LEN #ifdef HAVE_LINUX_XATTRS - write_varint(f, rxa->name_len); + && am_root < 0 +#endif + && name[RPRE_LEN] != '%' && HAS_PREFIX(name, RSYNC_PREFIX)) { + name += RPRE_LEN; + name_len -= RPRE_LEN; + } +#ifndef HAVE_LINUX_XATTRS + else { + /* Put everything else in the user namespace. */ + name_len += UPRE_LEN; + } +#endif + write_varint(f, name_len); write_varint(f, rxa->datum_len); - write_buf(f, rxa->name, rxa->name_len); -#else - /* We strip the rsync prefix from disguised namespaces - * and put everything else in the user namespace. */ - if (HAS_PREFIX(rxa->name, RSYNC_PREFIX) - && rxa->name[RPRE_LEN] != '%') { - write_varint(f, rxa->name_len - RPRE_LEN); - write_varint(f, rxa->datum_len); - write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN); - } else { - write_varint(f, rxa->name_len + UPRE_LEN); - write_varint(f, rxa->datum_len); +#ifndef HAVE_LINUX_XATTRS + if (name_len > rxa->name_len) { write_buf(f, USER_PREFIX, UPRE_LEN); - write_buf(f, rxa->name, rxa->name_len); + name_len -= UPRE_LEN; } #endif + write_buf(f, name, name_len); if (rxa->datum_len > MAX_FULL_DATUM) write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN); else @@ -455,9 +520,11 @@ int xattr_diff(struct file_struct *file, stat_x *sxp, int find_all) return !xattrs_equal; } -/* When called by the generator with a NULL fname, this tells the sender - * which abbreviated xattr values we need. When called by the sender - * (with a non-NULL fname), we send all the extra xattr data it needs. */ +/* When called by the generator (with a NULL fname), this tells the sender + * all the abbreviated xattr values we need. When called by the sender + * (with a non-NULL fname), we send all the extra xattr data it needs. + * The generator may also call with f_out < 0 to just change all the + * XSTATE_ABBREV states into XSTATE_DONE. */ void send_xattr_request(const char *fname, struct file_struct *file, int f_out) { item_list *lst = rsync_xal_l.items; @@ -476,6 +543,7 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out) rxa->datum[0] = XSTATE_DONE; continue; case XSTATE_TODO: + assert(f_out >= 0); break; default: continue; @@ -504,7 +572,8 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out) } } - write_byte(f_out, 0); /* end the list */ + if (f_out >= 0) + write_byte(f_out, 0); /* end the list */ } /* When called by the sender, read the request from the generator and mark @@ -538,8 +607,9 @@ int recv_xattr_request(struct file_struct *file, int f_in) who_am_i(), num, f_name(file, NULL)); exit_cleanup(RERR_STREAMIO); } - if (rxa->datum_len <= MAX_FULL_DATUM || rxa->datum[0] != XSTATE_ABBREV) { - rprintf(FERROR, "[%s] internal abbrev error!\n", who_am_i()); + if (!XATTR_ABBREV(*rxa) || rxa->datum[0] != XSTATE_ABBREV) { + rprintf(FERROR, "[%s] internal abbrev error on %s (%s, len=%ld)!\n", + who_am_i(), f_name(file, NULL), rxa->name, (long)rxa->datum_len); exit_cleanup(RERR_STREAMIO); } @@ -552,7 +622,7 @@ int recv_xattr_request(struct file_struct *file, int f_in) rxa->datum_len = read_varint(f_in); if (rxa->name_len + rxa->datum_len < rxa->name_len) - out_of_memory("recv_xattr_request"); /* overflow */ + overflow_exit("recv_xattr_request"); rxa->datum = new_array(char, rxa->datum_len + rxa->name_len); if (!rxa->datum) out_of_memory("recv_xattr_request"); @@ -574,6 +644,11 @@ void receive_xattr(struct file_struct *file, int f) { static item_list temp_xattr = EMPTY_ITEM_LIST; int count, num; +#ifdef HAVE_LINUX_XATTRS + int need_sort = 0; +#else + int need_sort = 1; +#endif int ndx = read_varint(f); if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) { @@ -599,10 +674,9 @@ void receive_xattr(struct file_struct *file, int f) size_t datum_len = read_varint(f); size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len; size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0; - if (dget_len + extra_len < dget_len) - out_of_memory("receive_xattr"); /* overflow */ - if (dget_len + extra_len + name_len < dget_len) - out_of_memory("receive_xattr"); /* overflow */ + if ((dget_len + extra_len < dget_len) + || (dget_len + extra_len + name_len < dget_len)) + overflow_exit("receive_xattr"); ptr = new_array(char, dget_len + extra_len + name_len); if (!ptr) out_of_memory("receive_xattr"); @@ -624,6 +698,7 @@ void receive_xattr(struct file_struct *file, int f) name -= RPRE_LEN; name_len += RPRE_LEN; memcpy(name, RSYNC_PREFIX, RPRE_LEN); + need_sort = 1; } #else /* This OS only has a user namespace, so we either @@ -655,6 +730,9 @@ void receive_xattr(struct file_struct *file, int f) rxa->num = num; } + if (need_sort && count > 1) + qsort(temp_xattr.items, count, sizeof (rsync_xa), rsync_xal_compare_names); + ndx = rsync_xal_l.count; /* pre-incremented count */ rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */ @@ -663,13 +741,15 @@ void receive_xattr(struct file_struct *file, int f) /* Turn the xattr data in stat_x into cached xattr data, setting the index * values in the file struct. */ -void cache_xattr(struct file_struct *file, stat_x *sxp) +void cache_tmp_xattr(struct file_struct *file, stat_x *sxp) { int ndx; if (!sxp->xattr) return; + if (prior_xattr_count == (size_t)-1) + prior_xattr_count = rsync_xal_l.count; ndx = find_matching_xattr(sxp->xattr); if (ndx < 0) rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */ @@ -677,6 +757,21 @@ void cache_xattr(struct file_struct *file, stat_x *sxp) F_XATTR(file) = ndx; } +void uncache_tmp_xattrs(void) +{ + if (prior_xattr_count != (size_t)-1) { + item_list *xattr_item = rsync_xal_l.items; + item_list *xattr_start = xattr_item + prior_xattr_count; + xattr_item += rsync_xal_l.count; + rsync_xal_l.count = prior_xattr_count; + while (xattr_item-- > xattr_start) { + rsync_xal_free(xattr_item); + free(xattr_item->items); + } + prior_xattr_count = (size_t)-1; + } +} + static int rsync_xal_set(const char *fname, item_list *xalp, const char *fnamecmp, stat_x *sxp) { @@ -684,7 +779,11 @@ static int rsync_xal_set(const char *fname, item_list *xalp, ssize_t list_len; size_t i, len; char *name, *ptr, sum[MAX_DIGEST_LEN]; - int name_len, ret = 0; +#ifdef HAVE_LINUX_XATTRS + int user_only = am_root <= 0; +#endif + size_t name_len; + int ret = 0; /* This puts the current name list into the "namebuf" buffer. */ if ((list_len = get_xattr_names(fname)) < 0) @@ -758,8 +857,8 @@ static int rsync_xal_set(const char *fname, item_list *xalp, #ifdef HAVE_LINUX_XATTRS /* We always ignore the system namespace, and non-root * ignores everything but the user namespace. */ - if (am_root ? HAS_PREFIX(name, SYSTEM_PREFIX) - : !HAS_PREFIX(name, USER_PREFIX)) + if (user_only ? !HAS_PREFIX(name, USER_PREFIX) + : HAS_PREFIX(name, SYSTEM_PREFIX)) continue; #endif if (am_root < 0 && name_len > RPRE_LEN @@ -799,6 +898,25 @@ int set_xattr(const char *fname, const struct file_struct *file, return -1; } +#ifdef NO_SPECIAL_XATTRS + if (IS_SPECIAL(sxp->st.st_mode)) { + errno = ENOTSUP; + return -1; + } +#endif +#ifdef NO_DEVICE_XATTRS + if (IS_DEVICE(sxp->st.st_mode)) { + errno = ENOTSUP; + return -1; + } +#endif +#ifdef NO_SYMLINK_XATTRS + if (S_ISLNK(sxp->st.st_mode)) { + errno = ENOTSUP; + return -1; + } +#endif + ndx = F_XATTR(file); return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp); } @@ -905,7 +1023,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode) fst.st_mode &= (_S_IFMT | CHMOD_BITS); fmode = new_mode & (_S_IFMT | CHMOD_BITS); - if (IS_DEVICE(fmode) || IS_SPECIAL(fmode)) { + if (IS_DEVICE(fmode)) { uint32 *devp = F_RDEV_P(file); rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp)); } else @@ -916,7 +1034,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode) | (S_ISDIR(fst.st_mode) ? 0700 : 0600); if (fst.st_mode != mode) do_chmod(fname, mode); - if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode)) + if (!IS_DEVICE(fst.st_mode)) fst.st_rdev = 0; /* just in case */ if (mode == fmode && fst.st_rdev == rdev