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_LONGLINK = 'K',
LF_PAXHDR = 'x', /* PAX header */
LF_PAXGLOBL = 'g',
};
#define islink(lf) (isreallink(lf) || issymlink(lf))
@ -106,6 +108,16 @@ typedef union {
char devminor[8];
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;
typedef struct {
@ -150,8 +162,9 @@ static int nblock = Dblock;
static int resync;
static char *usefile, *arname = "archive";
static char origdir[Maxlongname+1];
static Hdr *tpblk, *endblk;
static Hdr *curblk;
static Blk *tpblk, *endblk;
static Blk *curblk;
static Hdr globlhdr;
static void
usage(void)
@ -337,7 +350,7 @@ refill(int ar, char *bufs, int justhdr)
if (i != nblock) {
nblock = i;
fprint(2, "%s: blocking = %d\n", argv0, nblock);
endblk = (Hdr *)bufs + nblock;
endblk = (Blk *)bufs + nblock;
bytes = n;
}
} else if (justhdr && seekable && nexthdr - blkoff >= bytes) {
@ -360,7 +373,7 @@ refill(int ar, char *bufs, int justhdr)
return bufs;
}
static Hdr *
static Blk *
getblk(int ar, Refill rfp, int justhdr)
{
if (curblk == nil || curblk >= endblk) { /* input block exhausted? */
@ -371,26 +384,26 @@ getblk(int ar, Refill rfp, int justhdr)
return curblk++;
}
static Hdr *
static Blk *
getblkrd(int ar, int justhdr)
{
return getblk(ar, refill, justhdr);
}
static Hdr *
static Blk *
getblke(int ar)
{
return getblk(ar, nil, Alldata);
}
static Hdr *
static Blk *
getblkz(int ar)
{
Hdr *hp = getblke(ar);
Blk *bp = getblke(ar);
if (hp != nil)
memset(hp->data, 0, Tblock);
return hp;
if (bp != nil)
memset(bp->data, 0, Tblock);
return bp;
}
/*
@ -458,25 +471,25 @@ putblkmany(int ar, int blks)
* old archive when updating with `tar rf archive'
*/
static long
chksum(Hdr *hp)
chksum(Blk *bp)
{
int n = Tblock;
long i = 0;
uchar *cp = hp->data;
char oldsum[sizeof hp->chksum];
uchar *cp = bp->data;
char oldsum[sizeof bp->chksum];
memmove(oldsum, hp->chksum, sizeof oldsum);
memset(hp->chksum, ' ', sizeof hp->chksum);
memmove(oldsum, bp->chksum, sizeof oldsum);
memset(bp->chksum, ' ', sizeof bp->chksum);
while (n-- > 0)
i += *cp++;
memmove(hp->chksum, oldsum, sizeof oldsum);
memmove(bp->chksum, oldsum, sizeof oldsum);
return i;
}
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 */
static char *
name(Hdr *hp)
parsename(Blk *bp, char *buf, int nbuf)
{
int pfxlen, namlen;
char *fullname;
static char fullnamebuf[2+Maxname+1]; /* 2+ for ./ on relative names */
fullname = fullnamebuf+2;
namlen = strnlen(hp->name, sizeof hp->name);
if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */
memmove(fullname, hp->name, namlen);
fullname[namlen] = '\0';
return fullname;
namlen = strnlen(bp->name, sizeof bp->name);
if (bp->prefix[0] == '\0' || !isustar(bp)) { /* old-style name? */
assert(nbuf > namlen);
memmove(buf, bp->name, namlen);
buf[namlen] = '\0';
return buf;
}
/* name is in two pieces */
pfxlen = strnlen(hp->prefix, sizeof hp->prefix);
memmove(fullname, hp->prefix, pfxlen);
fullname[pfxlen] = '/';
memmove(fullname + pfxlen + 1, hp->name, namlen);
fullname[pfxlen + 1 + namlen] = '\0';
return fullname;
pfxlen = strnlen(bp->prefix, sizeof bp->prefix);
assert(nbuf > pfxlen + 1 + namlen);
memmove(buf, bp->prefix, pfxlen);
buf[pfxlen] = '/';
memmove(buf + pfxlen + 1, bp->name, namlen);
buf[pfxlen + 1 + namlen] = '\0';
return buf;
}
static int
isdir(Hdr *hp, char *name)
isdir(Blk *bp, char *name)
{
/* the mode test is ugly but sometimes necessary */
return hp->linkflag == LF_DIR ||
return bp->linkflag == LF_DIR ||
strrchr(name, '\0')[-1] == '/' ||
(strtoul(hp->mode, nil, 8)&0170000) == 040000;
(strtoul(bp->mode, nil, 8)&0170000) == 040000;
}
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.
*/
static Off
hdrsize(Hdr *hp)
hdrsize(Blk *bp)
{
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",
argv0, name(hp));
argv0, parsename(bp, buf, sizeof(buf)));
return 0;
} else if((uchar)hp->size[0] == Binsize) {
p = (uchar *)hp->size + sizeof hp->size - 1 -
} else if((uchar)bp->size[0] == Binsize) {
p = (uchar *)bp->size + sizeof bp->size - 1 -
sizeof(vlong); /* -1 for terminating space */
return G8BEBYTE(p);
}
return hdrotoull(hp->size, hp->size + sizeof hp->size, 0,
name(hp), "size");
return hdrotoull(bp->size, bp->size + sizeof bp->size, 0,
parsename(bp, buf, sizeof(buf)), "size");
}
/*
* return the number of bytes recorded in the archive.
*/
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 hdrsize(hp);
return hdrsize(bp);
}
static long
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");
}
static Hdr *
readhdr(int ar)
static Blk *
readhdrblk(int ar)
{
char buf[Maxname + 1];
long hdrcksum;
Hdr *hp;
Blk *bp;
hp = getblkrd(ar, Alldata);
if (hp == nil)
bp = getblkrd(ar, Alldata);
if (bp == nil)
sysfatal("unexpected EOF instead of archive header in %s",
arname);
if (eotar(hp)) /* end-of-archive block? */
if (eotar(bp)) /* end-of-archive block? */
return nil;
hdrcksum = parsecksum(hp->chksum, name(hp));
if (hdrcksum == -1 || chksum(hp) != hdrcksum) {
hdrcksum = parsecksum(bp->chksum, parsename(bp, buf, sizeof(buf)));
if (hdrcksum == -1 || chksum(bp) != hdrcksum) {
if (!resync)
sysfatal("bad archive header checksum in %s: "
"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...",
argv0, arname);
do {
hp = getblkrd(ar, Alldata);
if (hp == nil)
bp = getblkrd(ar, Alldata);
if (bp == nil)
sysfatal("unexpected EOF looking for archive header in %s",
arname);
hdrcksum = parsecksum(hp->chksum, name(hp));
} while (hdrcksum == -1 || chksum(hp) != hdrcksum);
fprint(2, "found %s\n", name(hp));
hdrcksum = parsecksum(bp->chksum, parsename(bp, buf, sizeof(buf)));
} while (hdrcksum == -1 || chksum(bp) != hdrcksum);
fprint(2, "found %s\n", parsename(bp, buf, sizeof(buf)));
}
nexthdr += Tblock*(1 + BYTES2TBLKS(hdrsize(hp)));
return hp;
nexthdr += Tblock*(1 + BYTES2TBLKS(hdrsize(bp)));
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
* pieces into hp->prefix and hp->name.
* pieces into bp->prefix and bp->name.
*/
static int
putfullname(Hdr *hp, char *name)
putfullname(Blk *bp, char *name)
{
int namlen, pfxlen;
char *sl, *osl;
String *slname = nil;
if (isdir(hp, name)) {
if (isdir(bp, name)) {
slname = s_new();
s_append(slname, name);
s_append(slname, "/"); /* posix requires this */
@ -679,8 +893,8 @@ putfullname(Hdr *hp, char *name)
namlen = strlen(name);
if (namlen <= Namsiz) {
strncpy(hp->name, name, Namsiz);
hp->prefix[0] = '\0'; /* ustar paranoia */
strncpy(bp->name, name, Namsiz);
bp->prefix[0] = '\0'; /* ustar paranoia */
return 0;
}
@ -693,12 +907,12 @@ putfullname(Hdr *hp, char *name)
* try various splits until one results in pieces that fit into the
* appropriate fields of the header. look for slashes from right
* 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, '/');
while (sl != nil) {
pfxlen = sl - name;
if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= Namsiz)
if (pfxlen <= sizeof bp->prefix && namlen-1 - pfxlen <= Namsiz)
break;
osl = sl;
*osl = '\0';
@ -711,16 +925,16 @@ putfullname(Hdr *hp, char *name)
return -1;
}
*sl = '\0';
strncpy(hp->prefix, name, sizeof hp->prefix);
strncpy(bp->prefix, name, sizeof bp->prefix);
*sl++ = '/';
strncpy(hp->name, sl, sizeof hp->name);
strncpy(bp->name, sl, sizeof bp->name);
if (slname)
s_free(slname);
return 0;
}
static int
mkhdr(Hdr *hp, Dir *dir, char *file)
mkhdr(Blk *bp, Dir *dir, char *file)
{
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
* and don't use snprint.
*/
sprint(hp->mode, "%6lo ", dir->mode & 0777);
sprint(hp->uid, "%6o ", aruid);
sprint(hp->gid, "%6o ", argid);
sprint(bp->mode, "%6lo ", dir->mode & 0777);
sprint(bp->uid, "%6o ", aruid);
sprint(bp->gid, "%6o ", argid);
if (dir->length >= (Off)1<<32) {
static int printed;
@ -738,22 +952,22 @@ mkhdr(Hdr *hp, Dir *dir, char *file)
printed = 1;
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 */
putbe((uchar *)hp->size+1, dir->length, sizeof hp->size - 2);
hp->size[sizeof hp->size - 1] = ' ';
putbe((uchar *)bp->size+1, dir->length, sizeof bp->size - 2);
bp->size[sizeof bp->size - 1] = ' ';
} else
sprint(hp->size, "%11lluo ", dir->length);
sprint(hp->mtime, "%11luo ", dir->mtime);
hp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1);
r = putfullname(hp, file);
sprint(bp->size, "%11lluo ", dir->length);
sprint(bp->mtime, "%11luo ", dir->mtime);
bp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1);
r = putfullname(bp, file);
if (posix) {
strncpy(hp->magic, "ustar", sizeof hp->magic);
strncpy(hp->version, "00", sizeof hp->version);
strncpy(hp->uname, dir->uid, sizeof hp->uname);
strncpy(hp->gname, dir->gid, sizeof hp->gname);
strncpy(bp->magic, "ustar", sizeof bp->magic);
strncpy(bp->version, "00", sizeof bp->version);
strncpy(bp->uname, dir->uid, sizeof bp->uname);
strncpy(bp->gname, dir->gid, sizeof bp->gname);
}
sprint(hp->chksum, "%6luo", chksum(hp));
sprint(bp->chksum, "%6luo", chksum(bp));
return r;
}
@ -806,7 +1020,7 @@ addtoar(int ar, char *file, char *shortf)
int n, fd, isdir;
long bytes, blksread;
ulong blksleft;
Hdr *hbp;
Blk *hbp;
Dir *dir;
String *name = nil;
@ -877,7 +1091,7 @@ replace(char **argv)
int i, ar;
ulong blksleft, blksread;
Off bytes;
Hdr *hp;
Blk *bp;
Compress *comp = nil;
Pushstate ps;
@ -897,8 +1111,8 @@ replace(char **argv)
if (usefile && !docreate) {
/* skip quickly to the end */
while ((hp = readhdr(ar)) != nil) {
bytes = hdrsize(hp);
while ((bp = readhdrblk(ar)) != nil) {
bytes = hdrsize(bp);
for (blksleft = BYTES2TBLKS(bytes);
blksleft > 0 && getblkrd(ar, Justnxthdr) != nil;
blksleft -= blksread) {
@ -1044,13 +1258,14 @@ xaccess(char *name, int mode)
}
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;
dir = mode & DMDIR;
cleanname(fname);
switch (hp->linkflag) {
switch (bp->linkflag) {
case LF_LINK:
case LF_SYMLINK1:
case LF_SYMLINK2:
@ -1063,7 +1278,7 @@ openfname(Hdr *hp, char *fname, int dir, int mode)
break;
default:
if (!keepexisting || access(fname, AEXIST) < 0) {
int rw = (dir? OREAD: OWRITE);
int rw = (dir ? OREAD: OWRITE);
fd = create(fname, rw, mode);
if (fd < 0) {
@ -1086,7 +1301,7 @@ copyfromar(int ar, int fd, char *fname, ulong blksleft, Off bytes)
{
int wrbytes;
ulong blksread;
Hdr *hbp;
Blk *hbp;
if (blksleft == 0 || bytes < 0)
bytes = 0;
@ -1118,20 +1333,22 @@ copyfromar(int ar, int fd, char *fname, ulong blksleft, Off bytes)
}
static void
wrmeta(int fd, Hdr *hp, long mtime, int mode) /* update metadata */
wrmeta(int fd, Hdr *hdr) /* update metadata */
{
Dir nd;
nulldir(&nd);
nd.mtime = mtime;
nd.mode = mode;
nd.mtime = hdr->mtime;
nd.mode = hdr->mode;
dirfwstat(fd, &nd);
if (isustar(hp)) {
if (hdr->gid) {
nulldir(&nd);
nd.gid = hp->gname;
nd.gid = hdr->gid;
dirfwstat(fd, &nd);
}
if (hdr->uid){
nulldir(&nd);
nd.uid = hp->uname;
nd.uid = hdr->uid;
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.
*/
static void
extract1(int ar, Hdr *hp, char *fname)
extract1(int ar, Blk *bp, Hdr *hdr)
{
int fd = -1, dir = 0;
long mtime = strtol(hp->mtime, nil, 8);
ulong mode = strtoul(hp->mode, nil, 8) & 0777;
Off bytes = hdrsize(hp); /* for printing */
ulong blksleft = BYTES2TBLKS(arsize(hp, fname));
int fd = -1;
Off bytes = hdr->size; /* for printing */
uvlong blksleft = BYTES2TBLKS(bytes);
char *path;
/* fiddle name, figure out mode and blocks */
if (isdir(hp, fname)) {
mode |= DMDIR|0700;
dir = 1;
}
switch (hp->linkflag) {
switch (bp->linkflag) {
case LF_LINK:
case LF_SYMLINK1:
case LF_SYMLINK2:
@ -1162,25 +1374,27 @@ extract1(int ar, Hdr *hp, char *fname)
blksleft = 0;
break;
}
if (relative)
if(fname[0] == '/')
*--fname = '.';
else if(fname[0] == '#'){
*--fname = '/';
*--fname = '.';
}
if((path = malloc(strlen(hdr->name) + 3)) == nil)
sysfatal("malloc: %r");
if (relative && hdr->name[0] == '/')
strcpy(path, ".");
else if(relative && hdr->name[0] == '#')
strcpy(path, "./");
else
path[0] = '\0';
strcat(path, hdr->name);
if (verb == Xtract)
fd = openfname(hp, fname, dir, mode);
fd = openfname(bp, path, hdr->mode);
else if (verbose) {
char *cp = ctime(mtime);
char *cp = ctime(hdr->mtime);
print("%M %8lld %-12.12s %-4.4s %s\n",
mode, bytes, cp+4, cp+24, fname);
print("%M %8lld %-12.12s %-4.4s %s\n",
hdr->mode, bytes, cp+4, cp+24, path);
} 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 */
if (fd >= 0) {
@ -1189,70 +1403,35 @@ extract1(int ar, Hdr *hp, char *fname)
* creating files in them, but we don't do that.
*/
if (settime)
wrmeta(fd, hp, mtime, mode);
wrmeta(fd, hdr);
close(fd);
}
free(path);
}
static void
skip(int ar, Hdr *hp, char *fname)
skip(int ar, Blk *bp, Hdr *hdr)
{
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) {
hbp = getblkrd(ar, Justnxthdr);
if (hbp == nil)
sysfatal("unexpected EOF on archive extracting %s from %s",
fname, arname);
hdr->name, arname);
blksread = gothowmany(blksleft);
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 *
extract(char **argv)
{
int ar;
char *longname;
Hdr *hp;
Blk *bp;
Hdr hdr;
Compress *comp;
Pushstate ps;
@ -1266,14 +1445,12 @@ extract(char **argv)
if (ar < 0)
sysfatal("can't open archive %s: %r", usefile);
while ((hp = readhdr(ar)) != nil) {
longname = getname(ar, hp);
if(longname == nil)
continue;
if (match(longname, argv))
extract1(ar, hp, longname);
while ((bp = readhdr(ar, &hdr)) != nil) {
if (match(hdr.name, argv))
extract1(ar, bp, &hdr);
else
skip(ar, hp, longname);
skip(ar, bp, &hdr);
freehdr(&hdr);
}
if (comp)
@ -1356,6 +1533,7 @@ main(int argc, char *argv[])
usage();
initblks();
nullhdr(&globlhdr);
switch (verb) {
case Toc:
case Xtract: