10fb3f58eSDavid du Colombier /*
20fb3f58eSDavid du Colombier * tar - `tape archiver', actually usable on any medium.
30fb3f58eSDavid du Colombier * POSIX "ustar" compliant when extracting, and by default when creating.
40fb3f58eSDavid du Colombier * this tar attempts to read and write multiple Tblock-byte blocks
50fb3f58eSDavid du Colombier * at once to and from the filesystem, and does not copy blocks
60fb3f58eSDavid du Colombier * around internally.
70fb3f58eSDavid du Colombier */
80fb3f58eSDavid du Colombier
93e12c5d1SDavid du Colombier #include <u.h>
103e12c5d1SDavid du Colombier #include <libc.h>
112ce711d4SDavid du Colombier #include <ctype.h>
120fb3f58eSDavid du Colombier #include <fcall.h> /* for %M */
130fb3f58eSDavid du Colombier #include <String.h>
143e12c5d1SDavid du Colombier
150fb3f58eSDavid du Colombier /*
160fb3f58eSDavid du Colombier * modified versions of those in libc.h; scans only the first arg for
170fb3f58eSDavid du Colombier * keyletters and options.
180fb3f58eSDavid du Colombier */
190fb3f58eSDavid du Colombier #define TARGBEGIN {\
200fb3f58eSDavid du Colombier (argv0 || (argv0 = *argv)), argv++, argc--;\
210fb3f58eSDavid du Colombier if (argv[0]) {\
220fb3f58eSDavid du Colombier char *_args, *_argt;\
230fb3f58eSDavid du Colombier Rune _argc;\
240fb3f58eSDavid du Colombier _args = &argv[0][0];\
250fb3f58eSDavid du Colombier _argc = 0;\
260fb3f58eSDavid du Colombier while(*_args && (_args += chartorune(&_argc, _args)))\
270fb3f58eSDavid du Colombier switch(_argc)
280fb3f58eSDavid du Colombier #define TARGEND SET(_argt); USED(_argt);USED(_argc);USED(_args); \
290fb3f58eSDavid du Colombier argc--, argv++; } \
300fb3f58eSDavid du Colombier USED(argv); USED(argc); }
310fb3f58eSDavid du Colombier #define TARGC() (_argc)
3240ef9009SDavid du Colombier
33ad6ca847SDavid du Colombier #define HOWMANY(a, size) (((a) + (size) - 1) / (size))
34ad6ca847SDavid du Colombier #define BYTES2TBLKS(bytes) HOWMANY(bytes, Tblock)
350fb3f58eSDavid du Colombier
36bbd061d4SDavid du Colombier /* read big-endian binary integers; args must be (uchar *) */
37bbd061d4SDavid du Colombier #define G2BEBYTE(x) (((x)[0]<<8) | (x)[1])
38bbd061d4SDavid du Colombier #define G3BEBYTE(x) (((x)[0]<<16) | ((x)[1]<<8) | (x)[2])
39bbd061d4SDavid du Colombier #define G4BEBYTE(x) (((x)[0]<<24) | ((x)[1]<<16) | ((x)[2]<<8) | (x)[3])
40bbd061d4SDavid du Colombier #define G8BEBYTE(x) (((vlong)G4BEBYTE(x)<<32) | (u32int)G4BEBYTE((x)+4))
41bbd061d4SDavid du Colombier
420fb3f58eSDavid du Colombier typedef vlong Off;
434f281771SDavid du Colombier typedef char *(*Refill)(int ar, char *bufs, int justhdr);
440fb3f58eSDavid du Colombier
450fb3f58eSDavid du Colombier enum { Stdin, Stdout, Stderr };
460fb3f58eSDavid du Colombier enum { Rd, Wr }; /* pipe fd-array indices */
470fb3f58eSDavid du Colombier enum { Output, Input };
480fb3f58eSDavid du Colombier enum { None, Toc, Xtract, Replace };
494f281771SDavid du Colombier enum { Alldata, Justnxthdr };
5040ef9009SDavid du Colombier enum {
510fb3f58eSDavid du Colombier Tblock = 512,
520fb3f58eSDavid du Colombier Namsiz = 100,
5340ef9009SDavid du Colombier Maxpfx = 155, /* from POSIX */
540fb3f58eSDavid du Colombier Maxname = Namsiz + 1 + Maxpfx,
55bbd061d4SDavid du Colombier Binsize = 0x80, /* flag in size[0], from gnu: positive binary size */
56bbd061d4SDavid du Colombier Binnegsz = 0xff, /* flag in size[0]: negative binary size */
57bbd061d4SDavid du Colombier
58bbd061d4SDavid du Colombier Nblock = 40, /* maximum blocksize */
59bbd061d4SDavid du Colombier Dblock = 20, /* default blocksize */
6014cc0f53SDavid du Colombier Debug = 0,
6140ef9009SDavid du Colombier };
6240ef9009SDavid du Colombier
6340ef9009SDavid du Colombier /* POSIX link flags */
6440ef9009SDavid du Colombier enum {
6540ef9009SDavid du Colombier LF_PLAIN1 = '\0',
6640ef9009SDavid du Colombier LF_PLAIN2 = '0',
6740ef9009SDavid du Colombier LF_LINK = '1',
6840ef9009SDavid du Colombier LF_SYMLINK1 = '2',
695437ee90SDavid du Colombier LF_SYMLINK2 = 's', /* 4BSD used this */
7040ef9009SDavid du Colombier LF_CHR = '3',
7140ef9009SDavid du Colombier LF_BLK = '4',
7240ef9009SDavid du Colombier LF_DIR = '5',
7340ef9009SDavid du Colombier LF_FIFO = '6',
7440ef9009SDavid du Colombier LF_CONTIG = '7',
7540ef9009SDavid du Colombier /* 'A' - 'Z' are reserved for custom implementations */
7640ef9009SDavid du Colombier };
7740ef9009SDavid du Colombier
7840ef9009SDavid du Colombier #define islink(lf) (isreallink(lf) || issymlink(lf))
7940ef9009SDavid du Colombier #define isreallink(lf) ((lf) == LF_LINK)
8040ef9009SDavid du Colombier #define issymlink(lf) ((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2)
810fb3f58eSDavid du Colombier
820fb3f58eSDavid du Colombier typedef union {
830fb3f58eSDavid du Colombier uchar data[Tblock];
840fb3f58eSDavid du Colombier struct {
850fb3f58eSDavid du Colombier char name[Namsiz];
863e12c5d1SDavid du Colombier char mode[8];
873e12c5d1SDavid du Colombier char uid[8];
883e12c5d1SDavid du Colombier char gid[8];
893e12c5d1SDavid du Colombier char size[12];
903e12c5d1SDavid du Colombier char mtime[12];
913e12c5d1SDavid du Colombier char chksum[8];
923e12c5d1SDavid du Colombier char linkflag;
930fb3f58eSDavid du Colombier char linkname[Namsiz];
940fb3f58eSDavid du Colombier
9540ef9009SDavid du Colombier /* rest are defined by POSIX's ustar format; see p1003.2b */
9640ef9009SDavid du Colombier char magic[6]; /* "ustar" */
9740ef9009SDavid du Colombier char version[2];
9840ef9009SDavid du Colombier char uname[32];
9940ef9009SDavid du Colombier char gname[32];
10040ef9009SDavid du Colombier char devmajor[8];
10140ef9009SDavid du Colombier char devminor[8];
1020fb3f58eSDavid du Colombier char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */
1030fb3f58eSDavid du Colombier };
1040fb3f58eSDavid du Colombier } Hdr;
1053e12c5d1SDavid du Colombier
1060fb3f58eSDavid du Colombier typedef struct {
1070fb3f58eSDavid du Colombier char *comp;
1080fb3f58eSDavid du Colombier char *decomp;
1090fb3f58eSDavid du Colombier char *sfx[4];
1100fb3f58eSDavid du Colombier } Compress;
1113e12c5d1SDavid du Colombier
1120fb3f58eSDavid du Colombier static Compress comps[] = {
1130fb3f58eSDavid du Colombier "gzip", "gunzip", { ".tar.gz", ".tgz" }, /* default */
1140fb3f58eSDavid du Colombier "compress", "uncompress", { ".tar.Z", ".tz" },
1150fb3f58eSDavid du Colombier "bzip2", "bunzip2", { ".tar.bz", ".tbz",
1160fb3f58eSDavid du Colombier ".tar.bz2",".tbz2" },
1170fb3f58eSDavid du Colombier };
1183e12c5d1SDavid du Colombier
1190fb3f58eSDavid du Colombier typedef struct {
1200fb3f58eSDavid du Colombier int kid;
1210fb3f58eSDavid du Colombier int fd; /* original fd */
1220fb3f58eSDavid du Colombier int rfd; /* replacement fd */
1230fb3f58eSDavid du Colombier int input;
1240fb3f58eSDavid du Colombier int open;
1250fb3f58eSDavid du Colombier } Pushstate;
12640ef9009SDavid du Colombier
1272ce711d4SDavid du Colombier #define OTHER(rdwr) ((rdwr) == Rd? Wr: Rd)
1280fb3f58eSDavid du Colombier
1290fb3f58eSDavid du Colombier static int debug;
13014cc0f53SDavid du Colombier static int fixednblock;
1310fb3f58eSDavid du Colombier static int verb;
1320fb3f58eSDavid du Colombier static int posix = 1;
1330fb3f58eSDavid du Colombier static int docreate;
1340fb3f58eSDavid du Colombier static int aruid;
1350fb3f58eSDavid du Colombier static int argid;
13623605fa3SDavid du Colombier static int relative = 1;
1370fb3f58eSDavid du Colombier static int settime;
1380fb3f58eSDavid du Colombier static int verbose;
1390fb3f58eSDavid du Colombier static int docompress;
1400fb3f58eSDavid du Colombier static int keepexisting;
1414ac22c89SDavid du Colombier static int ignerrs; /* flag: ignore i/o errors if possible */
1423366099cSDavid du Colombier static Off blkoff; /* offset of the current archive block (not Tblock) */
1434f281771SDavid du Colombier static Off nexthdr;
1440fb3f58eSDavid du Colombier
1450fb3f58eSDavid du Colombier static int nblock = Dblock;
1462ce711d4SDavid du Colombier static int resync;
1474ac22c89SDavid du Colombier static char *usefile, *arname = "archive";
1480fb3f58eSDavid du Colombier static char origdir[Maxname*2];
1490fb3f58eSDavid du Colombier static Hdr *tpblk, *endblk;
1500fb3f58eSDavid du Colombier static Hdr *curblk;
1510fb3f58eSDavid du Colombier
1520fb3f58eSDavid du Colombier static void
usage(void)1530fb3f58eSDavid du Colombier usage(void)
15440ef9009SDavid du Colombier {
1552ce711d4SDavid du Colombier fprint(2, "usage: %s {crtx}[PRTfgikmpsuvz] [archive] [file1 file2...]\n",
1560fb3f58eSDavid du Colombier argv0);
1570fb3f58eSDavid du Colombier exits("usage");
1580fb3f58eSDavid du Colombier }
1590fb3f58eSDavid du Colombier
1604ac22c89SDavid du Colombier /* I/O, with error retry or exit */
1614ac22c89SDavid du Colombier
1624ac22c89SDavid du Colombier static int
cope(char * name,int fd,void * buf,long len,Off off)1634ac22c89SDavid du Colombier cope(char *name, int fd, void *buf, long len, Off off)
1644ac22c89SDavid du Colombier {
1654ac22c89SDavid du Colombier fprint(2, "%s: %serror reading %s: %r\n", argv0,
1664ac22c89SDavid du Colombier (ignerrs? "ignoring ": ""), name);
1674ac22c89SDavid du Colombier if (!ignerrs)
1684ac22c89SDavid du Colombier exits("read error");
1694ac22c89SDavid du Colombier
1704ac22c89SDavid du Colombier /* pretend we read len bytes of zeroes */
1714ac22c89SDavid du Colombier memset(buf, 0, len);
1724ac22c89SDavid du Colombier if (off >= 0) /* seekable? */
1734ac22c89SDavid du Colombier seek(fd, off + len, 0);
1744ac22c89SDavid du Colombier return len;
1754ac22c89SDavid du Colombier }
1764ac22c89SDavid du Colombier
1774ac22c89SDavid du Colombier static int
eread(char * name,int fd,void * buf,long len)1784ac22c89SDavid du Colombier eread(char *name, int fd, void *buf, long len)
1794ac22c89SDavid du Colombier {
1804ac22c89SDavid du Colombier int rd;
1814ac22c89SDavid du Colombier Off off;
1824ac22c89SDavid du Colombier
1834ac22c89SDavid du Colombier off = seek(fd, 0, 1); /* for coping with errors */
1844ac22c89SDavid du Colombier rd = read(fd, buf, len);
1854ac22c89SDavid du Colombier if (rd < 0)
1864ac22c89SDavid du Colombier rd = cope(name, fd, buf, len, off);
1874ac22c89SDavid du Colombier return rd;
1884ac22c89SDavid du Colombier }
1894ac22c89SDavid du Colombier
1904ac22c89SDavid du Colombier static int
ereadn(char * name,int fd,void * buf,long len)1914ac22c89SDavid du Colombier ereadn(char *name, int fd, void *buf, long len)
1924ac22c89SDavid du Colombier {
1934ac22c89SDavid du Colombier int rd;
1944ac22c89SDavid du Colombier Off off;
1954ac22c89SDavid du Colombier
1964ac22c89SDavid du Colombier off = seek(fd, 0, 1);
1974ac22c89SDavid du Colombier rd = readn(fd, buf, len);
1984ac22c89SDavid du Colombier if (rd < 0)
1994ac22c89SDavid du Colombier rd = cope(name, fd, buf, len, off);
2004ac22c89SDavid du Colombier return rd;
2014ac22c89SDavid du Colombier }
2024ac22c89SDavid du Colombier
2034ac22c89SDavid du Colombier static int
ewrite(char * name,int fd,void * buf,long len)2044ac22c89SDavid du Colombier ewrite(char *name, int fd, void *buf, long len)
2054ac22c89SDavid du Colombier {
2064ac22c89SDavid du Colombier int rd;
2074ac22c89SDavid du Colombier
2084ac22c89SDavid du Colombier werrstr("");
2094ac22c89SDavid du Colombier rd = write(fd, buf, len);
2104ac22c89SDavid du Colombier if (rd != len)
2114ac22c89SDavid du Colombier sysfatal("error writing %s: %r", name);
2124ac22c89SDavid du Colombier return rd;
2134ac22c89SDavid du Colombier }
2144ac22c89SDavid du Colombier
2150fb3f58eSDavid du Colombier /* compression */
2160fb3f58eSDavid du Colombier
2170fb3f58eSDavid du Colombier static Compress *
compmethod(char * name)2180fb3f58eSDavid du Colombier compmethod(char *name)
2190fb3f58eSDavid du Colombier {
220*70f8d5d2SDavid du Colombier int i, nmlen, sfxlen;
2210fb3f58eSDavid du Colombier Compress *cp;
2220fb3f58eSDavid du Colombier
223*70f8d5d2SDavid du Colombier if (name != nil) {
224*70f8d5d2SDavid du Colombier nmlen = strlen(name);
2250fb3f58eSDavid du Colombier for (cp = comps; cp < comps + nelem(comps); cp++)
2260fb3f58eSDavid du Colombier for (i = 0; i < nelem(cp->sfx) && cp->sfx[i]; i++) {
2270fb3f58eSDavid du Colombier sfxlen = strlen(cp->sfx[i]);
2280fb3f58eSDavid du Colombier if (nmlen > sfxlen &&
2290fb3f58eSDavid du Colombier strcmp(cp->sfx[i], name+nmlen-sfxlen) == 0)
2300fb3f58eSDavid du Colombier return cp;
2310fb3f58eSDavid du Colombier }
232*70f8d5d2SDavid du Colombier }
2330fb3f58eSDavid du Colombier return docompress? comps: nil;
2340fb3f58eSDavid du Colombier }
2350fb3f58eSDavid du Colombier
2360fb3f58eSDavid du Colombier /*
2370fb3f58eSDavid du Colombier * push a filter, cmd, onto fd. if input, it's an input descriptor.
2380fb3f58eSDavid du Colombier * returns a descriptor to replace fd, or -1 on error.
2390fb3f58eSDavid du Colombier */
2400fb3f58eSDavid du Colombier static int
push(int fd,char * cmd,int input,Pushstate * ps)2410fb3f58eSDavid du Colombier push(int fd, char *cmd, int input, Pushstate *ps)
2420fb3f58eSDavid du Colombier {
2430fb3f58eSDavid du Colombier int nfd, pifds[2];
2440fb3f58eSDavid du Colombier String *s;
2450fb3f58eSDavid du Colombier
2460fb3f58eSDavid du Colombier ps->open = 0;
2470fb3f58eSDavid du Colombier ps->fd = fd;
2480fb3f58eSDavid du Colombier ps->input = input;
2490fb3f58eSDavid du Colombier if (fd < 0 || pipe(pifds) < 0)
2500fb3f58eSDavid du Colombier return -1;
2510fb3f58eSDavid du Colombier ps->kid = fork();
2520fb3f58eSDavid du Colombier switch (ps->kid) {
2530fb3f58eSDavid du Colombier case -1:
2540fb3f58eSDavid du Colombier return -1;
2550fb3f58eSDavid du Colombier case 0:
2560fb3f58eSDavid du Colombier if (input)
2570fb3f58eSDavid du Colombier dup(pifds[Wr], Stdout);
2580fb3f58eSDavid du Colombier else
2590fb3f58eSDavid du Colombier dup(pifds[Rd], Stdin);
2600fb3f58eSDavid du Colombier close(pifds[input? Rd: Wr]);
2610fb3f58eSDavid du Colombier dup(fd, (input? Stdin: Stdout));
2620fb3f58eSDavid du Colombier s = s_new();
2630fb3f58eSDavid du Colombier if (cmd[0] != '/')
2640fb3f58eSDavid du Colombier s_append(s, "/bin/");
2650fb3f58eSDavid du Colombier s_append(s, cmd);
2660fb3f58eSDavid du Colombier execl(s_to_c(s), cmd, nil);
2670fb3f58eSDavid du Colombier sysfatal("can't exec %s: %r", cmd);
2680fb3f58eSDavid du Colombier default:
2690fb3f58eSDavid du Colombier nfd = pifds[input? Rd: Wr];
2700fb3f58eSDavid du Colombier close(pifds[input? Wr: Rd]);
2710fb3f58eSDavid du Colombier break;
2720fb3f58eSDavid du Colombier }
2730fb3f58eSDavid du Colombier ps->rfd = nfd;
2740fb3f58eSDavid du Colombier ps->open = 1;
2750fb3f58eSDavid du Colombier return nfd;
2760fb3f58eSDavid du Colombier }
2770fb3f58eSDavid du Colombier
2780fb3f58eSDavid du Colombier static char *
pushclose(Pushstate * ps)2790fb3f58eSDavid du Colombier pushclose(Pushstate *ps)
2800fb3f58eSDavid du Colombier {
2810fb3f58eSDavid du Colombier Waitmsg *wm;
2820fb3f58eSDavid du Colombier
2830fb3f58eSDavid du Colombier if (ps->fd < 0 || ps->rfd < 0 || !ps->open)
2840fb3f58eSDavid du Colombier return "not open";
2850fb3f58eSDavid du Colombier close(ps->rfd);
2860fb3f58eSDavid du Colombier ps->rfd = -1;
2870fb3f58eSDavid du Colombier ps->open = 0;
2880fb3f58eSDavid du Colombier while ((wm = wait()) != nil && wm->pid != ps->kid)
2890fb3f58eSDavid du Colombier continue;
2900fb3f58eSDavid du Colombier return wm? wm->msg: nil;
2910fb3f58eSDavid du Colombier }
2920fb3f58eSDavid du Colombier
2930fb3f58eSDavid du Colombier /*
2940fb3f58eSDavid du Colombier * block-buffer management
2950fb3f58eSDavid du Colombier */
2960fb3f58eSDavid du Colombier
2970fb3f58eSDavid du Colombier static void
initblks(void)2980fb3f58eSDavid du Colombier initblks(void)
2990fb3f58eSDavid du Colombier {
3000fb3f58eSDavid du Colombier free(tpblk);
3010fb3f58eSDavid du Colombier tpblk = malloc(Tblock * nblock);
3020fb3f58eSDavid du Colombier assert(tpblk != nil);
3030fb3f58eSDavid du Colombier endblk = tpblk + nblock;
3040fb3f58eSDavid du Colombier }
3050fb3f58eSDavid du Colombier
3064f281771SDavid du Colombier /*
3074f281771SDavid du Colombier * (re)fill block buffers from archive. `justhdr' means we don't care
3084f281771SDavid du Colombier * about the data before the next header block.
3094f281771SDavid du Colombier */
3100fb3f58eSDavid du Colombier static char *
refill(int ar,char * bufs,int justhdr)3114f281771SDavid du Colombier refill(int ar, char *bufs, int justhdr)
3120fb3f58eSDavid du Colombier {
3130fb3f58eSDavid du Colombier int i, n;
3140fb3f58eSDavid du Colombier unsigned bytes = Tblock * nblock;
3154f281771SDavid du Colombier static int done, first = 1, seekable;
3160fb3f58eSDavid du Colombier
3170fb3f58eSDavid du Colombier if (done)
3180fb3f58eSDavid du Colombier return nil;
3190fb3f58eSDavid du Colombier
32026ad7229SDavid du Colombier blkoff = seek(ar, 0, 1); /* note position for `tar r' */
32114cc0f53SDavid du Colombier if (first)
32214cc0f53SDavid du Colombier seekable = blkoff >= 0;
3230fb3f58eSDavid du Colombier /* try to size non-pipe input at first read */
32414cc0f53SDavid du Colombier if (first && usefile && !fixednblock) {
3254ac22c89SDavid du Colombier n = eread(arname, ar, bufs, bytes);
3264ac22c89SDavid du Colombier if (n == 0)
3272ce711d4SDavid du Colombier sysfatal("EOF reading archive %s: %r", arname);
3280fb3f58eSDavid du Colombier i = n;
32914cc0f53SDavid du Colombier if (i % Tblock != 0)
3302ce711d4SDavid du Colombier sysfatal("%s: archive block size (%d) error", arname, i);
3310fb3f58eSDavid du Colombier i /= Tblock;
3320fb3f58eSDavid du Colombier if (i != nblock) {
3330fb3f58eSDavid du Colombier nblock = i;
3340fb3f58eSDavid du Colombier fprint(2, "%s: blocking = %d\n", argv0, nblock);
3350fb3f58eSDavid du Colombier endblk = (Hdr *)bufs + nblock;
3360fb3f58eSDavid du Colombier bytes = n;
3370fb3f58eSDavid du Colombier }
33814cc0f53SDavid du Colombier } else if (justhdr && seekable && nexthdr - blkoff >= bytes) {
3394f281771SDavid du Colombier /* optimisation for huge archive members on seekable media */
3404f281771SDavid du Colombier if (seek(ar, bytes, 1) < 0)
3412ce711d4SDavid du Colombier sysfatal("can't seek on archive %s: %r", arname);
3424f281771SDavid du Colombier n = bytes;
3430fb3f58eSDavid du Colombier } else
3444ac22c89SDavid du Colombier n = ereadn(arname, ar, bufs, bytes);
3454f281771SDavid du Colombier first = 0;
3464f281771SDavid du Colombier
3470fb3f58eSDavid du Colombier if (n == 0)
3482ce711d4SDavid du Colombier sysfatal("unexpected EOF reading archive %s", arname);
3494ac22c89SDavid du Colombier if (n % Tblock != 0)
3502ce711d4SDavid du Colombier sysfatal("partial block read from archive %s", arname);
3510fb3f58eSDavid du Colombier if (n != bytes) {
3520fb3f58eSDavid du Colombier done = 1;
3530fb3f58eSDavid du Colombier memset(bufs + n, 0, bytes - n);
3540fb3f58eSDavid du Colombier }
3550fb3f58eSDavid du Colombier return bufs;
3560fb3f58eSDavid du Colombier }
3570fb3f58eSDavid du Colombier
3580fb3f58eSDavid du Colombier static Hdr *
getblk(int ar,Refill rfp,int justhdr)3594f281771SDavid du Colombier getblk(int ar, Refill rfp, int justhdr)
3600fb3f58eSDavid du Colombier {
3610fb3f58eSDavid du Colombier if (curblk == nil || curblk >= endblk) { /* input block exhausted? */
3624f281771SDavid du Colombier if (rfp != nil && (*rfp)(ar, (char *)tpblk, justhdr) == nil)
3630fb3f58eSDavid du Colombier return nil;
3640fb3f58eSDavid du Colombier curblk = tpblk;
3650fb3f58eSDavid du Colombier }
3660fb3f58eSDavid du Colombier return curblk++;
3670fb3f58eSDavid du Colombier }
3680fb3f58eSDavid du Colombier
3690fb3f58eSDavid du Colombier static Hdr *
getblkrd(int ar,int justhdr)3704f281771SDavid du Colombier getblkrd(int ar, int justhdr)
3710fb3f58eSDavid du Colombier {
3724f281771SDavid du Colombier return getblk(ar, refill, justhdr);
3730fb3f58eSDavid du Colombier }
3740fb3f58eSDavid du Colombier
3750fb3f58eSDavid du Colombier static Hdr *
getblke(int ar)3760fb3f58eSDavid du Colombier getblke(int ar)
3770fb3f58eSDavid du Colombier {
3784f281771SDavid du Colombier return getblk(ar, nil, Alldata);
3790fb3f58eSDavid du Colombier }
3800fb3f58eSDavid du Colombier
3810fb3f58eSDavid du Colombier static Hdr *
getblkz(int ar)3820fb3f58eSDavid du Colombier getblkz(int ar)
3830fb3f58eSDavid du Colombier {
3840fb3f58eSDavid du Colombier Hdr *hp = getblke(ar);
3850fb3f58eSDavid du Colombier
3860fb3f58eSDavid du Colombier if (hp != nil)
3870fb3f58eSDavid du Colombier memset(hp->data, 0, Tblock);
3880fb3f58eSDavid du Colombier return hp;
3890fb3f58eSDavid du Colombier }
3900fb3f58eSDavid du Colombier
3910fb3f58eSDavid du Colombier /*
3920fb3f58eSDavid du Colombier * how many block buffers are available, starting at the address
3930fb3f58eSDavid du Colombier * just returned by getblk*?
3940fb3f58eSDavid du Colombier */
3950fb3f58eSDavid du Colombier static int
gothowmany(int max)3960fb3f58eSDavid du Colombier gothowmany(int max)
3970fb3f58eSDavid du Colombier {
3980fb3f58eSDavid du Colombier int n = endblk - (curblk - 1);
3990fb3f58eSDavid du Colombier
4000fb3f58eSDavid du Colombier return n > max? max: n;
4010fb3f58eSDavid du Colombier }
4020fb3f58eSDavid du Colombier
4030fb3f58eSDavid du Colombier /*
4040fb3f58eSDavid du Colombier * indicate that one is done with the last block obtained from getblke
4050fb3f58eSDavid du Colombier * and it is now available to be written into the archive.
4060fb3f58eSDavid du Colombier */
4070fb3f58eSDavid du Colombier static void
putlastblk(int ar)4080fb3f58eSDavid du Colombier putlastblk(int ar)
4090fb3f58eSDavid du Colombier {
4100fb3f58eSDavid du Colombier unsigned bytes = Tblock * nblock;
4110fb3f58eSDavid du Colombier
4120fb3f58eSDavid du Colombier /* if writing end-of-archive, aid compression (good hygiene too) */
4130fb3f58eSDavid du Colombier if (curblk < endblk)
4140fb3f58eSDavid du Colombier memset(curblk, 0, (char *)endblk - (char *)curblk);
4154ac22c89SDavid du Colombier ewrite(arname, ar, tpblk, bytes);
41640ef9009SDavid du Colombier }
41740ef9009SDavid du Colombier
41840ef9009SDavid du Colombier static void
putblk(int ar)4190fb3f58eSDavid du Colombier putblk(int ar)
42040ef9009SDavid du Colombier {
4210fb3f58eSDavid du Colombier if (curblk >= endblk)
4220fb3f58eSDavid du Colombier putlastblk(ar);
4230fb3f58eSDavid du Colombier }
4240fb3f58eSDavid du Colombier
4250fb3f58eSDavid du Colombier static void
putbackblk(int ar)4260fb3f58eSDavid du Colombier putbackblk(int ar)
4270fb3f58eSDavid du Colombier {
4280fb3f58eSDavid du Colombier curblk--;
4290fb3f58eSDavid du Colombier USED(ar);
4300fb3f58eSDavid du Colombier }
4310fb3f58eSDavid du Colombier
4320fb3f58eSDavid du Colombier static void
putreadblks(int ar,int blks)4330fb3f58eSDavid du Colombier putreadblks(int ar, int blks)
4340fb3f58eSDavid du Colombier {
4350fb3f58eSDavid du Colombier curblk += blks - 1;
4360fb3f58eSDavid du Colombier USED(ar);
4370fb3f58eSDavid du Colombier }
4380fb3f58eSDavid du Colombier
4390fb3f58eSDavid du Colombier static void
putblkmany(int ar,int blks)4400fb3f58eSDavid du Colombier putblkmany(int ar, int blks)
4410fb3f58eSDavid du Colombier {
4424ac22c89SDavid du Colombier assert(blks > 0);
4430fb3f58eSDavid du Colombier curblk += blks - 1;
4440fb3f58eSDavid du Colombier putblk(ar);
4450fb3f58eSDavid du Colombier }
4460fb3f58eSDavid du Colombier
4470fb3f58eSDavid du Colombier /*
4480fb3f58eSDavid du Colombier * common routines
4490fb3f58eSDavid du Colombier */
4500fb3f58eSDavid du Colombier
4513366099cSDavid du Colombier /*
4523366099cSDavid du Colombier * modifies hp->chksum but restores it; important for the last block of the
4533366099cSDavid du Colombier * old archive when updating with `tar rf archive'
4543366099cSDavid du Colombier */
455bbd061d4SDavid du Colombier static long
chksum(Hdr * hp)4560fb3f58eSDavid du Colombier chksum(Hdr *hp)
4570fb3f58eSDavid du Colombier {
4580fb3f58eSDavid du Colombier int n = Tblock;
4590fb3f58eSDavid du Colombier long i = 0;
4600fb3f58eSDavid du Colombier uchar *cp = hp->data;
4613366099cSDavid du Colombier char oldsum[sizeof hp->chksum];
4620fb3f58eSDavid du Colombier
4633366099cSDavid du Colombier memmove(oldsum, hp->chksum, sizeof oldsum);
4640fb3f58eSDavid du Colombier memset(hp->chksum, ' ', sizeof hp->chksum);
4650fb3f58eSDavid du Colombier while (n-- > 0)
4660fb3f58eSDavid du Colombier i += *cp++;
4673366099cSDavid du Colombier memmove(hp->chksum, oldsum, sizeof oldsum);
4680fb3f58eSDavid du Colombier return i;
4690fb3f58eSDavid du Colombier }
4700fb3f58eSDavid du Colombier
4710fb3f58eSDavid du Colombier static int
isustar(Hdr * hp)4720fb3f58eSDavid du Colombier isustar(Hdr *hp)
4730fb3f58eSDavid du Colombier {
4740fb3f58eSDavid du Colombier return strcmp(hp->magic, "ustar") == 0;
47540ef9009SDavid du Colombier }
47640ef9009SDavid du Colombier
47740ef9009SDavid du Colombier /*
47840ef9009SDavid du Colombier * s is at most n bytes long, but need not be NUL-terminated.
47940ef9009SDavid du Colombier * if shorter than n bytes, all bytes after the first NUL must also
48040ef9009SDavid du Colombier * be NUL.
48140ef9009SDavid du Colombier */
48240ef9009SDavid du Colombier static int
strnlen(char * s,int n)48340ef9009SDavid du Colombier strnlen(char *s, int n)
48440ef9009SDavid du Colombier {
4850fb3f58eSDavid du Colombier return s[n - 1] != '\0'? n: strlen(s);
48640ef9009SDavid du Colombier }
48740ef9009SDavid du Colombier
4880fb3f58eSDavid du Colombier /* set fullname from header */
4890fb3f58eSDavid du Colombier static char *
name(Hdr * hp)4900fb3f58eSDavid du Colombier name(Hdr *hp)
49140ef9009SDavid du Colombier {
49240ef9009SDavid du Colombier int pfxlen, namlen;
49323605fa3SDavid du Colombier char *fullname;
4942ce711d4SDavid du Colombier static char fullnamebuf[2+Maxname+1]; /* 2+ for ./ on relative names */
49540ef9009SDavid du Colombier
49623605fa3SDavid du Colombier fullname = fullnamebuf+2;
49740ef9009SDavid du Colombier namlen = strnlen(hp->name, sizeof hp->name);
4980fb3f58eSDavid du Colombier if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */
49940ef9009SDavid du Colombier memmove(fullname, hp->name, namlen);
50040ef9009SDavid du Colombier fullname[namlen] = '\0';
5010fb3f58eSDavid du Colombier return fullname;
50240ef9009SDavid du Colombier }
5030fb3f58eSDavid du Colombier
5040fb3f58eSDavid du Colombier /* name is in two pieces */
50540ef9009SDavid du Colombier pfxlen = strnlen(hp->prefix, sizeof hp->prefix);
50640ef9009SDavid du Colombier memmove(fullname, hp->prefix, pfxlen);
50740ef9009SDavid du Colombier fullname[pfxlen] = '/';
50840ef9009SDavid du Colombier memmove(fullname + pfxlen + 1, hp->name, namlen);
50940ef9009SDavid du Colombier fullname[pfxlen + 1 + namlen] = '\0';
5100fb3f58eSDavid du Colombier return fullname;
5110fb3f58eSDavid du Colombier }
5120fb3f58eSDavid du Colombier
5130fb3f58eSDavid du Colombier static int
isdir(Hdr * hp)5140fb3f58eSDavid du Colombier isdir(Hdr *hp)
5150fb3f58eSDavid du Colombier {
5160fb3f58eSDavid du Colombier /* the mode test is ugly but sometimes necessary */
5170fb3f58eSDavid du Colombier return hp->linkflag == LF_DIR ||
5180fb3f58eSDavid du Colombier strrchr(name(hp), '\0')[-1] == '/' ||
5190fb3f58eSDavid du Colombier (strtoul(hp->mode, nil, 8)&0170000) == 040000;
5200fb3f58eSDavid du Colombier }
5210fb3f58eSDavid du Colombier
5220fb3f58eSDavid du Colombier static int
eotar(Hdr * hp)5230fb3f58eSDavid du Colombier eotar(Hdr *hp)
5240fb3f58eSDavid du Colombier {
5250fb3f58eSDavid du Colombier return name(hp)[0] == '\0';
5260fb3f58eSDavid du Colombier }
5270fb3f58eSDavid du Colombier
528bbd061d4SDavid du Colombier /*
529bbd061d4SDavid du Colombier static uvlong
530bbd061d4SDavid du Colombier getbe(uchar *src, int size)
531bbd061d4SDavid du Colombier {
532bbd061d4SDavid du Colombier uvlong vl = 0;
533bbd061d4SDavid du Colombier
534bbd061d4SDavid du Colombier while (size-- > 0) {
535bbd061d4SDavid du Colombier vl <<= 8;
536bbd061d4SDavid du Colombier vl |= *src++;
537bbd061d4SDavid du Colombier }
538bbd061d4SDavid du Colombier return vl;
539bbd061d4SDavid du Colombier }
540bbd061d4SDavid du Colombier */
541bbd061d4SDavid du Colombier
542bbd061d4SDavid du Colombier static void
putbe(uchar * dest,uvlong vl,int size)543bbd061d4SDavid du Colombier putbe(uchar *dest, uvlong vl, int size)
544bbd061d4SDavid du Colombier {
545bbd061d4SDavid du Colombier for (dest += size; size-- > 0; vl >>= 8)
546bbd061d4SDavid du Colombier *--dest = vl;
547bbd061d4SDavid du Colombier }
548bbd061d4SDavid du Colombier
549bbd061d4SDavid du Colombier /*
5502ce711d4SDavid du Colombier * cautious parsing of octal numbers as ascii strings in
5512ce711d4SDavid du Colombier * a tar header block. this is particularly important for
5522ce711d4SDavid du Colombier * trusting the checksum when trying to resync.
5532ce711d4SDavid du Colombier */
5542ce711d4SDavid du Colombier static uvlong
hdrotoull(char * st,char * end,uvlong errval,char * name,char * field)5552ce711d4SDavid du Colombier hdrotoull(char *st, char *end, uvlong errval, char *name, char *field)
5562ce711d4SDavid du Colombier {
5572ce711d4SDavid du Colombier char *numb;
5582ce711d4SDavid du Colombier
5592ce711d4SDavid du Colombier for (numb = st; (*numb == ' ' || *numb == '\0') && numb < end; numb++)
5602ce711d4SDavid du Colombier ;
5612ce711d4SDavid du Colombier if (numb < end && isascii(*numb) && isdigit(*numb))
5622ce711d4SDavid du Colombier return strtoull(numb, nil, 8);
5632ce711d4SDavid du Colombier else if (numb >= end)
5642ce711d4SDavid du Colombier fprint(2, "%s: %s: empty %s in header\n", argv0, name, field);
5652ce711d4SDavid du Colombier else
5662ce711d4SDavid du Colombier fprint(2, "%s: %s: %s: non-numeric %s in header\n",
5672ce711d4SDavid du Colombier argv0, name, numb, field);
5682ce711d4SDavid du Colombier return errval;
5692ce711d4SDavid du Colombier }
5702ce711d4SDavid du Colombier
5712ce711d4SDavid du Colombier /*
572bbd061d4SDavid du Colombier * return the nominal size from the header block, which is not always the
573bbd061d4SDavid du Colombier * size in the archive (the archive size may be zero for some file types
574bbd061d4SDavid du Colombier * regardless of the nominal size).
575bbd061d4SDavid du Colombier *
576bbd061d4SDavid du Colombier * gnu and freebsd tars are now recording vlongs as big-endian binary
577bbd061d4SDavid du Colombier * with a flag in byte 0 to indicate this, which permits file sizes up to
578bbd061d4SDavid du Colombier * 2^64-1 (actually 2^80-1 but our file sizes are vlongs) rather than 2^33-1.
579bbd061d4SDavid du Colombier */
580bbd061d4SDavid du Colombier static Off
hdrsize(Hdr * hp)5814f281771SDavid du Colombier hdrsize(Hdr *hp)
5824f281771SDavid du Colombier {
583bbd061d4SDavid du Colombier uchar *p;
5844f281771SDavid du Colombier
585bbd061d4SDavid du Colombier if((uchar)hp->size[0] == Binnegsz) {
586bbd061d4SDavid du Colombier fprint(2, "%s: %s: negative length, which is insane\n",
587bbd061d4SDavid du Colombier argv0, name(hp));
588bbd061d4SDavid du Colombier return 0;
589bbd061d4SDavid du Colombier } else if((uchar)hp->size[0] == Binsize) {
590bbd061d4SDavid du Colombier p = (uchar *)hp->size + sizeof hp->size - 1 -
591bbd061d4SDavid du Colombier sizeof(vlong); /* -1 for terminating space */
592bbd061d4SDavid du Colombier return G8BEBYTE(p);
5932ce711d4SDavid du Colombier }
5942ce711d4SDavid du Colombier
5952ce711d4SDavid du Colombier return hdrotoull(hp->size, hp->size + sizeof hp->size, 0,
5962ce711d4SDavid du Colombier name(hp), "size");
597bbd061d4SDavid du Colombier }
598bbd061d4SDavid du Colombier
599bbd061d4SDavid du Colombier /*
600bbd061d4SDavid du Colombier * return the number of bytes recorded in the archive.
601bbd061d4SDavid du Colombier */
602bbd061d4SDavid du Colombier static Off
arsize(Hdr * hp)603bbd061d4SDavid du Colombier arsize(Hdr *hp)
604bbd061d4SDavid du Colombier {
6055437ee90SDavid du Colombier if(isdir(hp) || islink(hp->linkflag))
606bbd061d4SDavid du Colombier return 0;
607bbd061d4SDavid du Colombier return hdrsize(hp);
6084f281771SDavid du Colombier }
6094f281771SDavid du Colombier
6102ce711d4SDavid du Colombier static long
parsecksum(char * cksum,char * name)6112ce711d4SDavid du Colombier parsecksum(char *cksum, char *name)
6122ce711d4SDavid du Colombier {
6132ce711d4SDavid du Colombier Hdr *hp;
6142ce711d4SDavid du Colombier
6152ce711d4SDavid du Colombier return hdrotoull(cksum, cksum + sizeof hp->chksum, (uvlong)-1LL,
6162ce711d4SDavid du Colombier name, "checksum");
6172ce711d4SDavid du Colombier }
6182ce711d4SDavid du Colombier
6190fb3f58eSDavid du Colombier static Hdr *
readhdr(int ar)6200fb3f58eSDavid du Colombier readhdr(int ar)
6210fb3f58eSDavid du Colombier {
6220fb3f58eSDavid du Colombier long hdrcksum;
6230fb3f58eSDavid du Colombier Hdr *hp;
6240fb3f58eSDavid du Colombier
6254f281771SDavid du Colombier hp = getblkrd(ar, Alldata);
6260fb3f58eSDavid du Colombier if (hp == nil)
6272ce711d4SDavid du Colombier sysfatal("unexpected EOF instead of archive header in %s",
6282ce711d4SDavid du Colombier arname);
6290fb3f58eSDavid du Colombier if (eotar(hp)) /* end-of-archive block? */
6300fb3f58eSDavid du Colombier return nil;
6312ce711d4SDavid du Colombier
6322ce711d4SDavid du Colombier hdrcksum = parsecksum(hp->chksum, name(hp));
6332ce711d4SDavid du Colombier if (hdrcksum == -1 || chksum(hp) != hdrcksum) {
6342ce711d4SDavid du Colombier if (!resync)
6352ce711d4SDavid du Colombier sysfatal("bad archive header checksum in %s: "
6362ce711d4SDavid du Colombier "name %.100s...; expected %#luo got %#luo",
6372ce711d4SDavid du Colombier arname, hp->name, hdrcksum, chksum(hp));
6382ce711d4SDavid du Colombier fprint(2, "%s: skipping past archive header with bad checksum in %s...",
6392ce711d4SDavid du Colombier argv0, arname);
6402ce711d4SDavid du Colombier do {
6412ce711d4SDavid du Colombier hp = getblkrd(ar, Alldata);
6422ce711d4SDavid du Colombier if (hp == nil)
6432ce711d4SDavid du Colombier sysfatal("unexpected EOF looking for archive header in %s",
6442ce711d4SDavid du Colombier arname);
6452ce711d4SDavid du Colombier hdrcksum = parsecksum(hp->chksum, name(hp));
6462ce711d4SDavid du Colombier } while (hdrcksum == -1 || chksum(hp) != hdrcksum);
6472ce711d4SDavid du Colombier fprint(2, "found %s\n", name(hp));
6482ce711d4SDavid du Colombier }
649bbd061d4SDavid du Colombier nexthdr += Tblock*(1 + BYTES2TBLKS(arsize(hp)));
6500fb3f58eSDavid du Colombier return hp;
65140ef9009SDavid du Colombier }
65240ef9009SDavid du Colombier
65340ef9009SDavid du Colombier /*
6540fb3f58eSDavid du Colombier * tar r[c]
6550fb3f58eSDavid du Colombier */
6560fb3f58eSDavid du Colombier
6570fb3f58eSDavid du Colombier /*
6580fb3f58eSDavid du Colombier * if name is longer than Namsiz bytes, try to split it at a slash and fit the
65940ef9009SDavid du Colombier * pieces into hp->prefix and hp->name.
66040ef9009SDavid du Colombier */
66140ef9009SDavid du Colombier static int
putfullname(Hdr * hp,char * name)6620fb3f58eSDavid du Colombier putfullname(Hdr *hp, char *name)
66340ef9009SDavid du Colombier {
66440ef9009SDavid du Colombier int namlen, pfxlen;
66540ef9009SDavid du Colombier char *sl, *osl;
6660fb3f58eSDavid du Colombier String *slname = nil;
6670fb3f58eSDavid du Colombier
6680fb3f58eSDavid du Colombier if (isdir(hp)) {
6690fb3f58eSDavid du Colombier slname = s_new();
6700fb3f58eSDavid du Colombier s_append(slname, name);
6710fb3f58eSDavid du Colombier s_append(slname, "/"); /* posix requires this */
6720fb3f58eSDavid du Colombier name = s_to_c(slname);
6730fb3f58eSDavid du Colombier }
67440ef9009SDavid du Colombier
67540ef9009SDavid du Colombier namlen = strlen(name);
6760fb3f58eSDavid du Colombier if (namlen <= Namsiz) {
6770fb3f58eSDavid du Colombier strncpy(hp->name, name, Namsiz);
67840ef9009SDavid du Colombier hp->prefix[0] = '\0'; /* ustar paranoia */
67940ef9009SDavid du Colombier return 0;
68040ef9009SDavid du Colombier }
6810fb3f58eSDavid du Colombier
6820fb3f58eSDavid du Colombier if (!posix || namlen > Maxname) {
6830fb3f58eSDavid du Colombier fprint(2, "%s: name too long for tar header: %s\n",
6840fb3f58eSDavid du Colombier argv0, name);
68540ef9009SDavid du Colombier return -1;
68640ef9009SDavid du Colombier }
68740ef9009SDavid du Colombier /*
68840ef9009SDavid du Colombier * try various splits until one results in pieces that fit into the
68940ef9009SDavid du Colombier * appropriate fields of the header. look for slashes from right
69040ef9009SDavid du Colombier * to left, in the hopes of putting the largest part of the name into
69140ef9009SDavid du Colombier * hp->prefix, which is larger than hp->name.
69240ef9009SDavid du Colombier */
69340ef9009SDavid du Colombier sl = strrchr(name, '/');
69440ef9009SDavid du Colombier while (sl != nil) {
69540ef9009SDavid du Colombier pfxlen = sl - name;
6960fb3f58eSDavid du Colombier if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= Namsiz)
69740ef9009SDavid du Colombier break;
69840ef9009SDavid du Colombier osl = sl;
69940ef9009SDavid du Colombier *osl = '\0';
70040ef9009SDavid du Colombier sl = strrchr(name, '/');
70140ef9009SDavid du Colombier *osl = '/';
70240ef9009SDavid du Colombier }
70340ef9009SDavid du Colombier if (sl == nil) {
7040fb3f58eSDavid du Colombier fprint(2, "%s: name can't be split to fit tar header: %s\n",
7050fb3f58eSDavid du Colombier argv0, name);
70640ef9009SDavid du Colombier return -1;
70740ef9009SDavid du Colombier }
70840ef9009SDavid du Colombier *sl = '\0';
70940ef9009SDavid du Colombier strncpy(hp->prefix, name, sizeof hp->prefix);
7100fb3f58eSDavid du Colombier *sl++ = '/';
7110fb3f58eSDavid du Colombier strncpy(hp->name, sl, sizeof hp->name);
7120fb3f58eSDavid du Colombier if (slname)
7130fb3f58eSDavid du Colombier s_free(slname);
71440ef9009SDavid du Colombier return 0;
71540ef9009SDavid du Colombier }
71640ef9009SDavid du Colombier
7170fb3f58eSDavid du Colombier static int
mkhdr(Hdr * hp,Dir * dir,char * file)7180fb3f58eSDavid du Colombier mkhdr(Hdr *hp, Dir *dir, char *file)
7193e12c5d1SDavid du Colombier {
7203a189962SDavid du Colombier int r;
7213a189962SDavid du Colombier
7220fb3f58eSDavid du Colombier /*
7234ac22c89SDavid du Colombier * some of these fields run together, so we format them left-to-right
7244ac22c89SDavid du Colombier * and don't use snprint.
7250fb3f58eSDavid du Colombier */
7260fb3f58eSDavid du Colombier sprint(hp->mode, "%6lo ", dir->mode & 0777);
7270fb3f58eSDavid du Colombier sprint(hp->uid, "%6o ", aruid);
7280fb3f58eSDavid du Colombier sprint(hp->gid, "%6o ", argid);
729bbd061d4SDavid du Colombier if (dir->length >= (Off)1<<32) {
730bbd061d4SDavid du Colombier static int printed;
731bbd061d4SDavid du Colombier
732bbd061d4SDavid du Colombier if (!printed) {
733bbd061d4SDavid du Colombier printed = 1;
734bbd061d4SDavid du Colombier fprint(2, "%s: storing large sizes in \"base 256\"\n", argv0);
7350fb3f58eSDavid du Colombier }
736bbd061d4SDavid du Colombier hp->size[0] = Binsize;
737bbd061d4SDavid du Colombier /* emit so-called `base 256' representation of size */
738bbd061d4SDavid du Colombier putbe((uchar *)hp->size+1, dir->length, sizeof hp->size - 2);
739bbd061d4SDavid du Colombier hp->size[sizeof hp->size - 1] = ' ';
740bbd061d4SDavid du Colombier } else
7410fb3f58eSDavid du Colombier sprint(hp->size, "%11lluo ", dir->length);
7420fb3f58eSDavid du Colombier sprint(hp->mtime, "%11luo ", dir->mtime);
7430fb3f58eSDavid du Colombier hp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1);
7443a189962SDavid du Colombier r = putfullname(hp, file);
7450fb3f58eSDavid du Colombier if (posix) {
7460fb3f58eSDavid du Colombier strncpy(hp->magic, "ustar", sizeof hp->magic);
7470fb3f58eSDavid du Colombier strncpy(hp->version, "00", sizeof hp->version);
7480fb3f58eSDavid du Colombier strncpy(hp->uname, dir->uid, sizeof hp->uname);
7490fb3f58eSDavid du Colombier strncpy(hp->gname, dir->gid, sizeof hp->gname);
7500fb3f58eSDavid du Colombier }
7510fb3f58eSDavid du Colombier sprint(hp->chksum, "%6luo", chksum(hp));
7523a189962SDavid du Colombier return r;
7533e12c5d1SDavid du Colombier }
7543e12c5d1SDavid du Colombier
7550fb3f58eSDavid du Colombier static void addtoar(int ar, char *file, char *shortf);
7563e12c5d1SDavid du Colombier
7570fb3f58eSDavid du Colombier static void
addtreetoar(int ar,char * file,char * shortf,int fd)7580fb3f58eSDavid du Colombier addtreetoar(int ar, char *file, char *shortf, int fd)
7593e12c5d1SDavid du Colombier {
7600fb3f58eSDavid du Colombier int n;
7610fb3f58eSDavid du Colombier Dir *dent, *dirents;
7620fb3f58eSDavid du Colombier String *name = s_new();
7633e12c5d1SDavid du Colombier
7640fb3f58eSDavid du Colombier n = dirreadall(fd, &dirents);
765c6f8b3dbSDavid du Colombier if (n < 0)
766c6f8b3dbSDavid du Colombier fprint(2, "%s: dirreadall %s: %r\n", argv0, file);
7670fb3f58eSDavid du Colombier close(fd);
768c6f8b3dbSDavid du Colombier if (n <= 0)
7693e12c5d1SDavid du Colombier return;
7700fb3f58eSDavid du Colombier
7710fb3f58eSDavid du Colombier if (chdir(shortf) < 0)
7720fb3f58eSDavid du Colombier sysfatal("chdir %s: %r", file);
77314cc0f53SDavid du Colombier if (Debug)
7740fb3f58eSDavid du Colombier fprint(2, "chdir %s\t# %s\n", shortf, file);
7750fb3f58eSDavid du Colombier
7760fb3f58eSDavid du Colombier for (dent = dirents; dent < dirents + n; dent++) {
7770fb3f58eSDavid du Colombier s_reset(name);
7780fb3f58eSDavid du Colombier s_append(name, file);
7790fb3f58eSDavid du Colombier s_append(name, "/");
7800fb3f58eSDavid du Colombier s_append(name, dent->name);
7810fb3f58eSDavid du Colombier addtoar(ar, s_to_c(name), dent->name);
7829a747e4fSDavid du Colombier }
7830fb3f58eSDavid du Colombier s_free(name);
7840fb3f58eSDavid du Colombier free(dirents);
7850fb3f58eSDavid du Colombier
786f3703655SDavid du Colombier /*
787f3703655SDavid du Colombier * this assumes that shortf is just one component, which is true
788f3703655SDavid du Colombier * during directory descent, but not necessarily true of command-line
789f3703655SDavid du Colombier * arguments. Our caller (or addtoar's) must reset the working
790f3703655SDavid du Colombier * directory if necessary.
791f3703655SDavid du Colombier */
7920fb3f58eSDavid du Colombier if (chdir("..") < 0)
7930fb3f58eSDavid du Colombier sysfatal("chdir %s/..: %r", file);
79414cc0f53SDavid du Colombier if (Debug)
7950fb3f58eSDavid du Colombier fprint(2, "chdir ..\n");
7963e12c5d1SDavid du Colombier }
7973e12c5d1SDavid du Colombier
7980fb3f58eSDavid du Colombier static void
addtoar(int ar,char * file,char * shortf)7990fb3f58eSDavid du Colombier addtoar(int ar, char *file, char *shortf)
8003e12c5d1SDavid du Colombier {
8010fb3f58eSDavid du Colombier int n, fd, isdir;
8024ac22c89SDavid du Colombier long bytes, blksread;
8034ac22c89SDavid du Colombier ulong blksleft;
8040fb3f58eSDavid du Colombier Hdr *hbp;
8050fb3f58eSDavid du Colombier Dir *dir;
806859ebcadSDavid du Colombier String *name = nil;
807859ebcadSDavid du Colombier
808859ebcadSDavid du Colombier if (shortf[0] == '#') {
809859ebcadSDavid du Colombier name = s_new();
810859ebcadSDavid du Colombier s_append(name, "./");
811859ebcadSDavid du Colombier s_append(name, shortf);
812859ebcadSDavid du Colombier shortf = s_to_c(name);
813859ebcadSDavid du Colombier }
8143e12c5d1SDavid du Colombier
81514cc0f53SDavid du Colombier if (Debug)
81614cc0f53SDavid du Colombier fprint(2, "opening %s # %s\n", shortf, file);
8170fb3f58eSDavid du Colombier fd = open(shortf, OREAD);
8180fb3f58eSDavid du Colombier if (fd < 0) {
8190fb3f58eSDavid du Colombier fprint(2, "%s: can't open %s: %r\n", argv0, file);
820859ebcadSDavid du Colombier if (name)
821859ebcadSDavid du Colombier s_free(name);
8220fb3f58eSDavid du Colombier return;
8230fb3f58eSDavid du Colombier }
8240fb3f58eSDavid du Colombier dir = dirfstat(fd);
8250fb3f58eSDavid du Colombier if (dir == nil)
8260fb3f58eSDavid du Colombier sysfatal("can't fstat %s: %r", file);
8270fb3f58eSDavid du Colombier
8280fb3f58eSDavid du Colombier hbp = getblkz(ar);
82914cc0f53SDavid du Colombier isdir = (dir->qid.type & QTDIR) != 0;
8300fb3f58eSDavid du Colombier if (mkhdr(hbp, dir, file) < 0) {
8310fb3f58eSDavid du Colombier putbackblk(ar);
8320fb3f58eSDavid du Colombier free(dir);
8330fb3f58eSDavid du Colombier close(fd);
834859ebcadSDavid du Colombier if (name)
835859ebcadSDavid du Colombier s_free(name);
8360fb3f58eSDavid du Colombier return;
8370fb3f58eSDavid du Colombier }
8380fb3f58eSDavid du Colombier putblk(ar);
8390fb3f58eSDavid du Colombier
8400fb3f58eSDavid du Colombier blksleft = BYTES2TBLKS(dir->length);
8410fb3f58eSDavid du Colombier free(dir);
8420fb3f58eSDavid du Colombier
8430fb3f58eSDavid du Colombier if (isdir)
8440fb3f58eSDavid du Colombier addtreetoar(ar, file, shortf, fd);
8450fb3f58eSDavid du Colombier else {
8460fb3f58eSDavid du Colombier for (; blksleft > 0; blksleft -= blksread) {
8470fb3f58eSDavid du Colombier hbp = getblke(ar);
8480fb3f58eSDavid du Colombier blksread = gothowmany(blksleft);
8494ac22c89SDavid du Colombier assert(blksread >= 0);
8500fb3f58eSDavid du Colombier bytes = blksread * Tblock;
8514ac22c89SDavid du Colombier n = ereadn(file, fd, hbp->data, bytes);
8524ac22c89SDavid du Colombier assert(n >= 0);
8530fb3f58eSDavid du Colombier /*
8540fb3f58eSDavid du Colombier * ignore EOF. zero any partial block to aid
8550fb3f58eSDavid du Colombier * compression and emergency recovery of data.
8560fb3f58eSDavid du Colombier */
8570fb3f58eSDavid du Colombier if (n < Tblock)
8580fb3f58eSDavid du Colombier memset(hbp->data + n, 0, bytes - n);
8590fb3f58eSDavid du Colombier putblkmany(ar, blksread);
8600fb3f58eSDavid du Colombier }
8610fb3f58eSDavid du Colombier close(fd);
8620fb3f58eSDavid du Colombier if (verbose)
8630fb3f58eSDavid du Colombier fprint(2, "%s\n", file);
8640fb3f58eSDavid du Colombier }
865859ebcadSDavid du Colombier if (name)
866859ebcadSDavid du Colombier s_free(name);
8670fb3f58eSDavid du Colombier }
8680fb3f58eSDavid du Colombier
869*70f8d5d2SDavid du Colombier static void
skip(int ar,Hdr * hp,char * msg)870*70f8d5d2SDavid du Colombier skip(int ar, Hdr *hp, char *msg)
8710fb3f58eSDavid du Colombier {
8720fb3f58eSDavid du Colombier ulong blksleft, blksread;
8730fb3f58eSDavid du Colombier Off bytes;
8740fb3f58eSDavid du Colombier
875bbd061d4SDavid du Colombier bytes = arsize(hp);
876*70f8d5d2SDavid du Colombier for (blksleft = BYTES2TBLKS(bytes); blksleft > 0; blksleft -= blksread) {
877*70f8d5d2SDavid du Colombier if (getblkrd(ar, Justnxthdr) == nil)
878*70f8d5d2SDavid du Colombier sysfatal("unexpected EOF on archive %s %s", arname, msg);
8790fb3f58eSDavid du Colombier blksread = gothowmany(blksleft);
8800fb3f58eSDavid du Colombier putreadblks(ar, blksread);
8810fb3f58eSDavid du Colombier }
8820fb3f58eSDavid du Colombier }
883*70f8d5d2SDavid du Colombier
884*70f8d5d2SDavid du Colombier static void
skiptoend(int ar)885*70f8d5d2SDavid du Colombier skiptoend(int ar)
886*70f8d5d2SDavid du Colombier {
887*70f8d5d2SDavid du Colombier Hdr *hp;
888*70f8d5d2SDavid du Colombier
889*70f8d5d2SDavid du Colombier while ((hp = readhdr(ar)) != nil)
890*70f8d5d2SDavid du Colombier skip(ar, hp, "skipping to end");
891*70f8d5d2SDavid du Colombier
8920fb3f58eSDavid du Colombier /*
8930fb3f58eSDavid du Colombier * we have just read the end-of-archive Tblock.
8940fb3f58eSDavid du Colombier * now seek back over the (big) archive block containing it,
8950fb3f58eSDavid du Colombier * and back up curblk ptr over end-of-archive Tblock in memory.
8960fb3f58eSDavid du Colombier */
8973366099cSDavid du Colombier if (seek(ar, blkoff, 0) < 0)
898*70f8d5d2SDavid du Colombier sysfatal("can't seek back over end-of-archive in %s: %r", arname);
8990fb3f58eSDavid du Colombier curblk--;
9000fb3f58eSDavid du Colombier }
9010fb3f58eSDavid du Colombier
902*70f8d5d2SDavid du Colombier static char *
replace(char ** argv)903*70f8d5d2SDavid du Colombier replace(char **argv)
904*70f8d5d2SDavid du Colombier {
905*70f8d5d2SDavid du Colombier int i, ar;
906*70f8d5d2SDavid du Colombier char *arg;
907*70f8d5d2SDavid du Colombier Compress *comp = nil;
908*70f8d5d2SDavid du Colombier Pushstate ps;
909*70f8d5d2SDavid du Colombier
910*70f8d5d2SDavid du Colombier /* open archive to be updated */
911*70f8d5d2SDavid du Colombier if (usefile && docreate)
912*70f8d5d2SDavid du Colombier ar = create(usefile, OWRITE, 0666);
913*70f8d5d2SDavid du Colombier else if (usefile) {
914*70f8d5d2SDavid du Colombier if (docompress)
915*70f8d5d2SDavid du Colombier sysfatal("cannot update compressed archive");
916*70f8d5d2SDavid du Colombier ar = open(usefile, ORDWR);
917*70f8d5d2SDavid du Colombier } else
918*70f8d5d2SDavid du Colombier ar = Stdout;
919*70f8d5d2SDavid du Colombier
920*70f8d5d2SDavid du Colombier /* push compression filter, if requested */
921*70f8d5d2SDavid du Colombier if (docompress) {
922*70f8d5d2SDavid du Colombier comp = compmethod(usefile);
923*70f8d5d2SDavid du Colombier if (comp)
924*70f8d5d2SDavid du Colombier ar = push(ar, comp->comp, Output, &ps);
925*70f8d5d2SDavid du Colombier }
926*70f8d5d2SDavid du Colombier if (ar < 0)
927*70f8d5d2SDavid du Colombier sysfatal("can't open archive %s: %r", usefile);
928*70f8d5d2SDavid du Colombier
929*70f8d5d2SDavid du Colombier if (usefile && !docreate)
930*70f8d5d2SDavid du Colombier skiptoend(ar);
931*70f8d5d2SDavid du Colombier
932f3703655SDavid du Colombier for (i = 0; argv[i] != nil; i++) {
933728c92e5SDavid du Colombier arg = argv[i];
934728c92e5SDavid du Colombier cleanname(arg);
935728c92e5SDavid du Colombier if (strcmp(arg, "..") == 0 || strncmp(arg, "../", 3) == 0)
936728c92e5SDavid du Colombier fprint(2, "%s: name starting with .. is a bad idea\n",
937728c92e5SDavid du Colombier argv0);
938728c92e5SDavid du Colombier addtoar(ar, arg, arg);
939f3703655SDavid du Colombier chdir(origdir); /* for correctness & profiling */
940f3703655SDavid du Colombier }
9410fb3f58eSDavid du Colombier
9420fb3f58eSDavid du Colombier /* write end-of-archive marker */
9430fb3f58eSDavid du Colombier getblkz(ar);
9440fb3f58eSDavid du Colombier putblk(ar);
9450fb3f58eSDavid du Colombier getblkz(ar);
9460fb3f58eSDavid du Colombier putlastblk(ar);
9470fb3f58eSDavid du Colombier
9480fb3f58eSDavid du Colombier if (comp)
9490fb3f58eSDavid du Colombier return pushclose(&ps);
9500fb3f58eSDavid du Colombier if (ar > Stderr)
9510fb3f58eSDavid du Colombier close(ar);
9520fb3f58eSDavid du Colombier return nil;
9530fb3f58eSDavid du Colombier }
9540fb3f58eSDavid du Colombier
9550fb3f58eSDavid du Colombier /*
9560fb3f58eSDavid du Colombier * tar [xt]
9570fb3f58eSDavid du Colombier */
9580fb3f58eSDavid du Colombier
9590fb3f58eSDavid du Colombier /* is pfx a file-name prefix of name? */
9600fb3f58eSDavid du Colombier static int
prefix(char * name,char * pfx)9610fb3f58eSDavid du Colombier prefix(char *name, char *pfx)
9620fb3f58eSDavid du Colombier {
9630fb3f58eSDavid du Colombier int pfxlen = strlen(pfx);
9640fb3f58eSDavid du Colombier char clpfx[Maxname+1];
9650fb3f58eSDavid du Colombier
9660fb3f58eSDavid du Colombier if (pfxlen > Maxname)
9670fb3f58eSDavid du Colombier return 0;
9680fb3f58eSDavid du Colombier strcpy(clpfx, pfx);
9690fb3f58eSDavid du Colombier cleanname(clpfx);
970018fc28bSDavid du Colombier return strncmp(clpfx, name, pfxlen) == 0 &&
9710fb3f58eSDavid du Colombier (name[pfxlen] == '\0' || name[pfxlen] == '/');
9720fb3f58eSDavid du Colombier }
9730fb3f58eSDavid du Colombier
9740fb3f58eSDavid du Colombier static int
match(char * name,char ** argv)9750fb3f58eSDavid du Colombier match(char *name, char **argv)
9760fb3f58eSDavid du Colombier {
9770fb3f58eSDavid du Colombier int i;
9780fb3f58eSDavid du Colombier char clname[Maxname+1];
9790fb3f58eSDavid du Colombier
9800fb3f58eSDavid du Colombier if (argv[0] == nil)
9810fb3f58eSDavid du Colombier return 1;
9820fb3f58eSDavid du Colombier strcpy(clname, name);
9830fb3f58eSDavid du Colombier cleanname(clname);
9840fb3f58eSDavid du Colombier for (i = 0; argv[i] != nil; i++)
9850fb3f58eSDavid du Colombier if (prefix(clname, argv[i]))
9860fb3f58eSDavid du Colombier return 1;
9870fb3f58eSDavid du Colombier return 0;
9880fb3f58eSDavid du Colombier }
9890fb3f58eSDavid du Colombier
9908b6d9ba0SDavid du Colombier static void
cantcreate(char * s,int mode)9918b6d9ba0SDavid du Colombier cantcreate(char *s, int mode)
9928b6d9ba0SDavid du Colombier {
9938b6d9ba0SDavid du Colombier int len;
9948b6d9ba0SDavid du Colombier static char *last;
9958b6d9ba0SDavid du Colombier
9968b6d9ba0SDavid du Colombier /*
9978b6d9ba0SDavid du Colombier * Always print about files. Only print about directories
9988b6d9ba0SDavid du Colombier * we haven't printed about. (Assumes archive is ordered
9998b6d9ba0SDavid du Colombier * nicely.)
10008b6d9ba0SDavid du Colombier */
10018b6d9ba0SDavid du Colombier if(mode&DMDIR){
10028b6d9ba0SDavid du Colombier if(last){
10038b6d9ba0SDavid du Colombier /* already printed this directory */
10048b6d9ba0SDavid du Colombier if(strcmp(s, last) == 0)
10058b6d9ba0SDavid du Colombier return;
10068b6d9ba0SDavid du Colombier /* printed a higher directory, so printed this one */
10078b6d9ba0SDavid du Colombier len = strlen(s);
10088b6d9ba0SDavid du Colombier if(memcmp(s, last, len) == 0 && last[len] == '/')
10098b6d9ba0SDavid du Colombier return;
10108b6d9ba0SDavid du Colombier }
10118b6d9ba0SDavid du Colombier /* save */
10128b6d9ba0SDavid du Colombier free(last);
10138b6d9ba0SDavid du Colombier last = strdup(s);
10148b6d9ba0SDavid du Colombier }
10158b6d9ba0SDavid du Colombier fprint(2, "%s: can't create %s: %r\n", argv0, s);
10168b6d9ba0SDavid du Colombier }
10178b6d9ba0SDavid du Colombier
10180fb3f58eSDavid du Colombier static int
makedir(char * s)10190fb3f58eSDavid du Colombier makedir(char *s)
10200fb3f58eSDavid du Colombier {
10210fb3f58eSDavid du Colombier int f;
10220fb3f58eSDavid du Colombier
10230fb3f58eSDavid du Colombier if (access(s, AEXIST) == 0)
10240fb3f58eSDavid du Colombier return -1;
10250fb3f58eSDavid du Colombier f = create(s, OREAD, DMDIR | 0777);
10260fb3f58eSDavid du Colombier if (f >= 0)
10270fb3f58eSDavid du Colombier close(f);
10288b6d9ba0SDavid du Colombier else
10298b6d9ba0SDavid du Colombier cantcreate(s, DMDIR);
10300fb3f58eSDavid du Colombier return f;
10310fb3f58eSDavid du Colombier }
10320fb3f58eSDavid du Colombier
10338b6d9ba0SDavid du Colombier static int
mkpdirs(char * s)10340fb3f58eSDavid du Colombier mkpdirs(char *s)
10350fb3f58eSDavid du Colombier {
10368b6d9ba0SDavid du Colombier int err;
10378b6d9ba0SDavid du Colombier char *p;
10380fb3f58eSDavid du Colombier
10398b6d9ba0SDavid du Colombier p = s;
10408b6d9ba0SDavid du Colombier err = 0;
10418b6d9ba0SDavid du Colombier while (!err && (p = strchr(p+1, '/')) != nil) {
10420fb3f58eSDavid du Colombier *p = '\0';
10438b6d9ba0SDavid du Colombier err = (access(s, AEXIST) < 0 && makedir(s) < 0);
10440fb3f58eSDavid du Colombier *p = '/';
10450fb3f58eSDavid du Colombier }
10468b6d9ba0SDavid du Colombier return -err;
10470fb3f58eSDavid du Colombier }
10480fb3f58eSDavid du Colombier
1049312a1df1SDavid du Colombier /* Call access but preserve the error string. */
1050312a1df1SDavid du Colombier static int
xaccess(char * name,int mode)1051312a1df1SDavid du Colombier xaccess(char *name, int mode)
1052312a1df1SDavid du Colombier {
1053312a1df1SDavid du Colombier char err[ERRMAX];
1054312a1df1SDavid du Colombier int rv;
1055312a1df1SDavid du Colombier
1056312a1df1SDavid du Colombier err[0] = 0;
1057312a1df1SDavid du Colombier errstr(err, sizeof err);
1058312a1df1SDavid du Colombier rv = access(name, mode);
1059312a1df1SDavid du Colombier errstr(err, sizeof err);
1060312a1df1SDavid du Colombier return rv;
1061312a1df1SDavid du Colombier }
1062312a1df1SDavid du Colombier
10634ac22c89SDavid du Colombier static int
openfname(Hdr * hp,char * fname,int dir,int mode)10644ac22c89SDavid du Colombier openfname(Hdr *hp, char *fname, int dir, int mode)
10650fb3f58eSDavid du Colombier {
10664ac22c89SDavid du Colombier int fd;
10670fb3f58eSDavid du Colombier
10684ac22c89SDavid du Colombier fd = -1;
10690fb3f58eSDavid du Colombier cleanname(fname);
10700fb3f58eSDavid du Colombier switch (hp->linkflag) {
10710fb3f58eSDavid du Colombier case LF_LINK:
10720fb3f58eSDavid du Colombier case LF_SYMLINK1:
10730fb3f58eSDavid du Colombier case LF_SYMLINK2:
10740fb3f58eSDavid du Colombier fprint(2, "%s: can't make (sym)link %s\n",
10750fb3f58eSDavid du Colombier argv0, fname);
10763e12c5d1SDavid du Colombier break;
10770fb3f58eSDavid du Colombier case LF_FIFO:
10780fb3f58eSDavid du Colombier fprint(2, "%s: can't make fifo %s\n", argv0, fname);
10790fb3f58eSDavid du Colombier break;
10800fb3f58eSDavid du Colombier default:
10810fb3f58eSDavid du Colombier if (!keepexisting || access(fname, AEXIST) < 0) {
10820fb3f58eSDavid du Colombier int rw = (dir? OREAD: OWRITE);
10833e12c5d1SDavid du Colombier
10840fb3f58eSDavid du Colombier fd = create(fname, rw, mode);
10850fb3f58eSDavid du Colombier if (fd < 0) {
10860fb3f58eSDavid du Colombier mkpdirs(fname);
10870fb3f58eSDavid du Colombier fd = create(fname, rw, mode);
10883e12c5d1SDavid du Colombier }
10894ac22c89SDavid du Colombier if (fd < 0 && (!dir || xaccess(fname, AEXIST) < 0))
10908b6d9ba0SDavid du Colombier cantcreate(fname, mode);
10913e12c5d1SDavid du Colombier }
10920fb3f58eSDavid du Colombier if (fd >= 0 && verbose)
10930fb3f58eSDavid du Colombier fprint(2, "%s\n", fname);
10940fb3f58eSDavid du Colombier break;
10950fb3f58eSDavid du Colombier }
10964ac22c89SDavid du Colombier return fd;
10974ac22c89SDavid du Colombier }
10983e12c5d1SDavid du Colombier
10994ac22c89SDavid du Colombier /* copy from archive to file system (or nowhere for table-of-contents) */
11004ac22c89SDavid du Colombier static void
copyfromar(int ar,int fd,char * fname,ulong blksleft,Off bytes)11014ac22c89SDavid du Colombier copyfromar(int ar, int fd, char *fname, ulong blksleft, Off bytes)
11024ac22c89SDavid du Colombier {
11034ac22c89SDavid du Colombier int wrbytes;
11044ac22c89SDavid du Colombier ulong blksread;
11054ac22c89SDavid du Colombier Hdr *hbp;
11063e12c5d1SDavid du Colombier
11074ac22c89SDavid du Colombier if (blksleft == 0 || bytes < 0)
11085437ee90SDavid du Colombier bytes = 0;
11090fb3f58eSDavid du Colombier for (; blksleft > 0; blksleft -= blksread) {
11104f281771SDavid du Colombier hbp = getblkrd(ar, (fd >= 0? Alldata: Justnxthdr));
11110fb3f58eSDavid du Colombier if (hbp == nil)
11122ce711d4SDavid du Colombier sysfatal("unexpected EOF on archive extracting %s from %s",
11132ce711d4SDavid du Colombier fname, arname);
11140fb3f58eSDavid du Colombier blksread = gothowmany(blksleft);
11154ac22c89SDavid du Colombier if (blksread <= 0) {
11165437ee90SDavid du Colombier fprint(2, "%s: got %ld blocks reading %s!\n",
11175437ee90SDavid du Colombier argv0, blksread, fname);
11184ac22c89SDavid du Colombier blksread = 0;
11194ac22c89SDavid du Colombier }
11200fb3f58eSDavid du Colombier wrbytes = Tblock*blksread;
11214ac22c89SDavid du Colombier assert(bytes >= 0);
11220fb3f58eSDavid du Colombier if(wrbytes > bytes)
11230fb3f58eSDavid du Colombier wrbytes = bytes;
11244ac22c89SDavid du Colombier assert(wrbytes >= 0);
11254ac22c89SDavid du Colombier if (fd >= 0)
11264ac22c89SDavid du Colombier ewrite(fname, fd, hbp->data, wrbytes);
11270fb3f58eSDavid du Colombier putreadblks(ar, blksread);
11280fb3f58eSDavid du Colombier bytes -= wrbytes;
11294ac22c89SDavid du Colombier assert(bytes >= 0);
113040ef9009SDavid du Colombier }
11315437ee90SDavid du Colombier if (bytes > 0)
11322ce711d4SDavid du Colombier fprint(2, "%s: %lld bytes uncopied at EOF on archive %s; "
11332ce711d4SDavid du Colombier "%s not fully extracted\n", argv0, bytes, arname, fname);
11344ac22c89SDavid du Colombier }
11354ac22c89SDavid du Colombier
11364ac22c89SDavid du Colombier static void
wrmeta(int fd,Hdr * hp,long mtime,int mode)11372ce711d4SDavid du Colombier wrmeta(int fd, Hdr *hp, long mtime, int mode) /* update metadata */
11384ac22c89SDavid du Colombier {
11390fb3f58eSDavid du Colombier Dir nd;
11400fb3f58eSDavid du Colombier
11410fb3f58eSDavid du Colombier nulldir(&nd);
11420fb3f58eSDavid du Colombier nd.mtime = mtime;
11432ce711d4SDavid du Colombier nd.mode = mode;
1144499069deSDavid du Colombier dirfwstat(fd, &nd);
1145499069deSDavid du Colombier if (isustar(hp)) {
1146499069deSDavid du Colombier nulldir(&nd);
11470fb3f58eSDavid du Colombier nd.gid = hp->gname;
11480fb3f58eSDavid du Colombier dirfwstat(fd, &nd);
11492ce711d4SDavid du Colombier nulldir(&nd);
11502ce711d4SDavid du Colombier nd.uid = hp->uname;
11512ce711d4SDavid du Colombier dirfwstat(fd, &nd);
11520fb3f58eSDavid du Colombier }
1153499069deSDavid du Colombier }
11544ac22c89SDavid du Colombier
11554ac22c89SDavid du Colombier /*
11564ac22c89SDavid du Colombier * copy a file from the archive into the filesystem.
11574ac22c89SDavid du Colombier * fname is result of name(), so has two extra bytes at beginning.
11584ac22c89SDavid du Colombier */
11594ac22c89SDavid du Colombier static void
extract1(int ar,Hdr * hp,char * fname)11604ac22c89SDavid du Colombier extract1(int ar, Hdr *hp, char *fname)
11614ac22c89SDavid du Colombier {
11624ac22c89SDavid du Colombier int fd = -1, dir = 0;
11634ac22c89SDavid du Colombier long mtime = strtol(hp->mtime, nil, 8);
11644ac22c89SDavid du Colombier ulong mode = strtoul(hp->mode, nil, 8) & 0777;
11654ac22c89SDavid du Colombier Off bytes = hdrsize(hp); /* for printing */
11664ac22c89SDavid du Colombier ulong blksleft = BYTES2TBLKS(arsize(hp));
11674ac22c89SDavid du Colombier
11684ac22c89SDavid du Colombier /* fiddle name, figure out mode and blocks */
11694ac22c89SDavid du Colombier if (isdir(hp)) {
11704ac22c89SDavid du Colombier mode |= DMDIR|0700;
11714ac22c89SDavid du Colombier dir = 1;
11724ac22c89SDavid du Colombier }
11734ac22c89SDavid du Colombier switch (hp->linkflag) {
11744ac22c89SDavid du Colombier case LF_LINK:
11754ac22c89SDavid du Colombier case LF_SYMLINK1:
11764ac22c89SDavid du Colombier case LF_SYMLINK2:
11774ac22c89SDavid du Colombier case LF_FIFO:
11784ac22c89SDavid du Colombier blksleft = 0;
11794ac22c89SDavid du Colombier break;
11804ac22c89SDavid du Colombier }
11814ac22c89SDavid du Colombier if (relative)
11824ac22c89SDavid du Colombier if(fname[0] == '/')
11834ac22c89SDavid du Colombier *--fname = '.';
11844ac22c89SDavid du Colombier else if(fname[0] == '#'){
11854ac22c89SDavid du Colombier *--fname = '/';
11864ac22c89SDavid du Colombier *--fname = '.';
11874ac22c89SDavid du Colombier }
11884ac22c89SDavid du Colombier
11894ac22c89SDavid du Colombier if (verb == Xtract)
11904ac22c89SDavid du Colombier fd = openfname(hp, fname, dir, mode);
11914ac22c89SDavid du Colombier else if (verbose) {
11924ac22c89SDavid du Colombier char *cp = ctime(mtime);
11934ac22c89SDavid du Colombier
11944ac22c89SDavid du Colombier print("%M %8lld %-12.12s %-4.4s %s\n",
11954ac22c89SDavid du Colombier mode, bytes, cp+4, cp+24, fname);
11964ac22c89SDavid du Colombier } else
11974ac22c89SDavid du Colombier print("%s\n", fname);
11984ac22c89SDavid du Colombier
11994ac22c89SDavid du Colombier copyfromar(ar, fd, fname, blksleft, bytes);
12004ac22c89SDavid du Colombier
12014ac22c89SDavid du Colombier /* touch up meta data and close */
12024ac22c89SDavid du Colombier if (fd >= 0) {
12034ac22c89SDavid du Colombier /*
12044ac22c89SDavid du Colombier * directories should be wstated *after* we're done
12054ac22c89SDavid du Colombier * creating files in them, but we don't do that.
12064ac22c89SDavid du Colombier */
12074ac22c89SDavid du Colombier if (settime)
12082ce711d4SDavid du Colombier wrmeta(fd, hp, mtime, mode);
12090fb3f58eSDavid du Colombier close(fd);
12100fb3f58eSDavid du Colombier }
12110fb3f58eSDavid du Colombier }
12120fb3f58eSDavid du Colombier
12130fb3f58eSDavid du Colombier static char *
extract(char ** argv)12140fb3f58eSDavid du Colombier extract(char **argv)
12150fb3f58eSDavid du Colombier {
12160fb3f58eSDavid du Colombier int ar;
12170fb3f58eSDavid du Colombier char *longname;
1218*70f8d5d2SDavid du Colombier char msg[Maxname + 40];
1219*70f8d5d2SDavid du Colombier Compress *comp;
12200fb3f58eSDavid du Colombier Hdr *hp;
12210fb3f58eSDavid du Colombier Pushstate ps;
12220fb3f58eSDavid du Colombier
1223*70f8d5d2SDavid du Colombier /* open archive to be read */
1224*70f8d5d2SDavid du Colombier if (usefile)
12250fb3f58eSDavid du Colombier ar = open(usefile, OREAD);
1226*70f8d5d2SDavid du Colombier else
12270fb3f58eSDavid du Colombier ar = Stdin;
1228*70f8d5d2SDavid du Colombier
1229*70f8d5d2SDavid du Colombier /* push decompression filter if requested or extension is known */
1230*70f8d5d2SDavid du Colombier comp = compmethod(usefile);
12310fb3f58eSDavid du Colombier if (comp)
12320fb3f58eSDavid du Colombier ar = push(ar, comp->decomp, Input, &ps);
12330fb3f58eSDavid du Colombier if (ar < 0)
12340fb3f58eSDavid du Colombier sysfatal("can't open archive %s: %r", usefile);
12350fb3f58eSDavid du Colombier
12360fb3f58eSDavid du Colombier while ((hp = readhdr(ar)) != nil) {
12370fb3f58eSDavid du Colombier longname = name(hp);
12380fb3f58eSDavid du Colombier if (match(longname, argv))
12390fb3f58eSDavid du Colombier extract1(ar, hp, longname);
1240*70f8d5d2SDavid du Colombier else {
1241*70f8d5d2SDavid du Colombier snprint(msg, sizeof msg, "extracting %s", longname);
1242*70f8d5d2SDavid du Colombier skip(ar, hp, msg);
1243*70f8d5d2SDavid du Colombier }
12440fb3f58eSDavid du Colombier }
12450fb3f58eSDavid du Colombier
12460fb3f58eSDavid du Colombier if (comp)
12470fb3f58eSDavid du Colombier return pushclose(&ps);
12480fb3f58eSDavid du Colombier if (ar > Stderr)
12490fb3f58eSDavid du Colombier close(ar);
12500fb3f58eSDavid du Colombier return nil;
12513e12c5d1SDavid du Colombier }
12523e12c5d1SDavid du Colombier
12533e12c5d1SDavid du Colombier void
main(int argc,char * argv[])12540fb3f58eSDavid du Colombier main(int argc, char *argv[])
12553e12c5d1SDavid du Colombier {
12560fb3f58eSDavid du Colombier int errflg = 0;
12570fb3f58eSDavid du Colombier char *ret = nil;
12580fb3f58eSDavid du Colombier
12590fb3f58eSDavid du Colombier fmtinstall('M', dirmodefmt);
12600fb3f58eSDavid du Colombier
12610fb3f58eSDavid du Colombier TARGBEGIN {
12620fb3f58eSDavid du Colombier case 'c':
12630fb3f58eSDavid du Colombier docreate++;
12640fb3f58eSDavid du Colombier verb = Replace;
12650fb3f58eSDavid du Colombier break;
12660fb3f58eSDavid du Colombier case 'f':
12674ac22c89SDavid du Colombier usefile = arname = EARGF(usage());
12680fb3f58eSDavid du Colombier break;
12690fb3f58eSDavid du Colombier case 'g':
12700fb3f58eSDavid du Colombier argid = strtoul(EARGF(usage()), 0, 0);
12710fb3f58eSDavid du Colombier break;
12724ac22c89SDavid du Colombier case 'i':
12734ac22c89SDavid du Colombier ignerrs = 1;
12744ac22c89SDavid du Colombier break;
12750fb3f58eSDavid du Colombier case 'k':
12760fb3f58eSDavid du Colombier keepexisting++;
12770fb3f58eSDavid du Colombier break;
12780fb3f58eSDavid du Colombier case 'm': /* compatibility */
12790fb3f58eSDavid du Colombier settime = 0;
12800fb3f58eSDavid du Colombier break;
12810fb3f58eSDavid du Colombier case 'p':
12820fb3f58eSDavid du Colombier posix++;
12830fb3f58eSDavid du Colombier break;
12840fb3f58eSDavid du Colombier case 'P':
12850fb3f58eSDavid du Colombier posix = 0;
12860fb3f58eSDavid du Colombier break;
12870fb3f58eSDavid du Colombier case 'r':
12880fb3f58eSDavid du Colombier verb = Replace;
12890fb3f58eSDavid du Colombier break;
12900fb3f58eSDavid du Colombier case 'R':
129123605fa3SDavid du Colombier relative = 0;
12920fb3f58eSDavid du Colombier break;
12932ce711d4SDavid du Colombier case 's':
12942ce711d4SDavid du Colombier resync++;
12952ce711d4SDavid du Colombier break;
12960fb3f58eSDavid du Colombier case 't':
12970fb3f58eSDavid du Colombier verb = Toc;
12980fb3f58eSDavid du Colombier break;
12990fb3f58eSDavid du Colombier case 'T':
13000fb3f58eSDavid du Colombier settime++;
13010fb3f58eSDavid du Colombier break;
13020fb3f58eSDavid du Colombier case 'u':
13030fb3f58eSDavid du Colombier aruid = strtoul(EARGF(usage()), 0, 0);
13040fb3f58eSDavid du Colombier break;
13050fb3f58eSDavid du Colombier case 'v':
13060fb3f58eSDavid du Colombier verbose++;
13070fb3f58eSDavid du Colombier break;
13080fb3f58eSDavid du Colombier case 'x':
13090fb3f58eSDavid du Colombier verb = Xtract;
13100fb3f58eSDavid du Colombier break;
13110fb3f58eSDavid du Colombier case 'z':
13120fb3f58eSDavid du Colombier docompress++;
13130fb3f58eSDavid du Colombier break;
13140fb3f58eSDavid du Colombier case '-':
13150fb3f58eSDavid du Colombier break;
13160fb3f58eSDavid du Colombier default:
13170fb3f58eSDavid du Colombier fprint(2, "tar: unknown letter %C\n", TARGC());
13180fb3f58eSDavid du Colombier errflg++;
13190fb3f58eSDavid du Colombier break;
13200fb3f58eSDavid du Colombier } TARGEND
13210fb3f58eSDavid du Colombier
13220fb3f58eSDavid du Colombier if (argc < 0 || errflg)
13230fb3f58eSDavid du Colombier usage();
13240fb3f58eSDavid du Colombier
13250fb3f58eSDavid du Colombier initblks();
13260fb3f58eSDavid du Colombier switch (verb) {
13270fb3f58eSDavid du Colombier case Toc:
13280fb3f58eSDavid du Colombier case Xtract:
13290fb3f58eSDavid du Colombier ret = extract(argv);
13300fb3f58eSDavid du Colombier break;
13310fb3f58eSDavid du Colombier case Replace:
13320fb3f58eSDavid du Colombier if (getwd(origdir, sizeof origdir) == nil)
13330fb3f58eSDavid du Colombier strcpy(origdir, "/tmp");
13340fb3f58eSDavid du Colombier ret = replace(argv);
13350fb3f58eSDavid du Colombier break;
13360fb3f58eSDavid du Colombier default:
13370fb3f58eSDavid du Colombier usage();
13380fb3f58eSDavid du Colombier break;
13390fb3f58eSDavid du Colombier }
13400fb3f58eSDavid du Colombier exits(ret);
13413e12c5d1SDavid du Colombier }
1342