<div dir="ltr">*ping* (in case this got categorized as spam by gmail)</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Sun, Feb 14, 2021 at 9:13 PM Yi-Yo Chiang <<a href="mailto:yochiang@google.com">yochiang@google.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">If -u is specified, then replace any existing files or directories.<br>
<br>
If -u is not specified and the path to be inflated already exist, then<br>
report EEXIST error. This behaves slightly different from GNU cpio, as<br>
GNU cpio checks the timestamp and replaces when the timestamp of the<br>
existing file is older than the one from the archive. This still<br>
conforms to SUSv2 as it doesn't define how to behave when -u is not<br>
specified.<br>
<br>
There is an exception, if we are creating an existing directory, then<br>
don't report any error or try to rmdir() the directory as we can<br>
"create" the directory by chmod() / chown() the existing one.<br>
<br>
---<br>
 tests/cpio.test   | 16 ++++++++++++++++<br>
 toys/posix/cpio.c | 46 ++++++++++++++++++++++++++++++++++++----------<br>
 2 files changed, 52 insertions(+), 10 deletions(-)<br>
<br>
diff --git a/tests/cpio.test b/tests/cpio.test<br>
index 6ab3665a..7e2955a1 100755<br>
--- a/tests/cpio.test<br>
+++ b/tests/cpio.test<br>
@@ -42,4 +42,20 @@ touch a; chmod a-rwx a; ln -s a/cant b<br>
 toyonly testing "archives unreadable empty files" "cpio -o -H newc|cpio -it" "b\na\n" "" "b\na\n"<br>
 chmod u+rw a; rm -f a b<br>
<br>
+mkdir a<br>
+echo "old" >a/b<br>
+echo "a/b" | cpio -o -H newc >a.cpio<br>
+rm -rf a<br>
+testing "-i doesn't create leading directories" "cpio -i <a.cpio 2>/dev/null; [ -e a ] || echo yes" "yes\n" "" ""<br>
+rm -rf a<br>
+testing "-id creates leading directories" "cpio -id <a.cpio && cat a/b" "old\n" "" ""<br>
+rm -rf a a.cpio<br>
<br>
+mkdir a<br>
+echo "old" >a/b<br>
+find a | cpio -o -H newc >a.cpio<br>
+testing "-i keeps existing files" "echo new >a/b && cpio -i <a.cpio 2>/dev/null; cat a/b" "new\n" "" ""<br>
+testing "-id keeps existing files" "echo new >a/b && cpio -id <a.cpio 2>/dev/null; cat a/b" "new\n" "" ""<br>
+testing "-iu replaces existing files; no error" "echo new >a/b && cpio -iu <a.cpio && cat a/b" "old\n" "" ""<br>
+testing "-idu replaces existing files; no error" "echo new >a/b && cpio -idu <a.cpio && cat a/b" "old\n" "" ""<br>
+rm -rf a a.cpio<br>
diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c<br>
index 31c777c9..795f890c 100644<br>
--- a/toys/posix/cpio.c<br>
+++ b/toys/posix/cpio.c<br>
@@ -22,7 +22,7 @@ config CPIO<br>
   default y<br>
   help<br>
     usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [--no-preserve-owner]<br>
-           [ignored: -mdu -H newc]<br>
+           [ignored: -m -H newc]<br>
<br>
     Copy files into and out of a "newc" format cpio archive.<br>
<br>
@@ -32,6 +32,7 @@ config CPIO<br>
     -o Create archive (stdin=list of files, stdout=archive)<br>
     -t Test files (list only, stdin=archive, stdout=list of files)<br>
     -d Create directories if needed<br>
