sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

commit 211c565b3d560ad16ef20359d6efe37d0783c583
parent 85a9254d3a21a8ec0468a0c8f1985649866fde75
Author: Brad Barden <brad@13os.net>
Date:   Wed, 18 Nov 2015 20:50:50 -0600

tar: improve file status handling on extract

by re-ordering when chmod/chown is done, only a list of directories (not
all files) need be kept for fixing mtime.

this also fixes an issue where set-user-id files in a tar may not work. chmod
is done before chown and before the file is written. if ownership changes, or
the file is being written as a normal user, the setuid bit would be cleared.

also fixes ownership of symbolic links. previously a chown() was called,
which would change the ownership of the link target. lchown() is now
used for symbolic links.

renamed all ent, ent* functions to dir* as it better describes what they
do.

use timespec/utimensat instead of timeval/utimes to get AT_SYMLINK_NOFOLLOW

Diffstat:
Mtar.c | 69++++++++++++++++++++++++++++++++++++++++-----------------------------
1 file changed, 40 insertions(+), 29 deletions(-)

diff --git a/tar.c b/tar.c @@ -48,12 +48,12 @@ struct header { char prefix[155]; }; -static struct ent { +static struct dirtime { char *name; time_t mtime; -} *ents; +} *dirtimes; -static size_t entslen; +static size_t dirtimeslen; static int tarfd; static ino_t tarinode; @@ -72,20 +72,20 @@ static const char *filtertools[] = { }; static void -pushent(char *name, time_t mtime) +pushdirtime(char *name, time_t mtime) { - ents = reallocarray(ents, entslen + 1, sizeof(*ents)); - ents[entslen].name = strdup(name); - ents[entslen].mtime = mtime; - entslen++; + dirtimes = reallocarray(dirtimes, dirtimeslen + 1, sizeof(*dirtimes)); + dirtimes[dirtimeslen].name = strdup(name); + dirtimes[dirtimeslen].mtime = mtime; + dirtimeslen++; } -static struct ent * -popent(void) +static struct dirtime * +popdirtime(void) { - if (entslen) { - entslen--; - return &ents[entslen]; + if (dirtimeslen) { + dirtimeslen--; + return &dirtimes[dirtimeslen]; } return NULL; } @@ -254,6 +254,7 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) long mode, major, minor, type, mtime, uid, gid; struct header *h = (struct header *)b; int fd = -1; + struct timespec times[2]; if (!mflag && ((mtime = strtol(h->mtime, &p, 8)) < 0 || *p != '\0')) eprintf("strtol %s: invalid number\n", h->mtime); @@ -273,8 +274,6 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0600); if (fd < 0) eprintf("open %s:", fname); - if (fchmod(fd, mode) < 0) - eprintf("fchmod %s:", fname); break; case HARDLINK: case SYMLINK: @@ -290,6 +289,7 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) eprintf("strtol %s: invalid number\n", h->mode); if (mkdir(fname, (mode_t)mode) < 0 && errno != EEXIST) eprintf("mkdir %s:", fname); + pushdirtime(fname, mtime); break; case CHARDEV: case BLOCKDEV: @@ -317,8 +317,6 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) eprintf("strtol %s: invalid number\n", h->uid); if ((gid = strtol(h->gid, &p, 8)) < 0 || *p != '\0') eprintf("strtol %s: invalid number\n", h->gid); - if (!getuid() && chown(fname, uid, gid)) - weprintf("chown %s:", fname); if (fd != -1) { for (; l > 0; l -= BLKSIZ) @@ -327,7 +325,20 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) close(fd); } - pushent(fname, mtime); + times[0].tv_sec = times[1].tv_sec = mtime; + times[0].tv_nsec = times[1].tv_nsec = 0; + if (!mflag && utimensat(AT_FDCWD, fname, times, AT_SYMLINK_NOFOLLOW) < 0) + weprintf("utimensat %s:\n", fname); + if (h->type == SYMLINK) { + if (!getuid() && lchown(fname, uid, gid)) + weprintf("lchown %s:\n", fname); + } else { + if (!getuid() && chown(fname, uid, gid)) + weprintf("chown %s:\n", fname); + if (chmod(fname, mode) < 0) + eprintf("fchmod %s:\n", fname); + } + return 0; } @@ -420,9 +431,9 @@ static void xt(int argc, char *argv[], int mode) { char b[BLKSIZ], fname[256 + 1], *p; - struct timeval times[2]; + struct timespec times[2]; struct header *h = (struct header *)b; - struct ent *ent; + struct dirtime *dirtime; long size; int i, n; int (*fn)(char *, ssize_t, char[BLKSIZ]) = (mode == 'x') ? unarchive : print; @@ -463,16 +474,16 @@ xt(int argc, char *argv[], int mode) puts(fname); } - if (!mflag) { - while ((ent = popent())) { - times[0].tv_sec = times[1].tv_sec = ent->mtime; - times[0].tv_usec = times[1].tv_usec = 0; - if (utimes(ent->name, times) < 0) - weprintf("utimes %s:", ent->name); - free(ent->name); + if (mode == 'x' && !mflag) { + while ((dirtime = popdirtime())) { + times[0].tv_sec = times[1].tv_sec = dirtime->mtime; + times[0].tv_nsec = times[1].tv_nsec = 0; + if (utimensat(AT_FDCWD, dirtime->name, times, 0) < 0) + eprintf("utimensat %s:", fname); + free(dirtime->name); } - free(ents); - ents = NULL; + free(dirtimes); + dirtimes = NULL; } }