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