1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 #include <bio.h> 6 7 #define TBLOCK 512 8 #define NBLOCK 40 /* maximum blocksize */ 9 #define DBLOCK 20 /* default blocksize */ 10 #define NAMSIZ 100 11 12 enum { 13 Maxpfx = 155, /* from POSIX */ 14 Maxname = NAMSIZ + 1 + Maxpfx, 15 }; 16 17 /* POSIX link flags */ 18 enum { 19 LF_PLAIN1 = '\0', 20 LF_PLAIN2 = '0', 21 LF_LINK = '1', 22 LF_SYMLINK1 = '2', 23 LF_SYMLINK2 = 's', 24 LF_CHR = '3', 25 LF_BLK = '4', 26 LF_DIR = '5', 27 LF_FIFO = '6', 28 LF_CONTIG = '7', 29 /* 'A' - 'Z' are reserved for custom implementations */ 30 }; 31 32 #define islink(lf) (isreallink(lf) || issymlink(lf)) 33 #define isreallink(lf) ((lf) == LF_LINK) 34 #define issymlink(lf) ((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2) 35 union hblock 36 { 37 char dummy[TBLOCK]; 38 struct header 39 { 40 char name[NAMSIZ]; 41 char mode[8]; 42 char uid[8]; 43 char gid[8]; 44 char size[12]; 45 char mtime[12]; 46 char chksum[8]; 47 char linkflag; 48 char linkname[NAMSIZ]; 49 /* rest are defined by POSIX's ustar format; see p1003.2b */ 50 char magic[6]; /* "ustar" */ 51 char version[2]; 52 char uname[32]; 53 char gname[32]; 54 char devmajor[8]; 55 char devminor[8]; 56 char prefix[155]; /* if non-null, path = prefix "/" name */ 57 } dbuf; 58 } dblock, tbuf[NBLOCK]; 59 60 Dir *stbuf; 61 Biobuf bout; 62 static int ustar; /* flag: tape block just read is ustar format */ 63 static char *fullname; /* if non-nil, prefix "/" name */ 64 65 int rflag, xflag, vflag, tflag, mt, cflag, fflag, Tflag, Rflag; 66 int uflag, gflag; 67 static int posix; /* flag: we're writing ustar format archive */ 68 int chksum, recno, first; 69 int nblock = DBLOCK; 70 71 void usage(void); 72 void dorep(char **); 73 int endtar(void); 74 void getdir(void); 75 void passtar(void); 76 void putfile(char*, char *, char *); 77 void doxtract(char **); 78 void dotable(void); 79 void putempty(void); 80 void longt(Dir *); 81 int checkdir(char *, int, Qid*); 82 void tomodes(Dir *); 83 int checksum(void); 84 int checkupdate(char *); 85 int prefix(char *, char *); 86 int readtar(char *); 87 int writetar(char *); 88 void backtar(void); 89 void flushtar(void); 90 void affix(int, char *); 91 int volprompt(void); 92 93 static int 94 isustar(struct header *hp) 95 { 96 return strcmp(hp->magic, "ustar") == 0; 97 } 98 99 static void 100 setustar(struct header *hp) 101 { 102 strncpy(hp->magic, "ustar", sizeof hp->magic); 103 strncpy(hp->version, "00", sizeof hp->version); 104 } 105 106 /* 107 * s is at most n bytes long, but need not be NUL-terminated. 108 * if shorter than n bytes, all bytes after the first NUL must also 109 * be NUL. 110 */ 111 static int 112 strnlen(char *s, int n) 113 { 114 if (s[n - 1] != '\0') 115 return n; 116 else 117 return strlen(s); 118 } 119 120 /* set fullname from header; called from getdir() */ 121 static void 122 getfullname(struct header *hp) 123 { 124 int pfxlen, namlen; 125 126 if (fullname != nil) 127 free(fullname); 128 namlen = strnlen(hp->name, sizeof hp->name); 129 if (hp->prefix[0] == '\0' || !ustar) { 130 fullname = malloc(namlen + 1); 131 if (fullname == nil) 132 sysfatal("out of memory: %r"); 133 memmove(fullname, hp->name, namlen); 134 fullname[namlen] = '\0'; 135 return; 136 } 137 pfxlen = strnlen(hp->prefix, sizeof hp->prefix); 138 fullname = malloc(pfxlen + 1 + namlen + 1); 139 if (fullname == nil) 140 sysfatal("out of memory: %r"); 141 memmove(fullname, hp->prefix, pfxlen); 142 fullname[pfxlen] = '/'; 143 memmove(fullname + pfxlen + 1, hp->name, namlen); 144 fullname[pfxlen + 1 + namlen] = '\0'; 145 } 146 147 /* 148 * if name is longer than NAMSIZ bytes, try to split it at a slash and fit the 149 * pieces into hp->prefix and hp->name. 150 */ 151 static int 152 putfullname(struct header *hp, char *name) 153 { 154 int namlen, pfxlen; 155 char *sl, *osl; 156 157 namlen = strlen(name); 158 if (namlen <= NAMSIZ) { 159 strncpy(hp->name, name, NAMSIZ); 160 hp->prefix[0] = '\0'; /* ustar paranoia */ 161 return 0; 162 } 163 if (!posix || namlen > NAMSIZ + 1 + sizeof hp->prefix) { 164 fprint(2, "tar: name too long for tar header: %s\n", name); 165 return -1; 166 } 167 /* 168 * try various splits until one results in pieces that fit into the 169 * appropriate fields of the header. look for slashes from right 170 * to left, in the hopes of putting the largest part of the name into 171 * hp->prefix, which is larger than hp->name. 172 */ 173 sl = strrchr(name, '/'); 174 while (sl != nil) { 175 pfxlen = sl - name; 176 if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= NAMSIZ) 177 break; 178 osl = sl; 179 *osl = '\0'; 180 sl = strrchr(name, '/'); 181 *osl = '/'; 182 } 183 if (sl == nil) { 184 fprint(2, "tar: name can't be split to fit tar header: %s\n", 185 name); 186 return -1; 187 } 188 *sl = '\0'; 189 strncpy(hp->prefix, name, sizeof hp->prefix); 190 *sl = '/'; 191 strncpy(hp->name, sl + 1, sizeof hp->name); 192 return 0; 193 } 194 195 void 196 main(int argc, char **argv) 197 { 198 char *usefile; 199 char *cp, *ap; 200 201 if (argc < 2) 202 usage(); 203 204 Binit(&bout, 1, OWRITE); 205 usefile = 0; 206 argv[argc] = 0; 207 argv++; 208 for (cp = *argv++; *cp; cp++) 209 switch(*cp) { 210 case 'f': 211 usefile = *argv++; 212 if(!usefile) 213 usage(); 214 fflag++; 215 break; 216 case 'u': 217 ap = *argv++; 218 if(!ap) 219 usage(); 220 uflag = strtoul(ap, 0, 0); 221 break; 222 case 'g': 223 ap = *argv++; 224 if(!ap) 225 usage(); 226 gflag = strtoul(ap, 0, 0); 227 break; 228 case 'c': 229 cflag++; 230 rflag++; 231 break; 232 case 'p': 233 posix++; 234 break; 235 case 'r': 236 rflag++; 237 break; 238 case 'v': 239 vflag++; 240 break; 241 case 'x': 242 xflag++; 243 break; 244 case 'T': 245 Tflag++; 246 break; 247 case 't': 248 tflag++; 249 break; 250 case 'R': 251 Rflag++; 252 break; 253 case '-': 254 break; 255 default: 256 fprint(2, "tar: %c: unknown option\n", *cp); 257 usage(); 258 } 259 260 fmtinstall('M', dirmodefmt); 261 262 if (rflag) { 263 if (!usefile) { 264 if (cflag == 0) { 265 fprint(2, "tar: can only create standard output archives\n"); 266 exits("arg error"); 267 } 268 mt = dup(1, -1); 269 nblock = 1; 270 } 271 else if ((mt = open(usefile, ORDWR)) < 0) { 272 if (cflag == 0 || (mt = create(usefile, OWRITE, 0666)) < 0) { 273 fprint(2, "tar: cannot open %s: %r\n", usefile); 274 exits("open"); 275 } 276 } 277 dorep(argv); 278 } 279 else if (xflag) { 280 if (!usefile) { 281 mt = dup(0, -1); 282 nblock = 1; 283 } 284 else if ((mt = open(usefile, OREAD)) < 0) { 285 fprint(2, "tar: cannot open %s: %r\n", usefile); 286 exits("open"); 287 } 288 doxtract(argv); 289 } 290 else if (tflag) { 291 if (!usefile) { 292 mt = dup(0, -1); 293 nblock = 1; 294 } 295 else if ((mt = open(usefile, OREAD)) < 0) { 296 fprint(2, "tar: cannot open %s: %r\n", usefile); 297 exits("open"); 298 } 299 dotable(); 300 } 301 else 302 usage(); 303 exits(0); 304 } 305 306 void 307 usage(void) 308 { 309 fprint(2, "tar: usage tar {txrc}[Rvf] [tarfile] file1 file2...\n"); 310 exits("usage"); 311 } 312 313 void 314 dorep(char **argv) 315 { 316 char cwdbuf[2048], *cwd, thisdir[2048]; 317 char *cp, *cp2; 318 int cd; 319 320 if (getwd(cwdbuf, sizeof(cwdbuf)) == 0) { 321 fprint(2, "tar: can't find current directory: %r\n"); 322 exits("cwd"); 323 } 324 cwd = cwdbuf; 325 326 if (!cflag) { 327 getdir(); 328 do { 329 passtar(); 330 getdir(); 331 } while (!endtar()); 332 } 333 334 while (*argv) { 335 cp2 = *argv; 336 if (!strcmp(cp2, "-C") && argv[1]) { 337 argv++; 338 if (chdir(*argv) < 0) 339 perror(*argv); 340 cwd = *argv; 341 argv++; 342 continue; 343 } 344 cd = 0; 345 for (cp = *argv; *cp; cp++) 346 if (*cp == '/') 347 cp2 = cp; 348 if (cp2 != *argv) { 349 *cp2 = '\0'; 350 chdir(*argv); 351 if(**argv == '/') 352 strncpy(thisdir, *argv, sizeof(thisdir)); 353 else 354 snprint(thisdir, sizeof(thisdir), "%s/%s", cwd, *argv); 355 *cp2 = '/'; 356 cp2++; 357 cd = 1; 358 } else 359 strncpy(thisdir, cwd, sizeof(thisdir)); 360 putfile(thisdir, *argv++, cp2); 361 if(cd && chdir(cwd) < 0) { 362 fprint(2, "tar: can't cd back to %s: %r\n", cwd); 363 exits("cwd"); 364 } 365 } 366 putempty(); 367 putempty(); 368 flushtar(); 369 } 370 371 int 372 endtar(void) 373 { 374 if (dblock.dbuf.name[0] == '\0') { 375 backtar(); 376 return(1); 377 } 378 else 379 return(0); 380 } 381 382 void 383 getdir(void) 384 { 385 Dir *sp; 386 387 readtar((char*)&dblock); 388 if (dblock.dbuf.name[0] == '\0') 389 return; 390 if(stbuf == nil){ 391 stbuf = malloc(sizeof(Dir)); 392 if(stbuf == nil) 393 sysfatal("out of memory: %r"); 394 } 395 sp = stbuf; 396 sp->mode = strtol(dblock.dbuf.mode, 0, 8); 397 sp->uid = "adm"; 398 sp->gid = "adm"; 399 sp->length = strtol(dblock.dbuf.size, 0, 8); 400 sp->mtime = strtol(dblock.dbuf.mtime, 0, 8); 401 chksum = strtol(dblock.dbuf.chksum, 0, 8); 402 if (chksum != checksum()) 403 sysfatal("header checksum error"); 404 sp->qid.type = 0; 405 ustar = isustar(&dblock.dbuf); 406 getfullname(&dblock.dbuf); 407 /* the mode test is ugly but sometimes necessary */ 408 if (dblock.dbuf.linkflag == LF_DIR || (sp->mode&0170000) == 040000 || 409 strrchr(fullname, '\0')[-1] == '/') { 410 sp->qid.type |= QTDIR; 411 sp->mode |= DMDIR; 412 } 413 } 414 415 void 416 passtar(void) 417 { 418 long blocks; 419 char buf[TBLOCK]; 420 421 switch (dblock.dbuf.linkflag) { 422 case LF_LINK: 423 case LF_SYMLINK1: 424 case LF_SYMLINK2: 425 case LF_FIFO: 426 return; 427 } 428 blocks = stbuf->length; 429 blocks += TBLOCK-1; 430 blocks /= TBLOCK; 431 432 while (blocks-- > 0) 433 readtar(buf); 434 } 435 436 void 437 putfile(char *dir, char *longname, char *sname) 438 { 439 int infile; 440 long blocks; 441 char buf[TBLOCK]; 442 char curdir[4096]; 443 char shortname[4096]; 444 char *cp; 445 Dir *db; 446 int i, n; 447 448 if(strlen(sname) > sizeof shortname - 3){ 449 fprint(2, "tar: %s: name too long (max %d)\n", sname, sizeof shortname - 3); 450 return; 451 } 452 453 snprint(shortname, sizeof shortname, "./%s", sname); 454 infile = open(shortname, OREAD); 455 if (infile < 0) { 456 fprint(2, "tar: %s: cannot open file - %r\n", longname); 457 return; 458 } 459 460 if(stbuf != nil) 461 free(stbuf); 462 stbuf = dirfstat(infile); 463 464 if (stbuf->qid.type & QTDIR) { 465 /* Directory */ 466 for (i = 0, cp = buf; *cp++ = longname[i++];); 467 *--cp = '/'; 468 *++cp = 0; 469 stbuf->length = 0; 470 471 tomodes(stbuf); 472 if (putfullname(&dblock.dbuf, buf) < 0) { 473 close(infile); 474 return; /* putfullname already complained */ 475 } 476 dblock.dbuf.linkflag = LF_DIR; 477 sprint(dblock.dbuf.chksum, "%6o", checksum()); 478 writetar( (char *) &dblock); 479 480 if (chdir(shortname) < 0) { 481 fprint(2, "tar: can't cd to %s: %r\n", shortname); 482 snprint(curdir, sizeof(curdir), "cd %s", shortname); 483 exits(curdir); 484 } 485 sprint(curdir, "%s/%s", dir, sname); 486 while ((n = dirread(infile, &db)) > 0) { 487 for(i = 0; i < n; i++){ 488 strncpy(cp, db[i].name, sizeof buf - (cp-buf)); 489 putfile(curdir, buf, db[i].name); 490 } 491 free(db); 492 } 493 close(infile); 494 if (chdir(dir) < 0 && chdir("..") < 0) { 495 fprint(2, "tar: can't cd to ..(%s): %r\n", dir); 496 snprint(curdir, sizeof(curdir), "cd ..(%s)", dir); 497 exits(curdir); 498 } 499 return; 500 } 501 502 /* plain file; write header block first */ 503 tomodes(stbuf); 504 if (putfullname(&dblock.dbuf, longname) < 0) { 505 close(infile); 506 return; /* putfullname already complained */ 507 } 508 blocks = (stbuf->length + (TBLOCK-1)) / TBLOCK; 509 if (vflag) { 510 fprint(2, "a %s ", longname); 511 fprint(2, "%ld blocks\n", blocks); 512 } 513 dblock.dbuf.linkflag = LF_PLAIN1; 514 sprint(dblock.dbuf.chksum, "%6o", checksum()); 515 writetar( (char *) &dblock); 516 517 /* then copy contents */ 518 while ((i = readn(infile, buf, TBLOCK)) > 0 && blocks > 0) { 519 writetar(buf); 520 blocks--; 521 } 522 close(infile); 523 if (blocks != 0 || i != 0) 524 fprint(2, "%s: file changed size\n", longname); 525 while (blocks-- > 0) 526 putempty(); 527 } 528 529 530 void 531 doxtract(char **argv) 532 { 533 Dir null; 534 int wrsize; 535 long blocks, bytes; 536 char buf[TBLOCK], outname[Maxname+3+1]; 537 char **cp; 538 int ofile; 539 540 for (;;) { 541 getdir(); 542 if (endtar()) 543 break; 544 545 if (*argv == 0) 546 goto gotit; 547 548 for (cp = argv; *cp; cp++) 549 if (prefix(*cp, fullname)) 550 goto gotit; 551 passtar(); 552 continue; 553 554 gotit: 555 if(checkdir(fullname, stbuf->mode, &stbuf->qid)) 556 continue; 557 558 if (dblock.dbuf.linkflag == LF_LINK) { 559 fprint(2, "tar: can't link %s %s\n", 560 dblock.dbuf.linkname, fullname); 561 remove(fullname); 562 continue; 563 } 564 if (dblock.dbuf.linkflag == LF_SYMLINK1 || 565 dblock.dbuf.linkflag == LF_SYMLINK2) { 566 fprint(2, "tar: %s: cannot symlink\n", fullname); 567 continue; 568 } 569 if(fullname[0] != '/' || Rflag) 570 sprint(outname, "./%s", fullname); 571 else 572 strcpy(outname, fullname); 573 if ((ofile = create(outname, OWRITE, stbuf->mode & 0777)) < 0) { 574 fprint(2, "tar: %s - cannot create: %r\n", outname); 575 passtar(); 576 continue; 577 } 578 579 blocks = ((bytes = stbuf->length) + TBLOCK-1)/TBLOCK; 580 if (vflag) 581 fprint(2, "x %s, %ld bytes\n", fullname, bytes); 582 while (blocks-- > 0) { 583 readtar(buf); 584 wrsize = (bytes > TBLOCK? TBLOCK: bytes); 585 if (write(ofile, buf, wrsize) != wrsize) { 586 fprint(2, 587 "tar: %s: HELP - extract write error: %r\n", 588 fullname); 589 exits("extract write"); 590 } 591 bytes -= TBLOCK; 592 } 593 if(Tflag){ 594 nulldir(&null); 595 null.mtime = stbuf->mtime; 596 dirfwstat(ofile, &null); 597 } 598 close(ofile); 599 } 600 } 601 602 void 603 dotable(void) 604 { 605 for (;;) { 606 getdir(); 607 if (endtar()) 608 break; 609 if (vflag) 610 longt(stbuf); 611 Bprint(&bout, "%s", fullname); 612 if (dblock.dbuf.linkflag == '1') 613 Bprint(&bout, " linked to %s", dblock.dbuf.linkname); 614 if (dblock.dbuf.linkflag == 's') 615 Bprint(&bout, " -> %s", dblock.dbuf.linkname); 616 Bprint(&bout, "\n"); 617 passtar(); 618 } 619 } 620 621 void 622 putempty(void) 623 { 624 char buf[TBLOCK]; 625 626 memset(buf, 0, TBLOCK); 627 writetar(buf); 628 } 629 630 void 631 longt(Dir *st) 632 { 633 char *cp; 634 635 Bprint(&bout, "%M %4d/%1d ", st->mode, 0, 0); /* 0/0 uid/gid */ 636 Bprint(&bout, "%8lld", st->length); 637 cp = ctime(st->mtime); 638 Bprint(&bout, " %-12.12s %-4.4s ", cp+4, cp+24); 639 } 640 641 int 642 checkdir(char *name, int mode, Qid *qid) 643 { 644 char *cp; 645 int f; 646 Dir *d, null; 647 648 if(Rflag && *name == '/') 649 name++; 650 cp = name; 651 if(*cp == '/') 652 cp++; 653 for (; *cp; cp++) { 654 if (*cp == '/') { 655 *cp = '\0'; 656 if (access(name, 0) < 0) { 657 f = create(name, OREAD, DMDIR + 0775L); 658 if(f < 0) 659 fprint(2, "tar: mkdir %s failed: %r\n", name); 660 close(f); 661 } 662 *cp = '/'; 663 } 664 } 665 666 /* if this is a directory, chmod it to the mode in the tar plus 700 */ 667 if(cp[-1] == '/' || (qid->type&QTDIR)){ 668 if((d=dirstat(name)) != 0){ 669 nulldir(&null); 670 null.mode = DMDIR | (mode & 0777) | 0700; 671 dirwstat(name, &null); 672 free(d); 673 } 674 return 1; 675 } else 676 return 0; 677 } 678 679 void 680 tomodes(Dir *sp) 681 { 682 memset(dblock.dummy, 0, sizeof(dblock.dummy)); 683 sprint(dblock.dbuf.mode, "%6lo ", sp->mode & 0777); 684 sprint(dblock.dbuf.uid, "%6o ", uflag); 685 sprint(dblock.dbuf.gid, "%6o ", gflag); 686 sprint(dblock.dbuf.size, "%11llo ", sp->length); 687 sprint(dblock.dbuf.mtime, "%11lo ", sp->mtime); 688 if (posix) { 689 setustar(&dblock.dbuf); 690 strncpy(dblock.dbuf.uname, sp->uid, sizeof dblock.dbuf.uname); 691 strncpy(dblock.dbuf.gname, sp->gid, sizeof dblock.dbuf.gname); 692 } 693 } 694 695 int 696 checksum(void) 697 { 698 int i; 699 char *cp; 700 701 for (cp = dblock.dbuf.chksum; cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++) 702 *cp = ' '; 703 i = 0; 704 for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++) 705 i += *cp & 0xff; 706 return(i); 707 } 708 709 int 710 prefix(char *s1, char *s2) 711 { 712 while (*s1) 713 if (*s1++ != *s2++) 714 return(0); 715 if (*s2) 716 return(*s2 == '/'); 717 return(1); 718 } 719 720 int 721 readtar(char *buffer) 722 { 723 int i; 724 725 if (recno >= nblock || first == 0) { 726 if ((i = readn(mt, tbuf, TBLOCK*nblock)) <= 0) { 727 fprint(2, "tar: archive read error: %r\n"); 728 exits("archive read"); 729 } 730 if (first == 0) { 731 if ((i % TBLOCK) != 0) { 732 fprint(2, "tar: archive blocksize error: %r\n"); 733 exits("blocksize"); 734 } 735 i /= TBLOCK; 736 if (i != nblock) { 737 fprint(2, "tar: blocksize = %d\n", i); 738 nblock = i; 739 } 740 } 741 recno = 0; 742 } 743 first = 1; 744 memmove(buffer, &tbuf[recno++], TBLOCK); 745 return(TBLOCK); 746 } 747 748 int 749 writetar(char *buffer) 750 { 751 first = 1; 752 if (recno >= nblock) { 753 if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) { 754 fprint(2, "tar: archive write error: %r\n"); 755 exits("write"); 756 } 757 recno = 0; 758 } 759 memmove(&tbuf[recno++], buffer, TBLOCK); 760 if (recno >= nblock) { 761 if (write(mt, tbuf, TBLOCK*nblock) != TBLOCK*nblock) { 762 fprint(2, "tar: archive write error: %r\n"); 763 exits("write"); 764 } 765 recno = 0; 766 } 767 return(TBLOCK); 768 } 769 770 /* 771 * backup over last tar block 772 */ 773 void 774 backtar(void) 775 { 776 seek(mt, -TBLOCK*nblock, 1); 777 recno--; 778 } 779 780 void 781 flushtar(void) 782 { 783 write(mt, tbuf, TBLOCK*nblock); 784 } 785