* Written by Jay Fenlason, vaguely based on the ACLs patch.
*
* Copyright (C) 2004 Red Hat, Inc.
- * Copyright (C) 2006-2020 Wayne Davison
+ * Copyright (C) 2006-2022 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
extern int checksum_seed;
extern int saw_xattr_filter;
+extern struct name_num_item *xattr_sum_nni;
+extern int xattr_sum_len;
+
#define RSYNC_XAL_INITIAL 5
#define RSYNC_XAL_LIST_INITIAL 100
+#define MAX_XATTR_DIGEST_LEN MD5_DIGEST_LEN
#define MAX_FULL_DATUM 32
#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) && strncmp(str, prfx, sizeof (prfx) - 1) == 0)
#define SPRE_LEN ((int)sizeof SYSTEM_PREFIX - 1)
#ifdef HAVE_LINUX_XATTRS
-#define MIGHT_NEED_RPRE (am_root < 0)
+#define MIGHT_NEED_RPRE (am_root <= 0)
#define RSYNC_PREFIX USER_PREFIX "rsync."
#else
#define MIGHT_NEED_RPRE am_root
if (!datum_len && !extra_len)
extra_len = 1; /* request non-zero amount of memory */
- if (datum_len + extra_len < datum_len)
+ if (SIZE_MAX - datum_len < extra_len)
overflow_exit("get_xattr_data");
ptr = new_array(char, datum_len + extra_len);
if (datum_len > MAX_FULL_DATUM) {
/* For large datums, we store a flag and a checksum. */
- name_offset = 1 + MAX_DIGEST_LEN;
- sum_init(-1, checksum_seed);
+ name_offset = 1 + MAX_XATTR_DIGEST_LEN;
+ sum_init(xattr_sum_nni, checksum_seed);
sum_update(ptr, datum_len);
free(ptr);
{
const rsync_xa *rxas = xalp->items;
size_t i;
- int64 key = hashlittle(&xalp->count, sizeof xalp->count);
+ int64 key = hashlittle2(&xalp->count, sizeof xalp->count);
for (i = 0; i < xalp->count; i++) {
- key += hashlittle(rxas[i].name, rxas[i].name_len);
+ key += hashlittle2(rxas[i].name, rxas[i].name_len);
if (rxas[i].datum_len > MAX_FULL_DATUM)
- key += hashlittle(rxas[i].datum, MAX_DIGEST_LEN);
+ key += hashlittle2(rxas[i].datum, xattr_sum_len);
else
- key += hashlittle(rxas[i].datum, rxas[i].datum_len);
- }
-
- if (key == 0) {
- /* This is very unlikely, but we should never
- * return 0 as hashtable_find() doesn't like it. */
- return 1;
+ key += hashlittle2(rxas[i].datum, rxas[i].datum_len);
}
return key;
if (rxas1[j].datum_len > MAX_FULL_DATUM) {
if (memcmp(rxas1[j].datum + 1,
rxas2[j].datum + 1,
- MAX_DIGEST_LEN) != 0)
+ xattr_sum_len) != 0)
break;
} else {
if (memcmp(rxas1[j].datum, rxas2[j].datum,
if (rsync_xal_h == NULL)
rsync_xal_h = hashtable_create(512, HT_KEY64);
- if (rsync_xal_h == NULL)
- out_of_memory("rsync_xal_h hashtable_create()");
new_ref = new0(rsync_xa_list_ref);
new_ref->ndx = ndx;
#endif
write_buf(f, name, name_len);
if (rxa->datum_len > MAX_FULL_DATUM)
- write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN);
+ write_buf(f, rxa->datum + 1, xattr_sum_len);
else
write_bigbuf(f, rxa->datum, rxa->datum_len);
}
else if (snd_rxa->datum_len > MAX_FULL_DATUM) {
same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
&& memcmp(snd_rxa->datum + 1, rec_rxa->datum + 1,
- MAX_DIGEST_LEN) == 0;
+ xattr_sum_len) == 0;
/* Flag unrequested items that we need. */
if (!same && find_all && snd_rxa->datum[0] == XSTATE_ABBREV)
snd_rxa->datum[0] = XSTATE_TODO;
old_datum = rxa->datum;
rxa->datum_len = read_varint(f_in);
- if (rxa->name_len + rxa->datum_len < rxa->name_len)
+ if (SIZE_MAX - rxa->name_len < rxa->datum_len)
overflow_exit("recv_xattr_request");
rxa->datum = new_array(char, rxa->datum_len + rxa->name_len);
name = rxa->datum + rxa->datum_len;
rsync_xa *rxa;
size_t name_len = read_varint(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 dget_len = datum_len > MAX_FULL_DATUM ? 1 + (size_t)xattr_sum_len : datum_len;
size_t extra_len = MIGHT_NEED_RPRE ? RPRE_LEN : 0;
- if ((dget_len + extra_len < dget_len)
- || (dget_len + extra_len + name_len < dget_len + extra_len))
+ if (SIZE_MAX - dget_len < extra_len || SIZE_MAX - dget_len - extra_len < name_len)
overflow_exit("receive_xattr");
ptr = new_array(char, dget_len + extra_len + name_len);
name = ptr + dget_len + extra_len;
read_buf(f, ptr, dget_len);
else {
*ptr = XSTATE_ABBREV;
- read_buf(f, ptr + 1, MAX_DIGEST_LEN);
+ read_buf(f, ptr + 1, xattr_sum_len);
}
if (saw_xattr_filter) {
rsync_xa *rxas = xalp->items;
ssize_t list_len;
size_t i, len;
- char *name, *ptr, sum[MAX_DIGEST_LEN];
+ char *name, *ptr, sum[MAX_XATTR_DIGEST_LEN];
#ifdef HAVE_LINUX_XATTRS
int user_only = am_root <= 0;
#endif
name = rxas[i].name;
if (XATTR_ABBREV(rxas[i])) {
- int sum_len;
/* See if the fnamecmp version is identical. */
len = name_len = rxas[i].name_len;
if ((ptr = get_xattr_data(fnamecmp, name, &len, 1)) == NULL) {
goto still_abbrev;
}
- sum_init(-1, checksum_seed);
+ sum_init(xattr_sum_nni, checksum_seed);
sum_update(ptr, len);
- sum_len = sum_end(sum);
- if (memcmp(sum, rxas[i].datum + 1, sum_len) != 0) {
+ sum_end(sum);
+ if (memcmp(sum, rxas[i].datum + 1, xattr_sum_len) != 0) {
free(ptr);
goto still_abbrev;
}
{
rsync_xa_list *glst = rsync_xal_l.items;
item_list *lst;
- int ndx;
+ int ndx, added_write_perm = 0;
if (dry_run)
return 1; /* FIXME: --dry-run needs to compute this value */
}
#endif
+ /* If the target file lacks write permission, we try to add it
+ * temporarily so we can change the extended attributes. */
+ if (!am_root
+#ifdef SUPPORT_LINKS
+ && !S_ISLNK(sxp->st.st_mode)
+#endif
+ && access(fname, W_OK) < 0
+ && do_chmod(fname, (sxp->st.st_mode & CHMOD_BITS) | S_IWUSR) == 0)
+ added_write_perm = 1;
+
ndx = F_XATTR(file);
glst += ndx;
lst = &glst->xa_items;
- return rsync_xal_set(fname, lst, fnamecmp, sxp);
+ int return_value = rsync_xal_set(fname, lst, fnamecmp, sxp);
+ if (added_write_perm) /* remove the temporary write permission */
+ do_chmod(fname, sxp->st.st_mode);
+ return return_value;
}
#ifdef SUPPORT_ACLS
int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
{
- int mode, rdev_major, rdev_minor, uid, gid, len;
+ unsigned int mode;
+ int rdev_major, rdev_minor, uid, gid, len;
char buf[256];
if (am_root >= 0 || IS_DEVICE(fst->st_mode) || IS_SPECIAL(fst->st_mode))