tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: cp/mv and extended attributes preservation



On Tue, Jul 26, 2011 at 12:26:30PM +0000, Emmanuel Dreyfus wrote:
> Currently, our cp(1) will never preserve extended attributes, and mv(1)
> will only do so across the same filesystem (that is, when it can use
> rename(2)). While I found no specification that it should be done, it
> seems Linux and Darwin do that. 
> 
> Any objection to modify cp(1) and mv(1) to preserve extended attributes?

Attached is a patch that does it. I am concerned that with multi-file
cp or mv, it adds a system call for each copied file, even if it has
no extended attributes. I suspect the overhead is negligible compared 
to time spent in I/O, but if someone sees a better way of doing it...

-- 
Emmanuel Dreyfus
manu%netbsd.org@localhost
? bin/cp/obj
? bin/mv/obj
Index: bin/cp/cp.c
===================================================================
RCS file: /cvsroot/src/bin/cp/cp.c,v
retrieving revision 1.55
diff -U4 -r1.55 cp.c
--- bin/cp/cp.c 6 Feb 2011 12:37:49 -0000       1.55
+++ bin/cp/cp.c 26 Jul 2011 19:44:44 -0000
@@ -196,8 +196,15 @@
        /* Copy the umask for explicit mode setting. */
        myumask = umask(0);
        (void)umask(myumask);
 
+       /* 
+        * Warn that system extended attributes will not be preserved 
+        * if not root
+        */
+       if ((myuid != 0) && pflag)
+               warnx("system extended attribute will not be preserved");
+
        /* Save the target base in "to". */
        target = argv[--argc];
        if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path))
                errx(EXIT_FAILURE, "%s: name too long", target);
Index: bin/cp/utils.c
===================================================================
RCS file: /cvsroot/src/bin/cp/utils.c,v
retrieving revision 1.39
diff -U4 -r1.39 utils.c
--- bin/cp/utils.c      6 Feb 2011 12:37:49 -0000       1.39
+++ bin/cp/utils.c      26 Jul 2011 19:44:44 -0000
@@ -41,8 +41,9 @@
 #include <sys/mman.h>
 #include <sys/param.h>
 #include <sys/stat.h>
 #include <sys/time.h>
+#include <sys/extattr.h>
 
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -72,8 +73,76 @@
     }
     return (0);
 }
 
+static int
+copy_file_xattr(int namespace, int from_fd, int to_fd)
+{
+       ssize_t alen, vlen, maxvlen;
+       char *alist = NULL;
+       char *aval = NULL;
+       int i;
+       int error = -1;
+
+       alen = extattr_list_fd(from_fd, namespace, NULL, 0);
+       if (alen == -1) {
+               /* Silently ignore when EA are not supported */
+               if (errno == EOPNOTSUPP)
+                       error = 0;
+               goto out;
+       }
+
+       if (alen == 0) {
+               error = 0;
+               goto out;
+       }
+
+       if ((alist = malloc(alen)) == NULL)
+               goto out;
+
+       alen = extattr_list_fd(from_fd, namespace, alist, alen);
+       if (alen == -1)
+               goto out;
+
+       maxvlen = 1024;
+       if ((aval = malloc(maxvlen)) == NULL)
+               goto out;
+
+       for (i = 0; i < alen; i += (uint8_t)alist[i] + 1) {
+               char aname[NAME_MAX + 1];
+
+               (void)memcpy(aname, alist + i + 1, (uint8_t)alist[i]);
+               aname[(uint8_t)alist[i]] = '\0';
+
+               vlen = extattr_get_fd(from_fd, namespace, aname, NULL, 0);
+               if (vlen == -1)
+                       goto out;
+
+               if (vlen > maxvlen) {
+                       if ((aval = realloc(aval, vlen)) == NULL)
+                               goto out;
+                       maxvlen = vlen;
+               }
+
+               vlen = extattr_get_fd(from_fd, namespace, aname, aval, vlen);
+               if (vlen == -1)
+                       goto out;
+       
+               if (extattr_set_fd(to_fd, namespace, aname, aval, vlen) != vlen)
+                       goto out;
+       }
+
+       error = 0;
+out:
+       if (aval != NULL)
+               free(aval);
+       
+       if (alist != NULL)
+               free(alist);
+       
+       return error;
+}
+
 int
 copy_file(FTSENT *entp, int dne)
 {
        static char buf[MAXBSIZE];
@@ -226,8 +295,15 @@
                        }
                }
        }
 
