diff options
-rw-r--r-- | toys/posix/cp.c | 29 |
1 files changed, 18 insertions, 11 deletions
diff --git a/toys/posix/cp.c b/toys/posix/cp.c index ced756aa..9f22a6f0 100644 --- a/toys/posix/cp.c +++ b/toys/posix/cp.c @@ -54,6 +54,7 @@ int cp_node(struct dirtree *try) { 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 @@ int cp_node(struct dirtree *try) 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 @@ int cp_node(struct dirtree *try) // 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 @@ int cp_node(struct dirtree *try) 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 @@ int cp_node(struct dirtree *try) || 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)))) - { - 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) { + } 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)) + { + 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 @@ int cp_node(struct dirtree *try) 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 @@ int cp_node(struct dirtree *try) } 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. |