Add pax extended header support to tar.

Support for 'path=', 'uname=', 'gname=', 'size=', and 'atime=' pax
headers is useful.  Others are ignored, possibly with a warning.

We were running into missing support with the 'go' extraction.

At the same time, this cleans up the way that we handle paths,
getting rid of static buffers with hidden space at the front.
This commit is contained in:
Ori Bernstein 2019-11-14 13:52:41 -08:00
parent 4dab28b14f
commit a68bee44d3

View file

@ -78,6 +78,8 @@ enum {
LF_LONGNAME = 'L', /* GNU extenstion */ LF_LONGNAME = 'L', /* GNU extenstion */
LF_LONGLINK = 'K', LF_LONGLINK = 'K',
LF_PAXHDR = 'x', /* PAX header */
LF_PAXGLOBL = 'g',
}; };
#define islink(lf) (isreallink(lf) || issymlink(lf)) #define islink(lf) (isreallink(lf) || issymlink(lf))
@ -106,6 +108,16 @@ typedef union {
char devminor[8]; char devminor[8];
char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */ char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */
}; };
} Blk;
typedef struct {
char *name;
char *uid;
char *gid;
ulong mode;
vlong size;
vlong mtime;
vlong atime;
} Hdr; } Hdr;
typedef struct { typedef struct {
@ -150,8 +162,9 @@ static int nblock = Dblock;
static int resync; static int resync;
static char *usefile, *arname = "archive"; static char *usefile, *arname = "archive";
static char origdir[Maxlongname+1]; static char origdir[Maxlongname+1];
static Hdr *tpblk, *endblk; static Blk *tpblk, *endblk;
static Hdr *curblk; static Blk *curblk;
static Hdr globlhdr;
static void static void
usage(void) usage(void)
@ -337,7 +350,7 @@ refill(int ar, char *bufs, int justhdr)
if (i != nblock) { if (i != nblock) {
nblock = i; nblock = i;
fprint(2, "%s: blocking = %d\n", argv0, nblock); fprint(2, "%s: blocking = %d\n", argv0, nblock);
endblk = (Hdr *)bufs + nblock; endblk = (Blk *)bufs + nblock;
bytes = n; bytes = n;
} }
} else if (justhdr && seekable && nexthdr - blkoff >= bytes) { } else if (justhdr && seekable && nexthdr - blkoff >= bytes) {
@ -360,7 +373,7 @@ refill(int ar, char *bufs, int justhdr)
return bufs; return bufs;
} }
static Hdr * static Blk *
getblk(int ar, Refill rfp, int justhdr) getblk(int ar, Refill rfp, int justhdr)
{ {
if (curblk == nil || curblk >= endblk) { /* input block exhausted? */ if (curblk == nil || curblk >= endblk) { /* input block exhausted? */
@ -371,26 +384,26 @@ getblk(int ar, Refill rfp, int justhdr)
return curblk++; return curblk++;
} }
static Hdr * static Blk *
getblkrd(int ar, int justhdr) getblkrd(int ar, int justhdr)
{ {
return getblk(ar, refill, justhdr); return getblk(ar, refill, justhdr);
} }
static Hdr * static Blk *
getblke(int ar) getblke(int ar)
{ {
return getblk(ar, nil, Alldata); return getblk(ar, nil, Alldata);
} }
static Hdr * static Blk *
getblkz(int ar) getblkz(int ar)
{ {
Hdr *hp = getblke(ar); Blk *bp = getblke(ar);
if (hp != nil) if (bp != nil)
memset(hp->data, 0, Tblock); memset(bp->data, 0, Tblock);
return hp; return bp;
} }
/* /*
@ -458,25 +471,25 @@ putblkmany(int ar, int blks)
* old archive when updating with `tar rf archive' * old archive when updating with `tar rf archive'
*/ */
static long static long
chksum(Hdr *hp) chksum(Blk *bp)
{ {
int n = Tblock; int n = Tblock;
long i = 0; long i = 0;
uchar *cp = hp->data; uchar *cp = bp->data;
char oldsum[sizeof hp->chksum]; char oldsum[sizeof bp->chksum];
memmove(oldsum, hp->chksum, sizeof oldsum); memmove(oldsum, bp->chksum, sizeof oldsum);
memset(hp->chksum, ' ', sizeof hp->chksum); memset(bp->chksum, ' ', sizeof bp->chksum);
while (n-- > 0) while (n-- > 0)
i += *cp++; i += *cp++;
memmove(hp->chksum, oldsum, sizeof oldsum); memmove(bp->chksum, oldsum, sizeof oldsum);
return i; return i;
} }
static int static int
isustar(Hdr *hp) isustar(Blk *bp)
{ {
return strcmp(hp->magic, "ustar") == 0; return strcmp(bp->magic, "ustar") == 0;
} }
/* /*
@ -492,42 +505,42 @@ strnlen(char *s, int n)
/* set fullname from header */ /* set fullname from header */
static char * static char *
name(Hdr *hp) parsename(Blk *bp, char *buf, int nbuf)
{ {
int pfxlen, namlen; int pfxlen, namlen;
char *fullname;
static char fullnamebuf[2+Maxname+1]; /* 2+ for ./ on relative names */
fullname = fullnamebuf+2; namlen = strnlen(bp->name, sizeof bp->name);
namlen = strnlen(hp->name, sizeof hp->name); if (bp->prefix[0] == '\0' || !isustar(bp)) { /* old-style name? */
if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */ assert(nbuf > namlen);
memmove(fullname, hp->name, namlen); memmove(buf, bp->name, namlen);
fullname[namlen] = '\0'; buf[namlen] = '\0';
return fullname; return buf;
} }
/* name is in two pieces */ /* name is in two pieces */
pfxlen = strnlen(hp->prefix, sizeof hp->prefix); pfxlen = strnlen(bp->prefix, sizeof bp->prefix);
memmove(fullname, hp->prefix, pfxlen); assert(nbuf > pfxlen + 1 + namlen);
fullname[pfxlen] = '/'; memmove(buf, bp->prefix, pfxlen);
memmove(fullname + pfxlen + 1, hp->name, namlen); buf[pfxlen] = '/';
fullname[pfxlen + 1 + namlen] = '\0'; memmove(buf + pfxlen + 1, bp->name, namlen);
return fullname; buf[pfxlen + 1 + namlen] = '\0';
return buf;
} }
static int static int
isdir(Hdr *hp, char *name) isdir(Blk *bp, char *name)
{ {
/* the mode test is ugly but sometimes necessary */ /* the mode test is ugly but sometimes necessary */
return hp->linkflag == LF_DIR || return bp->linkflag == LF_DIR ||
strrchr(name, '\0')[-1] == '/' || strrchr(name, '\0')[-1] == '/' ||
(strtoul(hp->mode, nil, 8)&0170000) == 040000; (strtoul(bp->mode, nil, 8)&0170000) == 040000;
} }
static int static int
eotar(Hdr *hp) eotar(Blk *bp)
{ {
return name(hp)[0] == '\0'; char buf[Maxname + 1];
return parsename(bp, buf, sizeof(buf))[0] == '\0';
} }
/* /*
@ -583,94 +596,295 @@ hdrotoull(char *st, char *end, uvlong errval, char *name, char *field)
* 2^64-1 (actually 2^80-1 but our file sizes are vlongs) rather than 2^33-1. * 2^64-1 (actually 2^80-1 but our file sizes are vlongs) rather than 2^33-1.
*/ */
static Off static Off
hdrsize(Hdr *hp) hdrsize(Blk *bp)
{ {
uchar *p; uchar *p;
char buf[Maxname + 1];
if((uchar)hp->size[0] == Binnegsz) { if((uchar)bp->size[0] == Binnegsz) {
fprint(2, "%s: %s: negative length, which is insane\n", fprint(2, "%s: %s: negative length, which is insane\n",
argv0, name(hp)); argv0, parsename(bp, buf, sizeof(buf)));
return 0; return 0;
} else if((uchar)hp->size[0] == Binsize) { } else if((uchar)bp->size[0] == Binsize) {
p = (uchar *)hp->size + sizeof hp->size - 1 - p = (uchar *)bp->size + sizeof bp->size - 1 -
sizeof(vlong); /* -1 for terminating space */ sizeof(vlong); /* -1 for terminating space */
return G8BEBYTE(p); return G8BEBYTE(p);
} }
return hdrotoull(hp->size, hp->size + sizeof hp->size, 0, return hdrotoull(bp->size, bp->size + sizeof bp->size, 0,
name(hp), "size"); parsename(bp, buf, sizeof(buf)), "size");
} }
/* /*
* 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, char *fname) arsize(Blk *bp, char *fname)
{ {
if(isdir(hp, fname) || islink(hp->linkflag)) if(isdir(bp, fname) || islink(bp->linkflag))
return 0; return 0;
return hdrsize(hp); return hdrsize(bp);
} }
static long static long
parsecksum(char *cksum, char *name) parsecksum(char *cksum, char *name)
{ {
Hdr *hp; Blk *bp;
return hdrotoull(cksum, cksum + sizeof hp->chksum, (uvlong)-1LL, return hdrotoull(cksum, cksum + sizeof bp->chksum, (uvlong)-1LL,
name, "checksum"); name, "checksum");
} }
static Hdr * static Blk *
readhdr(int ar) readhdrblk(int ar)
{ {
char buf[Maxname + 1];
long hdrcksum; long hdrcksum;
Hdr *hp; Blk *bp;
hp = getblkrd(ar, Alldata); bp = getblkrd(ar, Alldata);
if (hp == nil) if (bp == nil)
sysfatal("unexpected EOF instead of archive header in %s", sysfatal("unexpected EOF instead of archive header in %s",
arname); arname);
if (eotar(hp)) /* end-of-archive block? */ if (eotar(bp)) /* end-of-archive block? */
return nil; return nil;
hdrcksum = parsecksum(hp->chksum, name(hp)); hdrcksum = parsecksum(bp->chksum, parsename(bp, buf, sizeof(buf)));
if (hdrcksum == -1 || chksum(hp) != hdrcksum) { if (hdrcksum == -1 || chksum(bp) != hdrcksum) {
if (!resync) if (!resync)
sysfatal("bad archive header checksum in %s: " sysfatal("bad archive header checksum in %s: "
"name %.100s...; expected %#luo got %#luo", "name %.100s...; expected %#luo got %#luo",
arname, hp->name, hdrcksum, chksum(hp)); arname, bp->name, hdrcksum, chksum(bp));
fprint(2, "%s: skipping past archive header with bad checksum in %s...", fprint(2, "%s: skipping past archive header with bad checksum in %s...",
argv0, arname); argv0, arname);
do { do {
hp = getblkrd(ar, Alldata); bp = getblkrd(ar, Alldata);
if (hp == nil) if (bp == nil)
sysfatal("unexpected EOF looking for archive header in %s", sysfatal("unexpected EOF looking for archive header in %s",
arname); arname);
hdrcksum = parsecksum(hp->chksum, name(hp)); hdrcksum = parsecksum(bp->chksum, parsename(bp, buf, sizeof(buf)));
} while (hdrcksum == -1 || chksum(hp) != hdrcksum); } while (hdrcksum == -1 || chksum(bp) != hdrcksum);
fprint(2, "found %s\n", name(hp)); fprint(2, "found %s\n", parsename(bp, buf, sizeof(buf)));
} }
nexthdr += Tblock*(1 + BYTES2TBLKS(hdrsize(hp))); nexthdr += Tblock*(1 + BYTES2TBLKS(hdrsize(bp)));
return hp;
return bp;
}
static int
getname(int ar, Blk *bp, Hdr *hdr)
{
char buf[Maxlongname+1];
ulong blksleft, blksread;
char *p;
int n;
if (bp->linkflag != LF_LONGNAME){
/* PAX attributes take precedence */
if(hdr->name == nil)
hdr->name = strdup(parsename(bp, buf, sizeof(buf)));
return 0;
}
p = buf;
for (blksleft = BYTES2TBLKS(hdrsize(bp)); blksleft > 0; blksleft -= blksread) {
bp = getblkrd(ar, Alldata);
if (bp == nil)
sysfatal("unexpected EOF on archive reading from %s", arname);
blksread = gothowmany(blksleft);
n = &buf[Maxlongname] - p;
if(Tblock*blksread < n)
n = Tblock*blksread;
memmove(p, bp->data, n);
p += n;
}
*p = '\0';
hdr->name = strdup(buf);
return 1;
}
static char *
matchattr(char *kvp, char *k)
{
if (strncmp(kvp, k, strlen(k)) == 0)
return kvp + strlen(k);
return nil;
}
static int
parsepax(int ar, Blk *bp, Hdr *hdr, int paxtype)
{
char *p, *lp, *le, *e, *kvp, *val, lenbuf[16];
ulong blksleft;
int n, off, len;
Blk *b;
if (!isustar(bp) || bp->linkflag != paxtype)
return 0;
off = 0;
len = -1;
kvp = nil;
lp = lenbuf;
le = lenbuf + sizeof(lenbuf);
for (blksleft = BYTES2TBLKS(hdrsize(bp)); blksleft > 0; ) {
b = getblkrd(ar, Alldata);
if (b == nil)
sysfatal("unexpected EOF on archive reading attr for %s from %s", bp->name, arname);
p = (char *)b->data;
e = (char *)b->data + sizeof(b->data);
while(p != e) {
if(*p == '\0')
break;
/*
* Copy out the length prefix. This may span a block boundary, so be
* careful about when we get the next block. The length prefix includes
* both its own length and the trailing newline. We want to trim the
* length prefix, since we already consumed it.
*/
if(len == -1){
while(p != e && *p >= '0' && *p <= '9'){
if(lp + 1 == le)
sysfatal("oversize length prefix in pax header");
*lp++ = *p++;
}
if (p == e && blksleft > 0)
goto nextblk;
if (*p++ != ' ')
sysfatal("invalid delimiter in pax header: %c (%s)\n", *p, p);
*lp++ = '\0';
len = atoi(lenbuf) - strlen(lenbuf) - 1;
}
if (kvp == nil && (kvp = malloc(len)) == nil)
sysfatal("out of memory: %r");
n = (len - off > e - p) ? e - p : len - off;
memcpy(kvp + off, p, n);
kvp[len - 1] = '\0';
off += n;
p += n;
assert(e >= p);
if (len == off) {
if ((val = matchattr(kvp, "path=")) != nil) {
free(hdr->name);
hdr->name = strdup(val);
} else if ((val = matchattr(kvp, "linkpath=")) != nil) {
/* Mostly for better error messages. */
free(hdr->name);
hdr->name = strdup(val);
} else if ((val = matchattr(kvp, "uname=")) != nil) {
free(hdr->uid);
hdr->uid = strdup(val);
} else if ((val = matchattr(kvp, "gname=")) != nil) {
free(hdr->gid);
hdr->gid = strdup(val);
} else if ((val = matchattr(kvp, "atime=")) != nil)
hdr->atime = strtoll(val, nil, 0);
else if ((val = matchattr(kvp, "mtime=")) != nil)
hdr->mtime = strtoll(val, nil, 0);
else if ((val = matchattr(kvp, "size=")) != nil)
hdr->size = strtoll(val, nil, 0);
else
if (matchattr(kvp, "comment=") == nil && matchattr(kvp, "ctime=") == nil)
fprint(2, "warning: pax attribute not supported: %s\n", kvp);
free(kvp);
kvp = nil;
lp = lenbuf;
off = 0;
len = -1;
}
}
nextblk:
blksleft--;
}
return 1;
}
static int
parsehdr(Hdr *hdr, Blk *bp)
{
int ustar;
ustar = isustar(bp);
if(hdr->mode == -1)
hdr->mode = (strtoul(bp->mode, nil, 8) & 0777);
if(hdr->mode == -1)
hdr->mode = globlhdr.mode;
if (isdir(bp, hdr->name))
hdr->mode |= DMDIR;
if (hdr->atime == -1)
hdr->atime = -1;
if (hdr->atime == -1)
hdr->atime = globlhdr.atime;
if (hdr->mtime == -1)
hdr->mtime = strtol(bp->mtime, nil, 8);
if (hdr->mtime == -1)
hdr->mtime = globlhdr.mtime;
if (hdr->size == -1)
hdr->size = arsize(bp, hdr->name);
if (ustar && hdr->uid == nil)
hdr->uid = strdup(bp->uname);
if (hdr->uid == nil)
hdr->uid = (globlhdr.uid != nil) ? strdup(globlhdr.uid) : nil;
if (ustar && hdr->gid == nil)
hdr->gid = strdup(bp->uname);
if (hdr->gid == nil)
hdr->gid = (globlhdr.gid != nil) ? strdup(globlhdr.gid) : nil;
return 0;
}
static void
nullhdr(Hdr *hdr)
{
hdr->name = nil;
hdr->uid = nil;
hdr->gid = nil;
hdr->mode = -1;
hdr->mtime = -1;
hdr->atime = -1;
hdr->size = -1;
}
static Blk *
readhdr(int ar, Hdr *hdr)
{
Blk *bp;
nullhdr(hdr);
again:
if ((bp = readhdrblk(ar)) == nil)
return nil;
if (parsepax(ar, bp, hdr, LF_PAXHDR))
goto again;
if (getname(ar, bp, hdr))
goto again;
if (parsehdr(hdr, bp) == -1)
sysfatal("could not parse header: %r");
if (parsepax(ar, bp, &globlhdr, LF_PAXGLOBL))
goto again;
return bp;
}
void
freehdr(Hdr *hdr)
{
free(hdr->name);
free(hdr->uid);
free(hdr->gid);
} }
/*
* tar r[c]
*/
/* /*
* if name is longer than Namsiz bytes, try to split it at a slash and fit the * if name is longer than Namsiz bytes, try to split it at a slash and fit the
* pieces into hp->prefix and hp->name. * pieces into bp->prefix and bp->name.
*/ */
static int static int
putfullname(Hdr *hp, char *name) putfullname(Blk *bp, char *name)
{ {
int namlen, pfxlen; int namlen, pfxlen;
char *sl, *osl; char *sl, *osl;
String *slname = nil; String *slname = nil;
if (isdir(hp, name)) { if (isdir(bp, 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 */
@ -679,8 +893,8 @@ putfullname(Hdr *hp, char *name)
namlen = strlen(name); namlen = strlen(name);
if (namlen <= Namsiz) { if (namlen <= Namsiz) {
strncpy(hp->name, name, Namsiz); strncpy(bp->name, name, Namsiz);
hp->prefix[0] = '\0'; /* ustar paranoia */ bp->prefix[0] = '\0'; /* ustar paranoia */
return 0; return 0;
} }
@ -693,12 +907,12 @@ putfullname(Hdr *hp, char *name)
* try various splits until one results in pieces that fit into the * try various splits until one results in pieces that fit into the
* appropriate fields of the header. look for slashes from right * appropriate fields of the header. look for slashes from right
* to left, in the hopes of putting the largest part of the name into * to left, in the hopes of putting the largest part of the name into
* hp->prefix, which is larger than hp->name. * bp->prefix, which is larger than bp->name.
*/ */
sl = strrchr(name, '/'); sl = strrchr(name, '/');
while (sl != nil) { while (sl != nil) {
pfxlen = sl - name; pfxlen = sl - name;
if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= Namsiz) if (pfxlen <= sizeof bp->prefix && namlen-1 - pfxlen <= Namsiz)
break; break;
osl = sl; osl = sl;
*osl = '\0'; *osl = '\0';
@ -711,16 +925,16 @@ putfullname(Hdr *hp, char *name)
return -1; return -1;
} }
*sl = '\0'; *sl = '\0';
strncpy(hp->prefix, name, sizeof hp->prefix); strncpy(bp->prefix, name, sizeof bp->prefix);
*sl++ = '/'; *sl++ = '/';
strncpy(hp->name, sl, sizeof hp->name); strncpy(bp->name, sl, sizeof bp->name);
if (slname) if (slname)
s_free(slname); s_free(slname);
return 0; return 0;
} }
static int static int
mkhdr(Hdr *hp, Dir *dir, char *file) mkhdr(Blk *bp, Dir *dir, char *file)
{ {
int r; int r;
@ -728,9 +942,9 @@ mkhdr(Hdr *hp, Dir *dir, char *file)
* some of these fields run together, so we format them left-to-right * some of these fields run together, so we format them left-to-right
* and don't use snprint. * and don't use snprint.
*/ */
sprint(hp->mode, "%6lo ", dir->mode & 0777); sprint(bp->mode, "%6lo ", dir->mode & 0777);
sprint(hp->uid, "%6o ", aruid); sprint(bp->uid, "%6o ", aruid);
sprint(hp->gid, "%6o ", argid); sprint(bp->gid, "%6o ", argid);
if (dir->length >= (Off)1<<32) { if (dir->length >= (Off)1<<32) {
static int printed; static int printed;
@ -738,22 +952,22 @@ mkhdr(Hdr *hp, Dir *dir, char *file)
printed = 1; printed = 1;
fprint(2, "%s: storing large sizes in \"base 256\"\n", argv0); fprint(2, "%s: storing large sizes in \"base 256\"\n", argv0);
} }
hp->size[0] = Binsize; bp->size[0] = Binsize;
/* emit so-called `base 256' representation of size */ /* emit so-called `base 256' representation of size */
putbe((uchar *)hp->size+1, dir->length, sizeof hp->size - 2); putbe((uchar *)bp->size+1, dir->length, sizeof bp->size - 2);
hp->size[sizeof hp->size - 1] = ' '; bp->size[sizeof bp->size - 1] = ' ';
} else } else
sprint(hp->size, "%11lluo ", dir->length); sprint(bp->size, "%11lluo ", dir->length);
sprint(hp->mtime, "%11luo ", dir->mtime); sprint(bp->mtime, "%11luo ", dir->mtime);
hp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1); bp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1);
r = putfullname(hp, file); r = putfullname(bp, file);
if (posix) { if (posix) {
strncpy(hp->magic, "ustar", sizeof hp->magic); strncpy(bp->magic, "ustar", sizeof bp->magic);
strncpy(hp->version, "00", sizeof hp->version); strncpy(bp->version, "00", sizeof bp->version);
strncpy(hp->uname, dir->uid, sizeof hp->uname); strncpy(bp->uname, dir->uid, sizeof bp->uname);
strncpy(hp->gname, dir->gid, sizeof hp->gname); strncpy(bp->gname, dir->gid, sizeof bp->gname);
} }
sprint(hp->chksum, "%6luo", chksum(hp)); sprint(bp->chksum, "%6luo", chksum(bp));
return r; return r;
} }
@ -806,7 +1020,7 @@ addtoar(int ar, char *file, char *shortf)
int n, fd, isdir; int n, fd, isdir;
long bytes, blksread; long bytes, blksread;
ulong blksleft; ulong blksleft;
Hdr *hbp; Blk *hbp;
Dir *dir; Dir *dir;
String *name = nil; String *name = nil;
@ -877,7 +1091,7 @@ replace(char **argv)
int i, ar; int i, ar;
ulong blksleft, blksread; ulong blksleft, blksread;
Off bytes; Off bytes;
Hdr *hp; Blk *bp;
Compress *comp = nil; Compress *comp = nil;
Pushstate ps; Pushstate ps;
@ -897,8 +1111,8 @@ 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 ((bp = readhdrblk(ar)) != nil) {
bytes = hdrsize(hp); bytes = hdrsize(bp);
for (blksleft = BYTES2TBLKS(bytes); for (blksleft = BYTES2TBLKS(bytes);
blksleft > 0 && getblkrd(ar, Justnxthdr) != nil; blksleft > 0 && getblkrd(ar, Justnxthdr) != nil;
blksleft -= blksread) { blksleft -= blksread) {
@ -1044,13 +1258,14 @@ xaccess(char *name, int mode)
} }
static int static int
openfname(Hdr *hp, char *fname, int dir, int mode) openfname(Blk *bp, char *fname, int mode)
{ {
int fd; int fd, dir;
fd = -1; fd = -1;
dir = mode & DMDIR;
cleanname(fname); cleanname(fname);
switch (hp->linkflag) { switch (bp->linkflag) {
case LF_LINK: case LF_LINK:
case LF_SYMLINK1: case LF_SYMLINK1:
case LF_SYMLINK2: case LF_SYMLINK2:
@ -1063,7 +1278,7 @@ openfname(Hdr *hp, char *fname, int dir, int mode)
break; break;
default: default:
if (!keepexisting || access(fname, AEXIST) < 0) { if (!keepexisting || access(fname, AEXIST) < 0) {
int rw = (dir? OREAD: OWRITE); int rw = (dir ? OREAD: OWRITE);
fd = create(fname, rw, mode); fd = create(fname, rw, mode);
if (fd < 0) { if (fd < 0) {
@ -1086,7 +1301,7 @@ copyfromar(int ar, int fd, char *fname, ulong blksleft, Off bytes)
{ {
int wrbytes; int wrbytes;
ulong blksread; ulong blksread;
Hdr *hbp; Blk *hbp;
if (blksleft == 0 || bytes < 0) if (blksleft == 0 || bytes < 0)
bytes = 0; bytes = 0;
@ -1118,20 +1333,22 @@ copyfromar(int ar, int fd, char *fname, ulong blksleft, Off bytes)
} }
static void static void
wrmeta(int fd, Hdr *hp, long mtime, int mode) /* update metadata */ wrmeta(int fd, Hdr *hdr) /* update metadata */
{ {
Dir nd; Dir nd;
nulldir(&nd); nulldir(&nd);
nd.mtime = mtime; nd.mtime = hdr->mtime;
nd.mode = mode; nd.mode = hdr->mode;
dirfwstat(fd, &nd); dirfwstat(fd, &nd);
if (isustar(hp)) { if (hdr->gid) {
nulldir(&nd); nulldir(&nd);
nd.gid = hp->gname; nd.gid = hdr->gid;
dirfwstat(fd, &nd); dirfwstat(fd, &nd);
}
if (hdr->uid){
nulldir(&nd); nulldir(&nd);
nd.uid = hp->uname; nd.uid = hdr->uid;
dirfwstat(fd, &nd); dirfwstat(fd, &nd);
} }
} }
@ -1141,20 +1358,15 @@ wrmeta(int fd, Hdr *hp, long mtime, int mode) /* update metadata */
* fname is result of getname(), so has two extra bytes at beginning. * fname is result of getname(), so has two extra bytes at beginning.
*/ */
static void static void
extract1(int ar, Hdr *hp, char *fname) extract1(int ar, Blk *bp, Hdr *hdr)
{ {
int fd = -1, dir = 0; int fd = -1;
long mtime = strtol(hp->mtime, nil, 8); Off bytes = hdr->size; /* for printing */
ulong mode = strtoul(hp->mode, nil, 8) & 0777; uvlong blksleft = BYTES2TBLKS(bytes);
Off bytes = hdrsize(hp); /* for printing */ char *path;
ulong blksleft = BYTES2TBLKS(arsize(hp, fname));
/* fiddle name, figure out mode and blocks */ /* fiddle name, figure out mode and blocks */
if (isdir(hp, fname)) { switch (bp->linkflag) {
mode |= DMDIR|0700;
dir = 1;
}
switch (hp->linkflag) {
case LF_LINK: case LF_LINK:
case LF_SYMLINK1: case LF_SYMLINK1:
case LF_SYMLINK2: case LF_SYMLINK2:
@ -1162,25 +1374,27 @@ extract1(int ar, Hdr *hp, char *fname)
blksleft = 0; blksleft = 0;
break; break;
} }
if (relative) if((path = malloc(strlen(hdr->name) + 3)) == nil)
if(fname[0] == '/') sysfatal("malloc: %r");
*--fname = '.'; if (relative && hdr->name[0] == '/')
else if(fname[0] == '#'){ strcpy(path, ".");
*--fname = '/'; else if(relative && hdr->name[0] == '#')
*--fname = '.'; strcpy(path, "./");
} else
path[0] = '\0';
strcat(path, hdr->name);
if (verb == Xtract) if (verb == Xtract)
fd = openfname(hp, fname, dir, mode); fd = openfname(bp, path, hdr->mode);
else if (verbose) { else if (verbose) {
char *cp = ctime(mtime); char *cp = ctime(hdr->mtime);
print("%M %8lld %-12.12s %-4.4s %s\n", print("%M %8lld %-12.12s %-4.4s %s\n",
mode, bytes, cp+4, cp+24, fname); hdr->mode, bytes, cp+4, cp+24, path);
} else } else
print("%s\n", fname); print("%s\n", path);
copyfromar(ar, fd, fname, blksleft, bytes); copyfromar(ar, fd, path, blksleft, hdr->size);
/* touch up meta data and close */ /* touch up meta data and close */
if (fd >= 0) { if (fd >= 0) {
@ -1189,70 +1403,35 @@ extract1(int ar, Hdr *hp, char *fname)
* creating files in them, but we don't do that. * creating files in them, but we don't do that.
*/ */
if (settime) if (settime)
wrmeta(fd, hp, mtime, mode); wrmeta(fd, hdr);
close(fd); close(fd);
} }
free(path);
} }
static void static void
skip(int ar, Hdr *hp, char *fname) skip(int ar, Blk *bp, Hdr *hdr)
{ {
ulong blksleft, blksread; ulong blksleft, blksread;
Hdr *hbp; Blk *hbp;
for (blksleft = BYTES2TBLKS(arsize(hp, fname)); blksleft > 0; for (blksleft = BYTES2TBLKS(arsize(bp, hdr->name)); blksleft > 0;
blksleft -= blksread) { blksleft -= blksread) {
hbp = getblkrd(ar, Justnxthdr); hbp = getblkrd(ar, Justnxthdr);
if (hbp == nil) if (hbp == nil)
sysfatal("unexpected EOF on archive extracting %s from %s", sysfatal("unexpected EOF on archive extracting %s from %s",
fname, arname); hdr->name, arname);
blksread = gothowmany(blksleft); blksread = gothowmany(blksleft);
putreadblks(ar, blksread); putreadblks(ar, blksread);
} }
} }
static char*
getname(int ar, Hdr *hp)
{
static char buf[2+Maxlongname+1], *namebuf = buf+2, *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(hdrsize(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;
}
return fname;
}
static char * static char *
extract(char **argv) extract(char **argv)
{ {
int ar; int ar;
char *longname; Blk *bp;
Hdr *hp; Hdr hdr;
Compress *comp; Compress *comp;
Pushstate ps; Pushstate ps;
@ -1266,14 +1445,12 @@ extract(char **argv)
if (ar < 0) if (ar < 0)
sysfatal("can't open archive %s: %r", usefile); sysfatal("can't open archive %s: %r", usefile);
while ((hp = readhdr(ar)) != nil) { while ((bp = readhdr(ar, &hdr)) != nil) {
longname = getname(ar, hp); if (match(hdr.name, argv))
if(longname == nil) extract1(ar, bp, &hdr);
continue;
if (match(longname, argv))
extract1(ar, hp, longname);
else else
skip(ar, hp, longname); skip(ar, bp, &hdr);
freehdr(&hdr);
} }
if (comp) if (comp)
@ -1356,6 +1533,7 @@ main(int argc, char *argv[])
usage(); usage();
initblks(); initblks();
nullhdr(&globlhdr);
switch (verb) { switch (verb) {
case Toc: case Toc:
case Xtract: case Xtract: