Fix directory heuristic for long file names.

Tar specifies that a filename ending with '/' is a directory. We were
incorrectly looking at the short name. This meant that when we have long
filenames with a '/' at the 100th character, we would decide it was a
directory.

This change uses the long name when deciding the size for extraction,
and trusts the header size when just skipping forward in the stream.
This commit is contained in:
Ori Bernstein 2019-11-05 10:48:51 -08:00
parent 8b79ad59f1
commit d72a404399

View file

@ -516,11 +516,11 @@ name(Hdr *hp)
} }
static int static int
isdir(Hdr *hp) isdir(Hdr *hp, char *name)
{ {
/* the mode test is ugly but sometimes necessary */ /* the mode test is ugly but sometimes necessary */
return hp->linkflag == LF_DIR || return hp->linkflag == LF_DIR ||
strrchr(name(hp), '\0')[-1] == '/' || strrchr(name, '\0')[-1] == '/' ||
(strtoul(hp->mode, nil, 8)&0170000) == 040000; (strtoul(hp->mode, nil, 8)&0170000) == 040000;
} }
@ -605,9 +605,9 @@ hdrsize(Hdr *hp)
* return the number of bytes recorded in the archive. * return the number of bytes recorded in the archive.
*/ */
static Off static Off
arsize(Hdr *hp) arsize(Hdr *hp, char *fname)
{ {
if(isdir(hp) || islink(hp->linkflag)) if(isdir(hp, fname) || islink(hp->linkflag))
return 0; return 0;
return hdrsize(hp); return hdrsize(hp);
} }
@ -651,7 +651,7 @@ readhdr(int ar)
} while (hdrcksum == -1 || chksum(hp) != hdrcksum); } while (hdrcksum == -1 || chksum(hp) != hdrcksum);
fprint(2, "found %s\n", name(hp)); fprint(2, "found %s\n", name(hp));
} }
nexthdr += Tblock*(1 + BYTES2TBLKS(arsize(hp))); nexthdr += Tblock*(1 + BYTES2TBLKS(hdrsize(hp)));
return hp; return hp;
} }
@ -670,7 +670,7 @@ putfullname(Hdr *hp, char *name)
char *sl, *osl; char *sl, *osl;
String *slname = nil; String *slname = nil;
if (isdir(hp)) { if (isdir(hp, name)) {
slname = s_new(); slname = s_new();
s_append(slname, name); s_append(slname, name);
s_append(slname, "/"); /* posix requires this */ s_append(slname, "/"); /* posix requires this */
@ -898,7 +898,7 @@ replace(char **argv)
if (usefile && !docreate) { if (usefile && !docreate) {
/* skip quickly to the end */ /* skip quickly to the end */
while ((hp = readhdr(ar)) != nil) { while ((hp = readhdr(ar)) != nil) {
bytes = arsize(hp); bytes = hdrsize(hp);
for (blksleft = BYTES2TBLKS(bytes); for (blksleft = BYTES2TBLKS(bytes);
blksleft > 0 && getblkrd(ar, Justnxthdr) != nil; blksleft > 0 && getblkrd(ar, Justnxthdr) != nil;
blksleft -= blksread) { blksleft -= blksread) {
@ -1147,10 +1147,10 @@ extract1(int ar, Hdr *hp, char *fname)
long mtime = strtol(hp->mtime, nil, 8); long mtime = strtol(hp->mtime, nil, 8);
ulong mode = strtoul(hp->mode, nil, 8) & 0777; ulong mode = strtoul(hp->mode, nil, 8) & 0777;
Off bytes = hdrsize(hp); /* for printing */ Off bytes = hdrsize(hp); /* for printing */
ulong blksleft = BYTES2TBLKS(arsize(hp)); ulong blksleft = BYTES2TBLKS(arsize(hp, fname));
/* fiddle name, figure out mode and blocks */ /* fiddle name, figure out mode and blocks */
if (isdir(hp)) { if (isdir(hp, fname)) {
mode |= DMDIR|0700; mode |= DMDIR|0700;
dir = 1; dir = 1;
} }
@ -1200,7 +1200,7 @@ skip(int ar, Hdr *hp, char *fname)
ulong blksleft, blksread; ulong blksleft, blksread;
Hdr *hbp; Hdr *hbp;
for (blksleft = BYTES2TBLKS(arsize(hp)); blksleft > 0; for (blksleft = BYTES2TBLKS(arsize(hp, fname)); blksleft > 0;
blksleft -= blksread) { blksleft -= blksread) {
hbp = getblkrd(ar, Justnxthdr); hbp = getblkrd(ar, Justnxthdr);
if (hbp == nil) if (hbp == nil)
@ -1226,7 +1226,7 @@ getname(int ar, Hdr *hp)
fname = name(hp); fname = name(hp);
if(hp->linkflag == LF_LONGNAME){ if(hp->linkflag == LF_LONGNAME){
p = namebuf; p = namebuf;
for (blksleft = BYTES2TBLKS(arsize(hp)); blksleft > 0; for (blksleft = BYTES2TBLKS(hdrsize(hp)); blksleft > 0;
blksleft -= blksread) { blksleft -= blksread) {
hp = getblkrd(ar, Alldata); hp = getblkrd(ar, Alldata);
if (hp == nil) if (hp == nil)