patch draft for extended attributes on linux

Martin Pool mbp at samba.org
Wed Jun 25 10:34:38 EST 2003


This draft patch adds support for transferring extended attributes
with a new --xattr option.  It ought to work on Linux with XFS or
ext2/ext3 filesystems with the SGI/bestbits attribute system.

It is partially working, but there seems to be some kind of hang bug
while transferring the file list.  I suspect it might be provoking a
problem in io.c.

You need to rerun autoconf, autoheader and configure after applying
it.

There is no mtime for xattrs, so they are transferred every time as
part of the file list.  This means that they will be updated correctly
if you change attributes but do not change the file.

I wrote this because it was required by a colleague.  I have mixed
feelings about whether this ought to be merged, even once it's working
correctly.  rsync hardly needs more options or protocol
variations. :-( (Amusingly enough I once said "-xattr" instead of
"--xattr" and it silently did something else.)



diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/Makefile.in xa/Makefile.in
--- rsync-2.5.6/Makefile.in	2003-01-21 05:26:14.000000000 +1100
+++ xa/Makefile.in	2003-06-24 15:08:09.000000000 +1000
@@ -34,7 +34,7 @@ OBJS1=rsync.o generator.o receiver.o cle
 	main.o checksum.o match.o syscall.o log.o backup.o
 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
 	fileio.o batch.o clientname.o
-OBJS3=progress.o pipe.o
+OBJS3=progress.o pipe.o xattr.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 \
 	popt/popthelp.o popt/poptparse.o
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/cleanup.c xa/cleanup.c
--- rsync-2.5.6/cleanup.c	2003-01-27 14:35:08.000000000 +1100
+++ xa/cleanup.c	2003-06-24 16:16:58.000000000 +1000
@@ -26,7 +26,7 @@
  * shutdown() of socket connections.  This eliminates the abortive
  * TCP RST sent by a Winsock-based system when the close() occurs.
  **/
-void close_all()
+void close_all(void)
 {
 #ifdef SHUTDOWN_ALL_SOCKETS
 	int max_fd;
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/configure.in xa/configure.in
--- rsync-2.5.6/configure.in	2003-01-28 16:27:40.000000000 +1100
+++ xa/configure.in	2003-06-24 20:27:45.000000000 +1000
@@ -5,7 +5,7 @@ AC_CONFIG_SRCDIR([byteorder.h])
 AC_CONFIG_HEADER(config.h)
 AC_PREREQ(2.52)
 
-RSYNC_VERSION=2.5.6
+RSYNC_VERSION=2.5.6-xa
 AC_SUBST(RSYNC_VERSION)
 AC_MSG_NOTICE([Configuring rsync $RSYNC_VERSION])
 
@@ -267,6 +267,7 @@ AC_CHECK_HEADERS(glob.h mcheck.h sys/sys
 AC_CHECK_HEADERS(netdb.h)
 AC_CHECK_HEADERS(malloc.h)
 AC_CHECK_HEADERS(float.h)
+AC_CHECK_HEADERS(attr/xattr.h)
 
 AC_CHECK_SIZEOF(int)
 AC_CHECK_SIZEOF(long)
@@ -414,6 +415,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strd
 AC_CHECK_FUNCS(fchmod fstat strchr readlink link utime utimes strftime)
 AC_CHECK_FUNCS(memmove lchown vsnprintf snprintf asprintf setsid glob strpbrk)
 AC_CHECK_FUNCS(strlcat strlcpy strtol mtrace mallinfo setgroups)
+AC_CHECK_FUNCS(lgetxattr)
 
 AC_CACHE_CHECK([for working socketpair],rsync_cv_HAVE_SOCKETPAIR,[
 AC_TRY_RUN([
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/flist.c xa/flist.c
--- rsync-2.5.6/flist.c	2003-01-19 05:00:23.000000000 +1100
+++ xa/flist.c	2003-06-25 08:29:52.000000000 +1000
@@ -1,7 +1,7 @@
 /* 
    Copyright (C) Andrew Tridgell 1996
    Copyright (C) Paul Mackerras 1996
-   Copyright (C) 2001, 2002 by Martin Pool <mbp at samba.org>
+   Copyright (C) 2001-2003 by Martin Pool <mbp at samba.org>
    
    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
@@ -422,6 +422,12 @@ static void send_file_entry(struct file_
 	}
 #endif
 
+#if SUPPORT_XATTRS
+        if (opt_xattr) {
+                xalist_send(f, file->xattrs);
+        }
+#endif
+
 #if SUPPORT_HARD_LINKS
 	if (preserve_hard_links && S_ISREG(file->mode)) {
 		if (remote_version < 26) {
@@ -457,7 +463,9 @@ static void send_file_entry(struct file_
 }
 
 
-
+/**
+ * This matches up with send_file_entry()
+ **/
 static void receive_file_entry(struct file_struct **fptr,
 			       unsigned flags, int f)
 {
@@ -555,6 +563,13 @@ static void receive_file_entry(struct fi
 			sanitize_path(file->link, file->dirname);
 		}
 	}
+
+#if SUPPORT_XATTRS
+        if (opt_xattr) {
+                xalist_receive(f, file);
+        }
+#endif
+        
 #if SUPPORT_HARD_LINKS
 	if (preserve_hard_links && S_ISREG(file->mode)) {
 		if (remote_version < 26) {
@@ -724,6 +739,12 @@ struct file_struct *make_file(int f, cha
 		file->basename = STRDUP(ap, fname);
 	}
 
+#if SUPPORT_XATTRS
+        if (opt_xattr) {
+                xalist_load(fname, file);
+        }
+#endif
+
 	file->modtime = st.st_mtime;
 	file->length = st.st_size;
 	file->mode = st.st_mode;
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/options.c xa/options.c
--- rsync-2.5.6/options.c	2003-01-28 14:11:57.000000000 +1100
+++ xa/options.c	2003-06-25 08:30:00.000000000 +1000
@@ -1,7 +1,7 @@
 /*  -*- c-file-style: "linux" -*-
  * 
  * Copyright (C) 1998-2001 by Andrew Tridgell <tridge at samba.org>
- * Copyright (C) 2000, 2001, 2002 by Martin Pool <mbp at samba.org>
+ * Copyright (C) 2000-2003 by Martin Pool <mbp at samba.org>
  * 
  * 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
@@ -83,6 +83,7 @@ int max_delete=0;
 int ignore_errors=0;
 int modify_window=0;
 int blocking_io=-1;
+int opt_xattr = 0;
 
 
 /** Network address family. **/
@@ -134,12 +135,17 @@ static void print_rsync_version(enum log
         char const *hardlinks = "no ";
         char const *links = "no ";
 	char const *ipv6 = "no ";
+        char const *xattrs = "no ";
 	STRUCT_STAT *dumstat;
 
 #ifdef HAVE_SOCKETPAIR
         got_socketpair = "";
 #endif
 
+#if SUPPORT_XATTRS
+        xattrs = "";
+#endif
+
 #if SUPPORT_HARD_LINKS
         hardlinks = "";
 #endif
@@ -155,7 +161,7 @@ static void print_rsync_version(enum log
         rprintf(f, "%s  version %s  protocol version %d\n",
                 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION);
         rprintf(f,
-                "Copyright (C) 1996-2002 by Andrew Tridgell and others\n");
+                "Copyright (C) 1996-2003 by Andrew Tridgell and others\n");
 	rprintf(f, "<http://rsync.samba.org/>\n");
         rprintf(f, "Capabilities: %d-bit files, %ssocketpairs, "
                 "%shard links, %ssymlinks, batchfiles, \n",
@@ -165,8 +171,8 @@ static void print_rsync_version(enum log
 	/* Note that this field may not have type ino_t.  It depends
 	 * on the complicated interaction between largefile feature
 	 * macros. */
-	rprintf(f, "              %sIPv6, %d-bit system inums, %d-bit internal inums\n",
-		ipv6, 
+	rprintf(f, "              %sxattrs, %sIPv6, %d-bit system inums, %d-bit internal inums\n",
+		xattrs, ipv6, 
 		(int) (sizeof(dumstat->st_ino) * 8),
 		(int) (sizeof(INO64_T) * 8));
 #ifdef MAINTAINER_MODE
@@ -270,6 +276,7 @@ void usage(enum logcode F)
   rprintf(F,"     --bwlimit=KBPS          limit I/O bandwidth, KBytes per second\n");
   rprintf(F,"     --write-batch=PREFIX    write batch fileset starting with PREFIX\n");
   rprintf(F,"     --read-batch=PREFIX     read batch fileset starting with PREFIX\n");
+  rprintf(F,"     --xattr                 copy extended attributes\n");
   rprintf(F," -h, --help                  show this help screen\n");
 #ifdef INET6
   rprintf(F," -4                          prefer IPv4\n");
@@ -366,6 +373,7 @@ static struct poptOption long_options[] 
   {"hard-links",      'H', POPT_ARG_NONE,   &preserve_hard_links , 0, 0, 0 },
   {"read-batch",       0,  POPT_ARG_STRING, &batch_prefix, OPT_READ_BATCH, 0, 0 },
   {"write-batch",      0,  POPT_ARG_STRING, &batch_prefix, OPT_WRITE_BATCH, 0, 0 },
+  {"xattr",            0,  POPT_ARG_NONE,   &opt_xattr, 0, 0, 0 },
 #ifdef INET6
   {0,		      '4', POPT_ARG_VAL,    &default_af_hint,   AF_INET , 0, 0 },
   {0,		      '6', POPT_ARG_VAL,    &default_af_hint,   AF_INET6 , 0, 0 },
@@ -825,6 +833,10 @@ void server_options(char **args,int *arg
 		args[ac++] = tmpdir;
 	}
 
+        if (opt_xattr) {
+                args[ac++] = "--xattr";
+        }
+
 	if (backup_dir && am_sender) {
 		/* only the receiver needs this option, if we are the sender
 		 *   then we need to send it to the receiver.
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/proto.h xa/proto.h
--- rsync-2.5.6/proto.h	2003-01-27 14:35:09.000000000 +1100
+++ xa/proto.h	2003-06-25 08:29:48.000000000 +1000
@@ -265,4 +265,12 @@ char *timestring(time_t t);
 int msleep(int t);
 int cmp_modtime(time_t file1, time_t file2);
 int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6);
+int xalist_apply(const char *filename,
+                 struct xa_list *xal);
+int xalist_load(char *fname,
+                struct file_struct *file);
+int xalist_send(int f,
+                struct xa_list *xal);
+int xalist_receive(int f,
+                   struct file_struct *file);
 int sys_gettimeofday(struct timeval *tv);
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/rsync.c xa/rsync.c
--- rsync-2.5.6/rsync.c	2001-12-21 02:33:13.000000000 +1100
+++ xa/rsync.c	2003-06-24 20:34:02.000000000 +1000
@@ -201,6 +201,10 @@ int set_perms(char *fname,struct file_st
 		updated = 1;
 	}
 
+#if SUPPORT_XATTRS
+        xalist_apply(fname, file->xattrs);
+#endif
+
 #ifdef HAVE_CHMOD
 	if (!S_ISLNK(st->st_mode)) {
 		if (st->st_mode != file->mode) {
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/rsync.h xa/rsync.h
--- rsync-2.5.6/rsync.h	2003-01-27 07:11:16.000000000 +1100
+++ xa/rsync.h	2003-06-24 20:41:08.000000000 +1000
@@ -1,7 +1,7 @@
 /* 
    Copyright (C) by Andrew Tridgell 1996, 2000
    Copyright (C) Paul Mackerras 1996
-   Copyright (C) 2001, 2002 by Martin Pool <mbp at samba.org>
+   Copyright (C) 2001-2003 by Martin Pool <mbp at samba.org>
    
    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
@@ -86,6 +86,10 @@ enum logcode {FNONE=0, FERROR=1, FINFO=2
 
 #include "config.h"
 
+#define SUPPORT_LINKS HAVE_READLINK
+#define SUPPORT_HARD_LINKS HAVE_LINK
+#define SUPPORT_XATTRS HAVE_LGETXATTR
+
 /* The default RSYNC_RSH is always set in config.h, either to "remsh",
  * "rsh", or otherwise something specified by the user.  HAVE_REMSH
  * controls parameter munging for HP/UX, etc. */
@@ -335,6 +339,8 @@ enum logcode {FNONE=0, FERROR=1, FINFO=2
 #define IN_LOOPBACKNET 127
 #endif
 
+struct xa_list;
+
 struct file_struct {
 	unsigned flags;
 	time_t modtime;
@@ -354,6 +360,10 @@ struct file_struct {
 	char *basedir;
 	char *link;
 	char *sum;
+
+#if SUPPORT_XATTRS        
+        struct xa_list *xattrs;
+#endif
 };
 
 
@@ -465,9 +475,6 @@ extern char *sys_errlist[];
 extern int errno;
 #endif
 
-#define SUPPORT_LINKS HAVE_READLINK
-#define SUPPORT_HARD_LINKS HAVE_LINK
-
 /* This could be bad on systems which have no lchown and where chown
  * follows symbollic links.  On such systems it might be better not to
  * try to chown symlinks at all. */
@@ -629,3 +636,8 @@ const char *get_panic_action(void);
 #define UNUSED(x) x __attribute__((__unused__))
 
 extern const char *io_write_phase, *io_read_phase;
+
+
+
+/* Global command-line options */
+extern int opt_xattr;
diff -urpdN -x .ignore -x packaging -x cvs.log -x configure -x config.h.in -x autom4te.cache -x config.log -x .cvsignore -x dummy -x .svn -x ID -x TAGS rsync-2.5.6/xattr.c xa/xattr.c
--- rsync-2.5.6/xattr.c	1970-01-01 10:00:00.000000000 +1000
+++ xa/xattr.c	2003-06-25 08:29:56.000000000 +1000
@@ -0,0 +1,404 @@
+/* -*- c-file-style: "linux"
+ *
+ * Copyright (C) 2003 by Martin Pool
+ * 
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * @file
+ *
+ * Support for Linux/IRIX XFS/ext2/ext3 extended attributes.
+ *
+ * Extended attributes are sent along with other attributes of a file
+ * in the file list.  They are only sent (and expected) if the --xattr
+ * command line option is specified.
+ *
+ * Attributes in the filesystem are a map of NAME:VALUE pairs,
+ * retrieved one at a time.
+ *
+ * Attributes are serialized to a byte stream when transferred across
+ * the network.  This list is very simple:
+ *
+ *  constant int 0x0a0aa0a0
+ *  repeat
+ *   int name_len
+ *   char name[]
+ *   int val_len
+ *   char val[]
+ *
+ * The list is terminated by a single uint32 zero.
+ *
+ * The constant is there as a little protection against protocol
+ * mistakes.
+ *
+ * Attributes also have to be stored in memory as part of the file
+ * list in between reading them in and applying them to the file.
+ * They may in fact need to be sent several times.  There is no
+ * standard form that I know of for them, so we just store them as a
+ * list of duples.
+ *
+ * Error checking is a bit messy but it's not really great in rsync as
+ * a whole.
+ *
+ * We could also do this by running the getfattr(1) command on each
+ * file to get all attributes and pack them into a portable format.
+ * But this might be expensive because it has to be invoked for every
+ * file, and I'm not sure it would be a lot easier to write.
+ **/
+
+#include "rsync.h"
+
+#if SUPPORT_XATTRS
+
+#include <sys/types.h>
+#include <attr/xattr.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+struct xa_list {
+        size_t name_len;
+        char *name;
+        size_t val_len;
+        char *val;
+
+        /* Pointer to next in list */
+        struct xa_list *next;
+};
+
+
+/**
+ * Get an attribute and return it in a newly-allocated buffer.
+ **/
+static int
+xa_lgetxattr_alloc(const char *path,
+                   const char *name,
+                   char **val_return,
+                   size_t * val_len_return)
+{
+	char *buf = NULL;
+	ssize_t ret;
+	ssize_t buf_len = 0;
+
+        /* Keep trying until we've allocated enough space.  It might
+        * take more than one try in the edge case where they grow
+        * while we're looking. */
+	while (1) {
+		ret = lgetxattr(path, name, buf, buf_len);
+		if (ret == -1) {
+			rprintf(FERROR, "getxattr %s %s: %s\n", path, name,
+                                strerror(errno));
+			return -1;
+		} else if (buf_len < ret) {
+			/* need more space */
+			buf_len = ret;
+			if ((buf = realloc(buf, buf_len)) == NULL)
+                                out_of_memory("allocating xattr value");
+		} else {
+			/* woot */
+			*val_return = buf;
+			*val_len_return = buf_len;
+			return 0;
+		}
+	}
+}
+
+
+/**
+ * Return the names of the attributes for this file in a
+ * newly-allocated buffer.
+ **/
+static int
+xa_llistxattr_alloc(const char *path,
+                    char **buf_return,
+                    ssize_t * buf_len_return)
+{
+	char *buf = NULL;
+	ssize_t ret;
+	ssize_t buf_len = 0;
+
+	while (1) {
+		ret = llistxattr(path, buf, buf_len);
+		if (ret == -1) {
+			rprintf(FERROR, "llistxattr %s: %s\n", path,
+                                strerror(errno));
+			return -1;
+		} else if (buf_len < ret) {
+			/* need more space */
+			buf_len = ret;
+			if ((buf = realloc(buf, buf_len)) == NULL)
+                                out_of_memory("allocating list of xattr names");
+		} else {
+			/* woot */
+			*buf_return = buf;
+			*buf_len_return = buf_len;
+			return 0;
+		}
+	}
+}
+
+
+/**
+ * Read the one named attribute into a new list entry and store a
+ * pointer to it in *list_ret.
+ *
+ * The new item takes ownership wrt free() of the filename and attrname.
+ **/
+static int xa_load_one(char *filename,
+                       char *attrname,
+                       struct xa_list **list_ret)
+{
+        char *val;
+        size_t val_len;
+        int ret;
+        struct xa_list *newitem;
+
+        if (verbose > 2)
+                rprintf(FINFO, "loading \"%s\" attr \"%s\"\n", filename, attrname);
+        
+        if ((ret = xa_lgetxattr_alloc(filename, attrname, &val, &val_len)))
+                return ret;
+
+        if (verbose > 2)
+                rprintf(FINFO, "loaded \"%s\" attr \"%s\", %ld bytes\n",
+                        filename, attrname, (long) val_len);
+        
+        if ((newitem = malloc(sizeof (struct xa_list))) == NULL)
+                out_of_memory("allocating xa_list item");
+                
+        newitem->name_len = strlen(attrname);
+        newitem->name = attrname;
+        newitem->val = val;
+        newitem->val_len = val_len;
+        newitem->next = NULL;
+
+        *list_ret = newitem;
+        return 0;
+}
+
+
+/**
+ * Read the extended attributes for a file into an in-memory list.
+ *
+ * For symlinks we read the attributes of the link.
+ **/
+static int xa_load_list(char *filename,
+                        struct xa_list **list_ret)
+{
+	char *namebuf;
+	size_t namebuf_len;
+        struct xa_list **pnext;
+        char *p, *end;
+
+        *list_ret = NULL;
+
+        if (xa_llistxattr_alloc(filename, &namebuf, &namebuf_len)) {
+                rprintf(FINFO, "failed to get attr list\n");
+                return -1;         /* failed */
+        }
+
+/*         rprintf(FINFO, "got %ld byte attr list\n", (long) namebuf_len); */
+
+	end = namebuf + namebuf_len;
+        pnext = list_ret;
+	for (p = namebuf; p < end; p += strlen(p) + 1) {
+                if (xa_load_one(filename, p, pnext))
+                        continue;
+                pnext = &((*pnext)->next);
+	}
+
+        free(namebuf);
+
+        return 0;
+}
+
+
+
+/**
+ * Apply an xattr list to a file (or link or directory).
+ *
+ * The pair of xa_load_list().
+ *
+ * Called from set_perms when in --xattr mode.  It's called at
+ * different points as the file is received, but it should always be
+ * called before the file is finished.
+ *
+ * At the moment we do not need to worry about any existing attributes
+ * on the file, because rsync always creates new files to replace the
+ * destination, rather than modifying them.
+ **/
+int xalist_apply(const char *filename,
+                 struct xa_list *xal)
+{
+        for (; xal; xal = xal->next) {
+                if ((lsetxattr(filename, xal->name, xal->val, xal->val_len, 0))) {
+                        rprintf(FERROR, "lsetxattr \"%s\" \"%s\": %s\n",
+                                filename,
+                                xal->name, strerror(errno));
+                        return -1;
+                }
+
+                if (verbose > 2)
+                        rprintf(FINFO, "lsetxattr \"%s\" \"%s\" %ld bytes\n",
+                                filename, xal->name, (long) xal->val_len);
+        }
+
+        return 0;
+}
+
+
+/**
+ * Read the attributes on a file, and store them in memory.
+ *
+ * @param f File descriptor for network connection.
+ **/
+int xalist_load(char *fname,
+                struct file_struct *file)
+{
+        int ret;
+        
+        if (verbose > 2)
+                rprintf(FINFO, "xalist_load(%s)\n", f_name(file));
+
+        file->xattrs = NULL;
+
+        if ((ret = xa_load_list(fname, &file->xattrs)))
+                return ret;
+
+        return 0;
+}
+
+
+/**
+ * Write an in-memory representation out onto the network on fd @p f.
+ **/
+int xalist_send(int f,
+                struct xa_list *xal)
+{
+        write_int(f, 0x0a0aa0a0U);
+
+        for (; xal; xal = xal->next) {
+                write_int(f, xal->name_len);
+                write_buf(f, xal->name, xal->name_len);
+
+                write_int(f, xal->val_len);
+                write_buf(f, xal->val, xal->val_len);
+        }
+
+        write_int(f, 0);
+        
+        return 0;
+}
+
+
+static int xalist_receive_one(int f,
+                              struct xa_list **ppnext)
+{
+        ssize_t name_len, val_len;
+        struct xa_list *xal;
+
+        /* Read name length and check */
+        name_len = read_int(f);
+
+        if (name_len < 0 || name_len > 65536) {
+                rprintf(FERROR, "xattr name is too long (%ld)\n",
+                        (long) name_len);
+                exit_cleanup(RERR_PROTOCOL);
+        }
+
+        if (name_len == 0) {
+                *ppnext = NULL;  /* end of list */
+                return 0;
+        }
+
+        /* Allocate */
+        if ((xal = malloc(sizeof (struct xa_list))) == NULL) 
+                out_of_memory("allocating xa_list");
+
+        /* Chain this entry in */
+        xal->next = NULL;
+        *ppnext = xal;
+
+        /* Read name */
+        xal->name_len = name_len;
+        /* name must be nul-terminated but the value is not */
+        if ((xal->name = malloc(name_len + 1)) == NULL)
+                out_of_memory("allocating xattrs");
+        read_sbuf(f, xal->name, xal->name_len);
+
+
+        /* Read value length and buffer */
+        val_len = read_int(f);
+        
+        if (val_len < 0 || val_len > 1<<28) {
+                rprintf(FERROR, "xattr value is too long (%ld)\n",
+                        (long) val_len);
+                exit_cleanup(RERR_PROTOCOL);
+        }
+        xal->val_len = val_len;
+
+        if ((xal->val = malloc(val_len)) == NULL)
+                out_of_memory("allocating xattrs");
+        
+        read_buf(f, xal->val, xal->val_len);
+
+        if (verbose > 2)
+                rprintf(FINFO, "received attribute \"%s\", %d bytes\n",
+                        xal->name, xal->val_len);
+                
+        return 0;     
+}
+
+
+/**
+ * Receive extended attributes.
+ *
+ * They're pulled off the network and stored in the file list
+ * structure in mmeory.  They're later applied to the file from
+ * set_perms().
+ **/
+int xalist_receive(int f,
+                   struct file_struct *file)
+{
+        uint32 v;
+        struct xa_list **ppnext;
+
+        if (verbose > 2)
+                rprintf(FINFO, "receive_xattr(%s)\n", f_name(file));
+
+        v = read_int(f);
+
+        if (v != 0x0a0aa0a0U)
+                rprintf(FERROR, "didn't get xattr placeholder\n");
+
+        /* Store the first one in here, then chain on from there */
+        file->xattrs = NULL;
+        ppnext = &file->xattrs;
+
+        while (1) {
+                xalist_receive_one(f, ppnext);
+                if (*ppnext == NULL)
+                        break;  /* that's it */
+                ppnext = &((*ppnext)->next); /* place for next one */
+        }
+
+        return 0;
+}
+
+
+#endif /* SUPPORT_XATTRS */


-- 
Martin 



More information about the rsync mailing list