[hackers] [sbase] [PATCH 3/3] tar: GNU long filename extension support

From: Thomas Oltmann <thomas.oltmann.hhg_AT_gmail.com>
Date: Thu, 26 Nov 2020 14:53:46 +0100

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
_AT_@ -30,7 +30,8 @@ enum Type {
 	BLOCKDEV  = '4',
 	DIRECTORY = '5',
 	FIFO      = '6',
-	RESERVED  = '7'
+	RESERVED  = '7',
+	LONGNAME  = 'L',
 };
 
 struct header {
_AT_@ -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);
_AT_@ -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 */
_AT_@ -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
Received on Thu Nov 26 2020 - 14:53:46 CET

This archive was generated by hypermail 2.3.0 : Thu Nov 26 2020 - 15:00:37 CET