diff options
Diffstat (limited to 'toys/posix')
-rw-r--r-- | toys/posix/cpio.c | 37 |
1 files changed, 23 insertions, 14 deletions
diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c index 0316cc9d..712980cc 100644 --- a/toys/posix/cpio.c +++ b/toys/posix/cpio.c @@ -109,7 +109,7 @@ void cpio_main(void) if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) { char *name, *tofree, *data; - unsigned size, mode, uid, gid, timestamp; + unsigned size, mode, uid, gid, timestamp, ala = 0; int test = toys.optflags & FLAG_t, err = 0; // Read header and name. @@ -132,6 +132,10 @@ void cpio_main(void) if (toys.optflags & (FLAG_t|FLAG_v)) puts(name); + // If we need to reopen dir or mknod for write later, force u+w now + if (!(toys.optflags & FLAG_no_preserve_owner) + && !S_ISREG(mode) && !S_ISLNK(mode) && !geteuid()) ala = 0200; + if (!test && strrchr(name, '/') && mkpathat(AT_FDCWD, name, 0, 2)) { perror_msg("mkpath '%s'", name); test++; @@ -141,7 +145,7 @@ void cpio_main(void) // properly aligned with next file. if (S_ISDIR(mode)) { - if (!test) err = mkdir(name, mode); + if (!test) err = mkdir(name, ala|mode); } else if (S_ISLNK(mode)) { data = strpad(afd, size, 0); if (!test) err = symlink(data, name); @@ -180,24 +184,29 @@ void cpio_main(void) close(fd); } } else if (!test) - err = mknod(name, mode, makedev(x8u(toybuf+78), x8u(toybuf+86))); + err = mknod(name, ala|mode, makedev(x8u(toybuf+78), x8u(toybuf+86))); // Set ownership and timestamp. if (!test && !err) { - // Creading dir/dev doesn't give us a filehandle, we have to refer to it - // by name to chown/utime, but how do we know it's the same item? - // Check that we at least have the right type of entity open, and do - // NOT restore dropped suid bit in this case. - if (!S_ISREG(mode) && !S_ISLNK(mode) && !geteuid() - && !(toys.optflags & FLAG_no_preserve_owner)) - { - int fd = open(name, O_RDONLY|O_NOFOLLOW); + // Creating dir/dev doesn't give us a filehandle, we have to refer to it + // by name to chown/utime, but how do we know it's the same item? Use + // filehandle to check that we at least have the right type of entity + // open. If we forced it writeable, chmod it back, but do _not_ restore + // dropped suid/sgid bit. + if (ala) { + int fd = open(name, O_WRONLY|O_NOFOLLOW); struct stat st; - if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT)) - err = fchown(fd, uid, gid); - else err = 1; + // If we forced it writeable, change it back, but do _not_ restore + // dropped suid/sgid bit. + + if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT)==(mode&S_IFMT)) { + if (!(err = fchown(fd, uid, gid))) + if ((ala|mode)!=mode) err = fchmod(fd, mode&~(S_ISUID|S_ISGID)); + } else err = 1; + // If we forced it writeable, change it back, but do _not_ restore + // dropped suid/sgid bit. close(fd); } |