V10/cmd/odist/pax/ship/pax/910208/base
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!!!