changeset 784:d8b2f7706f82

Teach cp to do mknod.
author Rob Landley <rob@landley.net>
date Wed, 09 Jan 2013 05:16:48 -0600
parents 7bbb49149bb6
children 50441fee583d
files toys/posix/cp.c
diffstat 1 files changed, 18 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/toys/posix/cp.c	Mon Jan 07 21:28:46 2013 -0600
+++ b/toys/posix/cp.c	Wed Jan 09 05:16:48 2013 -0600
@@ -54,6 +54,7 @@
 {
   int fdout, cfd = try->parent ? try->parent->extra : AT_FDCWD,
       tfd = dirtree_parentfd(try);
+  unsigned flags = toys.optflags;
   char *catch = try->parent ? try->name : TT.destname, *err = "%s";
   struct stat cst;
 
@@ -66,6 +67,9 @@
     goto dashp;
   }
 
+  // -d is only the same as -r for symlinks, not for directories
+  if (S_ISLNK(try->st.st_mode) & (flags & FLAG_d)) flags |= FLAG_r;
+
   // Detect recursive copies via repeated top node (cp -R .. .) or
   // identical source/target (fun with hardlinks).
   if ((TT.top.st_dev == try->st.st_dev && TT.top.st_ino == try->st.st_ino
@@ -82,10 +86,10 @@
 
   // Handle -i and -v
 
-  if ((toys.optflags & FLAG_i) && !faccessat(cfd, catch, R_OK, 0)
+  if ((flags & FLAG_i) && !faccessat(cfd, catch, R_OK, 0)
     && !yesno("cp: overwrite", 1)) return 0;
 
-  if (toys.optflags & FLAG_v) {
+  if (flags & FLAG_v) {
     char *s = dirtree_path(try, 0);
     printf("cp '%s'\n", s);
     free(s);
@@ -96,7 +100,7 @@
   if (S_ISDIR(try->st.st_mode)) {
     struct stat st2;
 
-    if (!(toys.optflags & (FLAG_a|FLAG_r|FLAG_R))) {
+    if (!(flags & (FLAG_a|FLAG_r|FLAG_R))) {
       err = "Skipped dir '%s'";
       catch = try->name;
 
@@ -111,13 +115,16 @@
       || 0>(try->extra = openat(cfd, catch, 0)) || fstat(try->extra, &st2)
       || !S_ISDIR(st2.st_mode));
     else return DIRTREE_COMEAGAIN;
-  } else if (S_ISLNK(try->st.st_mode)
-    && (try->parent || (toys.optflags & (FLAG_a|FLAG_d))))
+  } else if (flags & FLAG_l) {
+    if (!linkat(tfd, try->name, cfd, catch, 0)) err = 0;
+  } else if ((try->parent || (flags & (FLAG_a|FLAG_r)))
+             && !S_ISREG(try->st.st_mode))
   {
-    int i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf));
-    if (i > 0 && i < sizeof(toybuf) && !symlinkat(toybuf, cfd, catch)) err = 0;
-  } else if (toys.optflags & FLAG_l) {
-    if (!linkat(tfd, try->name, cfd, catch, 0)) err = 0;
+    if (S_ISLNK(try->st.st_mode)) {
+      int i = readlinkat(tfd, try->name, toybuf, sizeof(toybuf));
+      if (i > 0 && i < sizeof(toybuf) && !symlinkat(toybuf, cfd, catch))
+        err = 0;
+    } else if (!mknodat(cfd, catch, try->st.st_mode, try->st.st_dev)) err = 0;
   } else {
     int fdin, i;
 
@@ -126,7 +133,7 @@
     else {
       for (i=2 ; i; i--) {
         fdout = openat(cfd, catch, O_RDWR|O_CREAT|O_TRUNC, try->st.st_mode);
-        if (fdout>=0 || !(toys.optflags & FLAG_f)) break;
+        if (fdout>=0 || !(flags & FLAG_f)) break;
         unlinkat(cfd, catch, 0);
       }
       if (fdout >= 0) {
@@ -137,7 +144,7 @@
     }
 
 dashp:
-    if (toys.optflags & (FLAG_a|FLAG_p)) {
+    if (!(flags & FLAG_l) && (flags & (FLAG_a|FLAG_p))) {
       struct timespec times[2];
 
       // Inability to set these isn't fatal, some require root access.