xref: /plan9/sys/src/cmd/tar.c (revision 70f8d5d20cdf22bd7afcab3bf516824c1342bf79)
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