Various xattr fixes:
[rsync/rsync.git] / xattrs.c
index f58908d..6ab9698 100644 (file)
--- a/xattrs.c
+++ b/xattrs.c
@@ -64,6 +64,8 @@ extern int checksum_seed;
 #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"
 
 typedef struct {
        char *datum, *name;
@@ -90,7 +92,7 @@ static void rsync_xal_free(item_list *xalp)
        xalp->count = 0;
 }
 
-void free_xattr(statx *sxp)
+void free_xattr(stat_x *sxp)
 {
        if (!sxp->xattr)
                return;
@@ -130,7 +132,7 @@ static ssize_t get_xattr_names(const char *fname)
        if (errno == ERANGE) {
                list_len = sys_llistxattr(fname, NULL, 0);
                if (list_len < 0) {
-                       rsyserr(FERROR, errno,
+                       rsyserr(FERROR_XFER, errno,
                                "get_xattr_names: llistxattr(\"%s\",0) failed",
                                fname);
                        return -1;
@@ -146,7 +148,7 @@ static ssize_t get_xattr_names(const char *fname)
                        return list_len;
        }
 
-       rsyserr(FERROR, errno,
+       rsyserr(FERROR_XFER, errno,
                "get_xattr_names: llistxattr(\"%s\",%ld) failed",
                fname, (long)namebuf_len);
        return -1;
@@ -159,32 +161,35 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
                            int no_missing_error)
 {
        size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
+       size_t extra_len = *len_ptr;
        char *ptr;
 
+       *len_ptr = datum_len;
+
        if (datum_len == (size_t)-1) {
                if (errno == ENOTSUP || no_missing_error)
                        return NULL;
-               rsyserr(FERROR, errno,
+               rsyserr(FERROR_XFER, errno,
                        "get_xattr_data: lgetxattr(\"%s\",\"%s\",0) failed",
                        fname, name);
                return NULL;
        }
 
-       if (datum_len + *len_ptr < datum_len /* checks for overflow */
-        || !(ptr = new_array(char, datum_len + *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)))
                out_of_memory("get_xattr_data");
 
-       *len_ptr = datum_len;
-
        if (datum_len) {
                size_t len = sys_lgetxattr(fname, name, ptr, datum_len);
                if (len != datum_len) {
                        if (len == (size_t)-1) {
-                               rsyserr(FERROR, errno,
+                               rsyserr(FERROR_XFER, errno,
                                    "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
                                    " failed", fname, name, (long)datum_len);
                        } else {
-                               rprintf(FERROR,
+                               rprintf(FERROR_XFER,
                                    "get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
                                    " returned %ld\n", fname, name,
                                    (long)datum_len, (long)len);
@@ -211,7 +216,7 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
                return -1;
 
        for (name = namebuf; list_len > 0; name += name_len) {
-               rsync_xa *rxas;
+               rsync_xa *rxa;
 
                name_len = strlen(name) + 1;
                list_len -= name_len;
@@ -225,9 +230,12 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
 #endif
 
                /* No rsync.%FOO attributes are copied w/o 2 -X options. */
-               if (preserve_xattrs < 2 && name_len > RPRE_LEN
-                && name[RPRE_LEN] == '%' && HAS_PREFIX(name, RSYNC_PREFIX))
-                       continue;
+               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))
+                               continue;
+               }
 
                datum_len = name_len; /* Pass extra size to get_xattr_data() */
                if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
@@ -248,19 +256,19 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
                        name_offset = datum_len;
 
 #ifdef HAVE_LINUX_XATTRS
-               if (am_root < 0 && name_len > RPRE_LEN
+               if (am_root < 0 && name_len > RPRE_LEN && name[RPRE_LEN] != '%'
                 && HAS_PREFIX(name, RSYNC_PREFIX)) {
                        name += RPRE_LEN;
                        name_len -= RPRE_LEN;
                }
 #endif
 
-               rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
-               rxas->name = ptr + name_offset;
-               memcpy(rxas->name, name, name_len);
-               rxas->datum = ptr;
-               rxas->name_len = name_len;
-               rxas->datum_len = datum_len;
+               rxa = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL);
+               rxa->name = ptr + name_offset;
+               memcpy(rxa->name, name, name_len);
+               rxa->datum = ptr;
+               rxa->name_len = name_len;
+               rxa->datum_len = datum_len;
        }
        if (xalp->count > 1)
                qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
@@ -268,7 +276,7 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
 }
 
 /* Read the xattr(s) for this filename. */
-int get_xattr(const char *fname, statx *sxp)
+int get_xattr(const char *fname, stat_x *sxp)
 {
        sxp->xattr = new(item_list);
        *sxp->xattr = empty_xattr;
@@ -330,7 +338,7 @@ static void rsync_xal_store(item_list *xalp)
 }
 
 /* Send the make_xattr()-generated xattr list for this flist entry. */
-int send_xattr(statx *sxp, int f)
+int send_xattr(stat_x *sxp, int f)
 {
        int ndx = find_matching_xattr(sxp->xattr);
 
@@ -376,7 +384,7 @@ int send_xattr(statx *sxp, int f)
 /* Return a flag indicating if we need to change a file's xattrs.  If
  * "find_all" is specified, also mark any abbreviated xattrs that we
  * need so that send_xattr_request() can tell the sender about them. */
-int xattr_diff(struct file_struct *file, statx *sxp, int find_all)
+int xattr_diff(struct file_struct *file, stat_x *sxp, int find_all)
 {
        item_list *lst = rsync_xal_l.items;
        rsync_xa *snd_rxa, *rec_rxa;
@@ -481,8 +489,11 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
                        char *ptr;
 
                        /* Re-read the long datum. */
-                       if (!(ptr = get_xattr_data(fname, rxa->name, &len, 0)))
+                       if (!(ptr = get_xattr_data(fname, rxa->name, &len, 0))) {
+                               rprintf(FERROR_XFER, "failed to re-read xattr %s for %s\n", rxa->name, fname);
+                               write_varint(f_out, 0);
                                continue;
+                       }
 
                        write_varint(f_out, len); /* length might have changed! */
                        write_buf(f_out, ptr, len);
@@ -518,12 +529,12 @@ void xattr_clear_locals(struct file_struct *file)
  * any needed xattrs with a flag that lets us know they need to be sent to
  * the receiver.  When called by the receiver, reads the sent data and
  * stores it in place of its checksum. */
-void recv_xattr_request(struct file_struct *file, int f_in)
+int recv_xattr_request(struct file_struct *file, int f_in)
 {
        item_list *lst = rsync_xal_l.items;
        char *old_datum, *name;
        rsync_xa *rxa;
-       int rel_pos, cnt;
+       int rel_pos, cnt, got_xattr_data = 0;
 
        if (F_XATTR(file) < 0) {
                rprintf(FERROR, "recv_xattr_request: internal data error!\n");
@@ -561,7 +572,10 @@ void recv_xattr_request(struct file_struct *file, int f_in)
                rxa->name = name;
                free(old_datum);
                read_buf(f_in, rxa->datum, rxa->datum_len);
+               got_xattr_data = 1;
        }
+
+       return got_xattr_data;
 }
 
 /* ------------------------------------------------------------------------- */
@@ -657,9 +671,9 @@ void receive_xattr(struct file_struct *file, int f)
        F_XATTR(file) = ndx;
 }
 
-/* Turn the xattr data in statx into cached xattr data, setting the index
+/* 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, statx *sxp)
+void cache_xattr(struct file_struct *file, stat_x *sxp)
 {
        int ndx;
 
@@ -674,7 +688,7 @@ void cache_xattr(struct file_struct *file, statx *sxp)
 }
 
 static int rsync_xal_set(const char *fname, item_list *xalp,
-                        const char *fnamecmp, statx *sxp)
+                        const char *fnamecmp, stat_x *sxp)
 {
        rsync_xa *rxas = xalp->items;
        ssize_t list_len;
@@ -717,7 +731,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
                        if (fname == fnamecmp)
                                ; /* Value is already set when identical */
                        else if (sys_lsetxattr(fname, name, ptr, len) < 0) {
-                               rsyserr(FERROR, errno,
+                               rsyserr(FERROR_XFER, errno,
                                        "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
                                        fname, name);
                                ret = -1;
@@ -740,7 +754,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, errno,
+                       rsyserr(FERROR_XFER, errno,
                                "rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
                                fname, name);
                        ret = -1;
@@ -760,6 +774,9 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
                            : !HAS_PREFIX(name, USER_PREFIX))
                        continue;
 #endif
+               if (am_root < 0 && name_len > RPRE_LEN
+                && name[RPRE_LEN] == '%' && strcmp(name, XSTAT_ATTR) == 0)
+                       continue;
 
                for (i = 0; i < xalp->count; i++) {
                        if (strcmp(name, rxas[i].name) == 0)
@@ -767,7 +784,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
                }
                if (i == xalp->count) {
                        if (sys_lremovexattr(fname, name) < 0) {
-                               rsyserr(FERROR, errno,
+                               rsyserr(FERROR_XFER, errno,
                                        "rsync_xal_clear: lremovexattr(\"%s\",\"%s\") failed",
                                        fname, name);
                                ret = -1;
@@ -781,7 +798,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
 
 /* Set extended attributes on indicated filename. */
 int set_xattr(const char *fname, const struct file_struct *file,
-             const char *fnamecmp, statx *sxp)
+             const char *fnamecmp, stat_x *sxp)
 {
        int ndx;
        item_list *lst = rsync_xal_l.items;
@@ -798,6 +815,32 @@ int set_xattr(const char *fname, const struct file_struct *file,
        return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp);
 }
 
+#ifdef SUPPORT_ACLS
+char *get_xattr_acl(const char *fname, int is_access_acl, size_t *len_p)
+{
+       const char *name = is_access_acl ? XACC_ACL_ATTR : XDEF_ACL_ATTR;
+       *len_p = 0; /* no extra data alloc needed from get_xattr_data() */
+       return get_xattr_data(fname, name, len_p, 1);
+}
+
+int set_xattr_acl(const char *fname, int is_access_acl, const char *buf, size_t buf_len)
+{
+       const char *name = is_access_acl ? XACC_ACL_ATTR : XDEF_ACL_ATTR;
+       if (sys_lsetxattr(fname, name, buf, buf_len) < 0) {
+               rsyserr(FERROR_XFER, errno,
+                       "set_xattr_acl: lsetxattr(\"%s\",\"%s\") failed",
+                       fname, name);
+               return -1;
+       }
+       return 0;
+}
+
+int del_def_xattr_acl(const char *fname)
+{
+       return sys_lremovexattr(fname, XDEF_ACL_ATTR);
+}
+#endif
+
 int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
 {
        int mode, rdev_major, rdev_minor, uid, gid, len;
@@ -829,7 +872,7 @@ int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst
                        xst->st_gid = 0;
                        return 0;
                }
-               rsyserr(FERROR, errno, "failed to read xattr %s for %s",
+               rsyserr(FERROR_XFER, errno, "failed to read xattr %s for %s",
                        XSTAT_ATTR, full_fname(fname));
                return -1;
        }
@@ -850,7 +893,7 @@ int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst
        return 0;
 }
 
-int set_stat_xattr(const char *fname, struct file_struct *file)
+int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
 {
        STRUCT_STAT fst, xst;
        dev_t rdev;
@@ -860,19 +903,19 @@ int set_stat_xattr(const char *fname, struct file_struct *file)
                return 0;
 
        if (read_only || list_only) {
-               rsyserr(FERROR, EROFS, "failed to write xattr %s for %s",
+               rsyserr(FERROR_XFER, EROFS, "failed to write xattr %s for %s",
                        XSTAT_ATTR, full_fname(fname));
                return -1;
        }
 
        if (x_lstat(fname, &fst, &xst) < 0) {
-               rsyserr(FERROR, errno, "failed to re-stat %s",
+               rsyserr(FERROR_XFER, errno, "failed to re-stat %s",
                        full_fname(fname));
                return -1;
        }
 
        fst.st_mode &= (_S_IFMT | CHMOD_BITS);
-       fmode = file->mode & (_S_IFMT | CHMOD_BITS);
+       fmode = new_mode & (_S_IFMT | CHMOD_BITS);
 
        if (IS_DEVICE(fmode) || IS_SPECIAL(fmode)) {
                uint32 *devp = F_RDEV_P(file);
@@ -892,7 +935,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file)
         && fst.st_uid == F_OWNER(file) && fst.st_gid == F_GROUP(file)) {
                /* xst.st_mode will be 0 if there's no current stat xattr */
                if (xst.st_mode && sys_lremovexattr(fname, XSTAT_ATTR) < 0) {
-                       rsyserr(FERROR, errno,
+                       rsyserr(FERROR_XFER, errno,
                                "delete of stat xattr failed for %s",
                                full_fname(fname));
                        return -1;
@@ -910,7 +953,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file)
                if (sys_lsetxattr(fname, XSTAT_ATTR, buf, len) < 0) {
                        if (errno == EPERM && S_ISLNK(fst.st_mode))
                                return 0;
-                       rsyserr(FERROR, errno,
+                       rsyserr(FERROR_XFER, errno,
                                "failed to write xattr %s for %s",
                                XSTAT_ATTR, full_fname(fname));
                        return -1;