*ping* (in case this got categorized as spam by gmail) On Sun, Feb 14, 2021 at 9:13 PM Yi-Yo Chiang <yochi...@google.com> wrote:
> If -u is specified, then replace any existing files or directories. > > If -u is not specified and the path to be inflated already exist, then > report EEXIST error. This behaves slightly different from GNU cpio, as > GNU cpio checks the timestamp and replaces when the timestamp of the > existing file is older than the one from the archive. This still > conforms to SUSv2 as it doesn't define how to behave when -u is not > specified. > > There is an exception, if we are creating an existing directory, then > don't report any error or try to rmdir() the directory as we can > "create" the directory by chmod() / chown() the existing one. > > --- > tests/cpio.test | 16 ++++++++++++++++ > toys/posix/cpio.c | 46 ++++++++++++++++++++++++++++++++++++---------- > 2 files changed, 52 insertions(+), 10 deletions(-) > > diff --git a/tests/cpio.test b/tests/cpio.test > index 6ab3665a..7e2955a1 100755 > --- a/tests/cpio.test > +++ b/tests/cpio.test > @@ -42,4 +42,20 @@ touch a; chmod a-rwx a; ln -s a/cant b > toyonly testing "archives unreadable empty files" "cpio -o -H newc|cpio > -it" "b\na\n" "" "b\na\n" > chmod u+rw a; rm -f a b > > +mkdir a > +echo "old" >a/b > +echo "a/b" | cpio -o -H newc >a.cpio > +rm -rf a > +testing "-i doesn't create leading directories" "cpio -i <a.cpio > 2>/dev/null; [ -e a ] || echo yes" "yes\n" "" "" > +rm -rf a > +testing "-id creates leading directories" "cpio -id <a.cpio && cat a/b" > "old\n" "" "" > +rm -rf a a.cpio > > +mkdir a > +echo "old" >a/b > +find a | cpio -o -H newc >a.cpio > +testing "-i keeps existing files" "echo new >a/b && cpio -i <a.cpio > 2>/dev/null; cat a/b" "new\n" "" "" > +testing "-id keeps existing files" "echo new >a/b && cpio -id <a.cpio > 2>/dev/null; cat a/b" "new\n" "" "" > +testing "-iu replaces existing files; no error" "echo new >a/b && cpio > -iu <a.cpio && cat a/b" "old\n" "" "" > +testing "-idu replaces existing files; no error" "echo new >a/b && cpio > -idu <a.cpio && cat a/b" "old\n" "" "" > +rm -rf a a.cpio > diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c > index 31c777c9..795f890c 100644 > --- a/toys/posix/cpio.c > +++ b/toys/posix/cpio.c > @@ -22,7 +22,7 @@ config CPIO > default y > help > usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] > [--no-preserve-owner] > - [ignored: -mdu -H newc] > + [ignored: -m -H newc] > > Copy files into and out of a "newc" format cpio archive. > > @@ -32,6 +32,7 @@ config CPIO > -o Create archive (stdin=list of files, stdout=archive) > -t Test files (list only, stdin=archive, stdout=list of files) > -d Create directories if needed > + -u Copy unconditionally > -v Verbose > --no-preserve-owner (don't set ownership during extract) > */ > @@ -113,7 +114,8 @@ void cpio_main(void) > if (FLAG(i) || FLAG(t)) for (;;) { > char *name, *tofree, *data; > unsigned size, mode, uid, gid, timestamp; > - int test = FLAG(t), err = 0; > + int test = FLAG(t), err = 0, exist = 0; > + struct stat st; > > // Read header and name. > if (!(size =readall(afd, toybuf, 110))) break; > @@ -137,25 +139,50 @@ void cpio_main(void) > // (This output is unaffected by --quiet.) > if (FLAG(t) || FLAG(v)) puts(name); > > - if (!test && FLAG(d) && strrchr(name, '/') && mkpath(name)) { > + if (!test) exist = !lstat(name, &st); > + > + // Create leading directories if |name| doesn't exist. > + if (!test && !exist && FLAG(d) && strrchr(name, '/') && mkpath(name)) > { > perror_msg("mkpath '%s'", name); > test++; > } > > + // Don't report error or try to rmdir(name) if we want to mkdir(name) > later. > + if (!test && exist && !(S_ISDIR(st.st_mode) && S_ISDIR(mode))) { > + if (!FLAG(u)) { > + errno = EEXIST; > + perror_msg_raw(name); > + test++; > + } else if (S_ISDIR(st.st_mode) ? rmdir(name) : unlink(name)) { > + perror_msg("remove '%s'", name); > + test++; > + } else { > + exist = 0; > + } > + } > + > // Consume entire record even if it couldn't create file, so we're > // properly aligned with next file. > > if (S_ISDIR(mode)) { > - if (!test) err = mkdir(name, mode); > + if (!test) { > + // If |name| already exist as a directory, then just do a chmod() > to fix > + // up the directory permissions. > + if (!exist) err = mkdir(name, mode); > + else if (S_ISDIR(st.st_mode)) err = chmod(name, mode); > + else err = errno = EEXIST; > + } > } else if (S_ISLNK(mode)) { > data = strpad(afd, size, 0); > - if (!test) err = symlink(data, name); > + if (!test) { > + err = symlink(data, name); > + // Can't get a filehandle to a symlink, so do special chown > + if (!err && !geteuid() && !FLAG(no_preserve_owner)) > + err = lchown(name, uid, gid); > + } > free(data); > - // Can't get a filehandle to a symlink, so do special chown > - if (!err && !geteuid() && !FLAG(no_preserve_owner)) > - err = lchown(name, uid, gid); > } else if (S_ISREG(mode)) { > - int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, > mode); > + int fd = test ? 0 : open(name, O_CREAT|O_EXCL|O_WRONLY|O_NOFOLLOW, > mode); > > // If write fails, we still need to read/discard data to continue > with > // archive. Since doing so overwrites errno, report error now > @@ -197,7 +224,6 @@ void cpio_main(void) > && !FLAG(no_preserve_owner)) > { > int fd = open(name, O_RDONLY|O_NOFOLLOW); > - struct stat st; > > if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == > (mode&S_IFMT)) > err = fchown(fd, uid, gid); > -- > 2.30.0.478.g8a0d178c01-goog > > -- Yi-yo Chiang Software Engineer yochi...@google.com
_______________________________________________ Toybox mailing list Toybox@lists.landley.net http://lists.landley.net/listinfo.cgi/toybox-landley.net