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