[Toybox] [PATCH] cp: implements -p and --preserve options

José Bollo jobol at nonadev.net
Wed May 20 03:22:10 PDT 2015


---
 toys/posix/cp.c | 121
++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 101 insertions(+), 20 deletions(-)

diff --git a/toys/posix/cp.c b/toys/posix/cp.c
index 803bff8..ecef0f6 100644
--- a/toys/posix/cp.c
+++ b/toys/posix/cp.c
@@ -7,7 +7,7 @@
 // options shared between mv/cp must be in same order (right to left)
 // for FLAG macros to work out right in shared infrastructure.
 
-USE_CP(NEWTOY(cp,
"<2RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
+USE_CP(NEWTOY(cp,
"<2(preserve):RHLPp"USE_CP_MORE("rdaslvnF(remove-destination)")"fi[-HLP"USE_CP_MORE("d")"]"USE_CP_MORE("[-ni]"), TOYFLAG_BIN))
 USE_MV(NEWTOY(mv, "<2"USE_CP_MORE("vnF")"fi"USE_CP_MORE("[-ni]"),
TOYFLAG_BIN))
 USE_INSTALL(NEWTOY(install, "<1cdDpsvm:o:g:", TOYFLAG_USR|TOYFLAG_BIN))
 
@@ -23,12 +23,14 @@ config CP
     -f	delete destination files we can't write to
     -F	delete any existing destination file first
(--remove-destination)
     -i	interactive, prompt before overwriting existing DEST
-    -p	preserve timestamps, ownership, and permissions
+    -p	preserve timestamps, ownership, and mode
     -R	recurse into subdirectories (DEST must be a directory)
     -H	Follow symlinks listed on command line
     -L	Follow all symlinks
     -P	Do not follow symlinks [default]
-
+    --preserve[=LISTE_ATT]  preserve the specified attributes:
+    mode,ownership,timestamps,context,links,xattr,all
+ 
 config CP_MORE
   bool "cp -adlnrsv options"
   default y
@@ -86,7 +88,12 @@ config INSTALL
 #define FOR_cp
 #include "toys.h"
 
+#include <sys/xattr.h>
+
 GLOBALS(
+  // cp preserve
+  char *arg_preserve;
+
   // install's options
   char *group;
   char *user;
@@ -97,8 +104,76 @@ GLOBALS(
   int (*callback)(struct dirtree *try);
   uid_t uid;
   gid_t gid;
+  enum {
+    p_mode = 1,
+    p_ownership = 2,
+    p_timestamps = 4,
+    p_context = 8,
+    p_links = 16,
+    p_xattr = 32,
+    p_verbose = 64,
+    p_p = 7,
+    p_all = 127 } preserve;
 )
 
+// interprete '--preserve' list of attributes
+
+static int set_preserve(const char *list)
+{
+  int p = 0;
+  char *s;
+  while(*list) {
+    while (*list == ',') list++;
+    switch (*list) {
+    case 'a': s = "all"; p |= p_all; break;
+    case 'c': s = "context"; p |= p_context; break;
+    case 'l': s = "links"; p |= p_links; break;
+    case 'm': s = "mode"; p |= p_mode; break;
+    case 'o': s = "ownership"; p |= p_ownership; break;
+    case 't': s = "timestamps"; p |= p_timestamps; break;
+    case 'x': s = "xattr"; p |= p_xattr; break;
+    default: return -1;
+    }
+    while(*s) if (*s++ != *list++) return -1;
+    if (*list == 0) break;
+    if (*list++ != ',') return -1;
+  }
+  TT.preserve = p;
+  return 0;
+}
+
+// Copies extended attributes
+
+static int cp_xattrs(int fdin, int fdout)
+{
+  int err = 0;
+  ssize_t len, listlen;
+  char *list, *name, *value;
+
+  if ((listlen = flistxattr(fdin, NULL, 0)) > 0 && (list =
malloc(listlen))) {
+    flistxattr(fdin, list, listlen);
+    for (name = list ; (name - list) < listlen ; name +=
strlen(name)+1) {
+      /* test if the xattribute have to be copied */
+      if ((TT.preserve & p_xattr)
+      || ((TT.preserve & p_mode) &&
+              !
strncmp(name,"system.posix_acl_",sizeof("system.posix_acl_")-1))
+      || ((TT.preserve & p_context) &&
+              !strncmp(name,"security.",sizeof("security.")-1)))
+      {
+        /* yes, try to copy it */
+        if ((len = fgetxattr(fdin, name, 0, 0)) > 0 && (value =
malloc(len))) {
+          fgetxattr(fdin, name, value, len);
+          err += !!fsetxattr(fdout, name, value, len, 0);
+          free(value);
+        } else err++;
+      }
+    }
+    if (err && (TT.preserve & p_verbose))
+      return -1;
+  }
+  return 0;
+}
+
 // Callback from dirtree_read() for each file/directory under a source
dir.
 
 int cp_node(struct dirtree *try)
@@ -249,6 +324,10 @@ int cp_node(struct dirtree *try)
           fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC,
try->st.st_mode);
           if (fdout >= 0) {
             xsendfile(fdin, fdout);
+
+            if (TT.preserve & (p_xattr | p_mode | p_context))
+              cp_xattrs(fdin, fdout);
+
             err = 0;
           }
           close(fdin);
@@ -258,15 +337,13 @@ int cp_node(struct dirtree *try)
   }
 
   if (fdout != -1) {
-    if (flags & (FLAG_a|FLAG_p)) {
-      struct timespec times[2];
+    if (TT.preserve & p_mode) {
+      // permission bits already correct for mknod and don't apply to
symlink
+      if (fdout != AT_FDCWD)
+        fchmod(fdout, try->st.st_mode);
+    }
+    if (TT.preserve & p_ownership) {
       int rc;
-
-      // Inability to set these isn't fatal, some require root access.
-
-      times[0] = try->st.st_atim;
-      times[1] = try->st.st_mtim;
-
       // If we can't get a filehandle to the actual object, use racy
functions
       if (fdout == AT_FDCWD)
         rc = fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
@@ -274,17 +351,15 @@ int cp_node(struct dirtree *try)
       else rc = fchown(fdout, try->st.st_uid, try->st.st_gid);
       if (rc) {
         char *pp;
-
         perror_msg("chown '%s'", pp = dirtree_path(try, 0));
         free(pp);
       }
-
-      // permission bits already correct for mknod and don't apply to
symlink
+    }
+    if (TT.preserve & p_timestamps) {
+      struct timespec times[2] = { try->st.st_atim, try->st.st_mtim };
+      // Inability to set these isn't fatal, some require root access.
       if (fdout == AT_FDCWD) utimensat(cfd, catch, times,
AT_SYMLINK_NOFOLLOW);
-      else {
-        futimens(fdout, times);
-        fchmod(fdout, try->st.st_mode);
-      }
+      else futimens(fdout, times);
     }
 
     if (fdout != AT_FDCWD) xclose(fdout);
@@ -304,8 +379,14 @@ void cp_main(void)
   int i, destdir = !stat(destname, &TT.top) && S_ISDIR(TT.top.st_mode);
 
   if (toys.optc>1 && !destdir) error_exit("'%s' not directory",
destname);
-  if (toys.which->name[0] == 'm') toys.optflags |= FLAG_d|FLAG_p|
FLAG_R;
-  if (toys.optflags & (FLAG_a|FLAG_p)) umask(0);
+  if (toys.which->name[0] == 'm') toys.optflags |= FLAG_d|FLAG_a|
FLAG_R;
+  if ((toys.optflags & FLAG_preserve) && set_preserve(TT.arg_preserve)
< 0)
+    error_exit("bad --preserve value '%s'",TT.arg_preserve);
+  if (toys.optflags & FLAG_p) TT.preserve |= p_p;
+  if (toys.optflags & FLAG_a) TT.preserve |= p_all;
+  if (TT.preserve & p_mode) umask(0);
+  if (TT.preserve & p_links)
+    fprintf(stderr, "warning: --preserve=links not supported\n");
 
   if (!TT.callback) TT.callback = cp_node;
 
-- 
2.1.2





More information about the Toybox mailing list