Add pax from 4.4BSD-Lite Release 2
authorJason Self <j@jxself.org>
Mon, 10 Jun 2019 00:25:06 +0000 (17:25 -0700)
committerJason Self <j@jxself.org>
Mon, 10 Jun 2019 00:25:06 +0000 (17:25 -0700)
Signed-off-by: Jason Self <j@jxself.org>
27 files changed:
usr/src/bin/pax/Makefile [new file with mode: 0644]
usr/src/bin/pax/ar_io.c [new file with mode: 0644]
usr/src/bin/pax/ar_subs.c [new file with mode: 0644]
usr/src/bin/pax/buf_subs.c [new file with mode: 0644]
usr/src/bin/pax/cache.c [new file with mode: 0644]
usr/src/bin/pax/cache.h [new file with mode: 0644]
usr/src/bin/pax/cpio.c [new file with mode: 0644]
usr/src/bin/pax/cpio.h [new file with mode: 0644]
usr/src/bin/pax/extern.h [new file with mode: 0644]
usr/src/bin/pax/file_subs.c [new file with mode: 0644]
usr/src/bin/pax/ftree.c [new file with mode: 0644]
usr/src/bin/pax/ftree.h [new file with mode: 0644]
usr/src/bin/pax/gen_subs.c [new file with mode: 0644]
usr/src/bin/pax/options.c [new file with mode: 0644]
usr/src/bin/pax/options.h [new file with mode: 0644]
usr/src/bin/pax/pat_rep.c [new file with mode: 0644]
usr/src/bin/pax/pat_rep.h [new file with mode: 0644]
usr/src/bin/pax/pax.1 [new file with mode: 0644]
usr/src/bin/pax/pax.c [new file with mode: 0644]
usr/src/bin/pax/pax.h [new file with mode: 0644]
usr/src/bin/pax/sel_subs.c [new file with mode: 0644]
usr/src/bin/pax/sel_subs.h [new file with mode: 0644]
usr/src/bin/pax/tables.c [new file with mode: 0644]
usr/src/bin/pax/tables.h [new file with mode: 0644]
usr/src/bin/pax/tar.c [new file with mode: 0644]
usr/src/bin/pax/tar.h [new file with mode: 0644]
usr/src/bin/pax/tty_subs.c [new file with mode: 0644]

