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