⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tar.c

📁 这是一个同样来自贝尔实验室的和UNIX有着渊源的操作系统, 其简洁的设计和实现易于我们学习和理解
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * 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 <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 ROUNDUP(a, b)	(((a) + (b) - 1)/(b))#define BYTES2TBLKS(bytes) ROUNDUP(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 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 Off blkoff;	/* offset of the current archive block (not Tblock) */static Off nexthdr;static int nblock = Dblock;static char *usefile;static char origdir[Maxname*2];static Hdr *tpblk, *endblk;static Hdr *curblk;static voidusage(void){	fprint(2, "usage: %s {crtx}[PRTfgkmpuvz] [archive] file1 file2...\n",		argv0);	exits("usage");}/* compression */static Compress *compmethod(char *name){	int i, nmlen = strlen(name), sfxlen;	Compress *cp;	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;	if (first)		seekable = seek(ar, 0, 1) >= 0;	blkoff = seek(ar, 0, 1);		/* note position for `tar r' */	/* try to size non-pipe input at first read */	if (first && usefile) {		n = read(ar, bufs, bytes);		if (n <= 0)			sysfatal("error reading archive: %r");		i = n;		if (i % Tblock != 0) {			fprint(2, "%s: archive block size (%d) error\n",				argv0, i);			exits("blocksize");		}		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 - seek(ar, 0, 1) >= bytes) {		/* optimisation for huge archive members on seekable media */		if (seek(ar, bytes, 1) < 0)			sysfatal("can't seek on archive: %r");		n = bytes;	} else		n = readn(ar, bufs, bytes);	first = 0;	if (n == 0)		sysfatal("unexpected EOF reading archive");	else if (n < 0)		sysfatal("error reading archive: %r");	else if (n%Tblock != 0)		sysfatal("partial block read from archive");	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);	if (write(ar, tpblk, bytes) != bytes)		sysfatal("error writing archive: %r");}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){	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;	static char fullnamebuf[2 + Maxname + 1];	/* 2 at beginning for ./ on relative names */	char *fullname;	fullname = fullnamebuf+2;	namlen = strnlen(hp->name, sizeof hp->name);	if (hp->prefix[0] == '\0' || !isustar(hp)) {	/* old-style name? */		memmove(fullname, hp->name, namlen);		fullname[namlen] = '\0';		return fullname;	}	/* name is in two pieces */	pfxlen = strnlen(hp->prefix, sizeof hp->prefix);	memmove(fullname, hp->prefix, pfxlen);	fullname[pfxlen] = '/';	memmove(fullname + pfxlen + 1, hp->name, namlen);	fullname[pfxlen + 1 + namlen] = '\0';	return fullname;}static intisdir(Hdr *hp){	/* the mode test is ugly but sometimes necessary */	return hp->linkflag == LF_DIR ||		strrchr(name(hp), '\0')[-1] == '/' ||		(strtoul(hp->mode, nil, 8)&0170000) == 040000;}static inteotar(Hdr *hp){	return name(hp)[0] == '\0';}/*static uvlonggetbe(uchar *src, int size){	uvlong vl = 0;	while (size-- > 0) {		vl <<= 8;		vl |= *src++;	}	return vl;} */static voidputbe(uchar *dest, uvlong vl, int size){	for (dest += size; size-- > 0; vl >>= 8)		*--dest = vl;}/* * return the nominal size from the header block, which is not always the * size in the archive (the archive size may be zero for some file types * regardless of the nominal size). * * gnu and freebsd tars are now recording vlongs as big-endian binary * with a flag in byte 0 to indicate this, which permits file sizes up to * 2^64-1 (actually 2^80-1 but our file sizes are vlongs) rather than 2^33-1. */static Offhdrsize(Hdr *hp){	uchar *p;	if((uchar)hp->size[0] == Binnegsz) {		fprint(2, "%s: %s: negative length, which is insane\n",			argv0, name(hp));		return 0;	} else if((uchar)hp->size[0] == Binsize) {		p = (uchar *)hp->size + sizeof hp->size - 1 -			sizeof(vlong);		/* -1 for terminating space */		return G8BEBYTE(p);	} else		return strtoull(hp->size, nil, 8);}/* * return the number of bytes recorded in the archive. */static Offarsize(Hdr *hp){	if(isdir(hp) || islink(hp->linkflag))		return 0;	return hdrsize(hp);}static Hdr *readhdr(int ar){	long hdrcksum;	Hdr *hp;	hp = getblkrd(ar, Alldata);	if (hp == nil)		sysfatal("unexpected EOF instead of archive header");	if (eotar(hp))			/* end-of-archive block? */		return nil;	hdrcksum = strtoul(hp->chksum, nil, 8);	if (chksum(hp) != hdrcksum)		sysfatal("bad archive header checksum: name %.64s...",			hp->name);	nexthdr += Tblock*(1 + BYTES2TBLKS(arsize(hp)));	return hp;}/* * tar r[c] *//* * if name is longer than Namsiz bytes, try to split it at a slash and fit the * pieces into hp->prefix and hp->name. */static intputfullname(Hdr *hp, char *name){	int namlen, pfxlen;	char *sl, *osl;	String *slname = nil;	if (isdir(hp)) {		slname = s_new();		s_append(slname, name);		s_append(slname, "/");		/* posix requires this */		name = s_to_c(slname);	}	namlen = strlen(name);	if (namlen <= Namsiz) {		strncpy(hp->name, name, Namsiz);		hp->prefix[0] = '\0';		/* ustar paranoia */		return 0;	}	if (!posix || namlen > Maxname) {		fprint(2, "%s: name too long for tar header: %s\n",			argv0, name);		return -1;	}	/*	 * try various splits until one results in pieces that fit into the	 * appropriate fields of the header.  look for slashes from right	 * to left, in the hopes of putting the largest part of the name into	 * hp->prefix, which is larger than hp->name.	 */	sl = strrchr(name, '/');	while (sl != nil) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -