Tested manually:

  mkdir x
  setfattr -n user.key -v value x
  ./toybox cp -r --preserve=a x y
  getfattr -d y

I didn't want to be the first to add a test that uses setfattr/getfattr,
but if you say it's okay I'll send another patch with that.

Fixes: #112
---
 toys/posix/cp.c | 57 ++++++++++++++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 24 deletions(-)

diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 5e1a0163..5aee8bdd 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -276,30 +276,6 @@ static int cp_node(struct dirtree *try)
           err = 0;
         }

-        // We only copy xattrs for files because there's no flistxattrat()
-        if (TT.pflags&(_CP_xattr|_CP_context)) {
-          ssize_t listlen = flistxattr(fdin, 0, 0), len;
-          char *name, *value, *list;
-
-          if (listlen>0) {
-            list = xmalloc(listlen);
-            flistxattr(fdin, list, listlen);
-            list[listlen-1] = 0; // I do not trust this API.
-            for (name = list; name-list < listlen; name += strlen(name)+1) {
-              if (!(TT.pflags&_CP_xattr) && strncmp(name, "security.", 9))
-                continue;
-              if ((len = fgetxattr(fdin, name, 0, 0))>0) {
-                value = xmalloc(len);
-                if (len == fgetxattr(fdin, name, value, len))
-                  if (fsetxattr(fdout, name, value, len, 0))
-                    perror_msg("%s setxattr(%s=%s)", catch, name, value);
-                free(value);
-              }
-            }
-            free(list);
-          }
-        }
-
         close(fdin);
       }
     } while (err && (flags & (FLAG_f|FLAG_n)) && !unlinkat(cfd, catch, 0));
@@ -336,6 +312,39 @@ static int cp_node(struct dirtree *try)
       else futimens(fdout, times);
     }

+    // POSIX extended attributes
+    if (TT.pflags&(_CP_xattr|_CP_context)) {
+      int fdin = openat(tfd, try->name, O_RDONLY);
+
+      if (fdin == -1) {
+        perror_msg("openat %s", try->name);
+      } else {
+        ssize_t listlen = flistxattr(fdin, 0, 0);
+
+        if (listlen>0) {
+          char *list = xmalloc(listlen), *name, *value;
+
+          flistxattr(fdin, list, listlen);
+          list[listlen-1] = 0; // I do not trust this API.
+          for (name = list; name-list < listlen; name += strlen(name)+1) {
+            ssize_t len;
+
+            if (!(TT.pflags&_CP_xattr) && strncmp(name, "security.", 9))
+              continue;
+            if ((len = fgetxattr(fdin, name, 0, 0))>0) {
+              value = xmalloc(len);
+              if (len == fgetxattr(fdin, name, value, len))
+                if (fsetxattr(fdout, name, value, len, 0))
+                  perror_msg("%s setxattr(%s=%s)", catch, name, value);
+              free(value);
+            }
+          }
+          free(list);
+        }
+        close(fdin);
+      }
+    }
+
     // mode comes last because other syscalls can strip suid bit
     if (fdout != AT_FDCWD) {
       if (TT.pflags & _CP_mode) fchmod(fdout, try->st.st_mode);
-- 
2.21.0.rc0.258.g878e2cd30e-goog
From 655efaf0d838f9affab66ec205475a8900a05a30 Mon Sep 17 00:00:00 2001
From: Elliott Hughes <[email protected]>
Date: Tue, 19 Feb 2019 17:11:00 -0800
Subject: [PATCH] cp: fix copying xattrs on directories.

Tested manually:

  mkdir x
  setfattr -n user.key -v value x
  ./toybox cp -r --preserve=a x y
  getfattr -d y

I didn't want to be the first to add a test that uses setfattr/getfattr,
but if you say it's okay I'll send another patch with that.

Fixes: #112
---
 toys/posix/cp.c | 57 ++++++++++++++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 24 deletions(-)

diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 5e1a0163..5aee8bdd 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -276,30 +276,6 @@ static int cp_node(struct dirtree *try)
           err = 0;
         }
 
-        // We only copy xattrs for files because there's no flistxattrat()
-        if (TT.pflags&(_CP_xattr|_CP_context)) {
-          ssize_t listlen = flistxattr(fdin, 0, 0), len;
-          char *name, *value, *list;
-
-          if (listlen>0) {
-            list = xmalloc(listlen);
-            flistxattr(fdin, list, listlen);
-            list[listlen-1] = 0; // I do not trust this API.
-            for (name = list; name-list < listlen; name += strlen(name)+1) {
-              if (!(TT.pflags&_CP_xattr) && strncmp(name, "security.", 9))
-                continue;
-              if ((len = fgetxattr(fdin, name, 0, 0))>0) {
-                value = xmalloc(len);
-                if (len == fgetxattr(fdin, name, value, len))
-                  if (fsetxattr(fdout, name, value, len, 0))
-                    perror_msg("%s setxattr(%s=%s)", catch, name, value);
-                free(value);
-              }
-            }
-            free(list);
-          }
-        }
-
         close(fdin);
       }
     } while (err && (flags & (FLAG_f|FLAG_n)) && !unlinkat(cfd, catch, 0));
@@ -336,6 +312,39 @@ static int cp_node(struct dirtree *try)
       else futimens(fdout, times);
     }
 
+    // POSIX extended attributes
+    if (TT.pflags&(_CP_xattr|_CP_context)) {
+      int fdin = openat(tfd, try->name, O_RDONLY);
+
+      if (fdin == -1) {
+        perror_msg("openat %s", try->name);
+      } else {
+        ssize_t listlen = flistxattr(fdin, 0, 0);
+
+        if (listlen>0) {
+          char *list = xmalloc(listlen), *name, *value;
+
+          flistxattr(fdin, list, listlen);
+          list[listlen-1] = 0; // I do not trust this API.
+          for (name = list; name-list < listlen; name += strlen(name)+1) {
+            ssize_t len;
+
+            if (!(TT.pflags&_CP_xattr) && strncmp(name, "security.", 9))
+              continue;
+            if ((len = fgetxattr(fdin, name, 0, 0))>0) {
+              value = xmalloc(len);
+              if (len == fgetxattr(fdin, name, value, len))
+                if (fsetxattr(fdout, name, value, len, 0))
+                  perror_msg("%s setxattr(%s=%s)", catch, name, value);
+              free(value);
+            }
+          }
+          free(list);
+        }
+        close(fdin);
+      }
+    }
+
     // mode comes last because other syscalls can strip suid bit
     if (fdout != AT_FDCWD) {
       if (TT.pflags & _CP_mode) fchmod(fdout, try->st.st_mode);
-- 
2.21.0.rc0.258.g878e2cd30e-goog

_______________________________________________
Toybox mailing list
[email protected]
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to