Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
5 #define TBLOCK 512
6 #define NBLOCK 40 /* maximum blocksize */
7 #define DBLOCK 20 /* default blocksize */
8 #define NAMSIZ 100
9 union hblock
10 {
11 char dummy[TBLOCK];
12 struct header
13 {
14 char name[NAMSIZ];
15 char mode[8];
16 char uid[8];
17 char gid[8];
18 char size[12];
19 char mtime[12];
20 char chksum[8];
21 char linkflag;
22 char linkname[NAMSIZ];
23 } dbuf;
24 } dblock, tbuf[NBLOCK];
26 Dir *stbuf;
27 Biobuf bout;
29 int rflag, xflag, vflag, tflag, mt, cflag, fflag, Tflag, Rflag;
30 int uflag, gflag;
31 int chksum, recno, first;
32 int nblock = DBLOCK;
34 void usage(void);
35 void dorep(char **);
36 int endtar(void);
37 void getdir(void);
38 void passtar(void);
39 void putfile(char*, char *, char *);
40 void doxtract(char **);
41 void dotable(void);
42 void putempty(void);
43 void longt(Dir *);
44 int checkdir(char *, int, Qid*);
45 void tomodes(Dir *);
46 int checksum(void);
47 int checkupdate(char *);
48 int prefix(char *, char *);
49 int readtar(char *);
50 int writetar(char *);
51 void backtar(void);
52 void flushtar(void);
53 void affix(int, char *);
54 int volprompt(void);
55 void
56 main(int argc, char **argv)
57 {
58 char *usefile;
59 char *cp, *ap;
61 if (argc < 2)
62 usage();
64 Binit(&bout, 1, OWRITE);
65 usefile = 0;
66 argv[argc] = 0;
67 argv++;
68 for (cp = *argv++; *cp; cp++)
69 switch(*cp) {
70 case 'f':
71 usefile = *argv++;
72 if(!usefile)
73 usage();
74 fflag++;
75 break;
76 case 'u':
77 ap = *argv++;
78 if(!ap)
79 usage();
80 uflag = strtoul(ap, 0, 0);
81 break;
82 case 'g':
83 ap = *argv++;
84 if(!ap)
85 usage();
86 gflag = strtoul(ap, 0, 0);
87 break;
88 case 'c':
89 cflag++;
90 rflag++;
91 break;
92 case 'r':
93 rflag++;
94 break;
95 case 'v':
96 vflag++;
97 break;
98 case 'x':
99 xflag++;
100 break;
101 case 'T':
102 Tflag++;
103 break;
104 case 't':
105 tflag++;
106 break;
107 case 'R':
108 Rflag++;
109 break;
110 case '-':
111 break;
112 default:
113 fprint(2, "tar: %c: unknown option\n", *cp);
114 usage();
117 fmtinstall('M', dirmodefmt);
119 if (rflag) {
120 if (!usefile) {
121 if (cflag == 0) {
122 fprint(2, "tar: can only create standard output archives\n");
123 exits("arg error");
125 mt = dup(1, -1);
126 nblock = 1;
128 else if ((mt = open(usefile, ORDWR)) < 0) {
129 if (cflag == 0 || (mt = create(usefile, OWRITE, 0666)) < 0) {
130 fprint(2, "tar: cannot open %s: %r\n", usefile);
131 exits("open");
134 dorep(argv);
136 else if (xflag) {
137 if (!usefile) {
138 mt = dup(0, -1);
139 nblock = 1;
141 else if ((mt = open(usefile, OREAD)) < 0) {
142 fprint(2, "tar: cannot open %s: %r\n", usefile);
143 exits("open");
145 doxtract(argv);
147 else if (tflag) {
148 if (!usefile) {
149 mt = dup(0, -1);
150 nblock = 1;
152 else if ((mt = open(usefile, OREAD)) < 0) {
153 fprint(2, "tar: cannot open %s: %r\n", usefile);
154 exits("open");
156 dotable();
158 else
159 usage();
160 exits(0);
163 void
164 usage(void)
166 fprint(2, "tar: usage tar {txrc}[Rvf] [tarfile] file1 file2...\n");
167 exits("usage");
170 void
171 dorep(char **argv)
173 char cwdbuf[2048], *cwd, thisdir[2048];
174 char *cp, *cp2;
175 int cd;
177 if (getwd(cwdbuf, sizeof(cwdbuf)) == 0) {
178 fprint(2, "tar: can't find current directory: %r\n");
179 exits("cwd");
181 cwd = cwdbuf;
183 if (!cflag) {
184 getdir();
185 do {
186 passtar();
187 getdir();
188 } while (!endtar());
191 while (*argv) {
192 cp2 = *argv;
193 if (!strcmp(cp2, "-C") && argv[1]) {
194 argv++;
195 if (chdir(*argv) < 0)
196 perror(*argv);
197 cwd = *argv;
198 argv++;
199 continue;
201 cd = 0;
202 for (cp = *argv; *cp; cp++)
203 if (*cp == '/')
204 cp2 = cp;
205 if (cp2 != *argv) {
206 *cp2 = '\0';
207 chdir(*argv);
208 if(**argv == '/')
209 strncpy(thisdir, *argv, sizeof(thisdir));
210 else
211 snprint(thisdir, sizeof(thisdir), "%s/%s", cwd, *argv);
212 *cp2 = '/';
213 cp2++;
214 cd = 1;
215 } else
216 strncpy(thisdir, cwd, sizeof(thisdir));
217 putfile(thisdir, *argv++, cp2);
218 if(cd && chdir(cwd) < 0) {
219 fprint(2, "tar: can't cd back to %s: %r\n", cwd);
220 exits("cwd");
223 putempty();
224 putempty();
225 flushtar();
228 int
229 endtar(void)
231 if (dblock.dbuf.name[0] == '\0') {
232 backtar();
233 return(1);
235 else
236 return(0);
239 void
240 getdir(void)
242 Dir *sp;
244 readtar((char*)&dblock);
245 if (dblock.dbuf.name[0] == '\0')
246 return;
247 if(stbuf == nil){
248 stbuf = malloc(sizeof(Dir));
249 if(stbuf == nil) {
250 fprint(2, "tar: can't malloc: %r\n");
251 exits("malloc");
254 sp = stbuf;
255 sp->mode = strtol(dblock.dbuf.mode, 0, 8);
256 sp->uid = "adm";
257 sp->gid = "adm";
258 sp->length = strtol(dblock.dbuf.size, 0, 8);
259 sp->mtime = strtol(dblock.dbuf.mtime, 0, 8);
260 chksum = strtol(dblock.dbuf.chksum, 0, 8);
261 if (chksum != checksum()) {
262 fprint(2, "directory checksum error\n");
263 exits("checksum error");
265 sp->qid.type = 0;
266 /* the mode test is ugly but sometimes necessary */
267 if (dblock.dbuf.linkflag == '5' || (sp->mode&0170000) == 040000) {
268 sp->qid.type |= QTDIR;
269 sp->mode |= DMDIR;
273 void
274 passtar(void)
276 long blocks;
277 char buf[TBLOCK];
279 if (dblock.dbuf.linkflag == '1' || dblock.dbuf.linkflag == 's')
280 return;
281 blocks = stbuf->length;
282 blocks += TBLOCK-1;
283 blocks /= TBLOCK;
285 while (blocks-- > 0)
286 readtar(buf);
289 void
290 putfile(char *dir, char *longname, char *sname)
292 int infile;
293 long blocks;
294 char buf[TBLOCK];
295 char curdir[4096];
296 char shortname[4096];
297 char *cp, *cp2;
298 Dir *db;
299 int i, n;
301 if(strlen(sname) > sizeof shortname - 3){
302 fprint(2, "tar: %s: name too long (max %d)\n", sname, sizeof shortname - 3);
303 return;
306 snprint(shortname, sizeof shortname, "./%s", sname);
307 infile = open(shortname, OREAD);
308 if (infile < 0) {
309 fprint(2, "tar: %s: cannot open file - %r\n", longname);
310 return;
313 if(stbuf != nil)
314 free(stbuf);
315 stbuf = dirfstat(infile);
317 if (stbuf->qid.type & QTDIR) {
318 /* Directory */
319 for (i = 0, cp = buf; *cp++ = longname[i++];);
320 *--cp = '/';
321 *++cp = 0;
322 if( (cp - buf) >= NAMSIZ) {
323 fprint(2, "tar: %s: file name too long\n", longname);
324 close(infile);
325 return;
327 stbuf->length = 0;
328 tomodes(stbuf);
329 strcpy(dblock.dbuf.name,buf);
330 dblock.dbuf.linkflag = '5'; /* Directory */
331 sprint(dblock.dbuf.chksum, "%6o", checksum());
332 writetar( (char *) &dblock);
333 if (chdir(shortname) < 0) {
334 fprint(2, "tar: can't cd to %s: %r\n", shortname);
335 snprint(curdir, sizeof(curdir), "cd %s", shortname);
336 exits(curdir);
338 sprint(curdir, "%s/%s", dir, sname);
339 while ((n = dirread(infile, &db)) > 0) {
340 for(i = 0; i < n; i++){
341 strncpy(cp, db[i].name, sizeof buf - (cp-buf));
342 putfile(curdir, buf, db[i].name);
343 }free(db);
345 close(infile);
346 if (chdir(dir) < 0 && chdir("..") < 0) {
347 fprint(2, "tar: can't cd to ..(%s): %r\n", dir);
348 snprint(curdir, sizeof(curdir), "cd ..(%s)", dir);
349 exits(curdir);
351 return;
355 tomodes(stbuf);
357 cp2 = longname;
358 for (cp = dblock.dbuf.name, i=0; (*cp++ = *cp2++) && i < NAMSIZ; i++);
359 if (i >= NAMSIZ) {
360 fprint(2, "%s: file name too long\n", longname);
361 close(infile);
362 return;
365 blocks = (stbuf->length + (TBLOCK-1)) / TBLOCK;
366 if (vflag) {
367 fprint(2, "a %s ", longname);
368 fprint(2, "%ld blocks\n", blocks);
370 dblock.dbuf.linkflag = 0; /* Regular file */
371 sprint(dblock.dbuf.chksum, "%6o", checksum());
372 writetar( (char *) &dblock);
374 while ((i = readn(infile, buf, TBLOCK)) > 0 && blocks > 0) {
375 writetar(buf);
376 blocks--;
378 close(infile);
379 if (blocks != 0 || i != 0)
380 fprint(2, "%s: file changed size\n", longname);
381 while (blocks-- > 0)
382 putempty();
386 void
387 doxtract(char **argv)
389 Dir null;
390 long blocks, bytes;
391 char buf[TBLOCK], outname[NAMSIZ+4];
392 char **cp;
393 int ofile;
395 for (;;) {
396 getdir();
397 if (endtar())
398 break;
400 if (*argv == 0)
401 goto gotit;
403 for (cp = argv; *cp; cp++)
404 if (prefix(*cp, dblock.dbuf.name))
405 goto gotit;
406 passtar();
407 continue;
409 gotit:
410 if(checkdir(dblock.dbuf.name, stbuf->mode, &(stbuf->qid)))
411 continue;
413 if (dblock.dbuf.linkflag == '1') {
414 fprint(2, "tar: can't link %s %s\n",
415 dblock.dbuf.linkname, dblock.dbuf.name);
416 remove(dblock.dbuf.name);
417 continue;
419 if (dblock.dbuf.linkflag == 's') {
420 fprint(2, "tar: %s: cannot symlink\n", dblock.dbuf.name);
421 continue;
423 if(dblock.dbuf.name[0] != '/' || Rflag)
424 sprint(outname, "./%s", dblock.dbuf.name);
425 else
426 strcpy(outname, dblock.dbuf.name);
427 if ((ofile = create(outname, OWRITE, stbuf->mode & 0777)) < 0) {
428 fprint(2, "tar: %s - cannot create: %r\n", outname);
429 passtar();
430 continue;
433 blocks = ((bytes = stbuf->length) + TBLOCK-1)/TBLOCK;
434 if (vflag)
435 fprint(2, "x %s, %ld bytes\n",
436 dblock.dbuf.name, bytes);
437 while (blocks-- > 0) {
438 readtar(buf);
439 if (bytes > TBLOCK) {
440 if (write(ofile, buf, TBLOCK) < 0) {
441 fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
442 exits("extract write");
444 } else
445 if (write(ofile, buf, bytes) < 0) {
446 fprint(2, "tar: %s: HELP - extract write error: %r\n", dblock.dbuf.name);
447 exits("extract write");
449 bytes -= TBLOCK;
451 if(Tflag){
452 nulldir(&null);
453 null.mtime = stbuf->mtime;
454 dirfwstat(ofile, &null);
456 close(ofile);
460 void
461 dotable(void)
463 for (;;) {
464 getdir();
465 if (endtar())
466 break;
467 if (vflag)
468 longt(stbuf);
469 Bprint(&bout, "%s", dblock.dbuf.name);
470 if (dblock.dbuf.linkflag == '1')
471 Bprint(&bout, " linked to %s", dblock.dbuf.linkname);
472 if (dblock.dbuf.linkflag == 's')
473 Bprint(&bout, " -> %s", dblock.dbuf.linkname);
474 Bprint(&bout, "\n");
475 passtar();
479 void
480 putempty(void)
482 char buf[TBLOCK];
484 memset(buf, 0, TBLOCK);
485 writetar(buf);
488 void
489 longt(Dir *st)
491 char *cp;
493 Bprint(&bout, "%M %4d/%1d ", st->mode, 0, 0); /* 0/0 uid/gid */
494 Bprint(&bout, "%8lld", st->length);
495 cp = ctime(st->mtime);
496 Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24);
499 int
500 checkdir(char *name, int mode, Qid *qid)
502 char *cp;
503 int f;
504 Dir *d, null;
506 if(Rflag && *name == '/')
507 name++;
508 cp = name;
509 if(*cp == '/')
510 cp++;
511 for (; *cp; cp++) {
512 if (*cp == '/') {
513 *cp = '\0';
514 if (access(name, 0) < 0) {
515 f = create(name, OREAD, DMDIR + 0775L);
516 if(f < 0)
517 fprint(2, "tar: mkdir %s failed: %r\n", name);
518 close(f);
520 *cp = '/';
524 /* if this is a directory, chmod it to the mode in the tar plus 700 */
525 if(cp[-1] == '/' || (qid->type&QTDIR)){
526 if((d=dirstat(name)) != 0){
527 nulldir(&null);
528 null.mode = DMDIR | (mode & 0777) | 0700;
529 dirwstat(name, &null);
530 free(d);
532 return 1;
533 } else
534 return 0;
537 void
538 tomodes(Dir *sp)
540 char *cp;
542 for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
543 *cp = '\0';
544 sprint(dblock.dbuf.mode, "%6lo ", sp->mode & 0777);
545 sprint(dblock.dbuf.uid, "%6o ", uflag);
546 sprint(dblock.dbuf.gid, "%6o ", gflag);
547 sprint(dblock.dbuf.size, "%11llo ", sp->length);
548 sprint(dblock.dbuf.mtime, "%11lo ", sp->mtime);
551 int
552 checksum(void)
554 int i;
555 char *cp;
557 for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
558 *cp = ' ';
559 i = 0;
560 for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
561 i += *cp & 0xff;
562 return(i);
565 int
566 prefix(char *s1, char *s2)
568 while (*s1)
569 if (*s1++ != *s2++)
570 return(0);
571 if (*s2)
572 return(*s2 == '/');
573 return(1);
576 int
577 readtar(char *buffer)
579 int i;
581 if (recno >= nblock || first == 0) {
582 if ((i = readn(mt, tbuf, TBLOCK*nblock)) <= 0) {
583 fprint(2, "tar: archive read error: %r\n");
584 exits("archive read");
586 if (first == 0) {
587 if ((i % TBLOCK) != 0) {
588 fprint(2, "tar: archive blocksize error: %r\n");
589 exits("blocksize");
591 i /= TBLOCK;
592 if (i != nblock) {
593 fprint(2, "tar: blocksize = %d\n", i);
594 nblock = i;
597 recno = 0;
599 first = 1;
600 memmove(buffer, &tbuf[recno++], TBLOCK);
601 return(TBLOCK);
604 int
605 writetar(char *buffer)
607 first = 1;
608 if (recno >= nblock) {
609 if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
610 fprint(2, "tar: archive write error: %r\n");
611 exits("write");
613 recno = 0;
615 memmove(&tbuf[recno++], buffer, TBLOCK);
616 if (recno >= nblock) {
617 if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) {
618 fprint(2, "tar: archive write error: %r\n");
619 exits("write");
621 recno = 0;
623 return(TBLOCK);
626 /*
627 * backup over last tar block
628 */
629 void
630 backtar(void)
632 seek(mt, -TBLOCK*nblock, 1);
633 recno--;
636 void
637 flushtar(void)
639 write(mt, tbuf, TBLOCK*nblock);