diff --git a/usr/src/bin/pax/Makefile b/usr/src/bin/pax/Makefile
new file mode 100644 (file)
index 0000000..578f65a
--- /dev/null
@@ -0,0 +1,32 @@
+#       @(#)Makefile   8.1 (Berkeley) 5/31/93
+
+# To install on versions prior to BSD 4.4 the following may have to be
+# defined with CFLAGS +=
+#
+# -DNET2_STAT  Use NET2 or older stat structure. The version of the
+#              stat structure is easily determined by looking at the
+#              basic type of an off_t (often defined in the file:
+#              /usr/include/sys/types.h). If off_t is a long (and is
+#              NOT A quad) then you must define NET2_STAT.
+#              This define is important, as if you do have a quad_t
+#              off_t and define NET2_STAT, pax will compile but will
+#              NOT RUN PROPERLY.
+#
+# -DNET2_FTS   Use the older NET2 fts. To identify the version,
+#              examine the file: /usr/include/fts.h. If FTS_COMFOLLOW
+#              is not defined then you must define NET2_FTS.
+#              Pax may not compile if this not (un)defined properly.
+#
+# -DNET2_REGEX Use the older regexp.h not regex.h. The regex version
+#              is determined by looking at the value returned by
+#              regexec() (man 3 regexec). If regexec return a 1 for
+#              success (and NOT a 0 for success) you have the older
+#              regex routines and must define NET2_REGEX.
+#              Pax may not compile if this not (un)defined properly.
+
+PROG=   pax
+SRCS=  ar_io.c ar_subs.c buf_subs.c cache.c cpio.c file_subs.c ftree.c\
+       gen_subs.c options.c pat_rep.c pax.c sel_subs.c tables.c tar.c\
+       tty_subs.c
+
+.include <bsd.prog.mk>
diff --git a/usr/src/bin/pax/ar_io.c b/usr/src/bin/pax/ar_io.c
new file mode 100644 (file)
index 0000000..d1bc224
--- /dev/null
@@ -0,0 +1,1284 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ar_io.c    8.2 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mtio.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "extern.h"
+
+/*
+ * Routines which deal directly with the archive I/O device/file.
+ */
+
+#define DMOD           0666            /* default mode of created archives */
+#define EXT_MODE       O_RDONLY        /* open mode for list/extract */
+#define AR_MODE                (O_WRONLY | O_CREAT | O_TRUNC)  /* mode for archive */
+#define APP_MODE       O_RDWR          /* mode for append */
+#define STDO           "<STDOUT>"      /* psuedo name for stdout */
+#define STDN           "<STDIN>"       /* psuedo name for stdin */
+static int arfd = -1;                  /* archive file descriptor */
+static int artyp = ISREG;              /* archive type: file/FIFO/tape */
+static int arvol = 1;                  /* archive volume number */
+static int lstrval = -1;               /* return value from last i/o */
+static int io_ok;                      /* i/o worked on volume after resync */
+static int did_io;                     /* did i/o ever occur on volume? */
+static int done;                       /* set via tty termination */
+static struct stat arsb;               /* stat of archive device at open */
+static int invld_rec;                  /* tape has out of spec record size */
+static int wr_trail = 1;               /* trailer was rewritten in append */
+static int can_unlnk = 0;              /* do we unlink null archives?  */
+char *arcname;                         /* printable name of archive */
+
+static int get_phys __P((void));
+extern sigset_t s_mask;
+
+/*
+ * ar_open()
+ *     Opens the next archive volume. Determines the type of the device and
+ *     sets up block sizes as required by the archive device and the format.
+ *     Note: we may be called with name == NULL on the first open only.
+ * Return:
+ *     -1 on failure, 0 otherwise
+ */
+
+#if __STDC__
+int
+ar_open(char *name)
+#else
+int
+ar_open(name)
+       char *name;
+#endif
+{
+        struct mtget mb;
+
+       if (arfd != -1)
+               (void)close(arfd);
+       arfd = -1;
+       can_unlnk = did_io = io_ok = invld_rec = 0;
+       artyp = ISREG;
+       flcnt = 0;
+
+       /*
+        * open based on overall operation mode
+        */
+       switch (act) {
+       case LIST:
+       case EXTRACT:
+               if (name == NULL) {
+                       arfd = STDIN_FILENO;
+                       arcname = STDN;
+               } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0)
+                       syswarn(0, errno, "Failed open to read on %s", name);
+               break;
+       case ARCHIVE:
+               if (name == NULL) {
+                       arfd = STDOUT_FILENO;
+                       arcname = STDO;
+               } else if ((arfd = open(name, AR_MODE, DMOD)) < 0)
+                       syswarn(0, errno, "Failed open to write on %s", name);
+               else
+                       can_unlnk = 1;
+               break;
+       case APPND:
+               if (name == NULL) {
+                       arfd = STDOUT_FILENO;
+                       arcname = STDO;
+               } else if ((arfd = open(name, APP_MODE, DMOD)) < 0)
+                       syswarn(0, errno, "Failed open to read/write on %s",
+                               name);
+               break;
+       case COPY:
+               /*
+                * arfd not used in COPY mode
+                */
+               arcname = "<NONE>";
+               lstrval = 1;
+               return(0);
+       }
+       if (arfd < 0)
+               return(-1);
+
+       /*
+        * set up is based on device type
+        */
+       if (fstat(arfd, &arsb) < 0) {
+               syswarn(0, errno, "Failed stat on %s", arcname);
+               (void)close(arfd);
+               arfd = -1;
+               can_unlnk = 0;
+               return(-1);
+       }
+       if (S_ISDIR(arsb.st_mode)) {
+               warn(0, "Cannot write an archive on top of a directory %s",
+                   arcname);
+               (void)close(arfd);
+               arfd = -1;
+               can_unlnk = 0;
+               return(-1);
+       }
+
+       if (S_ISCHR(arsb.st_mode))
+               artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE;
+       else if (S_ISBLK(arsb.st_mode))
+               artyp = ISBLK;
+       else if ((lseek(arfd, (off_t)0L, SEEK_CUR) == -1) && (errno == ESPIPE))
+               artyp = ISPIPE;
+       else
+               artyp = ISREG;
+
+       /*
+        * make sure we beyond any doubt that we only can unlink regular files
+        * we created
+        */
+       if (artyp != ISREG)
+               can_unlnk = 0;
+       /*
+        * if we are writing, we are done
+        */
+       if (act == ARCHIVE) {
+               blksz = rdblksz = wrblksz;
+               lstrval = 1;
+               return(0);
+       }
+
+       /*
+        * set default blksz on read. APPNDs writes rdblksz on the last volume
+        * On all new archive volumes, we shift to wrblksz (if the user
+        * specified one, otherwize we will continue to use rdblksz). We
+        * must to set blocksize based on what kind of device the archive is
+        * stored.
+        */
+       switch(artyp) {
+       case ISTAPE:
+               /*
+                * Tape drives come in at least two flavors. Those that support
+                * variable sized records and those that have fixed sized
+                * records. They must be treated differently. For tape drives
+                * that support variable sized records, we must make large
+                * reads to make sure we get the entire record, otherwise we
+                * will just get the first part of the record (up to size we
+                * asked). Tapes with fixed sized records may or may not return
+                * multiple records in a single read. We really do not care
+                * what the physical record size is UNLESS we are going to
+                * append. (We will need the physical block size to rewrite
+                * the trailer). Only when we are appending do we go to the
+                * effort to figure out the true PHYSICAL record size.
+                */
+               blksz = rdblksz = MAXBLK;
+               break;
+       case ISPIPE:
+       case ISBLK:
+       case ISCHR:
+               /*
+                * Blocksize is not a major issue with these devices (but must
+                * be kept a multiple of 512). If the user specified a write
+                * block size, we use that to read. Under append, we must
+                * always keep blksz == rdblksz. Otherwise we go ahead and use
+                * the device optimal blocksize as (and if) returned by stat
+                * and if it is within pax specs.
+                */
+               if ((act == APPND) && wrblksz) {
+                       blksz = rdblksz = wrblksz;
+                       break;
+               }
+
+               if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) &&
+                   ((arsb.st_blksize % BLKMULT) == 0))
+                       rdblksz = arsb.st_blksize;
+               else
+                       rdblksz = DEVBLK;
+               /*
+                * For performance go for large reads when we can without harm
+                */
+               if ((act == APPND) || (artyp == ISCHR))
+                       blksz = rdblksz;
+               else
+                       blksz = MAXBLK;
+               break;
+       case ISREG:
+               /*
+                * if the user specified wrblksz works, use it. Under appends
+                * we must always keep blksz == rdblksz
+                */
+               if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){
+                       blksz = rdblksz = wrblksz;
+                       break;
+               }
+               /*
+                * See if we can find the blocking factor from the file size
+                */
+               for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT)
+                       if ((arsb.st_size % rdblksz) == 0)
+                               break;
+               /*
+                * When we cannont find a match, we may have a flawed archive.
+                */
+               if (rdblksz <= 0)
+                       rdblksz = FILEBLK;
+               /*
+                * for performance go for large reads when we can
+                */
+               if (act == APPND)
+                       blksz = rdblksz;
+               else
+                       blksz = MAXBLK;
+               break;
+       default:
+               /*
+                * should never happen, worse case, slow... 
+                */
+               blksz = rdblksz = BLKMULT;
+               break;
+       }
+       lstrval = 1;
+       return(0);
+}
+
+/*
+ * ar_close()
+ *     closes archive device, increments volume number, and prints i/o summary
+ */
+#if __STDC__
+void
+ar_close(void)
+#else
+void
+ar_close()
+#endif
+{
+       FILE *outf;
+
+       if (arfd < 0) {
+               did_io = io_ok = flcnt = 0;
+               return;
+       }
+
+       if (act == LIST)
+               outf = stdout;
+       else
+               outf = stderr;
+
+       /*
+        * Close archive file. This may take a LONG while on tapes (we may be
+        * forced to wait for the rewind to complete) so tell the user what is
+        * going on (this avoids the user hitting control-c thinking pax is
+        * broken).
+        */
+       if (vflag && (artyp == ISTAPE)) {
+               if (vfpart)
+                       (void)putc('\n', outf);
+               (void)fprintf(outf,
+                       "%s: Waiting for tape drive close to complete...",
+                       argv0);
+               (void)fflush(outf);
+       }
+
+       /*
+        * if nothing was written to the archive (and we created it), we remove
+        * it
+        */
+       if (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) &&
+           (arsb.st_size == 0)) {
+               (void)unlink(arcname);
+               can_unlnk = 0;
+       }
+
+       (void)close(arfd);
+
+       if (vflag && (artyp == ISTAPE)) {
+               (void)fputs("done.\n", outf);
+               vfpart = 0;
+               (void)fflush(outf);
+       }
+       arfd = -1;
+
+       if (!io_ok && !did_io) {
+               flcnt = 0;
+               return;
+       }
+       did_io = io_ok = 0;
+
+       /*
+        * The volume number is only increased when the last device has data
+        * and we have already determined the archive format.
+        */
+       if (frmt != NULL)
+               ++arvol;
+
+       if (!vflag) {
+               flcnt = 0;
+               return;
+       }
+
+       /*
+        * Print out a summary of I/O for this archive volume.
+        */
+       if (vfpart) {
+               (void)putc('\n', outf);
+               vfpart = 0;
+       }
+
+       /*
+        * If we have not determined the format yet, we just say how many bytes
+        * we have skipped over looking for a header to id. there is no way we
+        * could have written anything yet.
+        */
+       if (frmt == NULL) {
+#      ifdef NET2_STAT
+               (void)fprintf(outf, "%s: unknown format, %lu bytes skipped.\n",
+#      else
+               (void)fprintf(outf, "%s: unknown format, %qu bytes skipped.\n",
+#      endif
+                   argv0, rdcnt);
+               (void)fflush(outf);
+               flcnt = 0;
+               return;
+       }
+
+       (void)fprintf(outf,
+#      ifdef NET2_STAT
+           "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n",
+#      else
+           "%s: %s vol %d, %lu files, %qu bytes read, %qu bytes written.\n",
+#      endif
+           argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt);
+       (void)fflush(outf);
+       flcnt = 0;
+}
+
+/*
+ * ar_drain()
+ *     drain any archive format independent padding from an archive read
+ *     from a socket or a pipe. This is to prevent the process on the
+ *     other side of the pipe from getting a SIGPIPE (pax will stop
+ *     reading an archive once a format dependent trailer is detected).
+ */
+#if __STDC__
+void
+ar_drain(void)
+#else
+void
+ar_drain()
+#endif
+{
+       register int res;
+       char drbuf[MAXBLK];
+
+       /*
+        * we only drain from a pipe/socket. Other devices can be closed
+        * without reading up to end of file. We sure hope that pipe is closed
+        * on the other side so we will get an EOF.
+        */
+       if ((artyp != ISPIPE) || (lstrval <= 0))
+               return;
+
+       /*
+        * keep reading until pipe is drained
+        */
+       while ((res = read(arfd, drbuf, sizeof(drbuf))) > 0)
+               ;
+       lstrval = res;
+}
+
+/*
+ * ar_set_wr()
+ *     Set up device right before switching from read to write in an append.
+ *     device dependent code (if required) to do this should be added here.
+ *     For all archive devices we are already positioned at the place we want
+ *     to start writing when this routine is called.
+ * Return:
+ *     0 if all ready to write, -1 otherwise
+ */
+
+#if __STDC__
+int
+ar_set_wr(void)
+#else
+int
+ar_set_wr()
+#endif
+{
+       off_t cpos;
+
+       /*
+        * we must make sure the trailer is rewritten on append, ar_next()
+        * will stop us if the archive containing the trailer was not written
+        */
+       wr_trail = 0;
+       
+       /* 
+        * Add any device dependent code as required here
+        */
+       if (artyp != ISREG)
+               return(0);
+       /*
+        * Ok we have an archive in a regular file. If we were rewriting a
+        * file, we must get rid of all the stuff after the current offset
+        * (it was not written by pax).
+        */
+       if (((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) ||
+           (ftruncate(arfd, cpos) < 0)) {
+               syswarn(1, errno, "Unable to truncate archive file");
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * ar_app_ok()
+ *     check if the last volume in the archive allows appends. We cannot check
+ *     this until we are ready to write since there is no spec that says all 
+ *     volumes in a single archive have to be of the same type...
+ * Return:
+ *     0 if we can append, -1 otherwise.
+ */
+
+#if __STDC__
+int
+ar_app_ok(void)
+#else
+int
+ar_app_ok()
+#endif
+{
+       if (artyp == ISPIPE) {
+               warn(1, "Cannot append to an archive obtained from a pipe.");
+               return(-1);
+       }
+
+       if (!invld_rec)
+               return(0);
+       warn(1,"Cannot append, device record size %d does not support %s spec",
+               rdblksz, argv0);
+       return(-1);
+}
+
+/*
+ * ar_read()
+ *     read up to a specified number of bytes from the archive into the
+ *     supplied buffer. When dealing with tapes we may not always be able to
+ *     read what we want.
+ * Return:
+ *     Number of bytes in buffer. 0 for end of file, -1 for a read error.
+ */
+
+#if __STDC__
+int
+ar_read(register char *buf, register int cnt)
+#else
+int
+ar_read(buf, cnt)
+       register char *buf;
+       register int cnt;
+#endif
+{
+       register int res = 0;
+
+       /*
+        * if last i/o was in error, no more reads until reset or new volume
+        */
+       if (lstrval <= 0)
+               return(lstrval);
+
+       /*
+        * how we read must be based on device type
+        */
+       switch (artyp) {
+       case ISTAPE:
+               if ((res = read(arfd, buf, cnt)) > 0) {
+                       /*
+                        * CAUTION: tape systems may not always return the same
+                        * sized records so we leave blksz == MAXBLK. The
+                        * physical record size that a tape drive supports is
+                        * very hard to determine in a uniform and portable
+                        * manner.
+                        */
+                       io_ok = 1;
+                       if (res != rdblksz) {
+                               /*
+                                * Record size changed. If this is happens on
+                                * any record after the first, we probably have
+                                * a tape drive which has a fixed record size
+                                * we are getting multiple records in a single
+                                * read). Watch out for record blocking that
+                                * violates pax spec (must be a multiple of
+                                * BLKMULT).
+                                */
+                               rdblksz = res;
+                               if (rdblksz % BLKMULT)
+                                       invld_rec = 1;
+                       }
+                       return(res);
+               }
+               break;
+       case ISREG:
+       case ISBLK:
+       case ISCHR:
+       case ISPIPE:
+       default:
+               /*
+                * Files are so easy to deal with. These other things cannot
+                * be trusted at all. So when we are dealing with character
+                * devices and pipes we just take what they have ready for us
+                * and return. Trying to do anything else with them runs the
+                * risk of failure.
+                */
+               if ((res = read(arfd, buf, cnt)) > 0) {
+                       io_ok = 1;
+                       return(res);
+               }
+               break;
+       }
+
+       /*
+        * We are in trouble at this point, something is broken...
+        */
+       lstrval = res;
+       if (res < 0)
+               syswarn(1, errno, "Failed read on archive volume %d", arvol);
+       else
+               warn(0, "End of archive volume %d reached", arvol);
+       return(res);
+} 
+
+/*
+ * ar_write()
+ *     Write a specified number of bytes in supplied buffer to the archive
+ *     device so it appears as a single "block". Deals with errors and tries
+ *     to recover when faced with short writes.
+ * Return:
+ *     Number of bytes written. 0 indicates end of volume reached and with no
+ *     flaws (as best that can be detected). A -1 indicates an unrecoverable
+ *     error in the archive occured.
+ */
+
+#if __STDC__
+int
+ar_write(register char *buf, register int bsz)
+#else
+int
+ar_write(buf, bsz)
+       register char *buf;
+       register int bsz;
+#endif
+{
+       register int res;
+       off_t cpos;
+
+       /*
+        * do not allow pax to create a "bad" archive. Once a write fails on
+        * an archive volume prevent further writes to it.
+        */
+       if (lstrval <= 0)
+               return(lstrval);
+
+       if ((res = write(arfd, buf, bsz)) == bsz) {
+               wr_trail = 1;
+               io_ok = 1;
+               return(bsz);
+       }
+       /*
+        * write broke, see what we can do with it. We try to send any partial
+        * writes that may violate pax spec to the next archive volume.
+        */
+       if (res < 0)
+               lstrval = res;
+       else
+               lstrval = 0;
+
+       switch (artyp) {
+       case ISREG:
+               if ((res > 0) && (res % BLKMULT)) {
+                       /*
+                        * try to fix up partial writes which are not BLKMULT
+                        * in size by forcing the runt record to next archive
+                        * volume
+                        */
+                       if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0)
+                               break;
+                       cpos -= (off_t)res;
+                       if (ftruncate(arfd, cpos) < 0)
+                               break;
+                       res = lstrval = 0;
+                       break;
+               }
+               if (res >= 0)
+                       break;
+               /*
+                * if file is out of space, handle it like a return of 0
+                */
+               if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT))
+                       res = lstrval = 0;
+               break;
+       case ISTAPE:
+       case ISCHR:
+       case ISBLK:
+               if (res >= 0)
+                       break;
+               if (errno == EACCES) {
+                       warn(0, "Write failed, archive is write protected.");
+                       res = lstrval = 0;
+                       return(0);
+               }
+               /*
+                * see if we reached the end of media, if so force a change to
+                * the next volume
+                */
+               if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO))
+                       res = lstrval = 0;
+               break;
+       case ISPIPE:
+       default:
+               /*
+                * we cannot fix errors to these devices
+                */
+               break;
+       }
+
+       /*
+        * Better tell the user the bad news...
+        * if this is a block aligned archive format, we may have a bad archive
+        * if the format wants the header to start at a BLKMULT boundry. While
+        * we can deal with the mis-aligned data, it violates spec and other
+        * archive readers will likely fail. if the format is not block
+        * aligned, the user may be lucky (and the archive is ok).
+        */
+       if (res >= 0) {
+               if (res > 0)
+                       wr_trail = 1;
+               io_ok = 1;
+       }
+
+       /*
+        * If we were trying to rewrite the trailer and it didn't work, we
+        * must quit right away.
+        */
+       if (!wr_trail && (res <= 0)) {
+               warn(1,"Unable to append, trailer re-write failed. Quitting.");
+               return(res);
+       }
+               
+       if (res == 0) 
+               warn(0, "End of archive volume %d reached", arvol);
+       else if (res < 0)
+               syswarn(1, errno, "Failed write to archive volume: %d", arvol);
+       else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0))
+               warn(0,"WARNING: partial archive write. Archive MAY BE FLAWED");
+       else
+               warn(1,"WARNING: partial archive write. Archive IS FLAWED");
+       return(res);
+}
+
+/*
+ * ar_rdsync()
+ *     Try to move past a bad spot on a flawed archive as needed to continue
+ *     I/O. Clears error flags to allow I/O to continue.
+ * Return:
+ *     0 when ok to try i/o again, -1 otherwise.
+ */
+
+#if __STDC__
+int
+ar_rdsync(void)
+#else
+int
+ar_rdsync()
+#endif
+{
+       long fsbz;
+       off_t cpos;
+       off_t mpos;
+        struct mtop mb;
+
+       /*
+        * Fail resync attempts at user request (done) or this is going to be
+        * an update/append to a existing archive. if last i/o hit media end,
+        * we need to go to the next volume not try a resync
+        */
+       if ((done > 0) || (lstrval == 0))
+               return(-1);
+
+       if ((act == APPND) || (act == ARCHIVE)) {
+               warn(1, "Cannot allow updates to an archive with flaws.");
+               return(-1);
+       }
+       if (io_ok)
+               did_io = 1;
+
+       switch(artyp) {
+       case ISTAPE:
+               /*
+                * if the last i/o was a successful data transfer, we assume
+                * the fault is just a bad record on the tape that we are now
+                * past. If we did not get any data since the last resync try
+                * to move the tape foward one PHYSICAL record past any
+                * damaged tape section. Some tape drives are stubborn and need
+                * to be pushed.
+                */
+               if (io_ok) {
+                       io_ok = 0;
+                       lstrval = 1;
+                       break;
+               }
+               mb.mt_op = MTFSR;
+               mb.mt_count = 1;
+               if (ioctl(arfd, MTIOCTOP, &mb) < 0)
+                       break;
+               lstrval = 1;
+               break;
+       case ISREG:
+       case ISCHR:
+       case ISBLK:
+               /*
+                * try to step over the bad part of the device.
+                */
+               io_ok = 0;
+               if (((fsbz = arsb.st_blksize) <= 0) || (artyp != ISREG))
+                       fsbz = BLKMULT;
+               if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0)
+                       break;
+               mpos = fsbz - (cpos % (off_t)fsbz);
+               if (lseek(arfd, mpos, SEEK_CUR) < 0) 
+                       break;
+               lstrval = 1;
+               break;
+       case ISPIPE:
+       default:
+               /*
+                * cannot recover on these archive device types
+                */
+               io_ok = 0;
+               break;
+       }
+       if (lstrval <= 0) {
+               warn(1, "Unable to recover from an archive read failure.");
+               return(-1);
+       }
+       warn(0, "Attempting to recover from an archive read failure.");
+       return(0);
+}
+
+/*
+ * ar_fow()
+ *     Move the I/O position within the archive foward the specified number of
+ *     bytes as supported by the device. If we cannot move the requested
+ *     number of bytes, return the actual number of bytes moved in skipped.
+ * Return:
+ *     0 if moved the requested distance, -1 on complete failure, 1 on
+ *     partial move (the amount moved is in skipped)
+ */
+
+#if __STDC__
+int
+ar_fow(off_t sksz, off_t *skipped)
+#else
+int
+ar_fow(sksz, skipped)
+       off_t sksz;
+       off_t *skipped;
+#endif
+{
+       off_t cpos;
+       off_t mpos;
+
+       *skipped = 0;
+       if (sksz <= 0)
+               return(0);
+
+       /*
+        * we cannot move foward at EOF or error
+        */
+       if (lstrval <= 0)
+               return(lstrval);
+
+       /*
+        * Safer to read forward on devices where it is hard to find the end of
+        * the media without reading to it. With tapes we cannot be sure of the
+        * number of physical blocks to skip (we do not know physical block
+        * size at this point), so we must only read foward on tapes!
+        */
+       if (artyp != ISREG) 
+               return(0);
+
+       /*
+        * figure out where we are in the archive
+        */
+       if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) >= 0) {
+               /* 
+                * we can be asked to move farther than there are bytes in this
+                * volume, if so, just go to file end and let normal buf_fill()
+                * deal with the end of file (it will go to next volume by
+                * itself)
+                */
+               if ((mpos = cpos + sksz) > arsb.st_size) {
+                       *skipped = arsb.st_size - cpos;
+                       mpos = arsb.st_size;
+               } else
+                       *skipped = sksz;
+               if (lseek(arfd, mpos, SEEK_SET) >= 0)
+                       return(0);
+       }
+       syswarn(1, errno, "Foward positioning operation on archive failed");
+       lstrval = -1;
+       return(-1);
+}
+
+/*
+ * ar_rev()
+ *     move the i/o position within the archive backwards the specified byte
+ *     count as supported by the device. With tapes drives we RESET rdblksz to
+ *     the PHYSICAL blocksize.
+ *     NOTE: We should only be called to move backwards so we can rewrite the
+ *     last records (the trailer) of an archive (APPEND).
+ * Return:
+ *     0 if moved the requested distance, -1 on complete failure
+ */
+
+#if __STDC__
+int
+ar_rev(off_t sksz)
+#else
+int
+ar_rev(sksz)
+       off_t sksz;
+#endif
+{
+       off_t cpos;
+        struct mtop mb;
+       register int phyblk; 
+
+       /*
+        * make sure we do not have try to reverse on a flawed archive
+        */
+       if (lstrval < 0)
+               return(lstrval);
+
+       switch(artyp) {
+       case ISPIPE:
+               if (sksz <= 0) 
+                       break;
+               /*
+                * cannot go backwards on these critters
+                */
+               warn(1, "Reverse positioning on pipes is not supported.");
+               lstrval = -1;
+               return(-1);
+       case ISREG:
+       case ISBLK:
+       case ISCHR:
+       default:
+               if (sksz <= 0)
+                       break;
+
+               /*
+                * For things other than files, backwards movement has a very
+                * high probability of failure as we really do not know the
+                * true attributes of the device we are talking to (the device
+                * may not even have the ability to lseek() in any direction).
+                * First we figure out where we are in the archive.
+                */
+               if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) {
+                       syswarn(1, errno,
+                          "Unable to obtain current archive byte offset");
+                       lstrval = -1;
+                       return(-1);
+               }
+
+               /*
+                * we may try to go backwards past the start when the archive
+                * is only a single record. If this hapens and we are on a
+                * multi volume archive, we need to go to the end of the
+                * previous volume and continue our movement backwards from
+                * there.
+                */
+               if ((cpos -= sksz) < (off_t)0L) {
+                       if (arvol > 1) {
+                               /*
+                                * this should never happen
+                                */
+                               warn(1,"Reverse position on previous volume.");
+                               lstrval = -1;
+                               return(-1);
+                       }
+                       cpos = (off_t)0L;
+               }
+               if (lseek(arfd, cpos, SEEK_SET) < 0) {
+                       syswarn(1, errno, "Unable to seek archive backwards");
+                       lstrval = -1;
+                       return(-1);
+               }
+               break;
+       case ISTAPE:
+               /*
+                * Calculate and move the proper number of PHYSICAL tape
+                * blocks. If the sksz is not an even multiple of the physical
+                * tape size, we cannot do the move (this should never happen).
+                * (We also cannot handler trailers spread over two vols).
+                * get_phys() also makes sure we are in front of the filemark.
+                */
+               if ((phyblk = get_phys()) <= 0) {
+                       lstrval = -1;
+                       return(-1);
+               }
+
+               /*
+                * make sure future tape reads only go by physical tape block
+                * size (set rdblksz to the real size).
+                */
+               rdblksz = phyblk;
+
+               /*
+                * if no movement is required, just return (we must be after
+                * get_phys() so the physical blocksize is properly set)
+                */
+               if (sksz <= 0)
+                       break;
+
+               /*
+                * ok we have to move. Make sure the tape drive can do it.
+                */
+               if (sksz % phyblk) {
+                       warn(1,
+                           "Tape drive unable to backspace requested amount");
+                       lstrval = -1;
+                       return(-1);
+               }
+
+               /*
+                * move backwards the requested number of bytes
+                */
+               mb.mt_op = MTBSR;
+               mb.mt_count = sksz/phyblk;
+               if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+                       syswarn(1,errno, "Unable to backspace tape %d blocks.",
+                           mb.mt_count);
+                       lstrval = -1;
+                       return(-1);
+               }
+               break;
+       }
+       lstrval = 1;
+       return(0);
+}
+
+/*
+ * get_phys()
+ *     Determine the physical block size on a tape drive. We need the physical
+ *     block size so we know how many bytes we skip over when we move with 
+ *     mtio commands. We also make sure we are BEFORE THE TAPE FILEMARK when
+ *     return.
+ *     This is one really SLOW routine...
+ * Return:
+ *     physical block size if ok (ok > 0), -1 otherwise
+ */
+
+#if __STDC__
+static int
+get_phys(void)
+#else
+static int
+get_phys()
+#endif
+{
+       register int padsz = 0;
+       register int res;
+       register int phyblk;
+       struct mtop mb;
+       char scbuf[MAXBLK];
+
+       /*
+        * move to the file mark, and then back up one record and read it.
+        * this should tell us the physical record size the tape is using.
+        */
+       if (lstrval == 1) {
+               /*
+                * we know we are at file mark when we get back a 0 from
+                * read()
+                */
+               while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0)
+                       padsz += res;
+               if (res < 0) {
+                       syswarn(1, errno, "Unable to locate tape filemark.");
+                       return(-1);
+               }
+       }
+
+       /*
+        * move backwards over the file mark so we are at the end of the
+        * last record.
+        */
+       mb.mt_op = MTBSF;
+       mb.mt_count = 1;
+       if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+               syswarn(1, errno, "Unable to backspace over tape filemark.");
+               return(-1);
+       }
+
+       /*
+        * move backwards so we are in front of the last record and read it to
+        * get physical tape blocksize.
+        */
+       mb.mt_op = MTBSR;
+       mb.mt_count = 1;
+       if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+               syswarn(1, errno, "Unable to backspace over last tape block.");
+               return(-1);
+       }
+       if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) {
+               syswarn(1, errno, "Cannot determine archive tape blocksize.");
+               return(-1);
+       }
+
+       /*
+        * read foward to the file mark, then back up in front of the filemark
+        * (this is a bit paranoid, but should be safe to do).
+        */
+       while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0)
+               ;
+       if (res < 0) {
+               syswarn(1, errno, "Unable to locate tape filemark.");
+               return(-1);
+       }
+       mb.mt_op = MTBSF;
+       mb.mt_count = 1;
+       if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+               syswarn(1, errno, "Unable to backspace over tape filemark.");
+               return(-1);
+       }
+
+       /*
+        * set lstrval so we know that the filemark has not been seen
+        */
+       lstrval = 1;
+
+       /*
+        * return if there was no padding
+        */
+       if (padsz == 0)
+               return(phyblk);
+
+       /*
+        * make sure we can move backwards over the padding. (this should
+        * never fail).
+        */
+       if (padsz % phyblk) {
+               warn(1, "Tape drive unable to backspace requested amount");
+               return(-1);
+       }
+
+       /*
+        * move backwards over the padding so the head is where it was when
+        * we were first called (if required).
+        */
+       mb.mt_op = MTBSR;
+       mb.mt_count = padsz/phyblk;
+       if (ioctl(arfd, MTIOCTOP, &mb) < 0) {
+               syswarn(1,errno,"Unable to backspace tape over %d pad blocks",
+                   mb.mt_count);
+               return(-1);
+       }
+       return(phyblk);
+}
+
+/*
+ * ar_next()
+ *     prompts the user for the next volume in this archive. For some devices
+ *     we may allow the media to be changed. Otherwise a new archive is
+ *     prompted for. By pax spec, if there is no controlling tty or an eof is
+ *     read on tty input, we must quit pax.
+ * Return:
+ *     0 when ready to continue, -1 when all done
+ */
+
+#if __STDC__
+int
+ar_next(void)
+#else
+int
+ar_next()
+#endif
+{
+       char buf[PAXPATHLEN+2];
+       static int freeit = 0;
+       sigset_t o_mask;
+
+       /*
+        * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so
+        * things like writing EOF etc will be done) (Watch out ar_close() can
+        * also be called via a signal handler, so we must prevent a race.
+        */
+       if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0)
+               syswarn(0, errno, "Unable to set signal mask");
+       ar_close();
+       if (sigprocmask(SIG_SETMASK, &o_mask, (sigset_t *)NULL) < 0)
+               syswarn(0, errno, "Unable to restore signal mask");
+
+       if (done || !wr_trail)
+               return(-1);
+
+       tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0);
+
+       /*
+        * if i/o is on stdin or stdout, we cannot reopen it (we do not know
+        * the name), the user will be forced to type it in.
+        */
+       if (strcmp(arcname, STDO) && strcmp(arcname, STDN) && (artyp != ISREG)
+           && (artyp != ISPIPE)) {
+               if (artyp == ISTAPE) {
+                       tty_prnt("%s ready for archive tape volume: %d\n",
+                               arcname, arvol);
+                       tty_prnt("Load the NEXT TAPE on the tape drive");
+               } else {
+                       tty_prnt("%s ready for archive volume: %d\n",
+                               arcname, arvol);
+                       tty_prnt("Load the NEXT STORAGE MEDIA (if required)");
+               }
+
+               if ((act == ARCHIVE) || (act == APPND))
+                       tty_prnt(" and make sure it is WRITE ENABLED.\n");
+               else
+                       tty_prnt("\n");
+
+               for(;;) {
+                       tty_prnt("Type \"y\" to continue, \".\" to quit %s,",
+                               argv0);
+                       tty_prnt(" or \"s\" to switch to new device.\nIf you");
+                       tty_prnt(" cannot change storage media, type \"s\"\n");
+                       tty_prnt("Is the device ready and online? > ");
+
+                       if ((tty_read(buf,sizeof(buf))<0) || !strcmp(buf,".")){
+                               done = 1;
+                               lstrval = -1;
+                               tty_prnt("Quitting %s!\n", argv0);
+                               vfpart = 0;
+                               return(-1);
+                       }
+
+                       if ((buf[0] == '\0') || (buf[1] != '\0')) {
+                               tty_prnt("%s unknown command, try again\n",buf);
+                               continue;
+                       }
+
+                       switch (buf[0]) {
+                       case 'y':
+                       case 'Y':
+                               /*
+                                * we are to continue with the same device
+                                */
+                               if (ar_open(arcname) >= 0) 
+                                       return(0);
+                               tty_prnt("Cannot re-open %s, try again\n",
+                                       arcname);
+                               continue;
+                       case 's':
+                       case 'S':
+                               /*
+                                * user wants to open a different device
+                                */
+                               tty_prnt("Switching to a different archive\n");
+                               break;
+                       default:
+                               tty_prnt("%s unknown command, try again\n",buf);
+                               continue;
+                       }
+                       break;
+               }
+       } else
+               tty_prnt("Ready for archive volume: %d\n", arvol);
+
+       /*
+        * have to go to a different archive
+        */
+       for (;;) {
+               tty_prnt("Input archive name or \".\" to quit %s.\n", argv0);
+               tty_prnt("Archive name > ");
+
+               if ((tty_read(buf, sizeof(buf)) < 0) || !strcmp(buf, ".")) {
+                       done = 1;
+                       lstrval = -1;
+                       tty_prnt("Quitting %s!\n", argv0);
+                       vfpart = 0;
+                       return(-1);
+               }
+               if (buf[0] == '\0') {
+                       tty_prnt("Empty file name, try again\n");
+                       continue;
+               }
+                if (!strcmp(buf, "..")) {
+                        tty_prnt("Illegal file name: .. try again\n");
+                        continue;
+                }
+               if (strlen(buf) > PAXPATHLEN) {
+                       tty_prnt("File name too long, try again\n");
+                       continue;
+               }
+
+               /*
+                * try to open new archive
+                */
+               if (ar_open(buf) >= 0) {
+                       if (freeit) {
+                               (void)free(arcname);
+                               freeit = 0;
+                       }
+                       if ((arcname = strdup(buf)) == NULL) {
+                               done = 1;
+                               lstrval = -1;
+                               warn(0, "Cannot save archive name.");
+                               return(-1);
+                       }
+                       freeit = 1;
+                       break;
+               }
+               tty_prnt("Cannot open %s, try again\n", buf);
+               continue;
+       }
+       return(0);
+}
diff --git a/usr/src/bin/pax/ar_subs.c b/usr/src/bin/pax/ar_subs.c
new file mode 100644 (file)
index 0000000..ce09b93
--- /dev/null
@@ -0,0 +1,1234 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ar_subs.c  8.2 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "extern.h"
+
+static void wr_archive __P((register ARCHD *, int is_app));
+static int get_arc __P((void));
+static int next_head __P((register ARCHD *));
+extern sigset_t s_mask;
+
+/*
+ * Routines which control the overall operation modes of pax as specified by
+ * the user: list, append, read ...
+ */
+
+static char hdbuf[BLKMULT];             /* space for archive header on read */
+u_long flcnt;                          /* number of files processed */
+
+/*
+ * list()
+ *     list the contents of an archive which match user supplied pattern(s)
+ *     (no pattern matches all).
+ */
+
+#if __STDC__
+void
+list(void)
+#else
+void
+list()
+#endif
+{
+       register ARCHD *arcn;
+       register int res;
+       ARCHD archd;
+       time_t now;
+
+       arcn = &archd;
+       /*
+        * figure out archive type; pass any format specific options to the
+        * archive option processing routine; call the format init routine. We
+        * also save current time for ls_list() so we do not make a system
+        * call for each file we need to print. If verbose (vflag) start up
+        * the name and group caches.
+        */
+       if ((get_arc() < 0) || ((*frmt->options)() < 0) ||
+           ((*frmt->st_rd)() < 0))
+               return;
+
+       if (vflag && ((uidtb_start() < 0) || (gidtb_start() < 0)))
+               return;
+
+       now = time((time_t *)NULL);
+
+       /*
+        * step through the archive until the format says it is done
+        */
+       while (next_head(arcn) == 0) {
+               /*
+                * check for pattern, and user specified options match.
+                * When all patterns are matched we are done.
+                */
+               if ((res = pat_match(arcn)) < 0)
+                       break;
+
+               if ((res == 0) && (sel_chk(arcn) == 0)) {
+                       /*
+                        * pattern resulted in a selected file
+                        */
+                       if (pat_sel(arcn) < 0)
+                               break;
+
+                       /*
+                        * modify the name as requested by the user if name
+                        * survives modification, do a listing of the file
+                        */
+                       if ((res = mod_name(arcn)) < 0)
+                               break;
+                       if (res == 0)
+                               ls_list(arcn, now);
+               }
+
+               /*
+                * skip to next archive format header using values calculated
+                * by the format header read routine
+                */
+               if (rd_skip(arcn->skip + arcn->pad) == 1)
+                       break;
+       }
+
+       /*
+        * all done, let format have a chance to cleanup, and make sure that
+        * the patterns supplied by the user were all matched
+        */
+       (void)(*frmt->end_rd)();
+       (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL);
+       ar_close();
+       pat_chk();
+}
+
+/*
+ * extract()
+ *     extract the member(s) of an archive as specified by user supplied
+ *     pattern(s) (no patterns extracts all members)
+ */
+
+#if __STDC__
+void
+extract(void)
+#else
+void
+extract()
+#endif
+{
+       register ARCHD *arcn;
+       register int res;
+       off_t cnt;
+       ARCHD archd;
+       struct stat sb;
+       int fd;
+
+       arcn = &archd;
+       /*
+        * figure out archive type; pass any format specific options to the
+        * archive option processing routine; call the format init routine;
+        * start up the directory modification time and access mode database
+        */
+       if ((get_arc() < 0) || ((*frmt->options)() < 0) ||
+           ((*frmt->st_rd)() < 0) || (dir_start() < 0))
+               return;
+
+       /*
+        * When we are doing interactive rename, we store the mapping of names
+        * so we can fix up hard links files later in the archive.
+        */
+       if (iflag && (name_start() < 0))
+               return;
+
+       /*
+        * step through each entry on the archive until the format read routine
+        * says it is done
+        */
+       while (next_head(arcn) == 0) {
+
+               /*
+                * check for pattern, and user specified options match. When
+                * all the patterns are matched we are done
+                */
+               if ((res = pat_match(arcn)) < 0)
+                       break;
+
+               if ((res > 0) || (sel_chk(arcn) != 0)) {
+                       /*
+                        * file is not selected. skip past any file data and
+                        * padding and go back for the next archive member
+                        */
+                       (void)rd_skip(arcn->skip + arcn->pad);
+                       continue;
+               }
+
+               /*
+                * with -u or -D only extract when the archive member is newer
+                * than the file with the same name in the file system (nos
+                * test of being the same type is required).
+                * NOTE: this test is done BEFORE name modifications as
+                * specified by pax. this operation can be confusing to the
+                * user who might expect the test to be done on an existing
+                * file AFTER the name mod. In honesty the pax spec is probably
+                * flawed in this respect.
+                */
+               if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0))) {
+                       if (uflag && Dflag) {
+                               if ((arcn->sb.st_mtime <= sb.st_mtime) &&
+                                   (arcn->sb.st_ctime <= sb.st_ctime)) {
+                                       (void)rd_skip(arcn->skip + arcn->pad);
+                                       continue;
+                               }
+                       } else if (Dflag) {
+                               if (arcn->sb.st_ctime <= sb.st_ctime) {
+                                       (void)rd_skip(arcn->skip + arcn->pad);
+                                       continue;
+                               }
+                       } else if (arcn->sb.st_mtime <= sb.st_mtime) {
+                               (void)rd_skip(arcn->skip + arcn->pad);
+                               continue;
+                       }
+               }
+
+               /*
+                * this archive member is now been selected. modify the name.
+                */
+               if ((pat_sel(arcn) < 0) || ((res = mod_name(arcn)) < 0))
+                       break;
+               if (res > 0) {
+                       /*
+                        * a bad name mod, skip and purge name from link table
+                        */
+                       purg_lnk(arcn);
+                       (void)rd_skip(arcn->skip + arcn->pad);
+                       continue;
+               }
+
+               /*
+                * Non standard -Y and -Z flag. When the exisiting file is
+                * same age or newer skip
+                */
+               if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) {
+                       if (Yflag && Zflag) {
+                               if ((arcn->sb.st_mtime <= sb.st_mtime) &&
+                                   (arcn->sb.st_ctime <= sb.st_ctime)) {
+                                       (void)rd_skip(arcn->skip + arcn->pad);
+                                       continue;
+                               }
+                       } else if (Yflag) {
+                               if (arcn->sb.st_ctime <= sb.st_ctime) {
+                                       (void)rd_skip(arcn->skip + arcn->pad);
+                                       continue;
+                               }
+                       } else if (arcn->sb.st_mtime <= sb.st_mtime) {
+                               (void)rd_skip(arcn->skip + arcn->pad);
+                               continue;
+                       }
+               }
+
+               if (vflag) {
+                       (void)fputs(arcn->name, stderr);
+                       vfpart = 1;
+               }
+
+               /*
+                * all ok, extract this member based on type
+                */
+               if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) {
+                       /*
+                        * process archive members that are not regular files.
+                        * throw out padding and any data that might follow the
+                        * header (as determined by the format).
+                        */
+                       if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
+                               res = lnk_creat(arcn);
+                       else
+                               res = node_creat(arcn);
+
+                       (void)rd_skip(arcn->skip + arcn->pad);
+                       if (res < 0)
+                               purg_lnk(arcn);
+
+                       if (vflag && vfpart) {
+                               (void)putc('\n', stderr);
+                               vfpart = 0;
+                       }
+                       continue;
+               }
+               /*
+                * we have a file with data here. If we can not create it, skip
+                * over the data and purge the name from hard link table
+                */
+               if ((fd = file_creat(arcn)) < 0) {
+                       (void)rd_skip(arcn->skip + arcn->pad);
+                       purg_lnk(arcn);
+                       continue;
+               }
+               /*
+                * extract the file from the archive and skip over padding and
+                * any unprocessed data
+                */
+               res = (*frmt->rd_data)(arcn, fd, &cnt);
+               file_close(arcn, fd);
+               if (vflag && vfpart) {
+                       (void)putc('\n', stderr);
+                       vfpart = 0;
+               }
+               if (!res)
+                       (void)rd_skip(cnt + arcn->pad);
+       }
+
+       /*
+        * all done, restore directory modes and times as required; make sure
+        * all patterns supplied by the user were matched; block off signals
+        * to avoid chance for multiple entry into the cleanup code.
+        */
+       (void)(*frmt->end_rd)();
+       (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL);
+       ar_close();
+       proc_dir();
+       pat_chk();
+}
+
+/*
+ * wr_archive()
+ *     Write an archive. used in both creating a new archive and appends on
+ *     previously written archive.
+ */
+
+#if __STDC__
+static void
+wr_archive(register ARCHD *arcn, int is_app)
+#else
+static void
+wr_archive(arcn, is_app)
+       register ARCHD *arcn;
+       int is_app;
+#endif
+{
+       register int res;
+       register int hlk;
+       register int wr_one;
+       off_t cnt;
+       int (*wrf)();
+       int fd = -1;
+
+       /*
+        * if this format supports hard link storage, start up the database
+        * that detects them.
+        */
+       if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0))
+               return;
+
+       /*
+        * start up the file traversal code and format specific write
+        */
+       if ((ftree_start() < 0) || ((*frmt->st_wr)() < 0))
+               return;
+       wrf = frmt->wr;
+
+       /*
+        * When we are doing interactive rename, we store the mapping of names
+        * so we can fix up hard links files later in the archive.
+        */
+       if (iflag && (name_start() < 0))
+               return;
+
+       /*
+        * if this not append, and there are no files, we do no write a trailer
+        */
+       wr_one = is_app;
+
+       /*
+        * while there are files to archive, process them one at at time
+        */
+       while (next_file(arcn) == 0) {
+               /*
+                * check if this file meets user specified options match.
+                */
+               if (sel_chk(arcn) != 0)
+                       continue;
+               fd = -1;
+               if (uflag) {
+                       /*
+                        * only archive if this file is newer than a file with
+                        * the same name that is already stored on the archive
+                        */
+                       if ((res = chk_ftime(arcn)) < 0)
+                               break;
+                       if (res > 0)
+                               continue;
+               }
+
+               /*
+                * this file is considered selected now. see if this is a hard
+                * link to a file already stored
+                */
+               ftree_sel(arcn);
+               if (hlk && (chk_lnk(arcn) < 0))
+                       break;
+
+               if ((arcn->type == PAX_REG) || (arcn->type == PAX_HRG) ||
+                   (arcn->type == PAX_CTG)) {
+                       /*
+                        * we will have to read this file. by opening it now we
+                        * can avoid writing a header to the archive for a file
+                        * we were later unable to read (we also purge it from
+                        * the link table).
+                        */
+                       if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) {
+                               syswarn(1,errno, "Unable to open %s to read",
+                                       arcn->org_name);
+                               purg_lnk(arcn);
+                               continue;
+                       }
+               }
+
+               /*
+                * Now modify the name as requested by the user
+                */
+               if ((res = mod_name(arcn)) < 0) {
+                       /*
+                        * name modification says to skip this file, close the
+                        * file and purge link table entry
+                        */
+                       rdfile_close(arcn, &fd);
+                       purg_lnk(arcn);
+                       break;
+               }
+
+               if ((res > 0) || (docrc && (set_crc(arcn, fd) < 0))) {
+                       /*
+                        * unable to obtain the crc we need, close the file,
+                        * purge link table entry 
+                        */
+                       rdfile_close(arcn, &fd);
+                       purg_lnk(arcn);
+                       continue;
+               }
+
+               if (vflag) {
+                       (void)fputs(arcn->name, stderr);
+                       vfpart = 1;
+               }
+               ++flcnt;
+
+               /*
+                * looks safe to store the file, have the format specific
+                * routine write routine store the file header on the archive
+                */
+               if ((res = (*wrf)(arcn)) < 0) {
+                       rdfile_close(arcn, &fd);
+                       break;
+               }
+               wr_one = 1;
+               if (res > 0) {
+                       /* 
+                        * format write says no file data needs to be stored
+                        * so we are done messing with this file
+                        */
+                       if (vflag && vfpart) {
+                               (void)putc('\n', stderr);
+                               vfpart = 0;
+                       }
+                       rdfile_close(arcn, &fd);
+                       continue;
+               }
+
+               /*
+                * Add file data to the archive, quit on write error. if we
+                * cannot write the entire file contents to the archive we
+                * must pad the archive to replace the missing file data
+                * (otherwise during an extract the file header for the file
+                * which FOLLOWS this one will not be where we expect it to
+                * be).
+                */
+               res = (*frmt->wr_data)(arcn, fd, &cnt);
+               rdfile_close(arcn, &fd);
+               if (vflag && vfpart) {
+                       (void)putc('\n', stderr);
+                       vfpart = 0;
+               }
+               if (res < 0)
+                       break;
+
+               /*
+                * pad as required, cnt is number of bytes not written
+                */
+               if (((cnt > 0) && (wr_skip(cnt) < 0)) ||
+                   ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0)))
+                       break;
+       }
+
+       /*
+        * tell format to write trailer; pad to block boundry; reset directory
+        * mode/access times, and check if all patterns supplied by the user
+        * were matched. block off signals to avoid chance for multiple entry
+        * into the cleanup code
+        */
+       if (wr_one) {
+               (*frmt->end_wr)();
+               wr_fin();
+       }
+       (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL);
+       ar_close();
+       if (tflag)
+               proc_dir();
+       ftree_chk();
+}
+
+/*
+ * append()
+ *     Add file to previously written archive. Archive format specified by the
+ *     user must agree with archive. The archive is read first to collect
+ *     modification times (if -u) and locate the archive trailer. The archive
+ *     is positioned in front of the record with the trailer and wr_archive()
+ *     is called to add the new members.
+ *     PAX IMPLEMENTATION DETAIL NOTE:
+ *     -u is implemented by adding the new members to the end of the archive.
+ *     Care is taken so that these do not end up as links to the older 
+ *     version of the same file already stored in the archive. It is expected
+ *     when extraction occurs these newer versions will over-write the older
+ *     ones stored "earlier" in the archive (this may be a bad assumption as
+ *     it depends on the implementation of the program doing the extraction).
+ *     It is really difficult to splice in members without either re-writing
+ *     the entire archive (from the point were the old version was), or having
+ *     assistance of the format specification in terms of a special update
+ *     header that invalidates a previous archive record. The posix spec left
+ *     the method used to implement -u unspecified. This pax is able to
+ *     over write existing files that it creates.
+ */
+
+#if __STDC__
+void
+append(void)
+#else
+void
+append()
+#endif
+{
+       register ARCHD *arcn;
+       register int res;
+       ARCHD archd;
+       FSUB *orgfrmt;
+       int udev;
+       off_t tlen;
+
+       arcn = &archd;
+       orgfrmt = frmt;
+
+       /*
+        * Do not allow an append operation if the actual archive is of a
+        * different format than the user specified foramt.
+        */
+       if (get_arc() < 0)
+               return;
+       if ((orgfrmt != NULL) && (orgfrmt != frmt)) {
+               warn(1, "Cannot mix current archive format %s with %s",
+                   frmt->name, orgfrmt->name);
+               return;
+       }
+
+       /*
+        * pass the format any options and start up format
+        */
+       if (((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0))
+               return;
+
+       /*
+        * if we only are adding members that are newer, we need to save the
+        * mod times for all files we see.
+        */
+       if (uflag && (ftime_start() < 0))
+               return;
+
+       /*
+        * some archive formats encode hard links by recording the device and
+        * file serial number (inode) but copy the file anyway (multiple times)
+        * to the archive. When we append, we run the risk that newly added
+        * files may have the same device and inode numbers as those recorded
+        * on the archive but during a previous run. If this happens, when the
+        * archive is extracted we get INCORRECT hard links. We avoid this by
+        * remapping the device numbers so that newly added files will never
+        * use the same device number as one found on the archive. remapping
+        * allows new members to safely have links among themselves. remapping
+        * also avoids problems with file inode (serial number) truncations
+        * when the inode number is larger than storage space in the archive
+        * header. See the remap routines for more details.
+        */
+       if ((udev = frmt->udev) && (dev_start() < 0))
+               return;
+
+       /*
+        * reading the archive may take a long time. If verbose tell the user
+        */
+       if (vflag) {
+               (void)fprintf(stderr,
+                       "%s: Reading archive to position at the end...", argv0);
+               vfpart = 1;
+       }
+
+       /*
+        * step through the archive until the format says it is done
+        */
+       while (next_head(arcn) == 0) {
+               /*
+                * check if this file meets user specified options.
+                */
+               if (sel_chk(arcn) != 0) {
+                       if (rd_skip(arcn->skip + arcn->pad) == 1)
+                               break;
+                       continue;
+               }
+
+               if (uflag) {
+                       /*
+                        * see if this is the newest version of this file has
+                        * already been seen, if so skip.
+                        */
+                       if ((res = chk_ftime(arcn)) < 0)
+                               break;
+                       if (res > 0) {
+                               if (rd_skip(arcn->skip + arcn->pad) == 1)
+                                       break;
+                               continue;
+                       }
+               }
+
+               /*
+                * Store this device number. Device numbers seen during the
+                * read phase of append will cause newly appended files with a
+                * device number seen in the old part of the archive to be
+                * remapped to an unused device number.
+                */
+               if ((udev && (add_dev(arcn) < 0)) ||
+                   (rd_skip(arcn->skip + arcn->pad) == 1))
+                       break;
+       }
+
+       /*
+        * done, finish up read and get the number of bytes to back up so we
+        * can add new members. The format might have used the hard link table,
+        * purge it.
+        */
+       tlen = (*frmt->end_rd)();
+       lnk_end();
+
+       /*
+        * try to postion for write, if this fails quit. if any error occurs,
+        * we will refuse to write
+        */
+       if (appnd_start(tlen) < 0)
+               return;
+
+       /*
+        * tell the user we are done reading.
+        */
+       if (vflag && vfpart) {
+               (void)fputs("done.\n", stderr);
+               vfpart = 0;
+       }
+       
+       /*
+        * go to the writing phase to add the new members
+        */
+       wr_archive(arcn, 1);
+}
+
+/*
+ * archive()
+ *     write a new archive
+ */
+
+#if __STDC__
+void
+archive(void)
+#else
+void
+archive()
+#endif
+{
+       ARCHD archd;
+
+       /*
+        * if we only are adding members that are newer, we need to save the
+        * mod times for all files; set up for writing; pass the format any
+        * options write the archive
+        */
+       if ((uflag && (ftime_start() < 0)) || (wr_start() < 0))
+               return;
+       if ((*frmt->options)() < 0)
+               return;
+
+       wr_archive(&archd, 0);
+}
+
+/*
+ * copy()
+ *     copy files from one part of the file system to another. this does not
+ *     use any archive storage. The EFFECT OF THE COPY IS THE SAME as if an
+ *     archive was written and then extracted in the destination directory
+ *     (except the files are forced to be under the destination directory).
+ */
+
+#if __STDC__
+void
+copy(void)
+#else
+void
+copy()
+#endif
+{
+       register ARCHD *arcn;
+       register int res;
+       register int fddest;
+       register char *dest_pt;
+       register int dlen;
+       register int drem;
+       int fdsrc = -1;
+       struct stat sb;
+       ARCHD archd;
+       char dirbuf[PAXPATHLEN+1];
+
+       arcn = &archd;
+       /*
+        * set up the destination dir path and make sure it is a directory. We
+        * make sure we have a trailing / on the destination
+        */
+       dlen = l_strncpy(dirbuf, dirptr, PAXPATHLEN);
+       dest_pt = dirbuf + dlen;
+       if (*(dest_pt-1) != '/') {
+               *dest_pt++ = '/';
+               ++dlen;
+       }
+       *dest_pt = '\0';
+       drem = PAXPATHLEN - dlen;
+
+       if (stat(dirptr, &sb) < 0) {
+               syswarn(1, errno, "Cannot access destination directory %s",
+                       dirptr);
+               return;
+       }
+       if (!S_ISDIR(sb.st_mode)) {
+               warn(1, "Destination is not a directory %s", dirptr);
+               return;
+       }
+
+       /*
+        * start up the hard link table; file traversal routines and the
+        * modification time and access mode database 
+        */
+       if ((lnk_start() < 0) || (ftree_start() < 0) || (dir_start() < 0))
+               return;
+
+       /*
+        * When we are doing interactive rename, we store the mapping of names
+        * so we can fix up hard links files later in the archive.
+        */
+       if (iflag && (name_start() < 0))
+               return;
+
+       /*
+        * set up to cp file trees
+        */
+       cp_start();
+
+       /*
+        * while there are files to archive, process them
+        */
+       while (next_file(arcn) == 0) {
+               fdsrc = -1;
+
+               /*
+                * check if this file meets user specified options
+                */
+               if (sel_chk(arcn) != 0)
+                       continue;
+
+               /*
+                * if there is already a file in the destination directory with
+                * the same name and it is newer, skip the one stored on the
+                * archive.
+                * NOTE: this test is done BEFORE name modifications as
+                * specified by pax. this can be confusing to the user who
+                * might expect the test to be done on an existing file AFTER
+                * the name mod. In honesty the pax spec is probably flawed in
+                * this respect
+                */
+               if (uflag || Dflag) {
+                       /*
+                        * create the destination name
+                        */
+                       if (*(arcn->name) == '/')
+                               res = 1;
+                       else
+                               res = 0;
+                       if ((arcn->nlen - res) > drem) {
+                               warn(1, "Destination pathname too long %s",
+                                       arcn->name);
+                               continue;
+                       }
+                       (void)strncpy(dest_pt, arcn->name + res, drem);
+                       dirbuf[PAXPATHLEN] = '\0';
+
+                       /*
+                        * if existing file is same age or newer skip
+                        */
+                       res = lstat(dirbuf, &sb);
+                       *dest_pt = '\0';
+
+                       if (res == 0) {
+                               if (uflag && Dflag) {
+                                       if ((arcn->sb.st_mtime<=sb.st_mtime) &&
+                                           (arcn->sb.st_ctime<=sb.st_ctime))
+                                               continue;
+                               } else if (Dflag) {
+                                       if (arcn->sb.st_ctime <= sb.st_ctime)
+                                               continue;
+                               } else if (arcn->sb.st_mtime <= sb.st_mtime)
+                                       continue;
+                       }
+               }
+
+               /*
+                * this file is considered selected. See if this is a hard link
+                * to a previous file; modify the name as requested by the
+                * user; set the final destination.
+                */
+               ftree_sel(arcn);
+               if ((chk_lnk(arcn) < 0) || ((res = mod_name(arcn)) < 0))
+                       break;
+               if ((res > 0) || (set_dest(arcn, dirbuf, dlen) < 0)) {
+                       /*
+                        * skip file, purge from link table
+                        */
+                       purg_lnk(arcn);
+                       continue;
+               }
+
+               /*
+                * Non standard -Y and -Z flag. When the exisiting file is
+                * same age or newer skip
+                */
+               if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) {
+                       if (Yflag && Zflag) {
+                               if ((arcn->sb.st_mtime <= sb.st_mtime) &&
+                                   (arcn->sb.st_ctime <= sb.st_ctime))
+                                       continue;
+                       } else if (Yflag) {
+                               if (arcn->sb.st_ctime <= sb.st_ctime)
+                                       continue;
+                       } else if (arcn->sb.st_mtime <= sb.st_mtime)
+                               continue;
+               }
+
+               if (vflag) {
+                       (void)fputs(arcn->name, stderr);
+                       vfpart = 1;
+               }
+               ++flcnt;
+
+               /*
+                * try to create a hard link to the src file if requested
+                * but make sure we are not trying to overwrite ourselves.
+                */
+               if (lflag) 
+                       res = cross_lnk(arcn);
+               else
+                       res = chk_same(arcn);
+               if (res <= 0) {
+                       if (vflag && vfpart) {
+                               (void)putc('\n', stderr);
+                               vfpart = 0;
+                       }
+                       continue;
+               }
+
+               /*
+                * have to create a new file
+                */
+               if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) {
+                       /*
+                        * create a link or special file
+                        */
+                       if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
+                               res = lnk_creat(arcn);
+                       else
+                               res = node_creat(arcn);
+                       if (res < 0)
+                               purg_lnk(arcn);
+                       if (vflag && vfpart) {
+                               (void)putc('\n', stderr);
+                               vfpart = 0;
+                       }
+                       continue;
+               }
+
+               /*
+                * have to copy a regular file to the destination directory.
+                * first open source file and then create the destination file
+                */
+               if ((fdsrc = open(arcn->org_name, O_RDONLY, 0)) < 0) {
+                       syswarn(1, errno, "Unable to open %s to read",
+                           arcn->org_name);
+                       purg_lnk(arcn);
+                       continue;
+               }
+               if ((fddest = file_creat(arcn)) < 0) {
+                       rdfile_close(arcn, &fdsrc);
+                       purg_lnk(arcn);
+                       continue;
+               }
+
+               /*
+                * copy source file data to the destination file
+                */
+               cp_file(arcn, fdsrc, fddest);
+               file_close(arcn, fddest);
+               rdfile_close(arcn, &fdsrc);
+
+               if (vflag && vfpart) {
+                       (void)putc('\n', stderr);
+                       vfpart = 0;
+               }
+       }
+
+       /*
+        * restore directory modes and times as required; make sure all
+        * patterns were selected block off signals to avoid chance for
+        * multiple entry into the cleanup code.
+        */
+       (void)sigprocmask(SIG_BLOCK, &s_mask, (sigset_t *)NULL);
+       ar_close();
+       proc_dir();
+       ftree_chk();
+}
+
+/*
+ * next_head()
+ *     try to find a valid header in the archive. Uses format specific
+ *     routines to extract the header and id the trailer. Trailers may be
+ *     located within a valid header or in an invalid header (the location
+ *     is format specific. The inhead field from the option table tells us
+ *     where to look for the trailer).
+ *     We keep reading (and resyncing) until we get enough contiguous data
+ *     to check for a header. If we cannot find one, we shift by a byte
+ *     add a new byte from the archive to the end of the buffer and try again.
+ *     If we get a read error, we throw out what we have (as we must have
+ *     contiguous data) and start over again.
+ *     ASSUMED: headers fit within a BLKMULT header.
+ * Return:
+ *     0 if we got a header, -1 if we are unable to ever find another one
+ *     (we reached the end of input, or we reached the limit on retries. see
+ *     the specs for rd_wrbuf() for more details)
+ */
+
+#if __STDC__
+static int
+next_head(register ARCHD *arcn)
+#else
+static int
+next_head(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register int ret;
+       register char *hdend;
+       register int res;
+       register int shftsz;
+       register int hsz;
+       register int in_resync = 0;     /* set when we are in resync mode */
+       int cnt = 0;                    /* counter for trailer function */
+       
+       /*
+        * set up initial conditions, we want a whole frmt->hsz block as we
+        * have no data yet.
+        */
+       res = hsz = frmt->hsz;
+       hdend = hdbuf;
+       shftsz = hsz - 1;
+       for(;;) {
+               /*
+                * keep looping until we get a contiguous FULL buffer
+                * (frmt->hsz is the proper size)
+                */
+               for (;;) {
+                       if ((ret = rd_wrbuf(hdend, res)) == res)
+                               break;
+
+                       /*
+                        * some kind of archive read problem, try to resync the
+                        * storage device, better give the user the bad news.
+                        */
+                       if ((ret == 0) || (rd_sync() < 0)) {
+                               warn(1,"Premature end of file on archive read");
+                               return(-1);
+                       }
+                       if (!in_resync) {
+                               if (act == APPND) {
+                                       warn(1,
+                                         "Archive I/O error, cannot continue");
+                                       return(-1);
+                               }
+                               warn(1,"Archive I/O error. Trying to recover.");
+                               ++in_resync;
+                       }
+
+                       /*
+                        * oh well, throw it all out and start over
+                        */
+                       res = hsz;
+                       hdend = hdbuf;
+               }
+
+               /*
+                * ok we have a contiguous buffer of the right size. Call the
+                * format read routine. If this was not a valid header and this
+                * format stores trailers outside of the header, call the
+                * format specific trailer routine to check for a trailer. We
+                * have to watch out that we do not mis-identify file data or
+                * block padding as a header or trailer. Format specific
+                * trailer functions must NOT check for the trailer while we
+                * are running in resync mode. Some trailer functions may tell
+                * us that this block cannot contain a valid header either, so
+                * we then throw out the entire block and start over.
+                */
+               if ((*frmt->rd)(arcn, hdbuf) == 0)
+                       break;
+
+               if (!frmt->inhead) {
+                       /*
+                        * this format has trailers outside of valid headers
+                        */
+                       if ((ret = (*frmt->trail)(hdbuf,in_resync,&cnt)) == 0){
+                               /*
+                                * valid trailer found, drain input as required
+                                */
+                               ar_drain();
+                               return(-1);
+                       }
+
+                       if (ret == 1) {
+                               /*
+                                * we are in resync and we were told to throw
+                                * the whole block out because none of the
+                                * bytes in this block can be used to form a
+                                * valid header
+                                */
+                               res = hsz;
+                               hdend = hdbuf;
+                               continue;
+                       }
+               }
+
+               /*
+                * Brute force section.
+                * not a valid header. We may be able to find a header yet. So
+                * we shift over by one byte, and set up to read one byte at a
+                * time from the archive and place it at the end of the buffer.
+                * We will keep moving byte at a time until we find a header or
+                * get a read error and have to start over.
+                */
+               if (!in_resync) {
+                       if (act == APPND) {
+                               warn(1,"Unable to append, archive header flaw");
+                               return(-1);
+                       }
+                       warn(1,"Invalid header, starting valid header search.");
+                       ++in_resync;
+               }
+               bcopy(hdbuf+1, hdbuf, shftsz);
+               res = 1;
+               hdend = hdbuf + shftsz;
+       }
+
+       /*
+        * ok got a valid header, check for trailer if format encodes it in the
+        * the header. NOTE: the parameters are different than trailer routines
+        * which encode trailers outside of the header!
+        */
+       if (frmt->inhead && ((*frmt->trail)(arcn) == 0)) {
+               /*
+                * valid trailer found, drain input as required
+                */
+               ar_drain();
+               return(-1);
+       }
+
+       ++flcnt;
+       return(0);
+}
+
+/*
+ * get_arc()
+ *     Figure out what format an archive is. Handles archive with flaws by
+ *     brute force searches for a legal header in any supported format. The
+ *     format id routines have to be careful to NOT mis-identify a format.
+ *     ASSUMED: headers fit within a BLKMULT header.
+ * Return:
+ *     0 if archive found -1 otherwise
+ */
+
+#if __STDC__
+static int
+get_arc(void)
+#else
+static int
+get_arc()
+#endif
+{
+       register int i;
+       register int hdsz = 0;
+       register int res;
+       register int minhd = BLKMULT;
+       char *hdend;
+       int notice = 0;
+       
+       /*
+        * find the smallest header size in all archive formats and then set up
+        * to read the archive.
+        */
+       for (i = 0; ford[i] >= 0; ++i) {
+               if (fsub[ford[i]].hsz < minhd)
+                       minhd = fsub[ford[i]].hsz;
+       }
+       if (rd_start() < 0)
+               return(-1);
+       res = BLKMULT;
+       hdsz = 0;
+       hdend = hdbuf;
+       for(;;) {
+               for (;;) {
+                       /*
+                        * fill the buffer with at least the smallest header
+                        */
+                       i = rd_wrbuf(hdend, res);
+                       if (i > 0)
+                               hdsz += i;
+                       if (hdsz >= minhd)
+                               break;
+
+                       /*
+                        * if we cannot recover from a read error quit
+                        */
+                       if ((i == 0) || (rd_sync() < 0))
+                               goto out;
+
+                       /*
+                        * when we get an error none of the data we already
+                        * have can be used to create a legal header (we just
+                        * got an error in the middle), so we throw it all out
+                        * and refill the buffer with fresh data.
+                        */
+                       res = BLKMULT;
+                       hdsz = 0;
+                       hdend = hdbuf;
+                       if (!notice) {
+                               if (act == APPND)
+                                       return(-1);
+                               warn(1,"Cannot identify format. Searching...");
+                               ++notice;
+                       }
+               }
+
+               /*
+                * we have at least the size of the smallest header in any
+                * archive format. Look to see if we have a match. The array
+                * ford[] is used to specify the header id order to reduce the
+                * chance of incorrectly id'ing a valid header (some formats
+                * may be subsets of each other and the order would then be
+                * important).
+                */
+               for (i = 0; ford[i] >= 0; ++i) {
+                       if ((*fsub[ford[i]].id)(hdbuf, hdsz) < 0)
+                               continue;
+                       frmt = &(fsub[ford[i]]);
+                       /* 
+                        * yuck, to avoid slow special case code in the extract
+                        * routines, just push this header back as if it was
+                        * not seen. We have left extra space at start of the
+                        * buffer for this purpose. This is a bit ugly, but
+                        * adding all the special case code is far worse.
+                        */
+                       pback(hdbuf, hdsz);
+                       return(0);
+               }
+
+               /*
+                * We have a flawed archive, no match. we start searching, but
+                * we never allow additions to flawed archives
+                */
+               if (!notice) {
+                       if (act == APPND)
+                               return(-1);
+                       warn(1, "Cannot identify format. Searching...");
+                       ++notice;
+               }
+
+               /*
+                * brute force search for a header that we can id.
+                * we shift through byte at a time. this is slow, but we cannot
+                * determine the nature of the flaw in the archive in a
+                * portable manner
+                */
+               if (--hdsz > 0) {
+                       bcopy(hdbuf+1, hdbuf, hdsz);
+                       res = BLKMULT - hdsz;
+                       hdend = hdbuf + hdsz;
+               } else {
+                       res = BLKMULT;
+                       hdend = hdbuf;
+                       hdsz = 0;
+               }
+       }
+
+    out:
+       /*
+        * we cannot find a header, bow, apologize and quit
+        */
+       warn(1, "Sorry, unable to determine archive format.");
+       return(-1);
+}
diff --git a/usr/src/bin/pax/buf_subs.c b/usr/src/bin/pax/buf_subs.c
new file mode 100644 (file)
index 0000000..f3c0796
--- /dev/null
@@ -0,0 +1,1079 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pax.h"
+#include "extern.h"
+
+/*
+ * routines which implement archive and file buffering
+ */
+
+#define MINFBSZ                512             /* default block size for hole detect */
+#define MAXFLT          10              /* default media read error limit */
+
+/*
+ * Need to change bufmem to dynamic allocation when the upper
+ * limit on blocking size is removed (though that will violate pax spec)
+ * MAXBLK define and tests will also need to be updated.
+ */
+static char bufmem[MAXBLK+BLKMULT];    /* i/o buffer + pushback id space */
+static char *buf;                      /* normal start of i/o buffer */
+static char *bufend;                   /* end or last char in i/o buffer */
+static char *bufpt;                    /* read/write point in i/o buffer */
+int blksz = MAXBLK;                            /* block input/output size in bytes */
+int wrblksz;                           /* user spec output size in bytes */
+int maxflt = MAXFLT;                   /* MAX consecutive media errors */
+int rdblksz;                           /* first read blksize (tapes only) */
+off_t wrlimit;                         /* # of bytes written per archive vol */
+off_t wrcnt;                           /* # of bytes written on current vol */
+off_t rdcnt;                           /* # of bytes read on current vol */
+
+/*
+ * wr_start()
+ *     set up the buffering system to operate in a write mode
+ * Return:
+ *     0 if ok, -1 if the user specified write block size violates pax spec
+ */
+
+#if __STDC__
+int
+wr_start(void)
+#else
+int
+wr_start()
+#endif
+{
+       buf = &(bufmem[BLKMULT]);
+       /*
+        * Check to make sure the write block size meets pax specs. If the user
+        * does not specify a blocksize, we use the format default blocksize.
+        * We must be picky on writes, so we do not allow the user to create an
+        * archive that might be hard to read elsewhere. If all ok, we then
+        * open the first archive volume
+        */
+       if (!wrblksz)  
+               wrblksz = frmt->bsz;
+       if (wrblksz > MAXBLK) {
+               warn(1, "Write block size of %d too large, maximium is: %d",
+                       wrblksz, MAXBLK);
+               return(-1);
+       }
+       if (wrblksz % BLKMULT) {
+               warn(1, "Write block size of %d is not a %d byte multiple",
+                   wrblksz, BLKMULT);
+               return(-1);
+       }
+
+       /*
+        * we only allow wrblksz to be used with all archive operations 
+        */
+       blksz = rdblksz = wrblksz;
+       if ((ar_open(arcname) < 0) && (ar_next() < 0))
+               return(-1);
+       wrcnt = 0;
+       bufend = buf + wrblksz;
+       bufpt = buf;
+       return(0);
+}
+
+/*
+ * rd_start()
+ *     set up buffering system to read an archive
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+int
+rd_start(void)
+#else
+int
+rd_start()
+#endif
+{
+       /*
+        * leave space for the header pushback (see get_arc()). If we are
+        * going to append and user specified a write block size, check it
+        * right away
+        */
+       buf = &(bufmem[BLKMULT]);
+       if ((act == APPND) && wrblksz) {
+               if (wrblksz > MAXBLK) {
+                       warn(1,"Write block size %d too large, maximium is: %d",
+                               wrblksz, MAXBLK);
+                       return(-1);
+               }
+               if (wrblksz % BLKMULT) {
+                       warn(1, "Write block size %d is not a %d byte multiple",
+                       wrblksz, BLKMULT);
+                       return(-1);
+               }
+       }
+
+       /*
+        * open the archive
+        */
+       if ((ar_open(arcname) < 0) && (ar_next() < 0))
+               return(-1);
+       bufend = buf + rdblksz;
+       bufpt = bufend;
+       rdcnt = 0;
+       return(0);
+}
+
+/*
+ * cp_start()
+ *     set up buffer system for copying within the file system
+ */
+
+#if __STDC__
+void
+cp_start(void)
+#else
+void
+cp_start()
+#endif
+{
+       buf = &(bufmem[BLKMULT]);
+       rdblksz = blksz = MAXBLK;
+}
+
+/*
+ * appnd_start()
+ *     Set up the buffering system to append new members to an archive that
+ *     was just read. The last block(s) of an archive may contain a format
+ *     specific trailer. To append a new member, this trailer has to be
+ *     removed from the archive. The first byte of the trailer is replaced by
+ *     the start of the header of the first file added to the archive. The
+ *     format specific end read function tells us how many bytes to move
+ *     backwards in the archive to be positioned BEFORE the trailer. Two
+ *     different postions have to be adjusted, the O.S. file offset (e.g. the
+ *     position of the tape head) and the write point within the data we have
+ *     stored in the read (soon to become write) buffer. We may have to move
+ *     back several records (the number depends on the size of the archive
+ *     record and the size of the format trailer) to read up the record where
+ *     the first byte of the trailer is recorded. Trailers may span (and
+ *     overlap) record boundries.
+ *     We first calculate which record has the first byte of the trailer. We
+ *     move the OS file offset back to the start of this record and read it
+ *     up. We set the buffer write pointer to be at this byte (the byte where
+ *     the trailer starts). We then move the OS file pointer back to the
+ *     start of this record so a flush of this buffer will replace the record
+ *     in the archive.
+ *     A major problem is rewriting this last record. For archives stored
+ *     on disk files, this is trival. However, many devices are really picky
+ *     about the conditions under which they will allow a write to occur.
+ *     Often devices restrict the conditions where writes can be made writes,
+ *     so it may not be feasable to append archives stored on all types of
+ *     devices. 
+ * Return:
+ *     0 for success, -1 for failure
+ */
+
+#if __STDC__
+int
+appnd_start(off_t skcnt)
+#else
+int
+appnd_start(skcnt)
+       off_t skcnt;
+#endif
+{
+       register int res;
+       off_t cnt;
+
+       if (exit_val != 0) {
+               warn(0, "Cannot append to an archive that may have flaws.");
+               return(-1);
+       }
+       /*
+        * if the user did not specify a write blocksize, inherit the size used
+        * in the last archive volume read. (If a is set we still use rdblksz
+        * until next volume, cannot shift sizes within a single volume).
+        */
+       if (!wrblksz)
+               wrblksz = blksz = rdblksz;
+       else
+               blksz = rdblksz;
+
+       /*
+        * make sure that this volume allows appends
+        */
+       if (ar_app_ok() < 0)
+               return(-1);
+
+       /*
+        * Calculate bytes to move back and move in front of record where we
+        * need to start writing from. Remember we have to add in any padding
+        * that might be in the buffer after the trailer in the last block. We
+        * travel skcnt + padding ROUNDED UP to blksize.
+        */
+       skcnt += bufend - bufpt;
+       if ((cnt = (skcnt/blksz) * blksz) < skcnt)
+               cnt += blksz;
+       if (ar_rev((off_t)cnt) < 0)
+               goto out;
+
+       /*
+        * We may have gone too far if there is valid data in the block we are
+        * now in front of, read up the block and position the pointer after
+        * the valid data.
+        */
+       if ((cnt -= skcnt) > 0) {
+               /*
+                * watch out for stupid tape drives. ar_rev() will set rdblksz
+                * to be real physical blocksize so we must loop until we get
+                * the old rdblksz (now in blksz). If ar_rev() fouls up the
+                * determination of the physical block size, we will fail.
+                */
+               bufpt = buf;
+               bufend = buf + blksz;
+               while (bufpt < bufend) {
+                       if ((res = ar_read(bufpt, rdblksz)) <= 0)
+                               goto out;
+                       bufpt += res;
+               }
+               if (ar_rev((off_t)(bufpt - buf)) < 0)
+                       goto out;
+               bufpt = buf + cnt;
+               bufend = buf + blksz;
+       } else {
+               /*
+                * buffer is empty
+                */
+               bufend = buf + blksz;
+               bufpt = buf;
+       }
+       rdblksz = blksz;
+       rdcnt -= skcnt;
+       wrcnt = 0;
+
+       /*
+        * At this point we are ready to write. If the device requires special
+        * handling to write at a point were previously recorded data resides,
+        * that is handled in ar_set_wr(). From now on we operate under normal
+        * ARCHIVE mode (write) conditions
+        */
+       if (ar_set_wr() < 0)
+               return(-1);
+       act = ARCHIVE;
+       return(0);
+
+    out:
+       warn(1, "Unable to rewrite archive trailer, cannot append.");
+       return(-1);
+}
+       
+/*
+ * rd_sync()
+ *     A read error occurred on this archive volume. Resync the buffer and
+ *     try to reset the device (if possible) so we can continue to read. Keep
+ *     trying to do this until we get a valid read, or we reach the limit on
+ *     consecutive read faults (at which point we give up). The user can
+ *     adjust the read error limit through a command line option.
+ * Returns:
+ *     0 on success, and -1 on failure
+ */
+
+#if __STDC__
+int
+rd_sync(void)
+#else
+int
+rd_sync()
+#endif
+{
+       register int errcnt = 0;
+       register int res;
+
+       /*
+        * if the user says bail out on first fault, we are out of here...
+        */
+       if (maxflt == 0)
+               return(-1);
+       if (act == APPND) {
+               warn(1, "Unable to append when there are archive read errors.");
+               return(-1);
+       }
+
+       /*
+        * poke at device and try to get past media error
+        */
+       if (ar_rdsync() < 0) {
+               if (ar_next() < 0)
+                       return(-1);
+               else
+                       rdcnt = 0;
+       }
+
+       for (;;) {
+               if ((res = ar_read(buf, blksz)) > 0) {
+                       /*
+                        * All right! got some data, fill that buffer
+                        */
+                       bufpt = buf;
+                       bufend = buf + res;
+                       rdcnt += res;
+                       return(0);
+               }
+
+               /*
+                * Oh well, yet another failed read...
+                * if error limit reached, ditch. o.w. poke device to move past
+                * bad media and try again. if media is badly damaged, we ask
+                * the poor (and upset user at this point) for the next archive
+                * volume. remember the goal on reads is to get the most we
+                * can extract out of the archive.
+                */
+               if ((maxflt > 0) && (++errcnt > maxflt))
+                       warn(0,"Archive read error limit (%d) reached",maxflt);
+               else if (ar_rdsync() == 0)
+                       continue;
+               if (ar_next() < 0)
+                       break;
+               rdcnt = 0;
+               errcnt = 0;
+       }
+       return(-1);
+}
+
+/*
+ * pback()
+ *     push the data used during the archive id phase back into the I/O
+ *     buffer. This is required as we cannot be sure that the header does NOT
+ *     overlap a block boundry (as in the case we are trying to recover a
+ *     flawed archived). This was not designed to be used for any other
+ *     purpose. (What software engineering, HA!)
+ *     WARNING: do not even THINK of pback greater than BLKMULT, unless the
+ *     pback space is increased.
+ */
+
+#if __STDC__
+void
+pback(char *pt, int cnt)
+#else
+void
+pback(pt, cnt)
+       char *pt;
+       int cnt;
+#endif
+{
+       bufpt -= cnt;
+       bcopy(pt, bufpt, cnt);
+       return;
+}
+
+/*
+ * rd_skip()
+ *     skip foward in the archive during a archive read. Used to get quickly
+ *     past file data and padding for files the user did NOT select.
+ * Return:
+ *     0 if ok, -1 failure, and 1 when EOF on the archive volume was detected.
+ */
+
+#if __STDC__
+int
+rd_skip(off_t skcnt)
+#else
+int
+rd_skip(skcnt)
+       off_t skcnt;
+#endif
+{
+       off_t res;
+       off_t cnt;
+       off_t skipped = 0;
+
+       /*
+        * consume what data we have in the buffer. If we have to move foward
+        * whole records, we call the low level skip function to see if we can
+        * move within the archive without doing the expensive reads on data we
+        * do not want.
+        */
+       if (skcnt == 0)
+               return(0);
+       res = MIN((bufend - bufpt), skcnt);
+       bufpt += res;
+       skcnt -= res;
+
+       /*
+        * if skcnt is now 0, then no additional i/o is needed
+        */
+       if (skcnt == 0)
+               return(0);
+
+       /*
+        * We have to read more, calculate complete and partial record reads
+        * based on rdblksz. we skip over "cnt" complete records
+        */
+       res = skcnt%rdblksz;
+       cnt = (skcnt/rdblksz) * rdblksz;
+
+       /*
+        * if the skip fails, we will have to resync. ar_fow will tell us
+        * how much it can skip over. We will have to read the rest.
+        */
+       if (ar_fow(cnt, &skipped) < 0)
+               return(-1);
+       res += cnt - skipped;
+       rdcnt += skipped;
+
+       /*
+        * what is left we have to read (which may be the whole thing if
+        * ar_fow() told us the device can only read to skip records);
+        */
+       while (res > 0L) {
+               cnt = bufend - bufpt;
+               /*
+                * if the read fails, we will have to resync
+                */
+               if ((cnt <= 0) && ((cnt = buf_fill()) < 0))
+                       return(-1);
+               if (cnt == 0)
+                       return(1);
+               cnt = MIN(cnt, res);
+               bufpt += cnt;
+               res -= cnt;
+       }
+       return(0);
+}
+
+/* 
+ * wr_fin()
+ *     flush out any data (and pad if required) the last block. We always pad
+ *     with zero (even though we do not have to). Padding with 0 makes it a
+ *     lot easier to recover if the archive is damaged. zero paddding SHOULD
+ *     BE a requirement....
+ */
+
+#if __STDC__
+void
+wr_fin(void)
+#else
+void
+wr_fin()
+#endif
+{
+       if (bufpt > buf) {
+               bzero(bufpt, bufend - bufpt);
+               bufpt = bufend;
+               (void)buf_flush(blksz);
+       }
+}
+
+/*
+ * wr_rdbuf()
+ *     fill the write buffer from data passed to it in a buffer (usually used
+ *     by format specific write routines to pass a file header). On failure we
+ *     punt. We do not allow the user to continue to write flawed archives.
+ *     We assume these headers are not very large (the memory copy we use is
+ *     a bit expensive). 
+ * Return:
+ *     0 if buffer was filled ok, -1 o.w. (buffer flush failure)
+ */
+
+#if __STDC__
+int
+wr_rdbuf(register char *out, register int outcnt)
+#else
+int
+wr_rdbuf(out, outcnt)
+       register char *out;
+       register int outcnt;
+#endif
+{
+       register int cnt;
+
+       /*
+        * while there is data to copy copy into the write buffer. when the
+        * write buffer fills, flush it to the archive and continue
+        */
+       while (outcnt > 0) {
+               cnt = bufend - bufpt;
+               if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0))
+                       return(-1);
+               /*
+                * only move what we have space for
+                */
+               cnt = MIN(cnt, outcnt);
+               bcopy(out, bufpt, cnt);
+               bufpt += cnt;
+               out += cnt;
+               outcnt -= cnt;
+       }
+       return(0);
+}
+
+/*
+ * rd_wrbuf()
+ *     copy from the read buffer into a supplied buffer a specified number of
+ *     bytes. If the read buffer is empty fill it and continue to copy.
+ *     usually used to obtain a file header for processing by a format
+ *     specific read routine.
+ * Return
+ *     number of bytes copied to the buffer, 0 indicates EOF on archive volume,
+ *     -1 is a read error
+ */
+
+#if __STDC__
+int
+rd_wrbuf(register char *in, register int cpcnt)
+#else
+int
+rd_wrbuf(in, cpcnt)
+       register char *in;
+       register int cpcnt;
+#endif
+{
+       register int res;
+       register int cnt;
+       register int incnt = cpcnt;
+
+       /*
+        * loop until we fill the buffer with the requested number of bytes
+        */
+       while (incnt > 0) {
+               cnt = bufend - bufpt;
+               if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) {
+                       /*
+                        * read error, return what we got (or the error if
+                        * no data was copied). The caller must know that an
+                        * error occured and has the best knowledge what to
+                        * do with it
+                        */
+                       if ((res = cpcnt - incnt) > 0)
+                               return(res);
+                       return(cnt);
+               }
+
+               /*
+                * calculate how much data to copy based on whats left and
+                * state of buffer
+                */
+               cnt = MIN(cnt, incnt);
+               bcopy(bufpt, in, cnt);
+               bufpt += cnt;
+               incnt -= cnt;
+               in += cnt;
+       }
+       return(cpcnt);
+}
+
+/*
+ * wr_skip()
+ *     skip foward during a write. In other words add padding to the file.
+ *     we add zero filled padding as it makes flawed archives much easier to
+ *     recover from. the caller tells us how many bytes of padding to add
+ *     This routine was not designed to add HUGE amount of padding, just small
+ *     amounts (a few 512 byte blocks at most)
+ * Return:
+ *     0 if ok, -1 if there was a buf_flush failure
+ */
+
+#if __STDC__
+int
+wr_skip(off_t skcnt)
+#else
+int
+wr_skip(skcnt)
+       off_t skcnt;
+#endif
+{
+       register int cnt;
+
+       /*
+        * loop while there is more padding to add
+        */
+       while (skcnt > 0L) {
+               cnt = bufend - bufpt;
+               if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0))
+                       return(-1);
+               cnt = MIN(cnt, skcnt);
+               bzero(bufpt, cnt);
+               bufpt += cnt;
+               skcnt -= cnt;
+       }
+       return(0);
+}
+
+/*
+ * wr_rdfile()
+ *     fill write buffer with the contents of a file. We are passed an open
+ *     file descriptor to the file an the archive structure that describes the
+ *     file we are storing. The variable "left" is modified to contain the
+ *     number of bytes of the file we were NOT able to write to the archive.
+ *     it is important that we always write EXACTLY the number of bytes that
+ *     the format specific write routine told us to. The file can also get
+ *     bigger, so reading to the end of file would create an improper archive,
+ *     we just detect this case and warn the user. We never create a bad
+ *     archive if we can avoid it. Of course trying to archive files that are
+ *     active is asking for trouble. It we fail, we pass back how much we
+ *     could NOT copy and let the caller deal with it.
+ * Return:
+ *     0 ok, -1 if archive write failure. a short read of the file returns a
+ *     0, but "left" is set to be greater than zero.
+ */
+
+#if __STDC__
+int
+wr_rdfile(ARCHD *arcn, int ifd, off_t *left)
+#else
+int
+wr_rdfile(arcn, ifd, left)
+       ARCHD *arcn;
+       int ifd;
+       off_t *left;
+#endif
+{
+       register int cnt;
+       register int res = 0;
+       register off_t size = arcn->sb.st_size;
+       struct stat sb;
+
+       /*
+        * while there are more bytes to write
+        */
+       while (size > 0L) {
+               cnt = bufend - bufpt;
+               if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) {
+                       *left = size;
+                       return(-1);
+               }
+               cnt = MIN(cnt, size);
+               if ((res = read(ifd, bufpt, cnt)) <= 0)
+                       break;
+               size -= res;
+               bufpt += res;
+       }
+
+       /*
+        * better check the file did not change during this operation
+        * or the file read failed.
+        */
+       if (res < 0)
+               syswarn(1, errno, "Read fault on %s", arcn->org_name);
+       else if (size != 0L)
+               warn(1, "File changed size during read %s", arcn->org_name);
+       else if (fstat(ifd, &sb) < 0)
+               syswarn(1, errno, "Failed stat on %s", arcn->org_name);
+       else if (arcn->sb.st_mtime != sb.st_mtime)
+               warn(1, "File %s was modified during copy to archive",
+                       arcn->org_name);
+       *left = size;
+       return(0);
+}
+
+/*
+ * rd_wrfile()
+ *     extract the contents of a file from the archive. If we are unable to
+ *     extract the entire file (due to failure to write the file) we return
+ *     the numbers of bytes we did NOT process. This way the caller knows how
+ *     many bytes to skip past to find the next archive header. If the failure
+ *     was due to an archive read, we will catch that when we try to skip. If
+ *     the format supplies a file data crc value, we calculate the actual crc
+ *     so that it can be compared to the value stored in the header
+ * NOTE:
+ *     We call a special function to write the file. This function attempts to
+ *     restore file holes (blocks of zeros) into the file. When files are
+ *     sparse this saves space, and is a LOT faster. For non sparse files
+ *     the performance hit is small. As of this writing, no archive supports
+ *     information on where the file holes are.
+ * Return:
+ *     0 ok, -1 if archive read failure. if we cannot write the entire file,
+ *     we return a 0 but "left" is set to be the amount unwritten
+ */
+
+#if __STDC__
+int
+rd_wrfile(ARCHD *arcn, int ofd, off_t *left)
+#else
+int
+rd_wrfile(arcn, ofd, left)
+       ARCHD *arcn;
+       int ofd;
+       off_t *left;
+#endif
+{
+       register int cnt = 0;
+       register off_t size = arcn->sb.st_size;
+       register int res = 0;
+       register char *fnm = arcn->name;
+       int isem = 1;
+       int rem;
+       int sz = MINFBSZ;
+       struct stat sb;
+       u_long crc = 0L;
+
+       /*
+        * pass the blocksize of the file being written to the write routine,
+        * if the size is zero, use the default MINFBSZ
+        */
+        if (fstat(ofd, &sb) == 0) {
+               if (sb.st_blksize > 0)
+                       sz = (int)sb.st_blksize;
+        } else
+                syswarn(0,errno,"Unable to obtain block size for file %s",fnm);
+       rem = sz;
+       *left = 0L;
+
+       /*
+        * Copy the archive to the file the number of bytes specified. We have
+        * to assume that we want to recover file holes as none of the archive
+        * formats can record the location of file holes.
+        */
+       while (size > 0L) {
+               cnt = bufend - bufpt;
+               /*
+                * if we get a read error, we do not want to skip, as we may
+                * miss a header, so we do not set left, but if we get a write
+                * error, we do want to skip over the unprocessed data.
+                */
+               if ((cnt <= 0) && ((cnt = buf_fill()) <= 0))
+                       break;
+               cnt = MIN(cnt, size);
+               if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) {
+                       *left = size;
+                       break;
+               }
+
+               if (docrc) {
+                       /*
+                        * update the actual crc value
+                        */
+                       cnt = res;
+                       while (--cnt >= 0)
+                               crc += *bufpt++ & 0xff;
+               } else
+                       bufpt += res;
+               size -= res;
+       }
+
+       /*
+        * if the last block has a file hole (all zero), we must make sure this
+        * gets updated in the file. We force the last block of zeros to be
+        * written. just closing with the file offset moved foward may not put
+        * a hole at the end of the file.
+        */
+       if (isem && (arcn->sb.st_size > 0L))
+               file_flush(ofd, fnm, isem);
+
+       /*
+        * if we failed from archive read, we do not want to skip
+        */
+       if ((size > 0L) && (*left == 0L)) 
+               return(-1);
+
+       /*
+        * some formats record a crc on file data. If so, then we compare the
+        * calculated crc to the crc stored in the archive
+        */
+       if (docrc && (size == 0L) && (arcn->crc != crc))
+               warn(1,"Actual crc does not match expected crc %s",arcn->name);
+       return(0);
+}
+
+/*
+ * cp_file()
+ *     copy the contents of one file to another. used during -rw phase of pax
+ *     just as in rd_wrfile() we use a special write function to write the
+ *     destination file so we can properly copy files with holes.
+ */
+
+#if __STDC__
+void
+cp_file(ARCHD *arcn, int fd1, int fd2)
+#else
+void
+cp_file(arcn, fd1, fd2)
+       ARCHD *arcn;
+       int fd1;
+       int fd2;
+#endif
+{
+       register int cnt;
+       register off_t cpcnt = 0L;
+       register int res = 0;
+       register char *fnm = arcn->name;
+       register int no_hole = 0;
+       int isem = 1;
+       int rem;
+       int sz = MINFBSZ;
+       struct stat sb;
+
+       /*
+        * check for holes in the source file. If none, we will use regular
+        * write instead of file write.
+        */
+        if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= arcn->sb.st_size)
+               ++no_hole;
+
+       /*
+        * pass the blocksize of the file being written to the write routine,
+        * if the size is zero, use the default MINFBSZ
+        */
+        if (fstat(fd2, &sb) == 0) {
+               if (sb.st_blksize > 0)
+                       sz = sb.st_blksize;
+        } else
+                syswarn(0,errno,"Unable to obtain block size for file %s",fnm);
+       rem = sz;
+
+       /*
+        * read the source file and copy to destination file until EOF
+        */
+       for(;;) {
+               if ((cnt = read(fd1, buf, blksz)) <= 0)
+                       break;
+               if (no_hole)
+                       res = write(fd2, buf, cnt);
+               else
+                       res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm);
+               if (res != cnt)
+                       break;
+               cpcnt += cnt;
+       }
+
+       /*
+        * check to make sure the copy is valid.
+        */
+       if (res < 0)
+               syswarn(1, errno, "Failed write during copy of %s to %s",
+                       arcn->org_name, arcn->name);
+       else if (cpcnt != arcn->sb.st_size)
+               warn(1, "File %s changed size during copy to %s",
+                       arcn->org_name, arcn->name);
+       else if (fstat(fd1, &sb) < 0)
+               syswarn(1, errno, "Failed stat of %s", arcn->org_name);
+       else if (arcn->sb.st_mtime != sb.st_mtime)
+               warn(1, "File %s was modified during copy to %s",
+                       arcn->org_name, arcn->name);
+
+       /*
+        * if the last block has a file hole (all zero), we must make sure this
+        * gets updated in the file. We force the last block of zeros to be
+        * written. just closing with the file offset moved foward may not put
+        * a hole at the end of the file.
+        */
+       if (!no_hole && isem && (arcn->sb.st_size > 0L))
+               file_flush(fd2, fnm, isem);
+       return;
+}
+
+/*
+ * buf_fill()
+ *     fill the read buffer with the next record (or what we can get) from
+ *     the archive volume.
+ * Return:
+ *     Number of bytes of data in the read buffer, -1 for read error, and
+ *     0 when finished (user specified termination in ar_next()).
+ */
+
+#if __STDC__
+int
+buf_fill(void)
+#else
+int
+buf_fill()
+#endif
+{
+       register int cnt;
+       static int fini = 0;
+
+       if (fini)
+               return(0);
+
+       for(;;) {
+               /*
+                * try to fill the buffer. on error the next archive volume is
+                * opened and we try again.
+                */
+               if ((cnt = ar_read(buf, blksz)) > 0) {
+                       bufpt = buf;
+                       bufend = buf + cnt;
+                       rdcnt += cnt;
+                       return(cnt);
+               }
+
+               /*
+                * errors require resync, EOF goes to next archive
+                */
+               if (cnt < 0)
+                       break;
+               if (ar_next() < 0) {
+                       fini = 1;
+                       return(0);
+               }
+               rdcnt = 0;
+       }
+       exit_val = 1;
+       return(-1);
+}
+
+/*
+ * buf_flush()
+ *     force the write buffer to the archive. We are passed the number of
+ *     bytes in the buffer at the point of the flush. When we change archives
+ *     the record size might change. (either larger or smaller).
+ * Return:
+ *     0 if all is ok, -1 when a write error occurs.
+ */
+
+#if __STDC__
+int
+buf_flush(register int bufcnt)
+#else
+int
+buf_flush(bufcnt)
+       register int bufcnt;
+#endif
+{
+       register int cnt;
+       register int push = 0;
+       register int totcnt = 0;
+
+       /*
+        * if we have reached the user specified byte count for each archive
+        * volume, prompt for the next volume. (The non-standrad -R flag).
+        * NOTE: If the wrlimit is smaller than wrcnt, we will always write
+        * at least one record. We always round limit UP to next blocksize.
+        */
+       if ((wrlimit > 0) && (wrcnt > wrlimit)) {
+               warn(0, "User specified archive volume byte limit reached.");
+               if (ar_next() < 0) {
+                       wrcnt = 0;
+                       exit_val = 1;
+                       return(-1);
+               }
+               wrcnt = 0;
+
+               /*
+                * The new archive volume might have changed the size of the
+                * write blocksize. if so we figure out if we need to write
+                * (one or more times), or if there is now free space left in
+                * the buffer (it is no longer full). bufcnt has the number of
+                * bytes in the buffer, (the blocksize, at the point we were
+                * CALLED). Push has the amount of "extra" data in the buffer
+                * if the block size has shrunk from a volume change.
+                */
+               bufend = buf + blksz;
+               if (blksz > bufcnt)
+                       return(0);
+               if (blksz < bufcnt)
+                       push = bufcnt - blksz;
+       }
+
+       /*
+        * We have enough data to write at least one archive block
+        */
+       for (;;) {
+               /*
+                * write a block and check if it all went out ok
+                */
+               cnt = ar_write(buf, blksz); 
+               if (cnt == blksz) {
+                       /*
+                        * the write went ok
+                        */
+                       wrcnt += cnt;
+                       totcnt += cnt;
+                       if (push > 0) {
+                               /* we have extra data to push to the front.
+                                * check for more than 1 block of push, and if
+                                * so we loop back to write again
+                                */
+                               bcopy(bufend, buf, push);
+                               bufpt = buf + push;
+                               if (push >= blksz) {
+                                       push -= blksz;
+                                       continue;
+                               }
+                       } else
+                               bufpt = buf;
+                       return(totcnt);
+               } else if (cnt > 0) {
+                       /*
+                        * Oh drat we got a partial write!
+                        * if format doesnt care about alignment let it go,
+                        * we warned the user in ar_write().... but this means
+                        * the last record on this volume violates pax spec....
+                        */
+                       totcnt += cnt;
+                       wrcnt += cnt;
+                       bufpt = buf + cnt;
+                       cnt = bufcnt - cnt;
+                       bcopy(bufpt, buf, cnt);
+                       bufpt = buf + cnt;
+                       if (!frmt->blkalgn || ((cnt % frmt->blkalgn) == 0))
+                               return(totcnt);
+                       break;
+               }
+
+               /*
+                * All done, go to next archive
+                */
+               wrcnt = 0;
+               if (ar_next() < 0)
+                       break;
+
+               /*
+                * The new archive volume might also have changed the block
+                * size. if so, figure out if we have too much or too little
+                * data for using the new block size
+                */
+               bufend = buf + blksz;
+               if (blksz > bufcnt)
+                       return(0);
+               if (blksz < bufcnt)
+                       push = bufcnt - blksz;
+       }
+
+       /*
+        * write failed, stop pax. we must not create a bad archive!
+        */
+       exit_val = 1;
+       return(-1);
+}
diff --git a/usr/src/bin/pax/cache.c b/usr/src/bin/pax/cache.c
new file mode 100644 (file)
index 0000000..0974f6e
--- /dev/null
@@ -0,0 +1,475 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cache.c    8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "cache.h"
+#include "extern.h"
+
+/*
+ * routines that control user, group, uid and gid caches (for the archive
+ * member print routine).
+ * IMPORTANT:
+ * these routines cache BOTH hits and misses, a major performance improvement
+ */
+
+static int pwopn = 0;          /* is password file open */
+static int gropn = 0;          /* is group file open */
+static UIDC **uidtb = NULL;    /* uid to name cache */
+static GIDC **gidtb = NULL;    /* gid to name cache */
+static UIDC **usrtb = NULL;    /* user name to uid cache */
+static GIDC **grptb = NULL;    /* group name to gid cache */
+
+/*
+ * uidtb_start
+ *     creates an an empty uidtb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+int
+uidtb_start(void)
+#else
+int
+uidtb_start()
+#endif
+{
+       static int fail = 0;
+
+       if (uidtb != NULL)
+               return(0);
+       if (fail)
+               return(-1);
+       if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
+               ++fail;
+               warn(1, "Unable to allocate memory for user id cache table");
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * gidtb_start
+ *     creates an an empty gidtb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+int
+gidtb_start(void)
+#else
+int
+gidtb_start()
+#endif
+{
+       static int fail = 0;
+
+       if (gidtb != NULL)
+               return(0);
+       if (fail)
+               return(-1);
+       if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
+               ++fail;
+               warn(1, "Unable to allocate memory for group id cache table");
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * usrtb_start
+ *     creates an an empty usrtb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+int
+usrtb_start(void)
+#else
+int
+usrtb_start()
+#endif
+{
+       static int fail = 0;
+
+       if (usrtb != NULL)
+               return(0);
+       if (fail)
+               return(-1);
+       if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
+               ++fail;
+               warn(1, "Unable to allocate memory for user name cache table");
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * grptb_start
+ *     creates an an empty grptb
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+int
+grptb_start(void)
+#else
+int
+grptb_start()
+#endif
+{
+       static int fail = 0;
+
+       if (grptb != NULL)
+               return(0);
+       if (fail)
+               return(-1);
+       if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
+               ++fail;
+               warn(1,"Unable to allocate memory for group name cache table");
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * name_uid()
+ *     caches the name (if any) for the uid. If frc set, we always return the
+ *     the stored name (if valid or invalid match). We use a simple hash table.
+ * Return
+ *     Pointer to stored name (or a empty string)
+ */
+
+#if __STDC__
+char *
+name_uid(uid_t uid, int frc)
+#else
+char *
+name_uid(uid, frc)
+       uid_t uid;
+       int frc;
+#endif
+{
+       register struct passwd *pw;
+       register UIDC *ptr;
+
+       if ((uidtb == NULL) && (uidtb_start() < 0))
+               return("");
+
+       /*
+        * see if we have this uid cached
+        */
+       ptr = uidtb[uid % UID_SZ];
+       if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
+               /*
+                * have an entry for this uid
+                */
+               if (frc || (ptr->valid == VALID))
+                       return(ptr->name);
+               return("");
+       }
+
+       /*
+        * No entry for this uid, we will add it
+        */
+       if (!pwopn) {
+               setpassent(1);
+               ++pwopn;
+       }
+       if (ptr == NULL)
+               ptr = (UIDC *)malloc(sizeof(UIDC));
+
+       if ((pw = getpwuid(uid)) == NULL) {
+               /*
+                * no match for this uid in the local password file
+                * a string that is the uid in numberic format
+                */
+               if (ptr == NULL)
+                       return("");
+               ptr->uid = uid;
+               ptr->valid = INVALID;
+#              ifdef NET2_STAT
+               (void)sprintf(ptr->name, "%u", uid);
+#              else
+               (void)sprintf(ptr->name, "%lu", uid);
+#              endif
+               if (frc == 0)
+                       return("");
+       } else {
+               /*
+                * there is an entry for this uid in the password file
+                */
+               if (ptr == NULL)
+                       return(pw->pw_name);
+               ptr->uid = uid;
+               (void)strncpy(ptr->name, pw->pw_name, UNMLEN);
+               ptr->name[UNMLEN-1] = '\0';
+               ptr->valid = VALID;
+       }
+       return(ptr->name);
+}
+
+/*
+ * name_gid()
+ *     caches the name (if any) for the gid. If frc set, we always return the
+ *     the stored name (if valid or invalid match). We use a simple hash table.
+ * Return
+ *     Pointer to stored name (or a empty string)
+ */
+
+#if __STDC__
+char *
+name_gid(gid_t gid, int frc)
+#else
+char *
+name_gid(gid, frc)
+       gid_t gid;
+       int frc;
+#endif
+{
+       register struct group *gr;
+       register GIDC *ptr;
+
+       if ((gidtb == NULL) && (gidtb_start() < 0))
+               return("");
+
+       /*
+        * see if we have this gid cached
+        */
+       ptr = gidtb[gid % GID_SZ];
+       if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
+               /*
+                * have an entry for this gid
+                */
+               if (frc || (ptr->valid == VALID))
+                       return(ptr->name);
+               return("");
+       }
+
+       /*
+        * No entry for this gid, we will add it
+        */
+       if (!gropn) {
+               setgroupent(1);
+               ++gropn;
+       }
+       if (ptr == NULL)
+               ptr = (GIDC *)malloc(sizeof(GIDC));
+
+       if ((gr = getgrgid(gid)) == NULL) {
+               /*
+                * no match for this gid in the local group file, put in
+                * a string that is the gid in numberic format
+                */
+               if (ptr == NULL)
+                       return("");
+               ptr->gid = gid;
+               ptr->valid = INVALID;
+#              ifdef NET2_STAT
+               (void)sprintf(ptr->name, "%u", gid);
+#              else
+               (void)sprintf(ptr->name, "%lu", gid);
+#              endif
+               if (frc == 0)
+                       return("");
+       } else {
+               /*
+                * there is an entry for this group in the group file
+                */
+               if (ptr == NULL)
+                       return(gr->gr_name);
+               ptr->gid = gid;
+               (void)strncpy(ptr->name, gr->gr_name, GNMLEN);
+               ptr->name[GNMLEN-1] = '\0';
+               ptr->valid = VALID;
+       }
+       return(ptr->name);
+}
+
+/*
+ * uid_name()
+ *     caches the uid for a given user name. We use a simple hash table.
+ * Return
+ *     the uid (if any) for a user name, or a -1 if no match can be found
+ */
+
+#if __STDC__
+int
+uid_name(char *name, uid_t *uid)
+#else
+int
+uid_name(name, uid)
+       char *name;
+       uid_t *uid;
+#endif
+{
+       register struct passwd *pw;
+       register UIDC *ptr;
+       register int namelen;
+
+       /*
+        * return -1 for mangled names
+        */
+       if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
+               return(-1);
+       if ((usrtb == NULL) && (usrtb_start() < 0))
+               return(-1);
+
+       /*
+        * look up in hash table, if found and valid return the uid,
+        * if found and invalid, return a -1
+        */
+       ptr = usrtb[st_hash(name, namelen, UNM_SZ)];
+       if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+               if (ptr->valid == INVALID)
+                       return(-1);
+               *uid = ptr->uid;
+               return(0);
+       }
+
+       if (!pwopn) {
+               setpassent(1);
+               ++pwopn;
+       }
+
+       if (ptr == NULL)
+               ptr = (UIDC *)malloc(sizeof(UIDC));
+
+       /*
+        * no match, look it up, if no match store it as an invalid entry,
+        * or store the matching uid
+        */
+       if (ptr == NULL) {
+               if ((pw = getpwnam(name)) == NULL)
+                       return(-1);
+               *uid = pw->pw_uid;
+               return(0);
+       }
+       (void)strncpy(ptr->name, name, UNMLEN);
+       ptr->name[UNMLEN-1] = '\0';
+       if ((pw = getpwnam(name)) == NULL) {
+               ptr->valid = INVALID;
+               return(-1);
+       }
+       ptr->valid = VALID;
+       *uid = ptr->uid = pw->pw_uid;
+       return(0);
+}
+
+/*
+ * gid_name()
+ *     caches the gid for a given group name. We use a simple hash table.
+ * Return
+ *     the gid (if any) for a group name, or a -1 if no match can be found
+ */
+
+#if __STDC__
+int
+gid_name(char *name, gid_t *gid)
+#else
+int
+gid_name(name, gid)
+       char *name;
+       gid_t *gid;
+#endif
+{
+       register struct group *gr;
+       register GIDC *ptr;
+       register int namelen;
+
+       /*
+        * return -1 for mangled names
+        */
+       if (((namelen = strlen(name)) == 0) || (name[0] == '\0'))
+               return(-1);
+       if ((grptb == NULL) && (grptb_start() < 0))
+               return(-1);
+
+       /*
+        * look up in hash table, if found and valid return the uid,
+        * if found and invalid, return a -1
+        */
+       ptr = grptb[st_hash(name, namelen, GID_SZ)];
+       if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+               if (ptr->valid == INVALID)
+                       return(-1);
+               *gid = ptr->gid;
+               return(0);
+       }
+
+       if (!gropn) {
+               setgroupent(1);
+               ++gropn;
+       }
+       if (ptr == NULL)
+               ptr = (GIDC *)malloc(sizeof(GIDC));
+
+       /*
+        * no match, look it up, if no match store it as an invalid entry,
+        * or store the matching gid
+        */
+       if (ptr == NULL) {
+               if ((gr = getgrnam(name)) == NULL)
+                       return(-1);
+               *gid = gr->gr_gid;
+               return(0);
+       }
+
+       (void)strncpy(ptr->name, name, GNMLEN);
+       ptr->name[GNMLEN-1] = '\0';
+       if ((gr = getgrnam(name)) == NULL) {
+               ptr->valid = INVALID;
+               return(-1);
+       }
+       ptr->valid = VALID;
+       *gid = ptr->gid = gr->gr_gid;
+       return(0);
+}
diff --git a/usr/src/bin/pax/cache.h b/usr/src/bin/pax/cache.h
new file mode 100644 (file)
index 0000000..0c1d427
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      @(#)cache.h    8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Constants and data structures used to implement group and password file
+ * caches. Traditional passwd/group cache routines perform quite poorly with
+ * archives. The chances of hitting a valid lookup with an archive is quite a
+ * bit worse than with files already resident on the file system. These misses
+ * create a MAJOR performance cost. To adress this problem, these routines
+ * cache both hits and misses.
+ *
+ * NOTE:  name lengths must be as large as those stored in ANY PROTOCOL and
+ * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME
+ */
+#define UNMLEN         32      /* >= user name found in any protocol */
+#define GNMLEN         32      /* >= group name found in any protocol */
+#define UID_SZ         317     /* size of user_name/uid cache */
+#define UNM_SZ         317     /* size of user_name/uid cache */
+#define GID_SZ         251     /* size of gid cache */
+#define GNM_SZ         317     /* size of group name cache */
+#define VALID          1       /* entry and name are valid */
+#define INVALID                2       /* entry valid, name NOT valid */
+
+/*
+ * Node structures used in the user, group, uid, and gid caches.
+ */
+
+typedef struct uidc {
+       int valid;              /* is this a valid or a miss entry */
+       char name[UNMLEN];      /* uid name */
+       uid_t uid;              /* cached uid */
+} UIDC;
+
+typedef struct gidc {
+       int valid;              /* is this a valid or a miss entry */
+       char name[GNMLEN];      /* gid name */
+       gid_t gid;              /* cached gid */
+} GIDC;
diff --git a/usr/src/bin/pax/cpio.c b/usr/src/bin/pax/cpio.c
new file mode 100644 (file)
index 0000000..7371fb4
--- /dev/null
@@ -0,0 +1,1274 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cpio.c     8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "cpio.h"
+#include "extern.h"
+
+static int rd_nm __P((register ARCHD *, int));
+static int rd_ln_nm __P((register ARCHD *));
+static int com_rd __P((register ARCHD *));
+
+/*
+ * Routines which support the different cpio versions
+ */
+
+static int swp_head;           /* binary cpio header byte swap */
+
+/*
+ * Routines common to all versions of cpio
+ */
+
+/*
+ * cpio_strd()
+ *     Fire up the hard link detection code
+ * Return:
+ *      0 if ok -1 otherwise (the return values of lnk_start())
+ */
+
+#if __STDC__
+int
+cpio_strd(void)
+#else
+int
+cpio_strd()
+#endif
+{
+       return(lnk_start());
+}
+
+/*
+ * cpio_trail()
+ *     Called to determine if a header block is a valid trailer. We are
+ *     passed the block, the in_sync flag (which tells us we are in resync
+ *     mode; looking for a valid header), and cnt (which starts at zero)
+ *     which is used to count the number of empty blocks we have seen so far.
+ * Return:
+ *     0 if a valid trailer, -1 if not a valid trailer, 
+ */
+
+#if __STDC__
+int
+cpio_trail(register ARCHD *arcn)
+#else
+int
+cpio_trail(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * look for trailer id in file we are about to process
+        */
+       if ((strcmp(arcn->name, TRAILER) == 0) && (arcn->sb.st_size == 0))
+               return(0);
+       return(-1);
+}
+
+/*
+ * com_rd()
+ *     operations common to all cpio read functions.
+ * Return:
+ *     0
+ */
+
+#if __STDC__
+static int
+com_rd(register ARCHD *arcn)
+#else
+static int
+com_rd(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       arcn->skip = 0;
+       arcn->pat = NULL;
+       arcn->org_name = arcn->name;
+       switch(arcn->sb.st_mode & C_IFMT) {
+       case C_ISFIFO:
+               arcn->type = PAX_FIF;
+               break;
+       case C_ISDIR:
+               arcn->type = PAX_DIR;
+               break;
+       case C_ISBLK:
+               arcn->type = PAX_BLK;
+               break;
+       case C_ISCHR:
+               arcn->type = PAX_CHR;
+               break;
+       case C_ISLNK:
+               arcn->type = PAX_SLK;
+               break;
+       case C_ISOCK:
+               arcn->type = PAX_SCK;
+               break;
+       case C_ISCTG:
+       case C_ISREG:
+       default:
+               /*
+                * we have file data, set up skip (pad is set in the format
+                * specific sections)
+                */
+               arcn->sb.st_mode = (arcn->sb.st_mode & 0xfff) | C_ISREG;
+               arcn->type = PAX_REG;
+               arcn->skip = arcn->sb.st_size;
+               break;
+       }
+       if (chk_lnk(arcn) < 0)
+               return(-1);
+       return(0);
+}
+
+/*
+ * cpio_end_wr()
+ *     write the special file with the name trailer in the proper format
+ * Return:
+ *     result of the write of the trailer from the cpio specific write func
+ */
+
+#if __STDC__
+int
+cpio_endwr(void)
+#else
+int
+cpio_endwr()
+#endif
+{
+       ARCHD last;
+
+       /*
+        * create a trailer request and call the proper format write function
+        */
+       bzero((char *)&last, sizeof(last));
+       last.nlen = sizeof(TRAILER) - 1;
+       last.type = PAX_REG;
+       last.sb.st_nlink = 1;
+       (void)strcpy(last.name, TRAILER);
+       return((*frmt->wr)(&last));
+}
+
+/*
+ * rd_nam()
+ *     read in the file name which follows the cpio header
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+static int
+rd_nm(register ARCHD *arcn, int nsz)
+#else
+static int
+rd_nm(arcn, nsz)
+       register ARCHD *arcn;
+       int nsz;
+#endif
+{
+       /*
+        * do not even try bogus values
+        */
+       if ((nsz == 0) || (nsz > sizeof(arcn->name))) {
+               warn(1, "Cpio file name length %d is out of range", nsz);
+               return(-1);
+       }
+
+       /*
+        * read the name and make sure it is not empty and is \0 terminated
+        */
+       if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') ||
+           (arcn->name[0] == '\0')) {
+               warn(1, "Cpio file name in header is corrupted");
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * rd_ln_nm()
+ *     read in the link name for a file with links. The link name is stored
+ *     like file data (and is NOT \0 terminated!)
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+static int
+rd_ln_nm(register ARCHD *arcn)
+#else
+static int
+rd_ln_nm(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * check the length specified for bogus values
+        */
+       if ((arcn->sb.st_size == 0) ||
+           (arcn->sb.st_size >= sizeof(arcn->ln_name))) {
+#              ifdef NET2_STAT
+               warn(1, "Cpio link name length is invalid: %lu",
+                   arcn->sb.st_size);
+#              else
+               warn(1, "Cpio link name length is invalid: %qu",
+                   arcn->sb.st_size);
+#              endif
+               return(-1);
+       }
+
+       /*
+        * read in the link name and \0 terminate it
+        */
+       if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) !=
+           (int)arcn->sb.st_size) {
+               warn(1, "Cpio link name read error");
+               return(-1);
+       }
+       arcn->ln_nlen = arcn->sb.st_size;
+       arcn->ln_name[arcn->ln_nlen] = '\0';
+
+       /*
+        * watch out for those empty link names
+        */
+       if (arcn->ln_name[0] == '\0') {
+               warn(1, "Cpio link name is corrupt");
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * Routines common to the extended byte oriented cpio format
+ */
+
+/*
+ * cpio_id()
+ *      determine if a block given to us is a valid extended byte oriented
+ *     cpio header
+ * Return:
+ *      0 if a valid header, -1 otherwise
+ */
+
+#if __STDC__
+int
+cpio_id(char *blk, int size)
+#else
+int
+cpio_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       if ((size < sizeof(HD_CPIO)) ||
+           (strncmp(blk, AMAGIC, sizeof(AMAGIC) - 1) != 0))
+               return(-1);
+       return(0);
+}
+
+/*
+ * cpio_rd()
+ *     determine if a buffer is a byte oriented extended cpio archive entry.
+ *     convert and store the values in the ARCHD parameter.
+ * Return:
+ *     0 if a valid header, -1 otherwise.
+ */
+
+#if __STDC__
+int
+cpio_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+cpio_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register int nsz;
+       register HD_CPIO *hd;
+
+       /*
+        * check that this is a valid header, if not return -1
+        */
+       if (cpio_id(buf, sizeof(HD_CPIO)) < 0)
+               return(-1);
+       hd = (HD_CPIO *)buf;
+
+       /*
+        * byte oriented cpio (posix) does not have padding! extract the octal
+        * ascii fields from the header
+        */
+       arcn->pad = 0L;
+       arcn->sb.st_dev = (dev_t)asc_ul(hd->c_dev, sizeof(hd->c_dev), OCT);
+       arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), OCT);
+       arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), OCT);
+       arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), OCT);
+       arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), OCT);
+       arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink),
+           OCT);
+       arcn->sb.st_rdev = (dev_t)asc_ul(hd->c_rdev, sizeof(hd->c_rdev), OCT);
+       arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime, sizeof(hd->c_mtime),
+           OCT);
+       arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+#      ifdef NET2_STAT
+       arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,sizeof(hd->c_filesize),
+           OCT);
+#      else
+       arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,sizeof(hd->c_filesize),
+           OCT);
+#      endif
+
+       /*
+        * check name size and if valid, read in the name of this entry (name
+        * follows header in the archive)
+        */
+       if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),OCT)) < 2)
+               return(-1);
+       arcn->nlen = nsz - 1;
+       if (rd_nm(arcn, nsz) < 0)
+               return(-1);
+
+       if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) {
+               /*
+                * no link name to read for this file
+                */
+               arcn->ln_nlen = 0;
+               arcn->ln_name[0] = '\0';
+               return(com_rd(arcn));
+       }
+
+       /*
+        * check link name size and read in the link name. Link names are
+        * stored like file data.
+        */
+       if (rd_ln_nm(arcn) < 0)
+               return(-1);
+
+       /*
+        * we have a valid header (with a link)
+        */
+       return(com_rd(arcn));
+}
+
+/*
+ * cpio_endrd()
+ *      no cleanup needed here, just return size of the trailer (for append)
+ * Return:
+ *      size of trailer header in this format
+ */
+
+#if __STDC__
+off_t
+cpio_endrd(void)
+#else
+off_t
+cpio_endrd()
+#endif
+{
+       return((off_t)(sizeof(HD_CPIO) + sizeof(TRAILER)));
+}
+
+/*
+ * cpio_stwr()
+ *     start up the device mapping table
+ * Return:
+ *     0 if ok, -1 otherwise (what dev_start() returns)
+ */
+
+#if __STDC__
+int
+cpio_stwr(void)
+#else
+int
+cpio_stwr()
+#endif
+{
+       return(dev_start());
+}
+
+/*
+ * cpio_wr()
+ *     copy the data in the ARCHD to buffer in extended byte oriented cpio
+ *     format.
+ * Return
+ *      0 if file has data to be written after the header, 1 if file has NO
+ *     data to write after the header, -1 if archive write failed
+ */
+
+#if __STDC__
+int
+cpio_wr(register ARCHD *arcn)
+#else
+int
+cpio_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HD_CPIO *hd;
+       register int nsz;
+       char hdblk[sizeof(HD_CPIO)];
+
+       /*
+        * check and repair truncated device and inode fields in the header
+        */
+       if (map_dev(arcn, (u_long)CPIO_MASK, (u_long)CPIO_MASK) < 0)
+               return(-1);
+
+       arcn->pad = 0L;
+       nsz = arcn->nlen + 1;
+       hd = (HD_CPIO *)hdblk;
+       if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR))
+               arcn->sb.st_rdev = 0;
+
+       switch(arcn->type) {
+       case PAX_CTG:
+       case PAX_REG:
+       case PAX_HRG:
+               /*
+                * set data size for file data
+                */
+#              ifdef NET2_STAT
+               if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize,
+                   sizeof(hd->c_filesize), OCT)) {
+#              else
+               if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize,
+                   sizeof(hd->c_filesize), OCT)) {
+#              endif
+                       warn(1,"File is too large for cpio format %s",
+                           arcn->org_name);
+                       return(1);
+               }
+               break;
+       case PAX_SLK:
+               /*
+                * set data size to hold link name
+                */
+               if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize,
+                   sizeof(hd->c_filesize), OCT))
+                       goto out;
+               break;
+       default:
+               /*
+                * all other file types have no file data
+                */
+               if (ul_asc((u_long)0, hd->c_filesize, sizeof(hd->c_filesize),
+                    OCT))
+                       goto out;
+               break;
+       }
+
+       /*
+        * copy the values to the header using octal ascii
+        */
+       if (ul_asc((u_long)MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) ||
+           ul_asc((u_long)arcn->sb.st_dev, hd->c_dev, sizeof(hd->c_dev),
+               OCT) ||
+           ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino),
+               OCT) ||
+           ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode),
+               OCT) ||
+           ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid),
+               OCT) ||
+           ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid),
+               OCT) ||
+           ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink),
+                OCT) ||
+           ul_asc((u_long)arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev),
+               OCT) ||
+           ul_asc((u_long)arcn->sb.st_mtime,hd->c_mtime,sizeof(hd->c_mtime),
+               OCT) ||
+           ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT))
+               goto out;
+
+       /*
+        * write the file name to the archive
+        */
+       if ((wr_rdbuf(hdblk, (int)sizeof(HD_CPIO)) < 0) ||
+           (wr_rdbuf(arcn->name, nsz) < 0)) {
+               warn(1, "Unable to write cpio header for %s", arcn->org_name);
+               return(-1);
+       }
+
+       /*
+        * if this file has data, we are done. The caller will write the file
+        * data, if we are link tell caller we are done, go to next file
+        */
+       if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) ||
+           (arcn->type == PAX_HRG))
+               return(0);
+       if (arcn->type != PAX_SLK)
+               return(1);
+
+       /*
+        * write the link name to the archive, tell the caller to go to the
+        * next file as we are done.
+        */
+       if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) {
+               warn(1,"Unable to write cpio link name for %s",arcn->org_name);
+               return(-1);
+       }
+       return(1);
+
+    out:
+       /*
+        * header field is out of range
+        */
+       warn(1, "Cpio header field is too small to store file %s",
+           arcn->org_name);
+       return(1);
+}
+
+/*
+ * Routines common to the system VR4 version of cpio (with/without file CRC)
+ */
+
+/*
+ * vcpio_id()
+ *      determine if a block given to us is a valid system VR4 cpio header
+ *     WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header 
+ *     uses HEX
+ * Return:
+ *      0 if a valid header, -1 otherwise
+ */
+
+#if __STDC__
+int
+vcpio_id(char *blk, int size)
+#else
+int
+vcpio_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       if ((size < sizeof(HD_VCPIO)) ||
+           (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0))
+               return(-1);
+       return(0);
+}
+
+/*
+ * crc_id()
+ *      determine if a block given to us is a valid system VR4 cpio header
+ *     WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX
+ * Return:
+ *      0 if a valid header, -1 otherwise
+ */
+
+#if __STDC__
+int
+crc_id(char *blk, int size)
+#else
+int
+crc_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       if ((size < sizeof(HD_VCPIO)) ||
+           (strncmp(blk, AVCMAGIC, sizeof(AVCMAGIC) - 1) != 0))
+               return(-1);
+       return(0);
+}
+
+/*
+ * crc_strd()
+ w     set file data CRC calculations. Fire up the hard link detection code
+ * Return:
+ *      0 if ok -1 otherwise (the return values of lnk_start())
+ */
+
+#if __STDC__
+int
+crc_strd(void)
+#else
+int
+crc_strd()
+#endif
+{
+       docrc = 1;
+       return(lnk_start());
+}
+
+/*
+ * vcpio_rd()
+ *     determine if a buffer is a system VR4 archive entry. (with/without CRC)
+ *     convert and store the values in the ARCHD parameter.
+ * Return:
+ *     0 if a valid header, -1 otherwise.
+ */
+
+#if __STDC__
+int
+vcpio_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+vcpio_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register HD_VCPIO *hd;
+       dev_t devminor;
+       dev_t devmajor;
+       register int nsz;
+
+       /*
+        * during the id phase it was determined if we were using CRC, use the
+        * proper id routine.
+        */
+       if (docrc) {
+               if (crc_id(buf, sizeof(HD_VCPIO)) < 0)
+                       return(-1);
+       } else {
+               if (vcpio_id(buf, sizeof(HD_VCPIO)) < 0)
+                       return(-1);
+       }
+
+       hd = (HD_VCPIO *)buf;
+       arcn->pad = 0L;
+
+       /*
+        * extract the hex ascii fields from the header
+        */
+       arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), HEX);
+       arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), HEX);
+       arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), HEX);
+       arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), HEX);
+       arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime,sizeof(hd->c_mtime),HEX);
+       arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+#      ifdef NET2_STAT
+       arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,
+           sizeof(hd->c_filesize), HEX);
+#      else
+       arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,
+           sizeof(hd->c_filesize), HEX);
+#      endif
+       arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink),
+           HEX);
+       devmajor = (dev_t)asc_ul(hd->c_maj, sizeof(hd->c_maj), HEX);
+       devminor = (dev_t)asc_ul(hd->c_min, sizeof(hd->c_min), HEX);
+       arcn->sb.st_dev = TODEV(devmajor, devminor);
+       devmajor = (dev_t)asc_ul(hd->c_rmaj, sizeof(hd->c_maj), HEX);
+       devminor = (dev_t)asc_ul(hd->c_rmin, sizeof(hd->c_min), HEX);
+       arcn->sb.st_rdev = TODEV(devmajor, devminor);
+       arcn->crc = asc_ul(hd->c_chksum, sizeof(hd->c_chksum), HEX);
+
+       /*
+        * check the length of the file name, if ok read it in, return -1 if
+        * bogus
+        */
+       if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),HEX)) < 2)
+               return(-1);
+       arcn->nlen = nsz - 1;
+       if (rd_nm(arcn, nsz) < 0)
+               return(-1);
+
+       /*
+        * skip padding. header + filename is aligned to 4 byte boundries
+        */
+       if (rd_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)
+               return(-1);
+
+       /*
+        * if not a link (or a file with no data), calculate pad size (for
+        * padding which follows the file data), clear the link name and return
+        */
+       if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) {
+               /*
+                * we have a valid header (not a link)
+                */
+               arcn->ln_nlen = 0;
+               arcn->ln_name[0] = '\0';
+               arcn->pad = VCPIO_PAD(arcn->sb.st_size);
+               return(com_rd(arcn));
+       }
+
+       /*
+        * read in the link name and skip over the padding
+        */
+       if ((rd_ln_nm(arcn) < 0) ||
+           (rd_skip((off_t)(VCPIO_PAD(arcn->sb.st_size))) < 0))
+               return(-1);
+
+       /*
+        * we have a valid header (with a link)
+        */
+       return(com_rd(arcn));
+}
+
+/*
+ * vcpio_endrd()
+ *      no cleanup needed here, just return size of the trailer (for append)
+ * Return:
+ *      size of trailer header in this format
+ */
+
+#if __STDC__
+off_t
+vcpio_endrd(void)
+#else
+off_t
+vcpio_endrd()
+#endif
+{
+       return((off_t)(sizeof(HD_VCPIO) + sizeof(TRAILER) +
+               (VCPIO_PAD(sizeof(HD_VCPIO) + sizeof(TRAILER)))));
+}
+
+/*
+ * crc_stwr()
+ *     start up the device mapping table, enable crc file calculation
+ * Return:
+ *     0 if ok, -1 otherwise (what dev_start() returns)
+ */
+
+#if __STDC__
+int
+crc_stwr(void)
+#else
+int
+crc_stwr()
+#endif
+{
+       docrc = 1;
+       return(dev_start());
+}
+
+/*
+ * vcpio_wr()
+ *     copy the data in the ARCHD to buffer in system VR4 cpio
+ *     (with/without crc) format.
+ * Return
+ *     0 if file has data to be written after the header, 1 if file has
+ *     NO data to write after the header, -1 if archive write failed
+ */
+
+#if __STDC__
+int
+vcpio_wr(register ARCHD *arcn)
+#else
+int
+vcpio_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HD_VCPIO *hd;
+       unsigned int nsz;
+       char hdblk[sizeof(HD_VCPIO)];
+
+       /*
+        * check and repair truncated device and inode fields in the cpio
+        * header
+        */
+       if (map_dev(arcn, (u_long)VCPIO_MASK, (u_long)VCPIO_MASK) < 0)
+               return(-1);
+       nsz = arcn->nlen + 1;
+       hd = (HD_VCPIO *)hdblk;
+       if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR))
+               arcn->sb.st_rdev = 0;
+
+       /*
+        * add the proper magic value depending whether we were asked for
+        * file data crc's, and the crc if needed.
+        */
+       if (docrc) {
+               if (ul_asc((u_long)VCMAGIC, hd->c_magic, sizeof(hd->c_magic),
+                       OCT) ||
+                   ul_asc((u_long)arcn->crc,hd->c_chksum,sizeof(hd->c_chksum),
+                       HEX))
+                       goto out;
+       } else {
+               if (ul_asc((u_long)VMAGIC, hd->c_magic, sizeof(hd->c_magic),
+                       OCT) ||
+                   ul_asc((u_long)0L, hd->c_chksum, sizeof(hd->c_chksum),HEX))
+                       goto out;
+       }
+
+       switch(arcn->type) {
+       case PAX_CTG:
+       case PAX_REG:
+       case PAX_HRG:
+               /*
+                * caller will copy file data to the archive. tell him how
+                * much to pad.
+                */
+               arcn->pad = VCPIO_PAD(arcn->sb.st_size);
+#              ifdef NET2_STAT
+               if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize,
+                   sizeof(hd->c_filesize), HEX)) {
+#              else
+               if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize,
+                   sizeof(hd->c_filesize), HEX)) {
+#              endif
+                       warn(1,"File is too large for sv4cpio format %s",
+                           arcn->org_name);
+                       return(1);
+               }
+               break;
+       case PAX_SLK:
+               /*
+                * no file data for the caller to process, the file data has
+                * the size of the link
+                */
+               arcn->pad = 0L;
+               if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize,
+                   sizeof(hd->c_filesize), HEX))
+                       goto out;
+               break;
+       default:
+               /*
+                * no file data for the caller to process
+                */
+               arcn->pad = 0L;
+               if (ul_asc((u_long)0L, hd->c_filesize, sizeof(hd->c_filesize),
+                   HEX))
+                       goto out;
+               break;
+       }
+
+       /*
+        * set the other fields in the header
+        */
+       if (ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino),
+               HEX) ||
+           ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode),
+               HEX) ||
+           ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid),
+               HEX) ||
+           ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid),
+               HEX) ||
+           ul_asc((u_long)arcn->sb.st_mtime, hd->c_mtime, sizeof(hd->c_mtime),
+               HEX) ||
+           ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink),
+               HEX) ||
+           ul_asc((u_long)MAJOR(arcn->sb.st_dev),hd->c_maj, sizeof(hd->c_maj),
+               HEX) ||
+           ul_asc((u_long)MINOR(arcn->sb.st_dev),hd->c_min, sizeof(hd->c_min),
+               HEX) ||
+           ul_asc((u_long)MAJOR(arcn->sb.st_rdev),hd->c_rmaj,sizeof(hd->c_maj),
+               HEX) ||
+           ul_asc((u_long)MINOR(arcn->sb.st_rdev),hd->c_rmin,sizeof(hd->c_min),
+               HEX) ||
+           ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX))
+               goto out;
+
+       /*
+        * write the header, the file name and padding as required.
+        */
+       if ((wr_rdbuf(hdblk, (int)sizeof(HD_VCPIO)) < 0) ||
+           (wr_rdbuf(arcn->name, (int)nsz) < 0)  ||
+           (wr_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)) {
+               warn(1,"Could not write sv4cpio header for %s",arcn->org_name);
+               return(-1);
+       }
+
+       /*
+        * if we have file data, tell the caller we are done, copy the file
+        */
+       if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) ||
+           (arcn->type == PAX_HRG))
+               return(0);
+
+       /*
+        * if we are not a link, tell the caller we are done, go to next file
+        */
+       if (arcn->type != PAX_SLK)
+               return(1);
+
+       /*
+        * write the link name, tell the caller we are done.
+        */
+       if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) ||
+           (wr_skip((off_t)(VCPIO_PAD(arcn->ln_nlen))) < 0)) {
+               warn(1,"Could not write sv4cpio link name for %s",
+                   arcn->org_name);
+               return(-1);
+       }
+       return(1);
+
+    out:
+       /*
+        * header field is out of range
+        */
+       warn(1,"Sv4cpio header field is too small for file %s",arcn->org_name);
+       return(1);
+}
+
+/*
+ * Routines common to the old binary header cpio
+ */
+
+/*
+ * bcpio_id()
+ *      determine if a block given to us is a old binary cpio header
+ *     (with/without header byte swapping)
+ * Return:
+ *      0 if a valid header, -1 otherwise
+ */
+
+#if __STDC__
+int
+bcpio_id(char *blk, int size)
+#else
+int
+bcpio_id(blk, size)
+       char *blk;
+       int size;
+#endif
+{
+       if (size < sizeof(HD_BCPIO))
+               return(-1);
+
+       /*
+        * check both normal and byte swapped magic cookies
+        */
+       if (((u_short)SHRT_EXT(blk)) == MAGIC)
+               return(0);
+       if (((u_short)RSHRT_EXT(blk)) == MAGIC) {
+               if (!swp_head)
+                       ++swp_head;
+               return(0);
+       }
+       return(-1);
+}
+
+/*
+ * bcpio_rd()
+ *     determine if a buffer is a old binary archive entry. (it may have byte
+ *     swapped header) convert and store the values in the ARCHD parameter.
+ *     This is a very old header format and should not really be used.
+ * Return:
+ *     0 if a valid header, -1 otherwise.
+ */
+
+#if __STDC__
+int
+bcpio_rd(register ARCHD *arcn, register char *buf)
+#else
+int
+bcpio_rd(arcn, buf)
+       register ARCHD *arcn;
+       register char *buf;
+#endif
+{
+       register HD_BCPIO *hd;
+       register int nsz;
+
+       /*
+        * check the header
+        */
+       if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0)
+               return(-1);
+
+       arcn->pad = 0L;
+       hd = (HD_BCPIO *)buf;
+       if (swp_head) {
+               /*
+                * header has swapped bytes on 16 bit boundries
+                */
+               arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev));
+               arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino));
+               arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode));
+               arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid));
+               arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid));
+               arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink));
+               arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev));
+               arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1));
+               arcn->sb.st_mtime =  (arcn->sb.st_mtime << 16) |
+                       ((time_t)(RSHRT_EXT(hd->h_mtime_2)));
+               arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1));
+               arcn->sb.st_size = (arcn->sb.st_size << 16) |
+                       ((off_t)(RSHRT_EXT(hd->h_filesize_2)));
+               nsz = (int)(RSHRT_EXT(hd->h_namesize));
+       } else {
+               arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev));
+               arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino));
+               arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode));
+               arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid));
+               arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid));
+               arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink));
+               arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev));
+               arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1));
+               arcn->sb.st_mtime =  (arcn->sb.st_mtime << 16) |
+                       ((time_t)(SHRT_EXT(hd->h_mtime_2)));
+               arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1));
+               arcn->sb.st_size = (arcn->sb.st_size << 16) |
+                       ((off_t)(SHRT_EXT(hd->h_filesize_2)));
+               nsz = (int)(SHRT_EXT(hd->h_namesize));
+       }
+       arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime;
+
+       /*
+        * check the file name size, if bogus give up. otherwise read the file
+        * name
+        */
+       if (nsz < 2)
+               return(-1);
+       arcn->nlen = nsz - 1;
+       if (rd_nm(arcn, nsz) < 0)
+               return(-1);
+
+       /*
+        * header + file name are aligned to 2 byte boundries, skip if needed
+        */
+       if (rd_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)
+               return(-1);
+
+       /*
+        * if not a link (or a file with no data), calculate pad size (for
+        * padding which follows the file data), clear the link name and return
+        */
+       if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){
+               /*
+                * we have a valid header (not a link)
+                */
+               arcn->ln_nlen = 0;
+               arcn->ln_name[0] = '\0';
+               arcn->pad = BCPIO_PAD(arcn->sb.st_size);
+               return(com_rd(arcn));
+       }
+
+       if ((rd_ln_nm(arcn) < 0) ||
+           (rd_skip((off_t)(BCPIO_PAD(arcn->sb.st_size))) < 0))
+               return(-1);
+
+       /*
+        * we have a valid header (with a link)
+        */
+       return(com_rd(arcn));
+}
+
+/*
+ * bcpio_endrd()
+ *      no cleanup needed here, just return size of the trailer (for append)
+ * Return:
+ *      size of trailer header in this format
+ */
+
+#if __STDC__
+off_t
+bcpio_endrd(void)
+#else
+off_t
+bcpio_endrd()
+#endif
+{
+       return((off_t)(sizeof(HD_BCPIO) + sizeof(TRAILER) +
+               (BCPIO_PAD(sizeof(HD_BCPIO) + sizeof(TRAILER)))));
+}
+
+/*
+ * bcpio_wr()
+ *     copy the data in the ARCHD to buffer in old binary cpio format
+ *     There is a real chance of field overflow with this critter. So we
+ *     always check the conversion is ok. nobody in his their right mind
+ *     should write an achive in this format...
+ * Return
+ *      0 if file has data to be written after the header, 1 if file has NO
+ *     data to write after the header, -1 if archive write failed
+ */
+
+#if __STDC__
+int
+bcpio_wr(register ARCHD *arcn)
+#else
+int
+bcpio_wr(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register HD_BCPIO *hd;
+       register int nsz;
+       char hdblk[sizeof(HD_BCPIO)];
+       off_t t_offt;
+       int t_int;
+       time_t t_timet;
+
+       /*
+        * check and repair truncated device and inode fields in the cpio
+        * header
+        */
+       if (map_dev(arcn, (u_long)BCPIO_MASK, (u_long)BCPIO_MASK) < 0)
+               return(-1);
+
+       if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR))
+               arcn->sb.st_rdev = 0;
+       hd = (HD_BCPIO *)hdblk;
+
+       switch(arcn->type) {
+       case PAX_CTG:
+       case PAX_REG:
+       case PAX_HRG:
+               /*
+                * caller will copy file data to the archive. tell him how
+                * much to pad.
+                */
+               arcn->pad = BCPIO_PAD(arcn->sb.st_size);
+               hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size);
+               hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size);
+               hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size);
+               hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size);
+               t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1));
+               t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2)));
+               if (arcn->sb.st_size != t_offt) {
+                       warn(1,"File is too large for bcpio format %s",
+                           arcn->org_name);
+                       return(1);
+               }
+               break;
+       case PAX_SLK:
+               /*
+                * no file data for the caller to process, the file data has
+                * the size of the link
+                */
+               arcn->pad = 0L;
+               hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen);
+               hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen);
+               hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen);
+               hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen);
+               t_int = (int)(SHRT_EXT(hd->h_filesize_1));
+               t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2)));
+               if (arcn->ln_nlen != t_int)
+                       goto out;
+               break;
+       default:
+               /*
+                * no file data for the caller to process
+                */
+               arcn->pad = 0L;
+               hd->h_filesize_1[0] = (char)0;
+               hd->h_filesize_1[1] = (char)0;
+               hd->h_filesize_2[0] = (char)0;
+               hd->h_filesize_2[1] = (char)0;
+               break;
+       }
+
+       /*
+        * build up the rest of the fields
+        */
+       hd->h_magic[0] = CHR_WR_2(MAGIC);
+       hd->h_magic[1] = CHR_WR_3(MAGIC);
+       hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev);
+       hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev);
+       if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev)))
+               goto out;
+       hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino);
+       hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino);
+       if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino)))
+               goto out;
+       hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode);
+       hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode);
+       if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode)))
+               goto out;
+       hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid);
+       hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid);
+       if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid)))
+               goto out;
+       hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid);
+       hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid);
+       if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid)))
+               goto out;
+       hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink);
+       hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink);
+       if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink)))
+               goto out;
+       hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev);
+       hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev);
+       if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev)))
+               goto out;
+       hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime);
+       hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime);
+       hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime);
+       hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime);
+       t_timet = (time_t)(SHRT_EXT(hd->h_mtime_1));
+       t_timet =  (t_timet << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2)));
+       if (arcn->sb.st_mtime != t_timet)
+               goto out;
+       nsz = arcn->nlen + 1;
+       hd->h_namesize[0] = CHR_WR_2(nsz);
+       hd->h_namesize[1] = CHR_WR_3(nsz);
+       if (nsz != (int)(SHRT_EXT(hd->h_namesize)))
+               goto out;
+
+       /*
+        * write the header, the file name and padding as required.
+        */
+       if ((wr_rdbuf(hdblk, (int)sizeof(HD_BCPIO)) < 0) ||
+           (wr_rdbuf(arcn->name, nsz) < 0) ||
+           (wr_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)) {
+               warn(1, "Could not write bcpio header for %s", arcn->org_name);
+               return(-1);
+       }
+
+       /*
+        * if we have file data, tell the caller we are done
+        */
+       if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) ||
+           (arcn->type == PAX_HRG))
+               return(0);
+
+       /*
+        * if we are not a link, tell the caller we are done, go to next file
+        */
+       if (arcn->type != PAX_SLK)
+               return(1);
+
+       /*
+        * write the link name, tell the caller we are done.
+        */
+       if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) ||
+           (wr_skip((off_t)(BCPIO_PAD(arcn->ln_nlen))) < 0)) {
+               warn(1,"Could not write bcpio link name for %s",arcn->org_name);
+               return(-1);
+       }
+       return(1);
+
+    out:
+       /*
+        * header field is out of range
+        */
+       warn(1,"Bcpio header field is too small for file %s", arcn->org_name);
+       return(1);
+}
diff --git a/usr/src/bin/pax/cpio.h b/usr/src/bin/pax/cpio.h
new file mode 100644 (file)
index 0000000..4fcedb1
--- /dev/null
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)cpio.h      8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Defines common to all versions of cpio
+ */
+#define TRAILER                "TRAILER!!!"    /* name in last archive record */
+
+/*
+ * Header encoding of the different file types
+ */
+#define        C_ISDIR          040000         /* Directory */
+#define        C_ISFIFO         010000         /* FIFO */
+#define        C_ISREG         0100000         /* Regular file */
+#define        C_ISBLK          060000         /* Block special file */
+#define        C_ISCHR          020000         /* Character special file */
+#define        C_ISCTG         0110000         /* Reserved for contiguous files */
+#define        C_ISLNK         0120000         /* Reserved for symbolic links */
+#define        C_ISOCK         0140000         /* Reserved for sockets */
+#define C_IFMT         0170000         /* type of file */
+
+/*
+ * Data Interchange Format - Extended cpio header format - POSIX 1003.1-1990
+ */
+typedef struct {
+       char    c_magic[6];             /* magic cookie */
+       char    c_dev[6];               /* device number */
+       char    c_ino[6];               /* inode number */
+       char    c_mode[6];              /* file type/access */
+       char    c_uid[6];               /* owners uid */
+       char    c_gid[6];               /* owners gid */
+       char    c_nlink[6];             /* # of links at archive creation */
+       char    c_rdev[6];              /* block/char major/minor # */
+       char    c_mtime[11];            /* modification time */
+       char    c_namesize[6];          /* length of pathname */
+       char    c_filesize[11];         /* length of file in bytes */
+} HD_CPIO; 
+
+#define        MAGIC           070707          /* transportable archive id */
+
+#ifdef _PAX_
+#define        AMAGIC          "070707"        /* ascii equivalent string of MAGIC */
+#define CPIO_MASK      0x3ffff         /* bits valid in the dev/ino fields */
+                                       /* used for dev/inode remaps */
+#endif /* _PAX_ */
+
+/*
+ * Binary cpio header structure 
+ *
+ * CAUTION! CAUTION! CAUTION!
+ * Each field really represents a 16 bit short (NOT ASCII). Described as
+ * an array of chars in an attempt to improve portability!!
+ */
+typedef struct {
+       u_char  h_magic[2];
+       u_char  h_dev[2];
+       u_char  h_ino[2];
+       u_char  h_mode[2];
+       u_char  h_uid[2];
+       u_char  h_gid[2];
+       u_char  h_nlink[2];
+       u_char  h_rdev[2];
+       u_char  h_mtime_1[2];
+       u_char  h_mtime_2[2];
+       u_char  h_namesize[2];
+       u_char  h_filesize_1[2];
+       u_char  h_filesize_2[2];
+} HD_BCPIO; 
+
+#ifdef _PAX_
+/*
+ * extraction and creation macros for binary cpio
+ */
+#define SHRT_EXT(ch)   ((((unsigned)(ch)[0])<<8) | (((unsigned)(ch)[1])&0xff))
+#define RSHRT_EXT(ch)  ((((unsigned)(ch)[1])<<8) | (((unsigned)(ch)[0])&0xff))
+#define CHR_WR_0(val)  ((char)(((val) >> 24) & 0xff))
+#define CHR_WR_1(val)  ((char)(((val) >> 16) & 0xff))
+#define CHR_WR_2(val)  ((char)(((val) >> 8) & 0xff))
+#define CHR_WR_3(val)  ((char)((val) & 0xff))
+
+/*
+ * binary cpio masks and pads
+ */
+#define BCPIO_PAD(x)   ((2 - ((x) & 1)) & 1)   /* pad to next 2 byte word */
+#define BCPIO_MASK     0xffff                  /* mask for dev/ino fields */
+#endif /* _PAX_ */
+
+/*
+ * System VR4 cpio header structure (with/without file data crc)
+ */
+typedef struct {
+       char    c_magic[6];             /* magic cookie */
+       char    c_ino[8];               /* inode number */
+       char    c_mode[8];              /* file type/access */
+       char    c_uid[8];               /* owners uid */
+       char    c_gid[8];               /* owners gid */
+       char    c_nlink[8];             /* # of links at archive creation */
+       char    c_mtime[8];             /* modification time */
+       char    c_filesize[8];          /* length of file in bytes */
+       char    c_maj[8];               /* block/char major # */
+       char    c_min[8];               /* block/char minor # */
+       char    c_rmaj[8];              /* special file major # */
+       char    c_rmin[8];              /* special file minor # */
+       char    c_namesize[8];          /* length of pathname */
+       char    c_chksum[8];            /* 0 OR CRC of bytes of FILE data */
+} HD_VCPIO; 
+
+#define        VMAGIC          070701          /* sVr4 new portable archive id */
+#define        VCMAGIC         070702          /* sVr4 new portable archive id CRC */
+#ifdef _PAX_
+#define        AVMAGIC         "070701"        /* ascii string of above */
+#define        AVCMAGIC        "070702"        /* ascii string of above */
+#define VCPIO_PAD(x)   ((4 - ((x) & 3)) & 3)   /* pad to next 4 byte word */
+#define VCPIO_MASK     0xffffffff      /* mask for dev/ino fields */
+#endif /* _PAX_ */
diff --git a/usr/src/bin/pax/extern.h b/usr/src/bin/pax/extern.h
new file mode 100644 (file)
index 0000000..f151370
--- /dev/null
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)extern.h    8.2 (Berkeley) 4/18/94
+ */
+
+/*
+ * External references from each source file
+ */
+
+#include <sys/cdefs.h>
+
+/*
+ * ar_io.c
+ */
+extern char *arcname;
+int ar_open __P((char *));
+void ar_close __P((void));
+void ar_drain __P((void));
+int ar_set_wr __P((void));
+int ar_app_ok __P((void));
+int ar_read __P((register char *, register int));
+int ar_write __P((register char *, register int));
+int ar_rdsync __P((void));
+int ar_fow __P((off_t, off_t *));
+int ar_rev __P((off_t ));
+int ar_next __P((void));
+
+/*
+ * ar_subs.c
+ */
+extern u_long flcnt;
+void list __P((void));
+void extract __P((void));
+void append __P((void));
+void archive __P((void));
+void copy __P((void));
+
+/*
+ * buf_subs.c
+ */
+extern int blksz;
+extern int wrblksz;
+extern int maxflt;
+extern int rdblksz;
+extern off_t wrlimit;
+extern off_t rdcnt;
+extern off_t wrcnt;
+int wr_start __P((void));
+int rd_start __P((void));
+void cp_start __P((void));
+int appnd_start __P((off_t));
+int rd_sync __P((void));
+void pback __P((char *, int));
+int rd_skip __P((off_t));
+void wr_fin __P((void));
+int wr_rdbuf __P((register char *, register int));
+int rd_wrbuf __P((register char *, register int));
+int wr_skip __P((off_t));
+int wr_rdfile __P((ARCHD *, int, off_t *));
+int rd_wrfile __P((ARCHD *, int, off_t *));
+void cp_file __P((ARCHD *, int, int));
+int buf_fill __P((void));
+int buf_flush __P((register int));
+
+/*
+ * cache.c
+ */
+int uidtb_start __P((void));
+int gidtb_start __P((void));
+int usrtb_start __P((void));
+int grptb_start __P((void));
+char * name_uid __P((uid_t, int));
+char * name_gid __P((gid_t, int));
+int uid_name __P((char *, uid_t *));
+int gid_name __P((char *, gid_t *));
+
+/*
+ * cpio.c
+ */
+int cpio_strd __P((void));
+int cpio_trail __P((register ARCHD *));
+int cpio_endwr __P((void));
+int cpio_id __P((char *, int));
+int cpio_rd __P((register ARCHD *, register char *));
+off_t cpio_endrd __P((void));
+int cpio_stwr __P((void));
+int cpio_wr __P((register ARCHD *));
+int vcpio_id __P((char *, int));
+int crc_id __P((char *, int));
+int crc_strd __P((void));
+int vcpio_rd __P((register ARCHD *, register char *));
+off_t vcpio_endrd __P((void));
+int crc_stwr __P((void));
+int vcpio_wr __P((register ARCHD *));
+int bcpio_id __P((char *, int));
+int bcpio_rd __P((register ARCHD *, register char *));
+off_t bcpio_endrd __P((void));
+int bcpio_wr __P((register ARCHD *));
+
+/*
+ * file_subs.c
+ */
+int file_creat __P((register ARCHD *));
+void file_close __P((register ARCHD *, int));
+int lnk_creat __P((register ARCHD *));
+int cross_lnk __P((register ARCHD *));
+int chk_same __P((register ARCHD *));
+int node_creat __P((register ARCHD *));
+int unlnk_exist __P((register char *, register int));
+int chk_path __P((register char *, uid_t, gid_t));
+void set_ftime __P((char *fnm, time_t mtime, time_t atime, int frc));
+int set_ids __P((char *, uid_t, gid_t));
+void set_pmode __P((char *, mode_t));
+int file_write __P((int, char *, register int, int *, int *, int, char *));
+void file_flush __P((int, char *, int));
+void rdfile_close __P((register ARCHD *, register int *));
+int set_crc __P((register ARCHD *, register int));
+
+/*
+ * ftree.c
+ */
+int ftree_start __P((void));
+int ftree_add __P((register char *));
+void ftree_sel __P((register ARCHD *));
+void ftree_chk __P((void));
+int next_file __P((register ARCHD *));
+
+/*
+ * gen_subs.c
+ */
+void ls_list __P((register ARCHD *, time_t));
+void ls_tty __P((register ARCHD *));
+void zf_strncpy __P((register char *, register char *, int));
+int l_strncpy __P((register char *, register char *, int));
+u_long asc_ul __P((register char *, int, register int));
+int ul_asc __P((u_long, register char *, register int, register int));
+#ifndef NET2_STAT
+u_quad_t asc_uqd __P((register char *, int, register int));
+int uqd_asc __P((u_quad_t, register char *, register int, register int));
+#endif
+
+/*
+ * options.c
+ */
+extern FSUB fsub[];
+extern int ford[];
+void options __P((register int, register char **));
+OPLIST * opt_next __P((void));
+int opt_add __P((register char *));
+int bad_opt __P((void));
+
+/*
+ * pat_rep.c
+ */
+int rep_add __P((register char *));
+int pat_add __P((char *));
+void pat_chk __P((void));
+int pat_sel __P((register ARCHD *));
+int pat_match __P((register ARCHD *));
+int mod_name __P((register ARCHD *));
+int set_dest __P((register ARCHD *, char *, int));
+
+/*
+ * pax.c
+ */
+extern int act;
+extern FSUB *frmt;
+extern int cflag;
+extern int dflag;
+extern int iflag;
+extern int kflag;
+extern int lflag;
+extern int nflag;
+extern int tflag;
+extern int uflag;
+extern int vflag;
+extern int Dflag;
+extern int Hflag;
+extern int Lflag;
+extern int Xflag;
+extern int Yflag;
+extern int Zflag;
+extern int vfpart;
+extern int patime;
+extern int pmtime;
+extern int pmode;
+extern int pids;
+extern int exit_val;
+extern int docrc;
+extern char *dirptr;
+extern char *ltmfrmt;
+extern char *argv0;
+int main __P((int, char **));
+void sig_cleanup __P((int));
+
+/*
+ * sel_subs.c
+ */
+int sel_chk __P((register ARCHD *));
+int grp_add __P((register char *));
+int usr_add __P((register char *));
+int trng_add __P((register char *));
+
+/*
+ * tables.c
+ */
+int lnk_start __P((void));
+int chk_lnk __P((register ARCHD *));
+void purg_lnk __P((register ARCHD *));
+void lnk_end __P((void));
+int ftime_start __P((void));
+int chk_ftime __P((register ARCHD *));
+int name_start __P((void));
+int add_name __P((register char *, int, char *));
+void sub_name __P((register char *, int *));
+int dev_start __P((void));
+int add_dev __P((register ARCHD *));
+int map_dev __P((register ARCHD *, u_long, u_long));
+int atdir_start __P((void));
+void atdir_end __P((void));
+void add_atdir __P((char *, dev_t, ino_t, time_t, time_t));
+int get_atdir __P((dev_t, ino_t, time_t *, time_t *));
+int dir_start __P((void));
+void add_dir __P((char *, int, struct stat *, int));
+void proc_dir __P((void));
+u_int st_hash __P((char *, int, int));
+
+/*
+ * tar.c
+ */
+int tar_endwr __P((void));
+off_t tar_endrd __P((void));
+int tar_trail __P((register char *, register int, register int *));
+int tar_id __P((register char *, int));
+int tar_opt __P((void));
+int tar_rd __P((register ARCHD *, register char *));
+int tar_wr __P((register ARCHD *));
+int ustar_strd __P((void));
+int ustar_stwr __P((void));
+int ustar_id __P((char *, int));
+int ustar_rd __P((register ARCHD *, register char *));
+int ustar_wr __P((register ARCHD *));
+
+/*
+ * tty_subs.c
+ */
+int tty_init __P((void));
+void tty_prnt __P((char *, ...));
+int tty_read __P((char *, int));
+void warn __P((int, char *, ...));
+void syswarn __P((int, int, char *, ...));
diff --git a/usr/src/bin/pax/file_subs.c b/usr/src/bin/pax/file_subs.c
new file mode 100644 (file)
index 0000000..0781001
--- /dev/null
@@ -0,0 +1,1051 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)file_subs.c        8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/uio.h>
+#include <stdlib.h>
+#include "pax.h"
+#include "extern.h"
+
+static int
+mk_link __P((register char *,register struct stat *,register char *, int));
+
+/*
+ * routines that deal with file operations such as: creating, removing;
+ * and setting access modes, uid/gid and times of files
+ */
+
+#define FILEBITS               (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+#define SETBITS                        (S_ISUID | S_ISGID)
+#define ABITS                  (FILEBITS | SETBITS)
+
+/*
+ * file_creat()
+ *     Create and open a file.
+ * Return:
+ *     file descriptor or -1 for failure
+ */
+
+#if __STDC__
+int
+file_creat(register ARCHD *arcn)
+#else
+int
+file_creat(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       int fd = -1;
+       mode_t file_mode;
+       int oerrno;
+
+       /*
+        * assume file doesn't exist, so just try to create it, most times this
+        * works. We have to take special handling when the file does exist. To
+        * detect this, we use O_EXCL. For example when trying to create a
+        * file and a character device or fifo exists with the same name, we
+        * can accidently open the device by mistake (or block waiting to open)
+        * If we find that the open has failed, then figure spend the effore to
+        * figure out why. This strategy was found to have better average
+        * performance in common use than checking the file (and the path)
+        * first with lstat.
+        */
+       file_mode = arcn->sb.st_mode & FILEBITS;
+       if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL,
+           file_mode)) >= 0)
+               return(fd);
+
+       /*
+        * the file seems to exist. First we try to get rid of it (found to be
+        * the second most common failure when traced). If this fails, only
+        * then we go to the expense to check and create the path to the file
+        */
+       if (unlnk_exist(arcn->name, arcn->type) != 0)
+               return(-1);
+
+       for (;;) {
+               /*
+                * try to open it again, if this fails, check all the nodes in
+                * the path and give it a final try. if chk_path() finds that
+                * it cannot fix anything, we will skip the last attempt
+                */
+               if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC,
+                   file_mode)) >= 0)
+                       break;
+               oerrno = errno;
+               if (chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) {
+                       syswarn(1, oerrno, "Unable to create %s", arcn->name);
+                       return(-1);
+               }
+       }
+       return(fd);
+}
+
+/*
+ * file_close()
+ *     Close file descriptor to a file just created by pax. Sets modes,
+ *     ownership and times as required.
+ * Return:
+ *     0 for success, -1 for failure
+ */
+
+#if __STDC__
+void
+file_close(register ARCHD *arcn, int fd)
+#else
+void
+file_close(arcn, fd)
+       register ARCHD *arcn;
+       int fd;
+#endif
+{
+       int res = 0;
+
+       if (fd < 0)
+               return;
+       if (close(fd) < 0)
+               syswarn(0, errno, "Unable to close file descriptor on %s",
+                   arcn->name);
+
+       /*
+        * set owner/groups first as this may strip off mode bits we want
+        * then set file permission modes. Then set file access and
+        * modification times. 
+        */
+       if (pids)
+               res = set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid);
+
+       /*
+        * IMPORTANT SECURITY NOTE:
+        * if not preserving mode or we cannot set uid/gid, then PROHIBIT
+        * set uid/gid bits
+        */
+       if (!pmode || res)
+               arcn->sb.st_mode &= ~(SETBITS);
+       if (pmode)
+               set_pmode(arcn->name, arcn->sb.st_mode);
+       if (patime || pmtime)
+               set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0);
+}
+
+/*
+ * lnk_creat()
+ *     Create a hard link to arcn->ln_name from arcn->name. arcn->ln_name
+ *     must exist; 
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+int
+lnk_creat(register ARCHD *arcn)
+#else
+int
+lnk_creat(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       struct stat sb;
+
+       /*
+        * we may be running as root, so we have to be sure that link target
+        * is not a directory, so we lstat and check
+        */
+       if (lstat(arcn->ln_name, &sb) < 0) {
+               syswarn(1,errno,"Unable to link to %s from %s", arcn->ln_name,
+                   arcn->name);
+               return(-1);
+       }
+
+       if (S_ISDIR(sb.st_mode)) {
+               warn(1, "A hard link to the directory %s is not allowed",
+                   arcn->ln_name);
+               return(-1);
+       }
+
+       return(mk_link(arcn->ln_name, &sb, arcn->name, 0));
+}
+
+/*
+ * cross_lnk()
+ *     Create a hard link to arcn->org_name from arcn->name. Only used in copy
+ *     with the -l flag. No warning or error if this does not succeed (we will
+ *     then just create the file)
+ * Return:
+ *     1 if copy() should try to create this file node
+ *     0 if cross_lnk() ok, -1 for fatal flaw (like linking to self).
+ */
+
+#if __STDC__
+int
+cross_lnk(register ARCHD *arcn)
+#else
+int
+cross_lnk(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * try to make a link to orginal file (-l flag in copy mode). make sure
+        * we do not try to link to directories in case we are running as root
+        * (and it might succeed).
+        */
+       if (arcn->type == PAX_DIR)
+               return(1);
+       return(mk_link(arcn->org_name, &(arcn->sb), arcn->name, 1));
+}
+
+/*
+ * chk_same()
+ *     In copy mode if we are not trying to make hard links between the src
+ *     and destinations, make sure we are not going to overwrite ourselves by
+ *     accident. This slows things down a little, but we have to protect all
+ *     those people who make typing errors.
+ * Return:
+ *     1 the target does not exist, go ahead and copy
+ *     0 skip it file exists (-k) or may be the same as source file
+ */
+
+#if __STDC__
+int
+chk_same(register ARCHD *arcn)
+#else
+int
+chk_same(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       struct stat sb;
+
+       /* 
+        * if file does not exist, return. if file exists and -k, skip it
+        * quietly
+        */
+       if (lstat(arcn->name, &sb) < 0)
+               return(1);
+       if (kflag)
+               return(0);
+
+       /*
+        * better make sure the user does not have src == dest by mistake
+        */
+       if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) {
+               warn(1, "Unable to copy %s, file would overwrite itself",
+                   arcn->name);
+               return(0);
+       }
+       return(1);
+}
+
+/*
+ * mk_link()
+ *     try to make a hard link between two files. if ign set, we do not
+ *     complain.
+ * Return:
+ *     0 if successful (or we are done with this file but no error, such as
+ *     finding the from file exists and the user has set -k).
+ *     1 when ign was set to indicates we could not make the link but we
+ *     should try to copy/extract the file as that might work (and is an
+ *     allowed option). -1 an error occurred.
+ */
+
+#if __STDC__
+static int
+mk_link(register char *to, register struct stat *to_sb, register char *from,
+       int ign)
+#else
+static int
+mk_link(to, to_sb, from, ign)
+       register char *to;
+       register struct stat *to_sb;
+       register char *from;
+       int ign;
+#endif
+{
+       struct stat sb;
+       int oerrno;
+
+       /*
+        * if from file exists, it has to be unlinked to make the link. If the
+        * file exists and -k is set, skip it quietly
+        */
+       if (lstat(from, &sb) == 0) {
+               if (kflag)
+                       return(0);
+
+               /*
+                * make sure it is not the same file, protect the user
+                */
+               if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) {
+                       warn(1, "Unable to link file %s to itself", to);
+                       return(-1);;
+               }
+
+               /*
+                * try to get rid of the file, based on the type
+                */
+               if (S_ISDIR(sb.st_mode)) {
+                       if (rmdir(from) < 0) {
+                               syswarn(1, errno, "Unable to remove %s", from);
+                               return(-1);
+                       }
+               } else if (unlink(from) < 0) {
+                       if (!ign) {
+                               syswarn(1, errno, "Unable to remove %s", from);
+                               return(-1);
+                       }
+                       return(1);
+               }
+       }
+
+       /*
+        * from file is gone (or did not exist), try to make the hard link.
+        * if it fails, check the path and try it again (if chk_path() says to
+        * try again)
+        */
+       for (;;) {
+               if (link(to, from) == 0)
+                       break;
+               oerrno = errno;
+               if (chk_path(from, to_sb->st_uid, to_sb->st_gid) == 0)
+                       continue;
+               if (!ign) {
+                       syswarn(1, oerrno, "Could not link to %s from %s", to,
+                           from);
+                       return(-1);
+               }
+               return(1);
+       }
+
+       /*
+        * all right the link was made
+        */
+       return(0);
+}
+
+/*
+ * node_creat()
+ *     create an entry in the file system (other than a file or hard link).
+ *     If successful, sets uid/gid modes and times as required.
+ * Return:
+ *     0 if ok, -1 otherwise
+ */
+
+#if __STDC__
+int
+node_creat(register ARCHD *arcn)
+#else
+int
+node_creat(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register int res;
+       register int ign = 0;
+       register int oerrno;
+       register int pass = 0;
+       mode_t file_mode;
+       struct stat sb;
+
+       /*
+        * create node based on type, if that fails try to unlink the node and
+        * try again. finally check the path and try again. As noted in the
+        * file and link creation routines, this method seems to exhibit the
+        * best performance in general use workloads.
+        */
+       file_mode = arcn->sb.st_mode & FILEBITS;
+
+       for (;;) {
+               switch(arcn->type) {
+               case PAX_DIR:
+                       res = mkdir(arcn->name, file_mode);
+                       if (ign)
+                               res = 0;
+                       break;
+               case PAX_CHR:
+                       file_mode |= S_IFCHR;
+                       res = mknod(arcn->name, file_mode, arcn->sb.st_rdev);
+                       break;
+               case PAX_BLK:
+                       file_mode |= S_IFBLK;
+                       res = mknod(arcn->name, file_mode, arcn->sb.st_rdev);
+                       break;
+               case PAX_FIF:
+                       res = mkfifo(arcn->name, file_mode);
+                       break;
+               case PAX_SCK:
+                       /*
+                        * Skip sockets, operation has no meaning under BSD
+                        */
+                       warn(0,
+                           "%s skipped. Sockets cannot be copied or extracted",
+                           arcn->name);
+                       return(-1);
+               case PAX_SLK:
+                       if ((res = symlink(arcn->ln_name, arcn->name)) == 0)
+                               return(0);
+                       break;
+               case PAX_CTG:
+               case PAX_HLK:
+               case PAX_HRG:
+               case PAX_REG:
+               default:
+                       /*
+                        * we should never get here
+                        */
+                       warn(0, "%s has an unknown file type, skipping",
+                               arcn->name);
+                       return(-1);
+               }
+
+               /*
+                * if we were able to create the node break out of the loop,
+                * otherwise try to unlink the node and try again. if that
+                * fails check the full path and try a final time.
+                */
+               if (res == 0)
+                       break;
+
+               /*
+                * we failed to make the node
+                */
+               oerrno = errno;
+               if ((ign = unlnk_exist(arcn->name, arcn->type)) < 0)
+                       return(-1);
+
+               if (++pass <= 1)
+                       continue;
+
+               if (chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) {
+                       syswarn(1, oerrno, "Could not create: %s", arcn->name);
+                       return(-1);
+               }
+       }
+
+       /*
+        * we were able to create the node. set uid/gid, modes and times
+        */
+       if (pids)
+               res = set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid);
+       else
+               res = 0;
+
+       /*
+        * IMPORTANT SECURITY NOTE:
+        * if not preserving mode or we cannot set uid/gid, then PROHIBIT any
+        * set uid/gid bits
+        */
+       if (!pmode || res)
+               arcn->sb.st_mode &= ~(SETBITS);
+       if (pmode)
+               set_pmode(arcn->name, arcn->sb.st_mode);
+
+       if (arcn->type == PAX_DIR) {
+               /*
+                * Dirs must be processed again at end of extract to set times
+                * and modes to agree with those stored in the archive. However
+                * to allow extract to continue, we may have to also set owner
+                * rights. This allows nodes in the archive that are children
+                * of this directory to be extracted without failure. Both time
+                * and modes will be fixed after the entire archive is read and
+                * before pax exits.
+                */
+               if (access(arcn->name, R_OK | W_OK | X_OK) < 0) {
+                       if (lstat(arcn->name, &sb) < 0) {
+                               syswarn(0, errno,"Could not access %s (stat)",
+                                   arcn->name);
+                               set_pmode(arcn->name,file_mode | S_IRWXU);
+                       } else {
+                               /*
+                                * We have to add rights to the dir, so we make
+                                * sure to restore the mode. The mode must be
+                                * restored AS CREATED and not as stored if
+                                * pmode is not set.
+                                */
+                               set_pmode(arcn->name,
+                                   ((sb.st_mode & FILEBITS) | S_IRWXU));
+                               if (!pmode)
+                                       arcn->sb.st_mode = sb.st_mode;
+                       }
+
+                       /*
+                        * we have to force the mode to what was set here,
+                        * since we changed it from the default as created.
+                        */
+                       add_dir(arcn->name, arcn->nlen, &(arcn->sb), 1);
+               } else if (pmode || patime || pmtime)
+                       add_dir(arcn->name, arcn->nlen, &(arcn->sb), 0);
+       }
+
+       if (patime || pmtime)
+               set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0);
+       return(0);
+}
+
+/*
+ * unlnk_exist()
+ *     Remove node from file system with the specified name. We pass the type
+ *     of the node that is going to replace it. When we try to create a
+ *     directory and find that it already exists, we allow processing to
+ *     continue as proper modes etc will always be set for it later on.
+ * Return:
+ *     0 is ok to proceed, no file with the specified name exists
+ *     -1 we were unable to remove the node, or we should not remove it (-k)
+ *     1 we found a directory and we were going to create a directory.
+ */
+
+#if __STDC__
+int
+unlnk_exist(register char *name, register int type)
+#else
+int
+unlnk_exist(name, type)
+       register char *name;
+       register int type;
+#endif
+{
+       struct stat sb;
+
+       /*
+        * the file does not exist, or -k we are done
+        */
+       if (lstat(name, &sb) < 0)
+               return(0);
+       if (kflag)
+               return(-1);
+
+       if (S_ISDIR(sb.st_mode)) {
+               /*
+                * try to remove a directory, if it fails and we were going to
+                * create a directory anyway, tell the caller (return a 1)
+                */
+               if (rmdir(name) < 0) {
+                       if (type == PAX_DIR)
+                               return(1); 
+                       syswarn(1,errno,"Unable to remove directory %s", name);
+                       return(-1);
+               }
+               return(0);
+       }
+
+       /*
+        * try to get rid of all non-directory type nodes
+        */
+       if (unlink(name) < 0) {
+               syswarn(1, errno, "Could not unlink %s", name);
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * chk_path()
+ *     We were trying to create some kind of node in the file system and it
+ *     failed. chk_path() makes sure the path up to the node exists and is
+ *     writeable. When we have to create a directory that is missing along the
+ *     path somewhere, the directory we create will be set to the same
+ *     uid/gid as the file has (when uid and gid are being preserved).
+ *     NOTE: this routine is a real performance loss. It is only used as a
+ *     last resort when trying to create entries in the file system.
+ * Return:
+ *     -1 when it could find nothing it is allowed to fix.
+ *     0 otherwise
+ */
+
+#if __STDC__
+int
+chk_path( register char *name, uid_t st_uid, gid_t st_gid)
+#else
+int
+chk_path(name, st_uid, st_gid)
+       register char *name;
+       uid_t st_uid;
+       gid_t st_gid;
+#endif
+{
+       register char *spt = name;
+       struct stat sb;
+       int retval = -1;
+
+       /*
+        * watch out for paths with nodes stored directly in / (e.g. /bozo)
+        */
+       if (*spt == '/')
+               ++spt;
+
+       for(;;) {
+               /*
+                * work foward from the first / and check each part of the path
+                */
+               spt = strchr(spt, '/');
+               if (spt == NULL)
+                       break;
+               *spt = '\0';
+
+               /*
+                * if it exists we assume it is a directory, it is not within
+                * the spec (at least it seems to read that way) to alter the
+                * file system for nodes NOT EXPLICITLY stored on the archive.
+                * If that assumption is changed, you would test the node here
+                * and figure out how to get rid of it (probably like some
+                * recursive unlink()) or fix up the directory permissions if
+                * required (do an access()).
+                */
+               if (lstat(name, &sb) == 0) {
+                       *(spt++) = '/';
+                       continue;
+               }
+
+               /*
+                * the path fails at this point, see if we can create the
+                * needed directory and continue on
+                */
+               if (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+                       *spt = '/';
+                       retval = -1;
+                       break;
+               }
+
+               /*
+                * we were able to create the directory. We will tell the
+                * caller that we found something to fix, and it is ok to try
+                * and create the node again.
+                */
+               retval = 0;
+               if (pids)
+                       (void)set_ids(name, st_uid, st_gid);
+
+               /*
+                * make sure the user doen't have some strange umask that
+                * causes this newly created directory to be unusable. We fix
+                * the modes and restore them back to the creation default at
+                * the end of pax
+                */
+               if ((access(name, R_OK | W_OK | X_OK) < 0) &&
+                   (lstat(name, &sb) == 0)) {
+                       set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU));
+                       add_dir(name, spt - name, &sb, 1);
+               }
+               *(spt++) = '/';
+               continue;
+       }
+       return(retval);
+}
+
+/*
+ * set_ftime()
+ *     Set the access time and modification time for a named file. If frc is
+ *     non-zero we force these times to be set even if the the user did not
+ *     request access and/or modification time preservation (this is also
+ *     used by -t to reset access times).
+ *     When ign is zero, only those times the user has asked for are set, the
+ *     other ones are left alone. We do not assume the un-documented feature
+ *     of many utimes() implementations that consider a 0 time value as a do
+ *     not set request.
+ */
+
+#if __STDC__
+void
+set_ftime(char *fnm, time_t mtime, time_t atime, int frc)
+#else
+void
+set_ftime(fnm, mtime, atime, frc)
+       char *fnm;
+       time_t mtime;
+       time_t atime;
+       int frc;
+#endif
+{
+       static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}};
+       struct stat sb;
+
+       tv[0].tv_sec = (long)atime;
+       tv[1].tv_sec = (long)mtime;
+       if (!frc && (!patime || !pmtime)) {
+               /*
+                * if we are not forcing, only set those times the user wants
+                * set. We get the current values of the times if we need them.
+                */
+               if (lstat(fnm, &sb) == 0) {
+                       if (!patime)
+                               tv[0].tv_sec = (long)sb.st_atime;
+                       if (!pmtime)
+                               tv[1].tv_sec = (long)sb.st_mtime;
+               } else
+                       syswarn(0,errno,"Unable to obtain file stats %s", fnm);
+       }
+
+       /*
+        * set the times
+        */
+       if (utimes(fnm, tv) < 0)
+               syswarn(1, errno, "Access/modification time set failed on: %s",
+                   fnm);
+       return;
+}
+
+/*
+ * set_ids()
+ *     set the uid and gid of a file system node
+ * Return:
+ *     0 when set, -1 on failure
+ */
+
+#if __STDC__
+int
+set_ids(char *fnm, uid_t uid, gid_t gid)
+#else
+int
+set_ids(fnm, uid, gid)
+       char *fnm;
+       uid_t uid;
+       gid_t gid;
+#endif
+{
+       if (chown(fnm, uid, gid) < 0) {
+               syswarn(1, errno, "Unable to set file uid/gid of %s", fnm);
+               return(-1);
+       }
+       return(0);
+}
+
+/*
+ * set_pmode()
+ *     Set file access mode
+ */
+
+#if __STDC__
+void
+set_pmode(char *fnm, mode_t mode)
+#else
+void
+set_pmode(fnm, mode)
+       char *fnm;
+       mode_t mode;
+#endif
+{
+       mode &= ABITS;
+       if (chmod(fnm, mode) < 0)
+               syswarn(1, errno, "Could not set permissions on %s", fnm);
+       return;
+}
+
+/*
+ * file_write()
+ *     Write/copy a file (during copy or archive extract). This routine knows
+ *     how to copy files with lseek holes in it. (Which are read as file
+ *     blocks containing all 0's but do not have any file blocks associated
+ *     with the data). Typical examples of these are files created by dbm
+ *     variants (.pag files). While the file size of these files are huge, the
+ *     actual storage is quite small (the files are sparse). The problem is
+ *     the holes read as all zeros so are probably stored on the archive that
+ *     way (there is no way to determine if the file block is really a hole,
+ *     we only know that a file block of all zero's can be a hole).
+ *     At this writing, no major archive format knows how to archive files
+ *     with holes. However, on extraction (or during copy, -rw) we have to
+ *     deal with these files. Without detecting the holes, the files can
+ *     consume a lot of file space if just written to disk. This replacement
+ *     for write when passed the basic allocation size of a file system block,
+ *     uses lseek whenever it detects the input data is all 0 within that
+ *     file block. In more detail, the strategy is as follows:
+ *     While the input is all zero keep doing an lseek. Keep track of when we
+ *     pass over file block boundries. Only write when we hit a non zero
+ *     input. once we have written a file block, we continue to write it to
+ *     the end (we stop looking at the input). When we reach the start of the
+ *     next file block, start checking for zero blocks again. Working on file
+ *     block boundries significantly reduces the overhead when copying files
+ *     that are NOT very sparse. This overhead (when compared to a write) is
+ *     almost below the measurement resolution on many systems. Without it,
+ *     files with holes cannot be safely copied. It does has a side effect as
+ *     it can put holes into files that did not have them before, but that is
+ *     not a problem since the file contents are unchanged (in fact it saves
+ *     file space). (Except on paging files for diskless clients. But since we
+ *     cannot determine one of those file from here, we ignore them). If this
+ *     ever ends up on a system where CTG files are supported and the holes
+ *     are not desired, just do a conditional test in those routines that
+ *     call file_write() and have it call write() instead. BEFORE CLOSING THE
+ *     FILE, make sure to call file_flush() when the last write finishes with
+ *     an empty block. A lot of file systems will not create an lseek hole at
+ *     the end. In this case we drop a single 0 at the end to force the
+ *     trailing 0's in the file.
+ *     ---Parameters---
+ *     rem: how many bytes left in this file system block
+ *     isempt: have we written to the file block yet (is it empty)
+ *     sz: basic file block allocation size
+ *     cnt: number of bytes on this write
+ *     str: buffer to write
+ * Return:
+ *     number of bytes written, -1 on write (or lseek) error.
+ */
+
+#if __STDC__
+int
+file_write(int fd, char *str, register int cnt, int *rem, int *isempt, int sz,
+       char *name)
+#else
+int
+file_write(fd, str, cnt, rem, isempt, sz, name)
+       int fd;
+       char *str;
+       register int cnt;
+       int *rem;
+       int *isempt;
+       int sz;
+       char *name;
+#endif
+{
+       register char *pt;
+       register char *end;
+       register int wcnt;
+       register char *st = str;
+       
+       /*
+        * while we have data to process
+        */
+       while (cnt) {
+               if (!*rem) {
+                       /*
+                        * We are now at the start of file system block again
+                        * (or what we think one is...). start looking for
+                        * empty blocks again
+                        */
+                       *isempt = 1;
+                       *rem = sz;
+               }
+
+               /*
+                * only examine up to the end of the current file block or
+                * remaining characters to write, whatever is smaller
+                */
+               wcnt = MIN(cnt, *rem);
+               cnt -= wcnt;
+               *rem -= wcnt;
+               if (*isempt) {
+                       /*
+                        * have not written to this block yet, so we keep
+                        * looking for zero's
+                        */
+                       pt = st;
+                       end = st + wcnt;
+
+                       /*
+                        * look for a zero filled buffer
+                        */
+                       while ((pt < end) && (*pt == '\0'))
+                               ++pt;
+
+                       if (pt == end) {
+                               /*
+                                * skip, buf is empty so far
+                                */
+                               if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) {
+                                       syswarn(1,errno,"File seek on %s",
+                                           name);
+                                       return(-1);
+                               }
+                               st = pt;
+                               continue;
+                       }
+                       /*
+                        * drat, the buf is not zero filled
+                        */
+                       *isempt = 0;
+               }
+
+               /*
+                * have non-zero data in this file system block, have to write
+                */
+               if (write(fd, st, wcnt) != wcnt) {
+                       syswarn(1, errno, "Failed write to file %s", name);
+                       return(-1);
+               }
+               st += wcnt;
+       }
+       return(st - str);
+}
+
+/*
+ * file_flush()
+ *     when the last file block in a file is zero, many file systems will not
+ *     let us create a hole at the end. To get the last block with zeros, we
+ *     write the last BYTE with a zero (back up one byte and write a zero).
+ */
+
+#if __STDC__
+void
+file_flush(int fd, char *fname, int isempt)
+#else
+void
+file_flush(fd, fname, isempt)
+       int fd;
+       char *fname;
+       int isempt;
+#endif
+{
+       static char blnk[] = "\0";
+
+       /*
+        * silly test, but make sure we are only called when the last block is
+        * filled with all zeros.
+        */
+       if (!isempt)
+               return;
+
+       /*
+        * move back one byte and write a zero
+        */
+       if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) {
+               syswarn(1, errno, "Failed seek on file %s", fname);
+               return;
+       }
+
+       if (write(fd, blnk, 1) < 0)
+               syswarn(1, errno, "Failed write to file %s", fname);
+       return;
+}
+
+/*
+ * rdfile_close()
+ *     close a file we have beed reading (to copy or archive). If we have to
+ *     reset access time (tflag) do so (the times are stored in arcn).
+ */
+
+#if __STDC__
+void
+rdfile_close(register ARCHD *arcn, register int *fd)
+#else
+void
+rdfile_close(arcn, fd)
+       register ARCHD *arcn;
+       register int *fd;
+#endif
+{
+       /*
+        * make sure the file is open
+        */
+       if (*fd < 0)
+               return;
+
+       (void)close(*fd);
+       *fd = -1;
+       if (!tflag)
+               return;
+
+       /*
+        * user wants last access time reset
+        */
+       set_ftime(arcn->org_name, arcn->sb.st_mtime, arcn->sb.st_atime, 1);
+       return;
+}
+
+/*
+ * set_crc()
+ *     read a file to calculate its crc. This is a real drag. Archive formats
+ *     that have this, end up reading the file twice (we have to write the
+ *     header WITH the crc before writing the file contents. Oh well...
+ * Return:
+ *     0 if was able to calculate the crc, -1 otherwise
+ */
+
+#if __STDC__
+int
+set_crc(register ARCHD *arcn, register int fd)
+#else
+int
+set_crc(arcn, fd)
+       register ARCHD *arcn;
+       register int fd;
+#endif
+{
+       register int i;
+       register int res;
+       off_t cpcnt = 0L;
+       u_long size;
+       unsigned long crc = 0L;
+       char tbuf[FILEBLK];
+       struct stat sb;
+
+       if (fd < 0) {
+               /*
+                * hmm, no fd, should never happen. well no crc then.
+                */
+               arcn->crc = 0L;
+               return(0);
+       }
+
+       if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf))
+               size = (u_long)sizeof(tbuf);
+
+       /*
+        * read all the bytes we think that there are in the file. If the user
+        * is trying to archive an active file, forget this file.
+        */
+       for(;;) {
+               if ((res = read(fd, tbuf, size)) <= 0)
+                       break;
+               cpcnt += res;
+               for (i = 0; i < res; ++i)
+                       crc += (tbuf[i] & 0xff);
+       }
+
+       /*
+        * safety check. we want to avoid archiving files that are active as
+        * they can create inconsistant archive copies.
+        */
+       if (cpcnt != arcn->sb.st_size)
+               warn(1, "File changed size %s", arcn->org_name);
+       else if (fstat(fd, &sb) < 0)
+               syswarn(1, errno, "Failed stat on %s", arcn->org_name);
+       else if (arcn->sb.st_mtime != sb.st_mtime)
+               warn(1, "File %s was modified during read", arcn->org_name);
+       else if (lseek(fd, (off_t)0L, SEEK_SET) < 0)
+               syswarn(1, errno, "File rewind failed on: %s", arcn->org_name);
+       else {
+               arcn->crc = crc;
+               return(0);
+       }
+       return(-1);
+}
diff --git a/usr/src/bin/pax/ftree.c b/usr/src/bin/pax/ftree.c
new file mode 100644 (file)
index 0000000..5e8878f
--- /dev/null
@@ -0,0 +1,539 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ftree.c    8.2 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fts.h>
+#include "pax.h"
+#include "ftree.h"
+#include "extern.h"
+
+/*
+ * routines to interface with the fts library function.
+ *
+ * file args supplied to pax are stored on a single linked list (of type FTREE)
+ * and given to fts to be processed one at a time. pax "selects" files from
+ * the expansion of each arg into the corresponding file tree (if the arg is a
+ * directory, otherwise the node itself is just passed to pax). The selection
+ * is modified by the -n and -u flags. The user is informed when a specific
+ * file arg does not generate any selected files. -n keeps expanding the file
+ * tree arg until one of its files is selected, then skips to the next file
+ * arg. when the user does not supply the file trees as command line args to
+ * pax, they are read from stdin
+ */
+
+static FTS *ftsp = NULL;               /* curent FTS handle */
+static int ftsopts;                    /* options to be used on fts_open */
+static char *farray[2];                        /* array for passing each arg to fts */
+static FTREE *fthead = NULL;           /* head of linked list of file args */
+static FTREE *fttail = NULL;           /* tail of linked list of file args */
+static FTREE *ftcur = NULL;            /* current file arg being processed */
+static FTSENT *ftent = NULL;           /* current file tree entry */
+static int ftree_skip;                 /* when set skip to next file arg */
+
+static int ftree_arg __P((void));
+
+/*
+ * ftree_start()
+ *     initialize the options passed to fts_open() during this run of pax
+ *     options are based on the selection of pax options by the user
+ *     fts_start() also calls fts_arg() to open the first valid file arg. We
+ *     also attempt to reset directory access times when -t (tflag) is set.
+ * Return:
+ *     0 if there is at least one valid file arg to process, -1 otherwise
+ */
+
+#if __STDC__
+int
+ftree_start(void)
+#else
+int
+ftree_start()
+#endif
+{
+       /*
+        * set up the operation mode of fts, open the first file arg. We must
+        * use FTS_NOCHDIR, as the user may have to open multiple archives and
+        * if fts did a chdir off into the boondocks, we may create an archive
+        * volume in an place where the user did not expect to.
+        */
+       ftsopts = FTS_NOCHDIR;
+
+       /*
+        * optional user flags that effect file traversal
+        * -H command line symlink follow only (half follow)
+        * -L follow sylinks (logical)
+        * -P do not follow sylinks (physical). This is the default.
+        * -X do not cross over mount points
+        * -t preserve access times on files read.
+        * -n select only the first member of a file tree when a match is found
+        * -d do not extract subtrees rooted at a directory arg.
+        */
+       if (Lflag)
+               ftsopts |= FTS_LOGICAL;
+       else
+               ftsopts |= FTS_PHYSICAL;
+       if (Hflag)
+#      ifdef NET2_FTS
+               warn(0, "The -H flag is not supported on this version");
+#      else
+               ftsopts |= FTS_COMFOLLOW;
+#      endif
+       if (Xflag)
+               ftsopts |= FTS_XDEV;
+
+       if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) {
+               warn(1, "Unable to allocate memory for file name buffer");
+               return(-1);
+       }
+
+       if (ftree_arg() < 0)
+               return(-1);
+       if (tflag && (atdir_start() < 0))
+               return(-1);
+       return(0);
+}
+
+/*
+ * ftree_add()
+ *     add the arg to the linked list of files to process. Each will be
+ *     processed by fts one at a time
+ * Return:
+ *     0 if added to the linked list, -1 if failed
+ */
+
+#if __STDC__
+int
+ftree_add(register char *str)
+#else
+int
+ftree_add(str)
+       register char *str;
+#endif
+{
+       register FTREE *ft;
+       register int len;
+
+       /*
+        * simple check for bad args
+        */
+       if ((str == NULL) || (*str == '\0')) {
+               warn(0, "Invalid file name arguement");
+               return(-1);
+       }
+
+       /*
+        * allocate FTREE node and add to the end of the linked list (args are
+        * processed in the same order they were passed to pax). Get rid of any
+        * trailing / the user may pass us. (watch out for / by itself).
+        */
+       if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) {
+               warn(0, "Unable to allocate memory for filename");
+               return(-1);
+       }
+
+       if (((len = strlen(str) - 1) > 0) && (str[len] == '/'))
+               str[len] = '\0';
+       ft->fname = str;
+       ft->refcnt = 0;
+       ft->fow = NULL;
+       if (fthead == NULL) {
+               fttail = fthead = ft;
+               return(0);
+       }
+       fttail->fow = ft;
+       fttail = ft;
+       return(0);
+}
+
+/*
+ * ftree_sel()
+ *     this entry has been selected by pax. bump up reference count and handle
+ *     -n and -d processing.
+ */
+
+#if __STDC__
+void
+ftree_sel(register ARCHD *arcn)
+#else
+void
+ftree_sel(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       /*
+        * set reference bit for this pattern. This linked list is only used
+        * when file trees are supplied pax as args. The list is not used when
+        * the trees are read from stdin.
+        */
+       if (ftcur != NULL) 
+               ftcur->refcnt = 1;
+
+       /*
+        * if -n we are done with this arg, force a skip to the next arg when
+        * pax asks for the next file in next_file().
+        * if -d we tell fts only to match the directory (if the arg is a dir)
+        * and not the entire file tree rooted at that point.
+        */
+       if (nflag)
+               ftree_skip = 1;
+
+       if (!dflag || (arcn->type != PAX_DIR))
+               return;
+
+       if (ftent != NULL)
+               (void)fts_set(ftsp, ftent, FTS_SKIP);
+}
+
+/*
+ * ftree_chk()
+ *     called at end on pax execution. Prints all those file args that did not
+ *     have a selected member (reference count still 0)
+ */
+
+#if __STDC__
+void
+ftree_chk(void)
+#else
+void
+ftree_chk()
+#endif
+{
+       register FTREE *ft;
+       register int wban = 0;
+
+       /*
+        * make sure all dir access times were reset.
+        */
+       if (tflag)
+               atdir_end();
+
+       /*
+        * walk down list and check reference count. Print out those members
+        * that never had a match
+        */
+       for (ft = fthead; ft != NULL; ft = ft->fow) {
+               if (ft->refcnt > 0)
+                       continue;
+               if (wban == 0) {
+                       warn(1,"WARNING! These file names were not selected:");
+                       ++wban;
+               }
+               (void)fprintf(stderr, "%s\n", ft->fname);
+       }
+}
+
+/*
+ * ftree_arg()
+ *     Get the next file arg for fts to process. Can be from either the linked
+ *     list or read from stdin when the user did not them as args to pax. Each
+ *     arg is processed until the first successful fts_open().
+ * Return:
+ *     0 when the next arg is ready to go, -1 if out of file args (or EOF on
+ *     stdin).
+ */
+
+#if __STDC__
+static int
+ftree_arg(void)
+#else
+static int
+ftree_arg()
+#endif
+{
+       register char *pt;
+
+       /*
+        * close off the current file tree
+        */
+       if (ftsp != NULL) {
+               (void)fts_close(ftsp);
+               ftsp = NULL;
+       }
+
+       /*
+        * keep looping until we get a valid file tree to process. Stop when we
+        * reach the end of the list (or get an eof on stdin)
+        */
+       for(;;) {
+               if (fthead == NULL) {
+                       /*
+                        * the user didn't supply any args, get the file trees
+                        * to process from stdin; 
+                        */
+                       if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL)
+                               return(-1);
+                       if ((pt = strchr(farray[0], '\n')) != NULL)
+                               *pt = '\0';
+               } else {
+                       /*
+                        * the user supplied the file args as arguements to pax
+                        */
+                       if (ftcur == NULL)
+                               ftcur = fthead;
+                       else if ((ftcur = ftcur->fow) == NULL)
+                               return(-1);
+                       farray[0] = ftcur->fname;
+               }
+
+               /*
+                * watch it, fts wants the file arg stored in a array of char
+                * ptrs, with the last one a null. we use a two element array
+                * and set farray[0] to point at the buffer with the file name
+                * in it. We cannnot pass all the file args to fts at one shot
+                * as we need to keep a handle on which file arg generates what
+                * files (the -n and -d flags need this). If the open is
+                * successful, return a 0.
+                */
+               if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL)
+                       break;
+       }
+       return(0);
+}
+
+/*
+ * next_file()
+ *     supplies the next file to process in the supplied archd structure.
+ * Return:
+ *     0 when contents of arcn have been set with the next file, -1 when done.
+ */
+
+#if __STDC__
+int
+next_file(register ARCHD *arcn)
+#else
+int
+next_file(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       register int cnt;
+       time_t atime;
+       time_t mtime;
+
+       /*
+        * ftree_sel() might have set the ftree_skip flag if the user has the
+        * -n option and a file was selected from this file arg tree. (-n says
+        * only one member is matched for each pattern) ftree_skip being 1 
+        * forces us to go to the next arg now.
+        */
+       if (ftree_skip) {
+               /*
+                * clear and go to next arg
+                */
+               ftree_skip = 0;
+               if (ftree_arg() < 0)
+                       return(-1);
+       }
+
+       /*
+        * loop until we get a valid file to process
+        */
+       for(;;) {
+               if ((ftent = fts_read(ftsp)) == NULL) {
+                       /*
+                        * out of files in this tree, go to next arg, if none
+                        * we are done
+                        */
+                       if (ftree_arg() < 0)
+                               return(-1);
+                       continue;
+               }
+
+               /*
+                * handle each type of fts_read() flag
+                */
+               switch(ftent->fts_info) {
+               case FTS_D:
+               case FTS_DEFAULT:
+               case FTS_F:
+               case FTS_SL:
+               case FTS_SLNONE:
+                       /*
+                        * these are all ok
+                        */
+                       break;
+               case FTS_DP:
+                       /*
+                        * already saw this directory. If the user wants file
+                        * access times reset, we use this to restore the
+                        * access time for this directory since this is the
+                        * last time we will see it in this file subtree
+                        * remember to force the time (this is -t on a read
+                        * directory, not a created directory).
+                        */
+#                      ifdef NET2_FTS
+                       if (!tflag || (get_atdir(ftent->fts_statb.st_dev,
+                           ftent->fts_statb.st_ino, &mtime, &atime) < 0))
+#                      else
+                       if (!tflag || (get_atdir(ftent->fts_statp->st_dev,
+                           ftent->fts_statp->st_ino, &mtime, &atime) < 0))
+#                      endif
+                               continue;
+                       set_ftime(ftent->fts_path, mtime, atime, 1);
+                       continue;
+               case FTS_DC:
+                       /*
+                        * fts claims a file system cycle
+                        */
+                       warn(1,"File system cycle found at %s",ftent->fts_path);
+                       continue;
+               case FTS_DNR:
+#                      ifdef NET2_FTS
+                       syswarn(1, errno,
+#                      else
+                       syswarn(1, ftent->fts_errno,
+#                      endif
+                           "Unable to read directory %s", ftent->fts_path);
+                       continue;
+               case FTS_ERR:
+#                      ifdef NET2_FTS
+                       syswarn(1, errno,
+#                      else
+                       syswarn(1, ftent->fts_errno,
+#                      endif
+                           "File system traversal error");
+                       continue;
+               case FTS_NS:
+               case FTS_NSOK:
+#                      ifdef NET2_FTS
+                       syswarn(1, errno,
+#                      else
+                       syswarn(1, ftent->fts_errno,
+#                      endif
+                           "Unable to access %s", ftent->fts_path);
+                       continue;
+               }
+
+               /*
+                * ok got a file tree node to process. copy info into arcn
+                * structure (initialize as required)
+                */
+               arcn->skip = 0;
+               arcn->pad = 0;
+               arcn->ln_nlen = 0;
+               arcn->ln_name[0] = '\0';
+#              ifdef NET2_FTS
+               arcn->sb = ftent->fts_statb;
+#              else
+               arcn->sb = *(ftent->fts_statp);
+#              endif
+
+               /*
+                * file type based set up and copy into the arcn struct
+                * SIDE NOTE:
+                * we try to reset the access time on all files and directories
+                * we may read when the -t flag is specified. files are reset
+                * when we close them after copying. we reset the directories
+                * when we are done with their file tree (we also clean up at
+                * end in case we cut short a file tree traversal). However
+                * there is no way to reset access times on symlinks.
+                */
+               switch(S_IFMT & arcn->sb.st_mode) {
+               case S_IFDIR:
+                       arcn->type = PAX_DIR;
+                       if (!tflag)
+                               break;
+                       add_atdir(ftent->fts_path, arcn->sb.st_dev,
+                           arcn->sb.st_ino, arcn->sb.st_mtime,
+                           arcn->sb.st_atime);
+                       break;
+               case S_IFCHR:
+                       arcn->type = PAX_CHR;
+                       break;
+               case S_IFBLK:
+                       arcn->type = PAX_BLK;
+                       break;
+               case S_IFREG:
+                       /*
+                        * only regular files with have data to store on the
+                        * archive. all others will store a zero length skip.
+                        * the skip field is used by pax for actual data it has
+                        * to read (or skip over).
+                        */
+                       arcn->type = PAX_REG;
+                       arcn->skip = arcn->sb.st_size;
+                       break;
+               case S_IFLNK:
+                       arcn->type = PAX_SLK;
+                       /*
+                        * have to read the symlink path from the file
+                        */
+                       if ((cnt = readlink(ftent->fts_path, arcn->ln_name,
+                           PAXPATHLEN)) < 0) {
+                               syswarn(1, errno, "Unable to read symlink %s",
+                                   ftent->fts_path);
+                               continue;
+                       }
+                       /*
+                        * set link name length, watch out readlink does not
+                        * allways null terminate the link path
+                        */
+                       arcn->ln_name[cnt] = '\0';
+                       arcn->ln_nlen = cnt;
+                       break;
+               case S_IFSOCK:
+                       /*
+                        * under BSD storing a socket is senseless but we will
+                        * let the format specific write function make the
+                        * decision of what to do with it.
+                        */
+                       arcn->type = PAX_SCK;
+                       break;
+               case S_IFIFO:
+                       arcn->type = PAX_FIF;
+                       break;
+               }
+               break;
+       }
+
+       /*
+        * copy file name, set file name length
+        */
+       arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, PAXPATHLEN+1);
+       arcn->name[arcn->nlen] = '\0';
+       arcn->org_name = ftent->fts_path;
+       return(0);
+}
diff --git a/usr/src/bin/pax/ftree.h b/usr/src/bin/pax/ftree.h
new file mode 100644 (file)
index 0000000..2db7969
--- /dev/null
@@ -0,0 +1,46 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)ftree.h     8.1 (Berkeley) 5/31/93
+ */
+
+/*
+ * Data structure used by the ftree.c routines to store the file args to be
+ * handed to fts(). It keeps a reference count of which args generated a
+ * "selected" member
+ */
+
+typedef struct ftree {
+       char            *fname;         /* file tree name */
+       int             refcnt;         /* has tree had a selected file? */
+       struct ftree    *fow;           /* pointer to next entry on list */
+} FTREE;
diff --git a/usr/src/bin/pax/gen_subs.c b/usr/src/bin/pax/gen_subs.c
new file mode 100644 (file)
index 0000000..f09b589
--- /dev/null
@@ -0,0 +1,483 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <tzfile.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "pax.h"
+#include "extern.h"
+
+/*
+ * a collection of general purpose subroutines used by pax
+ */
+
+/*
+ * constants used by ls_list() when printing out archive members
+ */
+#define MODELEN 20
+#define DATELEN 64
+#define SIXMONTHS       ((DAYSPERNYEAR / 2) * SECSPERDAY)
+#define CURFRMT                "%b %e %H:%M"
+#define OLDFRMT                "%b %e  %Y"
+#ifndef UT_NAMESIZE
+#define UT_NAMESIZE    8
+#endif
+#define UT_GRPSIZE     6
+
+/*
+ * ls_list()
+ *     list the members of an archive in ls format
+ */
+
+#if __STDC__
+void
+ls_list(register ARCHD *arcn, time_t now)
+#else
+void
+ls_list(arcn, now)
+       register ARCHD *arcn;
+       time_t now;
+#endif
+{
+       register struct stat *sbp;
+       char f_mode[MODELEN];
+       char f_date[DATELEN];
+       char *timefrmt;
+
+       /*
+        * if not verbose, just print the file name
+        */
+       if (!vflag) {
+               (void)printf("%s\n", arcn->name);
+               (void)fflush(stdout);
+               return;
+       }
+
+       /*
+        * user wants long mode
+        */
+       sbp = &(arcn->sb);
+       strmode(sbp->st_mode, f_mode);
+
+       if (ltmfrmt == NULL) {
+               /*
+                * no locale specified format. time format based on age
+                * compared to the time pax was started.
+                */
+               if ((sbp->st_mtime + SIXMONTHS) <= now)
+                       timefrmt = OLDFRMT;
+               else
+                       timefrmt = CURFRMT;
+       } else
+               timefrmt = ltmfrmt;
+
+       /*
+        * print file mode, link count, uid, gid and time
+        */
+       if (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0)
+               f_date[0] = '\0';
+       (void)printf("%s%2u %-*s %-*s ", f_mode, sbp->st_nlink, UT_NAMESIZE,
+               name_uid(sbp->st_uid, 1), UT_GRPSIZE,
+               name_gid(sbp->st_gid, 1));
+
+       /*
+        * print device id's for devices, or sizes for other nodes
+        */
+       if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK))
+#              ifdef NET2_STAT
+               (void)printf("%4u,%4u ", MAJOR(sbp->st_rdev),
+#              else
+               (void)printf("%4lu,%4lu ", MAJOR(sbp->st_rdev),
+#              endif
+                   MINOR(sbp->st_rdev));
+       else {
+#              ifdef NET2_STAT
+               (void)printf("%9lu ", sbp->st_size);
+#              else
+               (void)printf("%9qu ", sbp->st_size);
+#              endif
+       }
+
+       /*
+        * print name and link info for hard and soft links
+        */
+       (void)printf("%s %s", f_date, arcn->name);
+       if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
+               (void)printf(" == %s\n", arcn->ln_name);
+       else if (arcn->type == PAX_SLK)
+               (void)printf(" => %s\n", arcn->ln_name);
+       else
+               (void)putchar('\n');
+       (void)fflush(stdout);
+       return;
+}
+
+/*
+ * tty_ls()
+ *     print a short summary of file to tty.
+ */
+
+#if __STDC__
+void
+ls_tty(register ARCHD *arcn)
+#else
+void
+ls_tty(arcn)
+       register ARCHD *arcn;
+#endif
+{
+       char f_date[DATELEN];
+       char f_mode[MODELEN];
+       char *timefrmt;
+
+       if (ltmfrmt == NULL) {
+               /*
+                * no locale specified format
+                */
+               if ((arcn->sb.st_mtime + SIXMONTHS) <= time((time_t *)NULL))
+                       timefrmt = OLDFRMT;
+               else
+                       timefrmt = CURFRMT;
+       } else
+               timefrmt = ltmfrmt;
+
+       /*
+        * convert time to string, and print
+        */
+       if (strftime(f_date, DATELEN, timefrmt,
+           localtime(&(arcn->sb.st_mtime))) == 0)
+               f_date[0] = '\0';
+       strmode(arcn->sb.st_mode, f_mode);
+       tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name);
+       return;
+}
+
+/*
+ * zf_strncpy()
+ *     copy src to dest up to len chars (stopping at first '\0'), when src is
+ *     shorter than len, pads to len with '\0'. big performance win (and 
+ *     a lot easier to code) over strncpy(), then a strlen() then a
+ *     bzero(). (or doing the bzero() first).
+ */
+
+#if __STDC__
+void
+zf_strncpy(register char *dest, register char *src, int len)
+#else
+void
+zf_strncpy(dest, src, len)
+       register char *dest;
+       register char *src;
+       int len;
+#endif
+{
+       register char *stop;
+
+       stop = dest + len;
+       while ((dest < stop) && (*src != '\0'))
+               *dest++ = *src++;
+       while (dest < stop)
+               *dest++ = '\0';
+       return;
+}
+
+/*
+ * l_strncpy()
+ *     copy src to dest up to len chars (stopping at first '\0')
+ * Return:
+ *     number of chars copied. (Note this is a real performance win over
+ *     doing a strncpy() then a strlen()
+ */
+
+#if __STDC__
+int
+l_strncpy(register char *dest, register char *src, int len)
+#else
+int
+l_strncpy(dest, src, len)
+       register char *dest;
+       register char *src;
+       int len;
+#endif
+{
+       register char *stop;
+       register char *start;
+
+       stop = dest + len;
+       start = dest;
+       while ((dest < stop) && (*src != '\0'))
+               *dest++ = *src++;
+       if (dest < stop)
+               *dest = '\0';
+       return(dest - start);
+}
+
+/*
+ * asc_ul()
+ *     convert hex/octal character string into a u_long. We do not have to
+ *     check for overflow! (the headers in all supported formats are not large
+ *     enough to create an overflow).
+ *     NOTE: strings passed to us are NOT TERMINATED.
+ * Return:
+ *     unsigned long value
+ */
+
+#if __STDC__
+u_long
+asc_ul(register char *str, int len, register int base)
+#else
+u_long
+asc_ul(str, len, base)
+       register char *str;
+       int len;
+       register int base;
+#endif
+{
+       register char *stop;
+       u_long tval = 0;
+
+       stop = str + len;
+
+       /*
+        * skip over leading blanks and zeros
+        */
+       while ((str < stop) && ((*str == ' ') || (*str == '0')))
+               ++str;
+
+       /*
+        * for each valid digit, shift running value (tval) over to next digit
+        * and add next digit
+        */
+       if (base == HEX) {
+               while (str < stop) {
+                       if ((*str >= '0') && (*str <= '9'))
+                               tval = (tval << 4) + (*str++ - '0');
+                       else if ((*str >= 'A') && (*str <= 'F'))
+                               tval = (tval << 4) + 10 + (*str++ - 'A');
+                       else if ((*str >= 'a') && (*str <= 'f'))
+                               tval = (tval << 4) + 10 + (*str++ - 'a');
+                       else
+                               break;
+               }
+       } else {
+               while ((str < stop) && (*str >= '0') && (*str <= '7'))
+                       tval = (tval << 3) + (*str++ - '0');
+       }
+       return(tval);
+}
+
+/*
+ * ul_asc()
+ *     convert an unsigned long into an hex/oct ascii string. pads with LEADING
+ *     ascii 0's to fill string completely
+ *     NOTE: the string created is NOT TERMINATED.
+ */
+
+#if __STDC__
+int
+ul_asc(u_long val, register char *str, register int len, register int base)
+#else
+int
+ul_asc(val, str, len, base)
+       u_long val;
+       register char *str;
+       register int len;
+       register int base;
+#endif
+{
+       register char *pt;
+       u_long digit;
+       
+       /*
+        * WARNING str is not '\0' terminated by this routine
+        */
+       pt = str + len - 1;
+
+       /*
+        * do a tailwise conversion (start at right most end of string to place
+        * least significant digit). Keep shifting until conversion value goes
+        * to zero (all digits were converted)
+        */
+       if (base == HEX) {
+               while (pt >= str) {
+                       if ((digit = (val & 0xf)) < 10)
+                               *pt-- = '0' + (char)digit;
+                       else 
+                               *pt-- = 'a' + (char)(digit - 10);
+                       if ((val = (val >> 4)) == (u_long)0)
+                               break;
+               }
+       } else {
+               while (pt >= str) {
+                       *pt-- = '0' + (char)(val & 0x7);
+                       if ((val = (val >> 3)) == (u_long)0)
+                               break;
+               }
+       }
+
+       /*
+        * pad with leading ascii ZEROS. We return -1 if we ran out of space.
+        */
+       while (pt >= str)
+               *pt-- = '0';
+       if (val != (u_long)0)
+               return(-1);
+       return(0);
+}
+
+#ifndef NET2_STAT
+/*
+ * asc_uqd()
+ *     convert hex/octal character string into a u_quad_t. We do not have to
+ *     check for overflow! (the headers in all supported formats are not large
+ *     enough to create an overflow).
+ *     NOTE: strings passed to us are NOT TERMINATED.
+ * Return:
+ *     u_quad_t value
+ */
+
+#if __STDC__
+u_quad_t
+asc_uqd(register char *str, int len, register int base)
+#else
+u_quad_t
+asc_uqd(str, len, base)
+       register char *str;
+       int len;
+       register int base;
+#endif
+{
+       register char *stop;
+       u_quad_t tval = 0;
+
+       stop = str + len;
+
+       /*
+        * skip over leading blanks and zeros
+        */
+       while ((str < stop) && ((*str == ' ') || (*str == '0')))
+               ++str;
+
+       /*
+        * for each valid digit, shift running value (tval) over to next digit
+        * and add next digit
+        */
+       if (base == HEX) {
+               while (str < stop) {
+                       if ((*str >= '0') && (*str <= '9'))
+                               tval = (tval << 4) + (*str++ - '0');
+                       else if ((*str >= 'A') && (*str <= 'F'))
+                               tval = (tval << 4) + 10 + (*str++ - 'A');
+                       else if ((*str >= 'a') && (*str <= 'f'))
+                               tval = (tval << 4) + 10 + (*str++ - 'a');
+                       else
+                               break;
+               }
+       } else {
+               while ((str < stop) && (*str >= '0') && (*str <= '7'))
+                       tval = (tval << 3) + (*str++ - '0');
+       }
+       return(tval);
+}
+
+/*
+ * uqd_asc()
+ *     convert an u_quad_t into a hex/oct ascii string. pads with LEADING
+ *     ascii 0's to fill string completely
+ *     NOTE: the string created is NOT TERMINATED.
+ */
+
+#if __STDC__
+int
+uqd_asc(u_quad_t val, register char *str, register int len, register int base)
+#else
+int
+uqd_asc(val, str, len, base)
+       u_quad_t val;
+       register char *str;
+       register int len;
+       register int base;
+#endif
+{
+       register char *pt;
+       u_quad_t digit;
+       
+       /*
+        * WARNING str is not '\0' terminated by this routine
+        */
+       pt = str + len - 1;
+
+       /*
+        * do a tailwise conversion (start at right most end of string to place
+        * least significant digit). Keep shifting until conversion value goes
+        * to zero (all digits were converted)
+        */
+       if (base == HEX) {
+               while (pt >= str) {
+                       if ((digit = (val & 0xf)) < 10)
+                               *pt-- = '0' + (char)digit;
+                       else 
+                               *pt-- = 'a' + (char)(digit - 10);
+                       if ((val = (val >> 4)) == (u_quad_t)0)
+                               break;
+               }
+       } else {
+               while (pt >= str) {
+                       *pt-- = '0' + (char)(val & 0x7);
+                       if ((val = (val >> 3)) == (u_quad_t)0)
+                               break;
+               }
+       }
+
+       /*
+        * pad with leading ascii ZEROS. We return -1 if we ran out of space.
+        */
+       while (pt >= str)
+               *pt-- = '0';
+       if (val != (u_quad_t)0)
+               return(-1);
+       return(0);
+}
+#endif
diff --git a/usr/src/bin/pax/options.c b/usr/src/bin/pax/options.c
new file mode 100644 (file)
index 0000000..330c86a
--- /dev/null
@@ -0,0 +1,1136 @@
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)options.c  8.2 (Berkeley) 4/18/94";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mtio.h>
+#include <sys/param.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <limits.h>
+#include "pax.h"
+#include "options.h"
+#include "cpio.h"
+#include "tar.h"
+#include "extern.h"
+
+/*
+ * Routines which handle command line options
+ */
+
+static char flgch[] = FLGCH;   /* list of all possible flags */
+static OPLIST *ophead = NULL;  /* head for format specific options -x */
+static OPLIST *optail = NULL;  /* option tail */
+
+static int no_op __P((void));
+static void printflg __P((unsigned int));
+static int c_frmt __P((const void *, const void *));
+static off_t str_offt __P((char *));
+static void pax_options __P((register int, register char **));
+static void pax_usage __P((void));
+static void tar_options __P((register int, register char **));
+static void tar_usage __P((void));
+#ifdef notdef
+static void cpio_options __P((register int, register char **));
+static void cpio_usage __P((void));
+#endif
+
+/*
+ *     Format specific routine table - MUST BE IN SORTED ORDER BY NAME
+ *     (see pax.h for description of each function)
+ *
+ *     name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read,
+ *     read, end_read, st_write, write, end_write, trail,
+ *     rd_data, wr_data, options
+ */
+
+FSUB fsub[] = {
+/* 0: OLD BINARY CPIO */
+       "bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd,
+       bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail,
+       rd_wrfile, wr_rdfile, bad_opt,
+
+/* 1: OLD OCTAL CHARACTER CPIO */
+       "cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd,
+       cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail,
+       rd_wrfile, wr_rdfile, bad_opt,
+
+/* 2: SVR4 HEX CPIO */
+       "sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd,
+       vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail,
+       rd_wrfile, wr_rdfile, bad_opt,
+
+/* 3: SVR4 HEX CPIO WITH CRC */
+       "sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd,
+       vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail,
+       rd_wrfile, wr_rdfile, bad_opt,
+
+/* 4: OLD TAR */
+       "tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op,
+       tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, tar_trail,
+       rd_wrfile, wr_rdfile, tar_opt,
+
+/* 5: POSIX USTAR */
+       "ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd,
+       ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, tar_trail,
+       rd_wrfile, wr_rdfile, bad_opt,
+};
+#define F_TAR  4       /* format when called as tar */
+#define DEFLT  5       /* default write format from list above */
+
+/*
+ * ford is the archive search order used by get_arc() to determine what kind
+ * of archive we are dealing with. This helps to properly id  archive formats
+ * some formats may be subsets of others....
+ */
+int ford[] = {5, 4, 3, 2, 1, 0, -1 };
+
+/*
+ * options()
+ *     figure out if we are pax, tar or cpio. Call the appropriate options
+ *     parser
+ */
+
+#if __STDC__
+void
+options(register int argc, register char **argv)
+#else
+void
+options(argc, argv)
+       register int argc;
+       register char **argv;
+#endif
+{
+
+       /*
+        * Are we acting like pax, tar or cpio (based on argv[0])
+        */
+       if ((argv0 = strrchr(argv[0], '/')) != NULL)
+               argv0++;
+       else
+               argv0 = argv[0];
+
+       if (strcmp(NM_TAR, argv0) == 0)
+               return(tar_options(argc, argv));
+#      ifdef notdef
+       else if (strcmp(NM_CPIO, argv0) == 0)
+               return(cpio_options(argc, argv));
+#      endif
+       /*
+        * assume pax as the default
+        */
+       argv0 = NM_PAX;
+       return(pax_options(argc, argv));
+}
+
+/*
+ * pax_options()
+ *     look at the user specified flags. set globals as required and check if
+ *     the user specified a legal set of flags. If not, complain and exit
+ */
+
+#if __STDC__
+static void
+pax_options(register int argc, register char **argv)
+#else
+static void
+pax_options(argc, argv)
+       register int argc;
+       register char **argv;
+#endif
+{
+       register int c;
+       register int i;
+       unsigned int flg = 0;
+       unsigned int bflg = 0;
+       register char *pt;
+        FSUB tmp;
+       extern char *optarg;
+       extern int optind;
+
+       /*
+        * process option flags
+        */
+       while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:B:DE:G:HLPT:U:XYZ"))
+           != EOF) {
+               switch (c) {
+               case 'a':
+                       /*
+                        * append
+                        */
+                       flg |= AF;
+                       break;
+               case 'b':
+                       /*
+                        * specify blocksize
+                        */
+                       flg |= BF;
+                       if ((wrblksz = (int)str_offt(optarg)) <= 0) {
+                               warn(1, "Invalid block size %s", optarg);
+                               pax_usage();
+                       }
+                       break;
+               case 'c':
+                       /*
+                        * inverse match on patterns
+                        */
+                       cflag = 1;
+                       flg |= CF;
+                       break;
+               case 'd':
+                       /*
+                        * match only dir on extract, not the subtree at dir
+                        */
+                       dflag = 1;
+                       flg |= DF;
+                       break;
+               case 'f':
+                       /*
+                        * filename where the archive is stored
+                        */
+                       arcname = optarg;
+                       flg |= FF;
+                       break;
+               case 'i':
+                       /*
+                        * interactive file rename
+                        */
+                       iflag = 1;
+                       flg |= IF;
+                       break;
+               case 'k':
+                       /*
+                        * do not clobber files that exist
+                        */
+                       kflag = 1;
+                       flg |= KF;
+                       break;
+               case 'l':
+                       /*
+                        * try to link src to dest with copy (-rw)
+                        */
+                       lflag = 1;
+                       flg |= LF;
+                       break;
+               case 'n':
+                       /*
+                        * select first match for a pattern only
+                        */
+                       nflag = 1;
+                       flg |= NF;
+                       break;
+               case 'o':
+                       /*
+                        * pass format specific options
+                        */
+                       flg |= OF;
+                       if (opt_add(optarg) < 0)
+                               pax_usage();
+              &nbs