Rationale: This extension is used in many tar archives found on the web.
Previously, all of those archives would get rejected by sbase tar.
Please note that this patch is somewhat preliminary:
The GNU long filename extension is very ad-hoc, and doesn't seem to
be fully documented anywhere. Therefore, it is possible that this
patch doesn't always behave exactly like GNU tar etc. would.
This patch only implements extracting and listing of archives with the
extension.
---
tar.c | 44 ++++++++++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 10 deletions(-)
diff --git a/tar.c b/tar.c
index 934b9c6..ee75e6c 100644
--- a/tar.c
+++ b/tar.c
@@ -30,7 +30,8 @@ enum Type {
BLOCKDEV = '4',
DIRECTORY = '5',
FIFO = '6',
- RESERVED = '7'
+ RESERVED = '7',
+ LONGNAME = 'L',
};
struct header {
@@ -449,15 +450,15 @@ xt(int argc, char *argv[], int mode)
struct timespec times[2];
struct header *h = (struct header *)b;
struct dirtime *dirtime;
- long size;
- int i, n;
+ long n, size;
+ int i;
int (*fn)(char *, ssize_t, char[BLKSIZ]) = (mode == 'x') ? unarchive :
print;
long fnmax = 256 + 1;
char *fname = ecalloc(1, fnmax);
while (eread(tarfd, b, BLKSIZ) > 0 && h->name[0]) {
chktar(h);
- sanitize(h), n = 0;
+ sanitize(h);
if ((size = strtol(h->size, &p, 8)) < 0 || *p != '\0')
eprintf("strtol %s: invalid number\n", h->size);
@@ -468,14 +469,35 @@ xt(int argc, char *argv[], int mode)
case 'x':
skipblk(size);
continue;
+
+ /* GNU tar long filename */
+ case LONGNAME:
+ if (size > fnmax) {
+ fnmax = size;
+ free(fname);
+ fname = ecalloc(1, fnmax);
+ }
+ for (n = 0; n < size; n += BLKSIZ) {
+ if (!(eread(tarfd, b, BLKSIZ) > 0))
+ eprintf("corrupt long filename\n");
+ memcpy(fname + n, b, MIN(size - n, BLKSIZ));
+ }
+ /* the filename must already be NUL-terminated in the
archive */
+ if (!size || fname[size-1])
+ eprintf("corrupt long filename\n");
+ continue;
}
- /* small dance around non-null terminated fields */
- if (h->prefix[0])
- n = snprintf(fname, fnmax, "%.*s/",
- (int)sizeof(h->prefix), h->prefix);
- snprintf(fname + n, fnmax - n, "%.*s",
- (int)sizeof(h->name), h->name);
+ /* only read filename if we don't already have a long name
queued up */
+ if (!fname[0]) {
+ /* small dance around non-null terminated fields */
+ n = 0;
+ if (h->prefix[0])
+ n = snprintf(fname, fnmax, "%.*s/",
+ (int)sizeof(h->prefix), h->prefix);
+ snprintf(fname + n, fnmax - n, "%.*s",
+ (int)sizeof(h->name), h->name);
+ }
if (argc) {
/* only extract the given files */
@@ -491,6 +513,8 @@ xt(int argc, char *argv[], int mode)
fn(fname, size, b);
if (vflag && mode != 't')
puts(fname);
+
+ fname[0] = 0;
}
free(fname);
--
2.28.0