+    -u Copy unconditionally<br>
     -v Verbose<br>
     --no-preserve-owner (don't set ownership during extract)<br>
 */<br>
@@ -113,7 +114,8 @@ void cpio_main(void)<br>
   if (FLAG(i) || FLAG(t)) for (;;) {<br>
     char *name, *tofree, *data;<br>
     unsigned size, mode, uid, gid, timestamp;<br>
-    int test = FLAG(t), err = 0;<br>
+    int test = FLAG(t), err = 0, exist = 0;<br>
+    struct stat st;<br>
<br>
     // Read header and name.<br>
     if (!(size =readall(afd, toybuf, 110))) break;<br>
@@ -137,25 +139,50 @@ void cpio_main(void)<br>
     // (This output is unaffected by --quiet.)<br>
     if (FLAG(t) || FLAG(v)) puts(name);<br>
<br>
-    if (!test && FLAG(d) && strrchr(name, '/') && mkpath(name)) {<br>
+    if (!test) exist = !lstat(name, &st);<br>
+<br>
+    // Create leading directories if |name| doesn't exist.<br>
+    if (!test && !exist && FLAG(d) && strrchr(name, '/') && mkpath(name)) {<br>
       perror_msg("mkpath '%s'", name);<br>
       test++;<br>
     }<br>
<br>
+    // Don't report error or try to rmdir(name) if we want to mkdir(name) later.<br>
+    if (!test && exist && !(S_ISDIR(st.st_mode) && S_ISDIR(mode))) {<br>
+      if (!FLAG(u)) {<br>
+        errno = EEXIST;<br>
+        perror_msg_raw(name);<br>
+        test++;<br>
+      } else if (S_ISDIR(st.st_mode) ? rmdir(name) : unlink(name)) {<br>
+        perror_msg("remove '%s'", name);<br>
+        test++;<br>
+      } else {<br>
+        exist = 0;<br>
+      }<br>
+    }<br>
+<br>
     // Consume entire record even if it couldn't create file, so we're<br>
     // properly aligned with next file.<br>
<br>
     if (S_ISDIR(mode)) {<br>
-      if (!test) err = mkdir(name, mode);<br>
+      if (!test) {<br>
+        // If |name| already exist as a directory, then just do a chmod() to fix<br>
+        // up the directory permissions.<br>
+        if (!exist) err = mkdir(name, mode);<br>
+        else if (S_ISDIR(st.st_mode)) err = chmod(name, mode);<br>
+        else err = errno = EEXIST;<br>
+      }<br>
     } else if (S_ISLNK(mode)) {<br>
       data = strpad(afd, size, 0);<br>
-      if (!test) err = symlink(data, name);<br>
+      if (!test) {<br>
+        err = symlink(data, name);<br>
+        // Can't get a filehandle to a symlink, so do special chown<br>
+        if (!err && !geteuid() && !FLAG(no_preserve_owner))<br>
+          err = lchown(name, uid, gid);<br>
+      }<br>
       free(data);<br>
-      // Can't get a filehandle to a symlink, so do special chown<br>
-      if (!err && !geteuid() && !FLAG(no_preserve_owner))<br>
-        err = lchown(name, uid, gid);<br>
     } else if (S_ISREG(mode)) {<br>
-      int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode);<br>
+      int fd = test ? 0 : open(name, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, mode);<br>
<br>
       // If write fails, we still need to read/discard data to continue with<br>
       // archive. Since doing so overwrites errno, report error now<br>
@@ -197,7 +224,6 @@ void cpio_main(void)<br>
           && !FLAG(no_preserve_owner))<br>
       {<br>
         int fd = open(name, O_RDONLY|O_NOFOLLOW);<br>
-        struct stat st;<br>
<br>
         if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT))<br>
           err = fchown(fd, uid, gid);<br>
-- <br>
2.30.0.478.g8a0d178c01-goog<br>
<br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><table width="90%" border="0" cellspacing="0" cellpadding="0" style="margin:0px;padding:0px;font-family:"Times New Roman";max-width:348px"><tbody style="margin:0px;padding:0px"><tr style="margin:0px;padding:0px"><td style="padding:0px"><table border="0" cellspacing="0" cellpadding="0" style="margin:0px;padding:20px 0px 0px"><tbody style="margin:0px;padding:0px"><tr style="margin:0px;padding:0px"><td valign="top" style="padding:0px 20px 0px 0px;vertical-align:top;border-right:1px solid rgb(213,213,213)"><img src="https://i.imgur.com/eGpkLls.png" width="200" height="64"><br></td><td style="padding:0px 0px 0px 20px"><table border="0" cellspacing="0" cellpadding="0" style="margin:0px;padding:0px"><tbody style="margin:0px;padding:0px"><tr style="margin:0px;padding:0px"><td colspan="2" style="font-family:Arial,Helvetica,Verdana,sans-serif;padding:1px 0px 5px;font-size:13px;line-height:13px;color:rgb(56,58,53);font-weight:700">Yi-yo Chiang</td></tr><tr style="margin:0px;padding:0px"><td colspan="2" style="font-family:Arial,Helvetica,Verdana,sans-serif;padding:0px 0px 5px;font-size:11px;line-height:13px;color:rgb(56,58,53)">Software Engineer</td></tr><tr style="margin:0px;padding:0px"><td colspan="2" style="font-family:Arial,Helvetica,Verdana,sans-serif;padding:0px 0px 5px;font-size:11px;line-height:13px;color:rgb(56,58,53)"><a href="mailto:yochiang@google.com" target="_blank">yochiang@google.com</a></td></tr><tr style="margin:0px;padding:0px"><td colspan="2" style="font-family:Arial,Helvetica,Verdana,sans-serif;padding:0px 0px 3px;font-size:11px;line-height:13px;color:rgb(3,112,248)"></td></tr></tbody></table></td></tr></tbody></table></td></tr></tbody></table></div></div>