| tar: man page update, and more robust & descriptive errors - sbase - suckless u… | |
| git clone git://git.suckless.org/sbase | |
| Log | |
| Files | |
| Refs | |
| README | |
| LICENSE | |
| --- | |
| commit 6b802ab9fd967e2478a29a583521a13fb91aef57 | |
| parent 5aa63dbe055d7a655ea14626abdf051ff29e057a | |
| Author: Xan Phung <[email protected]> | |
| Date: Sun, 1 Jun 2025 22:09:11 +1000 | |
| tar: man page update, and more robust & descriptive errors | |
| 1. Manual page has been updated to reflect previous change to tar. | |
| 2. Error messages are more detailed & secured against unterminated | |
| header strings. Previously, sanitize() assumed numeric fields were | |
| terminated by space or null, but did not check termination is | |
| actually present. | |
| 3. Default mode of the blank header in archive() is now 0600. For | |
| all file headers, this is immediately set to the file's mode. Only | |
| L-headers keep the 0600 value (which are not user visible). GNU | |
| tar uses 0644, but 0600 was chosen for symmetry with sbase | |
| unarchive(), which also uses 0600 as the initial mode. | |
| Diffstat: | |
| M tar.1 | 14 ++++++++++---- | |
| M tar.c | 42 +++++++++++++++++------------… | |
| 2 files changed, 33 insertions(+), 23 deletions(-) | |
| --- | |
| diff --git a/tar.1 b/tar.1 | |
| @@ -6,16 +6,18 @@ | |
| .Nd create, list or extract a tape archive | |
| .Sh SYNOPSIS | |
| .Nm | |
| +.Cm x | Cm t | Fl x | Fl t | |
| .Op Fl C Ar dir | |
| .Op Fl J | Fl Z | Fl a | Fl j | Fl z | |
| -.Fl x Op Fl m | Fl t | |
| +.Op Fl m | |
| +.Op Fl p | |
| .Op Fl f Ar file | |
| .Op Ar file ... | |
| .Nm | |
| -.Op Fl C Ar dir | |
| +.Cm c | Fl c Op Fl C Ar dir | |
| .Op Fl J | Fl Z | Fl a | Fl j | Fl z | |
| .Op Fl h | |
| -.Fl c Ar path ... | |
| +.Ar path ... | |
| .Op Fl f Ar file | |
| .Sh DESCRIPTION | |
| .Nm | |
| @@ -66,4 +68,8 @@ The | |
| utility is compliant with the UStar (Uniform Standard Tape ARchive) | |
| format defined in the | |
| .St -p1003.1-88 | |
| -specification. | |
| +specification. For long file paths (>99 bytes), the UStar, 'L' and 'x' | |
| +header formats are supported for reading (to a maximum size of PATH_MAX | |
| +or 255 bytes, depending on format), and the 'L' format is supported for | |
| +writing (with unlimited path size). Link targets are limited to the | |
| +UStar maximum of 100 bytes. | |
| diff --git a/tar.c b/tar.c | |
| @@ -186,15 +186,15 @@ static void | |
| putoctal(char *dst, unsigned num, int size) | |
| { | |
| if (snprintf(dst, size, "%.*o", size - 1, num) >= size) | |
| - eprintf("snprintf: input number too large\n"); | |
| + eprintf("putoctal: input number '%o' too large\n", num); | |
| } | |
| static int | |
| archive(const char *path) | |
| { | |
| static const struct header blank = { | |
| - "././@LongLink", "0000000" , "0000000", "0000000", "0000000000… | |
| - "00000000000" , " ", AREG , "" , "ustar", "0… | |
| + "././@LongLink", "0000600", "0000000", "0000000", "00000000000… | |
| + "00000000000" , " ", AREG , "" , "ustar", "00… | |
| }; | |
| char b[BLKSIZ + BLKSIZ], *p; | |
| struct header *h = (struct header *)b; | |
| @@ -221,8 +221,8 @@ archive(const char *path) | |
| 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); | |
| @@ -285,7 +285,7 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) | |
| int fd = -1, lnk = h->type == SYMLINK; | |
| if (!mflag && ((mtime = strtol(h->mtime, &p, 8)) < 0 || *p != '\0')) | |
| - eprintf("strtol %s: invalid number\n", h->mtime); | |
| + eprintf("strtol %s: invalid mtime\n", h->mtime); | |
| if (strcmp(fname, ".") && strcmp(fname, "./") && remove(fname) < 0) | |
| if (errno != ENOENT) weprintf("remove %s:", fname); | |
| @@ -298,7 +298,7 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) | |
| case AREG: | |
| case RESERVED: | |
| if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->mode); | |
| + eprintf("strtol %s: invalid mode\n", h->mode); | |
| fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0600); | |
| if (fd < 0) | |
| eprintf("open %s:", fname); | |
| @@ -313,7 +313,7 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) | |
| break; | |
| case DIRECTORY: | |
| if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->mode); | |
| + eprintf("strtol %s: invalid mode\n", h->mode); | |
| if (mkdir(fname, (mode_t)mode) < 0 && errno != EEXIST) | |
| eprintf("mkdir %s:", fname); | |
| pushdirtime(fname, mtime); | |
| @@ -321,18 +321,18 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) | |
| case CHARDEV: | |
| case BLOCKDEV: | |
| if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->mode); | |
| + eprintf("strtol %s: invalid mode\n", h->mode); | |
| if ((major = strtol(h->major, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->major); | |
| + eprintf("strtol %s: invalid major device\n", h->major); | |
| if ((minor = strtol(h->minor, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->minor); | |
| + eprintf("strtol %s: invalid minor device\n", h->minor); | |
| type = (h->type == CHARDEV) ? S_IFCHR : S_IFBLK; | |
| if (mknod(fname, type | mode, makedev(major, minor)) < 0) | |
| eprintf("mknod %s:", fname); | |
| break; | |
| case FIFO: | |
| if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->mode); | |
| + eprintf("strtol %s: invalid mode\n", h->mode); | |
| if (mknod(fname, S_IFIFO | mode, 0) < 0) | |
| eprintf("mknod %s:", fname); | |
| break; | |
| @@ -341,9 +341,9 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ]) | |
| } | |
| if ((uid = strtol(h->uid, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->uid); | |
| + eprintf("strtol %s: invalid uid\n", h->uid); | |
| if ((gid = strtol(h->gid, &p, 8)) < 0 || *p != '\0') | |
| - eprintf("strtol %s: invalid number\n", h->gid); | |
| + eprintf("strtol %s: invalid gid\n", h->gid); | |
| if (fd != -1) { | |
| for (; l > 0; l -= BLKSIZ) | |
| @@ -404,7 +404,7 @@ c(int dirfd, const char *name, struct stat *st, void *data,… | |
| static void | |
| sanitize(struct header *h) | |
| { | |
| - size_t i, j; | |
| + size_t i, j, l; | |
| struct { | |
| char *f; | |
| size_t l; | |
| @@ -423,10 +423,14 @@ sanitize(struct header *h) | |
| * NULs as per the ustar specification. Patch all of them to | |
| * use NULs so we can perform string operations on them. */ | |
| for (i = 0; i < LEN(fields); i++){ | |
| - for (j = 0; j < fields[i].l && fields[i].f[j] == ' '; j++); | |
| - for (; j < fields[i].l; j++) | |
| + j = 0, l = fields[i].l - 1; | |
| + for (; j < l && fields[i].f[j] == ' '; j++); | |
| + for (; j <= l; j++) | |
| if (fields[i].f[j] == ' ') | |
| fields[i].f[j] = '\0'; | |
| + if (fields[i].f[l]) | |
| + eprintf("numeric field #%d (%.*s) is not null or space… | |
| + i, l+1, fields[i].f); | |
| } | |
| } | |
| @@ -434,7 +438,7 @@ static void | |
| chktar(struct header *h) | |
| { | |
| const char *reason; | |
| - char tmp[sizeof h->chksum], *err = ""; | |
| + char tmp[sizeof h->chksum], *err; | |
| long sum, i; | |
| if (h->prefix[0] == '\0' && h->name[0] == '\0') { | |
| @@ -482,13 +486,13 @@ xt(int argc, char *argv[], int mode) | |
| if ((size = strtol(h->size, &p, 8)) < 0 || *p != '\0') | |
| eprintf("strtol %s: invalid size\n", h->size); | |
| - /* Long file path is read direcly into fname*/ | |
| + /* Long file path is read directly 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) { | |
| if (q + BLKSIZ >= fname + l) | |
| - eprintf("name exceeds buffer: %s\n", f… | |
| + eprintf("name exceeds buffer: %.*s\n",… | |
| eread(tarfd, q, BLKSIZ); | |
| } | |