X-Git-Url: https://mattmccutchen.net/rsync/rsync-patches.git/blobdiff_plain/4959107fb9ad9b4d463bdc03af0484f53ccf0681..ffa0c08b45e869703a574fb822c3f2753bbb6ad9:/xattrs.diff diff --git a/xattrs.diff b/xattrs.diff index baf0d9f..cf46b67 100644 --- a/xattrs.diff +++ b/xattrs.diff @@ -1,14 +1,25 @@ -Depends-On-Patch: acls.diff +This patch adds basic support for extended attributes. It works, but there +are some things that still needs to be improved (see TODO list below). -After applying this patch, run these commands for a successful build: +To use this patch, run these commands for a successful build: + patch -p1 acc_acl; - racl_list = &access_acl_list; - ndx_ptr = (char*)file + file_struct_len; -+#ifdef SUPPORT_XATTRS -+ if (preserve_xattrs) -+ ndx_ptr += 4; -+#endif - do { - if (!racl) - ndx = -1; -@@ -914,6 +923,10 @@ int set_acl(const char *fname, const str - - type = SMB_ACL_TYPE_ACCESS; - ndx_ptr = (char*)file + file_struct_len; -+#ifdef SUPPORT_XATTRS -+ if (preserve_xattrs) -+ ndx_ptr += 4; -+#endif - do { - acl_duo *duo_item; - BOOL eq; --- old/backup.c +++ new/backup.c -@@ -30,6 +30,7 @@ extern char *backup_dir; - +@@ -24,6 +24,7 @@ + extern int verbose; extern int am_root; extern int preserve_acls; +extern int preserve_xattrs; extern int preserve_devices; extern int preserve_specials; extern int preserve_links; -@@ -136,6 +137,9 @@ static int make_bak_dir(char *fullpath) +@@ -135,6 +136,9 @@ static int make_bak_dir(char *fullpath) #ifdef SUPPORT_ACLS sx.acc_acl = sx.def_acl = NULL; #endif @@ -96,7 +64,7 @@ TODO: if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS))) continue; #ifdef SUPPORT_ACLS -@@ -144,6 +148,12 @@ static int make_bak_dir(char *fullpath) +@@ -143,6 +147,12 @@ static int make_bak_dir(char *fullpath) cache_acl(file, &sx); } #endif @@ -109,7 +77,7 @@ TODO: set_file_attrs(fullpath, file, NULL, 0); free(file); } -@@ -195,6 +205,9 @@ static int keep_backup(char *fname) +@@ -194,6 +204,9 @@ static int keep_backup(const char *fname #ifdef SUPPORT_ACLS sx.acc_acl = sx.def_acl = NULL; #endif @@ -119,7 +87,7 @@ TODO: if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS))) return 1; /* the file could have disappeared */ -@@ -208,6 +221,12 @@ static int keep_backup(char *fname) +@@ -209,6 +222,12 @@ static int keep_backup(const char *fname cache_acl(file, &sx); } #endif @@ -132,13 +100,26 @@ TODO: /* Check to see if this is a device file, or link */ if ((am_root && preserve_devices && IS_DEVICE(file->mode)) +--- old/compat.c ++++ new/compat.c +@@ -58,6 +58,8 @@ void setup_protocol(int f_out,int f_in) + preserve_gid = ++file_extra_cnt; + if (preserve_acls && !am_sender) + preserve_acls = ++file_extra_cnt; ++ if (preserve_xattrs && !am_sender) ++ preserve_xattrs = ++file_extra_cnt; + + if (remote_protocol == 0) { + if (!read_batch) --- old/configure.in +++ new/configure.in -@@ -856,6 +856,30 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_ +@@ -883,6 +883,40 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_ AC_MSG_RESULT(no) ) +AC_CHECK_HEADERS(attr/xattr.h) ++AC_CHECK_HEADERS(sys/xattr.h) ++AC_CHECK_HEADERS(sys/extattr.h) +AC_MSG_CHECKING(whether to support extended attributes) +AC_ARG_ENABLE(xattr-support, +AC_HELP_STRING([--enable-xattr-support], [Include extended attribute support (default=no)]), @@ -149,17 +130,25 @@ TODO: + AC_MSG_RESULT(Using Linux xattrs) + AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs]) + ;; ++ darwin*) ++ AC_MSG_RESULT(Using OS X xattrs) ++ AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs]) ++ ;; ++ freebsd*) ++ AC_MSG_RESULT(Using FreeBSD extattrs) ++ AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs]) ++ ;; + *) -+ AC_MSG_RESULT(Xattrs requested but not linux. Good luck) ++ AC_MSG_RESULT(Xattrs requested but not Linux or OS X. Good luck...) + ;; + esac + ;; + *) + AC_MSG_RESULT(no) -+ AC_DEFINE(HAVE_NA_XATTRS, 1, [True if you don't have extended attributes]) ++ AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes]) + esac ], + AC_MSG_RESULT(no) -+ AC_DEFINE(HAVE_NO_XATTRL, 1, [True if you don't have extended attributes]) ++ AC_DEFINE(HAVE_NO_XATTRS, 1, [True if you don't have extended attributes]) +) + AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig]) @@ -167,7 +156,7 @@ TODO: --- old/flist.c +++ new/flist.c -@@ -41,6 +41,7 @@ extern int one_file_system; +@@ -43,6 +43,7 @@ extern int one_file_system; extern int copy_dirlinks; extern int keep_dirlinks; extern int preserve_acls; @@ -175,43 +164,7 @@ TODO: extern int preserve_links; extern int preserve_hard_links; extern int preserve_devices; -@@ -498,7 +499,7 @@ static struct file_struct *receive_file_ - char thisname[MAXPATHLEN]; - unsigned int l1 = 0, l2 = 0; - int alloc_len, basename_len, dirname_len, linkname_len, sum_len; --#ifdef SUPPORT_ACLS -+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS - int xtra_len; - #endif - OFF_T file_length; -@@ -610,10 +611,16 @@ static struct file_struct *receive_file_ - xtra_len = (S_ISDIR(mode) ? 2 : 1) * 4; - else - xtra_len = 0; -+#elif defined SUPPORT_XATTRS -+ xtra_len = 0; -+#endif -+#ifdef SUPPORT_XATTRS -+ if (preserve_xattrs) -+ xtra_len += 4; - #endif - - alloc_len = file_struct_len + dirname_len + basename_len --#ifdef SUPPORT_ACLS -+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS - + xtra_len - #endif - + linkname_len + sum_len; -@@ -622,7 +629,7 @@ static struct file_struct *receive_file_ - file = (struct file_struct *)bp; - memset(bp, 0, file_struct_len); - bp += file_struct_len; --#ifdef SUPPORT_ACLS -+#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS - bp += xtra_len; - #endif - -@@ -723,6 +730,10 @@ static struct file_struct *receive_file_ +@@ -865,6 +866,10 @@ static struct file_struct *recv_file_ent if (preserve_acls) receive_acl(file, f); #endif @@ -220,14 +173,23 @@ TODO: + receive_xattr(file, f ); +#endif - return file; - } -@@ -994,6 +1005,13 @@ static struct file_struct *send_file_nam + if (S_ISREG(mode) || S_ISLNK(mode)) + stats.total_size += file_length; +@@ -1137,7 +1142,7 @@ static struct file_struct *send_file_nam + int flags, int filter_flags) + { + struct file_struct *file; +-#ifdef SUPPORT_ACLS ++#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS + statx sx; + #endif + +@@ -1156,6 +1161,13 @@ static struct file_struct *send_file_nam return NULL; } #endif +#ifdef SUPPORT_XATTRS -+ if (preserve_xattrs) { ++ if (preserve_xattrs && f >= 0) { + sx.xattr = NULL; + if (get_xattr(fname, &sx) < 0) + return NULL; @@ -236,29 +198,20 @@ TODO: maybe_emit_filelist_progress(flist->count + flist_count_offset); -@@ -1006,11 +1024,19 @@ static struct file_struct *send_file_nam +@@ -1167,6 +1179,10 @@ static struct file_struct *send_file_nam if (preserve_acls) send_acl(&sx, f); #endif +#ifdef SUPPORT_XATTRS + if (preserve_xattrs) + send_xattr(&sx, f); -+#endif - } else { - #ifdef SUPPORT_ACLS - if (preserve_acls) - free_acl(&sx); - #endif -+#ifdef SUPPORT_XATTRS -+ if (preserve_xattrs) -+ free_xattr(&sx); +#endif } return file; } --- old/lib/sysxattr.c +++ new/lib/sysxattr.c -@@ -0,0 +1,44 @@ +@@ -0,0 +1,135 @@ +/* + * Extended attribute support for rsync. + * @@ -283,6 +236,8 @@ TODO: +#include "rsync.h" +#include "sysxattr.h" + ++#ifdef SUPPORT_XATTRS ++ +#if defined HAVE_LINUX_XATTRS + +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size) @@ -290,9 +245,19 @@ TODO: + return lgetxattr(path, name, value, size); +} + -+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) ++ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size) +{ -+ return lsetxattr(path, name, value, size, flags); ++ return fgetxattr(filedes, name, value, size); ++} ++ ++int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size) ++{ ++ return lsetxattr(path, name, value, size, 0); ++} ++ ++int sys_lremovexattr(const char *path, const char *name) ++{ ++ return lremovexattr(path, name); +} + +ssize_t sys_llistxattr(const char *path, char *list, size_t size) @@ -300,25 +265,117 @@ TODO: + return llistxattr(path, list, size); +} + ++#elif HAVE_OSX_XATTRS ++ ++ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size) ++{ ++ return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); ++} ++ ++ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size) ++{ ++ return fgetxattr(filedes, name, value, size, 0, 0); ++} ++ ++int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size) ++{ ++ return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW); ++} ++ ++int sys_lremovexattr(const char *path, const char *name) ++{ ++ return removexattr(path, name, XATTR_NOFOLLOW); ++} ++ ++ssize_t sys_llistxattr(const char *path, char *list, size_t size) ++{ ++ return listxattr(path, list, size, XATTR_NOFOLLOW); ++} ++ ++#elif HAVE_FREEBSD_XATTRS ++ ++ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size) ++{ ++ return extattr_get_link(path, EXTATTR_NAMESPACE_USER, name, value, size); ++} ++ ++ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size) ++{ ++ return extattr_get_fd(filedes, EXTATTR_NAMESPACE_USER, name, value, size); ++} ++ ++int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size) ++{ ++ return extattr_set_link(path, EXTATTR_NAMESPACE_USER, name, value, size); ++} ++ ++int sys_lremovexattr(const char *path, const char *name) ++{ ++ return extattr_delete_link(path, EXTATTR_NAMESPACE_USER, name); ++} ++ ++ssize_t sys_llistxattr(const char *path, char *list, size_t size) ++{ ++ unsigned char keylen; ++ ssize_t off, len = extattr_list_link(path, EXTATTR_NAMESPACE_USER, list, size); ++ ++ if (len <= 0 || (size_t)len > size) ++ return len; ++ ++ /* FreeBSD puts a single-byte length before each string, with no '\0' ++ * terminator. We need to change this into a series of null-terminted ++ * strings. Since the size is the same, we can simply transform the ++ * output in place. */ ++ for (off = 0; off < len; off += keylen + 1) { ++ keylen = ((unsigned char*)list)[off]; ++ if (off + keylen >= len) { ++ /* Should be impossible, but kernel bugs happen! */ ++ errno = EINVAL; ++ return -1; ++ } ++ memmove(list+off, list+off+1, keylen); ++ list[off+keylen] = '\0'; ++ } ++ ++ return len; ++} ++ +#else + -+#endif /* No xattrs */ ++#error You need to create xattr compatibility functions. ++ ++#endif ++ ++#endif /* SUPPORT_XATTRS */ --- old/lib/sysxattr.h +++ new/lib/sysxattr.h -@@ -0,0 +1,13 @@ -+#if defined SUPPORT_XATTRS && defined HAVE_ATTR_XATTR_H +@@ -0,0 +1,26 @@ ++#ifdef SUPPORT_XATTRS ++ ++#if defined HAVE_ATTR_XATTR_H +#include ++#elif defined HAVE_SYS_XATTR_H ++#include ++#elif defined HAVE_SYS_EXTATTR_H ++#include +#endif + -+#if defined HAVE_LINUX_XATTRS ++/* Linux 2.4 does not define this as a distinct errno value: */ ++#ifndef ENOATTR ++#define ENOATTR ENODATA ++#endif + +ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size); -+int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags); ++ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size); ++int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size); ++int sys_lremovexattr(const char *path, const char *name); +ssize_t sys_llistxattr(const char *path, char *list, size_t size); + +#else + -+#endif /* No xattrs */ ++/* No xattrs available */ ++ ++#endif --- old/options.c +++ new/options.c @@ -48,6 +48,7 @@ int copy_links = 0; @@ -348,19 +405,18 @@ TODO: #ifdef SUPPORT_LINKS links = ""; #endif -@@ -234,9 +238,9 @@ static void print_rsync_version(enum log - rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n"); - rprintf(f, "\n"); - rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, " -- "%shard links, %sACLs, %ssymlinks, batchfiles,\n", -+ "%shard links, %sACLs, %sxattrs, %ssymlinks, batchfiles,\n", - (int) (sizeof (OFF_T) * 8), -- got_socketpair, hardlinks, acls, links); -+ got_socketpair, hardlinks, acls, xattrs, links); +@@ -239,8 +243,8 @@ static void print_rsync_version(enum log + (int)(sizeof (int64) * 8)); + rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n", + got_socketpair, hardlinks, links, ipv6, have_inplace); +- rprintf(f, " %sappend, %sACLs\n", +- have_inplace, acls); ++ rprintf(f, " %sappend, %sACLs, %sxattrs\n", ++ have_inplace, acls, xattrs); - /* Note that this field may not have type ino_t. It depends - * on the complicated interaction between largefile feature -@@ -290,7 +294,7 @@ void usage(enum logcode F) + #ifdef MAINTAINER_MODE + rprintf(f, "Panic Action: \"%s\"\n", get_panic_action()); +@@ -286,7 +290,7 @@ void usage(enum logcode F) rprintf(F," -q, --quiet suppress non-error messages\n"); rprintf(F," --no-motd suppress daemon-mode MOTD (see manpage caveat)\n"); rprintf(F," -c, --checksum skip based on checksum, not mod-time & size\n"); @@ -369,7 +425,7 @@ TODO: rprintf(F," --no-OPTION turn off an implied OPTION (e.g. --no-D)\n"); rprintf(F," -r, --recursive recurse into directories\n"); rprintf(F," -R, --relative use relative path names\n"); -@@ -315,6 +319,9 @@ void usage(enum logcode F) +@@ -311,6 +315,9 @@ void usage(enum logcode F) #ifdef SUPPORT_ACLS rprintf(F," -A, --acls preserve ACLs (implies --perms)\n"); #endif @@ -379,7 +435,7 @@ TODO: rprintf(F," -o, --owner preserve owner (super-user only)\n"); rprintf(F," -g, --group preserve group\n"); rprintf(F," --devices preserve device files (super-user only)\n"); -@@ -437,6 +444,9 @@ static struct poptOption long_options[] +@@ -434,6 +441,9 @@ static struct poptOption long_options[] {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 }, {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 }, {"no-A", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 }, @@ -389,7 +445,7 @@ TODO: {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 }, {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 }, -@@ -1118,6 +1128,17 @@ int parse_arguments(int *argc, const cha +@@ -1122,6 +1132,17 @@ int parse_arguments(int *argc, const cha return 0; #endif @@ -403,11 +459,11 @@ TODO: + "extended attributes are not supported on this %s\n", + am_server ? "server" : "client"); + return 0; -+#endif /* SUPPORT_XATTRS */ ++#endif default: /* A large opt value means that set_refuse_options() -@@ -1564,6 +1585,10 @@ void server_options(char **args,int *arg +@@ -1590,6 +1611,10 @@ void server_options(char **args,int *arg if (preserve_acls) argstr[x++] = 'A'; #endif @@ -415,9 +471,9 @@ TODO: + if (preserve_xattrs) + argstr[x++] = 'X'; +#endif - if (preserve_uid) - argstr[x++] = 'o'; - if (preserve_gid) + if (recurse) + argstr[x++] = 'r'; + if (always_checksum) --- old/rsync.c +++ new/rsync.c @@ -33,6 +33,7 @@ @@ -428,31 +484,47 @@ TODO: extern int preserve_perms; extern int preserve_executability; extern int preserve_times; -@@ -229,6 +230,10 @@ int set_file_attrs(char *fname, struct f - if (preserve_acls && set_acl(fname, file, sxp) == 0) - updated = 1; - #endif +@@ -321,6 +322,10 @@ int set_file_attrs(char *fname, struct f + if (daemon_chmod_modes && !S_ISLNK(new_mode)) + new_mode = tweak_mode(new_mode, daemon_chmod_modes); + +#ifdef SUPPORT_XATTRS + if (preserve_xattrs && set_xattr(fname, file, sxp) == 0) + updated = 1; +#endif - - #ifdef HAVE_CHMOD - if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) { + #ifdef SUPPORT_ACLS + /* It's OK to call set_acl() now, even for a dir, as the generator + * will enable owner-writability using chmod, if necessary. --- old/rsync.h +++ new/rsync.h -@@ -501,6 +501,10 @@ struct idev { +@@ -554,6 +554,10 @@ struct idev_node { #define ACLS_NEED_MASK 1 #endif -+#ifdef HAVE_LINUX_XATTRS ++#ifndef HAVE_NO_XATTRS +#define SUPPORT_XATTRS 1 +#endif + #define GID_NONE ((gid_t)-1) - #define HL_CHECK_MASTER 0 -@@ -695,6 +699,9 @@ typedef struct { + union file_extras { +@@ -574,6 +578,7 @@ extern int file_extra_cnt; + extern int preserve_uid; + extern int preserve_gid; + extern int preserve_acls; ++extern int preserve_xattrs; + + #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename)) + #define EXTRA_LEN (sizeof (union file_extras)) +@@ -607,6 +612,7 @@ extern int preserve_acls; + #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum + #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum + #define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum ++#define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->unum + + /* These items are per-entry optional and mutally exclusive: */ + #define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num +@@ -798,6 +804,9 @@ typedef struct { struct rsync_acl *acc_acl; /* access ACL */ struct rsync_acl *def_acl; /* default ACL */ #endif @@ -481,9 +553,9 @@ TODO: -o, --owner preserve owner (super-user only) -g, --group preserve group --devices preserve device files (super-user only) -@@ -818,6 +819,11 @@ version makes it incompatible with sendi - rsync unless you double the bf(--acls) option (e.g. bf(-AA)). This - doubling is not needed when pulling files from an older rsync. +@@ -835,6 +836,11 @@ The ACL-sending protocol used by this ve + the patch that was shipped with 2.6.8. Sending ACLs to an older version + of the ACL patch is not supported. +dit(bf(-X, --xattrs)) This option causes rsync to update the remote +extended attributes to be the same as the local ones. This will work @@ -495,7 +567,7 @@ TODO: transfer. The resulting value is treated as though it was the permissions --- old/xattr.c +++ new/xattr.c -@@ -0,0 +1,360 @@ +@@ -0,0 +1,413 @@ +/* + * Extended Attribute support for rsync. + * Written by Jay Fenlason, vaguely based on the ACLs patch. @@ -524,24 +596,36 @@ TODO: +#ifdef SUPPORT_XATTRS + +extern int dry_run; -+extern unsigned int file_struct_len; ++extern int am_root; ++extern int read_only; ++extern int list_only; + +#define RSYNC_XAL_INITIAL 5 +#define RSYNC_XAL_LIST_INITIAL 100 + ++#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \ ++ && strncmp(str, prfx, sizeof (prfx) - 1) == 0) ++ ++#define USER_PREFIX "user." ++#define UPRE_LEN ((int)sizeof USER_PREFIX - 1) ++#define SYSTEM_PREFIX "system." ++#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1) ++ ++#ifdef HAVE_LINUX_XATTRS ++#define RPRE_LEN 0 ++#else ++#define RSYNC_PREFIX "rsync." ++#define RPRE_LEN ((int)sizeof RSYNC_PREFIX - 1) ++#endif ++ +typedef struct { -+ char *name; -+ char *datum; -+ size_t name_len; -+ size_t datum_len; ++ char *datum, *name; ++ size_t datum_len, name_len; +} rsync_xa; + +static size_t namebuf_len = 0; +static char *namebuf = NULL; + -+static size_t datumbuf_len = 0; -+static char *datumbuf = NULL; -+ +static item_list empty_xattr = EMPTY_ITEM_LIST; +static item_list rsync_xal_l = EMPTY_ITEM_LIST; + @@ -553,8 +637,8 @@ TODO: + rsync_xa *rxas = xalp->items; + + for (i = 0; i < xalp->count; i++) { -+ free(rxas[i].name); -+ /* free(rxas[i].value); */ ++ free(rxas[i].datum); ++ /*free(rxas[i].name);*/ + } + xalp->count = 0; +} @@ -575,105 +659,99 @@ TODO: + +static int rsync_xal_get(const char *fname, item_list *xalp) +{ -+ ssize_t name_size; -+ ssize_t datum_size; -+ ssize_t left; -+ char *name; -+ size_t len; -+ char *ptr; ++ ssize_t list_len, name_len, datum_len; ++ char *name, *ptr; + + if (!namebuf) { -+ namebuf_len = 100; ++ namebuf_len = 1024; + namebuf = new_array(char, namebuf_len); -+ datumbuf_len = 100; -+ datumbuf = new_array(char, datumbuf_len); -+ if (!namebuf || !datumbuf) ++ if (!namebuf) + out_of_memory("rsync_xal_get"); + } + -+ name_size = sys_llistxattr(fname, namebuf, namebuf_len); -+ if (name_size > (ssize_t)namebuf_len) { -+ name_size = -1; ++ /* The length returned includes all the '\0' terminators. */ ++ list_len = sys_llistxattr(fname, namebuf, namebuf_len); ++ if (list_len > (ssize_t)namebuf_len) { ++ list_len = -1; + errno = ERANGE; + } -+ if (name_size < 0) { ++ if (list_len < 0) { + if (errno == ENOTSUP) + return 0; + if (errno == ERANGE) { -+ name_size = sys_llistxattr(fname, NULL, 0); -+ if (name_size < 0) { -+ rsyserr(FERROR, errno, "%s: rsync_xal_get: llistxattr", ++ list_len = sys_llistxattr(fname, NULL, 0); ++ if (list_len < 0) { ++ rsyserr(FERROR, errno, ++ "rsync_xal_get: llistxattr(\"%s\",0) failed", + fname); + return -1; + } -+ namebuf = realloc_array(namebuf, char, name_size + 1); ++ namebuf = realloc_array(namebuf, char, list_len + 1024); + if (!namebuf) + out_of_memory("rsync_xal_get"); -+ namebuf_len = name_size; -+ name_size = sys_llistxattr(fname, namebuf, namebuf_len); -+ if (name_size < 0) { ++ namebuf_len = list_len + 1024; ++ list_len = sys_llistxattr(fname, namebuf, namebuf_len); ++ if (list_len < 0) { + rsyserr(FERROR, errno, -+ "%s: rsync_xal_get: re-llistxattr failed", -+ fname); ++ "rsync_xal_get: llistxattr(\"%s\",%ld) failed", ++ fname, (long)namebuf_len); + return -1; + } + } else { + rsyserr(FERROR, errno, -+ "%s: rsync_xal_get: llistxattr failed:", -+ fname); ++ "rsync_xal_get: llistxattr(\"%s\",%ld) failed", ++ fname, (long)namebuf_len); + return -1; + } + } -+ if (name_size == 0) -+ return 0; -+ for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) { -+ rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); -+ -+ len = strlen(name) + 1; -+ datum_size = sys_lgetxattr(fname, name, datumbuf, datumbuf_len); -+ if (datum_size > (ssize_t)datumbuf_len) { -+ datum_size = -1; -+ errno = ERANGE; -+ } -+ if (datum_size < 0) { ++ ++ for (name = namebuf; list_len > 0; name += name_len) { ++ rsync_xa *rxas; ++ ++ name_len = strlen(name) + 1; ++ list_len -= name_len; ++ ++#ifdef HAVE_LINUX_XATTRS ++ /* We don't send the system namespace. */ ++ if (HAS_PREFIX(name, SYSTEM_PREFIX)) ++ continue; ++#endif ++ ++ datum_len = sys_lgetxattr(fname, name, NULL, 0); ++ if (datum_len < 0) { + if (errno == ENOTSUP) + return -1; -+ if (errno == ERANGE) { -+ datum_size = sys_lgetxattr(fname, name, NULL, 0); -+ if (datum_size < 0) { -+ rsyserr(FERROR, errno, -+ "%s: rsync_xal_get: lgetxattr %s failed", -+ fname, name); -+ return -1; -+ } -+ datumbuf = realloc_array(datumbuf, char, datum_size + 1); -+ if (!datumbuf) -+ out_of_memory("rsync_xal_get"); -+ datumbuf_len = datum_size; -+ datum_size = sys_lgetxattr(fname, name, datumbuf, datumbuf_len); -+ if (datum_size < 0) { ++ rsyserr(FERROR, errno, ++ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",0) failed", ++ fname, name); ++ return -1; ++ } ++ ptr = new_array(char, name_len + datum_len); ++ if (!ptr) ++ out_of_memory("rsync_xal_get"); ++ if (datum_len) { ++ ssize_t len = sys_lgetxattr(fname, name, ptr, datum_len); ++ if (len != datum_len) { ++ if (len < 0) { + rsyserr(FERROR, errno, -+ "%s: rsync_xal_get: re-lgetxattr of %s failed", -+ name, fname); -+ return -1; ++ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)" ++ " failed", fname, name, (long)datum_len); ++ } else { ++ rprintf(FERROR, ++ "rsync_xal_get: lgetxattr(\"%s\",\"%s\",%ld)" ++ " returned %ld\n", fname, name, ++ (long)datum_len, (long)len); + } -+ } else { -+ rsyserr(FERROR, errno, -+ "%s: rsync_xal_get: lgetxattr %s failed", -+ fname, name); ++ free(ptr); + return -1; + } + } -+ ptr = new_array(char, len + datum_size); -+ if (!ptr) -+ out_of_memory("rsync_xal_get"); -+ memcpy(ptr, name, len); -+ if (datum_size) -+ memcpy(ptr + len, datumbuf, datum_size); -+ rxas->name_len = len; -+ rxas->name = ptr; -+ rxas->datum_len = datum_size; -+ rxas->datum = ptr + len; ++ rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); ++ rxas->name = ptr + datum_len; ++ rxas->datum = ptr; ++ rxas->name_len = name_len; ++ rxas->datum_len = datum_len; ++ memcpy(rxas->name, name, name_len); + } + if (xalp->count > 1) + qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names); @@ -726,8 +804,9 @@ TODO: + item_list *new_lst = EXPAND_ITEM_LIST(&rsync_xal_l, item_list, RSYNC_XAL_LIST_INITIAL); + /* Since the following call starts a new list, we know it will hold the + * entire initial-count, not just enough space for one new item. */ -+ (void)EXPAND_ITEM_LIST(new_lst, item_list, xalp->count); -+ memcpy(new_lst->items, xalp->items, xalp->count * sizeof (item_list)); ++ *new_lst = empty_xattr; ++ (void)EXPAND_ITEM_LIST(new_lst, rsync_xa, xalp->count); ++ memcpy(new_lst->items, xalp->items, xalp->count * sizeof (rsync_xa)); + new_lst->count = xalp->count; + xalp->count = 0; +} @@ -746,12 +825,28 @@ TODO: + write_byte(f, 'X'); + write_int(f, count); + for (rxa = sxp->xattr->items; count--; rxa++) { ++#ifdef HAVE_LINUX_XATTRS + write_int(f, rxa->name_len); + write_int(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_int(f, rxa->name_len - RPRE_LEN); ++ write_int(f, rxa->datum_len); ++ write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN); ++ } else { ++ write_int(f, rxa->name_len + UPRE_LEN); ++ write_int(f, rxa->datum_len); ++ write_buf(f, USER_PREFIX, UPRE_LEN); ++ write_buf(f, rxa->name, rxa->name_len); ++ } ++#endif + write_buf(f, rxa->datum, rxa->datum_len); + } -+ rsync_xal_store(sxp->xattr); ++ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */ + } + free_xattr(sxp); +} @@ -762,55 +857,80 @@ TODO: +void receive_xattr(struct file_struct *file, int f) +{ + static item_list temp_xattr = EMPTY_ITEM_LIST; -+ int tag = read_byte(f); -+ char *ndx_ptr = (char*)file + file_struct_len; -+ int ndx; ++ int ndx, tag = read_byte(f); + + if (tag == 'X') { + int i, count = read_int(f); + for (i = 0; i < count; i++) { -+ char *ptr; ++ char *ptr, *name; + rsync_xa *rxa; + size_t name_len = read_int(f); + size_t datum_len = read_int(f); -+ if (name_len + datum_len < name_len) ++#ifdef HAVE_LINUX_XATTRS ++ size_t extra_len = 0; ++#else ++ size_t extra_len = am_root ? RPRE_LEN : 0; ++ if (datum_len + extra_len < datum_len) + out_of_memory("receive_xattr"); /* overflow */ -+ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count); -+ ptr = new_array(char, name_len + datum_len); ++#endif ++ if (name_len + datum_len + extra_len < name_len) ++ out_of_memory("receive_xattr"); /* overflow */ ++ ptr = new_array(char, name_len + datum_len + extra_len); + if (!ptr) + out_of_memory("receive_xattr"); -+ read_buf(f, ptr, name_len); -+ read_buf(f, ptr + name_len, datum_len); ++ name = ptr + datum_len + extra_len; ++ read_buf(f, name, name_len); ++ read_buf(f, ptr, datum_len); ++#ifdef HAVE_LINUX_XATTRS ++ /* Non-root can only save the user namespace. */ ++ if (!am_root && !HAS_PREFIX(name, USER_PREFIX)) { ++ free(ptr); ++ continue; ++ } ++#else ++ /* This OS only has a user namespace, so we either ++ * strip the user prefix, or we put a non-user ++ * namespace inside our rsync hierarchy. */ ++ if (HAS_PREFIX(name, USER_PREFIX)) { ++ name += UPRE_LEN; ++ name_len -= UPRE_LEN; ++ } else if (am_root) { ++ name -= RPRE_LEN; ++ name_len += RPRE_LEN; ++ memcpy(name, RSYNC_PREFIX, RPRE_LEN); ++ } else { ++ free(ptr); ++ continue; ++ } ++#endif ++ rxa = EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count); ++ rxa->name = name; ++ rxa->datum = ptr; + rxa->name_len = name_len; + rxa->datum_len = datum_len; -+ rxa->name = ptr; -+ rxa->datum = ptr + name_len; + } -+ ndx = rsync_xal_l.count; -+ rsync_xal_store(&temp_xattr); ++ ndx = rsync_xal_l.count; /* pre-incremented count */ ++ rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */ + } else if (tag == 'x') { + ndx = read_int(f); + if (ndx < 0 || (size_t)ndx >= rsync_xal_l.count) { -+ rprintf(FERROR, "%s: receive_xattr: xa index %d out of range\n", -+ f_name(file, NULL), ndx); ++ rprintf(FERROR, "receive_xattr: xa index %d out of" ++ " range for %s\n", ndx, f_name(file, NULL)); + exit_cleanup(RERR_STREAMIO); + } + } else { -+ rprintf(FERROR, -+ "%s: receive_xattr: unknown extended attribute type tag: %c\n", -+ f_name(file, NULL), tag); ++ rprintf(FERROR, "receive_xattr: unknown extended attribute" ++ " type tag (%c) for %s\n", tag, f_name(file, NULL)); + exit_cleanup(RERR_STREAMIO); -+ ndx = 0; /* silence a compiler warning... */ + } + -+ SIVAL(ndx_ptr, 0, ndx); ++ F_XATTR(file) = ndx; +} + +/* Turn the xattr data in statx into cached xattr data, setting the index + * values in the file struct. */ +void cache_xattr(struct file_struct *file, statx *sxp) +{ -+ char *ndx_ptr = (char*)file + file_struct_len; + int ndx; + + if (!sxp->xattr) @@ -818,10 +938,10 @@ TODO: + + ndx = find_matching_xattr(sxp->xattr); + if (ndx == -1) -+ rsync_xal_store(sxp->xattr); ++ rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */ + free_xattr(sxp); + -+ SIVAL(ndx_ptr, 0, ndx); ++ F_XATTR(file) = ndx; +} + +static int rsync_xal_set(const char *fname, item_list *xalp) @@ -831,9 +951,10 @@ TODO: + int ret = 0; + + for (i = 0; i < xalp->count; i++) { -+ int status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len, 0); ++ int status = sys_lsetxattr(fname, rxas[i].name, rxas[i].datum, rxas[i].datum_len); + if (status < 0) { -+ rsyserr(FERROR, errno, "%s: rsync_xal_set: lsetxattr %s failed", ++ rsyserr(FERROR, errno, ++ "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed", + fname, rxas[i].name); + ret = -1; + } @@ -845,13 +966,17 @@ TODO: +int set_xattr(const char *fname, const struct file_struct *file, UNUSED(statx *sxp)) +{ + int ndx; -+ char *ndx_ptr = (char*)file + file_struct_len; + item_list *lst = rsync_xal_l.items; + + if (dry_run) + return 1; /* FIXME: --dry-run needs to compute this value */ + -+ ndx = IVAL(ndx_ptr, 0); ++ if (read_only || list_only) { ++ errno = EROFS; ++ return -1; ++ } ++ ++ ndx = F_XATTR(file); + return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */ +} +