10fb3f58eSDavid du Colombier /* 20fb3f58eSDavid du Colombier * tar - `tape archiver', actually usable on any medium. 30fb3f58eSDavid du Colombier * POSIX "ustar" compliant when extracting, and by default when creating. 40fb3f58eSDavid du Colombier * this tar attempts to read and write multiple Tblock-byte blocks 50fb3f58eSDavid du Colombier * at once to and from the filesystem, and does not copy blocks 60fb3f58eSDavid du Colombier * around internally. 70fb3f58eSDavid du Colombier */ 80fb3f58eSDavid du Colombier 93e12c5d1SDavid du Colombier #include <u.h> 103e12c5d1SDavid du Colombier #include <libc.h> 110fb3f58eSDavid du Colombier #include <fcall.h> /* for %M */ 120fb3f58eSDavid du Colombier #include <String.h> 133e12c5d1SDavid du Colombier 140fb3f58eSDavid du Colombier /* 150fb3f58eSDavid du Colombier * modified versions of those in libc.h; scans only the first arg for 160fb3f58eSDavid du Colombier * keyletters and options. 170fb3f58eSDavid du Colombier */ 180fb3f58eSDavid du Colombier #define TARGBEGIN {\ 190fb3f58eSDavid du Colombier (argv0 || (argv0 = *argv)), argv++, argc--;\ 200fb3f58eSDavid du Colombier if (argv[0]) {\ 210fb3f58eSDavid du Colombier char *_args, *_argt;\ 220fb3f58eSDavid du Colombier Rune _argc;\ 230fb3f58eSDavid du Colombier _args = &argv[0][0];\ 240fb3f58eSDavid du Colombier _argc = 0;\ 250fb3f58eSDavid du Colombier while(*_args && (_args += chartorune(&_argc, _args)))\ 260fb3f58eSDavid du Colombier switch(_argc) 270fb3f58eSDavid du Colombier #define TARGEND SET(_argt); USED(_argt);USED(_argc);USED(_args); \ 280fb3f58eSDavid du Colombier argc--, argv++; } \ 290fb3f58eSDavid du Colombier USED(argv); USED(argc); } 300fb3f58eSDavid du Colombier #define TARGC() (_argc) 3140ef9009SDavid du Colombier 320fb3f58eSDavid du Colombier #define ROUNDUP(a, b) (((a) + (b) - 1)/(b)) 330fb3f58eSDavid du Colombier #define BYTES2TBLKS(bytes) ROUNDUP(bytes, Tblock) 340fb3f58eSDavid du Colombier 350fb3f58eSDavid du Colombier typedef vlong Off; 364f281771SDavid du Colombier typedef char *(*Refill)(int ar, char *bufs, int justhdr); 370fb3f58eSDavid du Colombier 380fb3f58eSDavid du Colombier enum { Stdin, Stdout, Stderr }; 390fb3f58eSDavid du Colombier enum { Rd, Wr }; /* pipe fd-array indices */ 400fb3f58eSDavid du Colombier enum { Output, Input }; 410fb3f58eSDavid du Colombier enum { None, Toc, Xtract, Replace }; 424f281771SDavid du Colombier enum { Alldata, Justnxthdr }; 4340ef9009SDavid du Colombier enum { 440fb3f58eSDavid du Colombier Tblock = 512, 450fb3f58eSDavid du Colombier Nblock = 40, /* maximum blocksize */ 460fb3f58eSDavid du Colombier Dblock = 20, /* default blocksize */ 470fb3f58eSDavid du Colombier Namsiz = 100, 4840ef9009SDavid du Colombier Maxpfx = 155, /* from POSIX */ 490fb3f58eSDavid du Colombier Maxname = Namsiz + 1 + Maxpfx, 500fb3f58eSDavid du Colombier DEBUG = 0, 5140ef9009SDavid du Colombier }; 5240ef9009SDavid du Colombier 5340ef9009SDavid du Colombier /* POSIX link flags */ 5440ef9009SDavid du Colombier enum { 5540ef9009SDavid du Colombier LF_PLAIN1 = '\0', 5640ef9009SDavid du Colombier LF_PLAIN2 = '0', 5740ef9009SDavid du Colombier LF_LINK = '1', 5840ef9009SDavid du Colombier LF_SYMLINK1 = '2', 5940ef9009SDavid du Colombier LF_SYMLINK2 = 's', 6040ef9009SDavid du Colombier LF_CHR = '3', 6140ef9009SDavid du Colombier LF_BLK = '4', 6240ef9009SDavid du Colombier LF_DIR = '5', 6340ef9009SDavid du Colombier LF_FIFO = '6', 6440ef9009SDavid du Colombier LF_CONTIG = '7', 6540ef9009SDavid du Colombier /* 'A' - 'Z' are reserved for custom implementations */ 6640ef9009SDavid du Colombier }; 6740ef9009SDavid du Colombier 6840ef9009SDavid du Colombier #define islink(lf) (isreallink(lf) || issymlink(lf)) 6940ef9009SDavid du Colombier #define isreallink(lf) ((lf) == LF_LINK) 7040ef9009SDavid du Colombier #define issymlink(lf) ((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2) 710fb3f58eSDavid du Colombier 720fb3f58eSDavid du Colombier typedef union { 730fb3f58eSDavid du Colombier uchar data[Tblock]; 740fb3f58eSDavid du Colombier struct { 750fb3f58eSDavid du Colombier char name[Namsiz]; 763e12c5d1SDavid du Colombier char mode[8]; 773e12c5d1SDavid du Colombier char uid[8]; 783e12c5d1SDavid du Colombier char gid[8]; 793e12c5d1SDavid du Colombier char size[12]; 803e12c5d1SDavid du Colombier char mtime[12]; 813e12c5d1SDavid du Colombier char chksum[8]; 823e12c5d1SDavid du Colombier char linkflag; 830fb3f58eSDavid du Colombier char linkname[Namsiz]; 840fb3f58eSDavid du Colombier 8540ef9009SDavid du Colombier /* rest are defined by POSIX's ustar format; see p1003.2b */ 8640ef9009SDavid du Colombier char magic[6]; /* "ustar" */ 8740ef9009SDavid du Colombier char version[2]; 8840ef9009SDavid du Colombier char uname[32]; 8940ef9009SDavid du Colombier char gname[32]; 9040ef9009SDavid du Colombier char devmajor[8]; 9140ef9009SDavid du Colombier char devminor[8]; 920fb3f58eSDavid du Colombier char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */ 930fb3f58eSDavid du Colombier }; 940fb3f58eSDavid du Colombier } Hdr; 953e12c5d1SDavid du Colombier 960fb3f58eSDavid du Colombier typedef struct { 970fb3f58eSDavid du Colombier char *comp; 980fb3f58eSDavid du Colombier char *decomp; 990fb3f58eSDavid du Colombier char *sfx[4]; 1000fb3f58eSDavid du Colombier } Compress; 1013e12c5d1SDavid du Colombier 1020fb3f58eSDavid du Colombier static Compress comps[] = { 1030fb3f58eSDavid du Colombier "gzip", "gunzip", { ".tar.gz", ".tgz" }, /* default */ 1040fb3f58eSDavid du Colombier "compress", "uncompress", { ".tar.Z", ".tz" }, 1050fb3f58eSDavid du Colombier "bzip2", "bunzip2", { ".tar.bz", ".tbz", 1060fb3f58eSDavid du Colombier ".tar.bz2",".tbz2" }, 1070fb3f58eSDavid du Colombier }; 1083e12c5d1SDavid du Colombier 1090fb3f58eSDavid du Colombier typedef struct { 1100fb3f58eSDavid du Colombier int kid; 1110fb3f58eSDavid du Colombier int fd; /* original fd */ 1120fb3f58eSDavid du Colombier int rfd; /* replacement fd */ 1130fb3f58eSDavid du Colombier int input; 1140fb3f58eSDavid du Colombier int open; 1150fb3f58eSDavid du Colombier } Pushstate; 11640ef9009SDavid du Colombier 1170fb3f58eSDavid du Colombier #define OTHER(rdwr) (rdwr == Rd? Wr: Rd) 1180fb3f58eSDavid du Colombier 1190fb3f58eSDavid du Colombier static int debug; 1200fb3f58eSDavid du Colombier static int verb; 1210fb3f58eSDavid du Colombier static int posix = 1; 1220fb3f58eSDavid du Colombier static int docreate; 1230fb3f58eSDavid du Colombier static int aruid; 1240fb3f58eSDavid du Colombier static int argid; 12523605fa3SDavid du Colombier static int relative = 1; 1260fb3f58eSDavid du Colombier static int settime; 1270fb3f58eSDavid du Colombier static int verbose; 1280fb3f58eSDavid du Colombier static int docompress; 1290fb3f58eSDavid du Colombier static int keepexisting; 130*3366099cSDavid du Colombier static Off blkoff; /* offset of the current archive block (not Tblock) */ 1314f281771SDavid du Colombier static Off nexthdr; 1320fb3f58eSDavid du Colombier 1330fb3f58eSDavid du Colombier static int nblock = Dblock; 1340fb3f58eSDavid du Colombier static char *usefile; 1350fb3f58eSDavid du Colombier static char origdir[Maxname*2]; 1360fb3f58eSDavid du Colombier static Hdr *tpblk, *endblk; 1370fb3f58eSDavid du Colombier static Hdr *curblk; 1380fb3f58eSDavid du Colombier 1390fb3f58eSDavid du Colombier static void 1400fb3f58eSDavid du Colombier usage(void) 14140ef9009SDavid du Colombier { 1420fb3f58eSDavid du Colombier fprint(2, "usage: %s {crtx}[PRTfgkmpuvz] [archive] file1 file2...\n", 1430fb3f58eSDavid du Colombier argv0); 1440fb3f58eSDavid du Colombier exits("usage"); 1450fb3f58eSDavid du Colombier } 1460fb3f58eSDavid du Colombier 1470fb3f58eSDavid du Colombier /* compression */ 1480fb3f58eSDavid du Colombier 1490fb3f58eSDavid du Colombier static Compress * 1500fb3f58eSDavid du Colombier compmethod(char *name) 1510fb3f58eSDavid du Colombier { 1520fb3f58eSDavid du Colombier int i, nmlen = strlen(name), sfxlen; 1530fb3f58eSDavid du Colombier Compress *cp; 1540fb3f58eSDavid du Colombier 1550fb3f58eSDavid du Colombier for (cp = comps; cp < comps + nelem(comps); cp++) 1560fb3f58eSDavid du Colombier for (i = 0; i < nelem(cp->sfx) && cp->sfx[i]; i++) { 1570fb3f58eSDavid du Colombier sfxlen = strlen(cp->sfx[i]); 1580fb3f58eSDavid du Colombier if (nmlen > sfxlen && 1590fb3f58eSDavid du Colombier strcmp(cp->sfx[i], name + nmlen - sfxlen) == 0) 1600fb3f58eSDavid du Colombier return cp; 1610fb3f58eSDavid du Colombier } 1620fb3f58eSDavid du Colombier return docompress? comps: nil; 1630fb3f58eSDavid du Colombier } 1640fb3f58eSDavid du Colombier 1650fb3f58eSDavid du Colombier /* 1660fb3f58eSDavid du Colombier * push a filter, cmd, onto fd. if input, it's an input descriptor. 1670fb3f58eSDavid du Colombier * returns a descriptor to replace fd, or -1 on error. 1680fb3f58eSDavid du Colombier */ 1690fb3f58eSDavid du Colombier static int 1700fb3f58eSDavid du Colombier push(int fd, char *cmd, int input, Pushstate *ps) 1710fb3f58eSDavid du Colombier { 1720fb3f58eSDavid du Colombier int nfd, pifds[2]; 1730fb3f58eSDavid du Colombier String *s; 1740fb3f58eSDavid du Colombier 1750fb3f58eSDavid du Colombier ps->open = 0; 1760fb3f58eSDavid du Colombier ps->fd = fd; 1770fb3f58eSDavid du Colombier ps->input = input; 1780fb3f58eSDavid du Colombier if (fd < 0 || pipe(pifds) < 0) 1790fb3f58eSDavid du Colombier return -1; 1800fb3f58eSDavid du Colombier ps->kid = fork(); 1810fb3f58eSDavid du Colombier switch (ps->kid) { 1820fb3f58eSDavid du Colombier case -1: 1830fb3f58eSDavid du Colombier return -1; 1840fb3f58eSDavid du Colombier case 0: 1850fb3f58eSDavid du Colombier if (input) 1860fb3f58eSDavid du Colombier dup(pifds[Wr], Stdout); 1870fb3f58eSDavid du Colombier else 1880fb3f58eSDavid du Colombier dup(pifds[Rd], Stdin); 1890fb3f58eSDavid du Colombier close(pifds[input? Rd: Wr]); 1900fb3f58eSDavid du Colombier dup(fd, (input? Stdin: Stdout)); 1910fb3f58eSDavid du Colombier s = s_new(); 1920fb3f58eSDavid du Colombier if (cmd[0] != '/') 1930fb3f58eSDavid du Colombier s_append(s, "/bin/"); 1940fb3f58eSDavid du Colombier s_append(s, cmd); 1950fb3f58eSDavid du Colombier execl(s_to_c(s), cmd, nil); 1960fb3f58eSDavid du Colombier sysfatal("can't exec %s: %r", cmd); 1970fb3f58eSDavid du Colombier default: 1980fb3f58eSDavid du Colombier nfd = pifds[input? Rd: Wr]; 1990fb3f58eSDavid du Colombier close(pifds[input? Wr: Rd]); 2000fb3f58eSDavid du Colombier break; 2010fb3f58eSDavid du Colombier } 2020fb3f58eSDavid du Colombier ps->rfd = nfd; 2030fb3f58eSDavid du Colombier ps->open = 1; 2040fb3f58eSDavid du Colombier return nfd; 2050fb3f58eSDavid du Colombier } 2060fb3f58eSDavid du Colombier 2070fb3f58eSDavid du Colombier static char * 2080fb3f58eSDavid du Colombier pushclose(Pushstate *ps) 2090fb3f58eSDavid du Colombier { 2100fb3f58eSDavid du Colombier Waitmsg *wm; 2110fb3f58eSDavid du Colombier 2120fb3f58eSDavid du Colombier if (ps->fd < 0 || ps->rfd < 0 || !ps->open) 2130fb3f58eSDavid du Colombier return "not open"; 2140fb3f58eSDavid du Colombier close(ps->rfd); 2150fb3f58eSDavid du Colombier ps->rfd = -1; 2160fb3f58eSDavid du Colombier ps->open = 0; 2170fb3f58eSDavid du Colombier while ((wm = wait()) != nil && wm->pid != ps->kid) 2180fb3f58eSDavid du Colombier continue; 2190fb3f58eSDavid du Colombier return wm? wm->msg: nil; 2200fb3f58eSDavid du Colombier } 2210fb3f58eSDavid du Colombier 2220fb3f58eSDavid du Colombier /* 2230fb3f58eSDavid du Colombier * block-buffer management 2240fb3f58eSDavid du Colombier */ 2250fb3f58eSDavid du Colombier 2260fb3f58eSDavid du Colombier static void 2270fb3f58eSDavid du Colombier initblks(void) 2280fb3f58eSDavid du Colombier { 2290fb3f58eSDavid du Colombier free(tpblk); 2300fb3f58eSDavid du Colombier tpblk = malloc(Tblock * nblock); 2310fb3f58eSDavid du Colombier assert(tpblk != nil); 2320fb3f58eSDavid du Colombier endblk = tpblk + nblock; 2330fb3f58eSDavid du Colombier } 2340fb3f58eSDavid du Colombier 2354f281771SDavid du Colombier /* 2364f281771SDavid du Colombier * (re)fill block buffers from archive. `justhdr' means we don't care 2374f281771SDavid du Colombier * about the data before the next header block. 2384f281771SDavid du Colombier */ 2390fb3f58eSDavid du Colombier static char * 2404f281771SDavid du Colombier refill(int ar, char *bufs, int justhdr) 2410fb3f58eSDavid du Colombier { 2420fb3f58eSDavid du Colombier int i, n; 2430fb3f58eSDavid du Colombier unsigned bytes = Tblock * nblock; 2444f281771SDavid du Colombier static int done, first = 1, seekable; 2450fb3f58eSDavid du Colombier 2460fb3f58eSDavid du Colombier if (done) 2470fb3f58eSDavid du Colombier return nil; 2480fb3f58eSDavid du Colombier 2494f281771SDavid du Colombier if (first) 2504f281771SDavid du Colombier seekable = seek(ar, 0, 1) >= 0; 2510fb3f58eSDavid du Colombier /* try to size non-pipe input at first read */ 2520fb3f58eSDavid du Colombier if (first && usefile) { 253*3366099cSDavid du Colombier blkoff = seek(ar, 0, 1); /* note position */ 2540fb3f58eSDavid du Colombier n = read(ar, bufs, bytes); 2550fb3f58eSDavid du Colombier if (n <= 0) 2560fb3f58eSDavid du Colombier sysfatal("error reading archive: %r"); 2570fb3f58eSDavid du Colombier i = n; 2580fb3f58eSDavid du Colombier if (i % Tblock != 0) { 2590fb3f58eSDavid du Colombier fprint(2, "%s: archive block size (%d) error\n", 2600fb3f58eSDavid du Colombier argv0, i); 2610fb3f58eSDavid du Colombier exits("blocksize"); 2620fb3f58eSDavid du Colombier } 2630fb3f58eSDavid du Colombier i /= Tblock; 2640fb3f58eSDavid du Colombier if (i != nblock) { 2650fb3f58eSDavid du Colombier nblock = i; 2660fb3f58eSDavid du Colombier fprint(2, "%s: blocking = %d\n", argv0, nblock); 2670fb3f58eSDavid du Colombier endblk = (Hdr *)bufs + nblock; 2680fb3f58eSDavid du Colombier bytes = n; 2690fb3f58eSDavid du Colombier } 2704f281771SDavid du Colombier } else if (justhdr && seekable && nexthdr - seek(ar, 0, 1) >= bytes) { 2714f281771SDavid du Colombier /* optimisation for huge archive members on seekable media */ 2724f281771SDavid du Colombier if (seek(ar, bytes, 1) < 0) 2734f281771SDavid du Colombier sysfatal("can't seek on archive: %r"); 2744f281771SDavid du Colombier n = bytes; 2750fb3f58eSDavid du Colombier } else 2760fb3f58eSDavid du Colombier n = readn(ar, bufs, bytes); 2774f281771SDavid du Colombier first = 0; 2784f281771SDavid du Colombier 2790fb3f58eSDavid du Colombier if (n == 0) 2800fb3f58eSDavid du Colombier sysfatal("unexpected EOF reading archive"); 2810fb3f58eSDavid du Colombier else if (n < 0) 2820fb3f58eSDavid du Colombier sysfatal("error reading archive: %r"); 2830fb3f58eSDavid du Colombier else if (n%Tblock != 0) 2840fb3f58eSDavid du Colombier sysfatal("partial block read from archive"); 2850fb3f58eSDavid du Colombier if (n != bytes) { 2860fb3f58eSDavid du Colombier done = 1; 2870fb3f58eSDavid du Colombier memset(bufs + n, 0, bytes - n); 2880fb3f58eSDavid du Colombier } 2890fb3f58eSDavid du Colombier return bufs; 2900fb3f58eSDavid du Colombier } 2910fb3f58eSDavid du Colombier 2920fb3f58eSDavid du Colombier static Hdr * 2934f281771SDavid du Colombier getblk(int ar, Refill rfp, int justhdr) 2940fb3f58eSDavid du Colombier { 2950fb3f58eSDavid du Colombier if (curblk == nil || curblk >= endblk) { /* input block exhausted? */ 2964f281771SDavid du Colombier if (rfp != nil && (*rfp)(ar, (char *)tpblk, justhdr) == nil) 2970fb3f58eSDavid du Colombier return nil; 2980fb3f58eSDavid du Colombier curblk = tpblk; 2990fb3f58eSDavid du Colombier } 3000fb3f58eSDavid du Colombier return curblk++; 3010fb3f58eSDavid du Colombier } 3020fb3f58eSDavid du Colombier 3030fb3f58eSDavid du Colombier static Hdr * 3044f281771SDavid du Colombier getblkrd(int ar, int justhdr) 3050fb3f58eSDavid du Colombier { 3064f281771SDavid du Colombier return getblk(ar, refill, justhdr); 3070fb3f58eSDavid du Colombier } 3080fb3f58eSDavid du Colombier 3090fb3f58eSDavid du Colombier static Hdr * 3100fb3f58eSDavid du Colombier getblke(int ar) 3110fb3f58eSDavid du Colombier { 3124f281771SDavid du Colombier return getblk(ar, nil, Alldata); 3130fb3f58eSDavid du Colombier } 3140fb3f58eSDavid du Colombier 3150fb3f58eSDavid du Colombier static Hdr * 3160fb3f58eSDavid du Colombier getblkz(int ar) 3170fb3f58eSDavid du Colombier { 3180fb3f58eSDavid du Colombier Hdr *hp = getblke(ar); 3190fb3f58eSDavid du Colombier 3200fb3f58eSDavid du Colombier if (hp != nil) 3210fb3f58eSDavid du Colombier memset(hp->data, 0, Tblock); 3220fb3f58eSDavid du Colombier return hp; 3230fb3f58eSDavid du Colombier } 3240fb3f58eSDavid du Colombier 3250fb3f58eSDavid du Colombier /* 3260fb3f58eSDavid du Colombier * how many block buffers are available, starting at the address 3270fb3f58eSDavid du Colombier * just returned by getblk*? 3280fb3f58eSDavid du Colombier */ 3290fb3f58eSDavid du Colombier static int 3300fb3f58eSDavid du Colombier gothowmany(int max) 3310fb3f58eSDavid du Colombier { 3320fb3f58eSDavid du Colombier int n = endblk - (curblk - 1); 3330fb3f58eSDavid du Colombier 3340fb3f58eSDavid du Colombier return n > max? max: n; 3350fb3f58eSDavid du Colombier } 3360fb3f58eSDavid du Colombier 3370fb3f58eSDavid du Colombier /* 3380fb3f58eSDavid du Colombier * indicate that one is done with the last block obtained from getblke 3390fb3f58eSDavid du Colombier * and it is now available to be written into the archive. 3400fb3f58eSDavid du Colombier */ 3410fb3f58eSDavid du Colombier static void 3420fb3f58eSDavid du Colombier putlastblk(int ar) 3430fb3f58eSDavid du Colombier { 3440fb3f58eSDavid du Colombier unsigned bytes = Tblock * nblock; 3450fb3f58eSDavid du Colombier 3460fb3f58eSDavid du Colombier /* if writing end-of-archive, aid compression (good hygiene too) */ 3470fb3f58eSDavid du Colombier if (curblk < endblk) 3480fb3f58eSDavid du Colombier memset(curblk, 0, (char *)endblk - (char *)curblk); 3490fb3f58eSDavid du Colombier if (write(ar, tpblk, bytes) != bytes) 3500fb3f58eSDavid du Colombier sysfatal("error writing archive: %r"); 35140ef9009SDavid du Colombier } 35240ef9009SDavid du Colombier 35340ef9009SDavid du Colombier static void 3540fb3f58eSDavid du Colombier putblk(int ar) 35540ef9009SDavid du Colombier { 3560fb3f58eSDavid du Colombier if (curblk >= endblk) 3570fb3f58eSDavid du Colombier putlastblk(ar); 3580fb3f58eSDavid du Colombier } 3590fb3f58eSDavid du Colombier 3600fb3f58eSDavid du Colombier static void 3610fb3f58eSDavid du Colombier putbackblk(int ar) 3620fb3f58eSDavid du Colombier { 3630fb3f58eSDavid du Colombier curblk--; 3640fb3f58eSDavid du Colombier USED(ar); 3650fb3f58eSDavid du Colombier } 3660fb3f58eSDavid du Colombier 3670fb3f58eSDavid du Colombier static void 3680fb3f58eSDavid du Colombier putreadblks(int ar, int blks) 3690fb3f58eSDavid du Colombier { 3700fb3f58eSDavid du Colombier curblk += blks - 1; 3710fb3f58eSDavid du Colombier USED(ar); 3720fb3f58eSDavid du Colombier } 3730fb3f58eSDavid du Colombier 3740fb3f58eSDavid du Colombier static void 3750fb3f58eSDavid du Colombier putblkmany(int ar, int blks) 3760fb3f58eSDavid du Colombier { 3770fb3f58eSDavid du Colombier curblk += blks - 1; 3780fb3f58eSDavid du Colombier putblk(ar); 3790fb3f58eSDavid du Colombier } 3800fb3f58eSDavid du Colombier 3810fb3f58eSDavid du Colombier /* 3820fb3f58eSDavid du Colombier * common routines 3830fb3f58eSDavid du Colombier */ 3840fb3f58eSDavid du Colombier 385*3366099cSDavid du Colombier /* 386*3366099cSDavid du Colombier * modifies hp->chksum but restores it; important for the last block of the 387*3366099cSDavid du Colombier * old archive when updating with `tar rf archive' 388*3366099cSDavid du Colombier */ 3890fb3f58eSDavid du Colombier long 3900fb3f58eSDavid du Colombier chksum(Hdr *hp) 3910fb3f58eSDavid du Colombier { 3920fb3f58eSDavid du Colombier int n = Tblock; 3930fb3f58eSDavid du Colombier long i = 0; 3940fb3f58eSDavid du Colombier uchar *cp = hp->data; 395*3366099cSDavid du Colombier char oldsum[sizeof hp->chksum]; 3960fb3f58eSDavid du Colombier 397*3366099cSDavid du Colombier memmove(oldsum, hp->chksum, sizeof oldsum); 3980fb3f58eSDavid du Colombier memset(hp->chksum, ' ', sizeof hp->chksum); 3990fb3f58eSDavid du Colombier while (n-- > 0) 4000fb3f58eSDavid du Colombier i += *cp++; 401*3366099cSDavid du Colombier memmove(hp->chksum, oldsum, sizeof oldsum); 4020fb3f58eSDavid du Colombier return i; 4030fb3f58eSDavid du Colombier } 4040fb3f58eSDavid du Colombier 4050fb3f58eSDavid du Colombier static int 4060fb3f58eSDavid du Colombier isustar(Hdr *hp) 4070fb3f58eSDavid du Colombier { 4080fb3f58eSDavid du Colombier return strcmp(hp->magic, "ustar") == 0; 40940ef9009SDavid du Colombier } 41040ef9009SDavid du Colombier 41140ef9009SDavid du Colombier /* 41240ef9009SDavid du Colombier * s is at most n bytes long, but need not be NUL-terminated. 41340ef9009SDavid du Colombier * if shorter than n bytes, all bytes after the first NUL must also 41440ef9009SDavid du Colombier * be NUL. 41540ef9009SDavid du Colombier */ 41640ef9009SDavid du Colombier static int 41740ef9009SDavid du Colombier strnlen(char *s, int n) 41840ef9009SDavid du Colombier { 4190fb3f58eSDavid du Colombier return s[n - 1] != '\0'? n: strlen(s); 42040ef9009SDavid du Colombier } 42140ef9009SDavid du Colombier 4220fb3f58eSDavid du Colombier /* set fullname from header */ 4230fb3f58eSDavid du Colombier static char * 4240fb3f58eSDavid du Colombier name(Hdr *hp) 42540ef9009SDavid du Colombier { 42640ef9009SDavid du Colombier int pfxlen, namlen; 42723605fa3SDavid du Colombier static char fullnamebuf[2 + Maxname + 1]; /* 2 at beginning for ./ on relative names */ 42823605fa3SDavid du Colombier char *fullname; 42940ef9009SDavid du Colombier 43023605fa3SDavid du Colombier fullname = fullnamebuf+2; 43140ef9009SDavid du Colombier namlen = strnlen(hp->name, sizeof hp->name); 4320fb3f58eSDavid du Colombier if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */ 43340ef9009SDavid du Colombier memmove(fullname, hp->name, namlen); 43440ef9009SDavid du Colombier fullname[namlen] = '\0'; 4350fb3f58eSDavid du Colombier return fullname; 43640ef9009SDavid du Colombier } 4370fb3f58eSDavid du Colombier 4380fb3f58eSDavid du Colombier /* name is in two pieces */ 43940ef9009SDavid du Colombier pfxlen = strnlen(hp->prefix, sizeof hp->prefix); 44040ef9009SDavid du Colombier memmove(fullname, hp->prefix, pfxlen); 44140ef9009SDavid du Colombier fullname[pfxlen] = '/'; 44240ef9009SDavid du Colombier memmove(fullname + pfxlen + 1, hp->name, namlen); 44340ef9009SDavid du Colombier fullname[pfxlen + 1 + namlen] = '\0'; 4440fb3f58eSDavid du Colombier return fullname; 4450fb3f58eSDavid du Colombier } 4460fb3f58eSDavid du Colombier 4470fb3f58eSDavid du Colombier static int 4480fb3f58eSDavid du Colombier isdir(Hdr *hp) 4490fb3f58eSDavid du Colombier { 4500fb3f58eSDavid du Colombier /* the mode test is ugly but sometimes necessary */ 4510fb3f58eSDavid du Colombier return hp->linkflag == LF_DIR || 4520fb3f58eSDavid du Colombier strrchr(name(hp), '\0')[-1] == '/' || 4530fb3f58eSDavid du Colombier (strtoul(hp->mode, nil, 8)&0170000) == 040000; 4540fb3f58eSDavid du Colombier } 4550fb3f58eSDavid du Colombier 4560fb3f58eSDavid du Colombier static int 4570fb3f58eSDavid du Colombier eotar(Hdr *hp) 4580fb3f58eSDavid du Colombier { 4590fb3f58eSDavid du Colombier return name(hp)[0] == '\0'; 4600fb3f58eSDavid du Colombier } 4610fb3f58eSDavid du Colombier 4624f281771SDavid du Colombier Off 4634f281771SDavid du Colombier hdrsize(Hdr *hp) 4644f281771SDavid du Colombier { 4654f281771SDavid du Colombier Off bytes = strtoull(hp->size, nil, 8); 4664f281771SDavid du Colombier 4674f281771SDavid du Colombier if(isdir(hp)) 4684f281771SDavid du Colombier bytes = 0; 4694f281771SDavid du Colombier return bytes; 4704f281771SDavid du Colombier } 4714f281771SDavid du Colombier 4720fb3f58eSDavid du Colombier static Hdr * 4730fb3f58eSDavid du Colombier readhdr(int ar) 4740fb3f58eSDavid du Colombier { 4750fb3f58eSDavid du Colombier long hdrcksum; 4760fb3f58eSDavid du Colombier Hdr *hp; 4770fb3f58eSDavid du Colombier 4784f281771SDavid du Colombier hp = getblkrd(ar, Alldata); 4790fb3f58eSDavid du Colombier if (hp == nil) 4800fb3f58eSDavid du Colombier sysfatal("unexpected EOF instead of archive header"); 4810fb3f58eSDavid du Colombier if (eotar(hp)) /* end-of-archive block? */ 4820fb3f58eSDavid du Colombier return nil; 4830fb3f58eSDavid du Colombier hdrcksum = strtoul(hp->chksum, nil, 8); 4840fb3f58eSDavid du Colombier if (chksum(hp) != hdrcksum) 4850fb3f58eSDavid du Colombier sysfatal("bad archive header checksum: name %.64s...", 4860fb3f58eSDavid du Colombier hp->name); 4874f281771SDavid du Colombier nexthdr += Tblock*(1 + BYTES2TBLKS(hdrsize(hp))); 4880fb3f58eSDavid du Colombier return hp; 48940ef9009SDavid du Colombier } 49040ef9009SDavid du Colombier 49140ef9009SDavid du Colombier /* 4920fb3f58eSDavid du Colombier * tar r[c] 4930fb3f58eSDavid du Colombier */ 4940fb3f58eSDavid du Colombier 4950fb3f58eSDavid du Colombier /* 4960fb3f58eSDavid du Colombier * if name is longer than Namsiz bytes, try to split it at a slash and fit the 49740ef9009SDavid du Colombier * pieces into hp->prefix and hp->name. 49840ef9009SDavid du Colombier */ 49940ef9009SDavid du Colombier static int 5000fb3f58eSDavid du Colombier putfullname(Hdr *hp, char *name) 50140ef9009SDavid du Colombier { 50240ef9009SDavid du Colombier int namlen, pfxlen; 50340ef9009SDavid du Colombier char *sl, *osl; 5040fb3f58eSDavid du Colombier String *slname = nil; 5050fb3f58eSDavid du Colombier 5060fb3f58eSDavid du Colombier if (isdir(hp)) { 5070fb3f58eSDavid du Colombier slname = s_new(); 5080fb3f58eSDavid du Colombier s_append(slname, name); 5090fb3f58eSDavid du Colombier s_append(slname, "/"); /* posix requires this */ 5100fb3f58eSDavid du Colombier name = s_to_c(slname); 5110fb3f58eSDavid du Colombier } 51240ef9009SDavid du Colombier 51340ef9009SDavid du Colombier namlen = strlen(name); 5140fb3f58eSDavid du Colombier if (namlen <= Namsiz) { 5150fb3f58eSDavid du Colombier strncpy(hp->name, name, Namsiz); 51640ef9009SDavid du Colombier hp->prefix[0] = '\0'; /* ustar paranoia */ 51740ef9009SDavid du Colombier return 0; 51840ef9009SDavid du Colombier } 5190fb3f58eSDavid du Colombier 5200fb3f58eSDavid du Colombier if (!posix || namlen > Maxname) { 5210fb3f58eSDavid du Colombier fprint(2, "%s: name too long for tar header: %s\n", 5220fb3f58eSDavid du Colombier argv0, name); 52340ef9009SDavid du Colombier return -1; 52440ef9009SDavid du Colombier } 52540ef9009SDavid du Colombier /* 52640ef9009SDavid du Colombier * try various splits until one results in pieces that fit into the 52740ef9009SDavid du Colombier * appropriate fields of the header. look for slashes from right 52840ef9009SDavid du Colombier * to left, in the hopes of putting the largest part of the name into 52940ef9009SDavid du Colombier * hp->prefix, which is larger than hp->name. 53040ef9009SDavid du Colombier */ 53140ef9009SDavid du Colombier sl = strrchr(name, '/'); 53240ef9009SDavid du Colombier while (sl != nil) { 53340ef9009SDavid du Colombier pfxlen = sl - name; 5340fb3f58eSDavid du Colombier if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= Namsiz) 53540ef9009SDavid du Colombier break; 53640ef9009SDavid du Colombier osl = sl; 53740ef9009SDavid du Colombier *osl = '\0'; 53840ef9009SDavid du Colombier sl = strrchr(name, '/'); 53940ef9009SDavid du Colombier *osl = '/'; 54040ef9009SDavid du Colombier } 54140ef9009SDavid du Colombier if (sl == nil) { 5420fb3f58eSDavid du Colombier fprint(2, "%s: name can't be split to fit tar header: %s\n", 5430fb3f58eSDavid du Colombier argv0, name); 54440ef9009SDavid du Colombier return -1; 54540ef9009SDavid du Colombier } 54640ef9009SDavid du Colombier *sl = '\0'; 54740ef9009SDavid du Colombier strncpy(hp->prefix, name, sizeof hp->prefix); 5480fb3f58eSDavid du Colombier *sl++ = '/'; 5490fb3f58eSDavid du Colombier strncpy(hp->name, sl, sizeof hp->name); 5500fb3f58eSDavid du Colombier if (slname) 5510fb3f58eSDavid du Colombier s_free(slname); 55240ef9009SDavid du Colombier return 0; 55340ef9009SDavid du Colombier } 55440ef9009SDavid du Colombier 5550fb3f58eSDavid du Colombier static int 5560fb3f58eSDavid du Colombier mkhdr(Hdr *hp, Dir *dir, char *file) 5573e12c5d1SDavid du Colombier { 5580fb3f58eSDavid du Colombier /* 5590fb3f58eSDavid du Colombier * these fields run together, so we format them in order and don't use 5600fb3f58eSDavid du Colombier * snprint. 5610fb3f58eSDavid du Colombier */ 5620fb3f58eSDavid du Colombier sprint(hp->mode, "%6lo ", dir->mode & 0777); 5630fb3f58eSDavid du Colombier sprint(hp->uid, "%6o ", aruid); 5640fb3f58eSDavid du Colombier sprint(hp->gid, "%6o ", argid); 5650fb3f58eSDavid du Colombier /* 5660fb3f58eSDavid du Colombier * files > 2 bytes can't be described 5670fb3f58eSDavid du Colombier * (unless we resort to xustar or exustar formats). 5680fb3f58eSDavid du Colombier */ 5690fb3f58eSDavid du Colombier if (dir->length >= (Off)1<<33) { 5700fb3f58eSDavid du Colombier fprint(2, "%s: %s: too large for tar header format\n", 5710fb3f58eSDavid du Colombier argv0, file); 5720fb3f58eSDavid du Colombier return -1; 5730fb3f58eSDavid du Colombier } 5740fb3f58eSDavid du Colombier sprint(hp->size, "%11lluo ", dir->length); 5750fb3f58eSDavid du Colombier sprint(hp->mtime, "%11luo ", dir->mtime); 5760fb3f58eSDavid du Colombier hp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1); 5770fb3f58eSDavid du Colombier putfullname(hp, file); 5780fb3f58eSDavid du Colombier if (posix) { 5790fb3f58eSDavid du Colombier strncpy(hp->magic, "ustar", sizeof hp->magic); 5800fb3f58eSDavid du Colombier strncpy(hp->version, "00", sizeof hp->version); 5810fb3f58eSDavid du Colombier strncpy(hp->uname, dir->uid, sizeof hp->uname); 5820fb3f58eSDavid du Colombier strncpy(hp->gname, dir->gid, sizeof hp->gname); 5830fb3f58eSDavid du Colombier } 5840fb3f58eSDavid du Colombier sprint(hp->chksum, "%6luo", chksum(hp)); 5850fb3f58eSDavid du Colombier return 0; 5863e12c5d1SDavid du Colombier } 5873e12c5d1SDavid du Colombier 5880fb3f58eSDavid du Colombier static void addtoar(int ar, char *file, char *shortf); 5893e12c5d1SDavid du Colombier 5900fb3f58eSDavid du Colombier static void 5910fb3f58eSDavid du Colombier addtreetoar(int ar, char *file, char *shortf, int fd) 5923e12c5d1SDavid du Colombier { 5930fb3f58eSDavid du Colombier int n; 5940fb3f58eSDavid du Colombier Dir *dent, *dirents; 5950fb3f58eSDavid du Colombier String *name = s_new(); 5963e12c5d1SDavid du Colombier 5970fb3f58eSDavid du Colombier n = dirreadall(fd, &dirents); 5980fb3f58eSDavid du Colombier close(fd); 5990fb3f58eSDavid du Colombier if (n == 0) 6003e12c5d1SDavid du Colombier return; 6010fb3f58eSDavid du Colombier 6020fb3f58eSDavid du Colombier if (chdir(shortf) < 0) 6030fb3f58eSDavid du Colombier sysfatal("chdir %s: %r", file); 6040fb3f58eSDavid du Colombier if (DEBUG) 6050fb3f58eSDavid du Colombier fprint(2, "chdir %s\t# %s\n", shortf, file); 6060fb3f58eSDavid du Colombier 6070fb3f58eSDavid du Colombier for (dent = dirents; dent < dirents + n; dent++) { 6080fb3f58eSDavid du Colombier s_reset(name); 6090fb3f58eSDavid du Colombier s_append(name, file); 6100fb3f58eSDavid du Colombier s_append(name, "/"); 6110fb3f58eSDavid du Colombier s_append(name, dent->name); 6120fb3f58eSDavid du Colombier addtoar(ar, s_to_c(name), dent->name); 6139a747e4fSDavid du Colombier } 6140fb3f58eSDavid du Colombier s_free(name); 6150fb3f58eSDavid du Colombier free(dirents); 6160fb3f58eSDavid du Colombier 617f3703655SDavid du Colombier /* 618f3703655SDavid du Colombier * this assumes that shortf is just one component, which is true 619f3703655SDavid du Colombier * during directory descent, but not necessarily true of command-line 620f3703655SDavid du Colombier * arguments. Our caller (or addtoar's) must reset the working 621f3703655SDavid du Colombier * directory if necessary. 622f3703655SDavid du Colombier */ 6230fb3f58eSDavid du Colombier if (chdir("..") < 0) 6240fb3f58eSDavid du Colombier sysfatal("chdir %s/..: %r", file); 6250fb3f58eSDavid du Colombier if (DEBUG) 6260fb3f58eSDavid du Colombier fprint(2, "chdir ..\n"); 6273e12c5d1SDavid du Colombier } 6283e12c5d1SDavid du Colombier 6290fb3f58eSDavid du Colombier static void 6300fb3f58eSDavid du Colombier addtoar(int ar, char *file, char *shortf) 6313e12c5d1SDavid du Colombier { 6320fb3f58eSDavid du Colombier int n, fd, isdir; 6330fb3f58eSDavid du Colombier long bytes; 6340fb3f58eSDavid du Colombier ulong blksleft, blksread; 6350fb3f58eSDavid du Colombier Hdr *hbp; 6360fb3f58eSDavid du Colombier Dir *dir; 6373e12c5d1SDavid du Colombier 6380fb3f58eSDavid du Colombier fd = open(shortf, OREAD); 6390fb3f58eSDavid du Colombier if (fd < 0) { 6400fb3f58eSDavid du Colombier fprint(2, "%s: can't open %s: %r\n", argv0, file); 6410fb3f58eSDavid du Colombier return; 6420fb3f58eSDavid du Colombier } 6430fb3f58eSDavid du Colombier dir = dirfstat(fd); 6440fb3f58eSDavid du Colombier if (dir == nil) 6450fb3f58eSDavid du Colombier sysfatal("can't fstat %s: %r", file); 6460fb3f58eSDavid du Colombier 6470fb3f58eSDavid du Colombier hbp = getblkz(ar); 6480fb3f58eSDavid du Colombier isdir = !!(dir->qid.type&QTDIR); 6490fb3f58eSDavid du Colombier if (mkhdr(hbp, dir, file) < 0) { 6500fb3f58eSDavid du Colombier putbackblk(ar); 6510fb3f58eSDavid du Colombier free(dir); 6520fb3f58eSDavid du Colombier close(fd); 6530fb3f58eSDavid du Colombier return; 6540fb3f58eSDavid du Colombier } 6550fb3f58eSDavid du Colombier putblk(ar); 6560fb3f58eSDavid du Colombier 6570fb3f58eSDavid du Colombier blksleft = BYTES2TBLKS(dir->length); 6580fb3f58eSDavid du Colombier free(dir); 6590fb3f58eSDavid du Colombier 6600fb3f58eSDavid du Colombier if (isdir) 6610fb3f58eSDavid du Colombier addtreetoar(ar, file, shortf, fd); 6620fb3f58eSDavid du Colombier else { 6630fb3f58eSDavid du Colombier for (; blksleft > 0; blksleft -= blksread) { 6640fb3f58eSDavid du Colombier hbp = getblke(ar); 6650fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 6660fb3f58eSDavid du Colombier bytes = blksread * Tblock; 6670fb3f58eSDavid du Colombier n = readn(fd, hbp->data, bytes); 6680fb3f58eSDavid du Colombier if (n < 0) 6690fb3f58eSDavid du Colombier sysfatal("error reading %s: %r", file); 6700fb3f58eSDavid du Colombier /* 6710fb3f58eSDavid du Colombier * ignore EOF. zero any partial block to aid 6720fb3f58eSDavid du Colombier * compression and emergency recovery of data. 6730fb3f58eSDavid du Colombier */ 6740fb3f58eSDavid du Colombier if (n < Tblock) 6750fb3f58eSDavid du Colombier memset(hbp->data + n, 0, bytes - n); 6760fb3f58eSDavid du Colombier putblkmany(ar, blksread); 6770fb3f58eSDavid du Colombier } 6780fb3f58eSDavid du Colombier close(fd); 6790fb3f58eSDavid du Colombier if (verbose) 6800fb3f58eSDavid du Colombier fprint(2, "%s\n", file); 6810fb3f58eSDavid du Colombier } 6820fb3f58eSDavid du Colombier } 6830fb3f58eSDavid du Colombier 6840fb3f58eSDavid du Colombier static char * 6850fb3f58eSDavid du Colombier replace(char **argv) 6860fb3f58eSDavid du Colombier { 6870fb3f58eSDavid du Colombier int i, ar; 6880fb3f58eSDavid du Colombier ulong blksleft, blksread; 6890fb3f58eSDavid du Colombier Off bytes; 6900fb3f58eSDavid du Colombier Hdr *hp; 6910fb3f58eSDavid du Colombier Compress *comp = nil; 6920fb3f58eSDavid du Colombier Pushstate ps; 6930fb3f58eSDavid du Colombier 6940fb3f58eSDavid du Colombier if (usefile && docreate) { 6950fb3f58eSDavid du Colombier ar = create(usefile, OWRITE, 0666); 6960fb3f58eSDavid du Colombier if (docompress) 6970fb3f58eSDavid du Colombier comp = compmethod(usefile); 6980fb3f58eSDavid du Colombier } else if (usefile) 6990fb3f58eSDavid du Colombier ar = open(usefile, ORDWR); 7000fb3f58eSDavid du Colombier else 7010fb3f58eSDavid du Colombier ar = Stdout; 7020fb3f58eSDavid du Colombier if (comp) 7030fb3f58eSDavid du Colombier ar = push(ar, comp->comp, Output, &ps); 7040fb3f58eSDavid du Colombier if (ar < 0) 7050fb3f58eSDavid du Colombier sysfatal("can't open archive %s: %r", usefile); 7060fb3f58eSDavid du Colombier 7070fb3f58eSDavid du Colombier if (usefile && !docreate) { 7080fb3f58eSDavid du Colombier /* skip quickly to the end */ 7090fb3f58eSDavid du Colombier while ((hp = readhdr(ar)) != nil) { 7104f281771SDavid du Colombier bytes = hdrsize(hp); 7110fb3f58eSDavid du Colombier for (blksleft = BYTES2TBLKS(bytes); 7124f281771SDavid du Colombier blksleft > 0 && getblkrd(ar, Justnxthdr) != nil; 7130fb3f58eSDavid du Colombier blksleft -= blksread) { 7140fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 7150fb3f58eSDavid du Colombier putreadblks(ar, blksread); 7160fb3f58eSDavid du Colombier } 7170fb3f58eSDavid du Colombier } 7180fb3f58eSDavid du Colombier /* 7190fb3f58eSDavid du Colombier * we have just read the end-of-archive Tblock. 7200fb3f58eSDavid du Colombier * now seek back over the (big) archive block containing it, 7210fb3f58eSDavid du Colombier * and back up curblk ptr over end-of-archive Tblock in memory. 7220fb3f58eSDavid du Colombier */ 723*3366099cSDavid du Colombier if (seek(ar, blkoff, 0) < 0) 7240fb3f58eSDavid du Colombier sysfatal("can't seek back over end-of-archive: %r"); 7250fb3f58eSDavid du Colombier curblk--; 7260fb3f58eSDavid du Colombier } 7270fb3f58eSDavid du Colombier 728f3703655SDavid du Colombier for (i = 0; argv[i] != nil; i++) { 7290fb3f58eSDavid du Colombier addtoar(ar, argv[i], argv[i]); 730f3703655SDavid du Colombier chdir(origdir); /* for correctness & profiling */ 731f3703655SDavid du Colombier } 7320fb3f58eSDavid du Colombier 7330fb3f58eSDavid du Colombier /* write end-of-archive marker */ 7340fb3f58eSDavid du Colombier getblkz(ar); 7350fb3f58eSDavid du Colombier putblk(ar); 7360fb3f58eSDavid du Colombier getblkz(ar); 7370fb3f58eSDavid du Colombier putlastblk(ar); 7380fb3f58eSDavid du Colombier 7390fb3f58eSDavid du Colombier if (comp) 7400fb3f58eSDavid du Colombier return pushclose(&ps); 7410fb3f58eSDavid du Colombier if (ar > Stderr) 7420fb3f58eSDavid du Colombier close(ar); 7430fb3f58eSDavid du Colombier return nil; 7440fb3f58eSDavid du Colombier } 7450fb3f58eSDavid du Colombier 7460fb3f58eSDavid du Colombier /* 7470fb3f58eSDavid du Colombier * tar [xt] 7480fb3f58eSDavid du Colombier */ 7490fb3f58eSDavid du Colombier 7500fb3f58eSDavid du Colombier /* is pfx a file-name prefix of name? */ 7510fb3f58eSDavid du Colombier static int 7520fb3f58eSDavid du Colombier prefix(char *name, char *pfx) 7530fb3f58eSDavid du Colombier { 7540fb3f58eSDavid du Colombier int pfxlen = strlen(pfx); 7550fb3f58eSDavid du Colombier char clpfx[Maxname+1]; 7560fb3f58eSDavid du Colombier 7570fb3f58eSDavid du Colombier if (pfxlen > Maxname) 7580fb3f58eSDavid du Colombier return 0; 7590fb3f58eSDavid du Colombier strcpy(clpfx, pfx); 7600fb3f58eSDavid du Colombier cleanname(clpfx); 7610fb3f58eSDavid du Colombier return strncmp(pfx, name, pfxlen) == 0 && 7620fb3f58eSDavid du Colombier (name[pfxlen] == '\0' || name[pfxlen] == '/'); 7630fb3f58eSDavid du Colombier } 7640fb3f58eSDavid du Colombier 7650fb3f58eSDavid du Colombier static int 7660fb3f58eSDavid du Colombier match(char *name, char **argv) 7670fb3f58eSDavid du Colombier { 7680fb3f58eSDavid du Colombier int i; 7690fb3f58eSDavid du Colombier char clname[Maxname+1]; 7700fb3f58eSDavid du Colombier 7710fb3f58eSDavid du Colombier if (argv[0] == nil) 7720fb3f58eSDavid du Colombier return 1; 7730fb3f58eSDavid du Colombier strcpy(clname, name); 7740fb3f58eSDavid du Colombier cleanname(clname); 7750fb3f58eSDavid du Colombier for (i = 0; argv[i] != nil; i++) 7760fb3f58eSDavid du Colombier if (prefix(clname, argv[i])) 7770fb3f58eSDavid du Colombier return 1; 7780fb3f58eSDavid du Colombier return 0; 7790fb3f58eSDavid du Colombier } 7800fb3f58eSDavid du Colombier 7810fb3f58eSDavid du Colombier static int 7820fb3f58eSDavid du Colombier makedir(char *s) 7830fb3f58eSDavid du Colombier { 7840fb3f58eSDavid du Colombier int f; 7850fb3f58eSDavid du Colombier 7860fb3f58eSDavid du Colombier if (access(s, AEXIST) == 0) 7870fb3f58eSDavid du Colombier return -1; 7880fb3f58eSDavid du Colombier f = create(s, OREAD, DMDIR | 0777); 7890fb3f58eSDavid du Colombier if (f >= 0) 7900fb3f58eSDavid du Colombier close(f); 7910fb3f58eSDavid du Colombier return f; 7920fb3f58eSDavid du Colombier } 7930fb3f58eSDavid du Colombier 7940fb3f58eSDavid du Colombier static void 7950fb3f58eSDavid du Colombier mkpdirs(char *s) 7960fb3f58eSDavid du Colombier { 7970fb3f58eSDavid du Colombier int done = 0; 7980fb3f58eSDavid du Colombier char *p = s; 7990fb3f58eSDavid du Colombier 8000fb3f58eSDavid du Colombier while (!done && (p = strchr(p + 1, '/')) != nil) { 8010fb3f58eSDavid du Colombier *p = '\0'; 8020fb3f58eSDavid du Colombier done = (access(s, AEXIST) < 0 && makedir(s) < 0); 8030fb3f58eSDavid du Colombier *p = '/'; 8040fb3f58eSDavid du Colombier } 8050fb3f58eSDavid du Colombier } 8060fb3f58eSDavid du Colombier 8070fb3f58eSDavid du Colombier /* copy a file from the archive into the filesystem */ 80823605fa3SDavid du Colombier /* fname is result of name(), so has two extra bytes at beginning */ 8090fb3f58eSDavid du Colombier static void 8100fb3f58eSDavid du Colombier extract1(int ar, Hdr *hp, char *fname) 8110fb3f58eSDavid du Colombier { 8120fb3f58eSDavid du Colombier int wrbytes, fd = -1, dir = 0; 8130fb3f58eSDavid du Colombier long mtime = strtol(hp->mtime, nil, 8); 8140fb3f58eSDavid du Colombier ulong mode = strtoul(hp->mode, nil, 8) & 0777; 8154f281771SDavid du Colombier Off bytes = strtoll(hp->size, nil, 8); /* for printing */ 8164f281771SDavid du Colombier ulong blksread, blksleft = BYTES2TBLKS(hdrsize(hp)); 8170fb3f58eSDavid du Colombier Hdr *hbp; 8180fb3f58eSDavid du Colombier 8190fb3f58eSDavid du Colombier if (isdir(hp)) { 8200fb3f58eSDavid du Colombier mode |= DMDIR|0700; 8210fb3f58eSDavid du Colombier dir = 1; 8220fb3f58eSDavid du Colombier } 8230fb3f58eSDavid du Colombier switch (hp->linkflag) { 82440ef9009SDavid du Colombier case LF_LINK: 82540ef9009SDavid du Colombier case LF_SYMLINK1: 82640ef9009SDavid du Colombier case LF_SYMLINK2: 82740ef9009SDavid du Colombier case LF_FIFO: 8280fb3f58eSDavid du Colombier blksleft = 0; 8293e12c5d1SDavid du Colombier break; 8303e12c5d1SDavid du Colombier } 83123605fa3SDavid du Colombier if (relative) { 83223605fa3SDavid du Colombier if(fname[0] == '/') 83323605fa3SDavid du Colombier *--fname = '.'; 83423605fa3SDavid du Colombier else if(fname[0] == '#'){ 83523605fa3SDavid du Colombier *--fname = '/'; 83623605fa3SDavid du Colombier *--fname = '.'; 83723605fa3SDavid du Colombier } 83823605fa3SDavid du Colombier } 8390fb3f58eSDavid du Colombier if (verb == Xtract) { 8400fb3f58eSDavid du Colombier cleanname(fname); 8410fb3f58eSDavid du Colombier switch (hp->linkflag) { 8420fb3f58eSDavid du Colombier case LF_LINK: 8430fb3f58eSDavid du Colombier case LF_SYMLINK1: 8440fb3f58eSDavid du Colombier case LF_SYMLINK2: 8450fb3f58eSDavid du Colombier fprint(2, "%s: can't make (sym)link %s\n", 8460fb3f58eSDavid du Colombier argv0, fname); 8473e12c5d1SDavid du Colombier break; 8480fb3f58eSDavid du Colombier case LF_FIFO: 8490fb3f58eSDavid du Colombier fprint(2, "%s: can't make fifo %s\n", argv0, fname); 8500fb3f58eSDavid du Colombier break; 8510fb3f58eSDavid du Colombier default: 8520fb3f58eSDavid du Colombier if (!keepexisting || access(fname, AEXIST) < 0) { 8530fb3f58eSDavid du Colombier int rw = (dir? OREAD: OWRITE); 8543e12c5d1SDavid du Colombier 8550fb3f58eSDavid du Colombier fd = create(fname, rw, mode); 8560fb3f58eSDavid du Colombier if (fd < 0) { 8570fb3f58eSDavid du Colombier mkpdirs(fname); 8580fb3f58eSDavid du Colombier fd = create(fname, rw, mode); 8593e12c5d1SDavid du Colombier } 8600fb3f58eSDavid du Colombier if (fd < 0 && 8610fb3f58eSDavid du Colombier (!dir || access(fname, AEXIST) < 0)) 8620fb3f58eSDavid du Colombier fprint(2, "%s: can't create %s: %r\n", 8630fb3f58eSDavid du Colombier argv0, fname); 8643e12c5d1SDavid du Colombier } 8650fb3f58eSDavid du Colombier if (fd >= 0 && verbose) 8660fb3f58eSDavid du Colombier fprint(2, "%s\n", fname); 8670fb3f58eSDavid du Colombier break; 8680fb3f58eSDavid du Colombier } 8690fb3f58eSDavid du Colombier } else if (verbose) { 8700fb3f58eSDavid du Colombier char *cp = ctime(mtime); 8713e12c5d1SDavid du Colombier 8720fb3f58eSDavid du Colombier print("%M %8lld %-12.12s %-4.4s %s\n", 8730fb3f58eSDavid du Colombier mode, bytes, cp+4, cp+24, fname); 8747dd7cddfSDavid du Colombier } else 8750fb3f58eSDavid du Colombier print("%s\n", fname); 8763e12c5d1SDavid du Colombier 8770fb3f58eSDavid du Colombier for (; blksleft > 0; blksleft -= blksread) { 8784f281771SDavid du Colombier hbp = getblkrd(ar, (fd >= 0? Alldata: Justnxthdr)); 8790fb3f58eSDavid du Colombier if (hbp == nil) 8800fb3f58eSDavid du Colombier sysfatal("unexpected EOF on archive extracting %s", 8810fb3f58eSDavid du Colombier fname); 8820fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 8830fb3f58eSDavid du Colombier wrbytes = Tblock*blksread; 8840fb3f58eSDavid du Colombier if(wrbytes > bytes) 8850fb3f58eSDavid du Colombier wrbytes = bytes; 8860fb3f58eSDavid du Colombier if (fd >= 0 && write(fd, hbp->data, wrbytes) != wrbytes) 8870fb3f58eSDavid du Colombier sysfatal("write error on %s: %r", fname); 8880fb3f58eSDavid du Colombier putreadblks(ar, blksread); 8890fb3f58eSDavid du Colombier bytes -= wrbytes; 89040ef9009SDavid du Colombier } 8910fb3f58eSDavid du Colombier if (fd >= 0) { 8923e12c5d1SDavid du Colombier /* 8930fb3f58eSDavid du Colombier * directories should be wstated after we're done 8940fb3f58eSDavid du Colombier * creating files in them. 8953e12c5d1SDavid du Colombier */ 8960fb3f58eSDavid du Colombier if (settime) { 8970fb3f58eSDavid du Colombier Dir nd; 8980fb3f58eSDavid du Colombier 8990fb3f58eSDavid du Colombier nulldir(&nd); 9000fb3f58eSDavid du Colombier nd.mtime = mtime; 9010fb3f58eSDavid du Colombier if (isustar(hp)) 9020fb3f58eSDavid du Colombier nd.gid = hp->gname; 9030fb3f58eSDavid du Colombier dirfwstat(fd, &nd); 9040fb3f58eSDavid du Colombier } 9050fb3f58eSDavid du Colombier close(fd); 9060fb3f58eSDavid du Colombier } 9070fb3f58eSDavid du Colombier } 9080fb3f58eSDavid du Colombier 9090fb3f58eSDavid du Colombier static void 9100fb3f58eSDavid du Colombier skip(int ar, Hdr *hp, char *fname) 9113e12c5d1SDavid du Colombier { 9120fb3f58eSDavid du Colombier ulong blksleft, blksread; 9130fb3f58eSDavid du Colombier Hdr *hbp; 9140fb3f58eSDavid du Colombier 9154f281771SDavid du Colombier for (blksleft = BYTES2TBLKS(hdrsize(hp)); blksleft > 0; 9164f281771SDavid du Colombier blksleft -= blksread) { 9174f281771SDavid du Colombier hbp = getblkrd(ar, Justnxthdr); 9180fb3f58eSDavid du Colombier if (hbp == nil) 9190fb3f58eSDavid du Colombier sysfatal("unexpected EOF on archive extracting %s", 9200fb3f58eSDavid du Colombier fname); 9210fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 9220fb3f58eSDavid du Colombier putreadblks(ar, blksread); 9230fb3f58eSDavid du Colombier } 9240fb3f58eSDavid du Colombier } 9250fb3f58eSDavid du Colombier 9260fb3f58eSDavid du Colombier static char * 9270fb3f58eSDavid du Colombier extract(char **argv) 9280fb3f58eSDavid du Colombier { 9290fb3f58eSDavid du Colombier int ar; 9300fb3f58eSDavid du Colombier char *longname; 9310fb3f58eSDavid du Colombier Hdr *hp; 9320fb3f58eSDavid du Colombier Compress *comp = nil; 9330fb3f58eSDavid du Colombier Pushstate ps; 9340fb3f58eSDavid du Colombier 9350fb3f58eSDavid du Colombier if (usefile) { 9360fb3f58eSDavid du Colombier ar = open(usefile, OREAD); 9370fb3f58eSDavid du Colombier comp = compmethod(usefile); 9380fb3f58eSDavid du Colombier } else 9390fb3f58eSDavid du Colombier ar = Stdin; 9400fb3f58eSDavid du Colombier if (comp) 9410fb3f58eSDavid du Colombier ar = push(ar, comp->decomp, Input, &ps); 9420fb3f58eSDavid du Colombier if (ar < 0) 9430fb3f58eSDavid du Colombier sysfatal("can't open archive %s: %r", usefile); 9440fb3f58eSDavid du Colombier 9450fb3f58eSDavid du Colombier while ((hp = readhdr(ar)) != nil) { 9460fb3f58eSDavid du Colombier longname = name(hp); 9470fb3f58eSDavid du Colombier if (match(longname, argv)) 9480fb3f58eSDavid du Colombier extract1(ar, hp, longname); 9490fb3f58eSDavid du Colombier else 9500fb3f58eSDavid du Colombier skip(ar, hp, longname); 9510fb3f58eSDavid du Colombier } 9520fb3f58eSDavid du Colombier 9530fb3f58eSDavid du Colombier if (comp) 9540fb3f58eSDavid du Colombier return pushclose(&ps); 9550fb3f58eSDavid du Colombier if (ar > Stderr) 9560fb3f58eSDavid du Colombier close(ar); 9570fb3f58eSDavid du Colombier return nil; 9583e12c5d1SDavid du Colombier } 9593e12c5d1SDavid du Colombier 9603e12c5d1SDavid du Colombier void 9610fb3f58eSDavid du Colombier main(int argc, char *argv[]) 9623e12c5d1SDavid du Colombier { 9630fb3f58eSDavid du Colombier int errflg = 0; 9640fb3f58eSDavid du Colombier char *ret = nil; 9650fb3f58eSDavid du Colombier 9660fb3f58eSDavid du Colombier quotefmtinstall(); 9670fb3f58eSDavid du Colombier fmtinstall('M', dirmodefmt); 9680fb3f58eSDavid du Colombier 9690fb3f58eSDavid du Colombier TARGBEGIN { 9700fb3f58eSDavid du Colombier case 'c': 9710fb3f58eSDavid du Colombier docreate++; 9720fb3f58eSDavid du Colombier verb = Replace; 9730fb3f58eSDavid du Colombier break; 9740fb3f58eSDavid du Colombier case 'f': 9750fb3f58eSDavid du Colombier usefile = EARGF(usage()); 9760fb3f58eSDavid du Colombier break; 9770fb3f58eSDavid du Colombier case 'g': 9780fb3f58eSDavid du Colombier argid = strtoul(EARGF(usage()), 0, 0); 9790fb3f58eSDavid du Colombier break; 9800fb3f58eSDavid du Colombier case 'k': 9810fb3f58eSDavid du Colombier keepexisting++; 9820fb3f58eSDavid du Colombier break; 9830fb3f58eSDavid du Colombier case 'm': /* compatibility */ 9840fb3f58eSDavid du Colombier settime = 0; 9850fb3f58eSDavid du Colombier break; 9860fb3f58eSDavid du Colombier case 'p': 9870fb3f58eSDavid du Colombier posix++; 9880fb3f58eSDavid du Colombier break; 9890fb3f58eSDavid du Colombier case 'P': 9900fb3f58eSDavid du Colombier posix = 0; 9910fb3f58eSDavid du Colombier break; 9920fb3f58eSDavid du Colombier case 'r': 9930fb3f58eSDavid du Colombier verb = Replace; 9940fb3f58eSDavid du Colombier break; 9950fb3f58eSDavid du Colombier case 'R': 99623605fa3SDavid du Colombier relative = 0; 9970fb3f58eSDavid du Colombier break; 9980fb3f58eSDavid du Colombier case 't': 9990fb3f58eSDavid du Colombier verb = Toc; 10000fb3f58eSDavid du Colombier break; 10010fb3f58eSDavid du Colombier case 'T': 10020fb3f58eSDavid du Colombier settime++; 10030fb3f58eSDavid du Colombier break; 10040fb3f58eSDavid du Colombier case 'u': 10050fb3f58eSDavid du Colombier aruid = strtoul(EARGF(usage()), 0, 0); 10060fb3f58eSDavid du Colombier break; 10070fb3f58eSDavid du Colombier case 'v': 10080fb3f58eSDavid du Colombier verbose++; 10090fb3f58eSDavid du Colombier break; 10100fb3f58eSDavid du Colombier case 'x': 10110fb3f58eSDavid du Colombier verb = Xtract; 10120fb3f58eSDavid du Colombier break; 10130fb3f58eSDavid du Colombier case 'z': 10140fb3f58eSDavid du Colombier docompress++; 10150fb3f58eSDavid du Colombier break; 10160fb3f58eSDavid du Colombier case '-': 10170fb3f58eSDavid du Colombier break; 10180fb3f58eSDavid du Colombier default: 10190fb3f58eSDavid du Colombier fprint(2, "tar: unknown letter %C\n", TARGC()); 10200fb3f58eSDavid du Colombier errflg++; 10210fb3f58eSDavid du Colombier break; 10220fb3f58eSDavid du Colombier } TARGEND 10230fb3f58eSDavid du Colombier 10240fb3f58eSDavid du Colombier if (argc < 0 || errflg) 10250fb3f58eSDavid du Colombier usage(); 10260fb3f58eSDavid du Colombier 10270fb3f58eSDavid du Colombier initblks(); 10280fb3f58eSDavid du Colombier switch (verb) { 10290fb3f58eSDavid du Colombier case Toc: 10300fb3f58eSDavid du Colombier case Xtract: 10310fb3f58eSDavid du Colombier ret = extract(argv); 10320fb3f58eSDavid du Colombier break; 10330fb3f58eSDavid du Colombier case Replace: 10340fb3f58eSDavid du Colombier if (getwd(origdir, sizeof origdir) == nil) 10350fb3f58eSDavid du Colombier strcpy(origdir, "/tmp"); 10360fb3f58eSDavid du Colombier ret = replace(argv); 10370fb3f58eSDavid du Colombier break; 10380fb3f58eSDavid du Colombier default: 10390fb3f58eSDavid du Colombier usage(); 10400fb3f58eSDavid du Colombier break; 10410fb3f58eSDavid du Colombier } 10420fb3f58eSDavid du Colombier exits(ret); 10433e12c5d1SDavid du Colombier } 1044