1 /*
2  * tar - `tape archiver', actually usable on any medium.
3  *	POSIX "ustar" compliant when extracting, and by default when creating.
4  *	this tar attempts to read and write multiple Tblock-byte blocks
5  *	at once to and from the filesystem, and does not copy blocks
6  *	around internally.
7  */
8 
9 #include <u.h>
10 #include <libc.h>
11 #include <fcall.h>		/* for %M */
12 #include <libString.h>
13 
14 /*
15  * modified versions of those in libc.h; scans only the first arg for
16  * keyletters and options.
17  */
18 #define	TARGBEGIN {\
19 	if (!argv0) argv0 = *argv; \
20 	argv++, argc--;\
21 	if (argv[0]) {\
22 		char *_args, *_argt;\
23 		Rune _argc;\
24 		_args = &argv[0][0];\
25 		_argc = 0;\
26 		while(*_args && (_args += chartorune(&_argc, _args)))\
27 			switch(_argc)
28 #define	TARGEND	SET(_argt); USED(_argt);USED(_argc);USED(_args); \
29 	argc--, argv++; } \
30 	USED(argv); USED(argc); }
31 #define	TARGC() (_argc)
32 
33 #define ROUNDUP(a, b)	(((a) + (b) - 1)/(b))
34 #define BYTES2TBLKS(bytes) ROUNDUP(bytes, Tblock)
35 
36 /* read big-endian binary integers; args must be (uchar *) */
37 #define	G2BEBYTE(x)	(((x)[0]<<8)  |  (x)[1])
38 #define	G3BEBYTE(x)	(((x)[0]<<16) | ((x)[1]<<8)  |  (x)[2])
39 #define	G4BEBYTE(x)	(((x)[0]<<24) | ((x)[1]<<16) | ((x)[2]<<8) | (x)[3])
40 #define	G8BEBYTE(x)	(((vlong)G4BEBYTE(x)<<32) | (u32int)G4BEBYTE((x)+4))
41 
42 typedef vlong Off;
43 typedef char *(*Refill)(int ar, char *bufs, int justhdr);
44 
45 enum { Stdin, Stdout, Stderr };
46 enum { Rd, Wr };			/* pipe fd-array indices */
47 enum { Output, Input };
48 enum { None, Toc, Xtract, Replace };
49 enum { Alldata, Justnxthdr };
50 enum {
51 	Tblock = 512,
52 	Namsiz = 100,
53 	Maxpfx = 155,		/* from POSIX */
54 	Maxname = Namsiz + 1 + Maxpfx,
55 	Binsize = 0x80,		/* flag in size[0], from gnu: positive binary size */
56 	Binnegsz = 0xff,	/* flag in size[0]: negative binary size */
57 
58 	Nblock = 40,		/* maximum blocksize */
59 	Dblock = 20,		/* default blocksize */
60 	DEBUG = 0
61 };
62 
63 /* POSIX link flags */
64 enum {
65 	LF_PLAIN1 =	'\0',
66 	LF_PLAIN2 =	'0',
67 	LF_LINK =	'1',
68 	LF_SYMLINK1 =	'2',
69 	LF_SYMLINK2 =	's',		/* 4BSD used this */
70 	LF_CHR =	'3',
71 	LF_BLK =	'4',
72 	LF_DIR =	'5',
73 	LF_FIFO =	'6',
74 	LF_CONTIG =	'7'
75 	/* 'A' - 'Z' are reserved for custom implementations */
76 };
77 
78 #define islink(lf)	(isreallink(lf) || issymlink(lf))
79 #define isreallink(lf)	((lf) == LF_LINK)
80 #define issymlink(lf)	((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2)
81 
82 typedef struct Hdr {
83 	char	name[Namsiz];
84 	char	mode[8];
85 	char	uid[8];
86 	char	gid[8];
87 	char	size[12];
88 	char	mtime[12];
89 	char	chksum[8];
90 	char	linkflag;
91 	char	linkname[Namsiz];
92 
93 	/* rest are defined by POSIX's ustar format; see p1003.2b */
94 	char	magic[6];	/* "ustar" */
95 	char	version[2];
96 	char	uname[32];
97 	char	gname[32];
98 	char	devmajor[8];
99 	char	devminor[8];
100 	char	prefix[Maxpfx]; /* if non-null, path= prefix "/" name */
101 
102 	char pad[12];
103 } Hdr;
104 
105 typedef struct Compress {
106 	char	*comp;
107 	char	*decomp;
108 	char	*sfx[4];
109 } Compress;
110 
111 static Compress comps[] = {
112 	"gzip",		"gunzip",	{ ".tar.gz", ".tgz" },	/* default */
113 	"compress",	"uncompress",	{ ".tar.Z",  ".tz" },
114 	"bzip2",	"bunzip2",	{ ".tar.bz", ".tbz",
115 					  ".tar.bz2",".tbz2" },
116 };
117 
118 typedef struct Pushstate {
119 	int	kid;
120 	int	fd;	/* original fd */
121 	int	rfd;	/* replacement fd */
122 	int	input;
123 	int	open;
124 } Pushstate;
125 
126 #define OTHER(rdwr) (rdwr == Rd? Wr: Rd)
127 
128 /* static int debug; */
129 static int verb;
130 static int posix = 1;
131 static int docreate;
132 static int aruid;
133 static int argid;
134 static int relative = 1;
135 static int settime;
136 static int verbose;
137 static int docompress;
138 static int keepexisting;
139 static Off blkoff;	/* offset of the current archive block (not Tblock) */
140 static Off nexthdr;
141 
142 static int nblock = Dblock;
143 static char *usefile;
144 static char origdir[Maxname*2];
145 static Hdr *tpblk, *endblk;
146 static Hdr *curblk;
147 
148 static void
usage(void)149 usage(void)
150 {
151 	fprint(2, "usage: %s {crtx}[PRTfgkmpuvz] [archive] file1 file2...\n",
152 		argv0);
153 	exits("usage");
154 }
155 
156 /* compression */
157 
158 static Compress *
compmethod(char * name)159 compmethod(char *name)
160 {
161 	int i, nmlen = strlen(name), sfxlen;
162 	Compress *cp;
163 
164 	for (cp = comps; cp < comps + nelem(comps); cp++)
165 		for (i = 0; i < nelem(cp->sfx) && cp->sfx[i]; i++) {
166 			sfxlen = strlen(cp->sfx[i]);
167 			if (nmlen > sfxlen &&
168 			    strcmp(cp->sfx[i], name + nmlen - sfxlen) == 0)
169 				return cp;
170 		}
171 	return docompress? comps: nil;
172 }
173 
174 /*
175  * push a filter, cmd, onto fd.  if input, it's an input descriptor.
176  * returns a descriptor to replace fd, or -1 on error.
177  */
178 static int
push(int fd,char * cmd,int input,Pushstate * ps)179 push(int fd, char *cmd, int input, Pushstate *ps)
180 {
181 	int nfd, pifds[2];
182 
183 	ps->open = 0;
184 	ps->fd = fd;
185 	ps->input = input;
186 	if (fd < 0 || pipe(pifds) < 0)
187 		return -1;
188 	ps->kid = fork();
189 	switch (ps->kid) {
190 	case -1:
191 		return -1;
192 	case 0:
193 		if (input)
194 			dup(pifds[Wr], Stdout);
195 		else
196 			dup(pifds[Rd], Stdin);
197 		close(pifds[input? Rd: Wr]);
198 		dup(fd, (input? Stdin: Stdout));
199 		execl(cmd, cmd, nil);
200 		sysfatal("can't exec %s: %r", cmd);
201 	default:
202 		nfd = pifds[input? Rd: Wr];
203 		close(pifds[input? Wr: Rd]);
204 		break;
205 	}
206 	ps->rfd = nfd;
207 	ps->open = 1;
208 	return nfd;
209 }
210 
211 static char *
pushclose(Pushstate * ps)212 pushclose(Pushstate *ps)
213 {
214 	Waitmsg *wm;
215 
216 	if (ps->fd < 0 || ps->rfd < 0 || !ps->open)
217 		return "not open";
218 	close(ps->rfd);
219 	ps->rfd = -1;
220 	ps->open = 0;
221 	while ((wm = wait()) != nil && wm->pid != ps->kid)
222 		continue;
223 	return wm? wm->msg: nil;
224 }
225 
226 /*
227  * block-buffer management
228  */
229 
230 static void
initblks(void)231 initblks(void)
232 {
233 	free(tpblk);
234 	tpblk = malloc(Tblock * nblock);
235 	assert(tpblk != nil);
236 	endblk = tpblk + nblock;
237 }
238 
239 /*
240  * (re)fill block buffers from archive.  `justhdr' means we don't care
241  * about the data before the next header block.
242  */
243 static char *
refill(int ar,char * bufs,int justhdr)244 refill(int ar, char *bufs, int justhdr)
245 {
246 	int i, n;
247 	unsigned bytes = Tblock * nblock;
248 	static int done, first = 1, seekable;
249 
250 	if (done)
251 		return nil;
252 
253 	if (first)
254 		seekable = seek(ar, 0, 1) >= 0;
255 	blkoff = seek(ar, 0, 1);		/* note position for `tar r' */
256 	/* try to size non-pipe input at first read */
257 	if (first && usefile) {
258 		n = read(ar, bufs, bytes);
259 		if (n <= 0)
260 			sysfatal("error reading archive: %r");
261 		i = n;
262 		if (i % Tblock != 0) {
263 			fprint(2, "%s: archive block size (%d) error\n",
264 				argv0, i);
265 			exits("blocksize");
266 		}
267 		i /= Tblock;
268 		if (i != nblock) {
269 			nblock = i;
270 			fprint(2, "%s: blocking = %d\n", argv0, nblock);
271 			endblk = (Hdr *)bufs + nblock;
272 			bytes = n;
273 		}
274 	} else if (justhdr && seekable && nexthdr - seek(ar, 0, 1) >= bytes) {
275 		/* optimisation for huge archive members on seekable media */
276 		if (seek(ar, bytes, 1) < 0)
277 			sysfatal("can't seek on archive: %r");
278 		n = bytes;
279 	} else
280 		n = readn(ar, bufs, bytes);
281 	first = 0;
282 
283 	if (n == 0)
284 		sysfatal("unexpected EOF reading archive");
285 	else if (n < 0)
286 		sysfatal("error reading archive: %r");
287 	else if (n%Tblock != 0)
288 		sysfatal("partial block read from archive");
289 	if (n != bytes) {
290 		done = 1;
291 		memset(bufs + n, 0, bytes - n);
292 	}
293 	return bufs;
294 }
295 
296 static Hdr *
getblk(int ar,Refill rfp,int justhdr)297 getblk(int ar, Refill rfp, int justhdr)
298 {
299 	if (curblk == nil || curblk >= endblk) {  /* input block exhausted? */
300 		if (rfp != nil && (*rfp)(ar, (char *)tpblk, justhdr) == nil)
301 			return nil;
302 		curblk = tpblk;
303 	}
304 	return curblk++;
305 }
306 
307 static Hdr *
getblkrd(int ar,int justhdr)308 getblkrd(int ar, int justhdr)
309 {
310 	return getblk(ar, refill, justhdr);
311 }
312 
313 static Hdr *
getblke(int ar)314 getblke(int ar)
315 {
316 	return getblk(ar, nil, Alldata);
317 }
318 
319 static Hdr *
getblkz(int ar)320 getblkz(int ar)
321 {
322 	Hdr *hp = getblke(ar);
323 
324 	if (hp != nil)
325 		memset(hp, 0, Tblock);
326 	return hp;
327 }
328 
329 /*
330  * how many block buffers are available, starting at the address
331  * just returned by getblk*?
332  */
333 static int
gothowmany(int max)334 gothowmany(int max)
335 {
336 	int n = endblk - (curblk - 1);
337 
338 	return n > max? max: n;
339 }
340 
341 /*
342  * indicate that one is done with the last block obtained from getblke
343  * and it is now available to be written into the archive.
344  */
345 static void
putlastblk(int ar)346 putlastblk(int ar)
347 {
348 	unsigned bytes = Tblock * nblock;
349 
350 	/* if writing end-of-archive, aid compression (good hygiene too) */
351 	if (curblk < endblk)
352 		memset(curblk, 0, (char *)endblk - (char *)curblk);
353 	if (write(ar, tpblk, bytes) != bytes)
354 		sysfatal("error writing archive: %r");
355 }
356 
357 static void
putblk(int ar)358 putblk(int ar)
359 {
360 	if (curblk >= endblk)
361 		putlastblk(ar);
362 }
363 
364 static void
putbackblk(int ar)365 putbackblk(int ar)
366 {
367 	curblk--;
368 	USED(ar);
369 }
370 
371 static void
putreadblks(int ar,int blks)372 putreadblks(int ar, int blks)
373 {
374 	curblk += blks - 1;
375 	USED(ar);
376 }
377 
378 static void
putblkmany(int ar,int blks)379 putblkmany(int ar, int blks)
380 {
381 	curblk += blks - 1;
382 	putblk(ar);
383 }
384 
385 /*
386  * common routines
387  */
388 
389 /*
390  * modifies hp->chksum but restores it; important for the last block of the
391  * old archive when updating with `tar rf archive'
392  */
393 static long
chksum(Hdr * hp)394 chksum(Hdr *hp)
395 {
396 	int n = Tblock;
397 	long i = 0;
398 	uchar *cp = (uchar*)hp;
399 	char oldsum[sizeof hp->chksum];
400 
401 	memmove(oldsum, hp->chksum, sizeof oldsum);
402 	memset(hp->chksum, ' ', sizeof hp->chksum);
403 	while (n-- > 0)
404 		i += *cp++;
405 	memmove(hp->chksum, oldsum, sizeof oldsum);
406 	return i;
407 }
408 
409 static int
isustar(Hdr * hp)410 isustar(Hdr *hp)
411 {
412 	return strcmp(hp->magic, "ustar") == 0;
413 }
414 
415 /*
416  * s is at most n bytes long, but need not be NUL-terminated.
417  * if shorter than n bytes, all bytes after the first NUL must also
418  * be NUL.
419  */
420 static int
tar_sstrnlen(char * s,int n)421 tar_sstrnlen(char *s, int n)
422 {
423 	return s[n - 1] != '\0'? n: strlen(s);
424 }
425 
426 /* set fullname from header */
427 static char *
name(Hdr * hp)428 name(Hdr *hp)
429 {
430 	int pfxlen, namlen;
431 	static char fullnamebuf[2 + Maxname + 1];	/* 2 at beginning for ./ on relative names */
432 	char *fullname;
433 
434 	fullname = fullnamebuf+2;
435 	namlen = tar_sstrnlen(hp->name, sizeof hp->name);
436 	if (hp->prefix[0] == '\0' || !isustar(hp)) {	/* old-style name? */
437 		memmove(fullname, hp->name, namlen);
438 		fullname[namlen] = '\0';
439 		return fullname;
440 	}
441 
442 	/* name is in two pieces */
443 	pfxlen = tar_sstrnlen(hp->prefix, sizeof hp->prefix);
444 	memmove(fullname, hp->prefix, pfxlen);
445 	fullname[pfxlen] = '/';
446 	memmove(fullname + pfxlen + 1, hp->name, namlen);
447 	fullname[pfxlen + 1 + namlen] = '\0';
448 	return fullname;
449 }
450 
451 static int
isdir(Hdr * hp)452 isdir(Hdr *hp)
453 {
454 	/* the mode test is ugly but sometimes necessary */
455 	return hp->linkflag == LF_DIR ||
456 		strrchr(name(hp), '\0')[-1] == '/' ||
457 		(strtoul(hp->mode, nil, 8)&0170000) == 040000;
458 }
459 
460 static int
eotar(Hdr * hp)461 eotar(Hdr *hp)
462 {
463 	return name(hp)[0] == '\0';
464 }
465 
466 /*
467 static uvlong
468 getbe(uchar *src, int size)
469 {
470 	uvlong vl = 0;
471 
472 	while (size-- > 0) {
473 		vl <<= 8;
474 		vl |= *src++;
475 	}
476 	return vl;
477 }
478  */
479 
480 static void
putbe(uchar * dest,uvlong vl,int size)481 putbe(uchar *dest, uvlong vl, int size)
482 {
483 	for (dest += size; size-- > 0; vl >>= 8)
484 		*--dest = vl;
485 }
486 
487 /*
488  * return the nominal size from the header block, which is not always the
489  * size in the archive (the archive size may be zero for some file types
490  * regardless of the nominal size).
491  *
492  * gnu and freebsd tars are now recording vlongs as big-endian binary
493  * with a flag in byte 0 to indicate this, which permits file sizes up to
494  * 2^64-1 (actually 2^80-1 but our file sizes are vlongs) rather than 2^33-1.
495  */
496 static Off
hdrsize(Hdr * hp)497 hdrsize(Hdr *hp)
498 {
499 	uchar *p;
500 
501 	if((uchar)hp->size[0] == Binnegsz) {
502 		fprint(2, "%s: %s: negative length, which is insane\n",
503 			argv0, name(hp));
504 		return 0;
505 	} else if((uchar)hp->size[0] == Binsize) {
506 		p = (uchar *)hp->size + sizeof hp->size - 1 -
507 			sizeof(vlong);		/* -1 for terminating space */
508 		return G8BEBYTE(p);
509 	} else
510 		return strtoull(hp->size, nil, 8);
511 }
512 
513 /*
514  * return the number of bytes recorded in the archive.
515  */
516 static Off
arsize(Hdr * hp)517 arsize(Hdr *hp)
518 {
519 	if(isdir(hp) || islink(hp->linkflag))
520 		return 0;
521 	return hdrsize(hp);
522 }
523 
524 static Hdr *
readhdr(int ar)525 readhdr(int ar)
526 {
527 	long hdrcksum;
528 	Hdr *hp;
529 
530 	hp = getblkrd(ar, Alldata);
531 	if (hp == nil)
532 		sysfatal("unexpected EOF instead of archive header");
533 	if (eotar(hp))			/* end-of-archive block? */
534 		return nil;
535 	hdrcksum = strtoul(hp->chksum, nil, 8);
536 	if (chksum(hp) != hdrcksum)
537 		sysfatal("bad archive header checksum: name %.64s... %ld %ld",
538 			hp->name, chksum(hp), hdrcksum);
539 	nexthdr += Tblock*(1 + BYTES2TBLKS(arsize(hp)));
540 	return hp;
541 }
542 
543 /*
544  * tar r[c]
545  */
546 
547 /*
548  * if name is longer than Namsiz bytes, try to split it at a slash and fit the
549  * pieces into hp->prefix and hp->name.
550  */
551 static int
putfullname(Hdr * hp,char * name)552 putfullname(Hdr *hp, char *name)
553 {
554 	int namlen, pfxlen;
555 	char *sl, *osl;
556 	String *slname = nil;
557 
558 	if (isdir(hp)) {
559 		slname = s_new();
560 		s_append(slname, name);
561 		s_append(slname, "/");		/* posix requires this */
562 		name = s_to_c(slname);
563 	}
564 
565 	namlen = strlen(name);
566 	if (namlen <= Namsiz) {
567 		strncpy(hp->name, name, Namsiz);
568 		hp->prefix[0] = '\0';		/* ustar paranoia */
569 		return 0;
570 	}
571 
572 	if (!posix || namlen > Maxname) {
573 		fprint(2, "%s: name too long for tar header: %s\n",
574 			argv0, name);
575 		return -1;
576 	}
577 	/*
578 	 * try various splits until one results in pieces that fit into the
579 	 * appropriate fields of the header.  look for slashes from right
580 	 * to left, in the hopes of putting the largest part of the name into
581 	 * hp->prefix, which is larger than hp->name.
582 	 */
583 	sl = strrchr(name, '/');
584 	while (sl != nil) {
585 		pfxlen = sl - name;
586 		if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= Namsiz)
587 			break;
588 		osl = sl;
589 		*osl = '\0';
590 		sl = strrchr(name, '/');
591 		*osl = '/';
592 	}
593 	if (sl == nil) {
594 		fprint(2, "%s: name can't be split to fit tar header: %s\n",
595 			argv0, name);
596 		return -1;
597 	}
598 	*sl = '\0';
599 	strncpy(hp->prefix, name, sizeof hp->prefix);
600 	*sl++ = '/';
601 	strncpy(hp->name, sl, sizeof hp->name);
602 	if (slname)
603 		s_free(slname);
604 	return 0;
605 }
606 
607 static int
mkhdr(Hdr * hp,Dir * dir,char * file)608 mkhdr(Hdr *hp, Dir *dir, char *file)
609 {
610 	/*
611 	 * these fields run together, so we format them in order and don't use
612 	 * snprint.
613 	 */
614 	sprint(hp->mode, "%6lo ", dir->mode & 0777);
615 	sprint(hp->uid, "%6o ", aruid);
616 	sprint(hp->gid, "%6o ", argid);
617 	if (dir->length >= (Off)1<<32) {
618 		static int printed;
619 
620 		if (!printed) {
621 			printed = 1;
622 			fprint(2, "%s: storing large sizes in \"base 256\"\n", argv0);
623 		}
624 		hp->size[0] = (char)Binsize;
625 		/* emit so-called `base 256' representation of size */
626 		putbe((uchar *)hp->size+1, dir->length, sizeof hp->size - 2);
627 		hp->size[sizeof hp->size - 1] = ' ';
628 	} else
629 		sprint(hp->size, "%11lluo ", dir->length);
630 	sprint(hp->mtime, "%11luo ", dir->mtime);
631 	hp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1);
632 	putfullname(hp, file);
633 	if (posix) {
634 		strncpy(hp->magic, "ustar", sizeof hp->magic);
635 		strncpy(hp->version, "00", sizeof hp->version);
636 		strncpy(hp->uname, dir->uid, sizeof hp->uname);
637 		strncpy(hp->gname, dir->gid, sizeof hp->gname);
638 	}
639 	sprint(hp->chksum, "%6luo", chksum(hp));
640 	return 0;
641 }
642 
643 static void addtoar(int ar, char *file, char *shortf);
644 
645 static void
addtreetoar(int ar,char * file,char * shortf,int fd)646 addtreetoar(int ar, char *file, char *shortf, int fd)
647 {
648 	int n;
649 	Dir *dent, *dirents;
650 	String *name = s_new();
651 
652 	n = dirreadall(fd, &dirents);
653 	close(fd);
654 	if (n == 0)
655 		return;
656 
657 	if (chdir(shortf) < 0)
658 		sysfatal("chdir %s: %r", file);
659 	if (DEBUG)
660 		fprint(2, "chdir %s\t# %s\n", shortf, file);
661 
662 	for (dent = dirents; dent < dirents + n; dent++) {
663 		s_reset(name);
664 		s_append(name, file);
665 		s_append(name, "/");
666 		s_append(name, dent->name);
667 		addtoar(ar, s_to_c(name), dent->name);
668 	}
669 	s_free(name);
670 	free(dirents);
671 
672 	/*
673 	 * this assumes that shortf is just one component, which is true
674 	 * during directory descent, but not necessarily true of command-line
675 	 * arguments.  Our caller (or addtoar's) must reset the working
676 	 * directory if necessary.
677 	 */
678 	if (chdir("..") < 0)
679 		sysfatal("chdir %s/..: %r", file);
680 	if (DEBUG)
681 		fprint(2, "chdir ..\n");
682 }
683 
684 static void
addtoar(int ar,char * file,char * shortf)685 addtoar(int ar, char *file, char *shortf)
686 {
687 	int n, fd, isdir;
688 	long bytes;
689 	ulong blksleft, blksread;
690 	Hdr *hbp;
691 	Dir *dir;
692 	String *name = nil;
693 
694 	if (shortf[0] == '#') {
695 		name = s_new();
696 		s_append(name, "./");
697 		s_append(name, shortf);
698 		shortf = s_to_c(name);
699 	}
700 
701 	fd = open(shortf, OREAD);
702 	if (fd < 0) {
703 		fprint(2, "%s: can't open %s: %r\n", argv0, file);
704 		if (name)
705 			s_free(name);
706 		return;
707 	}
708 	dir = dirfstat(fd);
709 	if (dir == nil)
710 		sysfatal("can't fstat %s: %r", file);
711 
712 	hbp = getblkz(ar);
713 	isdir = !!(dir->qid.type&QTDIR);
714 	if (mkhdr(hbp, dir, file) < 0) {
715 		putbackblk(ar);
716 		free(dir);
717 		close(fd);
718 		if (name)
719 			s_free(name);
720 		return;
721 	}
722 	putblk(ar);
723 
724 	blksleft = BYTES2TBLKS(dir->length);
725 	free(dir);
726 
727 	if (isdir)
728 		addtreetoar(ar, file, shortf, fd);
729 	else {
730 		for (; blksleft > 0; blksleft -= blksread) {
731 			hbp = getblke(ar);
732 			blksread = gothowmany(blksleft);
733 			bytes = blksread * Tblock;
734 			n = readn(fd, hbp, bytes);
735 			if (n < 0)
736 				sysfatal("error reading %s: %r", file);
737 			/*
738 			 * ignore EOF.  zero any partial block to aid
739 			 * compression and emergency recovery of data.
740 			 */
741 			if (n < Tblock)
742 				memset((uchar*)hbp + n, 0, bytes - n);
743 			putblkmany(ar, blksread);
744 		}
745 		close(fd);
746 		if (verbose)
747 			fprint(2, "%s\n", file);
748 	}
749 	if (name)
750 		s_free(name);
751 }
752 
753 static char *
replace(char ** argv)754 replace(char **argv)
755 {
756 	int i, ar;
757 	ulong blksleft, blksread;
758 	Off bytes;
759 	Hdr *hp;
760 	Compress *comp = nil;
761 	Pushstate ps;
762 
763 	if (usefile && docreate) {
764 		ar = create(usefile, OWRITE, 0666);
765 		if (docompress)
766 			comp = compmethod(usefile);
767 	} else if (usefile)
768 		ar = open(usefile, ORDWR);
769 	else
770 		ar = Stdout;
771 	if (comp)
772 		ar = push(ar, comp->comp, Output, &ps);
773 	if (ar < 0)
774 		sysfatal("can't open archive %s: %r", usefile);
775 
776 	if (usefile && !docreate) {
777 		/* skip quickly to the end */
778 		while ((hp = readhdr(ar)) != nil) {
779 			bytes = arsize(hp);
780 			for (blksleft = BYTES2TBLKS(bytes);
781 			     blksleft > 0 && getblkrd(ar, Justnxthdr) != nil;
782 			     blksleft -= blksread) {
783 				blksread = gothowmany(blksleft);
784 				putreadblks(ar, blksread);
785 			}
786 		}
787 		/*
788 		 * we have just read the end-of-archive Tblock.
789 		 * now seek back over the (big) archive block containing it,
790 		 * and back up curblk ptr over end-of-archive Tblock in memory.
791 		 */
792 		if (seek(ar, blkoff, 0) < 0)
793 			sysfatal("can't seek back over end-of-archive: %r");
794 		curblk--;
795 	}
796 
797 	for (i = 0; argv[i] != nil; i++) {
798 		addtoar(ar, argv[i], argv[i]);
799 		chdir(origdir);		/* for correctness & profiling */
800 	}
801 
802 	/* write end-of-archive marker */
803 	getblkz(ar);
804 	putblk(ar);
805 	getblkz(ar);
806 	putlastblk(ar);
807 
808 	if (comp)
809 		return pushclose(&ps);
810 	if (ar > Stderr)
811 		close(ar);
812 	return nil;
813 }
814 
815 /*
816  * tar [xt]
817  */
818 
819 /* is pfx a file-name prefix of name? */
820 static int
prefix(char * name,char * pfx)821 prefix(char *name, char *pfx)
822 {
823 	int pfxlen = strlen(pfx);
824 	char clpfx[Maxname+1];
825 
826 	if (pfxlen > Maxname)
827 		return 0;
828 	strcpy(clpfx, pfx);
829 	cleanname(clpfx);
830 	return strncmp(pfx, name, pfxlen) == 0 &&
831 		(name[pfxlen] == '\0' || name[pfxlen] == '/');
832 }
833 
834 static int
match(char * name,char ** argv)835 match(char *name, char **argv)
836 {
837 	int i;
838 	char clname[Maxname+1];
839 
840 	if (argv[0] == nil)
841 		return 1;
842 	strcpy(clname, name);
843 	cleanname(clname);
844 	for (i = 0; argv[i] != nil; i++)
845 		if (prefix(clname, argv[i]))
846 			return 1;
847 	return 0;
848 }
849 
850 static void
cantcreate(char * s,int mode)851 cantcreate(char *s, int mode)
852 {
853 	int len;
854 	static char *last;
855 
856 	/*
857 	 * Always print about files.  Only print about directories
858 	 * we haven't printed about.  (Assumes archive is ordered
859 	 * nicely.)
860 	 */
861 	if(mode&DMDIR){
862 		if(last){
863 			/* already printed this directory */
864 			if(strcmp(s, last) == 0)
865 				return;
866 			/* printed a higher directory, so printed this one */
867 			len = strlen(s);
868 			if(memcmp(s, last, len) == 0 && last[len] == '/')
869 				return;
870 		}
871 		/* save */
872 		free(last);
873 		last = strdup(s);
874 	}
875 	fprint(2, "%s: can't create %s: %r\n", argv0, s);
876 }
877 
878 static int
makedir(char * s)879 makedir(char *s)
880 {
881 	int f;
882 
883 	if (access(s, AEXIST) == 0)
884 		return -1;
885 	f = create(s, OREAD, DMDIR | 0777);
886 	if (f >= 0)
887 		close(f);
888 	else
889 		cantcreate(s, DMDIR);
890 	return f;
891 }
892 
893 static int
mkpdirs(char * s)894 mkpdirs(char *s)
895 {
896 	int err;
897 	char *p;
898 
899 	p = s;
900 	err = 0;
901 	while (!err && (p = strchr(p+1, '/')) != nil) {
902 		*p = '\0';
903 		err = (access(s, AEXIST) < 0 && makedir(s) < 0);
904 		*p = '/';
905 	}
906 	return -err;
907 }
908 
909 /* Call access but preserve the error string. */
910 static int
xaccess(char * name,int mode)911 xaccess(char *name, int mode)
912 {
913 	char err[ERRMAX];
914 	int rv;
915 
916 	err[0] = 0;
917 	errstr(err, sizeof err);
918 	rv = access(name, mode);
919 	errstr(err, sizeof err);
920 	return rv;
921 }
922 
923 /* copy a file from the archive into the filesystem */
924 /* fname is result of name(), so has two extra bytes at beginning */
925 static void
extract1(int ar,Hdr * hp,char * fname)926 extract1(int ar, Hdr *hp, char *fname)
927 {
928 	int wrbytes, fd = -1, dir = 0;
929 	long mtime = strtol(hp->mtime, nil, 8);
930 	ulong mode = strtoul(hp->mode, nil, 8) & 0777;
931 	Off bytes = hdrsize(hp);		/* for printing */
932 	ulong blksread, blksleft = BYTES2TBLKS(arsize(hp));
933 	Hdr *hbp;
934 
935 	if (isdir(hp)) {
936 		mode |= DMDIR|0700;
937 		dir = 1;
938 	}
939 	switch (hp->linkflag) {
940 	case LF_LINK:
941 	case LF_SYMLINK1:
942 	case LF_SYMLINK2:
943 	case LF_FIFO:
944 		blksleft = 0;
945 		break;
946 	}
947 	if (relative) {
948 		if(fname[0] == '/')
949 			*--fname = '.';
950 		else if(fname[0] == '#'){
951 			*--fname = '/';
952 			*--fname = '.';
953 		}
954 	}
955 	if (verb == Xtract) {
956 		cleanname(fname);
957 		switch (hp->linkflag) {
958 		case LF_LINK:
959 		case LF_SYMLINK1:
960 		case LF_SYMLINK2:
961 			fprint(2, "%s: can't make (sym)link %s\n",
962 				argv0, fname);
963 			break;
964 		case LF_FIFO:
965 			fprint(2, "%s: can't make fifo %s\n", argv0, fname);
966 			break;
967 		default:
968 			if (!keepexisting || access(fname, AEXIST) < 0) {
969 				int rw = (dir? OREAD: OWRITE);
970 
971 				fd = create(fname, rw, mode);
972 				if (fd < 0) {
973 					mkpdirs(fname);
974 					fd = create(fname, rw, mode);
975 				}
976 				if (fd < 0 &&
977 				    (!dir || xaccess(fname, AEXIST) < 0))
978 				    	cantcreate(fname, mode);
979 			}
980 			if (fd >= 0 && verbose)
981 				fprint(2, "%s\n", fname);
982 			break;
983 		}
984 	} else if (verbose) {
985 		char *cp = ctime(mtime);
986 
987 		print("%M %8lld %-12.12s %-4.4s %s\n",
988 			mode, bytes, cp+4, cp+24, fname);
989 	} else
990 		print("%s\n", fname);
991 
992 	if (blksleft == 0)
993 		bytes = 0;
994 	for (; blksleft > 0; blksleft -= blksread) {
995 		hbp = getblkrd(ar, (fd >= 0? Alldata: Justnxthdr));
996 		if (hbp == nil)
997 			sysfatal("unexpected EOF on archive extracting %s",
998 				fname);
999 		blksread = gothowmany(blksleft);
1000 		if (blksread <= 0)
1001 			fprint(2, "%s: got %ld blocks reading %s!\n",
1002 				argv0, blksread, fname);
1003 		wrbytes = Tblock*blksread;
1004 		if(wrbytes > bytes)
1005 			wrbytes = bytes;
1006 		if (fd >= 0 && write(fd, hbp, wrbytes) != wrbytes)
1007 			sysfatal("write error on %s: %r", fname);
1008 		putreadblks(ar, blksread);
1009 		bytes -= wrbytes;
1010 	}
1011 	if (bytes > 0)
1012 		fprint(2,
1013 		    "%s: %lld bytes uncopied at eof; %s not fully extracted\n",
1014 			argv0, bytes, fname);
1015 	if (fd >= 0) {
1016 		/*
1017 		 * directories should be wstated after we're done
1018 		 * creating files in them.
1019 		 */
1020 		if (settime) {
1021 			Dir nd;
1022 
1023 			nulldir(&nd);
1024 			nd.mtime = mtime;
1025 			dirfwstat(fd, &nd);
1026 			if (isustar(hp)) {
1027 				nulldir(&nd);
1028 				nd.gid = hp->gname;
1029 				dirfwstat(fd, &nd);
1030 			}
1031 		}
1032 		close(fd);
1033 	}
1034 }
1035 
1036 static void
skip(int ar,Hdr * hp,char * fname)1037 skip(int ar, Hdr *hp, char *fname)
1038 {
1039 	ulong blksleft, blksread;
1040 	Hdr *hbp;
1041 
1042 	for (blksleft = BYTES2TBLKS(arsize(hp)); blksleft > 0;
1043 	     blksleft -= blksread) {
1044 		hbp = getblkrd(ar, Justnxthdr);
1045 		if (hbp == nil)
1046 			sysfatal("unexpected EOF on archive extracting %s",
1047 				fname);
1048 		blksread = gothowmany(blksleft);
1049 		putreadblks(ar, blksread);
1050 	}
1051 }
1052 
1053 static char *
extract(char ** argv)1054 extract(char **argv)
1055 {
1056 	int ar;
1057 	char *longname;
1058 	Hdr *hp;
1059 	Compress *comp = nil;
1060 	Pushstate ps;
1061 
1062 	if (usefile) {
1063 		ar = open(usefile, OREAD);
1064 		comp = compmethod(usefile);
1065 	} else
1066 		ar = Stdin;
1067 	if (comp)
1068 		ar = push(ar, comp->decomp, Input, &ps);
1069 	if (ar < 0)
1070 		sysfatal("can't open archive %s: %r", usefile);
1071 
1072 	while ((hp = readhdr(ar)) != nil) {
1073 		longname = name(hp);
1074 		if (match(longname, argv))
1075 			extract1(ar, hp, longname);
1076 		else
1077 			skip(ar, hp, longname);
1078 	}
1079 
1080 	if (comp)
1081 		return pushclose(&ps);
1082 	if (ar > Stderr)
1083 		close(ar);
1084 	return nil;
1085 }
1086 
1087 void
main(int argc,char * argv[])1088 main(int argc, char *argv[])
1089 {
1090 	int errflg = 0;
1091 	char *ret = nil;
1092 
1093 	fmtinstall('M', dirmodefmt);
1094 
1095 	if(sizeof(Hdr) != Tblock)
1096 		sysfatal("padding in hdr should be %d", Tblock-sizeof(Hdr)+sizeof(curblk->pad));
1097 	TARGBEGIN {
1098 	case 'c':
1099 		docreate++;
1100 		verb = Replace;
1101 		break;
1102 	case 'f':
1103 		usefile = EARGF(usage());
1104 		break;
1105 	case 'g':
1106 		argid = strtoul(EARGF(usage()), 0, 0);
1107 		break;
1108 	case 'k':
1109 		keepexisting++;
1110 		break;
1111 	case 'm':	/* compatibility */
1112 		settime = 0;
1113 		break;
1114 	case 'p':
1115 		posix++;
1116 		break;
1117 	case 'P':
1118 		posix = 0;
1119 		break;
1120 	case 'r':
1121 		verb = Replace;
1122 		break;
1123 	case 'R':
1124 		relative = 0;
1125 		break;
1126 	case 't':
1127 		verb = Toc;
1128 		break;
1129 	case 'T':
1130 		settime++;
1131 		break;
1132 	case 'u':
1133 		aruid = strtoul(EARGF(usage()), 0, 0);
1134 		break;
1135 	case 'v':
1136 		verbose++;
1137 		break;
1138 	case 'x':
1139 		verb = Xtract;
1140 		break;
1141 	case 'z':
1142 		docompress++;
1143 		break;
1144 	case '-':
1145 		break;
1146 	default:
1147 		fprint(2, "tar: unknown letter %C\n", TARGC());
1148 		errflg++;
1149 		break;
1150 	} TARGEND
1151 
1152 	if (argc < 0 || errflg)
1153 		usage();
1154 
1155 	initblks();
1156 	switch (verb) {
1157 	case Toc:
1158 	case Xtract:
1159 		ret = extract(argv);
1160 		break;
1161 	case Replace:
1162 		if (getwd(origdir, sizeof origdir) == nil)
1163 			strcpy(origdir, "/tmp");
1164 		ret = replace(argv);
1165 		break;
1166 	default:
1167 		usage();
1168 		break;
1169 	}
1170 	exits(ret);
1171 }
1172