OSDN Git Service

Merge branch 'master' of https://android.googlesource.com/platform/external/toybox...
[android-x86/external-toybox.git] / toys / posix / cp.c
index 6f347cc..6e035f7 100644 (file)
@@ -6,21 +6,24 @@
  *
  * Posix says "cp -Rf dir file" shouldn't delete file, but our -f does.
  *
+ * Deviations from posix: -adlnrsvF, --preserve... about half the
+ * functionality in this cp isn't in posix. Posix is stuck in the 1970's.
+ *
  * TODO: --preserve=links
  * TODO: what's this _CP_mode system.posix_acl_ business? We chmod()?
 
 // 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, "<2"USE_CP_PRESERVE("(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_CP(NEWTOY(cp, "<2"USE_CP_PRESERVE("(preserve):;")"RHLPprdaslvnF(remove-destination)fi[-HLPd][-ni]", TOYFLAG_BIN))
+USE_MV(NEWTOY(mv, "<2vnF(remove-destination)fi[-ni]", TOYFLAG_BIN))
 USE_INSTALL(NEWTOY(install, "<1cdDpsvm:o:g:", TOYFLAG_USR|TOYFLAG_BIN))
 
 config CP
   bool "cp"
   default y
   help
-    usage: cp [-fipRHLP] SOURCE... DEST
+    usage: cp [-adlnrsvfipRHLP] SOURCE... DEST
 
     Copy files from SOURCE to DEST.  If more than one SOURCE, DEST must
     be a directory.
@@ -33,14 +36,6 @@ config CP
     -H Follow symlinks listed on command line
     -L Follow all symlinks
     -P Do not follow symlinks [default]
-
-config CP_MORE
-  bool "cp -adlnrsv options"
-  default y
-  depends on CP
-  help
-    usage: cp [-adlnrsv]
-
     -a same as -dpr
     -d don't dereference symlinks
     -l hard link instead of copy
@@ -52,7 +47,7 @@ config CP_MORE
 config CP_PRESERVE
   bool "cp --preserve support"
   default y
-  depends on CP_MORE
+  depends on CP
   help
     usage: cp [--preserve=motcxa]
 
@@ -69,27 +64,17 @@ config CP_PRESERVE
 config MV
   bool "mv"
   default y
-  depends on CP
   help
-    usage: mv [-fi] SOURCE... DEST"
+    usage: mv [-fivn] SOURCE... DEST"
 
     -f force copy by deleting destination file
     -i interactive, prompt before overwriting existing DEST
-
-config MV_MORE
-  bool
-  default y
-  depends on MV && CP_MORE
-  help
-    usage: mv [-vn]
-
     -v verbose
     -n no clobber (don't overwrite DEST)
 
 config INSTALL
   bool "install"
   default y
-  depends on CP && CP_MORE
   help
     usage: install [-dDpsv] [-o USER] [-g GROUP] [-m MODE] [SOURCE...] DEST
 
@@ -105,11 +90,9 @@ config INSTALL
     -v Verbose
 */
 
+#define FORCE_FLAGS
 #define FOR_cp
 #include "toys.h"
-#if CFG_CP_PRESERVE
-#include <sys/xattr.h>
-#endif
 
 GLOBALS(
   union {
@@ -172,7 +155,7 @@ int cp_node(struct dirtree *try)
       return 0;
     }
 
-    // Handle -inv
+    // Handle -invF
 
     if (!faccessat(cfd, catch, F_OK, 0) && !S_ISDIR(cst.st_mode)) {
       char *s;
@@ -268,8 +251,8 @@ int cp_node(struct dirtree *try)
 
         // make symlink, or make block/char/fifo/socket
         if (S_ISLNK(try->st.st_mode)
-            ? (0 < (i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf))) &&
-               sizeof(toybuf) > i && !symlinkat(toybuf, cfd, catch))
+            ? ((i = readlinkat0(tfd, try->name, toybuf, sizeof(toybuf))) &&
+               !symlinkat(toybuf, cfd, catch))
             : !mknodat(cfd, catch, try->st.st_mode, try->st.st_rdev))
         {
           err = 0;
@@ -334,7 +317,13 @@ int cp_node(struct dirtree *try)
       if (fdout == AT_FDCWD)
         fchownat(cfd, catch, try->st.st_uid, try->st.st_gid,
                       AT_SYMLINK_NOFOLLOW);
-      else fchown(fdout, try->st.st_uid, try->st.st_gid);
+      else rc = fchown(fdout, try->st.st_uid, try->st.st_gid);
+      if (rc && !geteuid()) {
+        char *pp;
+
+        perror_msg("chown '%s'", pp = dirtree_path(try, 0));
+        free(pp);
+      }
     }
 
     // timestamp
@@ -356,7 +345,19 @@ int cp_node(struct dirtree *try)
         err = "%s";
   }
 
-  if (err) perror_msg(err, catch);
+  if (err) {
+    char *f = 0;
+
+    if (catch == try->name) {
+      f = dirtree_path(try, 0);
+      while (try->parent) try = try->parent;
+      catch = xmprintf("%s%s", TT.destname, f+strlen(try->name));
+      free(f);
+      f = catch;
+    }
+    perror_msg(err, catch);
+    free(f);
+  }
   return 0;
 }
 
@@ -398,7 +399,6 @@ void cp_main(void)
 
   // Loop through sources
   for (i=0; i<toys.optc; i++) {
-    struct dirtree *new;
     char *src = toys.optargs[i];
     int rc = 1;
 
@@ -407,29 +407,31 @@ void cp_main(void)
 
     errno = EXDEV;
     if (CFG_MV && toys.which->name[0] == 'm') {
-      if (!(toys.optflags & FLAG_f)) {
+      int force = toys.optflags & FLAG_f, no_clobber = toys.optflags & FLAG_n;
+
+      if (!force || no_clobber) {
         struct stat st;
+        int exists = !stat(TT.destname, &st);
 
-        // Technically "is writeable" is more complicated (022 is not writeable
-        // by the owner, just everybody _else_) but I don't care.
-        if (!stat(TT.destname, &st)
-          && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222)))
-        {
+        // Prompt if -i or file isn't writable.  Technically "is writable" is
+        // more complicated (022 is not writeable by the owner, just everybody
+        // _else_) but I don't care.
+        if (exists && ((toys.optflags & FLAG_i) || !(st.st_mode & 0222))) {
           fprintf(stderr, "%s: overwrite '%s'", toys.which->name, TT.destname);
           if (!yesno(1)) rc = 0;
           else unlink(TT.destname);
         }
+        // if -n and dest exists, don't try to rename() or copy
+        if (exists && no_clobber) rc = 0;
       }
-
       if (rc) rc = rename(src, TT.destname);
     }
 
-    // Skip nonexistent sources
+    // Copy if we didn't mv, skipping nonexistent sources
     if (rc) {
-      if (errno!=EXDEV ||
-        !(new = dirtree_start(src, toys.optflags&(FLAG_H|FLAG_L))))
+      if (errno!=EXDEV || dirtree_flagread(src, DIRTREE_SHUTUP+
+        DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), TT.callback))
           perror_msg("bad '%s'", src);
-      else dirtree_handle_callback(new, TT.callback);
     }
     if (destdir) free(TT.destname);
   }
@@ -497,8 +499,8 @@ void install_main(void)
   if (flags & FLAG_v) toys.optflags |= cp_flag_v();
   if (flags & (FLAG_p|FLAG_o|FLAG_g)) toys.optflags |= cp_flag_p();
 
-  if (TT.i.user) TT.uid = xgetpwnamid(TT.i.user)->pw_uid;
-  if (TT.i.group) TT.gid = xgetgrnamid(TT.i.group)->gr_gid;
+  if (TT.i.user) TT.uid = xgetuid(TT.i.user);
+  if (TT.i.group) TT.gid = xgetgid(TT.i.group);
 
   TT.callback = install_node;
   cp_main();