home *** CD-ROM | disk | FTP | other *** search
- /*
- * afio.c
- *
- * Manipulate archives and files.
- *
- * Copyright (c) 1985 Lachman Associates, Inc..
- *
- * This software was written by Mark Brukhartz at Lachman Associates,
- * Inc.. It may be distributed within the following restrictions:
- * (1) It may not be sold at a profit.
- * (2) This credit and notice must remain intact.
- * This software may be distributed with other software by a commercial
- * vendor, provided that it is included at no additional charge.
- *
- * Please report bugs to "uunet!sawmill!prslnk!buhrt"
- *
- * Options:
- * o Define INDEX to use index() in place of strchr() (v7, BSD).
- * o Define MEMCPY when an efficient memcpy() exists (SysV).
- * o Define MKDIR when a mkdir() system call is present (4.2BSD, SysVr3).
- * o Define NOVOID if your compiler doesn't like void casts.
- * o Define SYSTIME to use <sys/time.h> rather than <time.h> (4.2BSD).
- * o Define VOIDFIX to allow pointers to functions returning void (non-PCC).
- * o Define CTC3B2 to support AT&T 3B2 streaming cartridge tape.
- * o Define HAVEFCNTL if you have <fcntl.h>
- * o Define USESHMEM if you have shared memory (AT&T 3b1)
- * o Define MYTEMPNAM if you don't have tempnam()
- * o Define UNIXPC if you are on a 3b1, 7300, etc.
- * o Define HAVEMEMCMP if you have memcmp otherwise assumes bcmp
- * o Define DEFFMTCMD to being how to format the media you use the most.
- * o Define LONGZFILE if you want .Z to be tagged on the end of a 14 char
- *
- * BUGS:
- * Still needs '286 floppy support.
- *
- * Added by Jeff Buhrt:
- * Floppy Verify/format/restart output in the middle of a set,
- * compress files on output, extended error messages and logging
- *
- * NOTE: If you are compressing and the temp directory you pick is
- * on a NSF file system, watch out for "stale handle" caused
- * by opening the compress output file then unlinking before
- * returning an open file descriptor.
- */
-
- static char *ident = "$Header: /u/buhrt/src/afio/RCS/afio.c,v 2.3 1991/09/25 20:08:33 buhrt Exp $";
-
- #include <stdio.h>
- #include <errno.h>
- #include <sys/signal.h>
- #include <sys/types.h>
- #include <sys/ioctl.h>
- #include <sys/stat.h>
- #include <pwd.h>
- #include <grp.h>
- #include "patchlevel.h"
-
- #ifndef major
- # include <sys/sysmacros.h>
- #endif /* major */
-
- #ifdef SYSTIME
- # include <sys/time.h>
- #else /* SYSTIME */
- # include <time.h>
- #endif /* SYSTIME */
-
- #ifdef CTC3B2
- # include <sys/vtoc.h>
- # include <sys/ct.h>
- #endif /* CTC3B2 */
-
- #ifdef MYTEMPNAM
- #include <sys/file.h>
- #endif
-
- #ifdef USESHMEM
- #include <sys/ipc.h>
- #include <sys/shm.h>
-
- #define NUMSHKEYS 20
- #define SHMEMSIZE 262144 /* 2^18 (dev3b1) */
- #endif
-
- /* done writing to the archive */
- #define FALSE 0
- #define TRUE 1
- #define NOTDONE 0
- #define DONE 1
- #define NODIE 0
- #define DIE 1
-
- /*
- * Address link information base.
- */
- #define linkhash(ino) \
- (linkbase + (ino) % nel(linkbase))
-
- /*
- * Mininum value.
- */
- #define min(one, two) \
- (one < two ? one : two)
-
- /*
- * Number of array elements.
- */
- #define nel(a) \
- (sizeof(a) / sizeof(*(a)))
-
- /*
- * Remove a file or directory.
- */
- #define afremove(name, asb) \
- (((asb)->sb_mode & S_IFMT) == S_IFDIR ? rmdir(name) : unlink(name))
-
- /*
- * Swap bytes.
- */
- #define swab(n) \
- ((((ushort)(n) >> 8) & 0xff) | (((ushort)(n) << 8) & 0xff00))
-
- /*
- * Cast and reduce to unsigned short.
- */
- #define ush(n) \
- (((ushort) (n)) & 0177777)
-
- /*
- * Definitions.
- */
- #define reg register /* Convenience */
- #define uint unsigned int /* Not always in types.h */
- #define ushort unsigned short /* Not always in types.h */
- #define BLOCK 5120 /* Default archive block size */
- #define FSBUF (8*1024) /* Filesystem buffer size */
- #define H_COUNT 10 /* Number of items in ASCII header */
- #define H_PRINT "%06o%06o%06o%06o%06o%06o%06o%011lo%06o%011lo"
- #define H_SCAN "%6ho%6ho%6ho%6ho%6ho%6ho%6ho%11lo%6o%11lo"
- #define H_STRLEN 70 /* ASCII header string length */
- #define M_ASCII "070707" /* ASCII magic number */
- #define M_BINARY 070707 /* Binary magic number */
- #define M_STRLEN 6 /* ASCII magic number length */
- #define NULLDEV -1 /* Null device code */
- #define NULLINO 0 /* Null inode number */
- #define PATHELEM 256 /* Pathname element count limit */
- #define PATHSIZE 1024 /* Pathname length limit */
- #define S_IFSHF 12 /* File type shift (shb in stat.h) */
- #define S_IPERM 07777 /* File permission bits (shb in stat.h) */
- #define S_IPEXE 07000 /* Special execution bits (shb in stat.h) */
- #define S_IPOPN 0777 /* Open access bits (shb in stat.h) */
- #define STDIN 0 /* Standard input file descriptor */
- #define STDOUT 1 /* Standard output file descriptor */
- #define TTY "/dev/tty" /* For volume-change queries */
-
- /*
- * Some versions of the portable "C" compiler (PCC) can't handle
- * pointers to functions returning void.
- */
- #ifdef VOIDFIX
- # define VOIDFN void /* Expect "void (*fnptr)()" to work */
- #else /* VOIDFIX */
- # define VOIDFN int /* Avoid PCC "void (*fnptr)()" bug */
- #endif /* VOIDFIX */
-
- /*
- * Trailer pathnames. All must be of the same length.
- */
- #define TRAILER "TRAILER!!!" /* Archive trailer (cpio compatible) */
- #define TRAILZ 11 /* Trailer pathname length (including null) */
-
- /*
- * Open modes; there is no <fcntl.h> with v7 UNIX.
- */
- #ifdef HAVEFCNTL
- #include <fcntl.h>
- #else
- #define O_RDONLY 0 /* Read-only */
- #define O_WRONLY 1 /* Write-only */
- #define O_RDWR 2 /* Read/write */
- #endif
- /*
- * V7 and BSD UNIX use old-fashioned names for a couple of
- * string functions.
- */
- #ifdef INDEX
- # define strchr index /* Forward character search */
- # define strrchr rindex /* Reverse character search */
- #endif /* INDEX */
-
- /*
- * Some compilers can't handle void casts.
- */
- #ifdef NOVOID
- # define VOID /* Omit void casts */
- #else /* NOVOID */
- # define VOID (void) /* Quiet lint about ignored return values */
- #endif /* NOVOID */
-
- /*
- * Adb is more palatable when static functions and variables are
- * declared as globals. Lint gives more useful information when
- * statics are truly static.
- */
- #ifdef lint
- # define STATIC static /* Declare static variables for lint */
- #else /* lint */
- # define STATIC /* Make static variables global for adb */
- #endif /* lint */
-
- /*
- * Simple types.
- */
- typedef struct group Group; /* Structure for getgrgid(3) */
- typedef struct passwd Passwd; /* Structure for getpwuid(3) */
- typedef struct tm Time; /* Structure for localtime(3) */
-
- #ifdef S_IFLNK
- /*
- * File status with symbolic links. Kludged to hold symbolic
- * link pathname within structure.
- */
- typedef struct {
- struct stat sb_stat;
- char sb_link[PATHSIZE];
- } Stat;
- # define STAT(name, asb) stat(name, &(asb)->sb_stat)
- # define FSTAT(fd, asb) fstat(fd, &(asb)->sb_stat)
- # define LSTAT(name, asb) lstat(name, &(asb)->sb_stat)
- # define sb_dev sb_stat.st_dev
- # define sb_ino sb_stat.st_ino
- # define sb_mode sb_stat.st_mode
- # define sb_nlink sb_stat.st_nlink
- # define sb_uid sb_stat.st_uid
- # define sb_gid sb_stat.st_gid
- # define sb_rdev sb_stat.st_rdev
- # define sb_size sb_stat.st_size
- # define sb_atime sb_stat.st_atime
- # define sb_mtime sb_stat.st_mtime
- # define sb_ctime sb_stat.st_ctime
- # define sb_blksize sb_stat.st_blksize
- # define sb_blocks sb_stat.st_blocks
- #else /* S_IFLNK */
- /*
- * File status without symbolic links.
- */
- typedef struct stat Stat;
- # define STAT(name, asb) stat(name, asb)
- # define FSTAT(fd, asb) fstat(fd, asb)
- # define LSTAT(name, asb) stat(name, asb)
- # define sb_dev st_dev
- # define sb_ino st_ino
- # define sb_mode st_mode
- # define sb_nlink st_nlink
- # define sb_uid st_uid
- # define sb_gid st_gid
- # define sb_rdev st_rdev
- # define sb_size st_size
- # define sb_atime st_atime
- # define sb_mtime st_mtime
- # define sb_ctime st_ctime
- #endif /* S_IFLNK */
-
- /*
- * Binary archive header (obsolete).
- */
- typedef struct {
- short b_dev; /* Device code */
- ushort b_ino; /* Inode number */
- ushort b_mode; /* Type and permissions */
- ushort b_uid; /* Owner */
- ushort b_gid; /* Group */
- short b_nlink; /* Number of links */
- short b_rdev; /* Real device */
- ushort b_mtime[2]; /* Modification time (hi/lo) */
- ushort b_name; /* Length of pathname (with null) */
- ushort b_size[2]; /* Length of data */
- } Binary;
-
- /*
- * Child process structure.
- */
- typedef struct child {
- struct child *c_forw; /* Forward link */
- int c_pid; /* Process ID */
- int c_flags; /* Flags (CF_) */
- int c_status; /* Exit status */
- } Child;
-
- /*
- * Child process flags (c_flags).
- */
- #define CF_EXIT (1<<0) /* Exited */
-
- /*
- * Hard link sources. One or more are chained from each link
- * structure.
- */
- typedef struct name {
- struct name *p_forw; /* Forward chain (terminated) */
- struct name *p_back; /* Backward chain (circular) */
- char *p_name; /* Pathname to link from */
- } Path;
-
- /*
- * File linking information. One entry exists for each unique
- * file with with outstanding hard links.
- */
- typedef struct link {
- struct link *l_forw; /* Forward chain (terminated) */
- struct link *l_back; /* Backward chain (terminated) */
- dev_t l_dev; /* Device */
- ino_t l_ino; /* Inode */
- ushort l_nlink; /* Unresolved link count */
- off_t l_size; /* Length */
- Path *l_path; /* Pathname(s) to link from */
- } Link;
-
- /*
- * Pathnames to (or to not) be processed.
- */
- typedef struct pattern {
- struct pattern *p_forw; /* Forward chain */
- char *p_str; /* String */
- int p_len; /* Length of string */
- int p_not; /* Reverse logic */
- } Pattern;
-
- /*
- * External functions.
- */
- void _exit();
- void exit();
- void free();
- char *getenv();
- ushort getgid();
- Group *getgrgid();
- Passwd *getpwuid();
- ushort getuid();
- Time *localtime();
- off_t lseek();
- char *malloc();
- uint sleep();
- char *strcat();
- char *strchr();
- char *strcpy();
- char *strncpy();
- char *strrchr();
- time_t time();
-
-
- /*
- * Internal functions.
- */
- VOIDFN copyin();
- VOIDFN copyout();
- void compressfile();
- int dirchg();
- int dirmake();
- int dirneed();
- void fatal();
- void goodbye();
- VOIDFN in();
- void inalloc();
- int inascii();
- int inavail();
- int inbinary();
- int indata();
- int inentry();
- int infill();
- int inhead();
- int inread();
- int inskip();
- int inswab();
- int lineget();
- void linkalso();
- Link *linkfrom();
- void linkleft();
- Link *linkto();
- char *memcpy();
- char *memget();
- char *memstr();
- int mkdir();
- void nameadd();
- int namecmp();
- int nameopt();
- void next();
- void nextask();
- void nextclos();
- int nextopen();
- int openin();
- int openotty();
- int openqtty();
- int options();
- off_t optsize();
- VOIDFN out();
- void outalloc();
- uint outavail();
- int outdata();
- void outeof();
- void outflush();
- void outhead();
- void outpad();
- void outwait();
- void outwrite();
- VOIDFN pass();
- void passdata();
- int passitem();
- int pipechld();
- int pipeopen();
- void pipewait();
- void prsize();
- int rmdir();
- VOIDFN (*signal())();
- int fswrite();
- #ifdef USESHMEM
- char *shmemalloc();
- void shmemfree();
- #endif
- char *syserr();
- #ifdef MYTEMPNAM
- char *tempnam();
- else
- extern char *tempnam();
- #endif
- VOIDFN toc();
- void tocentry();
- void tocmode();
- void usage();
- void verify();
- int warn();
- int warnarch();
- int writedisk();
- int xfork();
- void xpause();
- int xwait();
-
- extern int atoi();
- extern int chdir();
- extern int chmod();
- extern int chown();
- extern int dup();
- extern int fork();
- extern int isatty();
- extern int link();
- extern int memcmp();
- extern int mknod();
- extern int pipe();
- extern int read();
- extern int stat();
- extern int strcmp();
- extern int strlen();
- extern int strncmp();
- extern int umask();
- extern int unlink();
- extern int utime();
- extern int wait();
- extern int write();
-
- /*
- * External variables.
- */
- extern int errno; /* System error code */
- extern char *sys_errlist[]; /* System error messages */
- extern int sys_nerr; /* Number of sys_errlist entries */
-
- /*
- * Static variables.
- */
- STATIC short Fflag; /*
- * floppy flag (write when buf full)
- * set -sdisk_size as well
- */
- STATIC short Zflag; /* compress the files that we can */
- STATIC short verifyflag; /* Verify (floppy) flag */
- STATIC short verifycnt;
- #ifdef CTC3B2
- STATIC short Cflag; /* Enable 3B2 CTC streaming (kludge) */
- #endif /* CTC3B2 */
- STATIC short dflag; /* Don't create missing directories */
- STATIC short fflag; /* Fork before writing to archive */
- STATIC short gflag; /* Change to input file directories */
- STATIC short hflag; /* Follow symbolic links */
- STATIC short jflag; /* Don't generate sparse filesystem blocks */
- STATIC short kflag; /* Skip initial junk to find a header */
- STATIC short lflag; /* Link rather than copying (when possible) */
- STATIC short mflag; /* Ignore archived timestamps */
- STATIC short nflag; /* Keep newer existing files */
- STATIC short uflag; /* Report files with unseen links */
- STATIC short vflag; /* Verbose */
- STATIC short xflag; /* Retain file ownership */
- STATIC short zflag; /* Print final statistics */
- STATIC short hidequit; /* show the quit option? */
- STATIC short abspaths; /* allow absolute path names? */
- STATIC uint arbsize = BLOCK;/* Archive block size */
- STATIC short areof; /* End of input volume reached */
- STATIC int arfd = -1; /* Archive file descriptor */
- STATIC off_t arleft; /* Space remaining within current volume */
- STATIC char *arname; /* Expanded archive name */
- STATIC uint arpad; /* Final archive block padding boundary */
- STATIC char arspec[PATHSIZE];/* Specified archive name */
- STATIC off_t aruntil; /* Volume size limit */
- STATIC uint arvolume = 1; /* Volume number */
- STATIC uint buflen; /* Archive buffer length */
- STATIC char *buffer; /* Archive buffer */
- STATIC char *bufidx; /* Archive buffer index */
- STATIC char *bufend; /* End of data within archive buffer */
- STATIC Child *children; /* Child processes */
- STATIC char *formatcmd = DEFFMTCMD; /* how to format */
- STATIC ushort gid; /* Group ID */
- STATIC Link *linkbase[256]; /* Unresolved link information */
- STATIC FILE *logfile = NULL;/* log same errors as stderr would */
- STATIC ushort mask; /* File creation mask */
- STATIC char *myname; /* Arg0 */
- extern char *optarg; /* Option argument */
- extern int optind; /* Command line index */
- STATIC int outpid; /* Process ID of outstanding outflush() */
- STATIC Pattern *pattern; /* Pathname matching patterns */
- STATIC char pwd[PATHSIZE]; /* Working directory (with "-g") */
- STATIC int pipepid; /* Pipeline process ID */
- STATIC time_t timenow; /* Current time */
- STATIC time_t timewait; /* Time spent awaiting new media */
- STATIC off_t total; /* Total number of bytes transferred */
- STATIC int ttyf; /* For interactive queries (yuk) */
- STATIC ushort uid; /* User ID */
- int uncompressrun = 0; /* is uncompress running? its pid if so */
- char uncompto[PATHSIZE]; /* name we uncompressed to */
-
- main(ac, av)
- int ac;
- reg char **av;
- {
- reg int c;
- reg uint group = 1;
- VOIDFN (*fn)() = NULL;
- time_t timedone;
- auto char remote[PATHSIZE];
-
- if (myname = strrchr(*av, '/'))
- ++myname;
- else
- myname = *av;
- mask = umask(0);
- uid = getuid();
- gid = getgid();
- if (uid == 0)
- ++xflag;
- VOID signal(SIGPIPE, SIG_IGN);
- while (c = options(ac, av, "ioptIOVCb:c:de:fghjklmns:uvxXy:Y:zFKZL:R:qA")) {
- switch (c) {
- case 'i':
- if (fn)
- usage();
- fn = in;
- break;
- case 'o':
- if (fn)
- usage();
- fn = out;
- break;
- case 'p':
- if (fn)
- usage();
- fn = pass;
- break;
- case 't':
- if (fn)
- usage();
- fn = toc;
- break;
- case 'I':
- if (fn)
- usage();
- fn = copyin;
- break;
- case 'O':
- if (fn)
- usage();
- fn = copyout;
- break;
- case 'V':
- VOID printf("%s: Version %s dated %s\n",
- myname, VERSION, DATE);
- exit(0);
- #ifdef CTC3B2
- case 'C':
- ++Cflag;
- arbsize = 31 * 512;
- group = 10;
- aruntil = 1469 * 31 * 512;
- break;
- #endif /* CTC3B2 */
- case 'b':
- if ((arbsize = (uint) optsize(optarg)) == 0)
- fatal(optarg, "Bad block size");
- break;
- case 'c':
- if ((group = (uint) optsize(optarg)) == 0)
- fatal(optarg, "Bad buffer count");
- break;
- case 'd':
- ++dflag;
- break;
- case 'e':
- arpad = (uint) optsize(optarg);
- break;
- case 'f':
- ++fflag;
- break;
- case 'g':
- ++gflag;
- break;
- case 'h':
- ++hflag;
- break;
- case 'j':
- ++jflag;
- break;
- case 'k':
- ++kflag;
- break;
- case 'l':
- ++lflag;
- break;
- case 'm':
- ++mflag;
- break;
- case 'n':
- ++nflag;
- break;
- case 's':
- aruntil = optsize(optarg);
- if (aruntil == 0)
- usage();
- break;
- case 'F':
- ++Fflag;
- break;
- case 'Z':
- ++Zflag;
- break;
- case 'K':
- ++verifyflag;
- break;
- case 'u':
- ++uflag;
- break;
- case 'v':
- ++vflag;
- break;
- case 'x':
- ++xflag;
- break;
- case 'X':
- xflag = 0;
- break;
- case 'y':
- nameadd(optarg, 0);
- break;
- case 'Y':
- nameadd(optarg, 1);
- break;
- case 'z':
- ++zflag;
- break;
- case 'L':
- if ((logfile = fopen(optarg, "a")) == (FILE *)0)
- { fprintf(stderr,
- "Can't open %s to append, get help\n",
- optarg);
- exit(1);
- }
- break;
- case 'R':
- formatcmd = optarg;
- break;
- case 'q':
- hidequit = TRUE;
- break;
- case 'A':
- abspaths = TRUE;
- break;
- default:
- usage();
- }
- }
- if (fn == NULL || av[optind] == NULL)
- usage();
- ttyf = openqtty();
- if (Fflag)
- { if ((buflen = aruntil) == 0)
- usage();
- }
- else
- buflen = arbsize * group;
- if (aruntil && (aruntil < arbsize))
- { fprintf(stderr, "Media size %d is less than buffer size %d\n",
- aruntil, arbsize);
- usage();
- }
- if (arpad == 0)
- arpad = arbsize;
- if (fn != pass) {
- reg char *colon;
- reg char *equal;
- reg int isoutput = (fn == out || fn == copyout);
-
- arname = strcpy(arspec, av[optind++]);
- if (colon = strchr(arspec, ':')) {
- *colon++ = '\0';
- if (equal = strchr(arspec, '='))
- *equal++ = '\0';
- VOID sprintf(arname = remote,
- "!rsh %s %s -%c -b %u -c %u %s",
- arspec, equal ? equal : myname,
- isoutput ? 'O' : 'I', arbsize,
- group, colon);
- if (equal)
- *--equal = '=';
- *--colon = ':';
- }
- if (gflag && *arname != '/' && *arname != '!')
- fatal(arspec, "Relative pathname");
- VOID signal(SIGINT, goodbye);
- #ifdef USESHMEM
- /* alloc the space as shared if we will be forking a lot */
- if ((*arspec != '!') && (Fflag || fflag)
- #if ((defined (USESHMEM)) && (defined (UNIXPC)))
- /*
- * UNIXPC and shared memory read/write bug
- * -only a write() is handled correctly below
- * in writedisk()
- */
- && ((fn == out) || (fn == copyout)))
- #else
- )
- #endif
- {
- if ((buffer = bufidx = bufend = shmemalloc(buflen+BLOCK, arname)) == NULL)
- fatal(arspec, "Cannot allocate I/O buffer (shmem)");
- }
- else
- #endif
- /*
- * +BLOCK is added to make sure we don't overrun buffer on a
- * read (internal read(1) length is thus met)
- */
- if ((buffer = bufidx = bufend = malloc(buflen+BLOCK)) == NULL)
- fatal(arspec, "Cannot allocate I/O buffer");
-
- /*
- * open a floppy at the last moment (if output), otherwise now
- * note we set arleft prematurely so we don't have to open the
- * disk now
- */
- if (!Fflag || !isoutput)
- { if (nextopen(isoutput ? O_WRONLY : O_RDONLY) < 0)
- goodbye(1);
- }
- else
- arleft = aruntil;
- }
- timenow = time((time_t *) NULL);
- (*fn)(av + optind);
- timedone = time((time_t *) NULL);
- if (uflag)
- linkleft();
- if (zflag) {
- reg FILE *stream;
-
- stream = fn == toc || arfd == STDOUT ? stderr : stdout;
- VOID fprintf(stream, "%s: ", myname);
- prsize(stream, total);
- VOID fprintf(stream, " bytes %s in %lu seconds. The backup was successfull!\n",
- fn == pass
- ? "transferred"
- : fn == out || fn == copyout
- ? "written"
- : "read",
- timedone - timenow - timewait);
- }
- if (logfile != (FILE *)0)
- { VOID fprintf(logfile, "%s: Successfully backed up ", myname);
- prsize(logfile, total);
- VOID fprintf(logfile,
- " bytes %s in %lu seconds (+waited %d seconds for disk swapping (%u disks)) finished at %s",
- fn == pass
- ? "transferred"
- : fn == out || fn == copyout
- ? "written"
- : "read",
- timedone - timenow - timewait,
- timewait,
- arvolume,
- ctime(&timedone));
- }
- nextclos();
- goodbye(0);
- /* NOTREACHED */
- }
-
- /*
- * copyin()
- *
- * Copy directly from the archive to the standard output.
- */
- STATIC VOIDFN
- copyin(av)
- reg char **av;
- {
- reg int got;
- reg uint have;
-
- if (*av)
- fatal(*av, "Extraneous argument");
- while (!areof) {
- VOID infill();
- while (have = bufend - bufidx)
- if ((got = write(STDOUT, bufidx, have)) < 0)
- fatal("<stdout>", syserr());
- else if (got > 0)
- bufidx += got;
- else
- return;
- }
- }
-
- /*
- * copyout()
- *
- * Copy directly from the standard input to the archive.
- */
- STATIC VOIDFN
- copyout(av)
- reg char **av;
- {
- reg int got;
- reg uint want;
-
- if (*av)
- fatal(*av, "Extraneous argument");
- for (;;) {
- while ((want = bufend - bufidx) == 0)
- outflush(NOTDONE);
- if ((got = read(STDIN, bufidx, want)) < 0)
- fatal("<stdin>", syserr());
- else if (got == 0)
- break;
- else
- bufidx += got;
- }
- outflush(DONE);
- if (fflag)
- outwait();
- }
-
- /*
- * dirchg()
- *
- * Change to the directory containing a given file.
- */
- STATIC int
- dirchg(name, local)
- reg char *name;
- reg char *local;
- {
- reg char *last;
- reg int len;
- auto char dir[PATHSIZE];
-
- if (*name != '/')
- return (warn(name, "Relative pathname"));
- for (last = name + strlen(name); last[-1] != '/'; --last)
- ;
- len = last - name;
- strncpy(dir, name, len)[len] = '\0';
- VOID strcpy(local, *last ? last : ".");
- if (strcmp(dir, pwd) == 0)
- return (0);
- if (chdir(dir) < 0)
- return (warn(name, syserr()));
- VOID strcpy(pwd, dir);
- return (0);
- }
-
- /*
- * dirmake()
- *
- * Make a directory. Returns zero if successful, -1 otherwise.
- */
- STATIC int
- dirmake(name, asb)
- reg char *name;
- reg Stat *asb;
- {
- if (mkdir(name, asb->sb_mode & S_IPOPN) < 0)
- return (-1);
- if (asb->sb_mode & S_IPEXE)
- VOID chmod(name, asb->sb_mode & S_IPERM);
- if (xflag)
- VOID chown(name,
- uid == 0 ? ush(asb->sb_uid) : uid,
- ush(asb->sb_gid));
- return (0);
- }
-
- /*
- * dirneed()
- *
- * Recursively create missing directories (with the same permissions
- * as their first existing parent). Temporarily modifies the 'name'
- * argument string. Returns zero if successful, -1 otherwise.
- */
- STATIC int
- dirneed(name)
- char *name;
- {
- reg char *cp;
- reg char *last;
- reg int ok;
- static Stat sb;
-
- last = NULL;
- for (cp = name; *cp; )
- if (*cp++ == '/')
- last = cp;
- if (last == NULL)
- return (STAT(".", &sb));
- *--last = '\0';
- ok = STAT(*name ? name : "/", &sb) == 0
- ? ((sb.sb_mode & S_IFMT) == S_IFDIR)
- : (!dflag && dirneed(name) == 0 && dirmake(name, &sb) == 0);
- *last = '/';
- return (ok ? 0 : -1);
- }
-
- /*
- * fatal()
- *
- * Print fatal message and exit.
- */
- STATIC void
- fatal(what, why)
- char *what;
- char *why;
- {
- VOID warn(what, why);
- goodbye(1);
- }
-
- /*
- * in()
- *
- * Read an archive.
- */
- STATIC VOIDFN
- in(av)
- reg char **av;
- {
- auto Stat sb;
- auto char name[PATHSIZE];
- int sel;
-
- if (*av)
- fatal(*av, "Extraneous argument");
- name[0] = '\0';
- while (inhead(name, &sb) == 0) {
- if (((sel = namecmp(name)) < 0) || inentry(name, &sb) < 0)
- if (inskip(sb.sb_size) < 0)
- VOID warn(name, "Skipped file data is corrupt");
- if (vflag && (sel == 0))
- { if (*uncompto)
- VOID fprintf(stderr, "%s uncompressed to: %s\n",
- name, uncompto);
- else
- VOID fprintf(stderr, "%s\n", name);
- }
- }
- }
-
- /*
- * inalloc()
- *
- * Allocate input buffer space (which was previously indexed
- * by inavail()).
- */
- STATIC void
- inalloc(len)
- reg uint len;
- {
- bufidx += len;
- total += len;
- }
-
- /*
- * inascii()
- *
- * Read an ASCII header. Returns zero if successful;
- * -1 otherwise. Assumes that the entire magic number
- * has been read.
- */
- STATIC int
- inascii(magic, name, asb)
- reg char *magic;
- reg char *name;
- reg Stat *asb;
- {
- auto uint namelen;
- auto char header[H_STRLEN + 1];
-
- if (strncmp(magic, M_ASCII, M_STRLEN) != 0)
- return (-1);
- if (inread(header, H_STRLEN) < 0)
- return (warnarch("Corrupt ASCII header", (off_t) H_STRLEN));
- header[H_STRLEN] = '\0';
- if (sscanf(header, H_SCAN, &asb->sb_dev,
- &asb->sb_ino, &asb->sb_mode, &asb->sb_uid,
- &asb->sb_gid, &asb->sb_nlink, &asb->sb_rdev,
- &asb->sb_mtime, &namelen, &asb->sb_size) != H_COUNT)
- return (warnarch("Bad ASCII header", (off_t) H_STRLEN));
- if (namelen == 0 || namelen >= PATHSIZE)
- return (warnarch("Bad ASCII pathname length", (off_t) H_STRLEN));
- if (inread(name, namelen) < 0)
- return (warnarch("Corrupt ASCII pathname", (off_t) namelen));
- if (name[namelen - 1] != '\0')
- return (warnarch("Bad ASCII pathname", (off_t) namelen));
- return (0);
- }
-
- /*
- * inavail()
- *
- * Index availible input data within the buffer. Stores a pointer
- * to the data and its length in given locations. Returns zero with
- * valid data, -1 if unreadable portions were replaced with nulls.
- */
- STATIC int
- inavail(bufp, lenp)
- reg char **bufp;
- uint *lenp;
- {
- reg uint have;
- reg int corrupt = 0;
-
- while ((have = bufend - bufidx) == 0)
- corrupt |= infill();
- *bufp = bufidx;
- *lenp = have;
- return (corrupt);
- }
-
- /*
- * inbinary()
- *
- * Read a binary header. Returns the number of trailing alignment
- * bytes to skip; -1 if unsuccessful.
- */
- STATIC int
- inbinary(magic, name, asb)
- reg char *magic;
- reg char *name;
- reg Stat *asb;
- {
- reg uint namefull;
- auto Binary binary;
-
- if ((int)*((ushort *) magic) != M_BINARY)
- return (-1);
- memcpy((char *) &binary,
- magic + sizeof(ushort),
- M_STRLEN - sizeof(ushort));
- if (inread((char *) &binary + M_STRLEN - sizeof(ushort),
- sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0)
- return (warnarch("Corrupt binary header",
- (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
- asb->sb_dev = binary.b_dev;
- asb->sb_ino = binary.b_ino;
- asb->sb_mode = binary.b_mode;
- asb->sb_uid = binary.b_uid;
- asb->sb_gid = binary.b_gid;
- asb->sb_nlink = binary.b_nlink;
- asb->sb_rdev = binary.b_rdev;
- asb->sb_mtime = binary.b_mtime[0] << 16 | binary.b_mtime[1];
- asb->sb_size = binary.b_size[0] << 16 | binary.b_size[1];
- if (binary.b_name == 0 || binary.b_name >= PATHSIZE)
- return (warnarch("Bad binary pathname length",
- (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
- if (inread(name, namefull = binary.b_name + binary.b_name % 2) < 0)
- return (warnarch("Corrupt binary pathname", (off_t) namefull));
- if (name[binary.b_name - 1] != '\0')
- return (warnarch("Bad binary pathname", (off_t) namefull));
- return (asb->sb_size % 2);
- }
-
- /*
- * indata()
- *
- * Install data from an archive. Returns given file descriptor.
- */
- STATIC int
- indata(fd, size, name)
- int fd;
- reg off_t size;
- char *name;
- {
- reg uint chunk;
- reg char *oops;
- reg int sparse;
- reg int corrupt;
- auto char *buf;
- auto uint avail;
-
- corrupt = sparse = 0;
- oops = NULL;
- while (size) {
- corrupt |= inavail(&buf, &avail);
- size -= (chunk = size < avail ? (uint) size : avail);
- if (oops == NULL && (sparse = fswrite(fd, buf, chunk)) < 0)
- oops = syserr();
- inalloc(chunk);
- }
- if (corrupt)
- VOID warn(name, "Corrupt archive data");
- if (oops)
- VOID warn(name, oops);
- else if (sparse > 0
- && (lseek(fd, (off_t) -1, 1) < 0
- || write(fd, "", 1) != 1))
- VOID warn(name, syserr());
- return (fd);
- }
-
- /*
- * inentry()
- *
- * Install a single archive entry. Returns zero if successful, -1 otherwise.
- */
- STATIC int
- inentry(name, asb)
- char *name;
- reg Stat *asb;
- {
- reg Link *linkp;
- reg int ifd;
- reg int ofd;
- auto time_t tstamp[2];
-
- if ((ofd = openotty(name, asb, linkp = linkfrom(asb), 0, Zflag)) > 0)
- { if (asb->sb_size || linkp == NULL || linkp->l_size == 0)
- VOID close(indata(ofd, asb->sb_size, name));
- else if ((ifd = open(linkp->l_path->p_name, O_RDONLY)) < 0)
- VOID warn(linkp->l_path->p_name, syserr());
- else {
- passdata(linkp->l_path->p_name, ifd, name, ofd);
- VOID close(ifd);
- VOID close(ofd);
- }
- /* safety */
- if (uncompressrun)
- { VOID xwait(uncompressrun, "inentry xwait()", TRUE);
- uncompressrun = 0;
- }
- }
- else if (ofd < 0)
- return (-1);
- else if (inskip(asb->sb_size) < 0)
- VOID warn(name, "Redundant file data is corrupt");
- tstamp[0] = tstamp[1] = mflag ? timenow : asb->sb_mtime;
- VOID utime(name, tstamp);
- return (0);
- }
-
- /*
- * infill()
- *
- * Fill the archive buffer. Remembers mid-buffer read failures and
- * reports them the next time through. Replaces unreadable data with
- * null characters. Returns zero with valid data, -1 otherwise.
- */
- STATIC int
- infill()
- {
- reg int got;
- static int failed;
-
- bufend = bufidx = buffer;
- if (!failed) {
- if (areof)
- if (total == 0)
- fatal(arspec, "No input");
- else
- next(O_RDONLY, "Input EOF");
- if (aruntil && arleft < arbsize)
- next(O_RDONLY, "Input limit reached");
- while (!failed
- && !areof
- && (aruntil == 0 || arleft >= arbsize)
- && buffer + buflen - bufend >= arbsize) {
- if ((got = read(arfd, bufend, arbsize)) > 0) {
- bufend += got;
- arleft -= got;
- } else if (got < 0)
- failed = warnarch(syserr(),
- (off_t) 0 - (bufend - bufidx));
- else
- ++areof;
- }
- }
- if (failed && bufend == buffer) {
- failed = 0;
- for (got = 0; got < arbsize; ++got)
- *bufend++ = '\0';
- return (-1);
- }
- return (0);
- }
-
- /*
- * inhead()
- *
- * Read a header. Quietly translates old-fashioned binary cpio headers
- * (and arranges to skip the possible alignment byte). Returns zero if
- * successful, -1 upon archive trailer.
- */
- STATIC int
- inhead(name, asb)
- reg char *name;
- reg Stat *asb;
- {
- reg off_t skipped;
- auto char magic[M_STRLEN];
- static int align;
-
- if (align > 0)
- VOID inskip((off_t) align);
- align = 0;
- for (;;) {
- VOID inread(magic, M_STRLEN);
- skipped = 0;
- while ((align = inascii(magic, name, asb)) < 0
- && (align = inbinary(magic, name, asb)) < 0
- && (align = inswab(magic, name, asb)) < 0) {
- if (++skipped == 1) {
- if (!kflag && total - sizeof(magic) == 0)
- fatal(arspec, "Unrecognizable archive");
- VOID warnarch("Bad magic number",
- (off_t) sizeof(magic));
- if (name[0])
- VOID warn(name, "May be corrupt");
- }
- memcpy(magic, magic + 1, sizeof(magic) - 1);
- VOID inread(magic + sizeof(magic) - 1, 1);
- }
- if (skipped) {
- VOID warnarch("Apparently resynchronized",
- (off_t) sizeof(magic));
- VOID warn(name, "Continuing");
- }
- if (strcmp(name, TRAILER) == 0)
- return (-1);
- if (nameopt(name) >= 0)
- break;
- VOID inskip(asb->sb_size + align);
- }
- #ifdef S_IFLNK
- if ((asb->sb_mode & S_IFMT) == S_IFLNK) {
- if (inread(asb->sb_link, (uint) asb->sb_size) < 0) {
- VOID warn(name, "Corrupt symbolic link");
- return (inhead(name, asb));
- }
- asb->sb_link[asb->sb_size] = '\0';
- asb->sb_size = 0;
- }
- #endif /* S_IFLNK */
- if ((name[0] == '/') && !abspaths)
- if (name[1])
- while (name[0] = name[1])
- ++name;
- else
- name[0] = '.';
- asb->sb_atime = asb->sb_ctime = asb->sb_mtime;
- return (0);
- }
-
- /*
- * inread()
- *
- * Read a given number of characters from the input archive. Returns
- * zero with valid data, -1 if unreadable portions were replaced by
- * null characters.
- */
- STATIC int
- inread(dst, len)
- reg char *dst;
- uint len;
- {
- reg uint have;
- reg uint want;
- reg int corrupt = 0;
- char *endx = dst + len;
-
- while (want = endx - dst) {
- while ((have = bufend - bufidx) == 0)
- corrupt |= infill();
- if (have > want)
- have = want;
- memcpy(dst, bufidx, have);
- bufidx += have;
- dst += have;
- total += have;
- }
- return (corrupt);
- }
-
- /*
- * inskip()
- *
- * Skip input archive data. Returns zero under normal circumstances,
- * -1 if unreadable data is encountered.
- */
- STATIC int
- inskip(len)
- reg off_t len;
- {
- reg uint chunk;
- reg int corrupt = 0;
-
- while (len) {
- while ((chunk = bufend - bufidx) == 0)
- corrupt |= infill();
- if (chunk > len)
- chunk = len;
- bufidx += chunk;
- len -= chunk;
- total += chunk;
- }
- return (corrupt);
- }
-
- /*
- * inswab()
- *
- * Read a reversed byte order binary header. Returns the number
- * of trailing alignment bytes to skip; -1 if unsuccessful.
- */
- STATIC int
- inswab(magic, name, asb)
- reg char *magic;
- reg char *name;
- reg Stat *asb;
- {
- reg ushort namesize;
- reg uint namefull;
- auto Binary binary;
-
- if ((int)*((ushort *) magic) != swab(M_BINARY))
- return (-1);
- memcpy((char *) &binary,
- magic + sizeof(ushort),
- M_STRLEN - sizeof(ushort));
- if (inread((char *) &binary + M_STRLEN - sizeof(ushort),
- sizeof(binary) - (M_STRLEN - sizeof(ushort))) < 0)
- return (warnarch("Corrupt swapped header",
- (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
- asb->sb_dev = (dev_t) swab(binary.b_dev);
- asb->sb_ino = (ino_t) swab(binary.b_ino);
- asb->sb_mode = swab(binary.b_mode);
- asb->sb_uid = swab(binary.b_uid);
- asb->sb_gid = swab(binary.b_gid);
- asb->sb_nlink = swab(binary.b_nlink);
- asb->sb_rdev = (dev_t) swab(binary.b_rdev);
- asb->sb_mtime = swab(binary.b_mtime[0]) << 16 | swab(binary.b_mtime[1]);
- asb->sb_size = swab(binary.b_size[0]) << 16 | swab(binary.b_size[1]);
- if ((namesize = swab(binary.b_name)) == 0 || namesize >= PATHSIZE)
- return (warnarch("Bad swapped pathname length",
- (off_t) sizeof(binary) - (M_STRLEN - sizeof(ushort))));
- if (inread(name, namefull = namesize + namesize % 2) < 0)
- return (warnarch("Corrupt swapped pathname", (off_t) namefull));
- if (name[namesize - 1] != '\0')
- return (warnarch("Bad swapped pathname", (off_t) namefull));
- return (asb->sb_size % 2);
- }
-
- /*
- * lineget()
- *
- * Get a line from a given stream. Returns 0 if successful, -1 at EOF.
- */
- STATIC int
- lineget(stream, buf)
- reg FILE *stream;
- reg char *buf;
- {
- reg int c;
-
- for (;;) {
- if ((c = getc(stream)) == EOF)
- return (-1);
- if (c == '\n')
- break;
- *buf++ = c;
- }
- *buf = '\0';
- return (0);
- }
-
- /*
- * linkalso()
- *
- * Add a destination pathname to an existing chain. Assumes that
- * at least one element is present.
- */
- STATIC void
- linkalso(linkp, name)
- reg Link *linkp;
- char *name;
- {
- reg Path *path;
-
- if (((path = (Path *) memget(sizeof(Path))) == NULL)
- || ((path->p_name = memstr(name)) == NULL))
- return;
- path->p_forw = NULL;
- path->p_back = linkp->l_path->p_back;
- path->p_back->p_forw = path;
- linkp->l_path->p_back = path;
- }
-
- /*
- * linkfrom()
- *
- * Find a file to link from. Returns a pointer to a link
- * structure, or NULL if unsuccessful.
- */
- STATIC Link *
- linkfrom(asb)
- reg Stat *asb;
- {
- reg Link *linkp;
- reg Link *linknext;
- reg Path *path;
- reg Path *pathnext;
- reg Link **abase;
-
- for (linkp = *(abase = linkhash(asb->sb_ino)); linkp; linkp = linknext)
- if (linkp->l_nlink == 0) {
- for (path = linkp->l_path; path; path = pathnext) {
- pathnext = path->p_forw;
- free(path->p_name);
- }
- free((char *) linkp->l_path);
- if (linknext = linkp->l_forw)
- linknext->l_back = linkp->l_back;
- if (linkp->l_back)
- linkp->l_back->l_forw = linkp->l_forw;
-