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