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