The improved --xattrs option is landing on the trunk.

This commit is contained in:
Wayne Davison committed 2007-04-07 17:22:25 +00:00
1 parent 1c53e5171c
commit 16edf86595
18 files changed
+1311 -64

No files matched your search

+2 -2
View File
@@ -28,13 +28,13 @@ VERSION=@VERSION@
HEADERS=byteorder.h config.h errcode.h proto.h rsync.h smb_acls.h lib/pool_alloc.h
LIBOBJ=lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o \
lib/permstring.o lib/pool_alloc.o lib/sysacls.o @LIBOBJS@
lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o @LIBOBJS@
ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
util.o main.o checksum.o match.o syscall.o log.o backup.o
OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o \
fileio.o batch.o clientname.o chmod.o acls.o
fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
OBJS3=progress.o pipe.o
DAEMON_OBJ = params.o loadparm.o clientserver.o access.o connection.o authenticate.o
popt_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
+23 -2
View File
@@ -23,6 +23,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;
@@ -133,6 +134,9 @@ static int make_bak_dir(char *fullpath)
} else {
#ifdef SUPPORT_ACLS
sx.acc_acl = sx.def_acl = NULL;
#endif
#ifdef SUPPORT_XATTRS
sx.xattr = NULL;
#endif
if (!(file = make_file(rel, NULL, NULL, 0, NO_FILTERS)))
continue;
@@ -143,7 +147,14 @@ static int make_bak_dir(char *fullpath)
free_acl(&sx);
}
#endif
set_file_attrs(fullpath, file, NULL, 0);
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
get_xattr(rel, &sx);
cache_xattr(file, &sx);
free_xattr(&sx);
}
#endif
set_file_attrs(fullpath, file, NULL, NULL, 0);
free(file);
}
}
@@ -194,6 +205,9 @@ static int keep_backup(const char *fname)
#ifdef SUPPORT_ACLS
sx.acc_acl = sx.def_acl = NULL;
#endif
#ifdef SUPPORT_XATTRS
sx.xattr = NULL;
#endif
if (!(file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
return 1; /* the file could have disappeared */
@@ -210,6 +224,13 @@ static int keep_backup(const char *fname)
free_acl(&sx);
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
get_xattr(fname, &sx);
cache_xattr(file, &sx);
free_xattr(&sx);
}
#endif
/* Check to see if this is a device file, or link */
if ((am_root && preserve_devices && IS_DEVICE(file->mode))
@@ -288,7 +309,7 @@ static int keep_backup(const char *fname)
robust_unlink(fname); /* Just in case... */
}
}
set_file_attrs(buf, file, NULL, 0);
set_file_attrs(buf, file, NULL, fname, 0);
unmake_file(file);
if (verbose > 1) {
+1 -1
View File
@@ -142,7 +142,7 @@ NORETURN void _exit_cleanup(int code, const char *file, int line)
flush_write_file(cleanup_fd_w);
close(cleanup_fd_w);
}
finish_transfer(cleanup_new_fname, fname, NULL,
finish_transfer(cleanup_new_fname, fname, NULL, NULL,
cleanup_file, 0, !partial_dir);
}
+10
View File
@@ -43,6 +43,7 @@ extern int protocol_version;
extern int preserve_uid;
extern int preserve_gid;
extern int preserve_acls;
extern int preserve_xattrs;
extern int preserve_hard_links;
extern int need_messages_from_generator;
extern int delete_mode, delete_before, delete_during, delete_after;
@@ -65,6 +66,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)
preserve_xattrs = ++file_extra_cnt;
if (remote_protocol == 0) {
if (!read_batch)
@@ -119,6 +122,13 @@ void setup_protocol(int f_out,int f_in)
protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
if (preserve_xattrs) {
rprintf(FERROR,
"--xattrs requires protocol 30 or higher"
" (negotiated %d).\n",
protocol_version);
exit_cleanup(RERR_PROTOCOL);
}
}
if (delete_mode && !(delete_before+delete_during+delete_after)) {
+41 -1
View File
@@ -856,7 +856,7 @@ else
AC_DEFINE(SUPPORT_ACLS, 1)
LIBS="$LIBS -lpacl"
;;
*darwin*)
darwin*)
AC_MSG_RESULT(MacOS ACLs not yet supported)
;;
*)
@@ -891,6 +891,46 @@ samba_cv_HAVE_ACL_GET_PERM_NP=yes,samba_cv_HAVE_ACL_GET_PERM_NP=no)])
esac
fi
AC_CHECK_HEADERS(attr/xattr.h)
AC_CHECK_HEADERS(sys/xattr.h)
AC_CHECK_HEADERS(sys/extattr.h)
#################################################
# check for extended attribute support
AC_MSG_CHECKING(whether to support extended attributes)
AC_ARG_ENABLE(xattr-support,
AC_HELP_STRING([--disable-xattr-support],
[Turn off extended attribute support]))
if test x"$enable_xattr_support" = x"no"; then
AC_MSG_RESULT(no)
else
case "$host_os" in
*linux*)
AC_MSG_RESULT(Using Linux xattrs)
AC_DEFINE(HAVE_LINUX_XATTRS, 1, [True if you have Linux xattrs])
AC_DEFINE(SUPPORT_XATTRS, 1, [Define to 1 to add support for extended attributes])
;;
darwin*)
AC_MSG_RESULT(Using OS X xattrs)
AC_DEFINE(HAVE_OSX_XATTRS, 1, [True if you have Mac OS X xattrs])
AC_DEFINE(SUPPORT_XATTRS, 1)
;;
freebsd*)
AC_MSG_RESULT(Using FreeBSD extattrs)
AC_DEFINE(HAVE_FREEBSD_XATTRS, 1, [True if you have FreeBSD xattrs])
AC_DEFINE(SUPPORT_XATTRS, 1)
;;
*)
if test x"$enable_xattr_support" = x"yes"; then
AC_MSG_ERROR(Failed to find extended attribute support)
else
AC_MSG_RESULT(No extended attribute support found)
fi
;;
esac
fi
AC_CONFIG_FILES([Makefile lib/dummy zlib/dummy popt/dummy shconfig])
AC_OUTPUT
+19 -1
View File
@@ -43,6 +43,7 @@ extern int one_file_system;
extern int copy_dirlinks;
extern int keep_dirlinks;
extern int preserve_acls;
extern int preserve_xattrs;
extern int preserve_links;
extern int preserve_hard_links;
extern int preserve_devices;
@@ -888,6 +889,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
if (preserve_acls && !S_ISLNK(mode))
receive_acl(file, f);
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs)
receive_xattr(file, f );
#endif
if (S_ISREG(mode) || S_ISLNK(mode))
stats.total_size += file_length;
@@ -1160,7 +1165,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
int flags, int filter_flags)
{
struct file_struct *file;
#ifdef SUPPORT_ACLS
#if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
statx sx;
#endif
@@ -1179,6 +1184,13 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
return NULL;
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs && f >= 0) {
sx.xattr = NULL;
if (get_xattr(fname, &sx) < 0)
return NULL;
}
#endif
maybe_emit_filelist_progress(flist->count + flist_count_offset);
@@ -1191,6 +1203,12 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
send_acl(&sx, f);
free_acl(&sx);
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
F_XATTR(file) = send_xattr(&sx, f);
free_xattr(&sx);
}
#endif
}
return file;
+57 -16
View File
@@ -36,6 +36,7 @@ extern int relative_paths;
extern int implied_dirs;
extern int keep_dirlinks;
extern int preserve_acls;
extern int preserve_xattrs;
extern int preserve_links;
extern int preserve_devices;
extern int preserve_specials;
@@ -532,11 +533,19 @@ int unchanged_attrs(const char *fname, struct file_struct *file, statx *sxp)
return 0;
}
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
if (!XATTR_READY(*sxp))
get_xattr(fname, sxp);
if (xattr_diff(file, sxp, 0))
return 0;
}
#endif
return 1;
}
void itemize(const char *fname, struct file_struct *file, int ndx, int statret,
void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statret,
statx *sxp, int32 iflags, uchar fnamecmp_type,
const char *xname)
{
@@ -562,16 +571,29 @@ void itemize(const char *fname, struct file_struct *file, int ndx, int statret,
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode)) {
if (!ACL_READY(*sxp))
get_acl(fname, sxp);
get_acl(fnamecmp, sxp);
if (set_acl(NULL, file, sxp) == 0)
iflags |= ITEM_REPORT_ACL;
}
#endif
} else
#ifdef SUPPORT_XATTRS
if (preserve_xattrs) {
if (!XATTR_READY(*sxp))
get_xattr(fnamecmp, sxp);
if (xattr_diff(file, sxp, 1))
iflags |= ITEM_REPORT_XATTR;
}
#endif
} else {
#ifdef SUPPORT_XATTRS
if (preserve_xattrs && xattr_diff(file, NULL, 1))
iflags |= ITEM_REPORT_XATTR;
#endif
iflags |= ITEM_IS_NEW;
}
iflags &= 0xffff;
if ((iflags & SIGNIFICANT_ITEM_FLAGS || verbose > 1
if ((iflags & (SIGNIFICANT_ITEM_FLAGS|ITEM_REPORT_XATTR) || verbose > 1
|| stdout_format_has_i > 1 || (xname && *xname)) && !read_batch) {
if (protocol_version >= 29) {
if (ndx >= 0)
@@ -581,6 +603,10 @@ void itemize(const char *fname, struct file_struct *file, int ndx, int statret,
write_byte(sock_f_out, fnamecmp_type);
if (iflags & ITEM_XNAME_FOLLOWS)
write_vstring(sock_f_out, xname, strlen(xname));
#ifdef SUPPORT_XATTRS
if (iflags & ITEM_REPORT_XATTR && !dry_run)
send_xattr_request(NULL, file, sock_f_out);
#endif
} else if (ndx >= 0) {
enum logcode code = logfile_format_has_i ? FINFO : FCLIENT;
log_item(code, file, &stats, iflags, xname);
@@ -855,14 +881,14 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
itemize(fname, file, ndx, 1, sxp,
itemize(cmpbuf, file, ndx, 1, sxp,
ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
0, "");
}
} else
#endif
if (itemizing)
itemize(fname, file, ndx, 0, sxp, 0, 0, NULL);
itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
if (verbose > 1 && maybe_ATTRS_REPORT)
rprintf(FCLIENT, "%s is uptodate\n", fname);
return -2;
@@ -879,9 +905,13 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
}
return -1;
}
set_file_attrs(fname, file, NULL, cmpbuf, 0);
if (itemizing)
itemize(fname, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
set_file_attrs(fname, file, NULL, 0);
itemize(cmpbuf, file, ndx, 0, sxp, ITEM_LOCAL_CHANGE, 0, NULL);
#ifdef SUPPORT_XATTRS
if (preserve_xattrs)
xattr_clear_locals(file);
#endif
if (maybe_ATTRS_REPORT
&& ((!itemizing && verbose && match_level == 2)
|| (verbose > 1 && match_level == 3))) {
@@ -1029,7 +1059,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
: ITEM_LOCAL_CHANGE
+ (match_level == 3 ? ITEM_XNAME_FOLLOWS : 0);
char *lp = match_level == 3 ? "" : NULL;
itemize(fname, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
itemize(cmpbuf, file, ndx, 0, sxp, chg + ITEM_MATCHED, 0, lp);
}
if (verbose > 1 && maybe_ATTRS_REPORT) {
rprintf(FCLIENT, "%s%s is uptodate\n",
@@ -1111,6 +1141,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
}
#ifdef SUPPORT_ACLS
sx.acc_acl = sx.def_acl = NULL;
#endif
#ifdef SUPPORT_XATTRS
sx.xattr = NULL;
#endif
if (dry_run > 1) {
if (fuzzy_dirlist) {
@@ -1224,7 +1257,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto cleanup;
}
}
if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, 0)
if (set_file_attrs(fname, file, real_ret ? NULL : &real_sx, NULL, 0)
&& verbose && code != FNONE && f_out != -1)
rprintf(code, "%s/\n", fname);
if (real_ret != 0 && one_file_system)
@@ -1278,9 +1311,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0
&& strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
/* The link is pointing to the right place. */
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
if (itemizing)
itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
@@ -1317,7 +1350,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
full_fname(fname), sl);
} else {
set_file_attrs(fname, file, NULL, 0);
set_file_attrs(fname, file, NULL, NULL, 0);
if (itemizing) {
itemize(fname, file, ndx, statret, &sx,
ITEM_LOCAL_CHANGE, 0, NULL);
@@ -1357,9 +1390,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
&& BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
&& sx.st.st_rdev == rdev) {
/* The device or special file is identical. */
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
if (itemizing)
itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
@@ -1399,7 +1432,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
rsyserr(FERROR, errno, "mknod %s failed",
full_fname(fname));
} else {
set_file_attrs(fname, file, NULL, 0);
set_file_attrs(fname, file, NULL, NULL, 0);
if (itemizing) {
itemize(fname, file, ndx, statret, &sx,
ITEM_LOCAL_CHANGE, 0, NULL);
@@ -1529,9 +1562,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
do_unlink(partialptr);
handle_partial_dir(partialptr, PDIR_DELETE);
}
set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
if (itemizing)
itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
set_file_attrs(fname, file, &sx, maybe_ATTRS_REPORT);
#ifdef SUPPORT_HARD_LINKS
if (preserve_hard_links && F_IS_HLINKED(file))
finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
@@ -1635,6 +1668,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
#ifdef SUPPORT_ACLS
if (preserve_acls)
free_acl(&real_sx);
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs)
free_xattr(&real_sx);
#endif
}
@@ -1657,7 +1694,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
if (f_copy >= 0) {
close(f_copy);
set_file_attrs(backupptr, back_file, NULL, 0);
set_file_attrs(backupptr, back_file, NULL, NULL, 0);
if (verbose > 1) {
rprintf(FINFO, "backed up %s to %s\n",
fname, backupptr);
@@ -1671,6 +1708,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
#ifdef SUPPORT_ACLS
if (preserve_acls)
free_acl(&sx);
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs)
free_xattr(&sx);
#endif
return;
}
+135
View File
@@ -0,0 +1,135 @@
/*
* Extended attribute support for rsync.
*
* Copyright (C) 2004 Red Hat, Inc.
* Written by Jay Fenlason.
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "rsync.h"
#include "sysxattrs.h"
#ifdef SUPPORT_XATTRS
#if defined HAVE_LINUX_XATTRS
ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
{
return lgetxattr(path, name, value, size);
}
ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
{
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)
{
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
#error You need to create xattr compatibility functions.
#endif
#endif /* SUPPORT_XATTRS */
+26
View File
@@ -0,0 +1,26 @@
#ifdef SUPPORT_XATTRS
#if defined HAVE_ATTR_XATTR_H
#include <attr/xattr.h>
#elif defined HAVE_SYS_XATTR_H
#include <sys/xattr.h>
#elif defined HAVE_SYS_EXTATTR_H
#include <sys/extattr.h>
#endif
/* 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);
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
/* No xattrs available */
#endif
+6 -6
View File
@@ -608,7 +608,7 @@ static void fix_basis_dirs(void)
}
/* This is only called by the sender. */
static void read_final_goodbye(int f_in, int f_out)
static void read_final_goodbye(int f_in)
{
int i, iflags, xlen;
uchar fnamecmp_type;
@@ -617,8 +617,8 @@ static void read_final_goodbye(int f_in, int f_out)
if (protocol_version < 29)
i = read_int(f_in);
else {
i = read_ndx_and_attrs(f_in, f_out, &iflags,
&fnamecmp_type, xname, &xlen);
i = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
xname, &xlen);
}
if (i != NDX_DONE) {
@@ -677,7 +677,7 @@ static void do_server_sender(int f_in, int f_out, int argc, char *argv[])
io_flush(FULL_FLUSH);
handle_stats(f_out);
if (protocol_version >= 24)
read_final_goodbye(f_in, f_out);
read_final_goodbye(f_in);
io_flush(FULL_FLUSH);
exit_cleanup(0);
}
@@ -740,7 +740,7 @@ static int do_recv(int f_in, int f_out, char *local_name)
kluge_around_eof = -1;
/* This should only get stopped via a USR2 signal. */
read_ndx_and_attrs(f_in, -1, &iflags, &fnamecmp_type,
read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
xname, &xlen);
rprintf(FERROR, "Invalid packet at end of run [%s]\n",
@@ -977,7 +977,7 @@ int client_run(int f_in, int f_out, pid_t pid, int argc, char *argv[])
io_flush(FULL_FLUSH);
handle_stats(-1);
if (protocol_version >= 24)
read_final_goodbye(f_in, f_out);
read_final_goodbye(f_in);
if (pid != -1) {
if (verbose > 3)
rprintf(FINFO,"client_run waiting on %d\n", (int) pid);
+29 -4
View File
@@ -47,6 +47,7 @@ int copy_links = 0;
int preserve_links = 0;
int preserve_hard_links = 0;
int preserve_acls = 0;
int preserve_xattrs = 0;
int preserve_perms = 0;
int preserve_executability = 0;
int preserve_devices = 0;
@@ -201,6 +202,7 @@ static void print_rsync_version(enum logcode f)
char const *have_inplace = "no ";
char const *hardlinks = "no ";
char const *acls = "no ";
char const *xattrs = "no ";
char const *links = "no ";
char const *ipv6 = "no ";
STRUCT_STAT *dumstat;
@@ -220,7 +222,9 @@ static void print_rsync_version(enum logcode f)
#ifdef SUPPORT_ACLS
acls = "";
#endif
#ifdef SUPPORT_XATTRS
xattrs = "";
#endif
#ifdef SUPPORT_LINKS
links = "";
#endif
@@ -239,8 +243,8 @@ static void print_rsync_version(enum logcode f)
(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);
#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");
rprintf(F," -a, --archive archive mode; same as -rlptgoD (no -H, -A)\n");
rprintf(F," -a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)\n");
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");
@@ -310,6 +314,9 @@ void usage(enum logcode F)
rprintf(F," --chmod=CHMOD affect file and/or directory permissions\n");
#ifdef SUPPORT_ACLS
rprintf(F," -A, --acls preserve ACLs (implies --perms)\n");
#endif
#ifdef SUPPORT_XATTRS
rprintf(F," -X, --xattrs preserve extended attributes (implies --perms)\n");
#endif
rprintf(F," -o, --owner preserve owner (super-user only)\n");
rprintf(F," -g, --group preserve group\n");
@@ -438,6 +445,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 },
{"xattrs", 'X', POPT_ARG_NONE, 0, 'X', 0, 0 },
{"no-xattrs", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
{"no-X", 0, POPT_ARG_VAL, &preserve_xattrs, 0, 0, 0 },
{"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 },
@@ -1126,6 +1136,17 @@ int parse_arguments(int *argc, const char ***argv, int frommain)
return 0;
#endif
case 'X':
#ifdef SUPPORT_XATTRS
preserve_xattrs = 1;
preserve_perms = 1;
break;
#else
snprintf(err_buf,sizeof(err_buf),
"extended attributes are not supported on this %s\n",
am_server ? "server" : "client");
return 0;
#endif
default:
/* A large opt value means that set_refuse_options()
@@ -1589,6 +1610,10 @@ void server_options(char **args,int *argc)
#ifdef SUPPORT_ACLS
if (preserve_acls)
argstr[x++] = 'A';
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs)
argstr[x++] = 'X';
#endif
if (recurse)
argstr[x++] = 'r';
+16 -5
View File
@@ -22,6 +22,7 @@
#include "rsync.h"
extern int verbose;
extern int dry_run;
extern int do_xfers;
extern int am_server;
extern int do_progress;
@@ -37,6 +38,7 @@ extern int protocol_version;
extern int relative_paths;
extern int preserve_hard_links;
extern int preserve_perms;
extern int preserve_xattrs;
extern int basis_dir_cnt;
extern int make_backups;
extern int cleanup_got_literal;
@@ -366,8 +368,8 @@ int recv_files(int f_in, char *local_name)
cleanup_disable();
/* This call also sets cur_flist. */
ndx = read_ndx_and_attrs(f_in, -1, &iflags,
&fnamecmp_type, xname, &xlen);
ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
xname, &xlen);
if (ndx == NDX_DONE) {
if (inc_recurse && first_flist) {
flist_free(first_flist);
@@ -397,8 +399,17 @@ int recv_files(int f_in, char *local_name)
if (verbose > 2)
rprintf(FINFO, "recv_files(%s)\n", fname);
#ifdef SUPPORT_XATTRS
if (iflags & ITEM_REPORT_XATTR && !dry_run)
recv_xattr_request(file, f_in);
#endif
if (!(iflags & ITEM_TRANSFER)) {
maybe_log_item(file, iflags, itemizing, xname);
#ifdef SUPPORT_XATTRS
if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && !dry_run)
set_file_attrs(fname, file, NULL, fname, 0);
#endif
continue;
}
if (phase == 2) {
@@ -655,15 +666,15 @@ int recv_files(int f_in, char *local_name)
temp_copy_name = NULL;
else
temp_copy_name = partialptr;
finish_transfer(fname, fnametmp, temp_copy_name,
file, recv_ok, 1);
finish_transfer(fname, fnametmp, fnamecmp,
temp_copy_name, file, recv_ok, 1);
if (fnamecmp == partialptr) {
do_unlink(partialptr);
handle_partial_dir(partialptr, PDIR_DELETE);
}
} else if (keep_partial && partialptr
&& handle_partial_dir(partialptr, PDIR_CREATE)) {
finish_transfer(partialptr, fnametmp, NULL,
finish_transfer(partialptr, fnametmp, fnamecmp, NULL,
file, recv_ok, !partial_dir);
if (delay_updates && recv_ok) {
bitbag_set_bit(delayed_bits, ndx);
+25 -16
View File
@@ -32,6 +32,7 @@
extern int verbose;
extern int dry_run;
extern int preserve_acls;
extern int preserve_xattrs;
extern int preserve_perms;
extern int preserve_executability;
extern int preserve_times;
@@ -91,10 +92,8 @@ void setup_iconv()
}
#endif
/* This is used by sender.c with a valid f_out, and by receive.c with
* f_out = -1. */
int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr,
uchar *type_ptr, char *buf, int *len_ptr)
int read_ndx_and_attrs(int f_in, int *iflag_ptr, uchar *type_ptr,
char *buf, int *len_ptr)
{
int len, iflags = 0;
struct file_list *flist;
@@ -181,11 +180,6 @@ int read_ndx_and_attrs(int f_in, int f_out, int *iflag_ptr,
ndx, who_am_i());
exit_cleanup(RERR_PROTOCOL);
}
} else if (f_out >= 0) {
if (inc_recurse)
send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
write_ndx_and_attrs(f_out, ndx, iflags,
fnamecmp_type, buf, len);
}
*iflag_ptr = iflags;
@@ -228,8 +222,8 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
return new_mode;
}
int set_file_attrs(char *fname, struct file_struct *file, statx *sxp,
int flags)
int set_file_attrs(const char *fname, struct file_struct *file, statx *sxp,
const char *fnamecmp, int flags)
{
int updated = 0;
statx sx2;
@@ -246,6 +240,9 @@ int set_file_attrs(char *fname, struct file_struct *file, statx *sxp,
}
#ifdef SUPPORT_ACLS
sx2.acc_acl = sx2.def_acl = NULL;
#endif
#ifdef SUPPORT_XATTRS
sx2.xattr = NULL;
#endif
if (!preserve_perms && S_ISDIR(new_mode)
&& sx2.st.st_mode & S_ISGID) {
@@ -261,6 +258,11 @@ int set_file_attrs(char *fname, struct file_struct *file, statx *sxp,
get_acl(fname, sxp);
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs && fnamecmp)
set_xattr(fname, file, fnamecmp, sxp);
#endif
if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
flags |= ATTRS_SKIP_MTIME;
if (!(flags & ATTRS_SKIP_MTIME)
@@ -353,10 +355,16 @@ int set_file_attrs(char *fname, struct file_struct *file, statx *sxp,
rprintf(FCLIENT, "%s is uptodate\n", fname);
}
cleanup:
if (sxp == &sx2) {
#ifdef SUPPORT_ACLS
if (preserve_acls && sxp == &sx2)
free_acl(&sx2);
if (preserve_acls)
free_acl(&sx2);
#endif
#ifdef SUPPORT_XATTRS
if (preserve_xattrs)
free_xattr(&sx2);
#endif
}
return updated;
}
@@ -378,7 +386,8 @@ RETSIGTYPE sig_int(UNUSED(int val))
* attributes (e.g. permissions, ownership, etc.). If partialptr is not
* NULL and the robust_rename() call is forced to copy the temp file, we
* stage the file into the partial-dir and then rename it into place. */
void finish_transfer(char *fname, char *fnametmp, char *partialptr,
void finish_transfer(const char *fname, const char *fnametmp,
const char *fnamecmp, const char *partialptr,
struct file_struct *file, int ok_to_set_time,
int overwriting_basis)
{
@@ -395,7 +404,7 @@ void finish_transfer(char *fname, char *fnametmp, char *partialptr,
return;
/* Change permissions before putting the file into place. */
set_file_attrs(fnametmp, file, NULL,
set_file_attrs(fnametmp, file, NULL, fnamecmp,
ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
/* move tmp file over real file */
@@ -419,7 +428,7 @@ void finish_transfer(char *fname, char *fnametmp, char *partialptr,
fnametmp = partialptr ? partialptr : fname;
do_set_file_attrs:
set_file_attrs(fnametmp, file, NULL,
set_file_attrs(fnametmp, file, NULL, fnamecmp,
ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
if (partialptr) {
+7 -1
View File
@@ -569,6 +569,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))
@@ -601,7 +602,8 @@ extern int preserve_acls;
/* When the associated option is on, all entries will have these present: */
#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_ACL(f) REQ_EXTRA(f, preserve_acls)->num
#define F_XATTR(f) REQ_EXTRA(f, preserve_xattrs)->num
/* These items are per-entry optional and mutally exclusive: */
#define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num
@@ -793,9 +795,13 @@ typedef struct {
struct rsync_acl *acc_acl; /* access ACL */
struct rsync_acl *def_acl; /* default ACL */
#endif
#ifdef SUPPORT_XATTRS
item_list *xattr;
#endif
} statx;
#define ACL_READY(sx) ((sx).acc_acl != NULL)
#define XATTR_READY(sx) ((sx).xattr != NULL)
#include "proto.h"
+7 -1
View File
@@ -301,7 +301,7 @@ to the detailed description below for a complete description. verb(
-q, --quiet suppress non-error messages
--no-motd suppress daemon-mode MOTD (see caveat)
-c, --checksum skip based on checksum, not mod-time & size
-a, --archive archive mode; same as -rlptgoD (no -H, -A)
-a, --archive archive mode; equals -rlptgoD (no -H,-A,-X)
--no-OPTION turn off an implied OPTION (e.g. --no-D)
-r, --recursive recurse into directories
-R, --relative use relative path names
@@ -324,6 +324,7 @@ to the detailed description below for a complete description. verb(
-E, --executability preserve executability
--chmod=CHMOD affect file and/or directory permissions
-A, --acls preserve ACLs (implies -p)
-X, --xattrs preserve extended attrs (implies -p)
-o, --owner preserve owner (super-user only)
-g, --group preserve group
--devices preserve device files (super-user only)
@@ -831,6 +832,11 @@ dit(bf(-A, --acls)) This option causes rsync to update the destination
ACLs to be the same as the source ACLs. This nonstandard option only
works if the remote rsync also supports it. bf(--acls) implies bf(--perms).
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
only if the remote machine's rsync supports this option also. This is
a non-standard option.
dit(bf(--chmod)) This option tells rsync to apply one or more
comma-separated "chmod" strings to the permission of the files in the
transfer. The resulting value is treated as though it was the permissions
+25 -8
View File
@@ -22,6 +22,7 @@
#include "rsync.h"
extern int verbose;
extern int dry_run;
extern int do_xfers;
extern int am_server;
extern int am_daemon;
@@ -33,6 +34,7 @@ extern int csum_length;
extern int append_mode;
extern int io_error;
extern int allowed_lull;
extern int preserve_xattrs;
extern int protocol_version;
extern int remove_source_files;
extern int updating_basis_file;
@@ -144,8 +146,9 @@ void successful_send(int ndx)
rsyserr(FERROR, errno, "sender failed to remove %s", fname);
}
void write_ndx_and_attrs(int f_out, int ndx, int iflags,
uchar fnamecmp_type, char *buf, int len)
static void write_ndx_and_attrs(int f_out, int ndx, int iflags,
const char *fname, struct file_struct *file,
uchar fnamecmp_type, char *buf, int len)
{
write_ndx(f_out, ndx);
if (protocol_version < 29)
@@ -155,6 +158,10 @@ void write_ndx_and_attrs(int f_out, int ndx, int iflags,
write_byte(f_out, fnamecmp_type);
if (iflags & ITEM_XNAME_FOLLOWS)
write_vstring(f_out, buf, len);
#ifdef SUPPORT_XATTRS
if (preserve_xattrs && iflags & ITEM_REPORT_XATTR && !dry_run)
send_xattr_request(fname, file, f_out);
#endif
}
void send_files(int f_in, int f_out)
@@ -183,8 +190,8 @@ void send_files(int f_in, int f_out)
send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
/* This call also sets cur_flist. */
ndx = read_ndx_and_attrs(f_in, f_out, &iflags,
&fnamecmp_type, xname, &xlen);
ndx = read_ndx_and_attrs(f_in, &iflags, &fnamecmp_type,
xname, &xlen);
if (ndx == NDX_DONE) {
if (inc_recurse && first_flist) {
flist_free(first_flist);
@@ -201,6 +208,9 @@ void send_files(int f_in, int f_out)
continue;
}
if (inc_recurse)
send_extra_file_list(f_out, FILECNT_LOOKAHEAD);
file = cur_flist->files[ndx - cur_flist->ndx_start];
if (F_ROOTDIR(file)) {
path = F_ROOTDIR(file);
@@ -215,8 +225,15 @@ void send_files(int f_in, int f_out)
if (verbose > 2)
rprintf(FINFO, "send_files(%d, %s%s%s)\n", ndx, path,slash,fname);
#ifdef SUPPORT_XATTRS
if (preserve_xattrs && iflags & ITEM_REPORT_XATTR)
recv_xattr_request(file, f_in);
#endif
if (!(iflags & ITEM_TRANSFER)) {
maybe_log_item(file, iflags, itemizing, xname);
write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
fnamecmp_type, xname, xlen);
continue;
}
if (phase == 2) {
@@ -251,8 +268,8 @@ void send_files(int f_in, int f_out)
if (!do_xfers) { /* log the transfer */
log_item(FCLIENT, file, &stats, iflags, NULL);
write_ndx_and_attrs(f_out, ndx, iflags, fnamecmp_type,
xname, xlen);
write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
fnamecmp_type, xname, xlen);
continue;
}
@@ -305,8 +322,8 @@ void send_files(int f_in, int f_out)
path,slash,fname, (double)st.st_size);
}
write_ndx_and_attrs(f_out, ndx, iflags, fnamecmp_type,
xname, xlen);
write_ndx_and_attrs(f_out, ndx, iflags, fname, file,
fnamecmp_type, xname, xlen);
write_sum_head(f_xfer, s);
if (verbose > 2)
+107
View File
@@ -0,0 +1,107 @@
#! /bin/sh
# This program is distributable under the terms of the GNU GPL (see
# COPYING).
# Test that rsync handles basic xattr preservation.
. $srcdir/testsuite/rsync.fns
$RSYNC --version | grep ", xattrs" >/dev/null || test_skipped "Rsync is configured without xattr support"
case "$RSYNC" in
*protocol=29*) test_skipped "xattr support requires protocol 30" ;;
esac
case "`xattr 2>&1`" in
*--list:*)
xset() {
xnam="$1"
xval="$2"
shift 2
xattr -s "$xnam" "$xval" "${@}"
}
xls() {
xattr -l "${@}"
}
;;
*)
xset() {
xnam="$1"
xval="$2"
shift 2
setfattr -n "$xnam" -v "$xval" "${@}"
}
xls() {
getfattr -d "${@}"
}
;;
esac
makepath "$fromdir/foo"
echo now >"$fromdir/file0"
echo something >"$fromdir/file1"
echo else >"$fromdir/file2"
echo deep >"$fromdir/foo/file3"
echo normal >"$fromdir/file4"
makepath "$chkdir/foo"
echo wow >"$chkdir/file1"
cp -p "$fromdir/foo/file3" "$chkdir/foo"
files='foo file0 file1 file2 foo/file3 file4'
cd "$fromdir"
xset user.foo foo file0 2>/dev/null || test_skipped "Unable to set an xattr"
xset user.bar bar file0
xset user.short 'this is short' file1
xset user.long 'this is a long attribute that will be truncated in the initial data send' file1
xset user.good 'this is good' file1
xset user.nice 'this is nice' file1
xset user.foo foo file2
xset user.bar bar file2
xset user.long 'a long attribute for our new file that tests to ensure that this works' file2
xset user.foo 'new foo' foo/file3
xset user.bar 'new bar' foo/file3
xset user.long 'this is also a long attribute that will be truncated in the initial data send' foo/file3
xset user.equal 'this long attribute should remain the same and not need to be transferred' foo/file3
xset user.short 'old short' "$chkdir/file1"
xset user.extra 'remove me' "$chkdir/file1"
xset user.foo 'old foo' "$chkdir/foo/file3"
xset user.equal 'this long attribute should remain the same and not need to be transferred' "$chkdir/foo/file3"
xls $files >"$scratchdir/xattrs.txt"
# OK, let's try a simple xattr copy.
checkit "$RSYNC -avX . \"$chkdir/\"" "$fromdir" "$chkdir"
cd "$chkdir"
xls $files | diff $diffopt "$scratchdir/xattrs.txt" -
cd "$fromdir"
checkit "$RSYNC -aiX --copy-dest=../chk . ../to" "$fromdir" "$todir"
cd "$todir"
xls $files | diff $diffopt "$scratchdir/xattrs.txt" -
cd "$fromdir"
xset user.nice 'this is nice, but different' file1
xls $files >"$scratchdir/xattrs.txt"
rm -rf "$todir"
checkit "$RSYNC -aiX --link-dest=../chk . ../to" "$chkdir" "$todir"
cd "$todir"
xls $files | diff $diffopt "$scratchdir/xattrs.txt" -
# The script would have aborted on error, so getting here means we've won.
exit 0
+775
View File
@@ -0,0 +1,775 @@
/*
* Extended Attribute support for rsync.
* Written by Jay Fenlason, vaguely based on the ACLs patch.
*
* Copyright (C) 2004 Red Hat, Inc.
* Copyright (C) 2006, 2007 Wayne Davison
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "rsync.h"
#include "lib/sysxattrs.h"
#ifdef SUPPORT_XATTRS
extern int dry_run;
extern int am_root;
extern int am_sender;
extern int am_generator;
extern int read_only;
extern int list_only;
extern int checksum_seed;
#define RSYNC_XAL_INITIAL 5
#define RSYNC_XAL_LIST_INITIAL 100
#define MAX_FULL_DATUM 32
#define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
&& strncmp(str, prfx, sizeof (prfx) - 1) == 0)
#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_LOCAL 3
#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 *datum, *name;
size_t datum_len, name_len;
} rsync_xa;
static size_t namebuf_len = 0;
static char *namebuf = NULL;
static item_list empty_xattr = EMPTY_ITEM_LIST;
static item_list rsync_xal_l = EMPTY_ITEM_LIST;
/* ------------------------------------------------------------------------- */
static void rsync_xal_free(item_list *xalp)
{
size_t i;
rsync_xa *rxas = xalp->items;
for (i = 0; i < xalp->count; i++) {
free(rxas[i].datum);
/*free(rxas[i].name);*/
}
xalp->count = 0;
}
void free_xattr(statx *sxp)
{
if (!sxp->xattr)
return;
rsync_xal_free(sxp->xattr);
free(sxp->xattr);
sxp->xattr = NULL;
}
static int rsync_xal_compare_names(const void *x1, const void *x2)
{
const rsync_xa *xa1 = x1;
const rsync_xa *xa2 = x2;
return strcmp(xa1->name, xa2->name);
}
static ssize_t get_xattr_names(const char *fname)
{
ssize_t list_len;
if (!namebuf) {
namebuf_len = 1024;
namebuf = new_array(char, namebuf_len);
if (!namebuf)
out_of_memory("get_xattr_names");
}
/* 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 (list_len >= 0)
return list_len;
if (errno == ENOTSUP)
return 0;
if (errno == ERANGE) {
list_len = sys_llistxattr(fname, NULL, 0);
if (list_len < 0) {
rsyserr(FERROR, errno,
"get_xattr_names: llistxattr(\"%s\",0) failed",
fname);
return -1;
}
if (namebuf_len)
free(namebuf);
namebuf_len = list_len + 1024;
namebuf = new_array(char, namebuf_len);
if (!namebuf)
out_of_memory("get_xattr_names");
list_len = sys_llistxattr(fname, namebuf, namebuf_len);
if (list_len >= 0)
return list_len;
}
rsyserr(FERROR, errno,
"get_xattr_names: llistxattr(\"%s\",%ld) failed",
fname, (long)namebuf_len);
return -1;
}
/* On entry, the *len_ptr parameter contains the size of the extra space we
* should allocate when we create a buffer for the data. On exit, it contains
* the length of the datum. */
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);
char *ptr;
if (datum_len == (size_t)-1) {
if (errno == ENOTSUP || no_missing_error)
return NULL;
rsyserr(FERROR, 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)))
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,
"get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
" failed", fname, name, (long)datum_len);
} else {
rprintf(FERROR,
"get_xattr_data: lgetxattr(\"%s\",\"%s\",%ld)"
" returned %ld\n", fname, name,
(long)datum_len, (long)len);
}
free(ptr);
return NULL;
}
}
return ptr;
}
static int rsync_xal_get(const char *fname, item_list *xalp)
{
ssize_t list_len, name_len;
size_t datum_len, name_offset;
char *name, *ptr;
#ifdef HAVE_LINUX_XATTRS
int user_only = am_sender ? 0 : !am_root;
#endif
/* This puts the name list into the "namebuf" buffer. */
if ((list_len = get_xattr_names(fname)) < 0)
return -1;
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 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 = name_len; /* Pass extra size to get_xattr_data() */
if (!(ptr = get_xattr_data(fname, name, &datum_len, 0)))
return -1;
if (datum_len > MAX_FULL_DATUM) {
/* For large datums, we store a flag and a checksum. */
name_offset = 1 + MAX_DIGEST_LEN;
sum_init(checksum_seed);
sum_update(ptr, datum_len);
free(ptr);
if (!(ptr = new_array(char, name_offset + name_len)))
out_of_memory("rsync_xal_get");
*ptr = XSTATE_ABBREV;
sum_end(ptr + 1);
} else
name_offset = datum_len;
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;
}
if (xalp->count > 1)
qsort(xalp->items, xalp->count, sizeof (rsync_xa), rsync_xal_compare_names);
return 0;
}
/* Read the xattr(s) for this filename. */
int get_xattr(const char *fname, statx *sxp)
{
sxp->xattr = new(item_list);
*sxp->xattr = empty_xattr;
if (rsync_xal_get(fname, sxp->xattr) < 0) {
free_xattr(sxp);
return -1;
}
return 0;
}
static int find_matching_xattr(item_list *xalp)
{
size_t i, j;
item_list *lst = rsync_xal_l.items;
for (i = 0; i < rsync_xal_l.count; i++) {
rsync_xa *rxas1 = lst[i].items;
rsync_xa *rxas2 = xalp->items;
/* Wrong number of elements? */
if (lst[i].count != xalp->count)
continue;
/* any elements different? */
for (j = 0; j < xalp->count; j++) {
if (rxas1[j].name_len != rxas2[j].name_len
|| rxas1[j].datum_len != rxas2[j].datum_len
|| strcmp(rxas1[j].name, rxas2[j].name))
break;
if (rxas1[j].datum_len > MAX_FULL_DATUM) {
if (memcmp(rxas1[j].datum + 1,
rxas2[j].datum + 1,
MAX_DIGEST_LEN) != 0)
break;
} else {
if (memcmp(rxas1[j].datum, rxas2[j].datum,
rxas2[j].datum_len))
break;
}
}
/* no differences found. This is The One! */
if (j == xalp->count)
return i;
}
return -1;
}
/* Store *xalp on the end of rsync_xal_l */
static void rsync_xal_store(item_list *xalp)
{
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. */
*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;
}
/* Send the make_xattr()-generated xattr list for this flist entry. */
int send_xattr(statx *sxp, int f)
{
int ndx = find_matching_xattr(sxp->xattr);
/* Send 0 (-1 + 1) to indicate that literal xattr data follows. */
write_abbrevint(f, ndx + 1);
if (ndx < 0) {
rsync_xa *rxa;
int count = sxp->xattr->count;
write_abbrevint(f, count);
for (rxa = sxp->xattr->items; count--; rxa++) {
#ifdef HAVE_LINUX_XATTRS
write_abbrevint(f, rxa->name_len);
write_abbrevint(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_abbrevint(f, rxa->name_len - RPRE_LEN);
write_abbrevint(f, rxa->datum_len);
write_buf(f, rxa->name + RPRE_LEN, rxa->name_len - RPRE_LEN);
} else {
write_abbrevint(f, rxa->name_len + UPRE_LEN);
write_abbrevint(f, rxa->datum_len);
write_buf(f, USER_PREFIX, UPRE_LEN);
write_buf(f, rxa->name, rxa->name_len);
}
#endif
if (rxa->datum_len > MAX_FULL_DATUM)
write_buf(f, rxa->datum + 1, MAX_DIGEST_LEN);
else
write_buf(f, rxa->datum, rxa->datum_len);
}
ndx = rsync_xal_l.count; /* pre-incremented count */
rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
}
return ndx;
}
/* 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)
{
item_list *lst = rsync_xal_l.items;
rsync_xa *snd_rxa, *rec_rxa;
int snd_cnt, rec_cnt;
int cmp, same, xattrs_equal = 1;
if (sxp && XATTR_READY(*sxp)) {
rec_rxa = sxp->xattr->items;
rec_cnt = sxp->xattr->count;
} else {
rec_rxa = NULL;
rec_cnt = 0;
}
if (F_XATTR(file) >= 0)
lst += F_XATTR(file);
else
lst = &empty_xattr;
snd_rxa = lst->items;
snd_cnt = lst->count;
/* If the count of the sender's xattrs is different from our
* (receiver's) xattrs, the lists are not the same. */
if (snd_cnt != rec_cnt) {
if (!find_all)
return 1;
xattrs_equal = 0;
}
while (snd_cnt) {
cmp = rec_cnt ? strcmp(snd_rxa->name, rec_rxa->name) : -1;
if (cmp > 0)
same = 0;
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;
/* Flag unrequested items that we need. */
if (!same && find_all && snd_rxa->datum[0] == XSTATE_ABBREV)
snd_rxa->datum[0] = XSTATE_TODO;
} else {
same = cmp == 0 && snd_rxa->datum_len == rec_rxa->datum_len
&& memcmp(snd_rxa->datum, rec_rxa->datum,
snd_rxa->datum_len) == 0;
}
if (!same) {
if (!find_all)
return 1;
xattrs_equal = 0;
}
if (cmp <= 0) {
snd_rxa++;
snd_cnt--;
}
if (cmp >= 0) {
rec_rxa++;
rec_cnt--;
}
}
if (rec_cnt)
xattrs_equal = 0;
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. */
void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
{
item_list *lst = rsync_xal_l.items;
int j, cnt, prior_req = -1;
rsync_xa *rxa;
lst += F_XATTR(file);
cnt = lst->count;
for (rxa = lst->items, j = 0; j < cnt; rxa++, j++) {
if (rxa->datum_len <= MAX_FULL_DATUM)
continue;
switch (rxa->datum[0]) {
case XSTATE_LOCAL:
/* Items set locally will get cached by receiver. */
rxa->datum[0] = XSTATE_DONE;
continue;
case XSTATE_TODO:
break;
default:
continue;
}
/* Flag that we handled this abbreviated item. */
rxa->datum[0] = XSTATE_DONE;
write_abbrevint(f_out, j - prior_req);
prior_req = j;
if (fname) {
size_t len = 0;
char *ptr;
/* Re-read the long datum. */
if (!(ptr = get_xattr_data(fname, rxa->name, &len, 0)))
continue;
write_abbrevint(f_out, len); /* length might have changed! */
write_buf(f_out, ptr, len);
free(ptr);
}
}
write_byte(f_out, 0); /* end the list */
}
/* Any items set locally by the generator that the receiver doesn't
* get told about get changed back to XSTATE_ABBREV. */
void xattr_clear_locals(struct file_struct *file)
{
item_list *lst = rsync_xal_l.items;
rsync_xa *rxa;
int cnt;
if (F_XATTR(file) < 0)
return;
lst += F_XATTR(file);
cnt = lst->count;
for (rxa = lst->items; cnt--; rxa++) {
if (rxa->datum_len <= MAX_FULL_DATUM)
continue;
if (rxa->datum[0] == XSTATE_LOCAL)
rxa->datum[0] = XSTATE_ABBREV;
}
}
/* When called by the sender, read the request from the generator and mark
* 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)
{
item_list *lst = rsync_xal_l.items;
char *old_datum, *name;
rsync_xa *rxa;
int rel_pos, cnt;
if (F_XATTR(file) < 0) {
rprintf(FERROR, "recv_xattr_request: internal data error!\n");
exit_cleanup(RERR_STREAMIO);
}
lst += F_XATTR(file);
cnt = lst->count;
rxa = lst->items;
rxa -= 1;
while ((rel_pos = read_abbrevint(f_in)) != 0) {
rxa += rel_pos;
cnt -= rel_pos;
if (cnt < 0 || rxa->datum_len <= MAX_FULL_DATUM
|| rxa->datum[0] != XSTATE_ABBREV) {
rprintf(FERROR, "recv_xattr_request: internal abbrev error!\n");
exit_cleanup(RERR_STREAMIO);
}
if (am_sender) {
rxa->datum[0] = XSTATE_TODO;
continue;
}
old_datum = rxa->datum;
rxa->datum_len = read_abbrevint(f_in);
if (rxa->name_len + rxa->datum_len < rxa->name_len)
out_of_memory("recv_xattr_request"); /* overflow */
rxa->datum = new_array(char, rxa->datum_len + rxa->name_len);
if (!rxa->datum)
out_of_memory("recv_xattr_request");
name = rxa->datum + rxa->datum_len;
memcpy(name, rxa->name, rxa->name_len);
rxa->name = name;
free(old_datum);
read_buf(f_in, rxa->datum, rxa->datum_len);
}
}
/* ------------------------------------------------------------------------- */
/* receive and build the rsync_xattr_lists */
void receive_xattr(struct file_struct *file, int f)
{
static item_list temp_xattr = EMPTY_ITEM_LIST;
int count;
int ndx = read_abbrevint(f);
if (ndx < 0 || (size_t)ndx > rsync_xal_l.count) {
rprintf(FERROR, "receive_xattr: xa index %d out of"
" range for %s\n", ndx, f_name(file, NULL));
exit_cleanup(RERR_STREAMIO);
}
if (ndx != 0) {
F_XATTR(file) = ndx - 1;
return;
}
if ((count = read_abbrevint(f)) != 0) {
(void)EXPAND_ITEM_LIST(&temp_xattr, rsync_xa, count);
temp_xattr.count = 0;
}
while (count--) {
char *ptr, *name;
rsync_xa *rxa;
size_t name_len = read_abbrevint(f);
size_t datum_len = read_abbrevint(f);
size_t dget_len = datum_len > MAX_FULL_DATUM ? 1 + MAX_DIGEST_LEN : datum_len;
#ifdef HAVE_LINUX_XATTRS
size_t extra_len = 0;
#else
size_t extra_len = am_root ? RPRE_LEN : 0;
if (dget_len + extra_len < dget_len)
out_of_memory("receive_xattr"); /* overflow */
#endif
if (dget_len + extra_len + name_len < dget_len)
out_of_memory("receive_xattr"); /* overflow */
ptr = new_array(char, dget_len + extra_len + name_len);
if (!ptr)
out_of_memory("receive_xattr");
name = ptr + dget_len + extra_len;
read_buf(f, name, name_len);
if (dget_len == datum_len)
read_buf(f, ptr, dget_len);
else {
*ptr = XSTATE_ABBREV;
read_buf(f, ptr + 1, MAX_DIGEST_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, 1);
rxa->name = name;
rxa->datum = ptr;
rxa->name_len = name_len;
rxa->datum_len = datum_len;
}
ndx = rsync_xal_l.count; /* pre-incremented count */
rsync_xal_store(&temp_xattr); /* adds item to rsync_xal_l */
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)
{
int ndx;
if (!sxp->xattr)
return;
ndx = find_matching_xattr(sxp->xattr);
if (ndx < 0)
rsync_xal_store(sxp->xattr); /* adds item to rsync_xal_l */
F_XATTR(file) = ndx;
}
static int rsync_xal_set(const char *fname, item_list *xalp,
const char *fnamecmp, statx *sxp)
{
rsync_xa *rxas = xalp->items;
ssize_t list_len;
size_t i, len;
char *name, *ptr, sum[MAX_DIGEST_LEN];
int name_len, ret = 0;
/* This puts the current name list into the "namebuf" buffer. */
if ((list_len = get_xattr_names(fname)) < 0)
return -1;
for (i = 0; i < xalp->count; i++) {
name = rxas[i].name;
if (XATTR_ABBREV(rxas[i])) {
/* See if the fnamecmp version is identical. */
len = name_len = rxas[i].name_len;
if ((ptr = get_xattr_data(fnamecmp, name, &len, 1)) == NULL) {
still_abbrev:
if (am_generator)
continue;
rprintf(FERROR, "Missing abbreviated xattr value, %s, for %s\n",
rxas[i].name, full_fname(fname));
ret = -1;
continue;
}
if (len != rxas[i].datum_len) {
free(ptr);
goto still_abbrev;
}
sum_init(checksum_seed);
sum_update(ptr, len);
sum_end(sum);
if (memcmp(sum, rxas[i].datum + 1, MAX_DIGEST_LEN) != 0) {
free(ptr);
goto still_abbrev;
}
if (fname == fnamecmp)
; /* Value is already set when identical */
else if (sys_lsetxattr(fname, name, ptr, len) < 0) {
rsyserr(FERROR, errno,
"rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
fname, name);
ret = -1;
} else /* make sure caller sets mtime */
sxp->st.st_mtime = (time_t)-1;
if (am_generator) { /* generator items stay abbreviated */
if (rxas[i].datum[0] == XSTATE_ABBREV)
rxas[i].datum[0] = XSTATE_LOCAL;
free(ptr);
continue;
}
memcpy(ptr + len, name, name_len);
free(rxas[i].datum);
rxas[i].name = name = ptr + len;
rxas[i].datum = ptr;
continue;
}
if (sys_lsetxattr(fname, name, rxas[i].datum, rxas[i].datum_len) < 0) {
rsyserr(FERROR, errno,
"rsync_xal_set: lsetxattr(\"%s\",\"%s\") failed",
fname, name);
ret = -1;
} else /* make sure caller sets mtime */
sxp->st.st_mtime = (time_t)-1;
}
/* Remove any extraneous names. */
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 (am_root ? HAS_PREFIX(name, SYSTEM_PREFIX)
: !HAS_PREFIX(name, USER_PREFIX))
continue;
#endif
for (i = 0; i < xalp->count; i++) {
if (strcmp(name, rxas[i].name) == 0)
break;
}
if (i == xalp->count) {
if (sys_lremovexattr(fname, name) < 0) {
rsyserr(FERROR, errno,
"rsync_xal_clear: lremovexattr(\"%s\",\"%s\") failed",
fname, name);
ret = -1;
} else /* make sure caller sets mtime */
sxp->st.st_mtime = (time_t)-1;
}
}
return ret;
}
/* Set extended attributes on indicated filename. */
int set_xattr(const char *fname, const struct file_struct *file,
const char *fnamecmp, statx *sxp)
{
int ndx;
item_list *lst = rsync_xal_l.items;
if (dry_run)
return 1; /* FIXME: --dry-run needs to compute this value */
if (read_only || list_only) {
errno = EROFS;
return -1;
}
ndx = F_XATTR(file);
return rsync_xal_set(fname, lst + ndx, fnamecmp, sxp);
}
#endif /* SUPPORT_XATTRS */