0707070000000000011006440044230044230000010000000430040163500002300000002174HISTORY Ugsf Ggsf portable archive interchange change history 08/11/88 fix delta checksum bug involving header <dev,ino> 07/17/88 ----- first pax release ----- 07/15/88 convert to pax 07/11/88 change putops(), addnumop(), addstrop() to *xop*() change end of medium handler 06/22/88 call doupdate() rather than update() emty files are valid archives 06/16/88 move *_header definition from cpio.h to cpio.c 06/01/88 add delta checksum and drop delta verify entries 05/24/88 use Phong's new delta/update interface 05/11/88 add -z for -io 05/10/88 complete libdelta additions for -z and -A options 05/03/88 add `-z file' delta/translate option 03/30/88 add dynamic buffer size 03/29/88 add tar interpretation for cpio file args fix binary format file name output alignment bug 12/22/87 add CPIO_EXTENDED extended field code 12/08/87 recode for updated hash library interface 12/01/87 fix link hash bug that linked unrelated files 10/06/87 add `-e filter' file output filter option 08/18/87 delete verbose obsolete option warning 08/11/87 use standard cpio file types rather than local S_IFMT from sys/stat.h 06/11/87 ----- first release ----- 05/01/87 first code 0707070000000000021006440044230044230000010000000472225247300002400000000445Makefile Ugsf Ggsf /* * pax makefile */ ancestor = 3 DEBUG == 1 SYSTEM == "$(_release_:O=1) $(_version_:O=1)" pax :: RELEASE HISTORY pax.1 pax.1.posix \ pax.h bio.c convert.c copy.c pax.c \ delta.c file.c format.c misc.c \ -lodelta -lx :INSTALLDIR: cpio tar cpio :: cpio.sh tar :: tar.sh :: stubs.c 0707070000000000031006440044230044230000010000000470261773200002300000005455RELEASE Ugsf Ggsf portable archive interchange changes since the last release 10/01/90 add mips ar format out of date check 08/11/90 header/trailer alignment now table driven 07/20/90 fix binary output bug that set header size and time to 1 06/01/90 fix misc delta compression bugs 05/01/90 fix DELTA_create bug that did not check COMPRESS 03/28/90 bump regular output block size to 8k 02/11/90 delta changes held off by #if new_delta_format until new libdelta base archive checksums incompatible with previous versions 02/06/90 fix newio() bug that looped when write() returned 0 01/25/90 add proposed posix 1003.1b archive/interchange format tune buffering change -R option for general record support generalize delta id file name -- incompatible with old format prepare for new libdelta and subsequent incompatibilities 12/01/89 add self-delta support, delete -A option (one algorithm with versions) 11/18/89 fix dir mode restoration bug 11/11/89 fix delta update with older file 10/31/89 add portarch and randarch readonly object formats 10/11/89 allow -f with -rwz 10/01/89 add -Bmaxblocks and -C to match latest tar/cpio extensions 08/22/89 align read() buffers to IOALIGN 07/27/89 add EFBIG & EDQUOT checks to newio() 07/04/89 fix cpio binary header mtime and size swab bug fix -rw bug that makes it work now! redo file post processing restoration 05/11/89 handle GNU-tar USTAR format botch missing intermediate directories inherit mode of nearest ancestor fix names on stdin bug that only did -P incorporate lar changes from David Muir expand -R arg syntax handle ansi/ibm D,F,S,U,V formats on output 03/28/89 fix bget() buffer boundary error for reads < buffersize 03/01/89 ignore DELTA_PASS file set*() calls 02/22/89 fix bread bug for 0 count and bget memcpy overlap 01/18/89 fix copyout() rfd<0 delta bug that generated multiple entries 01/11/89 fix dirsize!=0 bug; change ftwalk XDEV implementation 12/11/88 fix symlink->nowhere bug 11/22/88 add vmsbackup readonly format; fix lseek validity checks 11/17/88 add -T040 to simulate tape device blocking on input 11/11/88 add s5r4 asc and aschk formats 11/07/88 hard links and deltas don't cross volumes 11/01/88 add -n to read exact file list 10/20/88 add bread(0,...) to skip, bget() and bput() io optimizations 10/11/88 fix delta ops to use pattern args 10/04/88 fix argc bug that botched -rw with file args 10/01/88 for delta update, verify that untouched files match base archive 09/30/88 fix allocate() bug: malloc() -> calloc() 08/23/88 use libx/memsum() for checksum calculations 08/20/88 add `![command]' to end of medium replies 08/18/88 table of contents output to stdout 08/08/88 update for libx/ftwalk() and libx/strls() 07/28/88 streamline ftwalk() interface 07/21/88 finish up -a and -u tar compatibility 07/17/88 add cpio.sh and tar.sh interface scripts 07/15/88 convert to pax 0707070000000000041006440044230044230000010000000472007024000002100000045630bio.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax block io support */ #include "pax.h" #include "FEATURE/mtio" #ifdef _sys_mtio #include <ttyinfo.h> #include <sys/mtio.h> #endif #include "FEATURE/bcopy" #ifdef _lib_bcopy #define memcpy(a,b,c) bcopy(b,a,c) #endif #if DEBUG /* * -RBi input is BLOK file * -RBo output file is BLOK file */ static int blokread(fd, buf, n) int fd; char* buf; int n; { register int i; register int j; char c; static int eof; if (!state.in.blokflag) { state.in.blokflag = 1; eof = 0; if ((i = read(fd, buf, state.in.blok ? 4 : n)) < 4 || !strneq(buf, "\002\014\017\013", 4)) { if (state.in.blok) error(3, "input archive is not a BLOK file"); return(i); } if (i > 4 && lseek(fd, 4L, 0) != 4L) error(3, "cannot seek on input archive BLOK file -- use -RBi"); state.in.blok = 1; } if (state.in.blok) { j = 0; do { if ((i = read(fd, &c, 1)) < 1) return(i < 0 && ++eof == 1 ? 0 : -1); j <<= 7; j |= c & 0177; } while (c & 0200); if (j > 0) { if (j > n) error(2, "blokread: buffer overflow (%d>%d)", j, n); if ((i = read(fd, buf, j)) != j) error(2, "blokread: blocking error"); } else i = 0; } else i = read(fd, buf, n); return(i); } static int blokwrite(fd, buf, n) int fd; char* buf; int n; { register char* s; register int i; register int j; char blk[9]; if (state.out.blok) { s = blk; if (!state.out.blokflag) { state.out.blokflag = 1; *s++ = 002; *s++ = 014; *s++ = 017; *s++ = 013; } i = 0; if (j = (n >> 21) & 0177) { *s++ = j | 0200; i = 1; } if ((j = (n >> 14) & 0177) || i) { *s++ = j | 0200; i = 1; } if ((j = (n >> 7) & 0177) || i) { *s++ = j | 0200; i = 1; } *s++ = n & 0177; j = s - blk; if ((i = write(fd, blk, j)) != j) error(ERROR_SYSTEM|3, "blokwrite: count write error (%d!=%d)", i, j); if (n <= 0) i = n; else if ((i = write(fd, buf, n)) != n) error(ERROR_SYSTEM|3, "blokwrite: data write error (%d!=%d", i, n); } else i = write(fd, buf, n); return(i); } #define read(f,b,n) blokread(f,b,n) #define write(f,b,n) blokwrite(f,b,n) #endif static int bfill(); /* * initialize buffered io */ void binit() { struct stat st; state.buffersize = DEFBUFFER * DEFBLOCKS; if (state.operation == OUT) { if (!state.blocksize) { if (fstat(1, &st)) error(3, "%s: cannot stat", state.file); #if DEBUG if (state.test & 040) st.st_mode = S_IFCHR; #endif if ((st.st_mode & S_IFMT) == S_IFREG) { state.blocksize = format[state.formatout].regular; state.out.unblocked = 1; } else state.blocksize = format[state.formatout].special; state.buffersize = state.blocksize *= BLOCKSIZE; } switch (state.formatout) { case ALAR: case IBMAR: case PAX: if (!state.record.format) state.record.format = state.formatout == PAX ? PAX_RECFORMAT : ALAR_RECFORMAT; if (state.record.size <= 0) switch (state.record.format) { case 'D': case 'U': state.record.size = state.blocksize; break; case 'F': state.record.size = state.formatout == PAX ? PAX_RECSIZE : ALAR_RECSIZE; break; case 'S': case 'V': state.record.size = 0; break; default: error(3, "%s %c record format not supported on output", format[state.formatout].name, state.record.format); } if (state.blocksize < state.record.size) error(3, "block size (%d) must be >= record size (%d)", state.blocksize, state.record.size); if (state.record.size && (state.blocksize / state.record.size) * state.record.size != state.blocksize) error(1, "block size should be a multiple of record size"); break; default: if (state.record.format || state.record.size) error(1, "record format and size ignored for %s format", format[state.formatout].name); break; } } else { if (state.blocksize) state.buffersize = state.blocksize; else state.blocksize = DEFBLOCKS * BLOCKSIZE; if (state.record.size) error(1, "record size automatically determined on archive read"); } if (state.buffersize < state.blocksize) state.buffersize = state.blocksize; state.tmp.buffersize = state.buffersize; if (!(state.tmp.buffer = malloc(state.tmp.buffersize))) error(3, "cannot allocate buffer"); if (state.delta.op) { state.delta.buffersize = state.buffersize; if (!(state.delta.buffer = malloc(state.delta.buffersize)) || !(state.delta.hdr = state.delta.hdrbuf = malloc(state.delta.buffersize))) error(3, "cannot allocate buffer"); } switch (state.operation) { case OUT: if (!(format[state.formatout].flags & IN)) error(3, "%s: archive format not supported on output" , format[state.formatout].name); balloc(&state.out, 2 * state.buffersize); if (!state.append && !state.delta.op) break; /*FALLTHROUGH*/ case IN: if (!(format[state.formatout].flags & OUT)) error(3, "%s: archive format not supported on input" , format[state.formatout].name); balloc(&state.in, 2 * state.buffersize + MAXUNREAD); break; case IN|OUT: break; } #if DEBUG message(-1, "blocksize=%d buffersize=%d recordsize=%d", state.blocksize, state.buffersize, state.record.size); #endif } /* * allocate buffers for buffered io stream */ void balloc(io, n) register struct bio* io; register int n; { if (!(io->buffer = malloc(n))) error(3, "cannot allocate buffer"); io->next = io->last = io->buffer; } /* * buffered char input * at least n chars required, m chars max * if b is 0 then n chars are skipped * if must!=0 then EOF causes query for next input volume file */ int bread(b, n, m, must) register char* b; register long n; long m; int must; { register int c; register char* ob; if (state.in.eof) return(-1); if (m <= 0) return(0); ob = b; if (state.in.blocked) { if (!b) b = state.tmp.buffer; while ((c = read(state.append, b, m)) <= 0) { if (must) newio(state.append, c, 0); else if (state.in.empty) { state.in.eof = 1; return(-1); } else { if (c < 0) state.in.eof = 1; else { state.in.empty = 1; #if DEBUG if (ob) message(-7, "bread(%d@%ld):", c, state.in.count); #endif } return(c); } } state.in.empty = 0; if (must && c < n) return(-1); state.in.count += c; if (state.delta.sum > 0) state.delta.membersum = memsum(ob ? ob : state.tmp.buffer, c, state.delta.membersum); #if DEBUG if (ob) message(-7, "bread(%d@%ld): %-.*s%s", c, state.in.count, c > 32 ? 32 : c, ob, c > 32 ? "..." : ""); #endif return(c); } else { if (n <= 0) n = m; else m = n; for (;;) { if (n > (c = state.in.last - state.in.next)) { if (c > 0) { if (state.delta.sum > 0) state.delta.membersum = memsum(state.in.next, c, state.delta.membersum); if (ob) memcpy(b, state.in.next, c); b += c; n -= c; state.in.count += c; } state.in.next = state.in.last = state.in.buffer + MAXUNREAD; if (!ob && state.delta.sum <= 0) { static int seekok = -1; if (seekok < 0) { struct stat st; seekok = lseek(state.append, 0L, 1) == (state.in.count + state.in.last - state.in.next) && fstat(state.append, &st) != -1 && st.st_size > 0; } if (seekok && (c = n / BUFFERSIZE) && lseek(state.append, (long)(c *= BUFFERSIZE), 1) != -1) { b += c; n -= c; state.in.count += c; #if DEBUG message(-7, "bread: skip(%d@%ld) [%ld]", c, state.in.count); #endif } } if (bfill(must) < 0) { if (ob && (c = (b - ob))) { bunread(ob, c); return(0); } return(-1); } } else { if (state.delta.sum > 0) state.delta.membersum = memsum(state.in.next, n, state.delta.membersum); if (ob) memcpy(b, state.in.next, n); state.in.next += n; state.in.count += n; #if DEBUG if (ob) { n += b - ob; message(-7, "bread(%d@%ld): %-.*s%s", n, state.in.count, n > 32 ? 32 : n, ob, n > 32 ? "..." : ""); } #endif return(m); } } } } /* * fill input buffer at state.in.last * if must!=0 then EOF causes query for next input volume file */ static int bfill(must) int must; { register int c; if (state.in.eof) return(-1); while ((c = read(state.append, state.in.last, state.buffersize)) <= 0) { if (must) newio(state.append, c, 0); else { state.in.eof = 1; return(-1); } } #if DEBUG message(-8, "read(%d): %-.*s%s", c, c > 32 ? 32 : c, state.in.last, c > 32 ? "..." : ""); #endif state.in.eof = 0; state.in.last += c; return(0); } /* * pushback for next bread() */ void bunread(b, n) register char* b; register int n; { state.in.count -= n; if ((state.in.next -= n) < state.in.buffer + MAXUNREAD) { if (state.in.next < state.in.buffer) error(PANIC, "bunread(%d): too much pushback", n); memcpy(state.in.next, b, n); } #if DEBUG message(-7, "bunread(%d@%ld): %-.*s%s", n, state.in.count, n > 32 ? 32 : n, b, n > 32 ? "..." : ""); #endif } /* * bread() n chars and return a pointer to the char buffer */ char* bget(n) register int n; { register char* s; state.in.count += n; s = state.in.next; state.in.next += n; while (state.in.next > state.in.last) { if (state.in.last > state.in.buffer + MAXUNREAD + state.buffersize) { register char* b; register int k; register int m; k = state.in.last - s; m = round(k, IOALIGN) - k; #if DEBUG if (m) message(-8, "bget: buffer alignment offset=%d", m); #endif b = state.in.next = state.in.buffer + MAXUNREAD + m; state.in.last = b + k; m = s - b; while (k > m) { #if DEBUG message(-8, "bget: overlapping memcpy n=%d k=%d m=%d", n, k, m); #endif (void)memcpy(b, s, m); b += m; s += m; k -= m; } (void)memcpy(b, s, k); s = state.in.next; state.in.next += n; } if (bfill(1) < 0) return(0); } #if DEBUG message(-7, "bget(%d@%ld): %-.*s%s", n, state.in.count, n > 32 ? 32 : n, s, n > 32 ? "..." : ""); #endif return(s); } /* * back up input to bsave()'d position and prime output buffer */ void backup() { register long n; register long m; #ifdef MTIOCTOP struct mtop mt; #endif switch (state.formatin) { case ALAR: case IBMAR: case PAX: #ifdef MTIOCTOP mt.mt_op = MTBSF; mt.mt_count = 1; if (!(ioctl(1, MTIOCTOP, &mt))) return; #endif break; default: m = state.in.next - (state.in.buffer + MAXUNREAD); if ((n = state.in.count - m) > state.backup.count) { #if DEBUG message(-1, "backup(): reread %ld", n + m); #endif m = state.backup.last - (state.backup.buffer + MAXUNREAD); if (lseek(1, -(n + m), 1) == -1L) { #ifdef MTIOCTOP mt.mt_op = MTBSR; mt.mt_count = 2; if (ioctl(1, MTIOCTOP, &mt)) break; #else break; #endif } if (read(1, state.in.buffer + MAXUNREAD, m) != m) break; } else m = state.in.last - (state.in.buffer + MAXUNREAD); #if DEBUG message(-1, "backup(): %ld", m); #endif if ((m = lseek(1, -m, 1)) == -1L) { #ifdef MTIOCTOP mt.mt_op = MTBSR; mt.mt_count = 1; if (ioctl(1, MTIOCTOP, &mt)) break; #else break; #endif } if (state.backup.next < state.backup.last) bwrite(state.in.buffer + MAXUNREAD, state.backup.next - (state.backup.buffer + MAXUNREAD)); return; } error(3, "%s: cannot position %s archive for append", state.file, format[state.formatin].name); } /* * flush buffered input */ void bflushin() { state.in.count += state.in.last - state.in.next; state.in.next = state.in.last = state.in.buffer + MAXUNREAD; if (!state.in.eof) { while (read(state.append, state.tmp.buffer, state.buffersize) > 0); state.in.eof = 1; } } /* * flush buffered output */ void bflushout() { register int n; register int c; if (n = state.out.next - state.out.buffer) { state.out.next = state.out.buffer; while ((c = write(1, state.out.next, n)) != n) { if (c <= 0) newio(1, c, n); else { state.out.next += c; n -= c; } } state.out.next = state.out.buffer; } } /* * buffered output */ void bwrite(b, n) register char* b; register int n; { register int c; if (state.maxout && state.out.count >= state.maxout) { bflushout(); newio(1, 0, 0); } state.out.count += n; if (state.out.blocked) { #if DEBUG if (n > 0) {message(-7, "bwrite(%d@%ld): %-.*s...", n, state.out.count + n, n > 32 ? 32 : n, b);} else message(-7, "bwrite(%d@%ld):", n, state.out.count + n); #endif while ((c = write(1, b, n)) != n) { if (n <= 0) { #ifdef MTIOCTOP { struct mtop mt; mt.mt_op = MTWEOF; mt.mt_count = 1; if (ioctl(1, MTIOCTOP, &mt) >= 0) break; } #endif error(3, "cannot write tape EOF marks"); } if (c <= 0) newio(1, c, n); else if ((n -= c) > 0) b += c; else break; } } else { #if DEBUG if (n > 0) {message(-7, "bwrite(%d@%ld): %-.*s...", n, state.out.count + n, n > 32 ? 32 : n, b);} else message(-7, "bwrite(%d@%ld):", n, state.out.count + n); #endif for (;;) { if ((c = state.out.buffer + state.blocksize - state.out.next) <= n) { if (c) { memcpy(state.out.next, b, c); n -= c; b += c; } state.out.next = state.out.buffer; while ((c = write(1, state.out.next, state.blocksize)) != state.blocksize) { if (c <= 0) newio(1, c, n); else { memcpy(state.tmp.buffer, state.out.buffer + c, state.blocksize - c); memcpy(state.out.buffer, state.tmp.buffer, state.blocksize - c); state.out.next = state.out.buffer + state.blocksize - c; break; } } #if DEBUG message(-8, "write(%d): %-.32s...", c, state.out.buffer); #endif } else { memcpy(state.out.next, b, n); state.out.next += n; break; } } } } /* * bwrite() n chars that have been placed in state.out.next */ void bput(n) register int n; { state.out.count += n; #if DEBUG message(-7, "bput(%d@%ld): %-.*s%s", n, state.out.count, n > 32 ? 32 : n, state.out.next, n > 32 ? "..." : ""); #endif if ((state.out.next += n) > state.out.buffer + state.blocksize) { n = (state.out.next - state.out.buffer) - state.blocksize; state.out.count -= n; /* * flush out the buffer and slide over the remains */ bwrite(state.out.next = state.out.buffer + state.blocksize, n); } } static struct stat* devst; /* * find path name in /dev for <devst->st_dev,devst->st_ino> * called by ftwalk() */ /*ARGSUSED*/ static int devname(ftw) register struct FTW* ftw; { if (ftw->info == FTW_F && ftw->statb.st_dev == devst->st_dev && ftw->statb.st_ino == devst->st_ino) { #if DEBUG message(-1, "device name is %s", ftw->path); #endif state.file = strdup(ftw->path); return(1); } return(0); } /* * initilize tty file pointers for interactive prompting */ void interactive() { if (!state.rtty) { if (!(state.rtty = fopen("/dev/tty", "r")) || !(state.wtty = fopen("/dev/tty", "w"))) error(ERROR_SYSTEM|3, "cannot prompt for interactive input"); setbuf(state.rtty, (char*)0); setbuf(state.wtty, (char*)0); } } /* * check for new input or output stream * c is the io count causing the newio() * n is the pending buffered io count */ void newio(fd, c, n) register int fd; int c; int n; { register char* s; register char* rw; char* file; char* io; char* t; int vol; long z; struct stat st; static int locked; static long total; vol = 0; if (fd) { rw = "write"; io = "output"; state.out.offset += state.out.count - n; state.out.count = n; z = state.out.offset + state.out.count; if (state.out.blocked && state.record.file) switch (state.formatout) { case ALAR: case IBMAR: case PAX: if (locked) return; locked = 1; putlabels(state.record.file, "EOV"); locked = 0; vol = 1; break; } } else { rw = "read"; io = "input"; z = state.in.offset + state.in.count; } if (fstat(fd, &st) < 0) error(ERROR_SYSTEM|3, "%s: cannot stat", io); switch (st.st_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: file = 0; break; default: if (fd) switch (c < 0 ? errno : 0) { case 0: #ifdef EFBIG case EFBIG: #endif #ifdef EDQUOT case EDQUOT: #endif file = "file"; break; default: error(ERROR_SYSTEM|3, "%s %s error -- cannot recover", io, rw); break; } else file = "file"; break; } switch (c < 0 ? errno : 0) { case 0: case ENOSPC: case ENXIO: error(1, "end of %s medium", io); break; default: error(ERROR_SYSTEM|1, "%s %s error", io, rw); break; } if (total == z) error(1, "no %s on part %d", io, state.part--); else total = z; if (!file && state.file != definput && state.file != defoutput) { devst = &st; (void)ftwalk("/dev", devname, 0, (int(*)())0); } close(fd); if (file && state.file != definput && state.file != defoutput && strmatch(state.file, "*.+([0-9])") && (s = strrchr(state.file, '.')) && ((c = strtol(++s, (char*)0, 10)) == state.part || c == (state.part - 1))) { if (state.part == 1 && (!(state.file = strdup(state.file)) || !(s = strrchr(state.file, '.')))) error(3, "out of space"); s += strlen(s); while (*--s != '.') { if (*s < '9') { (*s)++; break; } *s = '0'; } if (*s != '.' && creat(state.file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == fd) { state.part++; error(1, "continuing %s %d %s on %s", state.part == state.volume + 1 ? "volume" : "part", state.part, io, state.file); return; } error(ERROR_SYSTEM|1, "%s: cannot create for writing", state.file); } if (file || state.file == definput || state.file == defoutput) { for (;;) { interactive(); putc('\007', state.wtty); fprintf(state.wtty, "Enter part %d %s %s name: ", state.part + 1, io, file ? file : "device"); if (!(s = fgetline(state.rtty, 0))) { putc('\n', state.wtty); finish(2); } if (*s) { if (!file) break; if (fd) { if ((n = creat(s, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) == fd) break; error(ERROR_SYSTEM|1, "%s: cannot creat", s); } else { if ((n = open(s, 0)) == fd) break; error(ERROR_SYSTEM|1, "%s: cannot read", s); } if (n >= 0) close(n); } } state.file = strdup(s); } if (!file) { for (;;) { interactive(); putc('\007', state.wtty); fprintf(state.wtty, eomprompt, state.part + 1); if (!(s = fgetline(state.rtty, 0))) { putc('\n', state.wtty); finish(2); } if (*s == '!') { static char* last; if (*++s) { if (last) free(last); last = strdup(s); } else s = last; if (!s) error(1, "no previous command"); else if (n = system(s)) error(1, "exit status %d", n); } else { file = *s ? s : state.file; if (open(file, fd + state.append) == fd) break; file = strtape(file, &t); if (!*t && open(file, fd + state.append) == fd) break; error(ERROR_SYSTEM|1, "cannot %s %s", rw, *s ? s : state.file); } } if (state.file != file) state.file = strdup(file); } state.part++; if (vol && !locked) { locked = 1; putprologue(); putlabels(state.record.file, "HDR"); locked = 0; } } 0707070000000000051006440044230044230000010000000471771460200002500000006257convert.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax conversion support */ #include "pax.h" /* * convert string to lower case in place */ char* strlower(s) register char* s; { register int c; register char* t; for (t = s; c = *t; t++) if (isupper(c)) *t = tolower(c); return(s); } /* * convert string to upper case in place */ char* strupper(s) register char* s; { register int c; register char* t; for (t = s; c = *t; t++) if (islower(c)) *t = toupper(c); return(s); } /* * convert binary header shorts to long */ long cpio_long(s) register short* s; { union integral u; u.l = 1; if (u.c[0]) { u.s[0] = s[1]; u.s[1] = s[0]; } else { u.s[0] = s[0]; u.s[1] = s[1]; } return(u.l); } /* * convert long to binary header shorts */ void cpio_short(s, n) register short* s; long n; { union integral u; u.l = 1; if (u.c[0]) { u.l = n; s[0] = u.s[1]; s[1] = u.s[0]; } else { u.l = n; s[0] = u.s[0]; s[1] = u.s[1]; } } /* * convert local mode to cpio mode */ int cpio_mode(f) register struct fileinfo* f; { register int type; switch (f->st->st_mode & S_IFMT) { case 0: type = 0; break; case S_IFIFO: type = CPIO_FIFO; break; case S_IFDIR: type = CPIO_DIR; break; case S_IFCHR: type = CPIO_CHR; break; case S_IFBLK: type = CPIO_BLK; break; case S_IFLNK: type = CPIO_LNK; break; case S_IFSOCK: type = CPIO_SOCK; break; default: error(1, "%s: unknown file type %07o -- regular file assumed", f->name, f->st->st_mode & S_IFMT); /*FALLTHROUGH*/ case S_IFREG: type = CPIO_REG; break; } return((f->st->st_mode & ~S_IFMT) | type); } /* * compute tar_header checksum */ int tar_checksum() { register char* p; register int n; p = tar_header.chksum; while (p < &tar_header.chksum[sizeof(tar_header.chksum)]) *p++ = ' '; n = 0; p = tar_block; while (p < &tar_block[TAR_HEADER]) n += *p++; return(n); } /* * compute running s5r4 file content checksum */ long asc_checksum(b, n, sum) register unsigned char* b; int n; register unsigned long sum; { register unsigned char* e; e = b + n; while (b < e) sum += *b++; return(sum); } /* * get label header number */ long getlabnum(p, byte, width, base) register char* p; int byte; int width; int base; { register char* e; register int c; long n; p += byte - 1; c = *(e = p + width); *e = 0; n = strtol(p, (char*)0, base); *e = c; return(n); } /* * get label header string */ char* getlabstr(p, byte, width, s) register char* p; int byte; int width; register char* s; { register char* e; char* v; v = s; p += byte - 1; e = p + width; while (p < e && (*s = *p++) != ' ') s++; *s = 0; return(v); } #if !new_delta_format /* * this is an obsolete version of the libx implementation */ #undef HASHPART #define HASHPART(b,h,c,l,r) (h = ((h = (h << (l)) ^ (h >> (r)) ^ (c)) & (1 << (b)) ? ~h : h) & ((((1 << ((b) - 1)) - 1) << 2) | 3)) #undef HASHLPART #define HASHLPART(h,c) HASHPART(31, h, c, 3, 2) unsigned long memsum(b, n, c) char* b; int n; register unsigned long c; { register unsigned char* p; register unsigned char* e; p = (unsigned char*)b; e = p + n; while (p < e) HASHLPART(c, *p++ + 1); return(c); } #endif 0707070000000000061006440044230044230000010000000472007013200002200000046057copy.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax file copy support */ #include "pax.h" static void recordin(); static void savesetin(); static void recordout(); /* * copy files in from archive */ void copyin() { register struct fileinfo* f; f = &file; while (getprologue()) { while (getheader(f)) { if (selectfile(f)) filein(f); else fileskip(f); gettrailer(f); } getepilogue(); } deltaverify(); } /* * copy a single file out to the archive * called by ftwalk() */ int copyout(ftw) register struct FTW* ftw; { register struct fileinfo* f; register struct deltainfo* d; int fd; int dfd; if (getfile(f = &file, ftw)) { if (selectfile(f) && (!state.verify || verify(f))) { fd = openin(f); d = state.delta.tab ? (struct deltainfo*)hashget(state.delta.tab, f->name) : (struct deltainfo*)0; if (state.delta.op & (COMPRESS|OUT)) { if (d) d->mark = 1; if (fd >= 0 && (!d || f->st->st_mtime != d->mtime)) { if (d) { f->delta.op = DELTA_update; f->st->st_dev = d->dev; f->st->st_ino = d->ino; #if DEBUG message(-2, "delta: delta: file=%s offset=%ld size=%ld", f->name, d->offset, d->size); #endif if (d->size) { if (state.delta.buffersize < d->size) { state.delta.buffersize = round(d->size, state.blocksize); if (state.delta.buffer) free(state.delta.buffer); if (!(state.delta.buffer = malloc(state.delta.buffersize))) error(3, "input files too large to delta"); } dfd = deltafd(d); if (read(dfd, state.delta.buffer, d->size) != d->size) error(3, "%s: base archive read error", f->name); (void)close(dfd); } } else { f->delta.op = DELTA_create; #if DEBUG message(-2, "delta: create: file=%s", f->name); #endif } if (d || (state.delta.op & COMPRESS)) { if (state.tmp.buffersize < f->st->st_size) { state.tmp.buffersize = round(f->st->st_size, state.blocksize); if (state.tmp.buffer) free(state.tmp.buffer); if (!(state.tmp.buffer = malloc(state.tmp.buffersize))) error(3, "input files too large to delta"); } if (f->st->st_size && read(fd, state.tmp.buffer, f->st->st_size) != f->st->st_size) error(ERROR_SYSTEM|3, "%s: read error", f->path); (void)close(fd); fd = dodelta(f, state.delta.buffer, d ? d->size : 0L, state.tmp.buffer); } } else { #if DEBUG message(-2, "delta: verify: file=%s", f->name); #endif f->delta.op = DELTA_verify; f->st->st_size = 0; } } if (fd >= 0) { if (!d || d->mtime != f->st->st_mtime) fileout(f, fd); else if (fd >= 0) close(fd); } } else ftw->status = FTW_SKIP; } return(0); } /* * low level for copyout() * if rfd<0 && st_size>0 then input from bread() */ void fileout(f, rfd) register struct fileinfo* f; int rfd; { register int n; register long c; int err; FILE* rfp; if (f->delta.op == DELTA_verify) { state.selected--; if (rfd >= 0) close(rfd); } else { putheader(f); switch (state.formatout) { case ALAR: case IBMAR: case PAX: if (state.out.blocked) { if (f->st->st_size > 0) { if (state.record.format == 'F' && !state.record.line) { /* * this is faster than recordout() */ state.record.file = f; err = 0; c = f->st->st_size; while (c > 0) { n = c > state.record.size ? state.record.size : c; /* * NOTE: we expect that all but the last * read returns state.record.size * if not the the intermediate short * reads are filled with 0's */ if (!err) { if (rfd >= 0) n = read(rfd, state.tmp.buffer, n); else if (bread(state.tmp.buffer, 0L, (long)n, 1) <= 0) n = -1; } if (n <= 0) { if (n) error(ERROR_SYSTEM|2, "%s: read error", f->path); else error(2, "%s: file size changed", f->path); (void)memset(state.tmp.buffer, 0, state.record.size); err = 1; } else { c -= n; if (n < state.record.size && (c > 0 || state.record.pad)) { (void)memset(state.tmp.buffer + n, 0, state.record.size - n); n = state.record.size; } bwrite(state.tmp.buffer, n); } } state.record.file = 0; if (rfd >= 0) close(rfd); } else if (rfd < 0) recordout(f, (FILE*)0); else if (!(rfp = fdopen(rfd, "r"))) { error(1, "%s: cannot read", f->path); close(rfd); } else { recordout(f, rfp); fclose(rfp); } } break; } /*FALLTHROUGH*/ default: err = 0; c = f->st->st_size; while (c > 0) { n = c > state.buffersize ? state.buffersize : c; if (!err) { if (rfd >= 0) n = read(rfd, state.out.next, n); else if (bread(state.out.next, 0L, (long)n, 1) <= 0) n = -1; } if (n <= 0) { if (n) error(ERROR_SYSTEM|2, "%s: read error", f->path); else error(2, "%s: file size changed", f->path); (void)memset(state.out.next, 0, state.buffersize); err = 1; } else { c -= n; bput(n); } } if (rfd >= 0) close(rfd); break; } puttrailer(f); } if (state.acctime && f->type != S_IFLNK && !(state.delta.op & IN)) settime(f->name, f->st->st_atime, f->st->st_mtime); } /* * fileout() record support */ static void recordout(f, fp) struct fileinfo* f; FILE* fp; { register int c; register char* p; register char* recdat; register char* blkdat; char* rec; char* blk; int span; int count = 0; int partial = 0; int truncated = 0; static char span_out[] = "0132"; static char* pardat; if (!fp) error(3, "cannot handle record output from buffer"); state.record.file = f; f->record.blocks = 0; span = 0; blk = state.tmp.buffer; /* * file loop */ for (;;) { p = blk; switch (state.record.format) { case 'V': p += 4; break; } blkdat = p; /* * block loop */ for (;;) { rec = p; switch (state.record.format) { case 'D': case 'V': p += 4; break; case 'S': p += 5; break; } recdat = p; /* * check for partial record from previous block */ if (partial) { (void)memcpy(recdat, pardat, partial); p += partial; partial = 0; } /* * record loop */ span &= 01; span <<= 1; for (;;) { if (p >= &rec[state.record.size] && state.record.size) { if (state.record.line) { truncated++; while ((c = getc(fp)) != EOF && c != '\n'); } break; } else if (p >= &blk[state.blocksize]) { if (state.record.format == 'S' || state.record.format == 'V') { if (p > recdat) { span |= 01; break; } } else if (partial = p - recdat) { /* * save partial record for next block */ if (!pardat && !(pardat = malloc(state.blocksize))) error(3, "out of space [record pushback buffer]"); (void)memcpy(pardat, recdat, partial); } p = rec; goto eob; } else if ((c = getc(fp)) == EOF) { if (p == recdat) { if (rec == blkdat) goto eof; p = rec; goto eob; } break; } else if (c == '\n' && state.record.line) break; else *p++ = c; } switch (state.record.format) { case 'D': c = recdat[0]; (void)sprintf(rec, "%04d", p - rec); recdat[0] = c; break; case 'F': if (c != EOF || state.record.pad) { (void)memset(p, ' ', state.record.size - (p - rec)); p = rec + state.record.size; } break; case 'S': c = recdat[0]; (void)sprintf(rec, "%c%04d", span_out[span], p - rec); recdat[0] = c; break; case 'U': if (p == recdat) *p++ = ' '; break; case 'V': rec[0] = ((p - rec) >> 8) & 0xff; rec[1] = (p - rec) & 0xff; rec[2] = span; rec[3] = 0; break; } if (state.record.charset && state.formatout == IBMAR) cvtatoe(recdat, recdat, p - recdat); count++; if (p >= &blk[state.blocksize] || state.record.format == 'U') break; } eob: switch (state.record.format) { case 'D': case 'S': if (state.record.pad) { (void)memset(p, '^', state.blocksize - (p - blk)); p = blk + state.blocksize; } break; case 'V': blk[0] = ((p - blk) >> 8) & 0xff; blk[1] = (p - blk) & 0xff; blk[2] = 0; blk[3] = 0; break; } bwrite(blk, p - blk); f->record.blocks++; } eof: state.record.file = 0; if (truncated) error(1, "%s: %d out of %d record%s truncated", f->name, truncated, count, count == 1 ? "" : "s"); } /* * low level for copyin() */ void filein(f) register struct fileinfo* f; { register long c; register int n; register char* s; int wfd; long checksum; struct stat st; if (f->skip || state.list) fileskip(f); else switch (f->delta.op) { case DELTA_create: if (f->delta.base) error(3, "%s: base archive mismatch", f->name); if (state.delta.op & COMPRESS) goto update; goto regular; case DELTA_update: if (!f->delta.base || f->delta.base->mtime >= f->st->st_mtime) error(3, "%s: base archive mismatch", f->name); update: if ((wfd = openout(f)) < 0) fileskip(f); else if (doupdate(f, state.delta.fd, f->name, wfd) < 0) error(ERROR_SYSTEM|2, "%s: delta update error", f->name); break; case DELTA_verify: if (!f->delta.base || f->delta.base->mtime != f->st->st_mtime) error(3, "%s: base archive mismatch", f->name); if ((*state.statf)(f->name, &st)) error(2, "%s: not copied from base archive", f->name); else if (st.st_size != f->delta.base->size || state.modtime && st.st_mtime != f->st->st_mtime) error(1, "%s: changed from base archive", f->name); break; case DELTA_delete: if (!f->delta.base) error(3, "%s: base archive mismatch", f->name); /*FALLTHROUGH*/ default: regular: wfd = openout(f); switch (state.formatin) { case ALAR: case IBMAR: case PAX: recordin(f, wfd); break; #if SAVESET case SAVESET: savesetin(f, wfd); break; #endif default: if (wfd >= 0) { checksum = 0; for (c = f->st->st_size; c > 0; c -= n) { n = (c > state.buffersize) ? state.buffersize : c; if (!(s = bget(n))) { error(ERROR_SYSTEM|2, "%s: read error", f->name); break; } if (write(wfd, s, n) != n) { error(ERROR_SYSTEM|2, "%s: write error", f->name); break; } if (state.formatin == ASCHK) checksum = asc_checksum(s, n, checksum); } close(wfd); setfile(f); if (state.formatin == ASCHK && checksum != f->checksum) error(1, "%s: %s checksum error (0x%08x != 0x%08x)", f->name, format[state.formatin].name, checksum, f->checksum); } else fileskip(f); break; } } listentry(f); } /* * filein() record support */ static void recordin(f, wfd) register struct fileinfo* f; int wfd; { register long n; register long size; int c; int i; int j; int k; int nl; long m; FILE* wfp; if (wfd < 0) wfp = 0; else if (!(wfp = fdopen(wfd, "w"))) error(1, "%s: cannot write", f->name); state.in.empty = 0; nl = state.record.line; size = 0; for (;;) { if (state.in.blocked) n = bread(state.tmp.buffer, 0L, (long)state.buffersize, 0); else if ((m = f->st->st_size - size) <= 0) n = 0; else if (wfp) { if (m > state.buffersize) m = state.buffersize; n = bread(state.tmp.buffer, 0L, m, 1); } else n = bread((char*)0, 0L, m, 1); if (n < 0) break; if (n == 0) { k = 1; state.delta.sum--; while (getlabel(f)) { if (strneq(alar_header, "EOV1", 4)) k = 0; else if (!strneq(alar_header, "EOF", 3) && !strneq(alar_header, "EOV", 3) && !strneq(alar_header, "UTL", 3) && ++n >= 16 && !state.keepgoing) error(3, "%s: %d invalid %s end of file/volume labels detected", f->name, n, format[state.formatin].name); } if (n) error(1, "%s: %d invalid %s end of file/volume labels detected", f->name, n, format[state.formatin].name); if (k) { state.delta.sum++; break; } f->record.section++; f->id = strcpy(state.tmp.buffer, f->id); f->name = strcpy(state.tmp.buffer + ALAR_NAMESIZE + 1, f->name); for (;;) { newio(0, 0, 0); if (getprologue()) { struct fileinfo v; struct stat st; v.st = &st; if (getheader(&v)) { if (streq(f->id, v.id) && streq(f->name, v.name) && f->record.section == v.record.section) { f->id = v.id; f->name = v.name; break; } error(1, "volume containing %s id %s section %d required", f->name, f->id, f->record.section); } state.volume--; } state.part--; } state.delta.sum++; continue; } if (f->record.format == 'V') { if ((k = ((unsigned char*)state.tmp.buffer)[0] << 8 | ((unsigned char*)state.tmp.buffer)[1]) != n) error(3, "%s: invalid %s V format block descriptor [%d!=%d]", f->name, format[state.formatin].name, k, n); i = 4; } else i = 0; while (i < n) { i += state.record.offset; if (state.tmp.buffer[i] == '^') switch (f->record.format) { case 'F': if (state.formatin == IBMAR || state.formatin == PAX) break; for (j = i; j < n && state.tmp.buffer[j] == '^'; j++); if (j < n) break; /*FALLTHROUGH*/ case 'D': case 'S': i = n; continue; } /* * get record size */ switch (f->record.format) { case 'D': if (sscanf(&state.tmp.buffer[i], "%4d", &k) != 1) k = -1; j = i + 4; break; case 'F': if (i + state.record.size > n) k = n - i; else if (state.record.line || state.record.offset) k = state.record.size; else k = n; j = i; break; case 'S': switch (state.tmp.buffer[i]) { case '0': case '3': nl = 1; break; default: nl = 0; break; } if (sscanf(&state.tmp.buffer[i + 1], "%4d", &k) != 1) k = -1; j = i + 5; break; case 'U': k = n; j = i; break; case 'V': nl = !(state.tmp.buffer[i + 2] & 01); k = ((unsigned char*)state.tmp.buffer)[i] << 8 | ((unsigned char*)state.tmp.buffer)[i + 1]; j = i + 4; break; } if (k < 0) { error(2, "invalid %s %c record size", format[state.formatin].name, f->record.format); break; } m = i += k; if (state.record.charset && state.formatin == IBMAR) cvtetoa(&state.tmp.buffer[j], &state.tmp.buffer[j], m - j); if (state.record.line) switch (f->record.format) { case 'F': case 'U': while (--m >= j && state.tmp.buffer[m] == ' '); m++; break; } k = m - j + nl; size += k; if (wfp) { if (nl) { c = state.tmp.buffer[m]; state.tmp.buffer[m] = '\n'; } if (fwrite(&state.tmp.buffer[j], 1, k, wfp) != k) { error(ERROR_SYSTEM|1, "%s: write error", f->name); break; } if (nl) state.tmp.buffer[m] = c; } } } if (f->st->st_size && f->st->st_size != size) error(1, "%s: header size %ld does not match data size %ld", f->name, f->st->st_size, size); f->st->st_size = size; if (wfp) { fclose(wfp); setfile(f); } if (n < 0) error(ERROR_SYSTEM|3, "%s: archive read error", f->name); } #if SAVESET /* * filein() saveset support */ static void savesetin(f, wfd) register struct fileinfo* f; int wfd; { register long c; int i; int j; int k; FILE* wfp; if (wfd < 0) wfp = 0; else if (!(wfp = fdopen(wfd, "w"))) error(1, "%s: cannot write", f->name); j = 0; k = 0; c = 0; while (getsaveset(f, 0)) { /* * this part transcribed from vmsbackup */ i = 0; if (wfp) while ((c + i) < f->st->st_size && i < state.saveset.lastsize) switch (state.saveset.recfmt) { case 1: /* fixed length */ if (j <= 0) j = state.saveset.reclen; fputc(state.saveset.bp[i], wfp); i++; j--; break; case 2: /* variable length */ case 3: /* with fixed control */ if (j <= 0) { j = k = gethalf(BYTE|HALF, &state.saveset.bp[i]); i += 2; if (state.saveset.recfmt == 3) { i += state.saveset.recvfc; j -= state.saveset.recvfc; } } else { if (j == k && state.saveset.recatt == 1) { if (state.saveset.bp[i] == '0') state.saveset.bp[i] = '\n'; else if (state.saveset.bp[i] == '1') state.saveset.bp[i] = '\f'; } fputc(state.saveset.bp[i], wfp); i++; j--; } if (j <= 0) { fputc('\n', wfp); if (i & 1) i++; } break; case 4: /* seq stream */ case 5: /* seq LF stream */ if (j <= 0) j = 512; if (state.saveset.bp[i] == '\n') j = 0; else j--; fputc(state.saveset.bp[i], wfp); i++; break; case 6: /* seq CR stream */ if (state.saveset.bp[i] == '\r') state.saveset.bp[i] = '\n'; fputc(state.saveset.bp[i], wfp); i++; break; default: error(state.keepgoing ? 1 : 3, "%s: invalid %s format data record format=%d", f->name, format[state.formatin].name, state.saveset.recfmt); goto next; } next: c += i; } if (wfp) { fclose(wfp); setfile(f); } } #endif /* * pass data from rfd to wfd */ void filepass(f, rfd, wfd) register struct fileinfo* f; int rfd; int wfd; { register long c; register long n; for (c = f->st->st_size; c > 0; c -= n) { if ((n = read(rfd, state.tmp.buffer, (c > state.buffersize) ? state.buffersize : c)) <= 0) { error(ERROR_SYSTEM|2, "%s: read error", f->name); break; } if (write(wfd, state.tmp.buffer, n) != n) { error(ERROR_SYSTEM|2, "%s: write error", f->name); break; } state.out.count += n; } (void)close(rfd); (void)close(wfd); setfile(f); listentry(f); } /* * skip over archive member f file data */ void fileskip(f) register struct fileinfo* f; { switch (state.formatin) { case ALAR: case IBMAR: case PAX: recordin(f, -1); break; #if SAVESET case SAVESET: savesetin(f, -1); break; #endif default: if (bread((char*)0, 0L, f->st->st_size, 1) < 0) error(ERROR_SYSTEM|2, "%s: skip error", f->name); break; } } /* * single file copyin() and copyout() smashed together * called by ftwalk() */ int copyinout(ftw) struct FTW* ftw; { register struct fileinfo* f; register int rfd; register int wfd; static char path[PATH_MAX]; if (getfile(f = &file, ftw) && selectfile(f)) { (void)strcpy(path, state.pwd); (void)strcpy(path + state.pwdlen, f->name + (*f->name == '/')); f->name = path; if ((wfd = openout(f)) >= 0) { if ((rfd = openin(f)) >= 0) filepass(f, rfd, wfd); else (void)close(wfd); } else if (wfd != -1) listentry(f); } return(0); } /* * compare ft1 and ft2 for ftwalk() sort */ int cmpftw(ft1, ft2) struct FTW* ft1; struct FTW* ft2; { return(strcmp(ft1->name, ft2->name)); } /* * copy files out using copyfile */ void copy(copyfile) register int (*copyfile)(); { register char* s; register int n; if (state.files) n = ftwalk((char*)state.files, copyfile, state.ftwflags|FTW_MULTIPLE, state.exact ? (int(*)())0 : cmpftw); else { n = 0; while (s = fgetline(stdin, 0)) if (n = ftwalk(s, copyfile, state.ftwflags, (int(*)())0)) break; if (n) error(2, "%s: not completely copied", s); } } /* * position archive for appending */ void append() { if (state.update) initdelta(); state.operation = IN; state.formatin = IN_DEFAULT; copyin(); state.append = 0; state.formatout = state.formatin; state.operation = OUT; } 0707070000000000071006440044230044230000010000000467117701400002300000005120cpio.sh Ugsf Ggsf : # Glenn Fowler # AT&T Bell Laboratories # @(#)cpio.sh (ulysses!gsf) 08/11/90 # # cpio -> pax interface script # command=cpio usage=" Usage: $command -o[acvBV] [-C size] [-M mesg] [-O file | >file ] <list $command -i[bcdfkmrtsuvBSV6] [-I file | <file] [pattern ...] $command -p[adlmuvV] directory" case $1 in *i*) mode="-r" ;; *o*) mode="-w" ;; *p*) mode="-rw" ;; *) echo "$command: one of -i, -o, -p must be specified$usage" >&2; exit 1 ;; esac options="" blocksize=1b debug= format=binary list="" logphys=-P d_default="-d" m_default="-m" u_default="-u" r_ok="1" w_ok="1" p_ok="1" while : do case $# in 0) break ;; esac case $1 in --) shift; break ;; -*) for opt in `echo '' $1 | sed -e 's/-//' -e 's/./& /g'` do case $opt in '#') case $debug in "") debug=echo ;; *) debug=args ;; esac ;; [bsS6]) ;; [klvV]) options="$options -$opt" ;; a) r_ok="" options="$options -p" ;; c) format=cpio ;; d) w_ok="" d_default="" ;; f) w_ok="" p_ok="" options="$options -c" ;; i) w_ok="" p_ok="" ;; m) w_ok="" m_default="" ;; o) r_ok="" p_ok="" u_default="" ;; p) r_ok="" w_ok="" ;; r) w_ok="" p_ok="" options="$options -i" ;; t) w_ok="" p_ok="" list="1" ;; u) w_ok="" u_default="" ;; B) blocksize=5k ;; L) logphys=-L ;; [CIMO]) a=`echo '' $1 | sed -e "s/[^$opt]*$opt//"` case $a in "") case $# in 1) echo "$command: option -$opt requires an argument$usage" >&2; exit 1 ;; esac shift a=$1 ;; esac case $opt in C) case $a in *[0-9]) a=${a}c ;; esac blocksize=$a ;; I) w_ok="" p_ok="" options="$options -f '$a'" ;; O) r_ok="" p_ok="" options="$options -f '$a'" ;; M) options="$options -$opt '$a'" ;; esac break ;; *) echo "$command: invalid option -$opt$usage" >&2; exit 1 ;; esac done ;; *) break ;; esac shift done case $mode in -r) case $r_ok in "") echo "$command: options inconsistent with archive read" >&2; exit 1 ;; esac options="$options -b $blocksize" ;; -w) case $w_ok in "") echo "$command: options inconsistent with archive write" >&2; exit 1 ;; esac case $# in 0) ;; *) echo "$command: arguments not expected" >&2; exit 1 ;; esac options="$options -x $format -b $blocksize" ;; -rw) case $p_ok in "") echo "$command: options inconsistent with file pass" >&2; exit 1 ;; esac case $# in 1) ;; *) echo "$command: a single directory argument is expected$usage" >&2; exit 1 ;; esac ;; esac case $list in "1") mode="" d_default="" m_default="" u_default="" ;; esac $debug pax $mode $logphys $options $d_default $m_default $u_default "$@" 0707070000000000101006440044230044230000010000000472226354600002300000026365delta.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax delta archive support */ #include "pax.h" static void deltacopy(); extern int delta(); extern int update(); #if 0 static char* getdelstr(f) register struct fileinfo* f; { register char* s; register char* b = state.delta.hdrbuf; unsigned char c; s = b; do { if (f->st->st_size <= 0) return(0); if (bread(&c, 1L, 1L, 1) <= 0) error(ERROR_SYSTEM|3, "%s: read error", f->path); f->st->st_size--; } while (*s++ = c); return(*b ? b : 0); } #endif /* * get supplemental delta header info */ void getdeltaheader(f) register struct fileinfo* f; { register char* s; f->delta.version = VERSION; f->delta.size = -1; if ((state.delta.op & (COLLECT|IN)) == IN) { if (state.delta.tab && (f->delta.base = (struct deltainfo*)hashget(state.delta.tab, f->path))) f->delta.base->mark = 1; f->delta.op = DELTA_create; #if new_delta_format if (f->st->st_size && (s = getdelstr(f))) { if (f->delta.op = *s++) f->delta.version = *s; while (s = getdelstr(f)) switch (*s++) { case 's': f->delta.size = strtol(s, (char*)0, 16); break; /* * NOTE: ignore unknown ops for future extensions */ } } #else if (f->st->st_size) { char c; f->delta.op = 0; do { if (bread(&c, 1L, 1L, 1) <= 0) error(ERROR_SYSTEM|3, "%s: read error", f->path); f->st->st_size--; if (!f->delta.op) f->delta.op = c; } while (c != '\n'); } #endif } else if (state.delta.op & CONVERT) f->delta.op = DELTA_pass; else f->delta.op = 0; } /* * add delta header string */ void adddelstr(op, s) int op; register char* s; { register char* p = state.delta.hdr; register char* e = state.delta.hdrbuf + state.buffersize - 3; if (p < e) { *p++ = op; while (*s && p < e) *p++ = *s++; *p++ = 0; state.delta.hdr = p; } #if DEBUG if (*s) error(PANIC, "adddelstr('%c',\"%s\") overflow", op, s); #endif } /* * add delta header number */ void adddelnum(op, n) int op; long n; { char buf[17]; (void)sprintf(buf, "%x", n); adddelstr(op, buf); } /* * output supplementary delta header info */ void putdeltaheader(f) register struct fileinfo* f; { register char* s; int n; if (f->delta.op && (n = state.delta.hdr - state.delta.hdrbuf)) { switch (state.formatout) { case IBMAR: if (state.formatout == IBMAR) cvtatoe(state.delta.hdrbuf, state.delta.hdrbuf, n); /*FALLTHROUGH*/ default: bwrite(state.delta.hdrbuf, n); break; } f->st->st_size -= n; f->linknamesize -= n; state.delta.hdr = state.delta.hdrbuf; } } /* * initialize delta tables */ void initdelta() { if (!(state.delta.tab = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_name, "delta", 0))) error(3, "cannot allocate delta table"); } /* * get delta base archive info */ void deltabase() { register char* s; register int fd; int format; int operation; struct stat st; if (!state.delta.base) state.delta.base = "/dev/null"; if ((state.delta.fd = open(state.delta.base, 0)) < 0 || fstat(state.delta.fd, &st)) error(ERROR_SYSTEM|3, "%s: cannot open base archive", state.delta.base); initdelta(); if (st.st_size) { operation = state.operation; state.operation = IN; fd = dup(0); (void)close(0); if (dup(state.delta.fd) != 0) error(ERROR_SYSTEM|3, "%s: cannot dup base archive", state.delta.base); if (lseek(0, 0L, 0) != 0) error(ERROR_SYSTEM|3, "%s: base archive must be seekable", state.delta.base); s = state.file; state.file = state.delta.base; format = state.formatin; state.formatin = IN_DEFAULT; if (state.delta.op) { state.delta.op |= COLLECT; state.delta.sum++; } copyin(); if (state.delta.op) { state.delta.op &= ~COLLECT; state.delta.sum--; } state.formatin = format; state.file = s; (void)close(0); if (dup(fd) != 0) error(3, "cannot dup standard input"); (void)close(fd); state.delta.size = state.in.offset + state.in.count; state.in.offset = 0; state.in.count = 0; #if DEBUG state.in.blokflag = 0; #endif state.in.eof = 0; state.volume = 0; state.operation = operation; } else state.delta.op |= COMPRESS; } /* * return read fd to delta base member d * some formats may require a tmp file copy */ int deltafd(d) register struct deltainfo* d; { register int fd; if ((fd = dup(state.delta.fd)) < 0) error(ERROR_SYSTEM|3, "%s: cannot reopen", state.delta.base); if (lseek(fd, d->offset, 0) < 0) error(ERROR_SYSTEM|3, "%s: base archive seek error", state.delta.base); return(fd); } /* * verify untouched base files */ void deltaverify() { register int wfd; register struct deltainfo* d; HASHPOSITION pos; if (!state.list && (state.delta.op & (COLLECT|IN)) == IN) { #if DEBUG message(-2, "verify untouched base files"); #endif hashscan(state.delta.tab, 0, &pos); while (hashnext(&pos)) { d = (struct deltainfo*)pos.bucket->value; message(-1, "%s: mark=%d", d->info->name, d->mark); if (!d->mark && selectfile(d->info) && (wfd = openout(d->info)) >= 0) { state.entries++; filepass(d->info, deltafd(d), wfd); } } hashdone(&pos); } } /* * update file deltas from archive and output to archive */ void deltapass() { register struct fileinfo* f; register long c; register long n; struct deltainfo* d; char* p; int fd; HASHPOSITION pos; #if DEBUG message(-1, "delta PASS %s", operations()); #endif putprologue(); f = &file; while (getprologue()) { while (getheader(f)) { switch (f->delta.op) { case DELTA_create: if (f->delta.base) error(3, "%s: base archive mismatch", f->name); /*FALLTHROUGH*/ case DELTA_pass: if (validout(f) && selectfile(f)) { if (state.delta.op & COMPRESS) { if (state.tmp.buffersize < f->st->st_size) { state.tmp.buffersize = round(f->st->st_size, state.blocksize); if (state.tmp.buffer) free(state.tmp.buffer); if (!(state.tmp.buffer = malloc(state.tmp.buffersize))) error(3, "input files too large to delta"); } p = state.tmp.buffer; for (c = f->st->st_size; c > 0; c -= state.buffersize) { n = c > state.buffersize ? state.buffersize : c; if (bread(p, n, n, 1) <= 0) { error(ERROR_SYSTEM|2, "%s: read error", f->name); break; } p += n; } goto delta; } f->delta.op = 0; fileout(f, -1); } else fileskip(f); break; case DELTA_delete: if (!f->delta.base) error(3, "%s: base archive mismatch", f->name); break; case DELTA_update: if (!f->delta.base || f->delta.base->mtime >= f->st->st_mtime) error(3, "%s: base archive mismatch", f->name); if (validout(f) && selectfile(f)) { if ((fd = creat(state.tmp.file, S_IRUSR|S_IRGRP|S_IROTH)) < 0) error(ERROR_SYSTEM|3, "%s: cannot create delta temporary file", state.tmp.file); if (doupdate(f, state.delta.fd, state.tmp.file, fd) < 0) error(ERROR_SYSTEM|2, "%s: delta update error", f->name); if ((fd = open(state.tmp.file, 0)) < 0) error(3, "%s: cannot read delta temporary file", state.tmp.file); if (remove(state.tmp.file)) error(1, "%s: cannot remove delta temporary file", state.tmp.file); if (state.delta.op & COMPRESS) { if (state.tmp.buffersize < f->st->st_size) { state.tmp.buffersize = round(f->st->st_size, state.blocksize); if (state.tmp.buffer) free(state.tmp.buffer); if (!(state.tmp.buffer = malloc(state.tmp.buffersize))) error(3, "input files too large to delta"); } if (f->st->st_size && read(fd, state.tmp.buffer, f->st->st_size) != f->st->st_size) error(ERROR_SYSTEM|3, "%s: read error", f->path); (void)close(fd); delta: f->delta.op = DELTA_create; fd = dodelta(f, state.delta.buffer, 0L, state.tmp.buffer); } else f->delta.op = 0; fileout(f, fd); } else fileskip(f); break; case DELTA_verify: if (!f->delta.base || f->delta.base->mtime != f->st->st_mtime) error(3, "%s: base archive mismatch", f->name); if (validout(f) && selectfile(f)) { f->delta.op = 0; deltacopy(f); } else fileskip(f); break; default: error(3, "%s: not a delta archive (2)", state.file); break; } gettrailer(f); } getepilogue(); } if (state.delta.tab) { /* * copy the non-empty untouched base hard links first */ #if DEBUG message(-2, "copy non-empty untouched base hard links"); #endif hashscan(state.delta.tab, 0, &pos); while (hashnext(&pos)) { d = (struct deltainfo*)pos.bucket->value; if (!d->mark && d->info->st->st_nlink > 1 && d->info->st->st_size > 0 && selectfile(d->info)) { d->mark = 1; deltacopy(d->info); } } hashdone(&pos); /* * copy the remaining untouched base files */ #if DEBUG message(-2, "copy remaining untouched base files"); #endif hashscan(state.delta.tab, 0, &pos); while (hashnext(&pos)) { d = (struct deltainfo*)pos.bucket->value; if (!d->mark && selectfile(d->info)) { state.entries++; deltacopy(d->info); } } hashdone(&pos); } putepilogue(); state.volume = 0; } /* * copy file from input to output archive */ static void deltacopy(f) register struct fileinfo* f; { f->st->st_size = f->delta.base->size; fileout(f, deltafd(f->delta.base)); } /* * copy delta base archive delete entries */ void deltadelete() { register struct fileinfo* f; HASHPOSITION pos; f = &file; hashscan(state.delta.tab, 0, &pos); while (hashnext(&pos)) { if (!((struct deltainfo*)pos.bucket->value)->mark) { state.entries++; state.selected++; initfile(f, pos.bucket->name, S_IFREG); f->delta.op = DELTA_delete; putheader(f); puttrailer(f); } } hashdone(&pos); } /* * delta algorithm wrapper */ /*ARGSUSED*/ int dodelta(f, old, oldsize, new) struct fileinfo* f; char* old; long oldsize; char* new; { int fd; if ((fd = creat(state.tmp.file, S_IRUSR)) < 0) error(3, "%s: cannot create delta temporary file", state.tmp.file); if (delta(old, oldsize, new, f->st->st_size, fd) < 0) error(3, "%s: delta write error", f->name); f->delta.size = f->st->st_size; if ((f->st->st_size = lseek(fd, 0L, 2)) < 0) error(3, "%s: delta seek error", f->name); (void)close(fd); if ((fd = open(state.tmp.file, 0)) < 0) error(3, "%s: cannot read delta temporary file", state.tmp.file); if (remove(state.tmp.file)) error(1, "%s: cannot remove delta temporary file", state.tmp.file); return(fd); } /* * delta update algorithm wrapper */ /*ARGSUSED*/ int doupdate(f, oldfd, file, wfd) struct fileinfo* f; int oldfd; char* file; int wfd; { register long c; register int n; register int v; int rfd; switch (f->delta.version) { case VERSION: c = lseek(0, 0L, 1); if (lseek(0, state.in.count, 0) != state.in.count) return(-1); if ((rfd = open(file, 0)) < 0) error(ERROR_SYSTEM|3, "%s: cannot read delta temporary file", file); v = update(oldfd, f->delta.base->offset, 0, wfd, rfd, 0L); if ((n = lseek(wfd, 0L, 2)) < 0) error(ERROR_SYSTEM|3, "%s: update seek error", f->name); (void)close(rfd); (void)close(wfd); if (state.operation == IN) setfile(f); if (lseek(0, c, 0) != c || v < 0) return(-1); fileskip(f); f->st->st_size = n; return(0); default: (void)close(wfd); fileskip(f); error(2, "%s: version %c delta not supported", f->name, f->delta.version); return(-1); } } 0707070000000000111006440044230044230000010000000474562247500002200000043006file.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax file support */ #include "pax.h" extern char* fmtuid(); extern char* fmtgid(); /* * return read file descriptor for current input file */ int openin(f) register struct fileinfo* f; { register int n; int rfd; int wfd; int pfd; char* cmdargv[3]; extern int cmdopen(); extern int cmdclose(); if (f->type != S_IFREG) return(-1); if (!state.filter) { if ((rfd = open(f->path, 0)) < 0) error(ERROR_SYSTEM|2, "%s: cannot read", f->path); else if (state.formatout == ASCHK) { f->checksum = 0; if (lseek(rfd, 0L, 0) != 0) error(ERROR_SYSTEM|1, "%s: %s checksum seek error", f->path, format[state.formatout].name); else { while ((n = read(rfd, state.tmp.buffer, state.buffersize)) > 0) f->checksum = asc_checksum(state.tmp.buffer, n, f->checksum); if (n < 0) error(ERROR_SYSTEM|2, "%s: %s checksum read error", f->path, format[state.formatout].name); if (lseek(rfd, 0L, 0) != 0) error(ERROR_SYSTEM|1, "%s: %s checksum seek error", f->path, format[state.formatout].name); } } return(rfd); } if ((wfd = creat(state.tmp.file, S_IRUSR)) < 0) { error(2, "%s: cannot create filter temporary %s", f->path, state.tmp.file); return(-1); } if ((rfd = open(state.tmp.file, 0)) < 0) { error(2, "%s: cannot open filter temporary %s", f->path, state.tmp.file); (void)close(wfd); if (remove(state.tmp.file)) error(1, "%s: cannot remove filter temporary %s", f->path, state.tmp.file); return(-1); } if (remove(state.tmp.file)) error(1, "%s: cannot remove filter temporary %s", f->path, state.tmp.file); cmdargv[0] = state.filter; cmdargv[1] = f->path; cmdargv[2] = 0; if ((pfd = cmdopen(state.filter, cmdargv, (char**)0, (int*)0, "r")) < 0) { error(2, "%s: cannot execute filter %s", f->path, state.filter); (void)close(rfd); (void)close(wfd); return(-1); } if (state.formatout == ASCHK) f->checksum = 0; f->st->st_size = 0; while ((n = read(pfd, state.tmp.buffer, state.buffersize)) > 0) { if (write(wfd, state.tmp.buffer, n) != n) { error(2, "%s: filter write error", f->path); break; } if (state.formatout == ASCHK) f->checksum = asc_checksum(state.tmp.buffer, n, f->checksum); f->st->st_size += n; } if (n < 0) error(ERROR_SYSTEM|2, "%s: %s filter read error", f->path, state.filter); if (n = cmdclose(pfd)) error(2, "%s: %s filter exit code %d", f->path, state.filter, n); (void)close(wfd); #if DEBUG message(-1, "%s: filter file size = %ld", f->path, f->st->st_size); #endif return(rfd); } /* * open file for writing, set all necessary info */ int openout(f) register struct fileinfo* f; { register int fd; struct stat st; (void)pathcanon(f->name); if ((*state.statf)(f->name, &st)) st.st_mode = 0; if (f->delta.op == DELTA_delete) { switch (st.st_mode & S_IFMT) { case 0: break; case S_IFDIR: if (!streq(f->name, ".") && !streq(f->name, "..") && rmdir(f->name)) error(ERROR_SYSTEM|2, "%s: cannot remove directory", f->name); break; default: if (remove(f->name)) error(ERROR_SYSTEM|2, "%s: cannot remove file", f->name); break; } return(-1); } if (state.operation == (IN|OUT)) { if (st.st_mode && f->st->st_ino == st.st_ino && f->st->st_dev == st.st_dev) { error(2, "attempt to pass %s to self", f->name); return(-1); } if (state.linkf && f->type != S_IFDIR && (state.linkf == putsymlink || f->st->st_dev == state.dev)) { if (st.st_mode) remove(f->name); if ((*state.linkf)(f->path, f->name)) { if (!st.st_mode && missdir(f)) { error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name); return(-1); } if (st.st_mode || (*state.linkf)(f->path, f->name)) { error(ERROR_SYSTEM|2, "%s: cannot link to %s", f->path, f->name); return(-1); } } setfile(f); return(-2); } } if ((state.delta.op & IN) && st.st_mode == f->st->st_mode && f->st->st_mtime == st.st_mtime && f->st->st_size == st.st_size) return(-1); switch (f->type) { case S_IFDIR: f->st->st_size = 0; if (f->name[0] == '.' && (f->name[1] == 0 || f->name[1] == '.' && f->name[2] == 0)) return(-1); if (st.st_mode) { if ((st.st_mode & S_IFMT) != S_IFDIR) { error(1, "current %s is not a directory", f->name); return(-1); } } else if (mkdir(f->name, f->st->st_mode) && (missdir(f) || mkdir(f->name, f->st->st_mode))) { error(ERROR_SYSTEM|2, "%s: cannot create directory", f->name); return(-1); } setfile(f); return(state.update && st.st_mode ? -1 : -2); case S_IFLNK: if (streq(f->name, f->linkname)) { error(1, "%s: symbolic link loops to self", f->name); return(-1); } if (st.st_mode) { if (state.update && f->st->st_mtime <= st.st_mtime) return(-1); if (remove(f->name)) { error(ERROR_SYSTEM|2, "cannot remove current %s", f->name); return(-1); } } if (putsymlink(f->linkname, f->name)) { if (!st.st_mode && missdir(f)) { error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name); return(-1); } if (st.st_mode || putsymlink(f->linkname, f->name)) { error(ERROR_SYSTEM|2, "%s: cannot symlink to %s", f->name, f->linkname); return(-1); } } return(-2); } if (!addlink(f)) return(-1); if (st.st_mode) { if (state.update && f->st->st_mtime <= st.st_mtime) { if (f->st->st_mtime < st.st_mtime) error(1, "current %s is newer", f->name); return(-1); } if (remove(f->name)) { error(ERROR_SYSTEM|2, "cannot remove current %s", f->name); return(-1); } } switch (f->type) { case S_IFIFO: case S_IFSOCK: f->st->st_rdev = 0; case S_IFBLK: case S_IFCHR: f->st->st_size = 0; if (mknod(f->name, f->st->st_mode, f->st->st_rdev)) { if (errno == EPERM) { error(ERROR_SYSTEM|2, "%s: cannot create %s special file", f->name, (f->type == S_IFBLK) ? "block" : "character"); return(-1); } if (!st.st_mode && missdir(f)) { error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name); return(-1); } if (st.st_mode || mknod(f->name, f->st->st_mode, f->st->st_rdev)) { error(ERROR_SYSTEM|2, "%s: cannot mknod", f->name); return(-1); } } setfile(f); return(-2); default: error(1, "%s: unknown file type 0%03o -- creating regular file", f->name, f->type >> 12); /*FALLTHROUGH*/ case S_IFREG: if ((fd = creat(f->name, f->st->st_mode)) < 0) { if (!st.st_mode && missdir(f)) { error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name); return(-1); } if (st.st_mode || (fd = creat(f->name, f->st->st_mode)) < 0) { error(ERROR_SYSTEM|2, "%s: cannot create", f->name); return(-1); } } return(fd); } } /* * get file info for output */ int getfile(f, ftw) register struct fileinfo* f; register struct FTW* ftw; { register char* name; static struct stat st; static char pathbuffer[PATH_MAX]; static char namebuffer[PATH_MAX]; name = ftw->path; #if DEBUG message(-4, "getfile(%s)", name); #endif if (ftw->pathlen >= sizeof(namebuffer)) { error(2, "%s: file name too long", name); return(0); } switch (ftw->info) { case FTW_NS: error(2, "%s: not found", name); return(0); case FTW_DNR: if (state.files) error(2, "%s: cannot read directory", name); break; case FTW_D: case FTW_DNX: if (!state.files) ftw->status = FTW_SKIP; else if (ftw->info == FTW_DNX) { error(2, "%s: cannot search directory", name); ftw->status = FTW_SKIP; } break; } if (state.xdev && ftw->statb.st_dev != ftw->parent->statb.st_dev) { ftw->status = FTW_SKIP; return(0); } state.delta.hdr = state.delta.hdrbuf; f->path = strcpy(pathbuffer, name); (void)pathcanon(strcpy(namebuffer, name)); f->name = map(namebuffer); if (state.files && state.operation == (IN|OUT) && dirprefix(state.destination, name)) return(0); f->namesize = strlen(f->name) + 1; /* this may not be necessary */ st = ftw->statb; f->st = &st; f->type = f->st->st_mode & S_IFMT; f->linktype = NOLINK; f->linkname = 0; f->linknamesize = 0; f->uidname = 0; f->gidname = 0; if (!validout(f)) return(0); if (state.operation == OUT && f->type != S_IFDIR) { if (!addlink(f)) f->st->st_size = 0; #if DEBUG message(-3, "getfile(%s): dev'=%d ino'=%d", f->name, f->st->st_dev, f->st->st_ino); #endif } state.entries++; f->delta.op = 0; f->skip = 0; #if DEBUG message(-2, "getfile(): path=%s name=%s mode=%s size=%ld", name, f->name, fmtmode(f->st->st_mode), f->st->st_size); #endif return(1); } /* * check that f is valid for archive output */ int validout(f) register struct fileinfo* f; { register char* s; static char linkbuffer[PATH_MAX]; static char idbuffer[ALAR_NAMESIZE + 1]; switch (f->type) { case S_IFCHR: case S_IFBLK: f->st->st_size = 0; break; case S_IFREG: f->st->st_rdev = 0; break; case S_IFLNK: if (f->st->st_size >= sizeof(linkbuffer) - 1) { error(2, "%s: symbolic link too long", f->path); return(0); } if (readlink(f->path, linkbuffer, sizeof(linkbuffer) - 1) != f->st->st_size) { error(2, "%s: cannot read symbolic link", f->path); return(0); } linkbuffer[f->st->st_size] = 0; f->linktype = SOFTLINK; (void)pathcanon(linkbuffer); f->linkname = (state.ftwflags & FTW_PHYSICAL) ? linkbuffer : map(linkbuffer); f->linknamesize = strlen(f->linkname) + 1; if (streq(f->path, f->linkname)) { error(2, "%s: symbolic link loops to self", f->path); return(0); } f->st->st_size = 0; f->st->st_rdev = 0; break; case S_IFDIR: if (streq(f->path, ".") || streq(f->path, "..")) return(0); f->st->st_size = 0; f->st->st_rdev = 0; break; } switch (state.formatout) { case ALAR: case IBMAR: case SAVESET: if (f->type != S_IFREG) { error(2, "%s: only regular files copied in %s format", f->path, format[state.formatout].name); return(0); } if (s = strrchr(f->name, '/')) { s++; error(1, "%s: file name stripped to %s", f->name, s); } else s = f->name; if (strlen(s) > sizeof(idbuffer) - 1) { error(2, "%s: file name too long", f->name); return(0); } f->id = strupper(strcpy(idbuffer, s)); break; case BINARY: if (f->namesize > BINARY_NAMESIZE) { error(2, "%s: file name too long", f->name); return(0); } break; case PAX: if (s = strrchr(f->name, '/')) s++; else s = f->name; f->id = strupper(strncpy(idbuffer, s, sizeof(idbuffer) - 1)); break; case TAR: case USTAR: if (f->namesize > sizeof(tar_header.name) + ((state.formatout == TAR) ? -(f->type == S_IFDIR) : sizeof(tar_header.prefix))) { error(2, "%s: file name too long", f->name); return(0); } if (f->linknamesize > sizeof(tar_header.linkname)) { error(2, "%s: link name too long", f->name); return(0); } break; } return(1); } /* * add file which may be a link * 0 returned if <dev,ino> already added */ int addlink(f) register struct fileinfo* f; { register struct linkinfo* p; register char* s; int fmt; struct fileid id; unsigned short us; static int warned; id.dev = f->st->st_dev; id.ino = f->st->st_ino; if (!(state.delta.op & (IN|OUT))) switch (state.operation) { case IN: us = id.dev; if (us > state.devcnt) { state.devcnt = us; state.inocnt = id.ino; } else if (us == state.devcnt) { us = id.ino; if (us > state.inocnt) state.inocnt = us; } break; case OUT: if (!++state.inocnt) { if (!++state.devcnt) goto toomany; state.inocnt = 1; } f->st->st_dev = state.devcnt; f->st->st_ino = state.inocnt; break; } if (f->type == S_IFDIR) return(0); fmt = state.operation == IN ? state.formatin : state.formatout; switch (fmt) { case ALAR: case IBMAR: case SAVESET: if (state.operation == IN || f->st->st_nlink <= 1) return(1); break; case TAR: case USTAR: if (state.operation == IN) { if (f->linktype == NOLINK) return(1); goto linked; } /*FALLTHROUGH*/ default: if (f->st->st_nlink <= 1) return(1); break; } if (p = (struct linkinfo*)hashget(state.linktab, (char*)&id)) { switch (fmt) { case ALAR: case IBMAR: case SAVESET: error(1, "%s: hard link information lost in %s format", f->name, format[fmt].name); return(1); } f->st->st_dev = p->id.dev; f->st->st_ino = p->id.ino; f->linktype = HARDLINK; f->linkname = p->name; f->linknamesize = p->namesize; if (state.operation == OUT) return(0); linked: #if DEBUG message(-1, "addlink(%s,%s)", f->name, f->linkname); #endif if (streq(f->name, f->linkname)) { error(2, "%s: hard link loops to self", f->name); return(0); } if (!state.list) { s = f->linkname; if (access(s, 0)) { f->skip = 1; error(2, "%s must exist for hard link %s", s, f->name); return(0); } remove(f->name); if (state.operation == IN && *s != '/') { (void)strcpy(state.pwd + state.pwdlen, s); s = state.pwd; } if (link(s, f->name)) { if (missdir(f)) { error(ERROR_SYSTEM|2, "%s: cannot create intermediate directories", f->name); return(0); } if (link(s, f->name)) { error(ERROR_SYSTEM|2, "%s: cannot link to %s", f->linkname, f->name); return(-1); } } } return(0); } if (!(p = allocate(struct linkinfo)) || !(p->name = strdup(f->name))) goto toomany; p->namesize = strlen(p->name) + 1; p->id.dev = f->st->st_dev; p->id.ino = f->st->st_ino; (void)hashput(state.linktab, (char*)0, p); return(-1); toomany: if (!warned) { warned = 1; error(1, "too many hard links -- some links may become copies"); } return(-1); } /* * get file uid and gid names given numbers */ void getidnames(f) register struct fileinfo* f; { if (!f->uidname) f->uidname = fmtuid(f->st->st_uid); if (!f->gidname) f->gidname = fmtgid(f->st->st_gid); } /* * set file uid and gid numbers given names */ void setidnames(f) register struct fileinfo* f; { register int id; if (f->uidname) { if ((id = struid(f->uidname)) < 0) { if (id == -1 && state.owner) error(1, "%s: invalid user name", f->uidname); f->uidname = 0; id = state.uid; } f->st->st_uid = id; } if (f->gidname) { if ((id = strgid(f->gidname)) < 0) { if (id == -1 && state.owner) error(1, "%s: invalid group name", f->gidname); f->gidname = 0; id = state.gid; } f->st->st_gid = id; } } /* * initialize file info with name and mode */ void initfile(f, name, mode) register struct fileinfo* f; register char* name; int mode; { static struct stat st; (void)memset(f, 0, sizeof(*f)); (void)memset(&st, 0, sizeof(st)); f->st = &st; if (name) { f->id = f->name = f->path = name; f->namesize = strlen(name) + 1; } f->st->st_mode = mode; f->st->st_nlink = 1; /* system V needs this!!! */ } /* * set copied file info */ void setfile(f) register struct fileinfo* f; { register struct postinfo* p; struct postinfo post; if (!(state.delta.op & OUT)) switch (f->type) { case S_IFLNK: break; case S_IFDIR: if (state.modtime || state.owner || (f->st->st_mode & (S_IWUSR|S_IXUSR)) != (S_IWUSR|S_IXUSR)) { if (!(p = allocate(struct postinfo))) error(3, "not enough space for file restoration info"); p->mtime = f->st->st_mtime; p->uid = f->st->st_uid; p->gid = f->st->st_gid; p->mode = f->st->st_mode; if ((f->st->st_mode & S_IRWXU) != S_IRWXU && chmod(f->name, f->st->st_mode|S_IRWXU)) error(1, "%s: cannot chmod to %s", f->name, fmtmode(f->st->st_mode|S_IRWXU) + 1); (void)hashput(state.restore, f->name, p); break; } /*FALLTHROUGH*/ default: p = &post; p->mtime = f->st->st_mtime; p->uid = f->st->st_uid; p->gid = f->st->st_gid; p->mode = f->st->st_mode; (void)restore(f->name, p); break; } } /* * set access and modification times of file */ void settime(name, atime, mtime) char* name; time_t atime; time_t mtime; { if (touch(name, atime, mtime, 0)) error(1, "%s: cannot set times", name); } /* * create directory and all path name components leading to directory */ int missdir(f) register struct fileinfo* f; { register char* s; register char* t; struct stat* st; struct stat* sp; struct stat st0; struct stat st1; s = f->name; (void)pathcanon(s); if (t = strchr(*s == '/' ? s + 1 : s, '/')) { if (!state.intermediate) { static int warned; if (!warned) { error(1, "omit the -d option to create intermediate directories"); warned = 1; } return(-1); } st = 0; sp = &st0; do { *t = 0; if (stat(s, sp)) { *t = '/'; break; } *t = '/'; st = sp; sp = (sp == &st0) ? &st1 : &st0; } while (t = strchr(t + 1, '/')); if (t) { if (!st && stat(".", st = &st0)) error(ERROR_SYSTEM|3, "%s: cannot stat .", s); sp = f->st; f->st = st; do { *t = 0; if (mkdir(s, st->st_mode & state.modemask)) { error(ERROR_SYSTEM|2, "%s: cannot create directory", s); *t = '/'; f->st = sp; return(-1); } setfile(f); *t = '/'; } while (t = strchr(t + 1, '/')); f->st = sp; } } return(0); } /* * restore file status after processing */ int restore(name, p) register char* name; register struct postinfo* p; { int m; struct stat st; if (state.owner) { if (state.flags & SETIDS) { p->uid = state.setuid; p->gid = state.setgid; } if (chown(name, p->uid, p->gid) < 0) error(1, "%s: cannot chown to (%d,%d)", name, p->uid, p->gid); } if (p->mode != S_IFMT) { if (chmod(name, p->mode & state.modemask)) error(1, "%s: cannot chmod to %s", name, fmtmode(p->mode & state.modemask) + 1); else if (m = p->mode & (S_ISUID|S_ISGID|S_ISVTX)) { if (stat(name, &st)) error(1, "%s: not found", name); else if (m ^= (st.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) error(1, "%s: mode %s not set", name, fmtmode(m) + 1); } } if (state.modtime) settime(name, p->mtime, p->mtime); return(0); } 0707070000000000121006440044230044230000010000000474561654100002400000137345format.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax archive format support */ #include "pax.h" static int isalar(); #if CPIO_EXTENDED static void getxops(); static void setxops(); static void putxops(); static void addxopstr(); static void addxopnum(); #endif /* * read archive prologue before files are copied */ int getprologue() { long size; if (state.volume && state.append) return(0); state.formatin = IN_DEFAULT; state.id.volume[0] = 0; state.in.offset += state.in.count; state.in.count = 0; if (bread(alar_header, (long)ALAR_HEADER, (long)ALAR_HEADER, 0) <= 0) { if (!bcount(state.in)) return(0); } else if (!isalar(alar_header)) { if (strneq(alar_header, PORTAR_MAG, PORTAR_MAGSIZ)) { bunread(alar_header, ALAR_HEADER - 8); if (bread(&portar_header, 0, (long)PORTAR_HEADER, 0) > 0) { if (!strneq(portar_header.ar_fmag, PORTAR_END, PORTAR_ENDSIZ) || sscanf(portar_header.ar_size, "%ld", &size) != 1) { bunread(&portar_header, PORTAR_HEADER); bunread(PORTAR_MAG, PORTAR_MAGSIZ); } else if (strmatch(portar_header.ar_name, PORTAR_SYM) && (state.formatin = PORTAR) || strmatch(portar_header.ar_name, RANDAR_SYM) && (state.formatin = RANDAR)) { size += (size & 01); if (bread((char*)0, 0L, size, 0) <= 0) error(3, "invalid %s format symbol table", format[state.formatin].name); } else { size = portar_header.ar_date[0]; portar_header.ar_date[0] = 0; state.formatin = strchr(portar_header.ar_name, PORTAR_TERM) ? PORTAR : RANDAR; portar_header.ar_date[0] = size; bunread(&portar_header, PORTAR_HEADER); } } } else if (strneq(alar_header, "<ar>", 4) || /* s5r1 */ gethalf(0, alar_header) == 0177545 || /* pdp11 */ gethalf(BYTE, alar_header) == 0177545) /* pdp11 */ error(3, "%s: use ar(1) for library archives", state.file); else bunread(alar_header, ALAR_HEADER); } if (state.volume++) { if (state.delta.op) { if (state.operation == (IN|OUT) || !(state.delta.op & COMPRESS)) error(3, "%s: %s archive cannot be multi-volume", state.file, (state.delta.op & COLLECT) ? "base" : "delta"); state.delta.op = 0; } /* * no hard links between volumes */ hashfree(state.linktab); if (!(state.linktab = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_namesize, sizeof(struct fileid), HASH_name, "links", 0))) error(3, "cannot re-allocate hard link table"); } state.entry = 0; return(1); } /* * check for ASCII or EBCDIC ALAR prologue in alar_header */ static int isalar(hdr) register char* hdr; { char buf[4]; if (!strneq(hdr, "VOL1", 4)) { cvtetoa(hdr, buf, 4); if (!strneq(buf, "VOL1", 4)) return(0); cvtetoa(hdr, hdr, ALAR_HEADER); state.formatin = IBMAR; } else state.formatin = ALAR; (void)getlabstr(hdr, 5, sizeof(state.id.volume) - 1, state.id.volume); (void)getlabstr(hdr, 25, 6, state.id.format); (void)getlabstr(hdr, 31, 7, state.id.implementation); (void)getlabstr(hdr, 38, 14, state.id.owner); state.in.blocked = !bcount(state.in); if (streq(state.id.format, PAX_ID)) { state.formatin = PAX; state.record.line = 0; } return(1); } /* * write archive prologue before files are copied */ void putprologue() { switch (state.formatout) { case PAX: state.record.line = 0; /*FALLTHROUGH*/ case ALAR: case IBMAR: #if DEBUG if (state.out.blok) state.out.blocked = 1; else #endif state.out.blocked = !state.out.unblocked; if (!state.id.owner[0]) { (void)strncpy(state.id.owner, fmtuid(getuid()), sizeof(state.id.owner) - 1); state.id.owner[sizeof(state.id.owner) - 1] = 0; (void)strupper(state.id.owner); } if (!state.id.volume[0]) { (void)strncpy(state.id.volume, state.id.owner, sizeof(state.id.volume) - 1); state.id.volume[sizeof(state.id.volume) - 1] = 0; } (void)strncpy(state.id.format, state.formatout == PAX ? PAX_ID : ALAR_ID, sizeof(state.id.format) - 1); (void)strncpy(state.id.implementation, IMPLEMENTATION, sizeof(state.id.implementation) - 1); (void)sprintf(alar_header, "VOL1%-6.6s %-6.6s%-7.7s%-14.14s 4", state.id.volume, state.id.format, state.id.implementation, state.id.owner); if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER); bwrite(alar_header, ALAR_HEADER); (void)sprintf(alar_header, "VOL2 00000000000000 "); if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER); bwrite(alar_header, ALAR_HEADER); if (state.delta.op & (COMPRESS|OUT)) { (void)sprintf(alar_header, "UVL1 %-6.6s%c%c%010ld%010ld ", ID, (state.delta.op & COMPRESS) ? TYPE_COMPRESS : TYPE_DELTA, VERSION, state.operation == OUT ? state.delta.size : 0L, state.operation == OUT ? state.delta.checksum : 0L); if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER); bwrite(alar_header, ALAR_HEADER); } break; default: if (state.delta.op & (COMPRESS|OUT)) { register struct fileinfo* f; struct fileinfo file; f = &file; #if new_delta_format (void)sprintf(state.tmp.file, "%c%s%c%c%c%c", INFO_SEP, ID, INFO_SEP, state.operation == OUT && state.delta.size ? TYPE_DELTA : TYPE_COMPRESS, VERSION, INFO_SEP); initfile(f, state.tmp.file, S_IFREG); #else initfile(f, "DELTA!!!", S_IFREG); #endif f->skip = 1; if (state.operation == OUT) { f->st->st_mtime = state.delta.size; f->st->st_uid = DELTA_LO(state.delta.checksum); f->st->st_gid = DELTA_HI(state.delta.checksum); } putheader(f); puttrailer(f); } break; } } /* * read archive epilogue after all files have been copied */ void getepilogue() { register char* s; register int n; register int i; if (state.append) backup(); else { switch (state.formatin) { case ALAR: case IBMAR: case PAX: case PORTAR: case RANDAR: #if SAVESET case SAVESET: #endif break; default: /* * check for more volumes * volumes begin on BLOCKSIZE boundaries * separated by up to MAXBLOCKS null byte filler */ i = MAXBLOCKS; if (!(n = round(state.in.count, BLOCKSIZE) - state.in.count) || bread(state.tmp.buffer, 0L, (long)n, 0) > 0) do { if (*(s = state.tmp.buffer) && n == BLOCKSIZE) { bunread(state.tmp.buffer, BLOCKSIZE); state.formatin = IN_DEFAULT; #if DEBUG message(-2, "go for next volume %-.32s...", state.tmp.buffer); #endif return; } while (s < state.tmp.buffer + n && !*s++); if (s < state.tmp.buffer + n) { if (state.volume > 1) error(1, "junk data after volume %d", state.volume); break; } n = BLOCKSIZE; } while (i-- > 0 && bread(state.tmp.buffer, 0L, n, 0) > 0); bflushin(); break; } state.formatin = IN_DEFAULT; } } /* * write archive epilogue after files have been copied */ void putepilogue() { register int n; register int boundary; register struct fileinfo* f; struct fileinfo tmp; static int selected; if (state.selected > selected) { selected = state.selected; boundary = state.out.count; switch (state.formatout) { case ALAR: case IBMAR: case PAX: bwrite(alar_header, 0); bwrite(alar_header, 0); break; case BINARY: case CPIO: case ASC: case ASCHK: f = &tmp; initfile(f, CPIO_TRAILER, 0); f->skip = 1; putheader(f); puttrailer(f); boundary = state.out.unblocked ? BLOCKSIZE : state.blocksize; break; case TAR: case USTAR: memset(tar_block, 0, TAR_HEADER); bwrite(tar_block, TAR_HEADER); bwrite(tar_block, TAR_HEADER); boundary = state.out.unblocked ? BLOCKSIZE : state.blocksize; break; } if ((n = boundary - state.out.count) < 0) n = round(state.out.count, boundary) - state.out.count; if (n) { memset(state.tmp.buffer, 0, n); bwrite(state.tmp.buffer, n); } bflushout(); state.volume++; } else { state.out.count = state.out.offset = 0; state.out.next = state.out.buffer; } } #if DEBUG /* * try to repair pax binary cpio header botch that always set * mtime and size to 1 */ static void repair(f) register struct fileinfo* f; { register char* b; register char* e; register int null; int n; long pos; unsigned long size; struct stat st; struct binary_header hdr; char buf[4096]; static int magic0; static int magic1; static time_t mtime; static int warned; if (!warned) { warned = 1; error(1, "repairing botched headers -- time stamps will be incorrect"); mtime = fstat(state.append, &st) ? (time_t)1 : st.st_mtime; b = (char*)&binary_header.magic; magic0 = b[state.swap != 0]; magic1 = b[state.swap == 0]; } /* * determine the file size by locating the next header * * NOTE: magic number is on even byte boundary * file data is on even byte boundary * input buffer size is even */ null = 0; pos = 0; size = 0; b = state.in.next; e = state.in.last; for (;;) { while (b < e) { if (*b++ == magic0) { if (*b++ == magic1) { /* * check a few header fields to weed * out bad magic */ if ((n = e - (b - 2)) < BINARY_HEADER) { memcpy(&hdr, b - 2, n); if (read(state.append, (char*)&hdr + n, BINARY_HEADER - n) != BINARY_HEADER - n) error(ERROR_SYSTEM|3, "repair read error"); if (lseek(state.append, (long)(n - BINARY_HEADER), 1) == -1L) error(3, "can only repair seekable input archives"); } else memcpy(&hdr, b - 2, BINARY_HEADER); if (state.swap) memswap(state.swap, (char*)&hdr, BINARY_HEADER); if (cpio_long(hdr.mtime) == 1 && cpio_long(hdr.size) == 1) { /* * found the next header */ f->st->st_size = size - (null == 1); f->st->st_mtime = mtime; if (pos && lseek(state.append, pos, 0) != pos) error(ERROR_SYSTEM|3, "repair seek error"); return; } } else if (*(b - 1) == 0) null++; } else { if (*(b - 1) == 0) null++; if (*b++ == 0) null++; } size += 2; } if (!pos && (pos = lseek(state.append, 0L, 1)) == -1L) error(3, "can only repair seekable input archives"); b = buf; if ((n = read(state.append, b, sizeof(buf))) <= 0) error(3, "cannot repair -- header missing"); e = buf + n - 1; } } #endif /* * read next archive entry header */ int getheader(f) register struct fileinfo* f; { register char* s; register int i; register long n; char* typename; long num; int warned; int checkdelta; int lab; int type; short magic; struct { long dev; long ino; long mode; long uid; long gid; long nlink; long rdev; long mtime; long size; long dev_major; long dev_minor; long rdev_major; long rdev_minor; long checksum; } lst; static char namebuffer[PATH_MAX * 2]; static char pathbuffer[PATH_MAX]; static char linkbuffer[PATH_MAX]; static char idbuffer[ALAR_NAMESIZE + 1]; static char uidname[9]; static char gidname[9]; volume: warned = 0; checkdelta = !state.entry++ && !(state.test & 020); type = 0; typename = ""; if (state.append) bsave(); again: for (;;) { f->record.format = 0; f->skip = 0; #if DEBUG message(-2, "%s:", format[state.formatin].name); #endif switch (state.formatin) { case ALAR: case IBMAR: case PAX: if (!(lab = getlabel(f))) return(0); f->name = namebuffer; f->st->st_dev = 0; f->st->st_ino = 0; f->st->st_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; f->st->st_uid = state.uid; f->st->st_gid = state.gid; f->st->st_nlink = 1; f->st->st_rdev = 0; f->st->st_size = 0; f->linktype = NOLINK; f->linkname = 0; f->uidname = 0; f->gidname = 0; type = 0; do { if (state.formatin == IBMAR) cvtetoa(alar_header, alar_header, ALAR_HEADER); if (checkdelta && strneq(alar_header, "UVL1", 4) && strneq(alar_header + 5, ID, IDLEN)) { checkdelta = 0; s = alar_header + 10; f->st->st_mtime = getlabnum(alar_header, 14, 10, 10); n = getlabnum(alar_header, 24, 10, 10); f->st->st_uid = DELTA_LO(n); f->st->st_gid = DELTA_HI(n); goto deltaverify; } else if (strneq(alar_header, "HDR", 3)) { if (getlabnum(alar_header, 4, 1, 10) != ++type) error(3, "%s format HDR label out of sequence", format[state.formatin].name); if (type == 1) { s = namebuffer; for (i = 4; i <= ALAR_NAMESIZE + 3; i++) { if (alar_header[i] == ' ') { if (i >= ALAR_NAMESIZE + 3 || alar_header[i + 1] == ' ') break; *s++ = '.'; } else *s++ = isupper(alar_header[i]) ? tolower(alar_header[i]) : alar_header[i]; } if ((n = getlabnum(alar_header, 40, 2, 10)) > 0 && n < 99) (void)sprintf(s, ".%02d", n); else *s = 0; f->record.section = getlabnum(alar_header, 28, 4, 10); getlabstr(alar_header, 5, ALAR_NAMESIZE, f->id = idbuffer); getlabstr(alar_header, 61, 6, state.id.format); getlabstr(alar_header, 67, 7, state.id.implementation); if (streq(state.id.format, PAX_ID)) state.formatin = PAX; #if SAVESET else if (streq(state.id.format, SAVESET_ID) && streq(state.id.implementation, SAVESET_IMPL)) state.formatin = SAVESET; #endif f->st->st_mtime = state.present; if (n = getlabnum(alar_header, 43, 2, 10)) { if (alar_header[41] == '0') n += 100; if ((i = getlabnum(alar_header, 45, 3, 10)) >= 0 && i <= 365) { f->st->st_mtime = i; while (n-- > 70) f->st->st_mtime += ((n % 4) || n == 100) ? 365 : 366; f->st->st_mtime *= 24L * 60L * 60L; f->st->st_mtime += 12L * 60L * 60L; } } } else if (type == 2) { switch (f->record.format = alar_header[4]) { case 'F': /* fixed length */ case 'D': /* decimal variable */ case 'S': /* spanned */ case 'U': /* input block size */ case 'V': /* binary variable */ break; default: error(2, "%s record format %c not supported", format[state.formatin].name, f->record.format); f->skip = 1; } state.blocksize = getlabnum(alar_header, 6, 5, 10); state.record.size = getlabnum(alar_header, 11, 5, 10); if (!state.in.blocked) f->st->st_size = getlabnum(alar_header, 16, 10, 10); state.record.offset = getlabnum(alar_header, 51, 2, 10); } else if (state.formatin == PAX) { if (type == 3) { f->st->st_mode = strmode(alar_header + 20); if (alar_header[20] == 'z') { f->st->st_mode |= S_IFREG; f->linktype = HARDLINK; } getlabstr(alar_header, 5, 8, f->uidname = uidname); getlabstr(alar_header, 13, 8, f->gidname = gidname); f->st->st_mtime = getlabnum(alar_header, 31, 10, 10); f->st->st_ctime = getlabnum(alar_header, 41, 10, 10); f->st->st_atime = getlabnum(alar_header, 51, 10, 10); f->st->st_size = getlabnum(alar_header, 61, 10, 10); } else if (type == 4) { if (f->linktype != NOLINK) { if ((n = lab - ALAR_VARHDR) >= sizeof(linkbuffer)) { n = sizeof(linkbuffer) - 1; error(1, "%s: link text too long -- truncating to %d", f->name, n); } getlabstr(alar_header, 10, n, f->linkname = linkbuffer); f->linknamesize = n + 1; } else switch (f->st->st_mode & S_IFMT) { case S_IFBLK: case S_IFCHR: i = getlabnum(alar_header, 5, 8, 8); num = getlabnum(alar_header, 13, 8, 8); f->st->st_rdev = makedev(i, num); break; case S_IFDIR: case S_IFREG: case S_IFIFO: case S_IFSOCK: break; default: error(1, "%s: unknown file type %07o -- regular file assumed", f->name, f->st->st_mode & S_IFMT); f->st->st_mode = S_IFREG|(f->st->st_mode & S_IFMT); break; } } else if (type == 5) { if ((n = lab - ALAR_VARHDR) >= sizeof(namebuffer)) { n = sizeof(namebuffer) - 1; error(1, "%s: name too long -- truncating to %d", f->name, n); } getlabstr(alar_header, 10, n, f->name = namebuffer); f->namesize = n + 1; } } } else if (!state.in.blocked && strneq(alar_header, "VOL1", 4)) { bunread(alar_header, lab); if (!(getprologue())) return(0); goto volume; } } while ((lab = getlabel(f))); #if SAVESET if (state.formatin != SAVESET) goto found; state.saveset.time = f->st->st_mtime; if (state.blocksize > state.saveset.blocksize) { state.saveset.blocksize = state.blocksize; if (state.saveset.block) free(state.saveset.block); if (!(state.saveset.block = malloc(state.saveset.blocksize))) error(3, "cannot allocate %s format buffer", format[state.formatin].name); } state.saveset.bp = state.saveset.block + state.blocksize; /*FALLTHROUGH*/ case SAVESET: f->name = namebuffer; if (!getsaveset(f, 1)) goto again; #endif goto found; case BINARY: if (bread(&binary_header, (long)BINARY_HEADER, (long)BINARY_HEADER, 0) <= 0) break; if (state.swap) { memcpy(state.tmp.buffer, &binary_header, BINARY_HEADER); memswap(state.swap, (char*)&binary_header, BINARY_HEADER); } f->magic = binary_header.magic; if (f->magic == CPIO_MAGIC) { f->namesize = binary_header.namesize; f->st->st_dev = binary_header.dev; f->st->st_ino = binary_header.ino; f->st->st_mode = binary_header.mode; f->st->st_uid = binary_header.uid; f->st->st_gid = binary_header.gid; f->st->st_nlink = binary_header.links; f->st->st_rdev = binary_header.rdev; f->st->st_mtime = cpio_long(binary_header.mtime); f->st->st_size = cpio_long(binary_header.size); cpio_common: f->linktype = NOLINK; f->linkname = 0; f->uidname = 0; f->gidname = 0; f->name = namebuffer; switch (state.formatin) { case BINARY: i = BINARY_ALIGN; n = BINARY_HEADER; break; case ASC: case ASCHK: i = ASC_ALIGN; n = ASC_HEADER; break; default: i = 0; break; } if (i) { if (n = (n + f->namesize) % i) i -= n; else i = 0; } if (f->namesize >= sizeof(namebuffer)) { error(2, "entry %d.%d file name too long", state.volume, state.entry); for (n = f->namesize + i; n > 0; n -= sizeof(namebuffer)) (void)bread(namebuffer, 0L, n > sizeof(namebuffer) ? (long)sizeof(namebuffer) : n, 1); f->skip = 1; } else { (void)bread(namebuffer, 0, (long)(f->namesize + i), 1); if (namebuffer[f->namesize - 1]) { bunread(&namebuffer[f->namesize - 1], 1); namebuffer[f->namesize - 1] = 0; error(state.keepgoing ? 1 : 3, "entry %d.%d file name terminating null missing", state.volume, state.entry); } #if CPIO_EXTENDED getxops(f); #endif } if (streq(f->name, CPIO_TRAILER)) { getdeltaheader(f); return(0); } switch (f->st->st_mode & CPIO_FMT) { case CPIO_FIFO: f->type = S_IFIFO; break; case CPIO_DIR: f->type = S_IFDIR; break; case CPIO_CHR: f->type = S_IFCHR; break; case CPIO_BLK: f->type = S_IFBLK; break; case CPIO_LNK: f->type = S_IFLNK; break; case CPIO_SOCK: f->type = S_IFSOCK; break; default: error(1, "%s: unknown file type %07o -- regular file assumed", f->name, f->st->st_mode & S_IFMT); /*FALLTHROUGH*/ case CPIO_REG: f->type = S_IFREG; break; } f->st->st_mode &= 07777; f->st->st_mode |= f->type; #if DEBUG if (f->st->st_mtime == 1 && f->st->st_size == 1) repair(f); #endif switch (f->type) { case S_IFLNK: getdeltaheader(f); if (f->st->st_size > sizeof(linkbuffer) - 1) { error(2, "entry %d.%d symbolic link text too long", state.volume, state.entry); f->skip = 1; } else { f->linktype = SOFTLINK; f->linkname = linkbuffer; (void)bread(linkbuffer, 0L, f->st->st_size, 1); linkbuffer[f->st->st_size] = 0; f->st->st_size = 0; } break; default: f->linktype = NOLINK; break; } goto found; } bunread(state.swap ? state.tmp.buffer : (char*)&binary_header, BINARY_HEADER); break; case CPIO: if (bread(state.tmp.buffer, 0L, (long)CPIO_HEADER, 0) <= 0) break; state.tmp.buffer[CPIO_HEADER] = 0; if (state.tmp.buffer[0] == '0' && sscanf(state.tmp.buffer, "%6o%6lo%6lo%6lo%6lo%6lo%6lo%6lo%11lo%6o%11lo", &f->magic, &lst.dev, &lst.ino, &lst.mode, &lst.uid, &lst.gid, &lst.nlink, &lst.rdev, &lst.mtime, &f->namesize, &lst.size) == 11 && f->magic == CPIO_MAGIC) { f->st->st_dev = lst.dev; f->st->st_ino = lst.ino; f->st->st_mode = lst.mode; f->st->st_uid = lst.uid; f->st->st_gid = lst.gid; f->st->st_nlink = lst.nlink; f->st->st_rdev = lst.rdev; f->st->st_mtime = lst.mtime; f->st->st_size = lst.size; goto cpio_common; } bunread(state.tmp.buffer, CPIO_HEADER); break; case TAR: case USTAR: if (bread(tar_block, 0L, (long)TAR_HEADER, 0) <= 0) break; if (!*tar_header.name) { if (state.entry == 1) goto notar; return(0); } if (sscanf(tar_header.mode, "%11lo", &num) != 1) goto notar; f->st->st_mode = num; if (sscanf(tar_header.uid, "%11lo", &num) != 1) goto notar; f->st->st_uid = num; if (sscanf(tar_header.gid, "%11lo", &num) != 1) goto notar; f->st->st_gid = num; if (sscanf(tar_header.size, "%11lo", &num) != 1) goto notar; f->st->st_size = num; if (sscanf(tar_header.mtime, "%11lo", &num) != 1) goto notar; f->st->st_mtime = num; if (sscanf(tar_header.chksum, "%11lo", &num) != 1) goto notar; if ((n = num) != (i = tar_checksum())) { if (state.entry == 1) goto notar; error(state.keepgoing ? 1 : 3, "%s format checksum error (%d != %d)", format[state.formatin].name, n, i); } if (state.formatin == USTAR) { if (!streq(tar_header.magic, TMAGIC)) { if (strneq(tar_header.magic, TMAGIC, TMAGLEN - 1)) error(1, "%s format botched -- %s format assumed", format[state.formatin].name, format[TAR].name); else if (state.entry > 1) goto notar; state.formatin = TAR; } else if (!strneq(tar_header.version, TVERSION, sizeof(tar_header.version))) { error(1, "%s format version %-.*s incompatible with implementation version %-.*s -- assuming %s", format[state.formatin].name, sizeof(tar_header.version), tar_header.version, sizeof(tar_header.version), TVERSION, format[TAR].name); state.formatin = TAR; } } tar_header.name[sizeof(tar_header.name)] = 0; if (state.formatin == USTAR && *tar_header.prefix) (void)sprintf(f->name = namebuffer, "%-.%*s%s", sizeof(tar_header.prefix), tar_header.prefix, tar_header.name); else f->name = tar_header.name; tar_header.linkname[sizeof(tar_header.name)] = 0; f->linkname = tar_header.linkname; f->linktype = NOLINK; f->st->st_nlink = 1; switch (tar_header.typeflag) { case LNKTYPE: f->linktype = HARDLINK; f->st->st_mode |= S_IFREG; f->st->st_nlink = 2; f->st->st_size = 0; /* lest they forget */ break; case SYMTYPE: f->linktype = SOFTLINK; f->st->st_mode |= S_IFLNK; break; case CHRTYPE: f->st->st_mode |= S_IFCHR; device: if (sscanf(tar_header.devmajor, "%11o", &num) != 1) goto notar; i = num; if (sscanf(tar_header.devminor, "%11o", &num) != 1) goto notar; f->st->st_rdev = makedev(i, num); break; case BLKTYPE: f->st->st_mode |= S_IFBLK; goto device; case DIRTYPE: f->st->st_mode |= S_IFDIR; break; case FIFOTYPE: f->st->st_mode |= S_IFIFO; break; #ifdef SOKTYPE case SOKTYPE: f->st->st_mode |= S_IFSOCK; break; #endif default: error(1, "unknown file type `%c' -- regular file assumed", tar_header.typeflag); /*FALLTHROUGH*/ case REGTYPE: case AREGTYPE: f->namesize = strlen(f->name) + 1; if (f->name[f->namesize - 2] == '/') { f->st->st_mode |= S_IFDIR; if (f->namesize > 2) f->name[--f->namesize - 1] = 0; } else f->st->st_mode |= S_IFREG; break; } f->uidname = 0; f->gidname = 0; if (state.formatin == USTAR) { if (*tar_header.uname) f->uidname = tar_header.uname; if (*tar_header.gname) f->gidname = tar_header.gname; } goto found; notar: bunread(tar_block, TAR_HEADER); break; case ASC: case ASCHK: if (bread(state.tmp.buffer, 0L, (long)ASC_HEADER, 0) <= 0) break; state.tmp.buffer[ASC_HEADER] = 0; if (state.tmp.buffer[0] == '0' && sscanf(state.tmp.buffer, "%6o%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8lx%8x%8lx", &f->magic, &lst.ino, &lst.mode, &lst.uid, &lst.gid, &lst.nlink, &lst.mtime, &lst.size, &lst.dev_major, &lst.dev_minor, &lst.rdev_major, &lst.rdev_minor, &f->namesize, &lst.checksum) == 14 && (f->magic == ASC_MAGIC || f->magic == ASCHK_MAGIC)) { if (f->magic == ASCHK_MAGIC) state.formatin = ASCHK; f->checksum = lst.checksum; f->st->st_dev = makedev(lst.dev_major, lst.dev_minor); f->st->st_ino = lst.ino; f->st->st_mode = lst.mode; f->st->st_uid = lst.uid; f->st->st_gid = lst.gid; f->st->st_nlink = lst.nlink; f->st->st_rdev = makedev(lst.rdev_major, lst.rdev_minor); f->st->st_mtime = lst.mtime; f->st->st_size = lst.size; goto cpio_common; } bunread(state.tmp.buffer, ASC_HEADER); break; case PORTAR: case RANDAR: if (bread(&portar_header, 0L, (long)PORTAR_HEADER, 0) <= 0) return(0); if (strneq(portar_header.ar_fmag, PORTAR_END, PORTAR_ENDSIZ) && sscanf(portar_header.ar_date, "%12ld%6ld%6ld%8lo%10ld", &lst.mtime, &lst.uid, &lst.gid, &lst.mode, &lst.size) == 5) { f->name = portar_header.ar_name; portar_header.ar_date[0] = 0; if ((s = strchr(f->name, PORTAR_TERM)) || (s = strchr(f->name, RANDAR_TERM))) *s = 0; f->st->st_dev = 0; f->st->st_ino = 0; f->st->st_mode = S_IFREG|(lst.mode&07777); f->st->st_uid = lst.uid; f->st->st_gid = lst.gid; f->st->st_nlink = 1; f->st->st_rdev = 0; f->st->st_mtime = lst.mtime; f->st->st_size = lst.size; f->linktype = NOLINK; f->linkname = 0; f->uidname = 0; f->gidname = 0; goto found; } bunread(&portar_header, PORTAR_HEADER); return(0); default: error(PANIC, "%s: incomplete input format implementation", format[state.formatin].name); break; } if (state.entry == 1) for (;;) switch (state.formatin) { case BINARY: state.formatin = USTAR; goto again; case CPIO: state.formatin = BINARY; state.swap = 0; if (bread(&binary_header.magic, 0L, (long)sizeof(binary_header.magic), 0) <= 0) break; bunread(&binary_header.magic, sizeof(binary_header.magic)); if (binary_header.magic == CPIO_MAGIC) goto again; state.swap = BYTE; magic = binary_header.magic; memswap(state.swap, (char*)&magic, sizeof(magic)); #if DEBUG message(-1, "binary_header.magic=0%05o swap(BYTE)=0%05o", (unsigned short)binary_header.magic, (unsigned short)magic); #endif if (magic == CPIO_MAGIC) { #if DEBUG message(-1, "swapping %s header bytes", format[state.formatin].name); #endif goto again; } break; case TAR: case USTAR: state.formatin = ASC; goto again; case ASC: case ASCHK: if (state.entries > 0 && state.volume > 1) { if (--state.volume > 1) error(1, "junk data after volume %d", state.volume); finish(0); } if (!state.keepgoing) error(3, "%s: unknown input format", state.file); state.formatin = IN_DEFAULT; goto skip; } skip: i = 3; if (state.keepgoing && bread(namebuffer, 0L, 1L, 0) > 0) { if (warned) continue; warned = 1; i = 1; } error(i, "entry %d.%d is out of phase", state.volume, state.entry); if (state.entry > 1) state.entry++; } found: if (checkdelta) { checkdelta = 0; i = 0; #if new_delta_format if (!f->st->st_size && !f->st->st_dev && !f->st->st_ino && !(f->st->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) && (s = f->name) && *s++ == INFO_SEP && *(s + strlen(s) - 1) == INFO_SEP) { if (strneq(s, ID, IDLEN) && (s = strchr(s, INFO_SEP))) { deltaverify: switch (*++s) { case TYPE_COMPRESS: typename = "compressed "; break; case TYPE_DELTA: typename = "delta "; break; default: error(3, "type %c encoding not supported", *s); } if (*++s != VERSION) error(3, "version %c encoding not supported", *s); /* * NOTE: [<INFO_SEP><OP><VAL>]* may appear here */ i = 1; } else { if (s = strchr(f->name + 3, INFO_SEP)) *s = 0; error(1, "unknown %s header ignored", f->name + 3); goto again; } } #else if (!f->st->st_size && !f->st->st_dev && !f->st->st_ino && !(f->st->st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) && streq(f->name, "DELTA!!!")) { deltaverify: typename = "delta "; i = 1; } #endif n = (state.delta.op & (COLLECT|IN)) == IN; #if DEBUG message(-5, "checkdelta: %sop=%d i=%d n=%d size=%ld:%ld LO=%d:%d HI=%d:%d", typename, state.delta.op, i, n, f->st->st_mtime, state.delta.size, DELTA_LO(f->st->st_uid), DELTA_LO(state.delta.checksum), DELTA_LO(f->st->st_gid), DELTA_HI(state.delta.checksum)); #endif if (i != n) { if (n) error(3, "%s: not a delta archive", state.file); else if (!(state.delta.op & COLLECT)) { if (*typename == 'd') error(state.list ? 1 : 3, "%s: base archive must be specified", state.file); /*HUH*/if (!(state.delta.op & IN)) deltabase(); } } if (i) { if (n) { if ((i = f->st->st_mtime != state.delta.size) || DELTA_LO(f->st->st_uid) != DELTA_LO(state.delta.checksum) || DELTA_LO(f->st->st_gid) != DELTA_HI(state.delta.checksum)) error(i ? 3 : 1, "base archive %s mismatch", i ? "size" : "checksum"); } if (!f->st->st_size) goto again; } } state.entries++; f->path = strcpy(pathbuffer, f->name); (void)pathcanon(f->name); f->name = map(f->name); f->namesize = strlen(f->name) + 1; if (f->linkname) { (void)pathcanon(f->linkname); if (!(state.ftwflags & FTW_PHYSICAL)) f->linkname = map(f->linkname); f->linknamesize = strlen(f->linkname) + 1; } else f->linknamesize = 0; if (f->uidname || f->gidname) setidnames(f); f->type = f->st->st_mode & S_IFMT; getdeltaheader(f); #if DEBUG if (state.trace) { s = &state.tmp.buffer[0]; if (f->record.format) (void)sprintf(s, " [%c,%d,%d]", f->record.format, state.blocksize, state.record.size); else *s = 0; message(-1, "path=%s name=%s entry=%d.%d size=%ld%s", f->path, f->name, state.volume, state.entry, f->st->st_size, s); } #endif if (state.entry == 1) { if (state.delta.op & COLLECT) { if (state.in.blocked) error(3, "%s: blocked base archives are not supported", state.delta.base); switch (state.formatin) { case ALAR: case IBMAR: #if SAVESET case SAVESET: #endif error(3, "%s: %s format base archives not supported", state.delta.base, format[state.formatin].name); } } if (state.summary && state.verbose) { if (state.in.blok) fprintf(stderr, "BLOK "); if (state.delta.op & COLLECT) fprintf(stderr, "base"); else fprintf(stderr, "volume %d", state.volume); if (state.id.volume[0]) fprintf(stderr, " label %s", state.id.volume); fprintf(stderr, " in %s%s format", typename, format[state.formatin].name); if (state.trace) { if (*state.id.format) fprintf(stderr, " %s", state.id.format); if (*state.id.implementation) fprintf(stderr, " implementation %s", state.id.implementation); } fprintf(stderr, "\n"); } } state.delta.sum++; return(1); } /* * write next archive entry header */ void putheader(f) register struct fileinfo* f; { register char* s; register int n; if (f->delta.op) { s = state.delta.hdrbuf; #if new_delta_format *s++ = f->delta.op; *s++ = VERSION; *s++ = 0; state.delta.hdr = s; adddelnum('s', f->delta.size); *state.delta.hdr++ = 0; #else *s++ = f->delta.op; *s++ = '2'; *s++ = '\n'; state.delta.hdr = s; #endif n = state.delta.hdr - state.delta.hdrbuf; f->st->st_size += n; f->linknamesize += n; } if (state.complete) { unsigned long c = f->st->st_size; switch (state.formatout) { case ALAR: case IBMAR: case PAX: c += 4 * ALAR_HEADER; break; case ASC: case ASCHK: c += ASC_HEADER + f->namesize; break; case BINARY: c += BINARY_HEADER + f->namesize; c = round(c, 2); break; case CPIO: c += CPIO_HEADER + f->namesize; break; case TAR: case USTAR: c += TAR_HEADER; c = round(c, BLOCKSIZE); break; } if (state.out.count + c > state.maxout) { if (c > state.maxout) error(1, "%s: too large to fit in one volume", f->name); else { state.complete = 0; putepilogue(); newio(1, 0, 0); putprologue(); state.complete = 1; } } } switch (state.formatout) { case ALAR: case IBMAR: case PAX: putlabels(f, "HDR"); break; case BINARY: binary_header.magic = CPIO_MAGIC; binary_header.namesize = f->namesize; cpio_short(binary_header.size, f->type == S_IFLNK ? (long)f->linknamesize : f->st->st_size); binary_header.dev = f->st->st_dev; binary_header.ino = f->st->st_ino; binary_header.mode = cpio_mode(f); binary_header.uid = f->st->st_uid; binary_header.gid = f->st->st_gid; binary_header.links = f->st->st_nlink; binary_header.rdev = f->st->st_rdev; if (binary_header.rdev != f->st->st_rdev) error(1, "%s: special device numbers truncated", f->name); cpio_short(binary_header.mtime, (long)f->st->st_mtime); bwrite(&binary_header, BINARY_HEADER); bwrite(f->name, f->namesize); if (n = (BINARY_HEADER + f->namesize) % BINARY_ALIGN) while (n++ < BINARY_ALIGN) bwrite("", 1); if (f->type == S_IFLNK) { cpio_link: putdeltaheader(f); if (streq(f->name, f->linkname)) error(1, "%s: symbolic link loops to self", f->name); bwrite(f->linkname, f->linknamesize); } break; case CPIO: #if CPIO_EXTENDED if (!f->skip) { getidnames(f); addxopstr('U', f->uidname); addxopstr('G', f->gidname); if (CPIO_TRUNCATE(f->st->st_rdev) != f->st->st_rdev) addxopnum('d', f->st->st_rdev); #if NUMBER_EVEN_THOUGH_NAME if (CPIO_TRUNCATE(f->st->st_gid) != f->st->st_gid) addxopnum('g', f->st->st_gid); if (CPIO_TRUNCATE(f->st->st_uid) != f->st->st_uid) addxopnum('u', f->st->st_uid); #endif setxops(f); } #else if (CPIO_TRUNCATE(f->st->st_rdev) != f->st->st_rdev) error(1, "%s: special device number truncated", f->name); if (CPIO_TRUNCATE(f->st->st_gid) != f->st->st_gid) error(1, "%s: gid number truncated", f->name); if (CPIO_TRUNCATE(f->st->st_uid) != f->st->st_uid) error(1, "%s: uid number truncated", f->name); #endif (void)sprintf(state.tmp.buffer, "%.6o%.6o%.6o%.6o%.6o%.6o%.6o%.6o%.11lo%.6o%.11lo", CPIO_TRUNCATE(CPIO_MAGIC), CPIO_TRUNCATE(f->st->st_dev), CPIO_TRUNCATE(f->st->st_ino), CPIO_TRUNCATE(cpio_mode(f)), CPIO_TRUNCATE(f->st->st_uid), CPIO_TRUNCATE(f->st->st_gid), CPIO_TRUNCATE(f->st->st_nlink), CPIO_TRUNCATE(f->st->st_rdev), f->st->st_mtime, f->namesize, f->type == S_IFLNK ? f->linknamesize : f->st->st_size); bwrite(state.tmp.buffer, CPIO_HEADER); #if CPIO_EXTENDED putxops(f); #else bwrite(f->name, f->namesize); #endif if (f->type == S_IFLNK) goto cpio_link; break; case ASC: f->checksum = 0; /*FALLTHROUGH*/ case ASCHK: (void)sprintf(state.tmp.buffer, "%.6o%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx%.8lx", state.formatout == ASC ? ASC_MAGIC : ASCHK_MAGIC, (long)f->st->st_ino, (long)cpio_mode(f), (long)f->st->st_uid, (long)f->st->st_gid, (long)f->st->st_nlink, (long)f->st->st_mtime, f->type == S_IFLNK ? (long)f->linknamesize : (long)f->st->st_size, (long)major(f->st->st_dev), (long)minor(f->st->st_dev), (long)major(f->st->st_rdev), (long)minor(f->st->st_rdev), (long)f->namesize, f->checksum); bwrite(state.tmp.buffer, ASC_HEADER); bwrite(f->name, f->namesize); if (n = (ASC_HEADER + f->namesize) % ASC_ALIGN) while (n++ < ASC_ALIGN) bwrite("", 1); if (f->type == S_IFLNK) goto cpio_link; break; case TAR: case USTAR: message(-1, "tar_header#1: name=%s namesize=%d max=%d", f->name, f->namesize, sizeof(tar_header.name)); memset(tar_block, 0, TAR_HEADER); if (f->namesize > sizeof(tar_header.name) + 1) { (void)sprintf(tar_header.prefix, "%-.*s", sizeof(tar_header.prefix), f->name); s = f->name + sizeof(tar_header.prefix); } else s = f->name; (void)sprintf(tar_header.name, "%s%s", s, f->type == S_IFDIR ? "/" : ""); switch (f->linktype) { case HARDLINK: tar_header.typeflag = LNKTYPE; linked: (void)sprintf(tar_header.linkname, "%s", f->linkname); break; case SOFTLINK: tar_header.typeflag = SYMTYPE; goto linked; default: switch (state.formatout == USTAR ? f->type : S_IFREG) { case S_IFCHR: tar_header.typeflag = CHRTYPE; special: (void)sprintf(tar_header.devmajor, "%*o ", sizeof(tar_header.devmajor) - 2, major(f->st->st_rdev)); (void)sprintf(tar_header.devminor, "%*o ", sizeof(tar_header.devminor) - 2, minor(f->st->st_rdev)); break; case S_IFBLK: tar_header.typeflag = BLKTYPE; goto special; case S_IFDIR: tar_header.typeflag = DIRTYPE; break; case S_IFIFO: tar_header.typeflag = FIFOTYPE; break; case S_IFSOCK: tar_header.typeflag = SOKTYPE; goto special; default: if (!f->skip) error(1, "%s: unknown file type 0%03o -- assuming regular file", f->name, f->type >> 12); /*FALLTHROUGH*/ case S_IFREG: tar_header.typeflag = REGTYPE; break; } break; } (void)sprintf(tar_header.mode, "%*o ", sizeof(tar_header.mode) - 2, f->st->st_mode & 07777); (void)sprintf(tar_header.uid, "%*o ", sizeof(tar_header.uid) - 2, f->st->st_uid); (void)sprintf(tar_header.gid, "%*o ", sizeof(tar_header.gid) - 2, f->st->st_gid); (void)sprintf(tar_header.size, "%*lo ", sizeof(tar_header.size) - 1, f->st->st_size); (void)sprintf(tar_header.mtime, "%*lo ", sizeof(tar_header.mtime) - 2, f->st->st_mtime); if (state.formatout == USTAR) { (void)strncpy(tar_header.magic, TMAGIC, sizeof(tar_header.magic)); (void)strncpy(tar_header.version, TVERSION, sizeof(tar_header.version)); getidnames(f); (void)strcpy(tar_header.uname, f->uidname); (void)strcpy(tar_header.gname, f->gidname); } (void)sprintf(tar_header.chksum, "%*o ", sizeof(tar_header.chksum) - 2, tar_checksum()); bwrite(tar_block, TAR_HEADER); break; } putdeltaheader(f); } /* * read entry trailer */ /*ARGSUSED*/ void gettrailer(f) struct fileinfo* f; { register int n; if (state.delta.sum-- > 0) { state.delta.checksum ^= state.delta.membersum; state.delta.membersum = 0; } if ((n = format[state.formatin].align) && (n = round(state.in.count, n) - state.in.count)) (void)bread(state.tmp.buffer, 0L, (long)n, 1); } /* * write entry trailer */ void puttrailer(f) register struct fileinfo* f; { register int n; switch (state.formatout) { case ALAR: case IBMAR: case PAX: putlabels(f, "EOF"); break; } if ((n = format[state.formatout].align) && (n = round(state.out.count, n) - state.out.count)) { memset(state.tmp.buffer, 0, n); bwrite(state.tmp.buffer, n); } listentry(f); } /* * return length of next label * variable length labels have label number > 3 and Vnnnn at position 5 * where nnnn is the decimal length of the entire label * nnnn may be < ALAR_HEADER but label block must be >= ALAR_HEADER * 0 returned at end of label group */ int getlabel(f) register struct fileinfo* f; { register int c; register int n; static char last[5]; static int done; if (done || (c = bread(alar_header, (long)ALAR_HEADER, (long)ALAR_LABEL, 0)) < ALAR_HEADER) return(*last = done = c = 0); if (state.formatin == IBMAR) cvtetoa(alar_header, alar_header, ALAR_HEADER); if (alar_header[4] == 'V' && ((n = getlabnum(alar_header, 4, 1, 10)) < 1 || n > 3) && (n = getlabnum(alar_header, 6, 4, 10)) != c) { if ((c = n - c) > 0) { if (state.in.blocked || bread(alar_header + ALAR_HEADER, 0L, (long)c, 1) != c) { c = ALAR_HEADER; error(2, "%s: %-*.*s: variable length label record too short", f->name, c, c, alar_header); } else { if (state.formatin == IBMAR) cvtetoa(alar_header + ALAR_HEADER, alar_header + ALAR_HEADER, c); c = n; } } else if (n <= ALAR_VARHDR) c = ALAR_VARHDR; else c = n; } if (!state.in.blocked && !*last && alar_header[3] == '2' && (strneq(alar_header, "HDR", 3) || strneq(alar_header, "EOF", 3) || strneq(alar_header, "EOV", 3))) getlabstr(alar_header, 26, 4, last); if (*last && strneq(alar_header, last, 4)) done = 1; #if DEBUG message(-4, "label: %-*.*s", c, c, alar_header); #endif return(c); } /* * output file HDR/EOF/EOV labels */ void putlabels(f, type) register struct fileinfo* f; char* type; { register int n; char* s; struct tm* tm; static int section = 1; static int sequence; switch (*type) { case 'E': bwrite(alar_header, 0); break; case 'H': sequence++; break; } tm = gmtime(&f->st->st_mtime); (void)sprintf(alar_header, "%s1%-17.17s000001%04d%04d000100 %02d%03d 00000 %06d%-6.6s%-7.7s ", type, f->id, section, sequence, tm->tm_year, tm->tm_yday, f->record.blocks, state.id.format, state.id.implementation); if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER); bwrite(alar_header, ALAR_HEADER); (void)sprintf(alar_header, "%s2%c%05d%05d%010d%s%c 00 ", type, state.record.format, state.blocksize, state.record.size, f->st->st_size, type, state.formatout == PAX && *type == 'H' ? '5' : '2'); if (state.formatout == IBMAR) cvtatoe(alar_header, alar_header, ALAR_HEADER); bwrite(alar_header, ALAR_HEADER); if (state.formatout == PAX && *type == 'H') { getidnames(f); s = fmtmode(f->st->st_mode); if (f->linktype == HARDLINK) *s = 'z'; (void)sprintf(alar_header, "%s3%-8.8s%-8.8s%-10.10s%010d%010d%010d%010d ", type, f->uidname, f->gidname, s, f->st->st_mtime, f->st->st_ctime, f->st->st_atime, f->st->st_size); bwrite(alar_header, ALAR_HEADER); n = ALAR_HEADER; if (f->linkname) { if ((n = f->linknamesize + 8) < ALAR_HEADER) n = ALAR_HEADER; (void)sprintf(alar_header, "%s4V%04d%-80s", type, f->linknamesize + 8, f->linkname); } else switch (f->type) { case S_IFBLK: case S_IFCHR: (void)sprintf(alar_header, "%s4%08o%08o%-15.15s ", type, major(f->st->st_rdev), minor(f->st->st_rdev), SYSTEM); break; case S_IFDIR: case S_IFREG: case S_IFIFO: (void)sprintf(alar_header, "%s4 ", type); break; default: (void)sprintf(alar_header, "%s4V%04d ", type, ALAR_VARHDR); break; } bwrite(alar_header, n); if ((n = f->namesize + 8) < ALAR_HEADER) n = ALAR_HEADER; (void)sprintf(alar_header, "%s5V%04d%-80s", type, f->namesize + 8, f->name); bwrite(alar_header, n); } bwrite(alar_header, 0); if (streq(type, "EOV")) { section++; sequence = 0; } else section = 1; } #ifdef SAVESET /* * get next saveset record * if header!=0 then all records skipped until REC_file found * otherwise REC_vbn cause non-zero return * 0 returned for no record match */ int getsaveset(f, header) register struct fileinfo* f; int header; { register char* p; register char* s; char* t; int i; long n; for (;;) { state.saveset.bp += state.saveset.lastsize; while (state.saveset.bp >= state.saveset.block + state.blocksize) { state.saveset.bp = state.saveset.block; state.saveset.lastsize = 0; if (bread(state.saveset.bp, 0L, (long)state.blocksize, 0) <= 0) { state.formatin = ALAR; if (header) gettrailer(f); return(0); } if (gethalf(BYTE|HALF, state.saveset.bp + BLKHDR_hdrsiz) != BLKHDR_SIZE) error(3, "invalid %s format block header", format[state.formatin].name); if (!(n = getlong(BYTE|HALF, state.saveset.bp + BLKHDR_blksiz))) state.saveset.bp += state.blocksize; else if (n != state.blocksize) error(3, "invalid %s format blocksize=%ld", format[state.formatin].name, n); state.saveset.bp += BLKHDR_SIZE; } state.saveset.lastsize = gethalf(BYTE|HALF, state.saveset.bp + RECHDR_size); i = gethalf(BYTE|HALF, state.saveset.bp + RECHDR_type); state.saveset.bp += RECHDR_SIZE; #if DEBUG message(-2, "record: type=%d size=%d", i, state.saveset.lastsize); #endif if (i == REC_file) { if (header) { p = state.saveset.bp; if (gethalf(BYTE|HALF, p) != FILHDR_MAGIC) error(3, "invalid %s format file header", format[state.formatin].name); p += 2; i = gethalf(BYTE|HALF, p + FILHDR_size); if (p + FILHDR_data + i > state.saveset.block + state.blocksize) error(3, "invalid %s format file attribute", format[state.formatin].name); t = f->name; n = 0; for (s = p + FILHDR_data + 1; s < p + FILHDR_data + i; s++) { if (isupper(*s)) *t++ = tolower(*s); else if (n) { if (*s == ';') break; *t++ = *s; } else if (*s == ']') { n = 1; *t++ = '/'; } else if (*s == '.') *t++ = '/'; else *t++ = *s; } *t = 0; for (i = 0; i < 5; i++) { s = p + FILHDR_size; if ((p += FILHDR_SIZE + gethalf(BYTE|HALF, s)) > state.saveset.block + state.blocksize) error(3, "invalid %s format file attribute", format[state.formatin].name); } state.saveset.recatt = getbyte(BYTE|HALF, p + FILHDR_data + FILATT_recfmt); state.saveset.recfmt = getbyte(BYTE|HALF, p + FILHDR_data + FILATT_recfmt); state.saveset.reclen = gethalf(BYTE|HALF, p + FILHDR_data + FILATT_reclen); state.saveset.recvfc = gethalf(BYTE|HALF, p + FILHDR_data + FILATT_recvfc); f->st->st_size = (long)(gethalf(BYTE|HALF, p + FILHDR_data + FILATT_blocks) - 1) * BLOCKSIZE + (long)gethalf(BYTE|HALF, p + FILHDR_data + FILATT_frag); for (; i < 15; i++) { s = p + FILHDR_size; if ((p += FILHDR_SIZE + gethalf(BYTE|HALF, s)) > state.saveset.block + state.blocksize) error(3, "invalid %s format file attribute", format[state.formatin].name); } /* * pure guesswork based on 100 nsec epoch * 17-NOV-1858 00:00:00 GMT */ if ((n = getlong(BYTE|HALF, p + FILHDR_data + 4) - 7355000) < 0) n = 1; else n = (n << 8) + getbyte(BYTE|HALF, p + FILHDR_data + 3); f->st->st_mtime = (time_t)n; return(1); } state.saveset.bp -= RECHDR_SIZE; state.saveset.lastsize = 0; return(0); } else if (i == REC_vbn && !header) return(1); } } #endif #if CPIO_EXTENDED static char opsbuf[PATH_MAX]; /* extended ops buffer */ static char* ops = opsbuf; /* opsbuf output pointer */ /* * get and execute extended ops from input */ static void getxops(f) register struct fileinfo* f; { register char* p; register char* s; register int c; long n; if (f->namesize > (n = strlen(f->name) + 1)) for (p = f->name + n; c = *p++;) { for (s = p; *p; p++); p++; n = strtol(s, (char*)0, 16); #if DEBUG message(-2, "%s: entry %d.%d op = %c%s", f->name, state.volume, state.entry, c, s); #endif switch (c) { case 'd': f->st->st_rdev = n; break; case 'g': f->st->st_gid = n; break; case 'u': f->st->st_uid = n; break; case 'G': f->gidname = s; break; case 'U': f->uidname = s; break; /* * NOTE: ignore unknown ops for future extensions */ } } } /* * set end of extended ops */ static void setxops(f) register struct fileinfo* f; { register int n; if (n = ops - opsbuf) { n++; *ops++ = 0; if ((f->namesize += n) > CPIO_NAMESIZE) error(1, "%s: extended ops may crash older cpio programs", f->name); } } /* * output filename and extended ops */ static void putxops(f) register struct fileinfo* f; { register int n; n = ops - opsbuf; bwrite(f->name, f->namesize -= n); if (n) bwrite(ops = opsbuf, n); } /* * add extended op string */ static void addxopstr(op, s) int op; register char* s; { register char* p = ops; register char* e = opsbuf + sizeof(opsbuf) - 3; if (p < e) { *p++ = op; while (*s && p < e) *p++ = *s++; *p++ = 0; ops = p; } #if DEBUG if (*s) error(PANIC, "addxopstr('%c',\"%s\") overflow", op, s); #endif } /* * add numeric op for current entry */ static void addxopnum(op, n) int op; long n; { char buf[17]; (void)sprintf(buf, "%x", n); addxopstr(op, buf); } #endif 0707070000000000131006440044230044230000010000000471771643500002200000013622misc.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax miscellaneous support */ #include "pax.h" /* * return format index given format name */ int getformat(name) register char* name; { register int i; (void)strlower(name); for (i = 0; format[i].name; i++) if (streq(name, format[i].name)) break; return(format[i].name ? i : streq(name, "-") || streq(name, "pax") ? OUT_DEFAULT : -1); } /* * check f with patterns given on cmd line */ int selectfile(f) register struct fileinfo* f; { register struct deltainfo* d; if (f->skip || f->namesize <= 1) return(0); if (f->record.format && state.record.pattern) { static char fmt[2]; fmt[0] = f->record.format; if (!strmatch(fmt, state.record.pattern)) return(0); } if (state.append || (state.delta.op & COLLECT)) { (void)addlink(f); if (state.delta.tab) { if (!(d = allocate(struct deltainfo))) error(3, "%s: out of space", f->name); d->dev = f->st->st_dev; d->ino = f->st->st_ino; d->mtime = f->st->st_mtime; d->offset = state.in.offset + state.in.count; d->size = f->st->st_size; if (state.delta.op & COLLECT) { if (!(d->info = (struct fileinfo*)memdup(f, sizeof(struct fileinfo))) || !(d->info->st = (struct stat*)memdup(f->st, sizeof(struct stat)))) error(3, "%s: out of space", f->name); d->info->name = hashput(state.delta.tab, f->name, (char*)d); if (d->info->uidname) d->info->uidname = strdup(d->info->uidname); if (d->info->gidname) d->info->gidname = strdup(d->info->gidname); d->info->delta.base = d; } else (void)hashput(state.delta.tab, f->name, (char*)d); } return(0); } if (!match(f->name) || state.verify && f->type != S_IFDIR && !verify(f)) return(0); state.selected++; if (state.list) (void)addlink(f); return(1); } /* * verify action on file * * EOF exit * NULL skip file * . keep file * <else> rename file */ int verify(f) register struct fileinfo* f; { register char* prompt; register int n; static char namebuffer[PATH_MAX + 2]; if (state.yesno) switch (state.operation) { case IN: prompt = "Read"; break; case OUT: prompt = "Write"; break; default: prompt = "Pass"; break; } else prompt = "Rename"; fprintf(state.wtty, "%s %s: " , prompt, f->name); if (!fgets(namebuffer, sizeof(namebuffer), state.rtty)) { putc('\n', state.wtty); finish(2); } n = strlen(namebuffer); namebuffer[n - 1] = 0; if (state.yesno) return(namebuffer[0] == 'y'); switch (namebuffer[0]) { case 0: return(0); case '.': if (!namebuffer[1]) break; /*FALLTHROUGH*/ default: f->namesize = pathcanon(f->name = namebuffer) - namebuffer + 1; break; } return(1); } /* * check for file name mapping * static data possibly returned * two simultaneous calls supported */ char* map(name) register char* name; { register struct maplist* mp; static char filebuffer[2][PATH_MAX]; static char* filename = filebuffer[0]; filename = (filename == filebuffer[0]) ? filebuffer[1] : filebuffer[0]; for (mp = state.maps; mp; mp = mp->next) if (reexec(mp->re, name)) { (void)resub(mp->re, name, mp->into, filename, mp->flags); if (mp->flags & RE_VERBOSE) fprintf(stderr, "%s >> %s\n", name, filename); return(filename); } return(name); } /* * list entry information based on state.drop, state.list and state.verbose */ void listentry(f) register struct fileinfo* f; { register char* s; long size; char buf[PATH_MAX]; if (!f->skip && (state.drop || state.list || state.verbose)) { if (state.drop) { if (++state.dropcount >= 50) { state.dropcount = 0; fprintf(stderr, ".\n"); } else { fprintf(stderr, "."); fflush(stderr); } } else { switch (f->delta.op) { case 0: s = 0; break; case DELTA_create: s = "create"; break; case DELTA_delete: s = "delete"; break; case DELTA_update: s = "update"; break; case DELTA_verify: s = "verify"; break; default: s = "??????"; break; } size = f->st->st_size; if (f->linktype == SOFTLINK) f->st->st_size = f->linknamesize - 1; else if (f->delta.size != -1) f->st->st_size = f->delta.size; (void)fmtls(buf, f->name, f->st, s, f->linktype == NOLINK ? (char*)0 : f->linkname, (state.list && state.verbose) ? LS_LONG : 0); f->st->st_size = size; fprintf(state.list ? stdout : stderr, "%s\n", buf); } } } /* * prepare patterns for match() */ char** initmatch(p) char** p; { register char** a; a = p; while (*a) (void)pathcanon(*a++); return(p); } /* * determine if file s matches input patterns */ int match(s) register char* s; { register char** p; register char* t; int n; if (!(p = state.patterns)) return(state.matchsense); if (state.exact) { n = 0; while (t = *p++) if (*t) { if (streq(s, t)) { *--p = ""; return(1); } n = 1; } if (!n) finish(0); } else while (t = *p++) { if (dirprefix(t, s) || strmatch(s, t)) return(state.matchsense); } return(!state.matchsense); } /* * return 1 if p is a directory prefix of s */ int dirprefix(p, s) register char* p; register char* s; { while (*p) if (*p++ != *s++) return(0); return(!*s || *s == '/'); } #if DEBUG /* * place op names from op into buf */ static char* opnames(p, name, op) register char* p; char* name; register int op; { register char* s; register int n; static struct { char* name; int op; } ops[] = { "IN", IN, "OUT", OUT, "COLLECT", COLLECT, "COMPRESS", COMPRESS, "CONVERT", CONVERT, }; extern char* strcopy(); if (name) { p = strcopy(p, name); *p++ = '='; } s = p; for (n = 0; n < elements(ops); n++) if (op & ops[n].op) { if (s != p) *p++ = '|'; p = strcopy(p, ops[n].name); } return(p); } /* * return the current operation names */ char* operations() { static char buf[256]; (void)sprintf(opnames(opnames(buf, "operation", state.operation), " delta", state.delta.op), " sum=%d", state.delta.sum); return(buf); } #endif 0707070000000000141006440044230044230000010000000470731313600002100000032544pax.1 Ugsf Ggsf .\" .\" G. S. Fowler .\" AT&T Bell Laboratories .\" .\" @(#)pax.1 (ulysses!gsf) 01/11/90 .\" .TH PAX 1 .SH NAME pax \- portable archive interchange .SH SYNOPSIS .B pax [ .B \-rmnov ] [ .B \-f .I archive ] [ .B \-s .RI / old / new /[gp] ] [ .I "pattern ..." ] .LP .B pax .B \-w [ .B mv ] [ .B \-b .I blocking ] [ .B \-f .I archive ] [ .B \-s .RI / old / new /[gp] ] [ .B \-x .I format ] [ .I "pathname ..." ] .LP .B pax .B \-rw [ .B mov ] [ .B \-s .RI / old / new /[gp] ] [ .I "pathname ..." ] .I directory .SH DESCRIPTION .I pax reads and writes archive files in various formats. There are four operation modes controlled by combinations of the .B \-r and .B \-w options. .PP .B "pax \-w" writes the files and directories named by the .I pathname arguments to the standard output together with pathname and status information. A directory .I pathname argument refers to the files and (recursively) subdirectories of that directory. If no .I pathname arguments are given then the standard input is read to get a list of pathnames to copy, one pathname per line. In this case only those pathnames appearing on the standard input are copied. .PP .B "pax \-r" reads files from the standard input that is assumed to be the result of a previous .B "pax \-w" command. Only files with names that match any of the .I pattern arguments are selected. A .I pattern is given in the name-generating notation of .IR sh (1), except that the .B / character is also matched. The default if no .I pattern is given is .BR * , which selects all files. The selected files are conditionally created and copied relative to the current directory tree, subject to the options described below. By default the owner and group of selected files will be that of the current user, and the permissions and modify times will be the same as those in the archive. If the .B \-r option is omitted then a table of contents of the selected files is listed on the standard output. .PP .B "pax \-rw" reads the files and directories named in the .I pathname arguments and copies them to the destination .IR directory . A directory .I pathname argument refers to the files and (recursively) subdirectories of that directory. If no .I pathname arguments are given then the standard input is read to get a list of pathnames to copy, one pathname per line. In this case only those pathnames appearing on the standard input are copied. .I directory must exist before the copy. .PP The standard archive formats are automatically detected on input. The default output archive format is implementation defined, but may be overridden by the .B \-x option described below. .I pax archives may be concatenated to combine multiple volumes on a single tape or file. This is accomplished by forcing any format prescribed pad data to be null bytes. Hard links are not maintained between volumes, and delta and base archives cannot be multi-volume. .PP A single archive may span many files/devices. The second and subsequent file names are prompted for on the terminal input. The response may be: .TP .BI ! command Execute .I command via .IR system (3) and prompt again for file name. .TP .B EOF Exit without further processing. .TP .B CR An empty input line retains the previous file name. .TP .I pathname The file name for the next archive part. .SS "Basic Options" These options support basic archive operations. .TP .BI b " blocking" Set the output blocking size. If no suffix (or a .B c suffix) is specified then .I blocking is in 1 character units. A .B b suffix multiplies .I blocking by 512 (1 block), a .B k suffix multiplies .I blocking by 1024 (1 kilobyte) and an .B m suffix multiplies .I blocking by 1048576 (1 megabyte). .I blocking is automatically determined on input and is ignored for .BR \-rw . The default .I blocking is .B 10k for block and character special archive files and implementation defined otherwise. The minimum .I blocking is .BR 1c . .TP .BI f " archive" .I archive is the pathname of the input or output archive, overriding the default standard input for .B \-r and .B \-rw or standard output for .BR \-w . .TP .B m File modification times are not retained. .TP .B n For .B \-r the pattern arguments are treated as ordinary file names. Only the first occurrence of each of these files in the input archive is read. .I pax exits with zero exit status after all files in the list have been read. If one or more files in the list is not found, .I pax writes a message to standard error for each of these files and exits with a non-zero exit status. The file names are compared before any of the .B \-i, .B \-s, or .B \-y options are applied. .TP .B o Restore file ownership as specified in the archive. The current user must have appropriate privileges. .TP \fBs\fP /\fIold\fP/\fInew\fP/[\fIglpu\fP] File names and symbolic link text are mapped according to the .IR ed (1) style substitution expression. Any non-null character may be used as a delimiter .RB ( / shown here). Multiple .B \-s expressions may be specified; the expressions are applied from left to right, terminating with the first successful substitution. A trailing .B l converts the matched string to lower case. A trailing .B p causes successful mappings to be listed on the standard error. A trailing .B u converts the matched string to upper case. File names that substitute to the null string are ignored on both input and output. The .B \-P option inhibits symbolic link text substitution. .TP .B v Produces a verbose table of contents listing on the standard output when both .B \-r and .B \-w are omitted. Otherwise the file names are listed on the standard error as they are encountered. .TP .BI x " format" Specifies the output archive .IR format . If specified with .B \-rw then the standard input is treated as an archive that is converted to a .I format archive on the standard output. The input format, which must be one of the following, is automatically determined. The default output format, named by .BR \- , is .BR cpio . The formats are: .RS .PD 0 .TP .B asc The .B s5r4 extended .IR cpio (5) character format. .TP .B ansi ANSI standard label tape format. Only regular files with simple pathnames are archived. Valid only for blocked devices. .TP .B asc The .B s5r4 extended .IR cpio (5) character format. .TP .B aschk The .B s5r4 extended .IR cpio (5) character format with header checksum. This format is misnamed .B crc in the .B s5r4 documentation. .TP .B binary The .IR cpio (5) binary format with symbolic links. This format is obsolete and should not be used on output. .TP .B cpio The .IR cpio (5) character format with symbolic links. This is the default output format. .TP .B ibmar EBCDIC standard label tape format. Only regular files with simple pathnames are archived. Valid only for tape devices. .TP .B posix The IEEE 1003.1b-1990 interchange format, partially compatible with the X3.27 standard labeled tape format. .TP .B portarch The svr2 portable object library format. Valid only on input. .TP .B randarch The BSD ranlib object library format. Valid only on input. .TP .B tar The .IR tar (5) format with symbolic links. .TP .B ustar The POSIX IEEE Std 1003.1-1988 tar format. .TP .B vmsbackup ANSI standard label VMS backup savset tape format. Valid only for input tape devices. .PD .RE .SS "Compatibility Options" These options provide functional compatibility with the old .IR cpio (1) and .IR tar (1) commands. .TP .B a For .B \-w append files to the end of the archive. .TP .B c Complement the match sense of the .I pattern arguments. .TP .B d Intermediate directories not explicitly listed in the archive are not created. .TP .B i Interactively .I rename files. A file is skipped if a null line is entered and .I pax exits if .B EOF is encountered. .TP .B l For .BR \-rw , files are linked rather than copied when possible. .TP .B p Preserve the access times of input files after they have been copied. .TP .BI t " device" .I device is an identifier that names the input or output archive device, overriding the default standard input for .B \-r or standard output for .BR \-w . Tape devices may be specified as .IR drive [ density\|rewind ] where .I drive is a drive number in the range [0\-7], .I density is one of .BR l , .B m and .B h for .B low (800 bpi), .B medium (1600 bpi \- default) and .B high (6250 bpi) tape densities and .I rewind is .B n to inhibit rewinding of the tape device when it is closed. Other forms for .I device are implementation defined. .TP .B u Copy each file only if it is newer than a pre-existing file with the same name. This option implies .BR \-a . .TP .B y Interactively prompt for the disposition of each file. .B EOF or an input line starting with .B q causes .I pax to exit. Otherwise an input line starting with anything other than .B y causes the file to be ignored. .SS "Extended Options" These options provide fine archive control, including delta archive operations. .TP .BI e " filter" Run the .I filter command on each file to be output. The current name of the file to be output is appended to the filter command string before the command is executed by the shell. .TP .B h Inhibit archive heading and summmary information messages to stderr. .TP .B k For .B \-r continue processing the archive after encountering an error by attempting to locate the next valid entry. This is useful for archives stored on unreliable media. .TP .BI z " base" Specifies the delta base archive .I base that is assumed to be the result of a previous .B "pax \-w" command. For .B \-w the input files are compared with the files in .I base and file delta information is placed in the output archive using the delta algorithm. For .B \-r the delta information in the input archive is used to update the output files with respect to the files in .IR base . For .B \-rw the delta information in the archive on the standard input is used to generate an archive on the standard output whose entries are updated with respect to the files in .IR base . If .I base is .B \- or an empty file then the input files are simply compressed. .B "\-z -" must also be specified to produce a compressed archive for .BR \-rw . .TP .BI B " count" Sets the maximum archive part output character count. .I pax prompts for the next archive part file name. Valid only with .BR \-w . .TP .B C Archive entries smaller than .BI \-B " maxblocks" must be contained within a single part. Valid only with .BR \-B . .TP .B L Copy a logical view of the input files. Symbolic links are followed, causing the pointed to files to be copied rather than the symbolic link information. This is the default. .TP .BI M " message" Set the .I "end of medium" prompt to .IR message . This message is used to prompt interactively for the next tape reel or cartridge in cases where the tape runs out before all files have been copied. .I message may contain one .IR printf (3) style integer format specification that is replaced with the next part number. .TP .B P Copy a physical view of the input files. Causes symbolic link information to be copied as opposed to the default (logical view) action of following symbolic links and copying the pointed to files. .TP \fBR\fP \fIoption\fP[\fIvalue\fP][,\fIoption\fP[\fIvalue\fP]...] Set record oriented format options. Multiple options may be concatenated using .BR , . Some options may be fixed for some formats. The options are: .RS .PD 0 .TP .B c Record data is subject to character set conversions. .TP .BI f format Set the output record format to .IR format . The supported record formats are: .RS .TP .B D Variable length with 4 byte record header. The record size default is 512. .TP .B F Fixed length with no record header. The record size default is 128. .TP .B S Spanned variable length with 4 byte record header. The record size default is 0 (no limit). .TP .B U Variable length with no record header. The output block size matches the size of each output record. The record size default is 512. .TP .B V Spanned variable length with binary 4 byte record header. The record size default is 0 (no limit). The .B D format is preferred. .PD .RE .TP .BI m pattern Only those files with input record format matching .I pattern are processed. .TP .B p Partial output blocks are padded to the full blocksize. .TP .BI s size Set the output record size to .IR size . .I size should divide the output blocking. .TP .BI v label Set the output volume label to .IR label . Some formats may truncate and/or case-convert .IR label . .PD .RE .TP .B S Similar to .B \-l except that symbolic links are created. .TP .BI U " id" Set file ownership to the default of the user named .IR id . Valid only for the super-user. .TP .B V Output a `.' as each file is encountered. This overrides the .B \-v option. .TP .B X Do not cross mount points when searching for files to output. .SH DIAGNOSTICS The number of files, blocks, and optionally the number of volumes and media parts are listed on the standard error. For .B \-v the input archive formats are also listed on the standard error. .SH EXAMPLES .TP .B "pax \-w \-t 1m ." Copies the contents of the current directory to tape drive 1, medium density. .TP .PD 0 .BI mkdir " newdir" .TP .BI cd " olddir" .TP .BI "pax \-rw ." " newdir" .PD Copies the .I olddir directory hierarchy to .IR newdir . .SH "SEE ALSO" ar(1), cpio(1), find(1), ksh(1), tar(1), tw(1), libdelta(3), cpio(5), tar(5) .SH BUGS Special privileges may be required to copy special files. .br Each archive format has a hard upper limit on member pathname sizes. .br Device, user-id and group-id numbers larger than 65535 cause additional header records to be output. These records are ignored by old versions of .IR cpio (1) and .IR tar (1). 0707070000000000151006440044230044230000010000000430261237100002700000014714pax.1.posix Ugsf Ggsf \" \" G. S. Fowler \" AT&T Bell Laboratories \" \" @(#)pax.1 (ulysses!gsf) 08/08/88 \" .TH PAX 1 .SH NAME pax \- portable archive interchange .SH SYNOPSIS .B pax [ .B \-rcimopuvy ] [ .B \-f .I archive ] [ .B \-s .RI / old / new /[gp] ] [ .B \-t .I device ] [ .I "pattern ..." ] .LP .B pax .B \-w [ .B adimuvy ] [ .B \-b .I blocking ] [ .B \-f .I archive ] [ .B \-s .RI / old / new /[gp] ] [ .B \-t .I device ] [ .B \-x .I format ] [ .I "pathname ..." ] .LP .B pax .B \-rw [ .B ilmopuvy ] [ .B \-s .RI / old / new /[gp] ] [ .I "pathname ..." ] .I directory .SH DESCRIPTION .I pax reads and writes archive files in the .IR cpio (5) and .IR tar (5) formats. There are four operation modes controlled by combinations of the .B \-r and .B \-w options. .PP .B "pax \-w" writes the files and directories named by the .I pathname arguments to the standard output together with pathname and status information. A directory .I pathname argument refers to the files and (recursively) subdirectories of that directory. If no .I pathname arguments are given then the standard input is read to get a list of pathnames to copy, one pathname per line. In this case only those pathnames appearing on the standard input are copied. .PP .B "pax \-r" reads files from the standard input that is assumed to be the result of a previous .B "pax \-w" command. Only files with names that match any of the .I pattern arguments are selected. A .I pattern is given in the name-generating notation of .IR sh (1), except that the .B / character is also matched. The default if no .I pattern is given is .BR * , which selects all files. The selected files are conditionally created and copied relative to the current directory tree, subject to the options described below. By default the owner and group of selected files will be that of the current user, and the permissions and modify times will be the same as those in the archive. If the .B \-r option is omitted then a table of contents of the selected files is listed on the standard error. .PP .B "pax \-rw" reads the files and directories named in the .I pathname arguments and copies them to the destination .IR directory . A directory .I pathname argument refers to the files and (recursively) subdirectories of that directory. If no .I pathname arguments are given then the standard input is read to get a list of pathnames to copy, one pathname per line. In this case only those pathnames appearing on the standard input are copied. .I directory must exist before the copy. .PP The standard archive formats are automatically detected on input. The default output archive format is implementation defined, but may be overridden by the .B \-x option described below. .I pax archives may be concatenated to combine multiple volumes on a single tape or file. This is accomplished by forcing any pad data to be null bytes. .SS "Basic Options" These options support basic archive operations. .TP .BI b " blocking" Block the output at .I blocking bytes per record. A .B k suffix multiplies .I blocking by 1024 and a .B b suffix multiplies .I blocking by 512. .I blocking is automatically determined on input and is ignored for .BR \-rw . The default .I blocking is .B 10k for block and character special archive files and .B 1b otherwise. The minimum .I blocking is .BR 1b . .TP .BI f " archive" .I archive is the pathname of the input or output archive, overriding the default standard input for .B \-r or standard output for .BR \-w . .TP .B m File modification times are not retained. .TP .B o Restore file ownership as specified in the archive. The current user must have appropriate privileges. .TP \fBs\fP /\fIold\fP/\fInew\fP/[\fIgp\fP] File names are mapped according to the .IR ed (1) style substitution expression. Any non-null character may be used as a delimiter .RB ( / shown here). Multiple .B \-s expressions may be specified; the expressions are applied from left to right, terminating with the first successful substitution. The optional trailing .B p causes successful mappings to be listed on the standard error. File names that substitute to the null string are ignored on both input and output. .TP .B v List file names as they are encountered. Produces a verbose table of contents listing when both .B \-r and .B \-w are omitted. .TP .B x Specifies the output archive .IR format . The input format, which must be one of the following, is automatically determined. The formats are: .RS .PD 0 .TP .B cpio The .IR cpio (5) character format. .TP .B tar The .IR tar (5) format. .TP .B ustar The proposed POSIX tar format. .PD .RE .SS "Compatibility Options" These options provide functional compatibility with the old .IR cpio (1) and .IR tar (1) commands. .TP .B a For .B \-w append files to the end of the archive. .TP .B c Complement the match sense of the .I pattern arguments. .TP .B d Intermediate directories not explicitly listed in the archive are not created. .TP .B i Interactively .I rename files. A file is skipped if a null line is entered and .I pax exits if .B EOF is encountered. .TP .B l For .BR \-rw , files are linked rather than copied when possible. .TP .B p Preserve the access times of input files after they have been copied. .TP .BI t " device" .I device is an implementation defined identifier that names the the input or output archive device, overriding the default standard input for .B \-r or standard output for .BR \-w . .TP .B u Copy each file only if it is newer than a pre-existing file with the same name. This option implies .BR \-a . .TP .B y Interactively prompt for the disposition of each file. .B EOF or an input line starting with .B q causes .I pax to exit. Otherwise an input line starting with anything other than .B y causes the file to be ignored. .SH DIAGNOSTICS The number of files, blocks, and optionally the number of volumes and media parts are listed on the standard error. For .B \-v the input archive formats are also listed on the standard error. .SH EXAMPLES .TP .B "pax \-w \-t 1m ." Copies the contents of the current directory to tape drive 1, medium density. .TP .PD 0 .BI mkdir " newdir" .TP .BI cd " olddir" .TP .BI "pax \-rw ." " newdir" .PD Copies the .I olddir directory hierarchy to .IR newdir . .SH "SEE ALSO" ar(1), cpio(1), find(1), sh(1), tar(1), cpio(5), tar(5) .SH BUGS Special privileges may be required to copy special files. .br Each archive format has a hard upper limit on member pathname sizes. .br Device, user-id and group-id numbers larger than 65535 cause additional header records to be output. These records are ignored by old versions of .IR cpio (1) and .IR tar (1). 0707070000000000161006440044230044230000010000000474560466500002100000033640pax.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax -- portable archive interchange * * current tests: * * 010 list bget() buffer alignment * 020 ignore delta/compress header info * 040 pretend output device is char special */ #if !lint static char id[] = "\n@(#)pax (AT&T Bell Laboratories) 01/01/91\0\n"; #endif #include "pax.h" char* definput = "standard input"; char* defoutput = "standard output"; char* eomprompt = "Change to part %d and hit RETURN: "; char alar_header[ALAR_LABEL]; struct binary_header binary_header; union tar_header_block tar_header_block; struct portar_header portar_header; struct fileinfo file; struct stateinfo state; struct formatinfo format[] = { ALAR_NAME, ALAR_REGULAR, ALAR_SPECIAL, ALAR_ALIGN, ALAR_FLAGS, BINARY_NAME, BINARY_REGULAR, BINARY_SPECIAL, BINARY_ALIGN, BINARY_FLAGS, CPIO_NAME, CPIO_REGULAR, CPIO_SPECIAL, CPIO_ALIGN, CPIO_FLAGS, IBMAR_NAME, IBMAR_REGULAR, IBMAR_SPECIAL, IBMAR_ALIGN, IBMAR_FLAGS, TAR_NAME, TAR_REGULAR, TAR_SPECIAL, TAR_ALIGN, TAR_FLAGS, USTAR_NAME, USTAR_REGULAR, USTAR_SPECIAL, USTAR_ALIGN, USTAR_FLAGS, ASC_NAME, ASC_REGULAR, ASC_SPECIAL, ASC_ALIGN, ASC_FLAGS, ASCHK_NAME, ASCHK_REGULAR, ASCHK_SPECIAL, ASCHK_ALIGN, ASCHK_FLAGS, SAVESET_NAME, SAVESET_REGULAR,SAVESET_SPECIAL,SAVESET_ALIGN, SAVESET_FLAGS, PAX_NAME, PAX_REGULAR, PAX_SPECIAL, PAX_ALIGN, PAX_FLAGS, PORTAR_NAME, PORTAR_REGULAR, PORTAR_SPECIAL, PORTAR_ALIGN, PORTAR_FLAGS, RANDAR_NAME, RANDAR_REGULAR, RANDAR_SPECIAL, RANDAR_ALIGN, RANDAR_FLAGS, 0, 0, 0, 0 }; static int signals[] = /* signals caught by interrupt() */ { SIGHUP, SIGINT, #if !DEBUG SIGQUIT, #endif SIGALRM, SIGTERM, }; static void interrupt(); /*ARGSUSED*/ main(argc, argv) int argc; char** argv; { register int i; register char* s; char* idname; char* p; char* t; int n; struct maplist* mp; struct maplist* lastmap; seterror('I', "pax", '2', &state.errors, 't', &state.trace, 0); state.gid = getegid(); state.uid = geteuid(); (void)umask(state.modemask = umask(0)); (void)time(&state.present); state.formatout = -1; #if physical_is_the_default state.ftwflags = FTW_DOT|FTW_PHYSICAL; #else state.ftwflags = FTW_DOT; #endif state.intermediate = 1; state.matchsense = 1; state.modtime = 1; state.delta.offset = 1; state.delta.sum = -1; state.part = 1; state.record.charset = 1; state.record.line = 1; state.summary = 1; initfile(&file, (char*)0, 0); state.tmp.file = pathtemp((char*)0, (char*)0, "pax"); idname = 0; lastmap = 0; while (i = optget(argv, "b:[blocking]f:[archive]kmors:[/old/new/[pg]]vwx:[format]z:[base]LP?R:[record-control]e:[filter]hnD#[debug]T#[test]cdilpat:[tape]uyB:[vol-max]CM:[vol-message]SU:[user]VX [<file-list | <archive] [file ... [directory]] | [pattern ...]")) switch (i) { /* basic options */ case 'b': state.blocksize = strton(opt_arg, &p, BLOCKSIZE); if (*p) error(3, "%s: invalid block size", opt_arg); if (state.blocksize < MINBLOCK) error(3, "block size must be at least %d", MINBLOCK); if (state.blocksize & (BLOCKSIZE - 1)) error(1, "block size should probably be a multiple of %d", BLOCKSIZE); break; case 'f': if (state.file) error(1, "%s overrides %s", opt_arg, state.file); state.file = opt_arg; break; case 'm': state.modtime = 0; break; case 'o': state.owner = 1; break; case 'r': state.operation |= IN; break; case 's': s = opt_arg; if (!(n = *s++)) error(3, "empty substitution"); while (*s && *s != n) if (*s++ == '\\' && !*s++) error(3, "`\\' cannot terminate lhs of substitution"); if (*s != n) error(3, "missing `%c' delimiter for lhs of substitution", n); *s++ = 0; if (!(mp = allocate(struct maplist))) error(3, "not enough space for substitution"); mp->re = recomp(opt_arg + 1, RE_EDSTYLE|RE_MATCH); mp->into = s; while (*s && *s != n) if (*s++ == '\\' && !*s++) error(3, "`\\' cannot terminate rhs of substitution"); if (*s != n) error(3, "missing `%c' delimiter for rhs of substitution", n); *s++ = 0; while (*s) switch (*s++) { case 'g': mp->flags |= RE_ALL; break; case 'l': mp->flags |= RE_LOWER; break; case 'p': mp->flags |= RE_VERBOSE; break; case 'u': mp->flags |= RE_UPPER; break; default: error(3, "extra character%s `%s' after substitution", *s ? "s" : "", s - 1); break; } if (lastmap) lastmap = lastmap->next = mp; else state.maps = lastmap = mp; break; case 'v': state.verbose = 1; break; case 'w': state.operation |= OUT; break; case 'x': if ((state.formatout = getformat(opt_arg)) < 0) error(3, "%s: unknown archive format", opt_arg); break; case 'R': s = opt_arg; do { if (t = strchr(s, ',')) *t++ = ','; switch (*s++) { case 'c': state.record.charset = 0; break; case 'f': state.record.format = *s; break; case 'l': state.record.line = 0; break; case 'm': state.record.pattern = s; break; case 'p': state.record.pad = 1; break; case 's': state.record.size = strton(s, &p, 0); if (*p) error(3, "%s: invalid record size", s); break; case 'v': (void)strncpy(state.id.volume, s, sizeof(state.id.volume) - 1); state.id.volume[sizeof(state.id.volume) - 1] = 0; (void)strupper(state.id.volume); break; #if DEBUG case 'B': if (!*s) state.in.blok = state.out.blok = 1; else while (*s) switch (*s++) { case 'i': state.in.blok = 1; break; case 'o': state.out.blok = 1; break; default: error(3, "-RB[io] expected", *(s - 1)); } break; #endif case 'U': if (!*s) state.out.unblocked = state.out.unblocked = 1; else while (*s) switch (*s++) { case 'i': state.in.unblocked = 1; break; case 'o': state.out.unblocked = 1; break; default: error(3, "-RU[io] expected", *(s - 1)); } break; default: error(3, "invalid tape option %c", *(s - 1)); } } while (s = t); break; /* extended options */ case 'e': state.filter = opt_arg; break; case 'h': state.summary = 0; break; case 'k': state.keepgoing = 1; break; case 'n': state.exact = 1; break; case 'z': if (streq(opt_arg, FILE_DEFAULT)) state.delta.op |= COMPRESS; else state.delta.base = opt_arg; break; case 'D': state.trace = -opt_num; break; case 'L': state.ftwflags &= ~FTW_PHYSICAL; break; case 'P': state.ftwflags |= FTW_PHYSICAL; break; case 'T': state.test |= opt_num; break; /* tar compatibility options */ case 'a': state.append = 1; break; case 't': if (state.file) error(1, "%s overrides %s", opt_arg, state.file); state.file = strtape(opt_arg, &p); if (*p) error(3, "%s: invalid tape unit specification", opt_arg); break; case 'u': state.update = 1; break; case 'y': state.verify = 1; state.yesno = 1; break; /* cpio compatibility options */ case 'c': state.matchsense = 0; break; case 'd': state.intermediate = 0; break; case 'i': state.verify = 1; break; case 'l': state.linkf = link; break; case 'p': state.acctime = 1; break; case 'B': state.maxout = strton(opt_arg, &p, 0); if (*p) error(3, "%s: invalid block count", opt_arg); break; case 'C': state.complete = 1; break; case 'M': eomprompt = opt_arg; break; case 'S': state.linkf = putsymlink; break; case 'U': idname = opt_arg; break; case 'V': state.drop = 1; break; case 'X': state.xdev = 1; break; /* option errors */ case '?': error(ERROR_USAGE|4, opt_arg); break; case ':': error(2, opt_arg); break; } argv += opt_index; argc -= opt_index; if (state.errors) error(ERROR_USAGE|4, optusage((char*)0)); if (!state.operation) { state.operation = IN; state.list = 1; } state.statf = (state.ftwflags & FTW_PHYSICAL) ? lstat : lpstat; if (state.delta.base) state.delta.op |= (state.operation & IN) ? IN : OUT; if (state.formatout < 0) state.formatout = OUT_DEFAULT; else switch (state.operation) { case OUT: if (state.append) error(1, "format ignored for archive append"); break; case (IN|OUT): if (!(state.delta.op & (IN|OUT))) state.delta.op |= (state.delta.op & COMPRESS) ? IN : CONVERT; break; default: error(1, "format ignored for archive read"); state.formatout = OUT_DEFAULT; break; } switch (state.operation) { case IN|OUT: if (state.append) error(1, "append ignored for pass mode"); if (!(state.delta.op & COMPRESS) && state.file) { error(1, "%s: file name ignored for pass mode", state.file); break; } /*FALLTHROUGH*/ case IN: if (state.operation == IN && (state.delta.op & (COMPRESS|IN)) == COMPRESS) error(1, "compress ignored for archive read"); if (state.append) error(1, "append ignored for archive read"); if (!state.file || streq(state.file, FILE_DEFAULT)) state.file = definput; else { close(0); if (open(state.file, 0) != 0) error(ERROR_SYSTEM|3, "%s: cannot read", state.file); } break; case OUT: if (state.update) state.append = 1; if (!state.file || streq(state.file, FILE_DEFAULT)) { if (state.append) error(3, "archive pathname must be specified for append"); state.file = defoutput; } else { close(1); if (state.append) { if (state.delta.op) error(3, "cannot append delta archives"); if (open(state.file, 2) != 1) error(ERROR_SYSTEM|3, "%s: cannot append", state.file); } else if (creat(state.file, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) != 1) error(ERROR_SYSTEM|3, "%s: cannot write", state.file); } break; } if (!(state.linktab = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_namesize, sizeof(struct fileid), HASH_name, "links", 0))) error(3, "cannot allocate hard link table"); if ((state.operation & IN) && !state.list && !(state.restore = hashalloc((HASHTABLE*)0, HASH_set, HASH_ALLOCATE, HASH_name, "restore", 0))) error(3, "cannot allocate directory table"); if (state.modtime) state.modemask = 0; if (state.owner || idname) { if (state.operation & IN) { state.owner = 1; state.modemask = 0; if (idname) { if ((state.setuid = struid(idname)) < 0 || (state.setgid = strgid(idname)) < 0) error(3, "%s: invalid user name", idname); state.flags |= SETIDS; } } else error(1, "ownership assignment ignored on archive write"); } if (state.verify) interactive(); state.modemask = ~state.modemask & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); for (i = 0; i < elements(signals); i++) if (signal(signals[i], interrupt) == SIG_IGN) (void)signal(signals[i], SIG_IGN); #if DEBUG message(-1, "%s", operations()); #endif switch (state.operation) { case IN: if (*argv) { state.patterns = initmatch(argv); if (state.exact) state.matchsense = 1; } else if (state.exact) error(3, "file arguments expected"); binit(); (void)getcwd(state.pwd, PATH_MAX); state.pwdlen = strlen(state.pwd); state.pwd[state.pwdlen++] = '/'; if (state.delta.op & IN) deltabase(); copyin(); if (state.exact) { argv = state.patterns; while (s = *argv++) if (*s) error(2, "%s: file not found in archive", s); } break; case OUT: if (*argv) state.files = argv; binit(); if (!state.maxout && state.complete) error(3, "maximum block count required"); if (state.append) append(); if (state.delta.op & OUT) deltabase(); putprologue(); copy(copyout); if (state.delta.op & OUT) deltadelete(); putepilogue(); break; case (IN|OUT): if (state.delta.op) { if (*argv) state.patterns = initmatch(argv); state.operation = OUT; binit(); state.operation = IN|OUT; if (state.delta.op & IN) deltabase(); deltapass(); } else { if (--argc < 0) { error(2, "destination directory required for pass mode"); error(ERROR_USAGE|4, optusage((char*)0)); } state.destination = argv[argc]; argv[argc] = 0; if (*argv) state.files = argv; if (state.blocksize) error(1, "blocking ignored in pass mode"); binit(); if (state.record.size) error(1, "record size ignored in pass mode"); /* * initialize destination dir */ (void)pathcanon(state.destination); if (stat(state.destination, file.st) || (file.st->st_mode & S_IFMT) != S_IFDIR) error(3, "%s: destination must be a directory", state.destination); state.dev = file.st->st_dev; (void)strcpy(state.pwd, state.destination); state.pwdlen = strlen(state.pwd); state.pwd[state.pwdlen++] = '/'; copy(copyinout); } break; } finish(0); } /* * print number of blocks actually copied and exit */ void finish(code) int code; { register char* x1 = &state.tmp.buffer[0]; register char* x2 = &state.tmp.buffer[state.buffersize / 2]; register long n; (void)remove(state.tmp.file); if (state.restore) (void)hashwalk(state.restore, 0, restore); fflush(stdout); if (state.dropcount) { fprintf(stderr, "\n"); fflush(stderr); } if (state.summary) { switch (state.operation) { case OUT: n = state.out.count + state.out.offset; break; case IN|OUT: n = state.out.count + state.out.offset; if (n || !(state.delta.op & IN)) break; /*FALLTHROUGH*/ case IN: n = state.in.count + state.in.offset; break; } if (state.entries) { if (state.volume > 1) (void)sprintf(x1, ", %d volumes", state.volume); else *x1 = 0; if (state.volume > 0 && state.part > state.volume) (void)sprintf(x2, ", %d parts", state.part - state.volume + 1); else *x2 = 0; n = (n + BLOCKSIZE - 1) / BLOCKSIZE; if (state.verbose) fprintf(stderr, "%d file%s, %ld block%s%s%s\n", state.selected, state.selected == 1 ? "" : "s", n, n == 1 ? "" : "s", x1, x2); else fprintf(stderr, "%ld block%s%s%s\n", n, n == 1 ? "" : "s", x1, x2); } } fflush(stderr); if (state.interrupt) { (void)signal(state.interrupt, SIG_DFL); kill(getpid(), state.interrupt); pause(); } exit(code ? code : state.errors != 0); } /* * clean up dir info before exit */ static void interrupt(sig) int sig; { (void)signal(sig, SIG_IGN); sigunblock(sig); switch (sig) { case SIGINT: case SIGQUIT: fprintf(stderr, "\n"); break; } state.interrupt = sig; finish(1); } /* * error hook for re lib */ void reerror(msg) char* msg; { error(3, "substitution: %s", msg); } 0707070000000000171006440044230044230000010000000474560470700002100000044273pax.h Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax common definitions */ #include <ls.h> #include <stdio.h> #include <sig.h> #include <ftwalk.h> #include <ctype.h> #include <limits.h> #include <hash.h> #include <re.h> #include <error.h> #include <time.h> #include <swap.h> #include <align.h> #ifndef PATH_MAX #define PATH_MAX 1024 #endif #ifndef S_IFIFO #define S_IFIFO 0010000 #endif #ifndef S_IFLNK #define S_IFLNK 0120000 #endif #ifndef S_IFSOCK #define S_IFSOCK 0140000 #endif #define PANIC ERROR_PANIC|ERROR_SOURCE,__FILE__,__LINE__ #define allocate(x) (x*)calloc(1,sizeof(x)) #define bcount(io) (io.last-io.next) #define bsave() (state.backup=state.in) #define elements(x) (sizeof(x)/sizeof(x[0])) #define message if(state.trace<0)error #define streq(a,b) ((*(a))==(*(a))&&!strcmp(a,b)) #define strneq(a,b,n) ((*(a))==(*(a))&&!strncmp(a,b,n)) #define round(x,y) (((long)(x)+((y)-1))&~((y)-1)) #define RE_VERBOSE (1<<RE_EXTERNAL)/* list re substitution */ /* * info per archive format * * NOTES: * * format indices must agree with format[] table below * * the *_NAMESIZE maximum file name lengths include the trailing \0 * * PATH_MAX must be >= max(*_NAMESIZE) */ #define ALAR 0 #define ALAR_NAME "ansi" #define ALAR_REGULAR 4 #define ALAR_SPECIAL ALAR_REGULAR #define ALAR_LABEL 2048 #define ALAR_HEADER 80 #define ALAR_VARHDR 9 #define ALAR_NAMESIZE 17 #define ALAR_RECSIZE (BLOCKSIZE>>2) #define ALAR_RECFORMAT 'D' #define ALAR_ALIGN 0 #define ALAR_FLAGS IN|OUT #define ALAR_ID "SLT" #define BINARY 1 #define BINARY_NAME "binary" #define BINARY_REGULAR DEFBUFFER #define BINARY_SPECIAL DEFBLOCKS #define BINARY_HEADER 26 #define BINARY_NAMESIZE 256 #define BINARY_ALIGN 2 #define BINARY_FLAGS IN|OUT struct binary_header { short magic; unsigned short dev; unsigned short ino; unsigned short mode; unsigned short uid; unsigned short gid; short links; unsigned short rdev; unsigned short mtime[2]; short namesize; unsigned short size[2]; }; #define CPIO 2 #define CPIO_NAME "cpio" #define CPIO_REGULAR DEFBUFFER #define CPIO_SPECIAL DEFBLOCKS #define CPIO_HEADER 76 #define CPIO_MAGIC 070707 #define CPIO_EXTENDED 1 #define CPIO_TRAILER "TRAILER!!!" #define CPIO_TRUNCATE(x) ((x)&0177777) #define CPIO_NAMESIZE 256 #define CPIO_ALIGN 0 #define CPIO_FLAGS IN|OUT #define CPIO_FMT 0170000 #define CPIO_FIFO 0010000 #define CPIO_DIR 0040000 #define CPIO_CHR 0020000 #define CPIO_BLK 0060000 #define CPIO_REG 0100000 #define CPIO_LNK 0120000 #define CPIO_SOCK 0140000 #define IBMAR 3 #define IBMAR_NAME "ibm" #define IBMAR_REGULAR ALAR_REGULAR #define IBMAR_SPECIAL ALAR_SPECIAL #define IBMAR_ALIGN ALAR_ALIGN #define IBMAR_FLAGS IN|OUT /* * NOTE: since USTAR is an extension of TAR they share the same header */ #define TAR 4 #define TAR_NAME "tar" #define TAR_REGULAR DEFBUFFER #define TAR_SPECIAL DEFBLOCKS #define TAR_HEADER TBLOCK #define TAR_NAMESIZE 100 #define TAR_ALIGN BLOCKSIZE #define TAR_FLAGS IN|OUT #define header tar_header_info #define hblock tar_header_block #include <tar.h> #undef header #undef hblock #define USTAR 5 #define USTAR_NAME TMAGIC #define USTAR_REGULAR TAR_REGULAR #define USTAR_SPECIAL TAR_SPECIAL #define USTAR_HEADER TAR_HEADER #define USTAR_ALIGN TAR_ALIGN #define USTAR_FLAGS IN|OUT #define tar_header tar_header_block.dbuf #define tar_block tar_header_block.dummy /* * s5r4 expanded cpio(5) char format * pax won't even bother with the binary counterpart */ #define ASC 6 #define ASC_NAME "asc" #define ASC_REGULAR CPIO_REGULAR #define ASC_SPECIAL CPIO_SPECIAL #define ASC_HEADER 110 #define ASC_MAGIC 070701 #define ASC_NAMESIZE 1024 #define ASC_ALIGN 4 #define ASC_FLAGS IN|OUT /* * s5r4 expanded cpio(5) char format with checksum * (misnamed ``crc'' -- the check is a simple sum of the header) * pax won't even bother with the binary counterpart */ #define ASCHK 7 #define ASCHK_NAME "aschk" #define ASCHK_REGULAR ASC_REGULAR #define ASCHK_SPECIAL ASC_SPECIAL #define ASCHK_MAGIC 070702 #define ASCHK_ALIGN ASC_ALIGN #define ASCHK_FLAGS IN|OUT /* * backup saveset format */ #define SAVESET 8 #define SAVESET_NAME "vmsbackup" #define SAVESET_REGULAR 0 #define SAVESET_SPECIAL 0 #define SAVESET_ALIGN 0 #define SAVESET_FLAGS IN #define SAVESET_ID "DECVMS" #define SAVESET_IMPL "BACKUP" #define BLKHDR_SIZE 256 #define BLKHDR_hdrsiz 0 #define BLKHDR_blksiz 40 #define FILHDR_SIZE 4 #define FILHDR_MAGIC 257 #define FILHDR_namelen 128 #define FILHDR_size 0 #define FILHDR_type 2 #define FILHDR_data 4 #define FILATT_blocks 10 #define FILATT_frag 12 #define FILATT_recatt 1 #define FILATT_recfmt 0 #define FILATT_reclen 2 #define FILATT_recvfc 15 #define RECHDR_SIZE 16 #define RECHDR_size 0 #define RECHDR_type 2 #define REC_file 3 #define REC_vbn 4 /* * the proposed POSIX IEEE Std 1003.1B-1990 interchange format */ #define PAX 9 #define PAX_NAME "posix" #define PAX_REGULAR 10 #define PAX_SPECIAL 10 #define PAX_ALIGN 0 #define PAX_FLAGS IN|OUT #define PAX_RECSIZE (state.blocksize) #define PAX_RECFORMAT 'F' #define PAX_ID "POSIX1" /* * portable (object) archive */ #define PORTAR 10 #define PORTAR_NAME "portarch" #define PORTAR_REGULAR 0 #define PORTAR_SPECIAL 0 #define PORTAR_HEADER sizeof(portar_header) #define PORTAR_MAG "!<arch>\n" #define PORTAR_MAGSIZ 8 #define PORTAR_SYM "(/|__________E???_)*" #define PORTAR_END "`\n" #define PORTAR_ENDSIZ 2 #define PORTAR_TERM '/' #define PORTAR_ALIGN 2 #define PORTAR_FLAGS IN struct portar_header { char ar_name[16]; char ar_date[12]; /* left-adj; decimal char*; blank fill */ char ar_uid[6]; /* " */ char ar_gid[6]; /* " */ char ar_mode[8]; /* left-adj; octal char*; blank fill */ char ar_size[10]; /* left-adj; decimal char*; blank fill */ char ar_fmag[2]; /* PORTAR_END */ }; /* * ranlib (object) archive -- almost PORTAR */ #define RANDAR 11 #define RANDAR_NAME "randarch" #define RANDAR_REGULAR 0 #define RANDAR_SPECIAL 0 #define RANDAR_SYM "(__.SYMDEF|__________E???X)*" #define RANDAR_TERM ' ' #define RANDAR_ALIGN PORTAR_ALIGN #define RANDAR_FLAGS IN #define FILE_DEFAULT "-" /* default in/out/delta file */ #define IN_DEFAULT CPIO /* first getheader() state */ #define OUT_DEFAULT CPIO /* default output format */ #define IN (1<<0) /* copy in */ #define OUT (1<<1) /* copy out */ #define COLLECT (1<<2) /* collect input member info */ #define COMPRESS (1<<3) /* output is self delta */ #define CONVERT (1<<4) /* convert archive format */ #define SETIDS (1<<0) /* set explicit uid and gid */ #define INFO_SEP '!' /* info header field separator */ #define ID "PAX" /* info header id */ #define IDLEN (sizeof(ID)-1) /* strlen(ID) */ #define IMPLEMENTATION "ATTPAX1" /* implementation id */ #define TYPE_COMPRESS 'C' /* compress encoding type */ #define TYPE_DELTA 'D' /* delta encoding type */ #define VERSION 'A' /* encoding type version */ #define DELTA_create 'c' /* delta create data op */ #define DELTA_delete 'd' /* delta delete data op */ #define DELTA_pass 'p' /* delta pass pseudo op */ #define DELTA_update 'u' /* delta update data op */ #define DELTA_verify 'v' /* delta verify data op */ #define DELTA_LO(c) ((c)&0xffff) /* lo order checksum bits */ #define DELTA_HI(c) DELTA_LO(c>>16) /* hi order checksum bits */ #define NOLINK 0 /* not a link */ #define HARDLINK '1' /* hard link to previous entry */ #define SOFTLINK '2' /* soft link to previous entry */ #define BLOCKSIZE 512 /* block size */ #define IOALIGN BOUND1 /* io buffer alignment */ #define MINBLOCK 1 /* smallest block size */ #define DEFBLOCKS 20 /* default blocking */ #define DEFBUFFER 16 /* default io buffer blocking */ #define MAXBLOCKS 40 /* largest blocking */ #define MAXUNREAD BLOCKSIZE /* max bunread() count */ struct bio /* buffered io info */ { char* next; /* next char pointer */ char* last; /* last char+1 pointer */ char* buffer; /* io buffer */ unsigned long count; /* char transfer count */ unsigned long offset; /* volume offset */ unsigned int blocked:1; /* blocked device io */ unsigned int blok:1; /* BLOK io file */ unsigned int blokflag:1; /* io file BLOK flag */ unsigned int empty:1; /* last read was empty */ unsigned int eof:1; /* hit EOF */ unsigned int unblocked:1; /* set unblocked device io */ }; struct fileinfo /* common internal file info */ { int magic; /* header magic number */ char* id; /* archive file id */ char* name; /* archive file name */ int namesize; /* name size with null byte */ char* path; /* local file name for reading */ struct { int op; /* op */ struct deltainfo* base; /* base file pointer */ long size; /* target file size */ int version; /* encoding type version */ } delta; /* delta info */ struct stat* st; /* stat() info from ftwalk() */ int type; /* st_mode type (S_IFMT bits) */ int linktype; /* NOLINK, HARDLINK, SOFTLINK */ char* linkname; /* link to this path */ int linknamesize; /* linkname size with null byte */ char* uidname; /* user id name */ char* gidname; /* group id name */ struct { int blocks; /* io block count */ int format; /* format */ int section; /* file section number */ } record; /* record format info */ long checksum; /* checksum */ unsigned int skip:1; /* skip this entry */ }; struct deltainfo /* delta entry info */ { struct fileinfo* info; /* deltapass() file info */ short dev; /* dev */ short ino; /* ino */ long mtime; /* modify time */ long offset; /* data offset */ long size; /* data size */ unsigned int mark:1; /* visit mark */ }; struct fileid /* unique file identifier */ { int dev; /* device */ int ino; /* inode */ }; struct formatinfo /* format info */ { char* name; /* name */ int regular; /* default regular blocking */ int special; /* default special blocking */ int align; /* trailer alignment */ int flags; /* io info */ }; struct linkinfo /* link info */ { char* name; /* name */ int namesize; /* name size with null byte */ struct fileid id; /* generated link file id */ }; struct maplist /* file name map list */ { struct maplist* next; /* next in list */ reprogram* re; /* compiled match re */ char* into; /* map into this */ int flags; /* resub() flags */ }; struct postinfo /* post processing restoration */ { time_t mtime; /* modify time */ int mode; /* permissions */ int uid; /* user id */ int gid; /* group id */ }; union integral /* byte|half swap probe */ { unsigned long l; unsigned short s[2]; unsigned char c[4]; }; struct stateinfo /* program state */ { int acctime; /* reset file access times */ int append; /* append -- must be 0 or 1 !!! */ struct bio backup; /* backup() position */ int blocksize; /* explicit buffer size */ int buffersize; /* io buffer size */ int complete; /* files completely in volume */ int current; /* current file[] index */ struct { char* base; /* base archive for delta */ char* buffer; /* temporary buffer */ int buffersize; /* temporary buffer size */ long checksum; /* archive running checksum */ long count; /* delta file count */ int fd; /* file read descriptor */ char* hdr; /* header pointer */ char* hdrbuf; /* header buffer */ long membersum; /* archive member checksum */ long offset; /* base file offset */ int op; /* delta op state */ long size; /* base file size */ int sum; /* compute input checksum if >0 */ HASHTABLE* tab; /* entry table */ } delta; /* delta sub state */ char* destination; /* pass mode destination dir */ dev_t dev; /* . device number */ unsigned short devcnt; /* dev assignment count */ HASHTABLE* restore; /* post proc restoration table */ int drop; /* drop a `.' for each file */ int dropcount; /* current line drop count */ int entries; /* total archive entry count */ int entry; /* archive entry number */ int errors; /* error count */ int exact; /* exact archive read */ char** files; /* alternate file name list */ char* file; /* io file name */ char* filter; /* file output filter command */ int flags; /* flags */ int formatin; /* input archive format index */ int formatout; /* output archive format index */ int ftwflags; /* ftwalk() flags */ int gid; /* current group id */ struct { char volume[7]; /* volume id */ char format[7]; /* format id */ char implementation[8];/* implementation id */ char owner[15]; /* owner id */ } id; /* id strings (including '\0') */ struct bio in; /* buffered input info */ unsigned short inocnt; /* ino assignment count */ int intermediate; /* make intermediate dirs */ int interrupt; /* this signal caused exit */ int keepgoing; /* keep going on error */ int (*linkf)(); /* use when possible for -rw */ HASHTABLE* linktab; /* hard link table */ int list; /* full file trace */ struct maplist* maps; /* file name maps */ int matchsense; /* pattern match sense */ unsigned long maxout; /* max volume/part output count */ int modemask; /* and with mode for chmod() */ int modtime; /* retain mtime */ int newer; /* append only if newer */ long offset; /* relative archive byte offset */ int operation; /* IN|OUT operation mode */ struct bio out; /* buffered output info */ int owner; /* set owner info */ int part; /* device change count */ char** patterns; /* name match patterns */ char pwd[PATH_MAX]; /* full path of . */ int pwdlen; /* pwd length sans null */ time_t present; /* for long file listing */ struct { int charset; /* convert record charset */ struct fileinfo* file; /* current output file */ int format; /* output format */ int line; /* convert records<->lines */ int pad; /* pad output record blocks */ char* pattern; /* format match pattern */ int offset; /* data buffer offset */ int size; /* io size */ } record; /* record info */ FILE* rtty; /* tty file read pointer */ #if SAVESET struct { char* block; /* current block */ int blocksize; /* max block size */ char* bp; /* block pointer */ int recatt; /* record attributes */ int recfmt; /* record format */ int reclen; /* record length */ int recvfc; /* record fixed control length */ int lastsize; /* size of last record */ time_t time; /* backup time */ } saveset; /* backup saveset state */ #endif int selected; /* number of selected files */ int setgid; /* set file gid to this value */ int setuid; /* set file uid to this value */ int (*statf)(); /* -L=lpstat() -P=lstat() */ int summary; /* output summary info */ int swap; /* {BYTE,HALF} swap operation */ int test; /* debug test bits */ struct { char* buffer; /* temporary buffer */ int buffersize; /* temporary buffer size */ char* file; /* tmp file name */ } tmp; /* temporary stuff */ int trace; /* error() debug trace level */ int uid; /* current user id */ int update; /* copy file only if newer */ int verbose; /* trace files when acted upon */ int verify; /* verify action on file */ int volume; /* archive volume number */ FILE* wtty; /* tty file write pointer */ int xdev; /* don't cross device boundaries*/ int yesno; /* interactive answer is yes/no */ }; extern char* definput; extern char* defoutput; extern char* eomprompt; extern char uidname[]; extern char gidname[]; extern struct formatinfo format[]; extern struct fileinfo file; extern struct stateinfo state; extern char alar_header[]; extern struct binary_header binary_header; extern union tar_header_block tar_header_block; extern struct portar_header portar_header; extern void finish(); extern void usage(); extern void copyin(); extern void copy(); extern int copyout(); extern void fileout(); extern void filein(); extern void filepass(); extern void fileskip(); extern int copyinout(); extern void append(); extern void initdelta(); extern void deltabase(); extern int deltafd(); extern void deltaverify(); extern void deltapass(); extern void deltadelete(); extern int getprologue(); extern void putprologue(); extern void getepilogue(); extern void putepilogue(); extern int getheader(); extern void getdeltaheader(); extern void adddelstr(); extern void adddelnum(); extern void putheader(); extern void putdeltaheader(); extern void gettrailer(); extern void puttrailer(); extern int getlabel(); extern void putlabels(); #if SAVESET extern int getsaveset(); extern int mkbyte(); extern int mkhalf(); extern long mklong(); #endif extern int getfile(); extern int selectfile(); extern int getnumber(); extern int getformat(); extern int verify(); extern int openin(); extern int openout(); extern int validout(); extern int addlink(); extern char* map(); extern void listentry(); extern void getidnames(); extern void setidnames(); extern void initfile(); extern char** initmatch(); extern int match(); extern int dirprefix(); extern char* strlower(); extern char* strupper(); extern void swap(); extern void setfile(); extern void settime(); extern int missdir(); extern int restore(); extern long cpio_long(); extern void cpio_short(); extern int cpio_mode(); extern int tar_checksum(); extern long asc_checksum(); extern long getlabnum(); extern char* getlabstr(); extern void binit(); extern void balloc(); extern int bread(); extern void bunread(); extern char* bget(); extern void backup(); extern void bflushin(); extern void bflushout(); extern void bwrite(); extern void bput(); extern void interactive(); extern void newio(); extern int dodelta(); extern int doupdate(); #if DEBUG extern char* operations(); #endif extern char* ctime(); extern char* fgetline(); extern char* fmtmode(); extern int fstat(); extern int link(); extern long lseek(); extern int lstat(); extern int lpstat(); extern char* malloc(); extern char* memcpy(); extern char* memdup(); extern unsigned long memsum(); extern char* pathcanon(); extern char* pathtemp(); extern int remove(); extern int stat(); extern char* strcpy(); extern char* strncpy(); extern char* strchr(); extern char* strrchr(); extern char* strtape(); extern long strtol(); extern long strton(); extern char* strdup(); extern int strmatch(); extern int struid(); extern int strgid(); extern int getsymlink(); extern int putsymlink(); #if __STDC__ extern FILE* fdopen(int, char*); #endif 0707070000000000201006440044230044230000010000000472663607400002300000000625stubs.c Ugsf Ggsf /* * G. S. Fowler * AT&T Bell Laboratories * * pax libdelta stubs for external distribution */ #include "pax.h" /*ARGSUSED*/ int delta(old, oldsize, new, newsize, fd) char* old; long oldsize; char* new; long newsize; int fd; { return(-1); } /*ARGSUSED*/ int update(oldfd, oldoffset, dfd, wfd, rfd, newoffset) int oldfd; long oldoffset; int dfd; int wfd; int rfd; long newoffset; { return(-1); } 0707070000000000211006440044230044230000010000000441674764000002200000003461tar.sh Ugsf Ggsf : # Glenn Fowler # AT&T Bell Laboratories # @(#)tar.sh (ulysses!gsf) 04/06/89 # # tar -> pax interface script # command=tar usage=" Usage: $command c[vwfbB[[0-9][hlm]]] [pathname ...] $command r[vwfbB[[0-9][hlm]]] [files ...] $command t[vwfbB[[0-9][hlm]] $command u[vwfbB[[0-9][hlm]]] [pathname ...] $command x[vwfblmB[[0-9][hlm]]] [pathname ...]" case $1 in *[tx]*) mode="-r" ;; *[cru]*) mode="-w" ;; *) echo "$command: one of crtux must be specified$usage" >&2; exit 1 ;; esac options="-P" file="-t 0" list="" r_ok="1" w_ok="1" arg=$1 lastopt="" shift for opt in `echo '' $arg | sed -e 's/^ -//' -e 's/./& /g'` do case $opt in [0-9]) file="-t $opt" ;; [hlm]) case $lastopt in [0-9]) file="${file}$opt" ;; *) case $opt in h) options="$options -L" ;; l) ;; m) r_ok="" options="$options -$opt" ;; esac ;; esac ;; [v]) options="$options -$opt" ;; b) case $# in 0) echo "$command: blocking factor argument expected$usage" >&2; exit 1 ;; esac options="$options -b ${1}b" shift ;; c) r_ok="" ;; f) case $# in 0) echo "$command: file name argument expected$usage" >&2; exit 1 ;; esac case $1 in -) file="" ;; *) file="-f $1" ;; esac shift ;; r) r_ok="" options="$options -a" ;; t) w_ok="" list="1" ;; u) r_ok="" options="$options -u" ;; w) options="$options -y" ;; x) w_ok="" ;; B) options="$options -b 10k" ;; *) echo "$command: invalid option -$opt$usage" >&2; exit 1 ;; esac lastopt=$opt done case $mode in -r) case $r_ok in "") echo "$command: options inconsistent with archive read" >&2; exit 1 ;; esac ;; -w) case $w_ok in "") echo "$command: options inconsistent with archive write" >&2; exit 1 ;; esac case $# in 0) set - "." ;; esac options="$options -x tar" ;; esac case $list in "1") mode="" ;; esac pax $mode $options $file "$@" 0707070000000000221006440044230044230000010000000475460106100002300000020027Mamfile Ugsf Ggsf note # # make abstract machine file generated from Makefile # # setv AS as setv ASFLAGS setv AR ar setv ARFLAGS cr setv CC cc setv CCFLAGS "-O" setv CPP "$CC -E" setv CPIO cpio setv CPIOFLAGS setv F77 f77 setv INSTALLROOT $HOME setv LD ld setv LDFLAGS setv LEX lex setv LEXFLAGS setv LPR lpr setv LPRFLAGS setv M4FLAGS setv MAKE nmake setv MAKEFLAGS setv PR pr setv PRFLAGS setv TAR tar setv YACC yacc setv YACCFLAGS -d make install make all make pax make bio.o make bio.c attr perm attr scan make FEATURE/bcopy exec : generate local FEATURE information for bcopy .... set - .... if test ! -d FEATURE .... then rm -rf FEATURE .... mkdir FEATURE .... fi .... { .... echo '/* local info for bcopy */' .... case "bcopy" in .... *.*) f=bcopy .... i=bcopy .... ;; .... *) f=bcopy .... i=bcopy .... echo "#undef $i .... int $i;" > x.${!-$$}.c .... cmd=: .... opt= .... if $CC $CCFLAGS -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1 .... then if $CC $CCFLAGS -Bstatic -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1 .... then opt=-Bstatic .... else cmd=x.${!-$$}.x .... fi .... fi .... echo "#undef $i .... extern int $i();static int ((*i)())=$i;main(){exit(i==(int((*)()))0);}" > x.${!-$$}.c .... if $CC $CCFLAGS $opt -c x.${!-$$}.c > /dev/null 2>&1 .... then if { $CC $CCFLAGS $opt -o x.${!-$$}.x x.${!-$$}.o && $cmd ;} > /dev/null 2>&1 .... then echo "#define _lib_$i 1 /* $i() in default lib(s) */" .... elif { $CC $CCFLAGS $opt -o x.${!-$$}.x x.${!-$$}.o -lm && $cmd ;} > /dev/null 2>&1 .... then echo "#define _mth_$i 1 /* $i() in math lib */" .... fi .... fi .... ;; .... esac .... echo "#include \"$f.h\"" > x.${!-$$}.c .... if $CC $CCFLAGS -E x.${!-$$}.c > /dev/null 2>&1 .... then echo "#define _hdr_$i 1 /* #include \"$f.h\" ok */" .... fi .... echo "#include \"sys/$f.h\"" > x.${!-$$}.c .... if $CC $CCFLAGS -E x.${!-$$}.c > /dev/null 2>&1 .... then echo "#define _sys_$i 1 /* #include \"sys/$f.h\" ok */" .... fi .... for j in / /usr/ .... do for i in bin etc ucb .... do if test -f $j$i/$f .... then echo "#define _bin_$f 1 /* $f in ?(/usr)/(bin|etc|ucb) */" .... break 2 .... fi .... done .... done .... } > FEATURE/bcopy .... rm -rf x.${!-$$}.c x.${!-$$}.o x.${!-$$}.x attr scan attr impl done FEATURE/bcopy make FEATURE/mtio exec : generate local FEATURE information for mtio .... set - .... if test ! -d FEATURE .... then rm -rf FEATURE .... mkdir FEATURE .... fi .... { .... echo '/* local info for mtio */' .... case "mtio" in .... *.*) f=mtio .... i=mtio .... ;; .... *) f=mtio .... i=mtio .... echo "#undef $i .... int $i;" > x.${!-$$}.c .... cmd=: .... opt= .... if $CC $CCFLAGS -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1 .... then if $CC $CCFLAGS -Bstatic -o x.${!-$$}.x x.${!-$$}.c > /dev/null 2>&1 .... then opt=-Bstatic .... else cmd=x.${!-$$}.x .... fi .... fi .... echo "#undef $i .... extern int $i();static int ((*i)())=$i;main(){exit(i==(int((*)()))0);}" > x.${!-$$}.c .... if $CC $CCFLAGS $opt -c x.${!-$$}.c > /dev/null 2>&1 .... then if { $CC $CCFLAGS $opt -o x.${!-$$}.x x.${!-$$}.o && $cmd ;} > /dev/null 2>&1 .... then echo "#define _lib_$i 1 /* $i() in default lib(s) */" .... elif { $CC $CCFLAGS $opt -o x.${!-$$}.x x.${!-$$}.o -lm && $cmd ;} > /dev/null 2>&1 .... then echo "#define _mth_$i 1 /* $i() in math lib */" .... fi .... fi .... ;; .... esac .... echo "#include \"$f.h\"" > x.${!-$$}.c .... if $CC $CCFLAGS -E x.${!-$$}.c > /dev/null 2>&1 .... then echo "#define _hdr_$i 1 /* #include \"$f.h\" ok */" .... fi .... echo "#include \"sys/$f.h\"" > x.${!-$$}.c .... if $CC $CCFLAGS -E x.${!-$$}.c > /dev/null 2>&1 .... then echo "#define _sys_$i 1 /* #include \"sys/$f.h\" ok */" .... fi .... for j in / /usr/ .... do for i in bin etc ucb .... do if test -f $j$i/$f .... then echo "#define _bin_$f 1 /* $f in ?(/usr)/(bin|etc|ucb) */" .... break 2 .... fi .... done .... done .... } > FEATURE/mtio .... rm -rf x.${!-$$}.c x.${!-$$}.o x.${!-$$}.x attr scan attr impl done FEATURE/mtio make pax.h attr perm attr scan attr impl make ../../../include/tar.h attr perm attr scan attr impl done ../../../include/tar.h make ../../../include/align.h attr perm attr scan attr impl done ../../../include/align.h make ../../../include/swap.h attr perm attr scan attr impl done ../../../include/swap.h make /usr/include/time.h attr perm attr scan attr impl done /usr/include/time.h make ../../../include/error.h attr perm attr scan attr impl done ../../../include/error.h make ../../../include/re.h attr perm attr scan attr impl done ../../../include/re.h make ../../../include/hash.h attr perm attr scan attr impl done ../../../include/hash.h make ../../../include/limits.h attr perm attr scan attr impl done ../../../include/limits.h make /usr/include/ctype.h attr perm attr scan attr impl done /usr/include/ctype.h make ../../../include/ftwalk.h attr perm attr scan attr impl done ../../../include/ftwalk.h make ../../../include/sig.h attr perm attr scan attr impl done ../../../include/sig.h make ../../../include/ls.h attr perm attr scan attr impl done ../../../include/ls.h done pax.h done bio.c prev bio.c setv DEBUG -DDEBUG exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c bio.c done bio.o make convert.o make convert.c attr perm attr scan prev pax.h done convert.c prev convert.c exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c convert.c done convert.o make copy.o make copy.c attr perm attr scan prev pax.h done copy.c prev copy.c exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c copy.c done copy.o make pax.o make pax.c attr perm attr scan prev pax.h done pax.c prev pax.c exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c pax.c done pax.o make delta.o make delta.c attr perm attr scan prev pax.h done delta.c prev delta.c exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c delta.c done delta.o make file.o make file.c attr perm attr scan prev pax.h done file.c prev file.c exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c file.c done file.o make format.o make format.c attr perm attr scan prev pax.h done format.c prev format.c setv SYSTEM -DSYSTEM="\"bsd \"" exec $CC $CCFLAGS -I. -I../../../include "$SYSTEM" "$DEBUG" -c format.c done format.o make misc.o make misc.c attr perm attr scan prev pax.h done misc.c prev misc.c exec $CC $CCFLAGS -I. -I../../../include "$DEBUG" -c misc.c done misc.o make ../../../lib/libodelta.a attr arch attr perm done ../../../lib/libodelta.a make ../../../lib/libx.a attr arch attr perm done ../../../lib/libx.a exec $CC $CCFLAGS "$DEBUG" "$SYSTEM" $LDFLAGS "$DEBUG" "$SYSTEM" -o pax bio.o convert.o copy.o pax.o delta.o file.o format.o misc.o ../../../lib/libodelta.a ../../../lib/libx.a done pax make cpio make cpio.sh attr perm attr scan done cpio.sh prev cpio.sh exec case '' in .... "") cp cpio.sh cpio .... ;; .... *) { .... i=`(read x; echo $x) < cpio.sh` .... case $i in .... \#!*) echo $i ;; .... esac .... echo .... cat cpio.sh .... } > cpio .... ;; .... esac .... chmod u+w,+x cpio done cpio make tar make tar.sh attr perm attr scan done tar.sh prev tar.sh exec case '' in .... "") cp tar.sh tar .... ;; .... *) { .... i=`(read x; echo $x) < tar.sh` .... case $i in .... \#!*) echo $i ;; .... esac .... echo .... cat tar.sh .... } > tar .... ;; .... esac .... chmod u+w,+x tar done tar done all make $INSTALLROOT/bin exec if test ! -d $INSTALLROOT/bin .... then rm -rf $INSTALLROOT/bin && mkdir $INSTALLROOT/bin || { rm -rf $INSTALLROOT && mkdir $INSTALLROOT && mkdir $INSTALLROOT/bin ;} || true .... fi done $INSTALLROOT/bin make $INSTALLROOT/bin/pax prev pax exec { cp pax $INSTALLROOT/bin/pax 2>/dev/null ;} || true done $INSTALLROOT/bin/pax make $INSTALLROOT/man/man1 exec if test ! -d $INSTALLROOT/man/man1 .... then rm -rf $INSTALLROOT/man/man1 && mkdir $INSTALLROOT/man/man1 || { rm -rf $INSTALLROOT/man && mkdir $INSTALLROOT/man && mkdir $INSTALLROOT/man/man1 ;} || true .... fi done $INSTALLROOT/man/man1 make $INSTALLROOT/man/man1/pax.1 make pax.1 attr perm done pax.1 exec { cp pax.1 $INSTALLROOT/man/man1/pax.1 2>/dev/null ;} || true done $INSTALLROOT/man/man1/pax.1 done install 0707070000000000000000000000000000000000010000000000000000000001300000000000TRAILER!!!