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; 360fb3f58eSDavid du Colombier typedef char *(*Refill)(int ar, char *bufs); 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 }; 4240ef9009SDavid du Colombier enum { 430fb3f58eSDavid du Colombier Tblock = 512, 440fb3f58eSDavid du Colombier Nblock = 40, /* maximum blocksize */ 450fb3f58eSDavid du Colombier Dblock = 20, /* default blocksize */ 460fb3f58eSDavid du Colombier Namsiz = 100, 4740ef9009SDavid du Colombier Maxpfx = 155, /* from POSIX */ 480fb3f58eSDavid du Colombier Maxname = Namsiz + 1 + Maxpfx, 490fb3f58eSDavid du Colombier DEBUG = 0, 5040ef9009SDavid du Colombier }; 5140ef9009SDavid du Colombier 5240ef9009SDavid du Colombier /* POSIX link flags */ 5340ef9009SDavid du Colombier enum { 5440ef9009SDavid du Colombier LF_PLAIN1 = '\0', 5540ef9009SDavid du Colombier LF_PLAIN2 = '0', 5640ef9009SDavid du Colombier LF_LINK = '1', 5740ef9009SDavid du Colombier LF_SYMLINK1 = '2', 5840ef9009SDavid du Colombier LF_SYMLINK2 = 's', 5940ef9009SDavid du Colombier LF_CHR = '3', 6040ef9009SDavid du Colombier LF_BLK = '4', 6140ef9009SDavid du Colombier LF_DIR = '5', 6240ef9009SDavid du Colombier LF_FIFO = '6', 6340ef9009SDavid du Colombier LF_CONTIG = '7', 6440ef9009SDavid du Colombier /* 'A' - 'Z' are reserved for custom implementations */ 6540ef9009SDavid du Colombier }; 6640ef9009SDavid du Colombier 6740ef9009SDavid du Colombier #define islink(lf) (isreallink(lf) || issymlink(lf)) 6840ef9009SDavid du Colombier #define isreallink(lf) ((lf) == LF_LINK) 6940ef9009SDavid du Colombier #define issymlink(lf) ((lf) == LF_SYMLINK1 || (lf) == LF_SYMLINK2) 700fb3f58eSDavid du Colombier 710fb3f58eSDavid du Colombier typedef union { 720fb3f58eSDavid du Colombier uchar data[Tblock]; 730fb3f58eSDavid du Colombier struct { 740fb3f58eSDavid du Colombier char name[Namsiz]; 753e12c5d1SDavid du Colombier char mode[8]; 763e12c5d1SDavid du Colombier char uid[8]; 773e12c5d1SDavid du Colombier char gid[8]; 783e12c5d1SDavid du Colombier char size[12]; 793e12c5d1SDavid du Colombier char mtime[12]; 803e12c5d1SDavid du Colombier char chksum[8]; 813e12c5d1SDavid du Colombier char linkflag; 820fb3f58eSDavid du Colombier char linkname[Namsiz]; 830fb3f58eSDavid du Colombier 8440ef9009SDavid du Colombier /* rest are defined by POSIX's ustar format; see p1003.2b */ 8540ef9009SDavid du Colombier char magic[6]; /* "ustar" */ 8640ef9009SDavid du Colombier char version[2]; 8740ef9009SDavid du Colombier char uname[32]; 8840ef9009SDavid du Colombier char gname[32]; 8940ef9009SDavid du Colombier char devmajor[8]; 9040ef9009SDavid du Colombier char devminor[8]; 910fb3f58eSDavid du Colombier char prefix[Maxpfx]; /* if non-null, path= prefix "/" name */ 920fb3f58eSDavid du Colombier }; 930fb3f58eSDavid du Colombier } Hdr; 943e12c5d1SDavid du Colombier 950fb3f58eSDavid du Colombier typedef struct { 960fb3f58eSDavid du Colombier char *comp; 970fb3f58eSDavid du Colombier char *decomp; 980fb3f58eSDavid du Colombier char *sfx[4]; 990fb3f58eSDavid du Colombier } Compress; 1003e12c5d1SDavid du Colombier 1010fb3f58eSDavid du Colombier static Compress comps[] = { 1020fb3f58eSDavid du Colombier "gzip", "gunzip", { ".tar.gz", ".tgz" }, /* default */ 1030fb3f58eSDavid du Colombier "compress", "uncompress", { ".tar.Z", ".tz" }, 1040fb3f58eSDavid du Colombier "bzip2", "bunzip2", { ".tar.bz", ".tbz", 1050fb3f58eSDavid du Colombier ".tar.bz2",".tbz2" }, 1060fb3f58eSDavid du Colombier }; 1073e12c5d1SDavid du Colombier 1080fb3f58eSDavid du Colombier typedef struct { 1090fb3f58eSDavid du Colombier int kid; 1100fb3f58eSDavid du Colombier int fd; /* original fd */ 1110fb3f58eSDavid du Colombier int rfd; /* replacement fd */ 1120fb3f58eSDavid du Colombier int input; 1130fb3f58eSDavid du Colombier int open; 1140fb3f58eSDavid du Colombier } Pushstate; 11540ef9009SDavid du Colombier 1160fb3f58eSDavid du Colombier #define OTHER(rdwr) (rdwr == Rd? Wr: Rd) 1170fb3f58eSDavid du Colombier 1180fb3f58eSDavid du Colombier static int debug; 1190fb3f58eSDavid du Colombier static int verb; 1200fb3f58eSDavid du Colombier static int posix = 1; 1210fb3f58eSDavid du Colombier static int docreate; 1220fb3f58eSDavid du Colombier static int aruid; 1230fb3f58eSDavid du Colombier static int argid; 124*23605fa3SDavid du Colombier static int relative = 1; 1250fb3f58eSDavid du Colombier static int settime; 1260fb3f58eSDavid du Colombier static int verbose; 1270fb3f58eSDavid du Colombier static int docompress; 1280fb3f58eSDavid du Colombier static int keepexisting; 1290fb3f58eSDavid du Colombier 1300fb3f58eSDavid du Colombier static int nblock = Dblock; 1310fb3f58eSDavid du Colombier static char *usefile; 1320fb3f58eSDavid du Colombier static char origdir[Maxname*2]; 1330fb3f58eSDavid du Colombier static Hdr *tpblk, *endblk; 1340fb3f58eSDavid du Colombier static Hdr *curblk; 1350fb3f58eSDavid du Colombier 1360fb3f58eSDavid du Colombier static void 1370fb3f58eSDavid du Colombier usage(void) 13840ef9009SDavid du Colombier { 1390fb3f58eSDavid du Colombier fprint(2, "usage: %s {crtx}[PRTfgkmpuvz] [archive] file1 file2...\n", 1400fb3f58eSDavid du Colombier argv0); 1410fb3f58eSDavid du Colombier exits("usage"); 1420fb3f58eSDavid du Colombier } 1430fb3f58eSDavid du Colombier 1440fb3f58eSDavid du Colombier /* compression */ 1450fb3f58eSDavid du Colombier 1460fb3f58eSDavid du Colombier static Compress * 1470fb3f58eSDavid du Colombier compmethod(char *name) 1480fb3f58eSDavid du Colombier { 1490fb3f58eSDavid du Colombier int i, nmlen = strlen(name), sfxlen; 1500fb3f58eSDavid du Colombier Compress *cp; 1510fb3f58eSDavid du Colombier 1520fb3f58eSDavid du Colombier for (cp = comps; cp < comps + nelem(comps); cp++) 1530fb3f58eSDavid du Colombier for (i = 0; i < nelem(cp->sfx) && cp->sfx[i]; i++) { 1540fb3f58eSDavid du Colombier sfxlen = strlen(cp->sfx[i]); 1550fb3f58eSDavid du Colombier if (nmlen > sfxlen && 1560fb3f58eSDavid du Colombier strcmp(cp->sfx[i], name + nmlen - sfxlen) == 0) 1570fb3f58eSDavid du Colombier return cp; 1580fb3f58eSDavid du Colombier } 1590fb3f58eSDavid du Colombier return docompress? comps: nil; 1600fb3f58eSDavid du Colombier } 1610fb3f58eSDavid du Colombier 1620fb3f58eSDavid du Colombier /* 1630fb3f58eSDavid du Colombier * push a filter, cmd, onto fd. if input, it's an input descriptor. 1640fb3f58eSDavid du Colombier * returns a descriptor to replace fd, or -1 on error. 1650fb3f58eSDavid du Colombier */ 1660fb3f58eSDavid du Colombier static int 1670fb3f58eSDavid du Colombier push(int fd, char *cmd, int input, Pushstate *ps) 1680fb3f58eSDavid du Colombier { 1690fb3f58eSDavid du Colombier int nfd, pifds[2]; 1700fb3f58eSDavid du Colombier String *s; 1710fb3f58eSDavid du Colombier 1720fb3f58eSDavid du Colombier ps->open = 0; 1730fb3f58eSDavid du Colombier ps->fd = fd; 1740fb3f58eSDavid du Colombier ps->input = input; 1750fb3f58eSDavid du Colombier if (fd < 0 || pipe(pifds) < 0) 1760fb3f58eSDavid du Colombier return -1; 1770fb3f58eSDavid du Colombier ps->kid = fork(); 1780fb3f58eSDavid du Colombier switch (ps->kid) { 1790fb3f58eSDavid du Colombier case -1: 1800fb3f58eSDavid du Colombier return -1; 1810fb3f58eSDavid du Colombier case 0: 1820fb3f58eSDavid du Colombier if (input) 1830fb3f58eSDavid du Colombier dup(pifds[Wr], Stdout); 1840fb3f58eSDavid du Colombier else 1850fb3f58eSDavid du Colombier dup(pifds[Rd], Stdin); 1860fb3f58eSDavid du Colombier close(pifds[input? Rd: Wr]); 1870fb3f58eSDavid du Colombier dup(fd, (input? Stdin: Stdout)); 1880fb3f58eSDavid du Colombier s = s_new(); 1890fb3f58eSDavid du Colombier if (cmd[0] != '/') 1900fb3f58eSDavid du Colombier s_append(s, "/bin/"); 1910fb3f58eSDavid du Colombier s_append(s, cmd); 1920fb3f58eSDavid du Colombier execl(s_to_c(s), cmd, nil); 1930fb3f58eSDavid du Colombier sysfatal("can't exec %s: %r", cmd); 1940fb3f58eSDavid du Colombier default: 1950fb3f58eSDavid du Colombier nfd = pifds[input? Rd: Wr]; 1960fb3f58eSDavid du Colombier close(pifds[input? Wr: Rd]); 1970fb3f58eSDavid du Colombier break; 1980fb3f58eSDavid du Colombier } 1990fb3f58eSDavid du Colombier ps->rfd = nfd; 2000fb3f58eSDavid du Colombier ps->open = 1; 2010fb3f58eSDavid du Colombier return nfd; 2020fb3f58eSDavid du Colombier } 2030fb3f58eSDavid du Colombier 2040fb3f58eSDavid du Colombier static char * 2050fb3f58eSDavid du Colombier pushclose(Pushstate *ps) 2060fb3f58eSDavid du Colombier { 2070fb3f58eSDavid du Colombier Waitmsg *wm; 2080fb3f58eSDavid du Colombier 2090fb3f58eSDavid du Colombier if (ps->fd < 0 || ps->rfd < 0 || !ps->open) 2100fb3f58eSDavid du Colombier return "not open"; 2110fb3f58eSDavid du Colombier close(ps->rfd); 2120fb3f58eSDavid du Colombier ps->rfd = -1; 2130fb3f58eSDavid du Colombier ps->open = 0; 2140fb3f58eSDavid du Colombier while ((wm = wait()) != nil && wm->pid != ps->kid) 2150fb3f58eSDavid du Colombier continue; 2160fb3f58eSDavid du Colombier return wm? wm->msg: nil; 2170fb3f58eSDavid du Colombier } 2180fb3f58eSDavid du Colombier 2190fb3f58eSDavid du Colombier /* 2200fb3f58eSDavid du Colombier * block-buffer management 2210fb3f58eSDavid du Colombier */ 2220fb3f58eSDavid du Colombier 2230fb3f58eSDavid du Colombier static void 2240fb3f58eSDavid du Colombier initblks(void) 2250fb3f58eSDavid du Colombier { 2260fb3f58eSDavid du Colombier free(tpblk); 2270fb3f58eSDavid du Colombier tpblk = malloc(Tblock * nblock); 2280fb3f58eSDavid du Colombier assert(tpblk != nil); 2290fb3f58eSDavid du Colombier endblk = tpblk + nblock; 2300fb3f58eSDavid du Colombier } 2310fb3f58eSDavid du Colombier 2320fb3f58eSDavid du Colombier /* (re)fill block buffers from archive */ 2330fb3f58eSDavid du Colombier static char * 2340fb3f58eSDavid du Colombier refill(int ar, char *bufs) 2350fb3f58eSDavid du Colombier { 2360fb3f58eSDavid du Colombier int i, n; 2370fb3f58eSDavid du Colombier unsigned bytes = Tblock * nblock; 2380fb3f58eSDavid du Colombier static int done, first = 1; 2390fb3f58eSDavid du Colombier 2400fb3f58eSDavid du Colombier if (done) 2410fb3f58eSDavid du Colombier return nil; 2420fb3f58eSDavid du Colombier 2430fb3f58eSDavid du Colombier /* try to size non-pipe input at first read */ 2440fb3f58eSDavid du Colombier if (first && usefile) { 2450fb3f58eSDavid du Colombier first = 0; 2460fb3f58eSDavid du Colombier n = read(ar, bufs, bytes); 2470fb3f58eSDavid du Colombier if (n <= 0) 2480fb3f58eSDavid du Colombier sysfatal("error reading archive: %r"); 2490fb3f58eSDavid du Colombier i = n; 2500fb3f58eSDavid du Colombier if (i % Tblock != 0) { 2510fb3f58eSDavid du Colombier fprint(2, "%s: archive block size (%d) error\n", 2520fb3f58eSDavid du Colombier argv0, i); 2530fb3f58eSDavid du Colombier exits("blocksize"); 2540fb3f58eSDavid du Colombier } 2550fb3f58eSDavid du Colombier i /= Tblock; 2560fb3f58eSDavid du Colombier if (i != nblock) { 2570fb3f58eSDavid du Colombier nblock = i; 2580fb3f58eSDavid du Colombier fprint(2, "%s: blocking = %d\n", argv0, nblock); 2590fb3f58eSDavid du Colombier endblk = (Hdr *)bufs + nblock; 2600fb3f58eSDavid du Colombier bytes = n; 2610fb3f58eSDavid du Colombier } 2620fb3f58eSDavid du Colombier } else 2630fb3f58eSDavid du Colombier n = readn(ar, bufs, bytes); 2640fb3f58eSDavid du Colombier if (n == 0) 2650fb3f58eSDavid du Colombier sysfatal("unexpected EOF reading archive"); 2660fb3f58eSDavid du Colombier else if (n < 0) 2670fb3f58eSDavid du Colombier sysfatal("error reading archive: %r"); 2680fb3f58eSDavid du Colombier else if (n%Tblock != 0) 2690fb3f58eSDavid du Colombier sysfatal("partial block read from archive"); 2700fb3f58eSDavid du Colombier if (n != bytes) { 2710fb3f58eSDavid du Colombier done = 1; 2720fb3f58eSDavid du Colombier memset(bufs + n, 0, bytes - n); 2730fb3f58eSDavid du Colombier } 2740fb3f58eSDavid du Colombier return bufs; 2750fb3f58eSDavid du Colombier } 2760fb3f58eSDavid du Colombier 2770fb3f58eSDavid du Colombier static Hdr * 2780fb3f58eSDavid du Colombier getblk(int ar, Refill rfp) 2790fb3f58eSDavid du Colombier { 2800fb3f58eSDavid du Colombier if (curblk == nil || curblk >= endblk) { /* input block exhausted? */ 2810fb3f58eSDavid du Colombier if (rfp != nil && (*rfp)(ar, (char *)tpblk) == nil) 2820fb3f58eSDavid du Colombier return nil; 2830fb3f58eSDavid du Colombier curblk = tpblk; 2840fb3f58eSDavid du Colombier } 2850fb3f58eSDavid du Colombier return curblk++; 2860fb3f58eSDavid du Colombier } 2870fb3f58eSDavid du Colombier 2880fb3f58eSDavid du Colombier static Hdr * 2890fb3f58eSDavid du Colombier getblkrd(int ar) 2900fb3f58eSDavid du Colombier { 2910fb3f58eSDavid du Colombier return getblk(ar, refill); 2920fb3f58eSDavid du Colombier } 2930fb3f58eSDavid du Colombier 2940fb3f58eSDavid du Colombier static Hdr * 2950fb3f58eSDavid du Colombier getblke(int ar) 2960fb3f58eSDavid du Colombier { 2970fb3f58eSDavid du Colombier return getblk(ar, nil); 2980fb3f58eSDavid du Colombier } 2990fb3f58eSDavid du Colombier 3000fb3f58eSDavid du Colombier static Hdr * 3010fb3f58eSDavid du Colombier getblkz(int ar) 3020fb3f58eSDavid du Colombier { 3030fb3f58eSDavid du Colombier Hdr *hp = getblke(ar); 3040fb3f58eSDavid du Colombier 3050fb3f58eSDavid du Colombier if (hp != nil) 3060fb3f58eSDavid du Colombier memset(hp->data, 0, Tblock); 3070fb3f58eSDavid du Colombier return hp; 3080fb3f58eSDavid du Colombier } 3090fb3f58eSDavid du Colombier 3100fb3f58eSDavid du Colombier /* 3110fb3f58eSDavid du Colombier * how many block buffers are available, starting at the address 3120fb3f58eSDavid du Colombier * just returned by getblk*? 3130fb3f58eSDavid du Colombier */ 3140fb3f58eSDavid du Colombier static int 3150fb3f58eSDavid du Colombier gothowmany(int max) 3160fb3f58eSDavid du Colombier { 3170fb3f58eSDavid du Colombier int n = endblk - (curblk - 1); 3180fb3f58eSDavid du Colombier 3190fb3f58eSDavid du Colombier return n > max? max: n; 3200fb3f58eSDavid du Colombier } 3210fb3f58eSDavid du Colombier 3220fb3f58eSDavid du Colombier /* 3230fb3f58eSDavid du Colombier * indicate that one is done with the last block obtained from getblke 3240fb3f58eSDavid du Colombier * and it is now available to be written into the archive. 3250fb3f58eSDavid du Colombier */ 3260fb3f58eSDavid du Colombier static void 3270fb3f58eSDavid du Colombier putlastblk(int ar) 3280fb3f58eSDavid du Colombier { 3290fb3f58eSDavid du Colombier unsigned bytes = Tblock * nblock; 3300fb3f58eSDavid du Colombier 3310fb3f58eSDavid du Colombier /* if writing end-of-archive, aid compression (good hygiene too) */ 3320fb3f58eSDavid du Colombier if (curblk < endblk) 3330fb3f58eSDavid du Colombier memset(curblk, 0, (char *)endblk - (char *)curblk); 3340fb3f58eSDavid du Colombier if (write(ar, tpblk, bytes) != bytes) 3350fb3f58eSDavid du Colombier sysfatal("error writing archive: %r"); 33640ef9009SDavid du Colombier } 33740ef9009SDavid du Colombier 33840ef9009SDavid du Colombier static void 3390fb3f58eSDavid du Colombier putblk(int ar) 34040ef9009SDavid du Colombier { 3410fb3f58eSDavid du Colombier if (curblk >= endblk) 3420fb3f58eSDavid du Colombier putlastblk(ar); 3430fb3f58eSDavid du Colombier } 3440fb3f58eSDavid du Colombier 3450fb3f58eSDavid du Colombier static void 3460fb3f58eSDavid du Colombier putbackblk(int ar) 3470fb3f58eSDavid du Colombier { 3480fb3f58eSDavid du Colombier curblk--; 3490fb3f58eSDavid du Colombier USED(ar); 3500fb3f58eSDavid du Colombier } 3510fb3f58eSDavid du Colombier 3520fb3f58eSDavid du Colombier static void 3530fb3f58eSDavid du Colombier putreadblks(int ar, int blks) 3540fb3f58eSDavid du Colombier { 3550fb3f58eSDavid du Colombier curblk += blks - 1; 3560fb3f58eSDavid du Colombier USED(ar); 3570fb3f58eSDavid du Colombier } 3580fb3f58eSDavid du Colombier 3590fb3f58eSDavid du Colombier static void 3600fb3f58eSDavid du Colombier putblkmany(int ar, int blks) 3610fb3f58eSDavid du Colombier { 3620fb3f58eSDavid du Colombier curblk += blks - 1; 3630fb3f58eSDavid du Colombier putblk(ar); 3640fb3f58eSDavid du Colombier } 3650fb3f58eSDavid du Colombier 3660fb3f58eSDavid du Colombier /* 3670fb3f58eSDavid du Colombier * common routines 3680fb3f58eSDavid du Colombier */ 3690fb3f58eSDavid du Colombier 3700fb3f58eSDavid du Colombier /* modifies hp->chksum */ 3710fb3f58eSDavid du Colombier long 3720fb3f58eSDavid du Colombier chksum(Hdr *hp) 3730fb3f58eSDavid du Colombier { 3740fb3f58eSDavid du Colombier int n = Tblock; 3750fb3f58eSDavid du Colombier long i = 0; 3760fb3f58eSDavid du Colombier uchar *cp = hp->data; 3770fb3f58eSDavid du Colombier 3780fb3f58eSDavid du Colombier memset(hp->chksum, ' ', sizeof hp->chksum); 3790fb3f58eSDavid du Colombier while (n-- > 0) 3800fb3f58eSDavid du Colombier i += *cp++; 3810fb3f58eSDavid du Colombier return i; 3820fb3f58eSDavid du Colombier } 3830fb3f58eSDavid du Colombier 3840fb3f58eSDavid du Colombier static int 3850fb3f58eSDavid du Colombier isustar(Hdr *hp) 3860fb3f58eSDavid du Colombier { 3870fb3f58eSDavid du Colombier return strcmp(hp->magic, "ustar") == 0; 38840ef9009SDavid du Colombier } 38940ef9009SDavid du Colombier 39040ef9009SDavid du Colombier /* 39140ef9009SDavid du Colombier * s is at most n bytes long, but need not be NUL-terminated. 39240ef9009SDavid du Colombier * if shorter than n bytes, all bytes after the first NUL must also 39340ef9009SDavid du Colombier * be NUL. 39440ef9009SDavid du Colombier */ 39540ef9009SDavid du Colombier static int 39640ef9009SDavid du Colombier strnlen(char *s, int n) 39740ef9009SDavid du Colombier { 3980fb3f58eSDavid du Colombier return s[n - 1] != '\0'? n: strlen(s); 39940ef9009SDavid du Colombier } 40040ef9009SDavid du Colombier 4010fb3f58eSDavid du Colombier /* set fullname from header */ 4020fb3f58eSDavid du Colombier static char * 4030fb3f58eSDavid du Colombier name(Hdr *hp) 40440ef9009SDavid du Colombier { 40540ef9009SDavid du Colombier int pfxlen, namlen; 406*23605fa3SDavid du Colombier static char fullnamebuf[2 + Maxname + 1]; /* 2 at beginning for ./ on relative names */ 407*23605fa3SDavid du Colombier char *fullname; 40840ef9009SDavid du Colombier 409*23605fa3SDavid du Colombier fullname = fullnamebuf+2; 41040ef9009SDavid du Colombier namlen = strnlen(hp->name, sizeof hp->name); 4110fb3f58eSDavid du Colombier if (hp->prefix[0] == '\0' || !isustar(hp)) { /* old-style name? */ 41240ef9009SDavid du Colombier memmove(fullname, hp->name, namlen); 41340ef9009SDavid du Colombier fullname[namlen] = '\0'; 4140fb3f58eSDavid du Colombier return fullname; 41540ef9009SDavid du Colombier } 4160fb3f58eSDavid du Colombier 4170fb3f58eSDavid du Colombier /* name is in two pieces */ 41840ef9009SDavid du Colombier pfxlen = strnlen(hp->prefix, sizeof hp->prefix); 41940ef9009SDavid du Colombier memmove(fullname, hp->prefix, pfxlen); 42040ef9009SDavid du Colombier fullname[pfxlen] = '/'; 42140ef9009SDavid du Colombier memmove(fullname + pfxlen + 1, hp->name, namlen); 42240ef9009SDavid du Colombier fullname[pfxlen + 1 + namlen] = '\0'; 4230fb3f58eSDavid du Colombier return fullname; 4240fb3f58eSDavid du Colombier } 4250fb3f58eSDavid du Colombier 4260fb3f58eSDavid du Colombier static int 4270fb3f58eSDavid du Colombier isdir(Hdr *hp) 4280fb3f58eSDavid du Colombier { 4290fb3f58eSDavid du Colombier /* the mode test is ugly but sometimes necessary */ 4300fb3f58eSDavid du Colombier return hp->linkflag == LF_DIR || 4310fb3f58eSDavid du Colombier strrchr(name(hp), '\0')[-1] == '/' || 4320fb3f58eSDavid du Colombier (strtoul(hp->mode, nil, 8)&0170000) == 040000; 4330fb3f58eSDavid du Colombier } 4340fb3f58eSDavid du Colombier 4350fb3f58eSDavid du Colombier static int 4360fb3f58eSDavid du Colombier eotar(Hdr *hp) 4370fb3f58eSDavid du Colombier { 4380fb3f58eSDavid du Colombier return name(hp)[0] == '\0'; 4390fb3f58eSDavid du Colombier } 4400fb3f58eSDavid du Colombier 4410fb3f58eSDavid du Colombier static Hdr * 4420fb3f58eSDavid du Colombier readhdr(int ar) 4430fb3f58eSDavid du Colombier { 4440fb3f58eSDavid du Colombier long hdrcksum; 4450fb3f58eSDavid du Colombier Hdr *hp; 4460fb3f58eSDavid du Colombier 4470fb3f58eSDavid du Colombier hp = getblkrd(ar); 4480fb3f58eSDavid du Colombier if (hp == nil) 4490fb3f58eSDavid du Colombier sysfatal("unexpected EOF instead of archive header"); 4500fb3f58eSDavid du Colombier if (eotar(hp)) /* end-of-archive block? */ 4510fb3f58eSDavid du Colombier return nil; 4520fb3f58eSDavid du Colombier hdrcksum = strtoul(hp->chksum, nil, 8); 4530fb3f58eSDavid du Colombier if (chksum(hp) != hdrcksum) 4540fb3f58eSDavid du Colombier sysfatal("bad archive header checksum: name %.64s...", 4550fb3f58eSDavid du Colombier hp->name); 4560fb3f58eSDavid du Colombier return hp; 45740ef9009SDavid du Colombier } 45840ef9009SDavid du Colombier 45940ef9009SDavid du Colombier /* 4600fb3f58eSDavid du Colombier * tar r[c] 4610fb3f58eSDavid du Colombier */ 4620fb3f58eSDavid du Colombier 4630fb3f58eSDavid du Colombier /* 4640fb3f58eSDavid du Colombier * if name is longer than Namsiz bytes, try to split it at a slash and fit the 46540ef9009SDavid du Colombier * pieces into hp->prefix and hp->name. 46640ef9009SDavid du Colombier */ 46740ef9009SDavid du Colombier static int 4680fb3f58eSDavid du Colombier putfullname(Hdr *hp, char *name) 46940ef9009SDavid du Colombier { 47040ef9009SDavid du Colombier int namlen, pfxlen; 47140ef9009SDavid du Colombier char *sl, *osl; 4720fb3f58eSDavid du Colombier String *slname = nil; 4730fb3f58eSDavid du Colombier 4740fb3f58eSDavid du Colombier if (isdir(hp)) { 4750fb3f58eSDavid du Colombier slname = s_new(); 4760fb3f58eSDavid du Colombier s_append(slname, name); 4770fb3f58eSDavid du Colombier s_append(slname, "/"); /* posix requires this */ 4780fb3f58eSDavid du Colombier name = s_to_c(slname); 4790fb3f58eSDavid du Colombier } 48040ef9009SDavid du Colombier 48140ef9009SDavid du Colombier namlen = strlen(name); 4820fb3f58eSDavid du Colombier if (namlen <= Namsiz) { 4830fb3f58eSDavid du Colombier strncpy(hp->name, name, Namsiz); 48440ef9009SDavid du Colombier hp->prefix[0] = '\0'; /* ustar paranoia */ 48540ef9009SDavid du Colombier return 0; 48640ef9009SDavid du Colombier } 4870fb3f58eSDavid du Colombier 4880fb3f58eSDavid du Colombier if (!posix || namlen > Maxname) { 4890fb3f58eSDavid du Colombier fprint(2, "%s: name too long for tar header: %s\n", 4900fb3f58eSDavid du Colombier argv0, name); 49140ef9009SDavid du Colombier return -1; 49240ef9009SDavid du Colombier } 49340ef9009SDavid du Colombier /* 49440ef9009SDavid du Colombier * try various splits until one results in pieces that fit into the 49540ef9009SDavid du Colombier * appropriate fields of the header. look for slashes from right 49640ef9009SDavid du Colombier * to left, in the hopes of putting the largest part of the name into 49740ef9009SDavid du Colombier * hp->prefix, which is larger than hp->name. 49840ef9009SDavid du Colombier */ 49940ef9009SDavid du Colombier sl = strrchr(name, '/'); 50040ef9009SDavid du Colombier while (sl != nil) { 50140ef9009SDavid du Colombier pfxlen = sl - name; 5020fb3f58eSDavid du Colombier if (pfxlen <= sizeof hp->prefix && namlen-1 - pfxlen <= Namsiz) 50340ef9009SDavid du Colombier break; 50440ef9009SDavid du Colombier osl = sl; 50540ef9009SDavid du Colombier *osl = '\0'; 50640ef9009SDavid du Colombier sl = strrchr(name, '/'); 50740ef9009SDavid du Colombier *osl = '/'; 50840ef9009SDavid du Colombier } 50940ef9009SDavid du Colombier if (sl == nil) { 5100fb3f58eSDavid du Colombier fprint(2, "%s: name can't be split to fit tar header: %s\n", 5110fb3f58eSDavid du Colombier argv0, name); 51240ef9009SDavid du Colombier return -1; 51340ef9009SDavid du Colombier } 51440ef9009SDavid du Colombier *sl = '\0'; 51540ef9009SDavid du Colombier strncpy(hp->prefix, name, sizeof hp->prefix); 5160fb3f58eSDavid du Colombier *sl++ = '/'; 5170fb3f58eSDavid du Colombier strncpy(hp->name, sl, sizeof hp->name); 5180fb3f58eSDavid du Colombier if (slname) 5190fb3f58eSDavid du Colombier s_free(slname); 52040ef9009SDavid du Colombier return 0; 52140ef9009SDavid du Colombier } 52240ef9009SDavid du Colombier 5230fb3f58eSDavid du Colombier static int 5240fb3f58eSDavid du Colombier mkhdr(Hdr *hp, Dir *dir, char *file) 5253e12c5d1SDavid du Colombier { 5260fb3f58eSDavid du Colombier /* 5270fb3f58eSDavid du Colombier * these fields run together, so we format them in order and don't use 5280fb3f58eSDavid du Colombier * snprint. 5290fb3f58eSDavid du Colombier */ 5300fb3f58eSDavid du Colombier sprint(hp->mode, "%6lo ", dir->mode & 0777); 5310fb3f58eSDavid du Colombier sprint(hp->uid, "%6o ", aruid); 5320fb3f58eSDavid du Colombier sprint(hp->gid, "%6o ", argid); 5330fb3f58eSDavid du Colombier /* 5340fb3f58eSDavid du Colombier * files > 2 bytes can't be described 5350fb3f58eSDavid du Colombier * (unless we resort to xustar or exustar formats). 5360fb3f58eSDavid du Colombier */ 5370fb3f58eSDavid du Colombier if (dir->length >= (Off)1<<33) { 5380fb3f58eSDavid du Colombier fprint(2, "%s: %s: too large for tar header format\n", 5390fb3f58eSDavid du Colombier argv0, file); 5400fb3f58eSDavid du Colombier return -1; 5410fb3f58eSDavid du Colombier } 5420fb3f58eSDavid du Colombier sprint(hp->size, "%11lluo ", dir->length); 5430fb3f58eSDavid du Colombier sprint(hp->mtime, "%11luo ", dir->mtime); 5440fb3f58eSDavid du Colombier hp->linkflag = (dir->mode&DMDIR? LF_DIR: LF_PLAIN1); 5450fb3f58eSDavid du Colombier putfullname(hp, file); 5460fb3f58eSDavid du Colombier if (posix) { 5470fb3f58eSDavid du Colombier strncpy(hp->magic, "ustar", sizeof hp->magic); 5480fb3f58eSDavid du Colombier strncpy(hp->version, "00", sizeof hp->version); 5490fb3f58eSDavid du Colombier strncpy(hp->uname, dir->uid, sizeof hp->uname); 5500fb3f58eSDavid du Colombier strncpy(hp->gname, dir->gid, sizeof hp->gname); 5510fb3f58eSDavid du Colombier } 5520fb3f58eSDavid du Colombier sprint(hp->chksum, "%6luo", chksum(hp)); 5530fb3f58eSDavid du Colombier return 0; 5543e12c5d1SDavid du Colombier } 5553e12c5d1SDavid du Colombier 5560fb3f58eSDavid du Colombier static void addtoar(int ar, char *file, char *shortf); 5573e12c5d1SDavid du Colombier 5580fb3f58eSDavid du Colombier static void 5590fb3f58eSDavid du Colombier addtreetoar(int ar, char *file, char *shortf, int fd) 5603e12c5d1SDavid du Colombier { 5610fb3f58eSDavid du Colombier int n; 5620fb3f58eSDavid du Colombier Dir *dent, *dirents; 5630fb3f58eSDavid du Colombier String *name = s_new(); 5643e12c5d1SDavid du Colombier 5650fb3f58eSDavid du Colombier n = dirreadall(fd, &dirents); 5660fb3f58eSDavid du Colombier close(fd); 5670fb3f58eSDavid du Colombier if (n == 0) 5683e12c5d1SDavid du Colombier return; 5690fb3f58eSDavid du Colombier 5700fb3f58eSDavid du Colombier if (chdir(shortf) < 0) 5710fb3f58eSDavid du Colombier sysfatal("chdir %s: %r", file); 5720fb3f58eSDavid du Colombier if (DEBUG) 5730fb3f58eSDavid du Colombier fprint(2, "chdir %s\t# %s\n", shortf, file); 5740fb3f58eSDavid du Colombier 5750fb3f58eSDavid du Colombier for (dent = dirents; dent < dirents + n; dent++) { 5760fb3f58eSDavid du Colombier s_reset(name); 5770fb3f58eSDavid du Colombier s_append(name, file); 5780fb3f58eSDavid du Colombier s_append(name, "/"); 5790fb3f58eSDavid du Colombier s_append(name, dent->name); 5800fb3f58eSDavid du Colombier addtoar(ar, s_to_c(name), dent->name); 5819a747e4fSDavid du Colombier } 5820fb3f58eSDavid du Colombier s_free(name); 5830fb3f58eSDavid du Colombier free(dirents); 5840fb3f58eSDavid du Colombier 5850fb3f58eSDavid du Colombier if (chdir("..") < 0) 5860fb3f58eSDavid du Colombier sysfatal("chdir %s/..: %r", file); 5870fb3f58eSDavid du Colombier if (DEBUG) 5880fb3f58eSDavid du Colombier fprint(2, "chdir ..\n"); 5893e12c5d1SDavid du Colombier } 5903e12c5d1SDavid du Colombier 5910fb3f58eSDavid du Colombier static void 5920fb3f58eSDavid du Colombier addtoar(int ar, char *file, char *shortf) 5933e12c5d1SDavid du Colombier { 5940fb3f58eSDavid du Colombier int n, fd, isdir; 5950fb3f58eSDavid du Colombier long bytes; 5960fb3f58eSDavid du Colombier ulong blksleft, blksread; 5970fb3f58eSDavid du Colombier Hdr *hbp; 5980fb3f58eSDavid du Colombier Dir *dir; 5993e12c5d1SDavid du Colombier 6000fb3f58eSDavid du Colombier fd = open(shortf, OREAD); 6010fb3f58eSDavid du Colombier if (fd < 0) { 6020fb3f58eSDavid du Colombier fprint(2, "%s: can't open %s: %r\n", argv0, file); 6030fb3f58eSDavid du Colombier return; 6040fb3f58eSDavid du Colombier } 6050fb3f58eSDavid du Colombier dir = dirfstat(fd); 6060fb3f58eSDavid du Colombier if (dir == nil) 6070fb3f58eSDavid du Colombier sysfatal("can't fstat %s: %r", file); 6080fb3f58eSDavid du Colombier 6090fb3f58eSDavid du Colombier hbp = getblkz(ar); 6100fb3f58eSDavid du Colombier isdir = !!(dir->qid.type&QTDIR); 6110fb3f58eSDavid du Colombier if (mkhdr(hbp, dir, file) < 0) { 6120fb3f58eSDavid du Colombier putbackblk(ar); 6130fb3f58eSDavid du Colombier free(dir); 6140fb3f58eSDavid du Colombier close(fd); 6150fb3f58eSDavid du Colombier return; 6160fb3f58eSDavid du Colombier } 6170fb3f58eSDavid du Colombier putblk(ar); 6180fb3f58eSDavid du Colombier 6190fb3f58eSDavid du Colombier blksleft = BYTES2TBLKS(dir->length); 6200fb3f58eSDavid du Colombier free(dir); 6210fb3f58eSDavid du Colombier 6220fb3f58eSDavid du Colombier if (isdir) 6230fb3f58eSDavid du Colombier addtreetoar(ar, file, shortf, fd); 6240fb3f58eSDavid du Colombier else { 6250fb3f58eSDavid du Colombier for (; blksleft > 0; blksleft -= blksread) { 6260fb3f58eSDavid du Colombier hbp = getblke(ar); 6270fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 6280fb3f58eSDavid du Colombier bytes = blksread * Tblock; 6290fb3f58eSDavid du Colombier n = readn(fd, hbp->data, bytes); 6300fb3f58eSDavid du Colombier if (n < 0) 6310fb3f58eSDavid du Colombier sysfatal("error reading %s: %r", file); 6320fb3f58eSDavid du Colombier /* 6330fb3f58eSDavid du Colombier * ignore EOF. zero any partial block to aid 6340fb3f58eSDavid du Colombier * compression and emergency recovery of data. 6350fb3f58eSDavid du Colombier */ 6360fb3f58eSDavid du Colombier if (n < Tblock) 6370fb3f58eSDavid du Colombier memset(hbp->data + n, 0, bytes - n); 6380fb3f58eSDavid du Colombier putblkmany(ar, blksread); 6390fb3f58eSDavid du Colombier } 6400fb3f58eSDavid du Colombier close(fd); 6410fb3f58eSDavid du Colombier if (verbose) 6420fb3f58eSDavid du Colombier fprint(2, "%s\n", file); 6430fb3f58eSDavid du Colombier } 6440fb3f58eSDavid du Colombier } 6450fb3f58eSDavid du Colombier 6460fb3f58eSDavid du Colombier static char * 6470fb3f58eSDavid du Colombier replace(char **argv) 6480fb3f58eSDavid du Colombier { 6490fb3f58eSDavid du Colombier int i, ar; 6500fb3f58eSDavid du Colombier ulong blksleft, blksread; 6510fb3f58eSDavid du Colombier Off bytes; 6520fb3f58eSDavid du Colombier Hdr *hp; 6530fb3f58eSDavid du Colombier Compress *comp = nil; 6540fb3f58eSDavid du Colombier Pushstate ps; 6550fb3f58eSDavid du Colombier 6560fb3f58eSDavid du Colombier if (usefile && docreate) { 6570fb3f58eSDavid du Colombier ar = create(usefile, OWRITE, 0666); 6580fb3f58eSDavid du Colombier if (docompress) 6590fb3f58eSDavid du Colombier comp = compmethod(usefile); 6600fb3f58eSDavid du Colombier } else if (usefile) 6610fb3f58eSDavid du Colombier ar = open(usefile, ORDWR); 6620fb3f58eSDavid du Colombier else 6630fb3f58eSDavid du Colombier ar = Stdout; 6640fb3f58eSDavid du Colombier if (comp) 6650fb3f58eSDavid du Colombier ar = push(ar, comp->comp, Output, &ps); 6660fb3f58eSDavid du Colombier if (ar < 0) 6670fb3f58eSDavid du Colombier sysfatal("can't open archive %s: %r", usefile); 6680fb3f58eSDavid du Colombier 6690fb3f58eSDavid du Colombier if (usefile && !docreate) { 6700fb3f58eSDavid du Colombier /* skip quickly to the end */ 6710fb3f58eSDavid du Colombier while ((hp = readhdr(ar)) != nil) { 6720fb3f58eSDavid du Colombier bytes = strtoull(hp->size, nil, 8); 6730fb3f58eSDavid du Colombier if(isdir(hp)) 6740fb3f58eSDavid du Colombier bytes = 0; 6750fb3f58eSDavid du Colombier for (blksleft = BYTES2TBLKS(bytes); 6760fb3f58eSDavid du Colombier blksleft > 0 && getblkrd(ar) != nil; 6770fb3f58eSDavid du Colombier blksleft -= blksread) { 6780fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 6790fb3f58eSDavid du Colombier putreadblks(ar, blksread); 6800fb3f58eSDavid du Colombier } 6810fb3f58eSDavid du Colombier } 6820fb3f58eSDavid du Colombier /* 6830fb3f58eSDavid du Colombier * we have just read the end-of-archive Tblock. 6840fb3f58eSDavid du Colombier * now seek back over the (big) archive block containing it, 6850fb3f58eSDavid du Colombier * and back up curblk ptr over end-of-archive Tblock in memory. 6860fb3f58eSDavid du Colombier */ 6870fb3f58eSDavid du Colombier if (seek(ar, -Tblock*nblock, 1) < 0) 6880fb3f58eSDavid du Colombier sysfatal("can't seek back over end-of-archive: %r"); 6890fb3f58eSDavid du Colombier curblk--; 6900fb3f58eSDavid du Colombier } 6910fb3f58eSDavid du Colombier 6920fb3f58eSDavid du Colombier for (i = 0; argv[i] != nil; i++) 6930fb3f58eSDavid du Colombier addtoar(ar, argv[i], argv[i]); 6940fb3f58eSDavid du Colombier 6950fb3f58eSDavid du Colombier /* write end-of-archive marker */ 6960fb3f58eSDavid du Colombier getblkz(ar); 6970fb3f58eSDavid du Colombier putblk(ar); 6980fb3f58eSDavid du Colombier getblkz(ar); 6990fb3f58eSDavid du Colombier putlastblk(ar); 7000fb3f58eSDavid du Colombier 7010fb3f58eSDavid du Colombier if (comp) 7020fb3f58eSDavid du Colombier return pushclose(&ps); 7030fb3f58eSDavid du Colombier if (ar > Stderr) 7040fb3f58eSDavid du Colombier close(ar); 7050fb3f58eSDavid du Colombier return nil; 7060fb3f58eSDavid du Colombier } 7070fb3f58eSDavid du Colombier 7080fb3f58eSDavid du Colombier /* 7090fb3f58eSDavid du Colombier * tar [xt] 7100fb3f58eSDavid du Colombier */ 7110fb3f58eSDavid du Colombier 7120fb3f58eSDavid du Colombier /* is pfx a file-name prefix of name? */ 7130fb3f58eSDavid du Colombier static int 7140fb3f58eSDavid du Colombier prefix(char *name, char *pfx) 7150fb3f58eSDavid du Colombier { 7160fb3f58eSDavid du Colombier int pfxlen = strlen(pfx); 7170fb3f58eSDavid du Colombier char clpfx[Maxname+1]; 7180fb3f58eSDavid du Colombier 7190fb3f58eSDavid du Colombier if (pfxlen > Maxname) 7200fb3f58eSDavid du Colombier return 0; 7210fb3f58eSDavid du Colombier strcpy(clpfx, pfx); 7220fb3f58eSDavid du Colombier cleanname(clpfx); 7230fb3f58eSDavid du Colombier return strncmp(pfx, name, pfxlen) == 0 && 7240fb3f58eSDavid du Colombier (name[pfxlen] == '\0' || name[pfxlen] == '/'); 7250fb3f58eSDavid du Colombier } 7260fb3f58eSDavid du Colombier 7270fb3f58eSDavid du Colombier static int 7280fb3f58eSDavid du Colombier match(char *name, char **argv) 7290fb3f58eSDavid du Colombier { 7300fb3f58eSDavid du Colombier int i; 7310fb3f58eSDavid du Colombier char clname[Maxname+1]; 7320fb3f58eSDavid du Colombier 7330fb3f58eSDavid du Colombier if (argv[0] == nil) 7340fb3f58eSDavid du Colombier return 1; 7350fb3f58eSDavid du Colombier strcpy(clname, name); 7360fb3f58eSDavid du Colombier cleanname(clname); 7370fb3f58eSDavid du Colombier for (i = 0; argv[i] != nil; i++) 7380fb3f58eSDavid du Colombier if (prefix(clname, argv[i])) 7390fb3f58eSDavid du Colombier return 1; 7400fb3f58eSDavid du Colombier return 0; 7410fb3f58eSDavid du Colombier } 7420fb3f58eSDavid du Colombier 7430fb3f58eSDavid du Colombier static int 7440fb3f58eSDavid du Colombier makedir(char *s) 7450fb3f58eSDavid du Colombier { 7460fb3f58eSDavid du Colombier int f; 7470fb3f58eSDavid du Colombier 7480fb3f58eSDavid du Colombier if (access(s, AEXIST) == 0) 7490fb3f58eSDavid du Colombier return -1; 7500fb3f58eSDavid du Colombier f = create(s, OREAD, DMDIR | 0777); 7510fb3f58eSDavid du Colombier if (f >= 0) 7520fb3f58eSDavid du Colombier close(f); 7530fb3f58eSDavid du Colombier return f; 7540fb3f58eSDavid du Colombier } 7550fb3f58eSDavid du Colombier 7560fb3f58eSDavid du Colombier static void 7570fb3f58eSDavid du Colombier mkpdirs(char *s) 7580fb3f58eSDavid du Colombier { 7590fb3f58eSDavid du Colombier int done = 0; 7600fb3f58eSDavid du Colombier char *p = s; 7610fb3f58eSDavid du Colombier 7620fb3f58eSDavid du Colombier while (!done && (p = strchr(p + 1, '/')) != nil) { 7630fb3f58eSDavid du Colombier *p = '\0'; 7640fb3f58eSDavid du Colombier done = (access(s, AEXIST) < 0 && makedir(s) < 0); 7650fb3f58eSDavid du Colombier *p = '/'; 7660fb3f58eSDavid du Colombier } 7670fb3f58eSDavid du Colombier } 7680fb3f58eSDavid du Colombier 7690fb3f58eSDavid du Colombier /* copy a file from the archive into the filesystem */ 770*23605fa3SDavid du Colombier /* fname is result of name(), so has two extra bytes at beginning */ 7710fb3f58eSDavid du Colombier static void 7720fb3f58eSDavid du Colombier extract1(int ar, Hdr *hp, char *fname) 7730fb3f58eSDavid du Colombier { 7740fb3f58eSDavid du Colombier int wrbytes, fd = -1, dir = 0; 7750fb3f58eSDavid du Colombier long mtime = strtol(hp->mtime, nil, 8); 7760fb3f58eSDavid du Colombier ulong mode = strtoul(hp->mode, nil, 8) & 0777; 7770fb3f58eSDavid du Colombier Off bytes = strtoll(hp->size, nil, 8); 7780fb3f58eSDavid du Colombier ulong blksread, blksleft = BYTES2TBLKS(bytes); 7790fb3f58eSDavid du Colombier Hdr *hbp; 7800fb3f58eSDavid du Colombier 7810fb3f58eSDavid du Colombier if (isdir(hp)) { 7820fb3f58eSDavid du Colombier mode |= DMDIR|0700; 7830fb3f58eSDavid du Colombier blksleft = 0; 7840fb3f58eSDavid du Colombier dir = 1; 7850fb3f58eSDavid du Colombier } 7860fb3f58eSDavid du Colombier switch (hp->linkflag) { 78740ef9009SDavid du Colombier case LF_LINK: 78840ef9009SDavid du Colombier case LF_SYMLINK1: 78940ef9009SDavid du Colombier case LF_SYMLINK2: 79040ef9009SDavid du Colombier case LF_FIFO: 7910fb3f58eSDavid du Colombier blksleft = 0; 7923e12c5d1SDavid du Colombier break; 7933e12c5d1SDavid du Colombier } 794*23605fa3SDavid du Colombier if (relative) { 795*23605fa3SDavid du Colombier if(fname[0] == '/') 796*23605fa3SDavid du Colombier *--fname = '.'; 797*23605fa3SDavid du Colombier else if(fname[0] == '#'){ 798*23605fa3SDavid du Colombier *--fname = '/'; 799*23605fa3SDavid du Colombier *--fname = '.'; 800*23605fa3SDavid du Colombier } 801*23605fa3SDavid du Colombier } 8020fb3f58eSDavid du Colombier if (verb == Xtract) { 8030fb3f58eSDavid du Colombier cleanname(fname); 8040fb3f58eSDavid du Colombier switch (hp->linkflag) { 8050fb3f58eSDavid du Colombier case LF_LINK: 8060fb3f58eSDavid du Colombier case LF_SYMLINK1: 8070fb3f58eSDavid du Colombier case LF_SYMLINK2: 8080fb3f58eSDavid du Colombier fprint(2, "%s: can't make (sym)link %s\n", 8090fb3f58eSDavid du Colombier argv0, fname); 8103e12c5d1SDavid du Colombier break; 8110fb3f58eSDavid du Colombier case LF_FIFO: 8120fb3f58eSDavid du Colombier fprint(2, "%s: can't make fifo %s\n", argv0, fname); 8130fb3f58eSDavid du Colombier break; 8140fb3f58eSDavid du Colombier default: 8150fb3f58eSDavid du Colombier if (!keepexisting || access(fname, AEXIST) < 0) { 8160fb3f58eSDavid du Colombier int rw = (dir? OREAD: OWRITE); 8173e12c5d1SDavid du Colombier 8180fb3f58eSDavid du Colombier fd = create(fname, rw, mode); 8190fb3f58eSDavid du Colombier if (fd < 0) { 8200fb3f58eSDavid du Colombier mkpdirs(fname); 8210fb3f58eSDavid du Colombier fd = create(fname, rw, mode); 8223e12c5d1SDavid du Colombier } 8230fb3f58eSDavid du Colombier if (fd < 0 && 8240fb3f58eSDavid du Colombier (!dir || access(fname, AEXIST) < 0)) 8250fb3f58eSDavid du Colombier fprint(2, "%s: can't create %s: %r\n", 8260fb3f58eSDavid du Colombier argv0, fname); 8273e12c5d1SDavid du Colombier } 8280fb3f58eSDavid du Colombier if (fd >= 0 && verbose) 8290fb3f58eSDavid du Colombier fprint(2, "%s\n", fname); 8300fb3f58eSDavid du Colombier break; 8310fb3f58eSDavid du Colombier } 8320fb3f58eSDavid du Colombier } else if (verbose) { 8330fb3f58eSDavid du Colombier char *cp = ctime(mtime); 8343e12c5d1SDavid du Colombier 8350fb3f58eSDavid du Colombier print("%M %8lld %-12.12s %-4.4s %s\n", 8360fb3f58eSDavid du Colombier mode, bytes, cp+4, cp+24, fname); 8377dd7cddfSDavid du Colombier } else 8380fb3f58eSDavid du Colombier print("%s\n", fname); 8393e12c5d1SDavid du Colombier 8400fb3f58eSDavid du Colombier for (; blksleft > 0; blksleft -= blksread) { 8410fb3f58eSDavid du Colombier hbp = getblkrd(ar); 8420fb3f58eSDavid du Colombier if (hbp == nil) 8430fb3f58eSDavid du Colombier sysfatal("unexpected EOF on archive extracting %s", 8440fb3f58eSDavid du Colombier fname); 8450fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 8460fb3f58eSDavid du Colombier wrbytes = Tblock*blksread; 8470fb3f58eSDavid du Colombier if(wrbytes > bytes) 8480fb3f58eSDavid du Colombier wrbytes = bytes; 8490fb3f58eSDavid du Colombier if (fd >= 0 && write(fd, hbp->data, wrbytes) != wrbytes) 8500fb3f58eSDavid du Colombier sysfatal("write error on %s: %r", fname); 8510fb3f58eSDavid du Colombier putreadblks(ar, blksread); 8520fb3f58eSDavid du Colombier bytes -= wrbytes; 85340ef9009SDavid du Colombier } 8540fb3f58eSDavid du Colombier if (fd >= 0) { 8553e12c5d1SDavid du Colombier /* 8560fb3f58eSDavid du Colombier * directories should be wstated after we're done 8570fb3f58eSDavid du Colombier * creating files in them. 8583e12c5d1SDavid du Colombier */ 8590fb3f58eSDavid du Colombier if (settime) { 8600fb3f58eSDavid du Colombier Dir nd; 8610fb3f58eSDavid du Colombier 8620fb3f58eSDavid du Colombier nulldir(&nd); 8630fb3f58eSDavid du Colombier nd.mtime = mtime; 8640fb3f58eSDavid du Colombier if (isustar(hp)) 8650fb3f58eSDavid du Colombier nd.gid = hp->gname; 8660fb3f58eSDavid du Colombier dirfwstat(fd, &nd); 8670fb3f58eSDavid du Colombier } 8680fb3f58eSDavid du Colombier close(fd); 8690fb3f58eSDavid du Colombier } 8700fb3f58eSDavid du Colombier } 8710fb3f58eSDavid du Colombier 8720fb3f58eSDavid du Colombier static void 8730fb3f58eSDavid du Colombier skip(int ar, Hdr *hp, char *fname) 8743e12c5d1SDavid du Colombier { 8750fb3f58eSDavid du Colombier Off bytes; 8760fb3f58eSDavid du Colombier ulong blksleft, blksread; 8770fb3f58eSDavid du Colombier Hdr *hbp; 8780fb3f58eSDavid du Colombier 8790fb3f58eSDavid du Colombier if (isdir(hp)) 8800fb3f58eSDavid du Colombier return; 8810fb3f58eSDavid du Colombier bytes = strtoull(hp->size, nil, 8); 8820fb3f58eSDavid du Colombier blksleft = BYTES2TBLKS(bytes); 8830fb3f58eSDavid du Colombier for (; blksleft > 0; blksleft -= blksread) { 8840fb3f58eSDavid du Colombier hbp = getblkrd(ar); 8850fb3f58eSDavid du Colombier if (hbp == nil) 8860fb3f58eSDavid du Colombier sysfatal("unexpected EOF on archive extracting %s", 8870fb3f58eSDavid du Colombier fname); 8880fb3f58eSDavid du Colombier blksread = gothowmany(blksleft); 8890fb3f58eSDavid du Colombier putreadblks(ar, blksread); 8900fb3f58eSDavid du Colombier } 8910fb3f58eSDavid du Colombier } 8920fb3f58eSDavid du Colombier 8930fb3f58eSDavid du Colombier static char * 8940fb3f58eSDavid du Colombier extract(char **argv) 8950fb3f58eSDavid du Colombier { 8960fb3f58eSDavid du Colombier int ar; 8970fb3f58eSDavid du Colombier char *longname; 8980fb3f58eSDavid du Colombier Hdr *hp; 8990fb3f58eSDavid du Colombier Compress *comp = nil; 9000fb3f58eSDavid du Colombier Pushstate ps; 9010fb3f58eSDavid du Colombier 9020fb3f58eSDavid du Colombier if (usefile) { 9030fb3f58eSDavid du Colombier ar = open(usefile, OREAD); 9040fb3f58eSDavid du Colombier comp = compmethod(usefile); 9050fb3f58eSDavid du Colombier } else 9060fb3f58eSDavid du Colombier ar = Stdin; 9070fb3f58eSDavid du Colombier if (comp) 9080fb3f58eSDavid du Colombier ar = push(ar, comp->decomp, Input, &ps); 9090fb3f58eSDavid du Colombier if (ar < 0) 9100fb3f58eSDavid du Colombier sysfatal("can't open archive %s: %r", usefile); 9110fb3f58eSDavid du Colombier 9120fb3f58eSDavid du Colombier while ((hp = readhdr(ar)) != nil) { 9130fb3f58eSDavid du Colombier longname = name(hp); 9140fb3f58eSDavid du Colombier if (match(longname, argv)) 9150fb3f58eSDavid du Colombier extract1(ar, hp, longname); 9160fb3f58eSDavid du Colombier else 9170fb3f58eSDavid du Colombier skip(ar, hp, longname); 9180fb3f58eSDavid du Colombier } 9190fb3f58eSDavid du Colombier 9200fb3f58eSDavid du Colombier if (comp) 9210fb3f58eSDavid du Colombier return pushclose(&ps); 9220fb3f58eSDavid du Colombier if (ar > Stderr) 9230fb3f58eSDavid du Colombier close(ar); 9240fb3f58eSDavid du Colombier return nil; 9253e12c5d1SDavid du Colombier } 9263e12c5d1SDavid du Colombier 9273e12c5d1SDavid du Colombier void 9280fb3f58eSDavid du Colombier main(int argc, char *argv[]) 9293e12c5d1SDavid du Colombier { 9300fb3f58eSDavid du Colombier int errflg = 0; 9310fb3f58eSDavid du Colombier char *ret = nil; 9320fb3f58eSDavid du Colombier 9330fb3f58eSDavid du Colombier quotefmtinstall(); 9340fb3f58eSDavid du Colombier fmtinstall('M', dirmodefmt); 9350fb3f58eSDavid du Colombier 9360fb3f58eSDavid du Colombier TARGBEGIN { 9370fb3f58eSDavid du Colombier case 'c': 9380fb3f58eSDavid du Colombier docreate++; 9390fb3f58eSDavid du Colombier verb = Replace; 9400fb3f58eSDavid du Colombier break; 9410fb3f58eSDavid du Colombier case 'f': 9420fb3f58eSDavid du Colombier usefile = EARGF(usage()); 9430fb3f58eSDavid du Colombier break; 9440fb3f58eSDavid du Colombier case 'g': 9450fb3f58eSDavid du Colombier argid = strtoul(EARGF(usage()), 0, 0); 9460fb3f58eSDavid du Colombier break; 9470fb3f58eSDavid du Colombier case 'k': 9480fb3f58eSDavid du Colombier keepexisting++; 9490fb3f58eSDavid du Colombier break; 9500fb3f58eSDavid du Colombier case 'm': /* compatibility */ 9510fb3f58eSDavid du Colombier settime = 0; 9520fb3f58eSDavid du Colombier break; 9530fb3f58eSDavid du Colombier case 'p': 9540fb3f58eSDavid du Colombier posix++; 9550fb3f58eSDavid du Colombier break; 9560fb3f58eSDavid du Colombier case 'P': 9570fb3f58eSDavid du Colombier posix = 0; 9580fb3f58eSDavid du Colombier break; 9590fb3f58eSDavid du Colombier case 'r': 9600fb3f58eSDavid du Colombier verb = Replace; 9610fb3f58eSDavid du Colombier break; 9620fb3f58eSDavid du Colombier case 'R': 963*23605fa3SDavid du Colombier relative = 0; 9640fb3f58eSDavid du Colombier break; 9650fb3f58eSDavid du Colombier case 't': 9660fb3f58eSDavid du Colombier verb = Toc; 9670fb3f58eSDavid du Colombier break; 9680fb3f58eSDavid du Colombier case 'T': 9690fb3f58eSDavid du Colombier settime++; 9700fb3f58eSDavid du Colombier break; 9710fb3f58eSDavid du Colombier case 'u': 9720fb3f58eSDavid du Colombier aruid = strtoul(EARGF(usage()), 0, 0); 9730fb3f58eSDavid du Colombier break; 9740fb3f58eSDavid du Colombier case 'v': 9750fb3f58eSDavid du Colombier verbose++; 9760fb3f58eSDavid du Colombier break; 9770fb3f58eSDavid du Colombier case 'x': 9780fb3f58eSDavid du Colombier verb = Xtract; 9790fb3f58eSDavid du Colombier break; 9800fb3f58eSDavid du Colombier case 'z': 9810fb3f58eSDavid du Colombier docompress++; 9820fb3f58eSDavid du Colombier break; 9830fb3f58eSDavid du Colombier case '-': 9840fb3f58eSDavid du Colombier break; 9850fb3f58eSDavid du Colombier default: 9860fb3f58eSDavid du Colombier fprint(2, "tar: unknown letter %C\n", TARGC()); 9870fb3f58eSDavid du Colombier errflg++; 9880fb3f58eSDavid du Colombier break; 9890fb3f58eSDavid du Colombier } TARGEND 9900fb3f58eSDavid du Colombier 9910fb3f58eSDavid du Colombier if (argc < 0 || errflg) 9920fb3f58eSDavid du Colombier usage(); 9930fb3f58eSDavid du Colombier 9940fb3f58eSDavid du Colombier initblks(); 9950fb3f58eSDavid du Colombier switch (verb) { 9960fb3f58eSDavid du Colombier case Toc: 9970fb3f58eSDavid du Colombier case Xtract: 9980fb3f58eSDavid du Colombier ret = extract(argv); 9990fb3f58eSDavid du Colombier break; 10000fb3f58eSDavid du Colombier case Replace: 10010fb3f58eSDavid du Colombier if (getwd(origdir, sizeof origdir) == nil) 10020fb3f58eSDavid du Colombier strcpy(origdir, "/tmp"); 10030fb3f58eSDavid du Colombier ret = replace(argv); 10040fb3f58eSDavid du Colombier chdir(origdir); /* for profiling */ 10050fb3f58eSDavid du Colombier break; 10060fb3f58eSDavid du Colombier default: 10070fb3f58eSDavid du Colombier usage(); 10080fb3f58eSDavid du Colombier break; 10090fb3f58eSDavid du Colombier } 10100fb3f58eSDavid du Colombier exits(ret); 10113e12c5d1SDavid du Colombier } 1012