1341 lines · 27.0 KB · C
RawDownload
/* * tar - `tape archiver', actually usable on any medium. *	POSIX "ustar" compliant when extracting, and by default when creating. *	this tar attempts to read and write multiple Tblock-byte blocks *	at once to and from the filesystem, and does not copy blocks *	around internally. */ #include <u.h>#include <libc.h>#include <ctype.h>#include <fcall.h>		/* for %M */#include <String.h> /* * modified versions of those in libc.h; scans only the first arg for * keyletters and options. */#define	TARGBEGIN {\	(argv0 || (argv0 = *argv)), argv++, argc--;\	if (argv[0]) {\		char *_args, *_argt;\		Rune _argc;\		_args = &argv[0][0];\		_argc = 0;\		while(*_args && (_args += chartorune(&_argc, _args)))\			switch(_argc)#define	TARGEND	SET(_argt); USED(_argt);USED(_argc);USED(_args); \	argc--, argv++; } \	USED(argv); USED(argc); }#define	TARGC() (_argc) #define HOWMANY(a, size)	(((a) + (size) - 1) / (size))#define BYTES2TBLKS(bytes)	HOWMANY(bytes, Tblock) /* read big-endian binary integers; args must be (uchar *) */#define	G2BEBYTE(x)	(((x)[0]<<8)  |  (x)[1])#define	G3BEBYTE(x)	(((x)[0]<<16) | ((x)[1]<<8)  |  (x)[2])#define	G4BEBYTE(x)	(((x)[0]<<24) | ((x)[1]<<16) | ((x)[2]<<8) | (x)[3])#define	G8BEBYTE(x)	(((vlong)G4BEBYTE(x)<<32) | (u32int)G4BEBYTE((x)+4)) typedef vlong Off;typedef char *(*Refill)(int ar, char *bufs, int justhdr); enum { Stdin, Stdout, Stderr };enum { Rd, Wr };			/* pipe fd-array indices */enum { Output, Input };enum { None, Toc, Xtract, Replace };enum { Alldata, Justnxthdr };enum {	Tblock = 512,	Namsiz = 100,	Maxpfx = 155,		/* from POSIX */	Maxname = Namsiz + 1 + Maxpfx,	Binsize = 0x80,		/* flag in size[0], from gnu: positive binary size */	Binnegsz = 0xff,	/* flag in size[0]: negative binary size */ 	Nblock = 40,		/* maximum blocksize */	Dblock = 20,		/* default blocksize */	Debug = 0,}; /* POSIX link flags */enum {	LF_PLAIN1 =	'\0',	LF_PLAIN2 =	'0',	LF_LINK =	'1',	LF_SYMLINK1 =	'2',	LF_SYMLINK2 =	's',		/* 4BSD used this */	LF_CHR =	'3',	LF_BLK =	'4',	LF_DIR =	'5',	LF_FIFO =	'6',	LF_CONTIG =	'7',	/* 'A' - 'Z' are reserved for custom implementations */}; #define islink(lf)	(isreallink(lf) || issymlink(lf))#define isreallink(lf)	((lf) == LF_LINK)#define issymlink(lf)	((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2) typedef union {	uchar	data[Tblock];	struct {		char	name[Namsiz];		char	mode[8];		char	uid[8];		char	gid[8];		char	size[12];		char	mtime[12];		char	chksum[8];		char	linkflag;		char	linkname[Namsiz]; 		/* rest are defined by POSIX's ustar format; see p1003.2b */		char	magic[6];	/* "ustar" */		char	version[2];		char	uname[32];		char	gname[32];		char	devmajor[8];		char	devminor[8];		char	prefix[Maxpfx]; /* if non-null, path= prefix "/" name */	};} Hdr; typedef struct {	char	*comp;	char	*decomp;	char	*sfx[4];} Compress; static Compress comps[] = {	"gzip",		"gunzip",	{ ".tar.gz", ".tgz" },	/* default */	"compress",	"uncompress",	{ ".tar.Z",  ".tz" },	"bzip2",	"bunzip2",	{ ".tar.bz", ".tbz",					  ".tar.bz2",".tbz2" },}; typedef struct {	int	kid;	int	fd;	/* original fd */	int	rfd;	/* replacement fd */	int	input;	int	open;} Pushstate; #define OTHER(rdwr) ((rdwr) == Rd? Wr: Rd) static int debug;static int fixednblock;static int verb;static int posix = 1;static int docreate;static int aruid;static int argid;static int relative = 1;static int settime;static int verbose;static int docompress;static int keepexisting;static int ignerrs;		/* flag: ignore i/o errors if possible */static Off blkoff;		/* offset of the current archive block (not Tblock) */static Off nexthdr; static int nblock = Dblock;static int resync;static char *usefile, *arname = "archive";static char origdir[Maxname*2];static Hdr *tpblk, *endblk;static Hdr *curblk; static voidusage(void){	fprint(2, "usage: %s {crtx}[PRTfgikmpsuvz] [archive] [file1 file2...]\n",		argv0);	exits("usage");} /* I/O, with error retry or exit */ static intcope(char *name, int fd, void *buf, long len, Off off){	fprint(2, "%s: %serror reading %s: %r\n", argv0,		(ignerrs? "ignoring ": ""), name);	if (!ignerrs)		exits("read error"); 	/* pretend we read len bytes of zeroes */	memset(buf, 0, len);	if (off >= 0)			/* seekable? */		seek(fd, off + len, 0);	return len;} static interead(char *name, int fd, void *buf, long len){	int rd;	Off off; 	off = seek(fd, 0, 1);		/* for coping with errors */	rd = read(fd, buf, len);	if (rd < 0)		rd = cope(name, fd, buf, len, off);	return rd;} static intereadn(char *name, int fd, void *buf, long len){	int rd;	Off off; 	off = seek(fd, 0, 1);	rd = readn(fd, buf, len);	if (rd < 0)		rd = cope(name, fd, buf, len, off);	return rd;} static intewrite(char *name, int fd, void *buf, long len){	int rd; 	werrstr("");	rd = write(fd, buf, len);	if (rd != len)		sysfatal("error writing %s: %r", name);	return rd;} /* compression */ static Compress *compmethod(char *name){	int i, nmlen, sfxlen;	Compress *cp; 	if (name != nil) {		nmlen = strlen(name);		for (cp = comps; cp < comps + nelem(comps); cp++)			for (i = 0; i < nelem(cp->sfx) && cp->sfx[i]; i++) {				sfxlen = strlen(cp->sfx[i]);				if (nmlen > sfxlen &&				    strcmp(cp->sfx[i], name+nmlen-sfxlen) == 0)					return cp;			}	}	return docompress? comps: nil;} /* * push a filter, cmd, onto fd.  if input, it's an input descriptor. * returns a descriptor to replace fd, or -1 on error. */static intpush(int fd, char *cmd, int input, Pushstate *ps){	int nfd, pifds[2];	String *s; 	ps->open = 0;	ps->fd = fd;	ps->input = input;	if (fd < 0 || pipe(pifds) < 0)		return -1;	ps->kid = fork();	switch (ps->kid) {	case -1:		return -1;	case 0:		if (input)			dup(pifds[Wr], Stdout);		else			dup(pifds[Rd], Stdin);		close(pifds[input? Rd: Wr]);		dup(fd, (input? Stdin: Stdout));		s = s_new();		if (cmd[0] != '/')			s_append(s, "/bin/");		s_append(s, cmd);		execl(s_to_c(s), cmd, nil);		sysfatal("can't exec %s: %r", cmd);	default:		nfd = pifds[input? Rd: Wr];		close(pifds[input? Wr: Rd]);		break;	}	ps->rfd = nfd;	ps->open = 1;	return nfd;} static char *pushclose(Pushstate *ps){	Waitmsg *wm; 	if (ps->fd < 0 || ps->rfd < 0 || !ps->open)		return "not open";	close(ps->rfd);	ps->rfd = -1;	ps->open = 0;	while ((wm = wait()) != nil && wm->pid != ps->kid)		continue;	return wm? wm->msg: nil;} /* * block-buffer management */ static voidinitblks(void){	free(tpblk);	tpblk = malloc(Tblock * nblock);	assert(tpblk != nil);	endblk = tpblk + nblock;} /* * (re)fill block buffers from archive.  `justhdr' means we don't care * about the data before the next header block. */static char *refill(int ar, char *bufs, int justhdr){	int i, n;	unsigned bytes = Tblock * nblock;	static int done, first = 1, seekable; 	if (done)		return nil; 	blkoff = seek(ar, 0, 1);		/* note position for `tar r' */	if (first)		seekable = blkoff >= 0;	/* try to size non-pipe input at first read */	if (first && usefile && !fixednblock) {		n = eread(arname, ar, bufs, bytes);		if (n == 0)			sysfatal("EOF reading archive %s: %r", arname);		i = n;		if (i % Tblock != 0)			sysfatal("%s: archive block size (%d) error", arname, i);		i /= Tblock;		if (i != nblock) {			nblock = i;			fprint(2, "%s: blocking = %d\n", argv0, nblock);			endblk = (Hdr *)bufs + nblock;			bytes = n;		}	} else if (justhdr && seekable && nexthdr - blkoff >= bytes) {		/* optimisation for huge archive members on seekable media */		if (seek(ar, bytes, 1) < 0)			sysfatal("can't seek on archive %s: %r", arname);		n = bytes;	} else		n = ereadn(arname, ar, bufs, bytes);	first = 0; 	if (n == 0)		sysfatal("unexpected EOF reading archive %s", arname);	if (n % Tblock != 0)		sysfatal("partial block read from archive %s", arname);	if (n != bytes) {		done = 1;		memset(bufs + n, 0, bytes - n);	}	return bufs;} static Hdr *getblk(int ar, Refill rfp, int justhdr){	if (curblk == nil || curblk >= endblk) {  /* input block exhausted? */		if (rfp != nil && (*rfp)(ar, (char *)tpblk, justhdr) == nil)			return nil;		curblk = tpblk;	}	return curblk++;} static Hdr *getblkrd(int ar, int justhdr){	return getblk(ar, refill, justhdr);} static Hdr *getblke(int ar){	return getblk(ar, nil, Alldata);} static Hdr *getblkz(int ar){	Hdr *hp = getblke(ar); 	if (hp != nil)		memset(hp->data, 0, Tblock);	return hp;} /* * how many block buffers are available, starting at the address * just returned by getblk*? */static intgothowmany(int max){	int n = endblk - (curblk - 1); 	return n > max? max: n;} /* * indicate that one is done with the last block obtained from getblke * and it is now available to be written into the archive. */static voidputlastblk(int ar){	unsigned bytes = Tblock * nblock; 	/* if writing end-of-archive, aid compression (good hygiene too) */	if (curblk < endblk)		memset(curblk, 0, (char *)endblk - (char *)curblk);	ewrite(arname, ar, tpblk, bytes);} static voidputblk(int ar){	if (curblk >= endblk)		putlastblk(ar);} static voidputbackblk(int ar){	curblk--;	USED(ar);} static voidputreadblks(int ar, int blks){	curblk += blks - 1;	USED(ar);} static voidputblkmany(int ar, int blks){	assert(blks > 0);	curblk += blks - 1;	putblk(ar);} /* * common routines */ /* * modifies hp->chksum but restores it; important for the last block of the * old archive when updating with `tar rf archive' */static longchksum(Hdr *hp){	int n = Tblock;	long i = 0;	uchar *cp = hp->data;	char oldsum[sizeof hp->chksum]; 	memmove(oldsum, hp->chksum, sizeof oldsum);	memset(hp->chksum, ' ', sizeof hp->chksum);	while (n-- > 0)		i += *cp++;	memmove(hp->chksum, oldsum, sizeof oldsum);	return i;} static intisustar(Hdr *hp){	return strcmp(hp->magic, "ustar") == 0;} /* * s is at most n bytes long, but need not be NUL-terminated. * if shorter than n bytes, all bytes after the first NUL must also * be NUL. */static intstrnlen(char *s, int n){	return s[n - 1] != '\0'? n: strlen(s);} /* set fullname from header */static char *name(Hdr *hp){	int pfxlen, namlen;	char *fullname;	static char fullnamebuf[2+Maxname+1];  /* 2+ for ./ on relative names */ 	fullname = fullnamebuf+2;	namlen = strnlen(hp->name, sizeof hp->name);	if (hp->prefix[0] == '\0' || !isustar(hp)) {	/* old-style name? */		memmove(fullname, hp->name, namlen);		fullname[namlen] = '\0';

Lines 1–500 of 1341.