--- tar.c | 203 ++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 113 insertions(+), 90 deletions(-) diff --git a/tar.c b/tar.c index 6a9ab11..b28d419 100644 --- a/tar.c +++ b/tar.c _AT_@ -19,7 +19,7 @@ #include "fs.h" #include "util.h" -#define BLKSIZ 512 +#define BLKSIZ (sizeof (struct header)) enum Type { REG = '0', _AT_@ -50,6 +50,7 @@ struct header { char major[8]; char minor[8]; char prefix[155]; + char padding[12]; }; static struct dirtime { _AT_@ -169,6 +170,17 @@ ewrite(int fd, const void *buf, size_t n) return r; } +static long +chksum(struct header *h) +{ + long s2, i; + + memset(h->chksum, ' ', sizeof(h->chksum)); + for (i = 0, s2 = 0; i < sizeof(*h); i++) + s2 += *((unsigned char *)h + i); + return s2; +} + static void putoctal(char *dst, unsigned num, int size) { _AT_@ -179,14 +191,16 @@ putoctal(char *dst, unsigned num, int size) static int archive(const char *path) { - char b[BLKSIZ]; - const char *base, *p; - struct group *gr; - struct header *h; + static const struct header blank = { + "././_AT_LongLink", "0000000", "0000000", "0000000", "00000000000", + "00000000000", " ", AREG, "", "ustar", "00", + }; + char b[BLKSIZ + BLKSIZ], *p; + struct header *h = (struct header *)b; + struct group *gr; struct passwd *pw; struct stat st; - size_t chksum, i, nlen, plen; - ssize_t l, r; + ssize_t l, n, r; int fd = -1; if (lstat(path, &st) < 0) { _AT_@ -196,47 +210,37 @@ archive(const char *path) weprintf("ignoring %s\n", path); return 0; } - pw = getpwuid(st.st_uid); gr = getgrgid(st.st_gid); - h = (struct header *)b; - memset(b, 0, sizeof(b)); - - plen = 0; - base = path; - if ((nlen = strlen(base)) >= sizeof(h->name)) { - /* - * Cover case where path name is too long (in which case we - * need to split it to prefix and name). - */ - if ((base = strrchr(path, '/')) == NULL) - goto too_long; - for (p = base++; p > path && *p == '/'; --p) - ; - - nlen -= base - path; - plen = p - path + 1; - if (nlen >= sizeof(h->name) || plen >= sizeof(h->prefix)) - goto too_long; + *h = blank; + n = strlcpy(h->name, path, sizeof(h->name)); + if (n >= sizeof(h->name)) { + *++h = blank; + h->type = 'L'; + putoctal(h->size, n, sizeof(h->size)); + putoctal(h->chksum, chksum(h), sizeof(h->chksum)); + + ewrite(tarfd, (char *)h, BLKSIZ); + for (p = (char *)path; n > 0; n -= BLKSIZ, p += BLKSIZ) { + if (n < BLKSIZ) { + p = memcpy(h--, p, n); + memset(p + n, 0, BLKSIZ - n); + } + ewrite(tarfd, p, BLKSIZ); + } } - memcpy(h->name, base, nlen); - memcpy(h->prefix, path, plen); - putoctal(h->mode, (unsigned)st.st_mode & 0777, sizeof(h->mode)); putoctal(h->uid, (unsigned)st.st_uid, sizeof(h->uid)); putoctal(h->gid, (unsigned)st.st_gid, sizeof(h->gid)); - putoctal(h->size, 0, sizeof(h->size)); putoctal(h->mtime, (unsigned)st.st_mtime, sizeof(h->mtime)); - memcpy( h->magic, "ustar", sizeof(h->magic)); - memcpy( h->version, "00", sizeof(h->version)); estrlcpy(h->uname, pw ? pw->pw_name : "", sizeof(h->uname)); estrlcpy(h->gname, gr ? gr->gr_name : "", sizeof(h->gname)); if (S_ISREG(st.st_mode)) { h->type = REG; - putoctal(h->size, (unsigned)st.st_size, sizeof(h->size)); + putoctal(h->size, st.st_size, sizeof(h->size)); fd = open(path, O_RDONLY); if (fd < 0) eprintf("open %s:", path); _AT_@ -255,10 +259,7 @@ archive(const char *path) h->type = FIFO; } - memset(h->chksum, ' ', sizeof(h->chksum)); - for (i = 0, chksum = 0; i < sizeof(*h); i++) - chksum += (unsigned char)b[i]; - putoctal(h->chksum, chksum, sizeof(h->chksum)); + putoctal(h->chksum, chksum(h), sizeof(h->chksum)); ewrite(tarfd, b, BLKSIZ); if (fd != -1) { _AT_@ -271,24 +272,21 @@ archive(const char *path) } return 0; - -too_long: - eprintf("filename too long: %s\n", path); } static int unarchive(char *fname, ssize_t l, char b[BLKSIZ]) { - char lname[101], *tmp, *p; - long mode, major, minor, type, mtime, uid, gid; struct header *h = (struct header *)b; - int fd = -1; struct timespec times[2]; + char lname[101], *tmp, *p; + long mode, major, minor, type, mtime, uid, gid; + int fd = -1, lnk = h->type == HARDLINK; if (!mflag && ((mtime = strtol(h->mtime, &p, 8)) < 0 || *p != '\0')) eprintf("strtol %s: invalid number\n", h->mtime); - if (remove(fname) < 0 && errno != ENOENT) - weprintf("remove %s:", fname); + if (strcmp(fname, ".") && strcmp(fname, "./") && remove(fname) < 0) + if (errno != ENOENT) weprintf("remove %s:", fname); tmp = estrdup(fname); mkdirp(dirname(tmp), 0777, 0777); _AT_@ -308,10 +306,9 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) case SYMLINK: snprintf(lname, sizeof(lname), "%.*s", (int)sizeof(h->linkname), h->linkname); - if (((h->type == HARDLINK) ? link : symlink)(lname, fname) < 0) - eprintf("%s %s -> %s:", - (h->type == HARDLINK) ? "link" : "symlink", - fname, lname); + if ((lnk ? link:symlink)(lname, fname) < 0) + eprintf("%s %s -> %s:", lnk ? "link" : "symlink", fname, lname); + lnk++; break; case DIRECTORY: if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0') _AT_@ -354,14 +351,14 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) close(fd); } - if (h->type == HARDLINK) + if (lnk > 1) return 0; 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:", fname); - if (h->type == SYMLINK) { + if (lnk) { if (!getuid() && lchown(fname, uid, gid)) weprintf("lchown %s:", fname); } else { _AT_@ -435,9 +432,9 @@ sanitize(struct header *h) static void chktar(struct header *h) { - char tmp[8], *err, *p = (char *)h; const char *reason; - long s1, s2, i; + char tmp[8], *err; + long s1, i; if (h->prefix[0] == '\0' && h->name[0] == '\0') { reason = "empty filename"; _AT_@ -457,10 +454,7 @@ chktar(struct header *h) reason = "invalid checksum"; goto bad; } - memset(h->chksum, ' ', sizeof(h->chksum)); - for (i = 0, s2 = 0; i < sizeof(*h); i++) - s2 += (unsigned char)p[i]; - if (s1 != s2) { + if (s1 != chksum(h)) { reason = "incorrect checksum"; goto bad; } _AT_@ -473,45 +467,68 @@ bad: static void xt(int argc, char *argv[], int mode) { - char b[BLKSIZ], fname[256 + 1], *p; + long size, n, l; + char b[BLKSIZ], fname[l = PATH_MAX], *p, *q = NULL; + int i; + int (*fn)(char *, ssize_t, char[BLKSIZ]) = (mode == 'x') ? unarchive : print; struct timespec times[2]; struct header *h = (struct header *)b; struct dirtime *dirtime; - long size; - int i, n; - int (*fn)(char *, ssize_t, char[BLKSIZ]) = (mode == 'x') ? unarchive : print; - while (eread(tarfd, b, BLKSIZ) > 0 && (h->name[0] || h->prefix[0])) { + while (eread(tarfd, b, BLKSIZ) > 0 && h->name[0]) { chktar(h); - sanitize(h), n = 0; - - /* small dance around non-null terminated fields */ - if (h->prefix[0]) - n = snprintf(fname, sizeof(fname), "%.*s/", - (int)sizeof(h->prefix), h->prefix); - snprintf(fname + n, sizeof(fname) - n, "%.*s", - (int)sizeof(h->name), h->name); + sanitize(h); if ((size = strtol(h->size, &p, 8)) < 0 || *p != '\0') - eprintf("strtol %s: invalid number\n", h->size); + eprintf("strtol %s: invalid size\n", h->size); - if (argc) { - /* only extract the given files */ - for (i = 0; i < argc; i++) - if (!strcmp(argv[i], fname)) + /* long file path is read direcly into fname*/ + if (h->type == 'L' || h->type == 'x' || h->type == 'g') { + + /* read header only up to size of fname buffer */ + for (q = fname; q < fname+size; q += BLKSIZ) { + eread(tarfd, q, BLKSIZ); + if (q + BLKSIZ > fname + l) + eprintf("name exceeds buffer: %s\n", l, fname); + } + + /* convert pax x header with 'path=' field into L header */ + if (h->type == 'x') for (q = fname; q < fname+size-16; q += n) { + if ((n = strtol(q, &p, 10)) < 0 || *p != ' ') + eprintf("strtol %.*s: invalid number\n", p+1-q, q); + if (n && strncmp(p+1, "path=", 5) == 0) { + memmove(fname, p+6, size = q+n - p-6 - 1); + h->type = 'L'; break; + } + } + fname[size] = '\0'; + + /* non L-like header (eg. pax 'g') is skipped by setting q=null */ + if (h->type != 'L') q = NULL; + continue; + } + + /* ustar path is copied into fname if no L header (ie: q is NULL) */ + if (q) q = NULL; else { + n = !h->prefix[0] ? 0 : + snprintf(fname, l, "%.*s/", (int)sizeof h->prefix, h->prefix); + snprintf(fname + n, l - n, "%.*s", (int)sizeof h->name, h->name); + } + + /* if argc > 0 then only extract the given files */ + if (argc) { + for (i = 0; i < argc; i++) { + n = strlen(argv[i]); + if (!strncmp(argv[i], fname, n)) + if (strchr("/", fname[n]) || argv[i][n-1] == '/') break; + } if (i == argc) { skipblk(size); continue; } } - /* ignore global pax header craziness */ - if (h->type == 'g' || h->type == 'x') { - skipblk(size); - continue; - } - fn(fname, size, b); if (vflag && mode != 't') puts(fname); _AT_@ -530,12 +547,15 @@ xt(int argc, char *argv[], int mode) } } +char **args; +int argn; + static void usage(void) { - eprintf("usage: %s [-C dir] [-J | -Z | -a | -j | -z] -x [-m | -t] " + eprintf("usage: %s [x | t | -x | -t] [-C dir] [-J | -Z | -a | -j | -z] [-m] [-p] " "[-f file] [file ...]\n" - " %s [-C dir] [-J | -Z | -a | -j | -z] [-h] -c path ... " + " %s [c | -c] [-C dir] [-J | -Z | -a | -j | -z] [-h] path ... " "[-f file]\n", argv0, argv0); } _AT_@ -547,6 +567,10 @@ main(int argc, char *argv[]) char *file = NULL, *dir = ".", mode = '\0'; int fd; + argv0 = argv[0]; + if (argc > 1 && strchr("cxt", mode = *argv[1])) + *(argv[1]+1) ? *argv[1] = '-' : (*++argv = argv0, --argc); + ARGBEGIN { case 'x': case 'c': _AT_@ -576,18 +600,15 @@ main(int argc, char *argv[]) case 'v': vflag = 1; break; + case 'p': + break; /* Do nothing as already default behaviour */ default: usage(); } ARGEND - if (!mode) - usage(); - if (mode == 'c') - if (!argc) - usage(); - switch (mode) { case 'c': + if (!argc) usage(); tarfd = 1; if (file && *file != '-') { tarfd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0644); _AT_@ -626,6 +647,8 @@ main(int argc, char *argv[]) eprintf("chdir %s:", dir); xt(argc, argv, mode); break; + default: + usage(); } return recurse_status; -- 2.49.0Received on Tue Apr 29 2025 - 14:20:12 CEST
This archive was generated by hypermail 2.3.0 : Tue Apr 29 2025 - 20:12:39 CEST