X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/blobdiff_plain/d3d07a5e860f1cde0e234ec7a1aff7111a2c514f..6500e0769a56919df1d47759828104a08df850bb:/xattrs.c diff --git a/xattrs.c b/xattrs.c index a436572b..3a7cb25b 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 @@ -32,6 +33,9 @@ extern int am_generator; extern int read_only; extern int list_only; extern int preserve_xattrs; +extern int preserve_links; +extern int preserve_devices; +extern int preserve_specials; extern int checksum_seed; #define RSYNC_XAL_INITIAL 5 @@ -44,9 +48,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 +66,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 +85,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 +120,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 +138,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", + full_fname(fname), big_num(arg)); return -1; } list_len = sys_llistxattr(fname, NULL, 0); @@ -169,14 +178,15 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr return NULL; rsyserr(FERROR_XFER, errno, "get_xattr_data: lgetxattr(\"%s\",\"%s\",0) failed", - fname, name); + full_fname(fname), name); return NULL; } 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) { @@ -185,11 +195,11 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr if (len == (size_t)-1) { rsyserr(FERROR_XFER, errno, "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)" - " failed", fname, name, (long)datum_len); + " failed", full_fname(fname), name, (long)datum_len); } else { rprintf(FERROR_XFER, "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)" - " returned %ld\n", fname, name, + " returned %ld\n", full_fname(fname), name, (long)datum_len, (long)len); } free(ptr); @@ -231,7 +241,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 +266,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 +287,26 @@ int get_xattr(const char *fname, stat_x *sxp) { sxp->xattr = new(item_list); *sxp->xattr = empty_xattr; + + if (S_ISREG(sxp->st.st_mode) || S_ISDIR(sxp->st.st_mode)) { + /* Everyone supports this. */ + } else if (S_ISLNK(sxp->st.st_mode)) { +#ifndef NO_SYMLINK_XATTRS + if (!preserve_links) +#endif + return 0; + } else if (IS_SPECIAL(sxp->st.st_mode)) { +#ifndef NO_SPECIAL_XATTRS + if (!preserve_specials) +#endif + return 0; + } else if (IS_DEVICE(sxp->st.st_mode)) { +#ifndef NO_DEVICE_XATTRS + if (!preserve_devices) +#endif + return 0; + } + if (rsync_xal_get(fname, sxp->xattr) < 0) { free_xattr(sxp); return -1; @@ -289,6 +314,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, + "copy_xattrs: lsetxattr(\"%s\",\"%s\") failed", + full_fname(dest), name); + errno = save_errno; + return -1; + } + free(ptr); + } + + return 0; +} + static int find_matching_xattr(item_list *xalp) { size_t i, j; @@ -340,7 +407,7 @@ static void rsync_xal_store(item_list *xalp) } /* Send the make_xattr()-generated xattr list for this flist entry. */ -int send_xattr(stat_x *sxp, int f) +int send_xattr(int f, stat_x *sxp) { int ndx = find_matching_xattr(sxp->xattr); @@ -352,25 +419,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 +529,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 +552,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 +581,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 @@ -520,7 +598,7 @@ int recv_xattr_request(struct file_struct *file, int f_in) if (F_XATTR(file) < 0) { rprintf(FERROR, "recv_xattr_request: internal data error!\n"); - exit_cleanup(RERR_STREAMIO); + exit_cleanup(RERR_PROTOCOL); } lst += F_XATTR(file); @@ -536,11 +614,12 @@ int recv_xattr_request(struct file_struct *file, int f_in) if (!cnt || rxa->num != num) { rprintf(FERROR, "[%s] could not find xattr #%d for %s\n", who_am_i(), num, f_name(file, NULL)); - exit_cleanup(RERR_STREAMIO); + exit_cleanup(RERR_PROTOCOL); } - if (rxa->datum_len <= MAX_FULL_DATUM || rxa->datum[0] != XSTATE_ABBREV) { - rprintf(FERROR, "[%s] internal abbrev error!\n", who_am_i()); - exit_cleanup(RERR_STREAMIO); + 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_PROTOCOL); } if (am_sender) { @@ -552,7 +631,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"); @@ -570,10 +649,15 @@ int recv_xattr_request(struct file_struct *file, int f_in) /* ------------------------------------------------------------------------- */ /* receive and build the rsync_xattr_lists */ -void receive_xattr(struct file_struct *file, int f) +void receive_xattr(int f, struct file_struct *file) { 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) { @@ -586,7 +670,7 @@ void receive_xattr(struct file_struct *file, int f) F_XATTR(file) = ndx - 1; return; } - + if ((count = read_varint(f)) != 0) { (void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count); temp_xattr.count = 0; @@ -599,10 +683,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 +707,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 +739,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 +750,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 +766,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 +788,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) @@ -723,7 +831,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp, else if (sys_lsetxattr(fname, name, ptr, len) < 0) { rsyserr(FERROR_XFER, errno, "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed", - fname, name); + full_fname(fname), name); ret = -1; } else /* make sure caller sets mtime */ sxp->st.st_mtime = (time_t)-1; @@ -744,7 +852,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", - fname, name); + full_fname(fname), name); ret = -1; } else /* make sure caller sets mtime */ sxp->st.st_mtime = (time_t)-1; @@ -758,8 +866,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 @@ -773,8 +881,8 @@ 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_clear: lremovexattr(\"%s\",\"%s\") failed", - fname, name); + "rsync_xal_set: lremovexattr(\"%s\",\"%s\") failed", + full_fname(fname), name); ret = -1; } else /* make sure caller sets mtime */ sxp->st.st_mtime = (time_t)-1; @@ -799,6 +907,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); } @@ -817,7 +944,7 @@ int set_xattr_acl(const char *fname, int is_access_acl, const char *buf, size_t if (sys_lsetxattr(fname, name, buf, buf_len) < 0) { rsyserr(FERROR_XFER, errno, "set_xattr_acl: lsetxattr(\"%s\",\"%s\") failed", - fname, name); + full_fname(fname), name); return -1; } return 0; @@ -905,7 +1032,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 +1043,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