+       if (copy_file_xattr(EXTATTR_NAMESPACE_USER, from_fd, to_fd) != 0)
+               warn("%s: error copying user extended attributes", to.p_path);
+
+       if ((myuid == 0) && 
+           (copy_file_xattr(EXTATTR_NAMESPACE_SYSTEM, from_fd, to_fd) != 0))
+               warn("%s: error copying system extended attributes", to.p_path);
+
        (void)close(from_fd);
 
        if (rval == 1) {
                (void)close(to_fd);
Index: bin/mv/mv.c
===================================================================
RCS file: /cvsroot/src/bin/mv/mv.c,v
retrieving revision 1.41
diff -U4 -r1.41 mv.c
--- bin/mv/mv.c 20 Jul 2008 00:52:40 -0000      1.41
+++ bin/mv/mv.c 26 Jul 2011 19:44:44 -0000
@@ -49,8 +49,9 @@
 #include <sys/param.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
+#include <sys/extattr.h>
 
 #include <err.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -72,8 +73,77 @@
 int    fastcopy(char *, char *, struct stat *);
 void   usage(void);
 int    main(int, char *[]);
 
+
+static int
+copy_file_xattr(int namespace, int from_fd, int to_fd)
+{
+       ssize_t alen, vlen, maxvlen;
+       char *alist = NULL;
+       char *aval = NULL;
+       int i;
+       int error = -1;
+
+       alen = extattr_list_fd(from_fd, namespace, NULL, 0);
+       if (alen == -1) {
+               /* Silently ignore when EA are not supported */
+               if (errno == EOPNOTSUPP)
+                       error = 0;
+               goto out;
+       }
+
+       if (alen == 0) {
+               error = 0;
+               goto out;
+       }
+
+       if ((alist = malloc(alen)) == NULL)
+               goto out;
+
+       alen = extattr_list_fd(from_fd, namespace, alist, alen);
+       if (alen == -1)
+               goto out;
+
+       maxvlen = 1024;
+       if ((aval = malloc(maxvlen)) == NULL)
+               goto out;
+
+       for (i = 0; i < alen; i += (uint8_t)alist[i] + 1) {
+               char aname[NAME_MAX + 1];
+
+               (void)memcpy(aname, alist + i + 1, (uint8_t)alist[i]);
+               aname[(uint8_t)alist[i]] = '\0';
+
+               vlen = extattr_get_fd(from_fd, namespace, aname, NULL, 0);
+               if (vlen == -1)
+                       goto out;
+
+               if (vlen > maxvlen) {
+                       if ((aval = realloc(aval, vlen)) == NULL)
+                               goto out;
+                       maxvlen = vlen;
+               }
+
+               vlen = extattr_get_fd(from_fd, namespace, aname, aval, vlen);
+               if (vlen == -1)
+                       goto out;
+       
+               if (extattr_set_fd(to_fd, namespace, aname, aval, vlen) != vlen)
+                       goto out;
+       }
+
+       error = 0;
+out:
+       if (aval != NULL)
+               free(aval);
+       
+       if (alist != NULL)
+               free(alist);
+       
+       return error;
+}
+
 int
 main(int argc, char *argv[])
 {
        int ch, len, rval;
@@ -315,8 +385,15 @@
                warn("%s: set mode", to);
        if (fchflags(to_fd, sbp->st_flags) && (errno != EOPNOTSUPP))
                warn("%s: set flags (was: 0%07o)", to, sbp->st_flags);
 
+       if (copy_file_xattr(EXTATTR_NAMESPACE_USER, from_fd, to_fd) == -1)
+               warn("%s: error copying user extended attributes", to);
+
+       if ((getuid() == 0) &&
+           (copy_file_xattr(EXTATTR_NAMESPACE_SYSTEM, from_fd, to_fd) == -1))
+               warn("%s: error copying system extended attribute", to);
+
        if (close(to_fd)) {
                warn("%s", to);
                return (1);
        }


Home | Main Index | Thread Index | Old Index