code: plan9front

Download patch

ref: 041d732be72a13b9b7250fdab0ea4c46ad9d8326
parent: 76ee4c3988f4f4e89514c203d8b7781abd29f24a
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Tue May 19 08:39:45 EDT 2015

tar, tarfs: implement longname support

this allows extracting tar archives that use longnames extension,
where the real filename is stored in a special entry with
linkflag == 'L' before the file entry. also skip longlink entries
with linkflag == 'K'.

--- a/sys/src/cmd/tapefs/tarfs.c
+++ b/sys/src/cmd/tapefs/tarfs.c
@@ -14,6 +14,7 @@
 	Namsiz = 100,
 	Maxpfx = 155,		/* from POSIX */
 	Maxname = Namsiz + 1 + Maxpfx,
+	Maxlongname = 65535,
 	Binsize = 0x80,		/* flag in size[0], from gnu: positive binary size */
 	Binnegsz = 0xff,	/* flag in size[0]: negative binary size */
 };
@@ -30,7 +31,12 @@
 	LF_DIR =	'5',
 	LF_FIFO =	'6',
 	LF_CONTIG =	'7',
+
 	/* 'A' - 'Z' are reserved for custom implementations */
+
+	LF_LONGNAME =	'L',		/* GNU extension */
+	LF_LONGLINK =	'K',
+
 };
 
 typedef union {
@@ -106,7 +112,9 @@
 void
 populate(char *name)
 {
-	long chksum, linkflg;
+	char longname[Maxlongname+1];
+	char *nextname = nil;
+	long chksum, linkflg, namelen;
 	vlong blkno;
 	char *fname;
 	Fileinf f;
@@ -121,7 +129,9 @@
 		seek(tapefile, Tblock*blkno, 0);
 		if (readn(tapefile, hp->dummy, sizeof hp->dummy) < sizeof hp->dummy)
 			break;
-		fname = tarname(hp);
+		fname = nextname, nextname = nil;
+		if(fname == nil || fname[0] == '\0')
+			fname = tarname(hp);
 		if (fname[0] == '\0')
 			break;
 
@@ -162,6 +172,16 @@
 		if (linkflg) {
 			/*fprint(2, "link %s->%s skipped\n", fname, hp->linkname);*/
 			f.size = 0;
+		} else if (hp->linkflag == LF_LONGLINK) {
+			;
+		} else if (hp->linkflag == LF_LONGNAME) {
+			namelen = Maxlongname;
+			if(f.size < namelen)
+				namelen = f.size;
+			namelen = readn(tapefile, longname, namelen);
+			if(namelen < 0) namelen = 0;
+			longname[namelen] = '\0';
+			nextname = longname;
 		} else {
 			/* accept this file */
 			f.name = fname;
@@ -169,8 +189,8 @@
 				fprint(2, "%s: null name skipped\n", argv0);
 			else
 				poppath(f, 1);
-			blkno += (f.size + Tblock - 1)/Tblock;
 		}
+		blkno += (f.size + Tblock - 1)/Tblock;
 	}
 }
 
--- a/sys/src/cmd/tar.c
+++ b/sys/src/cmd/tar.c
@@ -52,6 +52,7 @@
 	Namsiz = 100,
 	Maxpfx = 155,		/* from POSIX */
 	Maxname = Namsiz + 1 + Maxpfx,
+	Maxlongname = 65535,
 	Binsize = 0x80,		/* flag in size[0], from gnu: positive binary size */
 	Binnegsz = 0xff,	/* flag in size[0]: negative binary size */
 
@@ -72,7 +73,11 @@
 	LF_DIR =	'5',
 	LF_FIFO =	'6',
 	LF_CONTIG =	'7',
+
 	/* 'A' - 'Z' are reserved for custom implementations */
+
+	LF_LONGNAME =	'L',		/* GNU extenstion */
+	LF_LONGLINK = 	'K',
 };
 
 #define islink(lf)	(isreallink(lf) || issymlink(lf))
@@ -145,7 +150,7 @@
 static int nblock = Dblock;
 static int resync;
 static char *usefile, *arname = "archive";
-static char origdir[Maxname*2];
+static char origdir[Maxlongname+1];
 static Hdr *tpblk, *endblk;
 static Hdr *curblk;
 
@@ -934,12 +939,11 @@
 static int
 prefix(char *name, char *pfx)
 {
+	char clpfx[Maxlongname+1];
 	int pfxlen = strlen(pfx);
-	char clpfx[Maxname+1];
 
-	if (pfxlen > Maxname)
-		return 0;
-	strcpy(clpfx, pfx);
+	clpfx[Maxlongname] = '\0';
+	strncpy(clpfx, pfx, Maxlongname);
 	cleanname(clpfx);
 	return strncmp(clpfx, name, pfxlen) == 0 &&
 		(name[pfxlen] == '\0' || name[pfxlen] == '/');
@@ -948,12 +952,13 @@
 static int
 match(char *name, char **argv)
 {
+	char clname[Maxlongname+1];
 	int i;
-	char clname[Maxname+1];
 
 	if (argv[0] == nil)
 		return 1;
-	strcpy(clname, name);
+	clname[Maxlongname] = '\0';
+	strncpy(clname, name, Maxlongname);
 	cleanname(clname);
 	for (i = 0; argv[i] != nil; i++)
 		if (prefix(clname, argv[i]))
@@ -1045,6 +1050,7 @@
 	case LF_LINK:
 	case LF_SYMLINK1:
 	case LF_SYMLINK2:
+	case LF_LONGLINK:
 		fprint(2, "%s: can't make (sym)link %s\n",
 			argv0, fname);
 		break;
@@ -1201,6 +1207,46 @@
 	}
 }
 
+static char*
+getname(int ar, Hdr *hp)
+{
+	static char namebuf[Maxlongname+1], *nextname = nil;
+	ulong blksleft, blksread;
+	char *fname, *p;
+	int n;
+
+	if(nextname != nil && nextname[0] != '\0'){
+		fname = nextname, nextname = nil;
+		return fname;
+	}
+	fname = name(hp);
+	if(hp->linkflag == LF_LONGNAME){
+		p = namebuf;
+		for (blksleft = BYTES2TBLKS(arsize(hp)); blksleft > 0;
+		     blksleft -= blksread) {
+			hp = getblkrd(ar, Alldata);
+			if (hp == nil)
+				sysfatal("unexpected EOF on archive reading %s from %s",
+					fname, arname);
+			blksread = gothowmany(blksleft);
+			n = &namebuf[Maxlongname] - p;
+			if(Tblock*blksread < n)
+				n = Tblock*blksread;
+			memmove(p, hp->data, n);
+			p += n;
+			putreadblks(ar, blksread);
+		}
+		*p = '\0';
+		fname = nil;
+		nextname = namebuf;
+	} else {
+		namebuf[Maxlongname] = '\0';
+		strncpy(namebuf, fname, Maxlongname);
+		fname = namebuf;
+	}
+	return fname;
+}
+
 static char *
 extract(char **argv)
 {
@@ -1221,7 +1267,9 @@
 		sysfatal("can't open archive %s: %r", usefile);
 
 	while ((hp = readhdr(ar)) != nil) {
-		longname = name(hp);
+		longname = getname(ar, hp);
+		if(longname == nil)
+			continue;
 		if (match(longname, argv))
 			extract1(ar, hp, longname);
 		else