--- tar.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/tar.c b/tar.c index 53a737c..8cd1abe 100644 --- a/tar.c +++ b/tar.c _AT_@ -16,6 +16,8 @@ #include "util.h" #define BLKSIZ 512 +// COPY_CHUNK_SIZE must be a power of 2 +#define COPY_CHUNK_SIZE 8192 enum Type { REG = '0', _AT_@ -236,10 +238,13 @@ archive(const char *path) ewrite(tarfd, b, BLKSIZ); if (fd != -1) { - while ((l = eread(fd, b, BLKSIZ)) > 0) { - if (l < BLKSIZ) - memset(b + l, 0, BLKSIZ - l); - ewrite(tarfd, b, BLKSIZ); + char chunk[COPY_CHUNK_SIZE]; + while ((l = eread(fd, chunk, COPY_CHUNK_SIZE)) > 0) { + // Ceiling to BLKSIZ boundary + int ceilsize = (l + (BLKSIZ-1)) & ~(BLKSIZ-1); + if (l < ceilsize) + memset(chunk + l, 0, ceilsize - l); + ewrite(tarfd, chunk, ceilsize); } close(fd); } _AT_@ -250,7 +255,7 @@ archive(const char *path) static int unarchive(char *fname, ssize_t l, char b[BLKSIZ]) { - char lname[101], *tmp, *p; + char lname[101], *p; long mode, major, minor, type, mtime, uid, gid; struct header *h = (struct header *)b; int fd = -1; _AT_@ -261,9 +266,13 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) if (remove(fname) < 0 && errno != ENOENT) weprintf("remove %s:", fname); - tmp = estrdup(fname); - mkdirp(dirname(tmp), 0777, 0777); - free(tmp); + // tar files normally create the directory chain. This is a fallback + // for noncompliant tar files. + if (h->type != DIRECTORY) { + char* tmp = estrdup(fname); + mkdirp(dirname(tmp), 0777, 0777); + free(tmp); + } switch (h->type) { case REG: _AT_@ -319,9 +328,25 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) eprintf("strtol %s: invalid number\n", h->gid); if (fd != -1) { - for (; l > 0; l -= BLKSIZ) - if (eread(tarfd, b, BLKSIZ) > 0) - ewrite(fd, b, MIN(l, BLKSIZ)); + // Ceiling to BLKSIZ boundary + int readsize = (l + (BLKSIZ-1)) & ~(BLKSIZ-1); + char chunk[COPY_CHUNK_SIZE]; + int lastread = 0; + + for (; readsize > 0; l -= lastread, readsize -= lastread) { + int chunk_size = MIN(readsize, COPY_CHUNK_SIZE); + // Short reads are legal, so don't expect to read + // everything that was requested. + lastread = eread(tarfd, chunk, chunk_size); + if (lastread == 0) { + close(fd); + remove(fname); + eprintf("unexpected end of file reading %s.\n", + fname); + } + + ewrite(fd, chunk, MIN(l, lastread)); + } close(fd); } _AT_@ -331,7 +356,7 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) 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); + weprintf("utimensat %s %d:\n", fname, errno); if (h->type == SYMLINK) { if (!getuid() && lchown(fname, uid, gid)) weprintf("lchown %s:\n", fname); _AT_@ -349,10 +374,23 @@ static void skipblk(ssize_t l) { char b[BLKSIZ]; - - for (; l > 0; l -= BLKSIZ) - if (!eread(tarfd, b, BLKSIZ)) - break; + int lastread = 0; + // Ceiling to BLKSIZ boundary + int ceilsize = (l + (BLKSIZ-1)) & ~(BLKSIZ-1); + + off_t offset = lseek(tarfd, ceilsize, SEEK_CUR); + if (offset >= ceilsize) + return; + if (errno != ESPIPE) + eprintf("unexpected end of file.\n"); + + // This is a pipe, socket or FIFO. Fall back to a sequential read. + for (; ceilsize > 0; ceilsize -= lastread) { + int chunk_size = MIN(ceilsize, BLKSIZ); + lastread = eread(tarfd, b, chunk_size); + if (lastread == 0) + eprintf("unexpected end of file %d.\n", errno); + } } static int _AT_@ -370,7 +408,7 @@ c(const char *path, struct stat *st, void *data, struct recursor *r) if (vflag) puts(path); - if (S_ISDIR(st->st_mode)) + if (st && S_ISDIR(st->st_mode)) recurse(path, NULL, r); } -- 2.7.4Received on Sat Sep 09 2017 - 11:08:42 CEST
This archive was generated by hypermail 2.3.0 : Sat Sep 09 2017 - 11:12:24 CEST