root/usr/src/cmd/cpio/cpio.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
 * Copyright 2012 Milan Jurik. All rights reserved.
 * Copyright (c) 2012 Gary Mills
 */

/*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
/*      All Rights Reserved                                     */

/*
 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
 */

/*
 * Portions of this source code were derived from Berkeley 4.3 BSD
 * under license from the Regents of the University of California.
 */

#include <stdio.h>
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <memory.h>
#include <string.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/mkdev.h>
#include <sys/param.h>
#include <utime.h>
#include <pwd.h>
#include <grp.h>
#include <signal.h>
#include <ctype.h>
#include <locale.h>
#include <sys/ioctl.h>
#include <sys/mtio.h>
#include <sys/fdio.h>
#include "cpio.h"
#include <sys/acl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fnmatch.h>
#include <libgen.h>
#include <libintl.h>
#include <dirent.h>
#include <limits.h>
#include <aclutils.h>
#if defined(_PC_SATTR_ENABLED)
#include <libnvpair.h>
#include <attr.h>
#include <libcmdutils.h>
#endif  /* _PC_SATTR_ENABLED */
#ifdef SOLARIS_PRIVS
#include <priv.h>
#endif  /* SOLARIS_PRIVS */

/*
 * Special kludge for off_t being a signed quantity.
 */
typedef ulong_t         u_off_t;

#define SECMODE 0xe080

#define DEVNULL         "/dev/null"
#define XATTRHDR        ".hdr"

#define NAMELEN         32
#define TYPELEN         16
#define PERMLEN         4

#define FILE_COPIED     1
#define FILE_LINKED     2
#define FILE_PASS_ERR   -1

#define ARCHIVE_NORMAL  0
#define ARCHIVE_ACL     1
#define ARCHIVE_XATTR   2
#define ARCHIVE_SPARSE  3

#ifndef VIEW_READONLY
#define VIEW_READONLY   "SUNWattr_ro"
#endif

#ifndef VIEW_READWRITE
#define VIEW_READWRITE  "SUNWattr_rw"
#endif


#define LSTAT(dir, path, statbuf) fstatat(dir, \
    get_component((Gen.g_attrnam_p == NULL) ? \
    path : Gen.g_attrnam_p), statbuf, AT_SYMLINK_NOFOLLOW)
#define STAT(dir, path, statbuf) fstatat(dir, \
    get_component((Gen.g_attrnam_p == NULL) ? \
    path : Gen.g_attrnam_p), statbuf, 0)

/*
 * Convert from and to old dev_t formats.
 */
#define SVR3_MAJOR(x)   ((major_t)((dev_t)(x) >> ONBITSMINOR) & OMAXMAJ)
#define SVR3_MINOR(x)   ((minor_t)((dev_t)(x) & OMAXMIN))
#define TO_SVR3(maj, min) \
        ((((ushort_t)(maj) & OMAXMAJ) << ONBITSMINOR) | \
        ((ushort_t)(min) & OMAXMIN))

/*
 *      These limits reflect the maximum size regular file that
 *      can be archived, depending on the archive type. For archives
 *      with character-format headers (odc, tar, ustar) we use
 *      CHAR_OFFSET_MAX.  For archives with SVR4 ASCII headers (-c, -H crc)
 *      we store filesize in an 8-char hexadecimal string and use
 *      ASC_OFFSET_MAX.  Otherwise, we are limited to the size that will
 *      fit in a signed int value.
 */
#define CHAR_OFFSET_MAX 077777777777ULL /* 11 octal digits */
#define ASC_OFFSET_MAX  0XFFFFFFFF      /* 8 hexadecimal digits */
#define BIN_OFFSET_MAX  INT_MAX         /* signed int max value */

#define POSIXMODES      07777

static char     aclchar = ' ';

static struct Lnk *add_lnk(struct Lnk **);
static int bfill(void);
static void bflush(void);
static int chgreel(int dir);
static int ckname(int);
static void ckopts(long mask);
static uint_t cksum(char hdr, int byt_cnt, int *err);
static int creat_hdr(void);
static int creat_lnk(int dirfd, char *name1_p, char *name2_p);
static int creat_spec(int dirfd);
static int creat_tmp(char *nam_p);
static void data_in(int proc_mode);
static void data_out(void);
static void data_pass(void);
static void file_in(void);
static int file_out(void);
static int file_pass(void);
static void flush_lnks(void);
static int gethdr(void);
static int getname(void);
static void getpats(int largc, char **largv);
static void ioerror(int dir);
static int matched(void);
static int missdir(char *nam_p);
static long mklong(short v[]);
static void mkshort(short sval[], long v);
static int openout(int dirfd);
static int read_hdr(int hdr);
static void reclaim(struct Lnk *l_p);
static void rstbuf(void);
static void setpasswd(char *nam);
static void rstfiles(int over, int dirfd);
static void scan4trail(void);
static void setup(int largc, char **largv);
static void set_tym(int dirfd, char *nam_p, time_t atime, time_t mtime);
static void sigint(int sig);
static void swap(char *buf_p, int cnt);
static void usage(void);
static void verbose(char *nam_p);
static void write_hdr(int arcflag, off_t len);
static void write_trail(void);
static int ustar_dir(void);
static int ustar_spec(void);
static struct stat *convert_to_old_stat(struct stat *, char *, char *);
static void read_bar_vol_hdr(void);
static void read_bar_file_hdr(void);
static void setup_uncompress(FILE **);
static void skip_bar_volhdr(void);
static void bar_file_in(void);
static int g_init(int *devtype, int *fdes);
static int g_read(int, int, char *, unsigned);
static int g_write(int, int, char *, unsigned);
static int is_floppy(int);
static int is_tape(int);
static void write_ancillary(char *buf, size_t len, boolean_t padding);
static int remove_dir(char *);
static int save_cwd(void);
static void rest_cwd(int cwd);

static void xattrs_out(int (*func)());
static void get_parent(char *path, char *dir);
static void prepare_xattr_hdr(char **attrbuf, char *filename,
    char *attrname, char typeflag, struct Lnk *linkinfo, int *rlen);
static char tartype(int type);
static int openfile(int omode);
static mode_t attrmode(char type);
static char *get_component(char *path);
static int open_dir(char *name);
static int open_dirfd();
static void close_dirfd();
static void write_xattr_hdr();
static char *skipslashes(char *string, char *start);
static int read_xattr_hdr();
static void chop_endslashes(char *path);


/* helpful types */

static
struct passwd   *Curpw_p,       /* Current password entry for -t option */
                *Rpw_p,         /* Password entry for -R option */
                *dpasswd;

static
struct group    *Curgr_p,       /* Current group entry for -t option */
                *dgroup;

/* Data structure for buffered I/O. */

static
struct buf_info {
        char    *b_base_p,      /* Pointer to base of buffer */
                *b_out_p,       /* Position to take bytes from buffer at */
                *b_in_p,        /* Position to put bytes into buffer at */
                *b_end_p;       /* Pointer to end of buffer */
        int     b_cnt,          /* Count of unprocessed bytes */
                b_size;         /* Size of buffer in bytes */
} Buffr;

/* Generic header format */

static
struct gen_hdr {
        uint_t  g_magic,        /* Magic number field */
                g_ino,          /* Inode number of file */
                g_mode,         /* Mode of file */
                g_uid,          /* Uid of file */
                g_gid,          /* Gid of file */
                g_nlink,        /* Number of links */
                g_mtime;        /* Modification time */
        off_t   g_filesz;       /* Length of file */
        dev_t   g_dev,          /* File system of file */
                g_rdev;         /* Major/minor numbers of special files */
        uint_t  g_namesz,       /* Length of filename */
                g_cksum;        /* Checksum of file */
        char    g_gname[32],
                g_uname[32],
                g_version[2],
                g_tmagic[6],
                g_typeflag;
        char    *g_tname,
                *g_prefix,
                *g_nam_p,       /* Filename */
                *g_attrparent_p, /* attribute parent */
                *g_attrpath_p, /* attribute path */
                *g_attrnam_p,   /* attribute */
                *g_attrfnam_p,  /* Real file name attr belongs to */
                *g_linktoattrfnam_p, /* file linked attribute belongs to */
                *g_linktoattrnam_p,  /* attribute g_attrnam_p is linked to */
                *g_dirpath;     /* dirname currently opened */
        int     g_dirfd;        /* directory file descriptor */
        int     g_passdirfd;    /* directory fd to pass to */
        int     g_rw_sysattr;   /* read-write system attribute */
        int     g_baseparent_fd;        /* base file's parent fd */
        holes_info_t *g_holes;  /* sparse file information */

} Gen, *G_p;

/* Data structure for handling multiply-linked files */
static
char    prebuf[PRESIZ+1],
        nambuf[NAMSIZ+1],
        fullnam[MAXNAM+1];


static
struct Lnk {
        short   L_cnt,          /* Number of links encountered */
                L_data;         /* Data has been encountered if 1 */
        struct gen_hdr  L_gen;  /* gen_hdr information for this file */
        struct Lnk      *L_nxt_p,       /* Next file in list */
                        *L_bck_p,       /* Previous file in list */
                        *L_lnk_p;       /* Next link for this file */
} Lnk_hd;

static
struct hdr_cpio Hdr;

/*
 * -------------------------------------------------------------------------
 *                 Stuff needed to pre-view the name stream
 *
 * issymlink is used to remember that the current file is a symlink between
 * getname() and file_pass(); the former trashes this information immediately
 * when -L is specified.
 */

static
int     issymlink = 0;

static
FILE    *In_p = stdin;          /* Where the input comes from */

typedef struct sl_info
{
        struct sl_info *llink;  /* Left subtree ptr (tree depth in *sl_head) */
        struct sl_info *rlink;  /* Right subtree ptr */
        int bal;                /* Subtree balance factor */
        uint_t  sl_count;       /* Number of symlinks */
        int     sl_ftype;       /* file type of inode */
        ino_t   sl_ino;         /* Inode of file */
        ino_t   sl_ino2;        /* alternate inode for -Hodc */
} sl_info_t;

typedef struct data_in
{
        int             data_in_errno;
        char            data_in_swapfile;
        char            data_in_proc_mode;
        char            data_in_rd_eof;
        char            data_in_wr_part;
        char            data_in_compress_flag;
        long            data_in_cksumval;
        FILE            *data_in_pipef;
} data_in_t;

/*
 * The following structure maintains a hash entry for the
 * balancing trees which are allocated for each device nodes.
 */
typedef struct sl_info_link
{
        dev_t           dev;
        sl_info_t       *head;
        struct sl_info_link *next;
} sl_info_link_t;

#define SL_INFO_ALLOC_CHUNK     1024
#define NDEVHENTRY              0x40
#define DEV_HASHKEY(x)          ((x) & (NDEVHENTRY -1))

/*
 * For remapping dev,inode for -Hodc archives.
 */

typedef struct sl_remap
{
        dev_t                   dev;            /* device */
        int                     inode_count;    /* # inodes seen on dev */
        struct sl_remap         *next;          /* next in the chain */
} sl_remap_t;

/* forward declarations */

static sl_info_t        *sl_info_alloc(void);
static sl_info_t        *sl_insert(dev_t, ino_t, int);
static uint_t           sl_numlinks(dev_t, ino_t, int);
static void             sl_preview_synonyms(void);
static void             sl_remember_tgt(const struct stat *, int, int);
static sl_info_t        *sl_search(dev_t, ino_t, int);
static sl_info_t        *sl_devhash_lookup(dev_t);
static void             sl_devhash_insert(dev_t, sl_info_t *);

extern int              sl_compare(ino_t, int, ino_t, int);
#define sl_compare(lino, lftype, rino, rftype)  (lino < rino ? -1 : \
            (lino > rino ? 1 : (lftype < rftype ? -1 : \
            (lftype > rftype ? 1 : 0))))

/* global storage */

static sl_remap_t  *sl_remap_head = NULL; /* head of the inode-remap list */
static sl_info_link_t   *sl_devhash[NDEVHENTRY]; /* hash table */

/*
 * -------------------------------------------------------------------------
 */

static
struct stat     ArchSt, /* stat(2) information of the archive */
                SrcSt,  /* stat(2) information of source file */
                DesSt,  /* stat(2) of destination file */
                *OldSt = NULL;  /* stat info converted to svr32 format */

/*
 * bin_mag: Used to validate a binary magic number,
 * by combining to bytes into an unsigned short.
 */

static
union bin_mag {
        unsigned char b_byte[2];
        ushort_t b_half;
} Binmag;

static
union tblock *Thdr_p;   /* TAR header pointer */

static union b_block *bar_Vhdr;
static struct gen_hdr Gen_bar_vol;

/*
 * swpbuf: Used in swap() to swap bytes within a halfword,
 * halfwords within a word, or to reverse the order of the
 * bytes within a word.  Also used in mklong() and mkshort().
 */

static
union swpbuf {
        unsigned char   s_byte[4];
        ushort_t        s_half[2];
        uint_t  s_word;
} *Swp_p;

static
char    *myname,                /* program name */
        Adir,                   /* Flags object as a directory */
        Hiddendir,              /* Processing hidden attribute directory */
        Aspec,                  /* Flags object as a special file */
        Do_rename,              /* Indicates rename() is to be used */
        Time[50],               /* Array to hold date and time */
        Ttyname[] = "/dev/tty", /* Controlling console */
        T_lname[MAXPATHLEN],    /* Array to hold links name for tar */
        *Buf_p,                 /* Buffer for file system I/O */
        *Full_p,                /* Pointer to full pathname */
        *Efil_p,                /* -E pattern file string */
        *Eom_p = "Change to part %d and press RETURN key. [q] ",
        *Fullnam_p,             /* Full pathname */
        *Attrfile_p,            /* attribute file */
        *Hdr_p,                 /* -H header type string */
        *IOfil_p,               /* -I/-O input/output archive string */
        *Lnkend_p,              /* Pointer to end of Lnknam_p */
        *Lnknam_p,              /* Buffer for linking files with -p option */
        *Nam_p,                 /* Array to hold filename */
        *Savenam_p,             /* copy of filename xattr belongs to */
        *Own_p,                 /* New owner login id string */
        *Renam_p,               /* Buffer for renaming files */
        *Renam_attr_p,          /* Buffer for renaming attr with sys attrs */
        *Renametmp_p,           /* Tmp Buffer for renaming files */
        *Symlnk_p,              /* Buffer for holding symbolic link name */
        *Over_p,                /* Holds temporary filename when overwriting */
        **Pat_pp = 0,           /* Pattern strings */
        bar_linkflag,           /* flag to indicate if the file is a link */
        bar_linkname[MAXPATHLEN]; /* store the name of the link */

static
int     Append = 0,     /* Flag set while searching to end of archive */
        Archive,        /* File descriptor of the archive */
        Buf_error = 0,  /* I/O error occurred during buffer fill */
        Compress_sparse = 0,    /* Compress sparse files */
        Def_mode = 0777,        /* Default file/directory protection modes */
        Device,         /* Device type being accessed (used with libgenIO) */
        Error_cnt = 0,  /* Cumulative count of I/O errors */
        Finished = 1,   /* Indicates that a file transfer has completed */
        Hdrsz = ASCSZ,  /* Fixed length portion of the header */
        Hdr_type,               /* Flag to indicate type of header selected */
        Ifile,          /* File des. of file being archived */
        Ofile,          /* File des. of file being extracted from archive */
        Use_old_stat = 0,    /* Create an old style -Hodc hdr (small dev's) */
        Onecopy = 0,    /* Flags old vs. new link handling */
        Pad_val = 0,    /* Indicates the number of bytes to pad (if any) */
        PageSize = 0,   /* The native page size, used for figuring block size */
        Volcnt = 1,     /* Number of archive volumes processed */
        Verbcnt = 0,    /* Count of number of dots '.' output */
        Eomflag = 0,
        Dflag = 0,
        Atflag = 0,     /* Archive/restore extended attributes */
        SysAtflag = 0,  /* Archive/restore extended system attributes */
        Compressed,     /* Flag to indicate if the bar archive is compressed */
        Bar_vol_num = 0, /* Volume number count for bar archive */
        privileged = 0, /* Flag set if running with higher privileges */
        attr_baseparent_fd = -1;        /* attribute's base file descriptor */


static
gid_t   Lastgid = (gid_t)-1;    /* Used with -t & -v to record current gid */

static
uid_t   Lastuid = (uid_t)-1;    /* Used with -t & -v to record current uid */

static
long    Args,           /* Mask of selected options */
        Max_namesz = CPATH;     /* Maximum size of pathnames/filenames */

static
int     Bufsize = BUFSZ;        /* Default block size */


static ulong_t    Blocks;       /* full blocks transferred */
static ulong_t    SBlocks;      /* cumulative char count from short reads */


static off_t    Max_offset = BIN_OFFSET_MAX;    /* largest file size */
static off_t    Max_filesz;                     /* from getrlimit */

static uint_t   Savedev;

static
FILE    *Ef_p,                  /* File pointer of pattern input file */
        *Err_p = stderr,        /* File pointer for error reporting */
        *Out_p = stdout,        /* File pointer for non-archive output */
        *Rtty_p,                /* Input file pointer for interactive rename */
        *Wtty_p;                /* Output file ptr for interactive rename */

static
ushort_t        Ftype = S_IFMT; /* File type mask */

/* ACL support */
static struct sec_attr {
        char    attr_type;
        char    attr_len[7];
        char    attr_info[1];
} *attr;

static int      Pflag = 0;      /* flag indicates that acl is preserved */
static int      acl_is_set = 0; /* True if an acl was set on the file */

acl_t *aclp;

#if defined(O_XATTR)
typedef enum {
        ATTR_OK,
        ATTR_SKIP,
        ATTR_CHDIR_ERR,
        ATTR_OPEN_ERR,
        ATTR_XATTR_ERR,
        ATTR_SATTR_ERR
} attr_status_t;
#endif

#if defined(O_XATTR)
typedef enum {
        ARC_CREATE,
        ARC_RESTORE
} arc_action_t;
#endif


/*
 *
 * cpio has been changed to support extended attributes.
 *
 * As part of this change cpio has been changed to use the new *at() syscalls
 * such as openat, fchownat(), unlinkat()...
 *
 * This was done so that attributes can be handled with as few code changes
 * as possible.
 *
 * What this means is that cpio now opens the directory that a file or directory
 * resides in and then performs *at() functions to manipulate the entry.
 *
 * For example a new file is now created like this:
 *
 * dfd = open(<some dir path>)
 * fd = openat(dfd, <name>,....);
 *
 * or in the case of an extended attribute
 *
 * dfd = attropen(<pathname>, ".", ....)
 *
 * Once we have a directory file descriptor all of the *at() functions can
 * be applied to it.
 *
 * unlinkat(dfd, <component name>,...)
 * fchownat(dfd, <component name>,..)
 *
 * This works for both normal namespace files and extended attribute file
 *
 */

/*
 * Extended attribute layout
 *
 * Extended attributes are stored in two pieces.
 * 1. An attribute header which has information about
 *    what file the attribute is for and what the attribute
 *    is named.
 * 2. The attribute record itself.  Stored as a normal file type
 *    of entry.
 * Both the header and attribute record have special modes/typeflags
 * associated with them.
 *
 * The names of the header in the archive look like:
 * /dev/null/attr.hdr
 *
 * The name of the attribute looks like:
 * /dev/null/attr.
 *
 * This is done so that an archiver that doesn't understand these formats
 * can just dispose of the attribute records unless the user chooses to
 * rename them via cpio -r or pax -i
 *
 * The format is composed of a fixed size header followed
 * by a variable sized xattr_buf. If the attribute is a hard link
 * to another attribute, then another xattr_buf section is included
 * for the link.
 *
 * The xattr_buf is used to define the necessary "pathing" steps
 * to get to the extended attribute.  This is necessary to support
 * a fully recursive attribute model where an attribute may itself
 * have an attribute.
 *
 * The basic layout looks like this.
 *
 *     --------------------------------
 *     |                              |
 *     |         xattr_hdr            |
 *     |                              |
 *     --------------------------------
 *     --------------------------------
 *     |                              |
 *     |        xattr_buf             |
 *     |                              |
 *     --------------------------------
 *     --------------------------------
 *     |                              |
 *     |      (optional link info)    |
 *     |                              |
 *     --------------------------------
 *     --------------------------------
 *     |                              |
 *     |      attribute itself        |
 *     |      stored as normal tar    |
 *     |      or cpio data with       |
 *     |      special mode or         |
 *     |      typeflag                |
 *     |                              |
 *     --------------------------------
 *
 */

/*
 * Extended attributes structures
 *
 * xattrhead is the complete extended attribute header, as read of off
 * disk/tape. It includes the variable xattr_buf portion.
 *
 * xattrp is basically an offset into xattrhead that points to the
 * "pathing" section which defines how to get to the attribute.
 *
 * xattr_linkp is identical to xattrp except that it is used for linked
 * attributes.  It provides the pathing steps to get to the linked
 * attribute.
 *
 * These structures are updated when an extended attribute header is read off
 * of disk/tape.
 */
static struct xattr_hdr *xattrhead;
static struct xattr_buf *xattrp;
static struct xattr_buf *xattr_linkp;
static int              xattrbadhead;   /* is extended attribute header bad? */

static int      append_secattr(char **, int *, acl_t *);

/*
 * Note regarding cpio and changes to ensure cpio doesn't try to second
 * guess whether it runs with sufficient privileges or not:
 *
 * cpio has been changed so that it doesn't carry a second implementation of
 * the kernel's policy with respect to privileges.  Instead of attempting
 * to restore uid and gid from an archive only if cpio is run as uid 0,
 * cpio now *always* tries to restore the uid and gid from the archive
 * except when the -R option is specified.  When the -R is specified,
 * the uid and gid of the restored file will be changed to those of the
 * login id specified.  In addition, chown(), set_tym(), and chmod() should
 * only be executed once during archive extraction, and to ensure
 * setuid/setgid bits are restored properly, chown() should always be
 * executed before chmod().
 *
 * Note regarding debugging mechanism for cpio:
 *
 * The following mechanism is provided to allow us to debug cpio in complicated
 * situations, like when it is part of a pipe.  The idea is that you compile
 * with -DWAITAROUND defined, and then add the "-z" command line option to the
 * target cpio invocation.  If stderr is available, it will tell you to which
 * pid to attach the debugger; otherwise, use ps to find it.  Attach to the
 * process from the debugger, and, *PRESTO*, you are there!
 *
 * Simply assign "waitaround = 0" once you attach to the process, and then
 * proceed from there as usual.
 */

#ifdef WAITAROUND
int waitaround = 0;             /* wait for rendezvous with the debugger */
#endif

#define EXIT_CODE       (Error_cnt > 255 ? 255 : Error_cnt)

/*
 * main: Call setup() to process options and perform initializations,
 * and then select either copy in (-i), copy out (-o), or pass (-p) action.
 */

int
main(int argc, char **argv)
{
        int i;
        int passret;

        (void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
#define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
#endif
        (void) textdomain(TEXT_DOMAIN);

        (void) memset(&Gen, 0, sizeof (Gen));
        myname = e_strdup(E_EXIT, basename(argv[0]));
        setup(argc, argv);

        if (signal(SIGINT, sigint) == SIG_IGN)
                (void) signal(SIGINT, SIG_IGN);
        switch (Args & (OCi | OCo | OCp)) {
        case OCi: /* COPY IN */
                Hdr_type = NONE;
                if (Atflag || SysAtflag) {
                        /*
                         * Save the current working directory, so
                         * we can change back here after cd'ing into
                         * the attribute directory when processing
                         * attributes.
                         */
                        if ((attr_baseparent_fd = save_cwd()) < 0) {
                                msg(EXT, "Unable to open current directory.");
                        }
                }
                while ((i = gethdr()) != 0) {
                        Gen.g_dirfd = -1;
                        if (i == 1) {
                                file_in();
                                /*
                                 * Any ACL info for this file would or should
                                 * have been used after file_in(); clear out
                                 * aclp so it is is not erroneously used on
                                 * the next file.
                                 */
                                if (aclp != NULL) {
                                        acl_free(aclp);
                                        aclp = NULL;
                                }
                                acl_is_set = 0;
                        }
                        (void) memset(&Gen, 0, sizeof (Gen));
                }
                /* Do not count "extra" "read-ahead" buffered data */
                if (Buffr.b_cnt > Bufsize)
                        Blocks -=  (Buffr.b_cnt / Bufsize);
                break;
        case OCo: /* COPY OUT */
                if (Args & OCA) {
                        scan4trail();
                }

                Gen.g_dirfd = -1;
                Gen.g_dirpath = NULL;
                sl_preview_synonyms();

                while ((i = getname()) != 0) {
                        if (i == 1) {
                                (void) file_out();
                                if (Atflag || SysAtflag) {
                                        if (Gen.g_dirfd != -1) {
                                                (void) close(Gen.g_dirfd);
                                        }
                                        Gen.g_dirfd = -1;
                                        xattrs_out(file_out);
                                }
                        }
                        if (aclp != NULL) {
                                acl_free(aclp);
                                aclp = NULL;
                                acl_is_set = 0;
                        }
                }
                write_trail();
                break;
        case OCp: /* PASS */
                sl_preview_synonyms();

                Gen.g_dirfd = -1;
                Gen.g_passdirfd = -1;
                Gen.g_dirpath = NULL;
                Compress_sparse = 1;
                while (getname()) {
                        /*
                         * If file is a fully qualified path then
                         * file_pass will strip off the leading '/'
                         * and we need to save off the unstripped
                         * name for attribute traversal.
                         */
                        if (Atflag || SysAtflag) {
                                (void) strcpy(Savenam_p, Gen.g_nam_p);
                        }
                        passret = file_pass();
                        if (aclp != NULL) {
                                acl_free(aclp);
                                aclp = NULL;
                                acl_is_set = 0;
                        }
                        if (Gen.g_passdirfd != -1)
                                (void) close(Gen.g_passdirfd);
                        Gen.g_passdirfd = -1;
                        if (Atflag || SysAtflag) {
                                if (Gen.g_dirfd != -1) {
                                        (void) close(Gen.g_dirfd);
                                }
                                Gen.g_dirfd = -1;
                                if (passret != FILE_LINKED) {
                                        Gen.g_nam_p = Savenam_p;
                                        xattrs_out(file_pass);
                                }
                        }
                }
                break;
        default:
                msg(EXT, "Impossible action.");
        }
        if (Ofile > 0) {
                if (close(Ofile) != 0)
                        msg(EXTN, "close error");
        }
        if (Archive > 0) {
                if (close(Archive) != 0)
                        msg(EXTN, "close error");
        }
        if ((Args & OCq) == 0) {
                Blocks = (ulong_t)(Blocks * Bufsize + SBlocks +
                    0x1FF) >> 9;
                msg(EPOST, "%ld blocks", Blocks);
        }
        if (Error_cnt)
                msg(EPOST, "%d error(s)", Error_cnt);
        return (EXIT_CODE);
}

/*
 * add_lnk: Add a linked file's header to the linked file data structure, by
 * either adding it to the end of an existing sub-list or starting
 * a new sub-list.  Each sub-list saves the links to a given file.
 *
 * Directly returns a pointer to the new entry; returns a pointer to the head
 * of the sub-list in which that entry is located through the argument.
 */

static struct Lnk *
add_lnk(struct Lnk **sublist_return)
{
        struct Lnk *new_entry, *sublist;

        for (sublist = Lnk_hd.L_nxt_p;
            sublist != &Lnk_hd;
            sublist = sublist->L_nxt_p) {
                if (sublist->L_gen.g_ino == G_p->g_ino &&
                    sublist->L_gen.g_dev == G_p->g_dev) {
                        /* found */
                        break;
                }
        }

        new_entry = e_zalloc(E_EXIT, sizeof (struct Lnk));

        new_entry->L_lnk_p = NULL;
        new_entry->L_gen = *G_p; /* structure copy */

        new_entry->L_gen.g_nam_p = e_zalloc(E_EXIT, (size_t)G_p->g_namesz);

        (void) strcpy(new_entry->L_gen.g_nam_p, G_p->g_nam_p);

        if (sublist == &Lnk_hd) {
                /* start new sub-list */
                new_entry->L_nxt_p = &Lnk_hd;
                new_entry->L_bck_p = Lnk_hd.L_bck_p;
                Lnk_hd.L_bck_p = new_entry->L_bck_p->L_nxt_p = new_entry;
                new_entry->L_lnk_p = NULL;
                new_entry->L_cnt = 1;
                new_entry->L_data = Onecopy ? 0 : 1;
                sublist = new_entry;
        } else {
                /* add to existing sub-list */
                struct Lnk *ptr;

                sublist->L_cnt++;

                for (ptr = sublist;
                    ptr->L_lnk_p != NULL;
                    ptr = ptr->L_lnk_p) {
                        ptr->L_gen.g_filesz = G_p->g_filesz;
                }

                ptr->L_gen.g_filesz = G_p->g_filesz;
                ptr->L_lnk_p = new_entry;
        }

        *sublist_return = sublist;
        return (new_entry);
}

/*
 * bfill: Read req_cnt bytes (out of filelen bytes) from the I/O buffer,
 * moving them to rd_buf_p.  When there are no bytes left in the I/O buffer,
 * Fillbuf is set and the I/O buffer is filled.  The variable dist is the
 * distance to lseek if an I/O error is encountered with the -k option set
 * (converted to a multiple of Bufsize).
 */

static int
bfill(void)
{
        int i = 0, rv = 0;
        static int eof = 0;

        if (!Dflag) {
        while ((Buffr.b_end_p - Buffr.b_in_p) >= Bufsize) {
                errno = 0;
                if ((rv = g_read(Device, Archive, Buffr.b_in_p, Bufsize)) < 0) {
                        if (((Buffr.b_end_p - Buffr.b_in_p) >= Bufsize) &&
                            (Eomflag == 0)) {
                                Eomflag = 1;
                                return (1);
                        }
                        if (errno == ENOSPC) {
                                (void) chgreel(INPUT);
                                if (Hdr_type == BAR) {
                                        skip_bar_volhdr();
                                }
                                continue;
                        } else if (Args & OCk) {
                                if (i++ > MX_SEEKS)
                                        msg(EXT, "Cannot recover.");
                                if (lseek(Archive, Bufsize, SEEK_REL) < 0)
                                        msg(EXTN, "Cannot lseek()");
                                Error_cnt++;
                                Buf_error++;
                                rv = 0;
                                continue;
                        } else
                                ioerror(INPUT);
                } /* (rv = g_read(Device, Archive ... */
                if (Hdr_type != BAR || rv == Bufsize) {
                        Buffr.b_in_p += rv;
                        Buffr.b_cnt += rv;
                }
                if (rv == Bufsize) {
                        eof = 0;
                        Blocks++;
                } else if (rv == 0) {
                        if (!eof) {
                                eof = 1;
                                break;
                        }
                        (void) chgreel(INPUT);
                        eof = 0;        /* reset the eof after chgreel  */

                        /*
                         * if spans multiple volume, skip the volume header of
                         * the next volume so that the file currently being
                         * extracted can continue to be extracted.
                         */
                        if (Hdr_type == BAR) {
                                skip_bar_volhdr();
                        }

                        continue;
                } else {
                        eof = 0;
                        SBlocks += (ulong_t)rv;
                }
        } /* (Buffr.b_end_p - Buffr.b_in_p) <= Bufsize */

        } else {                        /* Dflag */
                errno = 0;
                if ((rv = g_read(Device, Archive, Buffr.b_in_p, Bufsize)) < 0) {
                        return (-1);
                } /* (rv = g_read(Device, Archive ... */
                Buffr.b_in_p += rv;
                Buffr.b_cnt += rv;
                if (rv == Bufsize) {
                        eof = 0;
                        Blocks++;
                } else if (!rv) {
                        if (!eof) {
                                eof = 1;
                                return (rv);
                        }
                        return (-1);
                } else {
                        eof = 0;
                        SBlocks += (ulong_t)rv;
                }
        }
        return (rv);
}

/*
 * bflush: Move wr_cnt bytes from data_p into the I/O buffer.  When the
 * I/O buffer is full, Flushbuf is set and the buffer is written out.
 */

static void
bflush(void)
{
        int rv;

        while (Buffr.b_cnt >= Bufsize) {
                errno = 0;
                if ((rv = g_write(Device, Archive, Buffr.b_out_p,
                    Bufsize)) < 0) {
                        if (errno == ENOSPC && !Dflag)
                                rv = chgreel(OUTPUT);
                        else
                                ioerror(OUTPUT);
                }
                Buffr.b_out_p += rv;
                Buffr.b_cnt -= rv;
                if (rv == Bufsize)
                        Blocks++;
                else if (rv > 0)
                        SBlocks += (ulong_t)rv;
        }
        rstbuf();
}

/*
 * chgreel: Determine if end-of-medium has been reached.  If it has,
 * close the current medium and prompt the user for the next medium.
 */

static int
chgreel(int dir)
{
        int lastchar, tryagain, askagain, rv;
        int tmpdev;
        char str[APATH];
        struct stat statb;

        rv = 0;
        if (fstat(Archive, &statb) < 0)
                msg(EXTN, "Error during stat() of archive");
        if ((statb.st_mode & S_IFMT) != S_IFCHR) {
                if (dir == INPUT) {
                        msg(EXT, "%s%s\n",
                            "Can't read input:  end of file encountered ",
                            "prior to expected end of archive.");
                }
        }
        msg(EPOST, "\007End of medium on \"%s\".", dir ? "output" : "input");
        if (is_floppy(Archive))
                (void) ioctl(Archive, FDEJECT, NULL);
        if ((close(Archive) != 0) && (dir == OUTPUT))
                msg(EXTN, "close error");
        Archive = 0;
        Volcnt++;
        for (;;) {
                if (Rtty_p == NULL)
                        Rtty_p = fopen(Ttyname, "r");
                do { /* tryagain */
                        if (IOfil_p) {
                                do {
                                        msg(EPOST, Eom_p, Volcnt);
                                        if (!Rtty_p || fgets(str, sizeof (str),
                                            Rtty_p) == NULL)
                                                msg(EXT, "Cannot read tty.");
                                        askagain = 0;
                                        switch (*str) {
                                        case '\n':
                                                (void) strcpy(str, IOfil_p);
                                                break;
                                        case 'q':
                                                exit(EXIT_CODE);
                                        default:
                                                askagain = 1;
                                        }
                                } while (askagain);
                        } else {

                                if (Hdr_type == BAR)
                                        Bar_vol_num++;

                                msg(EPOST,
                                    "To continue, type device/file name when "
                                    "ready.");
                                if (!Rtty_p || fgets(str, sizeof (str),
                                    Rtty_p) == NULL)
                                        msg(EXT, "Cannot read tty.");
                                lastchar = strlen(str) - 1;
                                if (*(str + lastchar) == '\n') /* remove '\n' */
                                        *(str + lastchar) = '\0';
                                if (!*str)
                                        exit(EXIT_CODE);
                        }
                        tryagain = 0;
                        if ((Archive = open(str, dir)) < 0) {
                                msg(ERRN, "Cannot open \"%s\"", str);
                                tryagain = 1;
                        }
                } while (tryagain);
                (void) g_init(&tmpdev, &Archive);
                if (tmpdev != Device)
                        msg(EXT, "Cannot change media types in mid-stream.");
                if (dir == INPUT)
                        break;
                else { /* dir == OUTPUT */
                        errno = 0;
                        if ((rv = g_write(Device, Archive, Buffr.b_out_p,
                            Bufsize)) == Bufsize)
                                break;
                        else
                                msg(ERR,
                                    "Unable to write this medium, try "
                                    "another.");
                }
        } /* ;; */
        Eomflag = 0;
        return (rv);
}

/*
 * ckname: Check filenames against user specified patterns,
 * and/or ask the user for new name when -r is used.
 */

static int
ckname(int flag)
{
        int     lastchar;
        size_t  rename_bufsz = Max_namesz + 1;

        if (Hdr_type != TAR && Hdr_type != USTAR && Hdr_type != BAR) {
                /* Re-visit tar size issues later */
                if (G_p->g_namesz - 1 > Max_namesz) {
                        msg(ERR, "Name exceeds maximum length - skipped.");
                        return (F_SKIP);
                }
        }

        if (Pat_pp && !matched())
                return (F_SKIP);

        /* rename interactively */
        if ((Args & OCr) && !Adir && !G_p->g_rw_sysattr) {
                (void) fprintf(Wtty_p, gettext("Rename \"%s%s%s\"? "),
                    (G_p->g_attrnam_p == NULL) ? G_p->g_nam_p : Renam_p,
                    (G_p->g_attrnam_p == NULL) ? "" : gettext(" Attribute "),
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p);
                (void) fflush(Wtty_p);
                if (fgets(Renametmp_p, rename_bufsz, Rtty_p) == NULL)
                        msg(EXT, "Cannot read tty.");
                if (feof(Rtty_p))
                        exit(EXIT_CODE);
                lastchar = strlen(Renametmp_p) - 1;

                /* remove trailing '\n' */
                if (*(Renametmp_p + lastchar) == '\n')
                        *(Renametmp_p + lastchar) = '\0';
                if (*Renametmp_p == '\0') {
                        msg(POST, "%s%s%s Skipped.",
                            (G_p->g_attrnam_p == NULL) ? G_p->g_nam_p :
                            G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p);
                        if (G_p->g_attrparent_p == NULL) {
                                *G_p->g_nam_p = '\0';
                        }
                        if (Renam_attr_p) {
                                *Renam_attr_p = '\0';
                        }
                        return (F_SKIP);
                } else if (strcmp(Renametmp_p, ".") != 0) {
                        if (G_p->g_attrnam_p == NULL) {
                                if (strlen(Renametmp_p) > strlen(
                                    G_p->g_nam_p)) {
                                        if ((G_p->g_nam_p != &nambuf[0]) &&
                                            (G_p->g_nam_p != &fullnam[0])) {
                                                free(G_p->g_nam_p);
                                                G_p->g_nam_p = e_zalloc(E_EXIT,
                                                    rename_bufsz);
                                        }
                                }
                                if (Renam_attr_p) {
                                        *Renam_attr_p = '\0';
                                }
                                if ((strlcpy(Renam_p, Renametmp_p,
                                    rename_bufsz) > rename_bufsz) ||
                                    (strlcpy(G_p->g_nam_p, Renametmp_p,
                                    rename_bufsz) > rename_bufsz)) {
                                        msg(EXTN, "buffer overflow");
                                }
                        } else {
                                if (G_p->g_attrnam_p != NULL) {
                                        free(G_p->g_attrnam_p);
                                        G_p->g_attrnam_p = e_strdup(E_EXIT,
                                            Renametmp_p);
                                        (void) strcpy(G_p->g_nam_p, Renam_p);
                                        if (Renam_attr_p) {
                                                if (strlcpy(Renam_attr_p,
                                                    Renametmp_p, rename_bufsz) >
                                                    rename_bufsz) {
                                                        msg(EXTN,
                                                            "buffer overflow");
                                                }
                                        }
                                }
                        }
                } else {
                        if (G_p->g_attrnam_p == NULL) {
                                *Renam_p = '\0';
                        }
                        if (Renam_attr_p) {
                                *Renam_attr_p = '\0';
                        }
                }
        }
        if (flag != 0 || Onecopy == 0) {
                VERBOSE((Args & OCt), G_p->g_nam_p);
        }
        if (Args & OCt)
                return (F_SKIP);
        return (F_EXTR);
}

/*
 * ckopts: Check the validity of all command line options.
 */

static void
ckopts(long mask)
{
        int oflag;
        char *t_p;
        long errmsk;
        uid_t   Euid = geteuid();       /* Effective uid of invoker */
#ifdef SOLARIS_PRIVS
        priv_set_t *privset;
        priv_set_t *zones_privset;
#endif  /* SOLARIS_PRIVS */

        if (mask & OCi) {
                errmsk = mask & INV_MSK4i;
        } else if (mask & OCo) {
                errmsk = mask & INV_MSK4o;
        } else if (mask & OCp) {
                errmsk = mask & INV_MSK4p;
        } else {
                msg(ERR, "One of -i, -o or -p must be specified.");
                errmsk = 0;
        }

        if (errmsk) {
                /* if non-zero, invalid options were specified */
                Error_cnt++;
        }

        if ((mask & OCa) && (mask & OCm) && ((mask & OCi) ||
            (mask & OCo))) {
                msg(ERR, "-a and -m are mutually exclusive.");
        }

        if ((mask & OCc) && (mask & OCH) &&
            (strcmp("odc", Hdr_p) != 0 && strcmp("odc_sparse", Hdr_p) != 0)) {
                msg(ERR, "-c and -H are mutually exclusive.");
        }

        if ((mask & OCv) && (mask & OCV)) {
                msg(ERR, "-v and -V are mutually exclusive.");
        }

        if ((mask & OCt) && (mask & OCV)) {
                msg(ERR, "-t and -V are mutually exclusive.");
        }

        if ((mask & OCB) && (mask & OCC)) {
                msg(ERR, "-B and -C are mutually exclusive.");
        }

        if ((mask & OCH) && (mask & OC6)) {
                msg(ERR, "-H and -6 are mutually exclusive.");
        }

        if ((mask & OCM) && !((mask & OCI) || (mask & OCO))) {
                msg(ERR, "-M not meaningful without -O or -I.");
        }

        if ((mask & OCA) && !(mask & OCO)) {
                msg(ERR, "-A requires the -O option.");
        }

        if (Bufsize <= 0) {
                msg(ERR, "Illegal size given for -C option.");
        }

        if (mask & OCH) {
                t_p = Hdr_p;

                while (*t_p != '\0') {
                        if (isupper(*t_p)) {
                                *t_p = 'a' + (*t_p - 'A');
                        }

                        t_p++;
                }

                if (!(strcmp("odc", Hdr_p))) {
                        Hdr_type = CHR;
                        Max_namesz = CPATH;
                        Onecopy = 0;
                        Use_old_stat = 1;
                } else if (!(strcmp("odc_sparse", Hdr_p))) {
                        Hdr_type = CHR;
                        Max_namesz = CPATH;
                        Onecopy = 0;
                        Use_old_stat = 1;
                        Compress_sparse = 1;
                } else if (!(strcmp("ascii_sparse", Hdr_p))) {
                        Hdr_type = ASC;
                        Max_namesz = APATH;
                        Onecopy = 1;
                        Compress_sparse = 1;
                } else if (!(strcmp("crc", Hdr_p))) {
                        Hdr_type = CRC;
                        Max_namesz = APATH;
                        Onecopy = 1;
                } else if (!(strcmp("tar", Hdr_p))) {
                        if (Args & OCo) {
                                Hdr_type = USTAR;
                                Max_namesz = HNAMLEN - 1;
                        } else {
                                Hdr_type = TAR;
                                Max_namesz = TNAMLEN - 1;
                        }
                        Onecopy = 0;
                } else if (!(strcmp("ustar", Hdr_p))) {
                        Hdr_type = USTAR;
                        Max_namesz = HNAMLEN - 1;
                        Onecopy = 0;
                } else if (!(strcmp("bar", Hdr_p))) {
                        if ((Args & OCo) || (Args & OCp)) {
                                msg(ERR,
                                    "Header type bar can only be used with -i");
                        }

                        if (Args & OCP) {
                                msg(ERR,
                                    "Can't preserve using bar header");
                        }

                        Hdr_type = BAR;
                        Max_namesz = TNAMLEN - 1;
                        Onecopy = 0;
                } else {
                        msg(ERR, "Invalid header \"%s\" specified", Hdr_p);
                }
        }

        if (mask & OCr) {
                Rtty_p = fopen(Ttyname, "r");
                Wtty_p = fopen(Ttyname, "w");

                if (Rtty_p == NULL || Wtty_p == NULL) {
                        msg(ERR, "Cannot rename, \"%s\" missing", Ttyname);
                }
        }

        if ((mask & OCE) && (Ef_p = fopen(Efil_p, "r")) == NULL) {
                msg(ERR, "Cannot open \"%s\" to read patterns", Efil_p);
        }

        if ((mask & OCI) && (Archive = open(IOfil_p, O_RDONLY)) < 0) {
                msg(ERR, "Cannot open \"%s\" for input", IOfil_p);
        }

        if (mask & OCO) {
                if (mask & OCA) {
                        if ((Archive = open(IOfil_p, O_RDWR)) < 0) {
                                msg(ERR,
                                    "Cannot open \"%s\" for append",
                                    IOfil_p);
                        }
                } else {
                        oflag = (O_WRONLY | O_CREAT | O_TRUNC);

                        if ((Archive = open(IOfil_p, oflag, 0777)) < 0) {
                                msg(ERR,
                                    "Cannot open \"%s\" for output",
                                    IOfil_p);
                        }
                }
        }

#ifdef SOLARIS_PRIVS
        if ((privset = priv_allocset()) == NULL) {
                msg(ERR, "Unable to allocate privilege set");
        } else if (getppriv(PRIV_EFFECTIVE, privset) != 0) {
                msg(ERR, "Unable to obtain privilege set");
        } else {
                zones_privset = priv_str_to_set("zone", "", NULL);
                if (zones_privset != NULL) {
                        privileged = (priv_issubset(zones_privset,
                            privset) == B_TRUE);
                        priv_freeset(zones_privset);
                } else {
                        msg(ERR, "Unable to map privilege to privilege set");
                }
        }
        if (privset != NULL) {
                priv_freeset(privset);
        }
#else
        privileged = (Euid == 0);
#endif  /* SOLARIS_PRIVS */

        if (mask & OCR) {
                if ((Rpw_p = getpwnam(Own_p)) == NULL) {
                        msg(ERR, "\"%s\" is not a valid user id", Own_p);
                } else if ((Euid != Rpw_p->pw_uid) && !privileged) {
                        msg(ERR, "R option only valid for super-user or "
                            "id matches login id of user executing cpio");
                }
        }

        if ((mask & OCo) && !(mask & OCO)) {
                Out_p = stderr;
        }

        if ((mask & OCp) && ((mask & (OCB|OCC)) == 0)) {
                /*
                 * We are in pass mode with no block size specified.  Use the
                 * larger of the native page size and 8192.
                 */

                Bufsize = (PageSize > 8192) ? PageSize : 8192;
        }
}

/*
 * cksum: Calculate the simple checksum of a file (CRC) or header
 * (TARTYP (TAR and USTAR)).  For -o and the CRC header, the file is opened and
 * the checksum is calculated.  For -i and the CRC header, the checksum
 * is calculated as each block is transferred from the archive I/O buffer
 * to the file system I/O buffer.  The TARTYP (TAR and USTAR) headers calculate
 * the simple checksum of the header (with the checksum field of the
 * header initialized to all spaces (\040).
 */

static uint_t
cksum(char hdr, int byt_cnt, int *err)
{
        char *crc_p, *end_p;
        int cnt;
        uint_t checksum = 0, have;
        off_t lcnt;

        if (err != NULL)
                *err = 0;
        switch (hdr) {
        case CRC:
                if (Args & OCi) { /* do running checksum */
                        end_p = Buffr.b_out_p + byt_cnt;
                        for (crc_p = Buffr.b_out_p; crc_p < end_p; crc_p++)
                                checksum += (uint_t)*crc_p;
                        break;
                }
                /* OCo - do checksum of file */
                lcnt = G_p->g_filesz;

                while (lcnt > 0) {
                        have = (lcnt < Bufsize) ? lcnt : Bufsize;
                        errno = 0;
                        if (read(Ifile, Buf_p, have) != have) {
                                msg(ERR, "Error computing checksum.");
                                if (err != NULL)
                                        *err = 1;
                                break;
                        }
                        end_p = Buf_p + have;
                        for (crc_p = Buf_p; crc_p < end_p; crc_p++)
                                checksum += (long)*crc_p;
                        lcnt -= have;
                }
                if (lseek(Ifile, (off_t)0, SEEK_ABS) < 0)
                        msg(ERRN, "Cannot reset file after checksum");
                break;
        case TARTYP: /* TAR and USTAR */
                crc_p = Thdr_p->tbuf.t_cksum;
                for (cnt = 0; cnt < TCRCLEN; cnt++) {
                        *crc_p = '\040';
                        crc_p++;
                }
                crc_p = (char *)Thdr_p;
                for (cnt = 0; cnt < TARSZ; cnt++) {
                        /*
                         * tar uses unsigned checksum, so we must use unsigned
                         * here in order to be able to read tar archives.
                         */
                        checksum += (long)((unsigned char)(*crc_p));
                        crc_p++;
                }
                break;
        default:
                msg(EXT, "Impossible header type.");
        } /* hdr */
        return (checksum);
}

/*
 * creat_hdr: Fill in the generic header structure with the specific
 *            header information based on the value of Hdr_type.
 *
 *            return (1) if this process was successful, and (0) otherwise.
 */

static int
creat_hdr(void)
{
        ushort_t ftype;
        int fullnamesize;
        dev_t dev;
        ino_t ino;

        ftype = SrcSt.st_mode & Ftype;
        Adir = (ftype == S_IFDIR);
        Aspec = (ftype == S_IFBLK || ftype == S_IFCHR || ftype == S_IFIFO ||
            ftype == S_IFSOCK);
        switch (Hdr_type) {
                case BIN:
                        Gen.g_magic = CMN_BIN;
                        break;
                case CHR:
                        Gen.g_magic = CMN_BIN;
                        break;
                case ASC:
                        Gen.g_magic = CMN_ASC;
                        break;
                case CRC:
                        Gen.g_magic = CMN_CRC;
                        break;
                case USTAR:
                        /*
                         * If the length of the full name is greater than 256,
                         * print out a message and return.
                         */
                        if ((fullnamesize = strlen(Gen.g_nam_p)) > MAXNAM) {
                                msg(ERR,
                                    "%s: file name too long", Gen.g_nam_p);
                                return (0);
                        } else if (fullnamesize > NAMSIZ) {
                                /*
                                 * The length of the full name is greater than
                                 * 100, so we must split the filename from the
                                 * path
                                 */
                                char namebuff[NAMSIZ+1];
                                char prebuff[PRESIZ+1];
                                char *lastslash;
                                int presize, namesize;

                                (void) memset(namebuff, '\0',
                                    sizeof (namebuff));
                                (void) memset(prebuff, '\0', sizeof (prebuff));

                                lastslash = strrchr(Gen.g_nam_p, '/');

                                if (lastslash != NULL) {
                                        namesize = strlen(++lastslash);
                                        presize = fullnamesize - namesize - 1;
                                } else {
                                        namesize = fullnamesize;
                                        lastslash = Gen.g_nam_p;
                                        presize = 0;
                                }

                                /*
                                 * If the filename is greater than 100 we can't
                                 * archive the file
                                 */
                                if (namesize > NAMSIZ) {
                                        msg(ERR,
                                            "%s: filename is greater than %d",
                                            lastslash, NAMSIZ);
                                        return (0);
                                }
                                (void) strncpy(&namebuff[0], lastslash,
                                    namesize);
                                /*
                                 * If the prefix is greater than 155 we can't
                                 * archive the file.
                                 */
                                if (presize > PRESIZ) {
                                        msg(ERR,
                                            "%s: prefix is greater than %d",
                                            Gen.g_nam_p, PRESIZ);
                                        return (0);
                                }
                                (void) strncpy(&prebuff[0], Gen.g_nam_p,
                                    presize);

                                Gen.g_tname = e_zalloc(E_EXIT, namesize + 1);
                                (void) strcpy(Gen.g_tname, namebuff);

                                Gen.g_prefix = e_zalloc(E_EXIT, presize + 1);
                                (void) strcpy(Gen.g_prefix, prebuff);
                        } else {
                                Gen.g_tname = Gen.g_nam_p;
                        }
                        (void) strcpy(Gen.g_tmagic, "ustar");
                        (void) memcpy(Gen.g_version, "00", 2);

                        dpasswd = getpwuid(SrcSt.st_uid);
                        if (dpasswd == NULL) {
                                msg(EPOST,
                                    "cpio: could not get passwd information "
                                    "for %s%s%s",
                                    (Gen.g_attrnam_p == NULL) ?
                                    Gen.g_nam_p : Gen.g_attrfnam_p,
                                    (Gen.g_attrnam_p == NULL) ?
                                    "" : Gen.g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (Gen.g_attrnam_p == NULL) ?
                                    "" : Gen.g_attrnam_p);
                                /* make name null string */
                                Gen.g_uname[0] = '\0';
                        } else {
                                (void) strncpy(&Gen.g_uname[0],
                                    dpasswd->pw_name, 32);
                        }
                        dgroup = getgrgid(SrcSt.st_gid);
                        if (dgroup == NULL) {
                                msg(EPOST,
                                    "cpio: could not get group information "
                                    "for %s%s%s",
                                    (Gen.g_attrnam_p == NULL) ?
                                    Gen.g_nam_p : Gen.g_attrfnam_p,
                                    (Gen.g_attrnam_p == NULL) ?
                                    "" : Gen.g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (Gen.g_attrnam_p == NULL) ?
                                    "" : Gen.g_attrnam_p);
                                /* make name null string */
                                Gen.g_gname[0] = '\0';
                        } else {
                                (void) strncpy(&Gen.g_gname[0],
                                    dgroup->gr_name, 32);
                        }
                        Gen.g_typeflag = tartype(ftype);
                        /* FALLTHROUGH */
                case TAR:
                        (void) memset(T_lname, '\0', sizeof (T_lname));
                        break;
                default:
                        msg(EXT, "Impossible header type.");
        }

        if (Use_old_stat && (Gen.g_attrnam_p != NULL)) {
                /*
                 * When processing extended attributes, creat_hdr()
                 * can get called multiple times which means that
                 * SrcSt.st.st_dev would have gotten converted to
                 * -Hodc format.  We should always use the original
                 * device here as we need to be able to match on
                 * the original device id from the file that was
                 * previewed in sl_preview_synonyms().
                 */
                dev = Savedev;
        } else {
                dev = SrcSt.st_dev;
        }
        ino = SrcSt.st_ino;

        if (Use_old_stat) {
                SrcSt = *OldSt;
        }

        Gen.g_namesz = strlen(Gen.g_nam_p) + 1;
        Gen.g_uid = SrcSt.st_uid;
        Gen.g_gid = SrcSt.st_gid;
        Gen.g_dev = SrcSt.st_dev;

        if (Use_old_stat) {
                /* -Hodc */

                sl_info_t *p = sl_search(dev, ino, ftype);
                Gen.g_ino = p ? p->sl_ino2 : -1;

                if (Gen.g_ino == UINT_MAX) {
                        msg(ERR, "%s%s%s: cannot be archived - inode too big "
                            "for -Hodc format",
                            (Gen.g_attrnam_p == NULL) ?
                            Gen.g_nam_p : Gen.g_attrfnam_p,
                            (Gen.g_attrnam_p == NULL) ? "" : Gen.g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (Gen.g_attrnam_p == NULL) ? "" : Gen.g_attrnam_p);
                        return (0);
                }
        } else {
                Gen.g_ino = SrcSt.st_ino;
        }

        Gen.g_mode = SrcSt.st_mode;
        Gen.g_mtime = SrcSt.st_mtime;
        Gen.g_nlink = Adir ? SrcSt.st_nlink : sl_numlinks(dev, ino, ftype);

        if (ftype == S_IFREG || ftype == S_IFLNK)
                Gen.g_filesz = (off_t)SrcSt.st_size;
        else
                Gen.g_filesz = (off_t)0;
        Gen.g_rdev = SrcSt.st_rdev;
        return (1);
}

/*
 * creat_lnk: Create a link from the existing name1_p to name2_p.
 */

static
int
creat_lnk(int dirfd, char *name1_p, char *name2_p)
{
        int cnt = 0;

        do {
                errno = 0;
                if (!link(name1_p, name2_p)) {
                        if (aclp != NULL) {
                                acl_free(aclp);
                                aclp = NULL;
                                acl_is_set = 0;
                        }
                        cnt = 0;
                        break;
                } else if ((errno == EEXIST) && (cnt == 0)) {
                        struct stat lsb1;
                        struct stat lsb2;

                        /*
                         * Check to see if we are trying to link this
                         * file to itself.  If so, count the effort as
                         * successful.  If the two files are different,
                         * or if either lstat is unsuccessful, proceed
                         * as we would have otherwise; the appropriate
                         * error will be reported subsequently.
                         */

                        if (lstat(name1_p, &lsb1) != 0) {
                                msg(ERR, "Cannot lstat source file %s",
                                    name1_p);
                        } else {
                                if (lstat(name2_p, &lsb2) != 0) {
                                        msg(ERR, "Cannot lstat "
                                            "destination file %s", name2_p);
                                } else {
                                        if (lsb1.st_dev == lsb2.st_dev &&
                                            lsb1.st_ino == lsb2.st_ino) {
                                                VERBOSE((Args & (OCv | OCV)),
                                                    name2_p);
                                                return (0);
                                        }
                                }
                        }

                        if (!(Args & OCu) && G_p->g_mtime <= DesSt.st_mtime)
                                msg(ERR, "Existing \"%s\" same age or newer",
                                    name2_p);
                        else if (unlinkat(dirfd, get_component(name2_p), 0) < 0)
                                msg(ERRN, "Error cannot unlink \"%s\"",
                                    name2_p);
                }
                cnt++;
        } while ((cnt < 2) && missdir(name2_p) == 0);
        if (!cnt) {
                char *newname;
                char *fromname;
                char *attrname;

                newname = name2_p;
                fromname = name1_p;
                attrname = Gen.g_attrnam_p;
                if (attrname) {
                        if (Args & OCp) {
                                newname = fromname = Fullnam_p;
                        } else {
                                newname = Gen.g_attrfnam_p;
                        }
                }
                if (Args & OCv) {
                        (void) fprintf(Err_p,
                            gettext("%s%s%s linked to %s%s%s\n"), newname,
                            (attrname == NULL) ? "" : gettext(" attribute "),
                            (attrname == NULL) ? "" : attrname,
                            (attrname == NULL) ? fromname : newname,
                            (attrname == NULL) ? "" : gettext(" attribute "),
                            (attrname == NULL) ? "" : name1_p);
                } else {
                        VERBOSE((Args & (OCv | OCV)), newname);
                }
        } else if (cnt == 1)
                msg(ERRN,
                    "Unable to create directory for \"%s\"", name2_p);
        else if (cnt == 2)
                msg(ERRN,
                    "Cannot link \"%s\" and \"%s\"", name1_p, name2_p);
        return (cnt);
}

/*
 * creat_spec:
 *   Create one of the following:
 *       directory
 *       character special file
 *       block special file
 *       fifo
 *       socket
 */

static int
creat_spec(int dirfd)
{
        char *nam_p;
        int cnt, result, rv = 0;
        char *curdir;
        char *lastslash;

        Do_rename = 0;  /* creat_tmp() may reset this */

        if (Args & OCp) {
                nam_p = Fullnam_p;
        } else {
                nam_p = G_p->g_nam_p;
        }

        /*
         * Is this the extraction of the hidden attribute directory?
         * If we are processing the hidden attribute directory of an
         * attribute, then just return as modes and times cannot be set.
         * Otherwise, if we are processing a hidden attribute, just set
         * the mode/times correctly and return.
         */

        if (Hiddendir) {
                if (G_p->g_attrparent_p == NULL) {
                        if (Args & OCR) {
                                if (fchownat(dirfd, ".", Rpw_p->pw_uid,
                                    Rpw_p->pw_gid, 0) != 0) {
                                        msg(ERRN,
                                            "Cannot chown() \"attribute "
                                            "directory of file %s\"",
                                            G_p->g_attrfnam_p);
                                }
                        } else if ((fchownat(dirfd, ".", G_p->g_uid,
                            G_p->g_gid, 0) != 0) && privileged) {
                                msg(ERRN,
                                    "Cannot chown() \"attribute directory of "
                                    "file %s\"", G_p->g_attrfnam_p);
                        }

                        if (fchmod(dirfd, G_p->g_mode) != 0) {
                                msg(ERRN,
                                    "Cannot chmod() \"attribute directory of "
                                    "file %s\"", G_p->g_attrfnam_p);
                        }

                        acl_is_set = 0;
                        if (Pflag && aclp != NULL) {
                                if (facl_set(dirfd, aclp) < 0) {
                                        msg(ERRN,
                                            "failed to set acl on attribute"
                                            " directory of %s ",
                                            G_p->g_attrfnam_p);
                                } else {
                                        acl_is_set = 1;
                                }
                                acl_free(aclp);
                                aclp = NULL;
                        }
                }

                return (1);
        }

        result = stat(nam_p, &DesSt);

        if (ustar_dir() || Adir) {
                /*
                 *  The archive file is a directory.
                 *  Skip "." and ".."
                 */

                curdir = strrchr(nam_p, '.');

                if (curdir != NULL && curdir[1] == '\0') {
                        lastslash = strrchr(nam_p, '/');

                        if (lastslash != NULL) {
                                lastslash++;
                        } else {
                                lastslash = nam_p;
                        }

                        if (!(strcmp(lastslash, ".")) ||
                            !(strcmp(lastslash, ".."))) {
                                return (1);
                        }
                }

                if (result == 0) {
                        /* A file by the same name exists. */

                        /* Take care of ACLs */
                        acl_is_set = 0;

                        if (Pflag && aclp != NULL) {
                                if (acl_set(nam_p, aclp) < 0) {
                                        msg(ERRN,
                                            "\"%s\": failed to set acl",
                                            nam_p);
                                } else {
                                        acl_is_set = 1;
                                }

                                acl_free(aclp);
                                aclp = NULL;
                        }
                        if (Args & OCd) {
                                /*
                                 * We are creating directories.  Keep the
                                 * existing file.
                                 */

                                rstfiles(U_KEEP, dirfd);
                        }

                        /* Report success. */

                        return (1);
                }
        } else {
                /* The archive file is not a directory. */

                if (result == 0) {
                        /*
                         * A file by the same name exists.  Move it to a
                         * temporary file.
                         */

                        if (creat_tmp(nam_p) < 0) {
                                /*
                                 * We weren't able to create the temp file.
                                 * Report failure.
                                 */

                                return (0);
                        }
                }
        }

        /*
         * This pile tries to create the file directly, and, if there is a
         * problem, creates missing directories, and then tries to create the
         * file again.  Two strikes and you're out.
         */

        cnt = 0;

        do {
                if (ustar_dir() || Adir) {
                        /* The archive file is a directory. */

                        result = mkdir(nam_p, G_p->g_mode);
                } else if (ustar_spec() || Aspec) {
                        /*
                         * The archive file is block special,
                         * char special, socket, or a fifo.
                         * Note that, for a socket, the third
                         * parameter to mknod() is ignored.
                         */
                        result = mknod(nam_p, (int)G_p->g_mode,
                            (int)G_p->g_rdev);
                }

                if (result >= 0) {
                        /*
                         * The file creation succeeded.  Take care of the ACLs.
                         */

                        acl_is_set = 0;

                        if (Pflag && aclp != NULL) {
                                if (acl_set(nam_p, aclp) < 0) {
                                        msg(ERRN,
                                            "\"%s\": failed to set acl", nam_p);
                                } else {
                                        acl_is_set = 1;
                                }

                                acl_free(aclp);
                                aclp = NULL;
                        }

                        cnt = 0;
                        break;
                }

                cnt++;
        } while (cnt < 2 && missdir(nam_p) == 0);

        switch (cnt) {
        case 0:
                rv = 1;
                rstfiles(U_OVER, dirfd);
                break;

        case 1:
                msg(ERRN,
                    "Cannot create directory for \"%s\"", nam_p);

                if (*Over_p == '\0') {
                        rstfiles(U_KEEP, dirfd);
                }

                break;

        case 2:
                if (ustar_dir() || Adir) {
                        msg(ERRN, "Cannot create directory \"%s\"", nam_p);
                } else if (ustar_spec() || Aspec) {
                        msg(ERRN, "Cannot mknod() \"%s\"", nam_p);
                }

                if (*Over_p == '\0') {
                        rstfiles(U_KEEP, dirfd);
                }

                break;

        default:
                msg(EXT, "Impossible case.");
        }

        return (rv);
}

/*
 * creat_tmp:
 */

static int
creat_tmp(char *nam_p)
{
        char *t_p;
        int     cwd = -1;

        if ((Args & OCp) && G_p->g_ino == DesSt.st_ino &&
            G_p->g_dev == DesSt.st_dev) {
                msg(ERR, "Attempt to pass a file to itself.");
                return (-1);
        }

        if (G_p->g_mtime <= DesSt.st_mtime && !(Args & OCu)) {
                msg(ERR, "Existing \"%s\" same age or newer", nam_p);
                return (-1);
        }

        /* Make the temporary file name. */

        (void) strcpy(Over_p, nam_p);
        t_p = Over_p + strlen(Over_p);

        while (t_p != Over_p) {
                if (*(t_p - 1) == '/')
                        break;
                t_p--;
        }

        (void) strcpy(t_p, "XXXXXX");

        if (G_p->g_attrnam_p != NULL) {
                /*
                 * Save our current directory, so we can go into
                 * the attribute directory to make the temp file
                 * and then return.
                 */

                cwd = save_cwd();
                (void) fchdir(G_p->g_dirfd);
        }

        (void) mktemp(Over_p);

        if (G_p->g_attrnam_p != NULL) {
                /* Return to the current directory. */

                rest_cwd(cwd);
        }

        if (*Over_p == '\0') {
                /* mktemp reports a failure. */

                msg(ERR, "Cannot get temporary file name.");
                return (-1);
        }

        /*
         * If it's a regular file, write to the temporary file, and then rename
         * in order to accommodate potential executables.
         *
         * Note: g_typeflag is only defined (set) for USTAR archive types.  It
         * defaults to 0 in the cpio-format-regular file case, so this test
         * succeeds.
         */

        if (G_p->g_typeflag == 0 &&
            (DesSt.st_mode & (uint_t)Ftype) == S_IFREG &&
            (G_p->g_mode & (uint_t)Ftype) == S_IFREG) {
                /*
                 * The archive file and the filesystem file are both regular
                 * files.  We write to the temporary file in this case.
                 */

                if (Args & OCp) {
                        if (G_p->g_attrnam_p == NULL) {
                                Fullnam_p = Over_p;
                        } else {
                                Attrfile_p = Over_p;
                        }
                } else {
                        G_p->g_nam_p = Over_p;
                        if (G_p->g_attrnam_p != NULL) {
                                Attrfile_p = Over_p;
                        }
                }

                if (G_p->g_attrnam_p == NULL) {
                        Over_p = nam_p;
                } else {
                        Over_p = G_p->g_attrnam_p;
                }

                Do_rename = 1;
        } else {
                /*
                 * Either the archive file or the filesystem file is not a
                 * regular file.
                 */

                Do_rename = 0;

                if (S_ISDIR(DesSt.st_mode)) {
                        /*
                         * The filesystem file is a directory.
                         *
                         * Save the current working directory because we will
                         * want to restore it back just in case remove_dir()
                         * fails or get confused about where we should be.
                         */

                        *Over_p = '\0';
                        cwd = save_cwd();

                        if (remove_dir(nam_p) < 0) {
                                msg(ERRN,
                                    "Cannot remove the directory \"%s\"",
                                    nam_p);
                                /*
                                 * Restore working directory back to the one
                                 * saved earlier.
                                 */

                                rest_cwd(cwd);
                                return (-1);
                        }

                        /*
                         * Restore working directory back to the one
                         * saved earlier
                         */

                        rest_cwd(cwd);
                } else {
                        /*
                         * The file is not a directory. Will use the original
                         * link/unlink construct, however, if the file is
                         * namefs, link would fail with EXDEV. Therefore, we
                         * use rename() first to back up the file.
                         */
                        if (rename(nam_p, Over_p) < 0) {
                                /*
                                 * If rename failed, try old construction
                                 * method.
                                 */
                                if (link(nam_p, Over_p) < 0) {
                                        msg(ERRN,
                                            "Cannot rename temporary file "
                                            "\"%s\" to \"%s\"", Over_p, nam_p);
                                        *Over_p = '\0';
                                        return (-1);
                                }

                                if (unlink(nam_p) < 0) {
                                        msg(ERRN,
                                            "Cannot unlink() current \"%s\"",
                                            nam_p);
                                        (void) unlink(Over_p);
                                        *Over_p = '\0';
                                        return (-1);
                                }
                        }
                }
        }

        return (1);
}

/*
 * Copy the datasize amount of data from the input file to buffer.
 *
 * ifd          - Input file descriptor.
 * buffer       - Buffer (allocated by caller) to copy data to.
 * datasize     - The amount of data to read from the input file
 *              and copy to the buffer.
 * error        - When reading from an Archive file, indicates unreadable
 *              data was encountered, otherwise indicates errno.
 * data_in_info - Information needed when called from data_in().
 */
static ssize_t
read_chunk(int ifd, char *buffer, size_t datasize, data_in_t *data_in_info)
{
        if (Args & OCp) {
                return (read(ifd, buffer, datasize));
        } else {
                FILL(datasize);
                if (data_in_info->data_in_proc_mode != P_SKIP) {
                        if (Hdr_type == CRC)
                                data_in_info->data_in_cksumval += cksum(CRC,
                                    datasize, NULL);
                        if (data_in_info->data_in_swapfile)
                                swap(Buffr.b_out_p, datasize);


                        /*
                         * if the bar archive is compressed, set up a pipe and
                         * do the de-compression while reading in the file
                         */
                        if (Hdr_type == BAR) {
                                if (data_in_info->data_in_compress_flag == 0 &&
                                    Compressed) {
                                        setup_uncompress(
                                            &(data_in_info->data_in_pipef));
                                        data_in_info->data_in_compress_flag++;
                                }
                        }
                }
                (void) memcpy(buffer, Buffr.b_out_p, datasize);
                Buffr.b_out_p += datasize;
                Buffr.b_cnt -= datasize;
                return (datasize);
        }
}

/*
 * Read as much data as we can.
 *
 * ifd          - input file descriptor.
 * buf          - Buffer (allocated by caller) to copy data to.
 * bytes        - The amount of data to read from the input file
 *              and copy to the buffer.
 * rdblocksz    - The size of the chunk of data to read.
 *
 * Return number of bytes failed to read.
 * Return -1 when buffer is empty and read failed.
 */
static int
read_bytes(int ifd, char *buf, size_t bytes, size_t rdblocksz,
    data_in_t *data_in_info)
{
        size_t  bytesread;
        ssize_t got;

        for (bytesread = 0; bytesread < bytes; bytesread += got) {
                /*
                 * Read the data from either the input file descriptor
                 * or the archive file.  read_chunk() will only return
                 * <= 0 if data_copy() was called from data_pass().
                 */
                if ((got = read_chunk(ifd, buf + bytesread,
                    min(bytes - bytesread, rdblocksz),
                    data_in_info)) <= 0) {
                        /*
                         * We come here only in the pass mode.
                         * If data couldn't be read from the input file
                         * descriptor, return number of bytes in the buf.
                         * If buffer is empty, return -1.
                         */
                        if (bytesread == 0) {
                                if (got == 0) /* EOF */
                                        data_in_info->data_in_rd_eof = 1;
                                return (-1);
                        }
                        return (bytes - bytesread);
                }
        }
        return (0);
}

/*
 * Write as much data as we can.
 *
 * ofd          - output file descriptor.
 * buf          - Source buffer to output data from.
 * maxwrite     - The amount of data to write to the output.
 *
 * return 0 upon success.
 */
static int
write_bytes(int ofd, char *buf, size_t maxwrite, data_in_t *data_in_info)
{
        ssize_t cnt;

        errno = 0;
        if ((cnt = write(ofd, buf, maxwrite)) < (ssize_t)maxwrite) {
                data_in_info->data_in_errno = errno;
                /*
                 * data_in() needs to know if it was an actual write(2)
                 * failure, or if we just couldn't write all of the data
                 * requested so that we know that the rest of the file's
                 * data can be read but not written.
                 */
                if (cnt != -1)
                        data_in_info->data_in_wr_part = 1;
                return (1);
        } else if (Args & OCp) {
                Blocks += (ulong_t)((cnt + (Bufsize - 1)) / Bufsize);
        }
        return (0);
}

/*
 * Perform I/O for given byte size with using limited i/o block size
 * and supplied buffer.
 *
 * ifd/ofd      - i/o file descriptor
 * buf          - buffer to be used for i/o
 * bytes        - Amount to read/write
 * wrblocksz    - Output block size.
 * rdblocksz    - Read block size.
 *
 * Return 0 upon success. Return negative if read failed.
 * Return positive non-zero if write failed.
 */
static int
rdwr_bytes(int ifd, int ofd, char *buf, off_t bytes,
    size_t wrblocksz, size_t rdblocksz, data_in_t *data_in_info)
{
        int rv, sz;
        int error = 0;
        int write_it = (data_in_info->data_in_proc_mode != P_SKIP);

        while (bytes > 0) {
                /*
                 * If the number of bytes left to write is smaller than
                 * the preferred I/O size, then we're about to do our final
                 * write to the file, so just set wrblocksz to the number of
                 * bytes left to write.
                 */
                if (bytes < wrblocksz)
                        wrblocksz = bytes;

                /* Read input till satisfy output block size */
                sz = read_bytes(ifd, buf, wrblocksz, rdblocksz, data_in_info);
                if (sz < 0)
                        return (sz);

                if (write_it) {
                        rv = write_bytes(ofd, buf,
                            wrblocksz - sz, data_in_info);
                        if (rv != 0) {
                                /*
                                 * If we wrote partial, we return and quits.
                                 * Otherwise, read through the rest of input
                                 * to go to the next file.
                                 */
                                if ((Args & OCp) ||
                                    data_in_info->data_in_wr_part) {
                                        return (rv);
                                } else {
                                        write_it = 0;
                                }
                                error = 1;
                        }
                }
                bytes -= (wrblocksz - sz);
        }
        return (error);
}

/*
 * Write zeros for give size.
 *
 * ofd          - output file descriptor
 * buf          - buffer to fill with zeros
 * bytes        - Amount to write
 * wrblocksz    - Write block size
 *
 * return 0 upon success.
 */
static int
write_zeros(int ofd, char *buf, off_t bytes, size_t wrblocksz,
    data_in_t *data_in_info)
{
        int     rv;

        (void) memset(buf, 0, min(bytes, wrblocksz));
        while (bytes > 0) {
                if (bytes < wrblocksz)
                        wrblocksz = bytes;
                rv = write_bytes(ofd, buf, wrblocksz, data_in_info);
                if (rv != 0)
                        return (rv);
                bytes -= wrblocksz;
        }
        return (0);
}

/*
 * To figure out the size of the buffer used to accumulate data from
 * readtape() and to write to the file, we need to determine the largest
 * chunk of data to be written to the file at one time. This is determined
 * based on the following three things:
 *      1) The size of the archived file.
 *      2) The preferred I/O size of the file.
 *      3) If the file is a read-write system attribute file.
 * If the size of the file is less than the preferred I/O size or it's a
 * read-write system attribute file, which must be written in one operation,
 * then set the maximum write size to the size of the archived file.
 * Otherwise, the maximum write size is preferred I/O size.
 */
static int
calc_maxwrite(int ofd, int rw_sysattr, off_t bytes, size_t blocksize)
{
        struct stat tsbuf;
        size_t maxwrite;
        size_t piosize;         /* preferred I/O size */

        if (rw_sysattr || bytes < blocksize) {
                maxwrite = bytes;
        } else {
                if (fstat(ofd, &tsbuf) == 0) {
                        piosize = tsbuf.st_blksize;
                } else {
                        piosize = blocksize;
                }
                maxwrite = min(bytes, piosize);
        }
        return (maxwrite);
}
/*
 * data_copy() and data_copy_with_holes() copy data from the input
 * file to output file descriptor. If ifd is -1, then the input file is
 * the archive file.
 *
 * Parameters
 *      ifd             - Input file descriptor to read from.
 *      ofd             - Output file descriptor of extracted file.
 *      rw_sysattr      - Flag indicating if a file is an extended
 *                      system attribute file.
 *      bytes           - Amount of data (file size) of copy/write.
 *      blocksize       - Amount of data to read at a time from either
 *                      the input file descriptor or from the archive.
 *      data_in_info    - information needed while reading data when
 *                      called by data_in().
 *      holes           - Information of holes in the input file.
 *
 * Return code
 *      0               Success
 *      < 0             An error occurred during the read of the input
 *                      file
 *      > 0             An error occurred during the write of the output
 *                      file descriptor.
 */
static int
data_copy(int ifd, int ofd, int rw_sysattr, off_t bytes,
    size_t blocksize, data_in_t *data_in_info)
{
        char *buf;
        size_t maxwrite;
        int rv;

        /* No data to copy. */
        if (bytes == 0)
                return (0);

        maxwrite = calc_maxwrite(ofd, rw_sysattr, bytes, blocksize);
        buf = e_zalloc(E_EXIT, maxwrite);

        rv = rdwr_bytes(ifd, ofd, buf, bytes, maxwrite,
            blocksize, data_in_info);

        free(buf);
        return (rv);
}

static int
data_copy_with_holes(int ifd, int ofd, int rw_sysattr, off_t bytes,
    size_t blocksize, data_in_t *data_in_info, holes_info_t *holes)
{
        holes_list_t    *hl;
        off_t           curpos, noff, datasize;
        char            *buf;
        size_t          maxwrite;
        int             rv, error;

        if (bytes == 0)
                return (0);

        maxwrite = calc_maxwrite(ofd, rw_sysattr, bytes, blocksize);
        buf = e_zalloc(E_EXIT, maxwrite);

        error = 0;
        curpos = 0;
        for (hl = holes->holes_list; hl != NULL; hl = hl->hl_next) {
                if (curpos != hl->hl_data) {
                        /* adjust output position */
                        noff = lseek(ofd, hl->hl_data, SEEK_SET);
                        if (noff != hl->hl_data) {
                                /*
                                 * Can't seek to the target, try to adjust
                                 * position by filling with zeros.
                                 */
                                datasize = hl->hl_data - curpos;
                                rv = write_zeros(ofd, buf, datasize,
                                    maxwrite, data_in_info);
                                if (rv != 0)
                                        goto errout;
                        }
                        /*
                         * Data is contiguous in the archive, but fragmented
                         * in the regular file, so we also adjust the input
                         * file position in pass mode.
                         */
                        if (Args & OCp) {
                                /* adjust input position */
                                (void) lseek(ifd, hl->hl_data, SEEK_SET);
                        }
                        curpos = hl->hl_data;
                }
                datasize = hl->hl_hole - hl->hl_data;
                if (datasize == 0) {
                        /*
                         * There is a hole at the end of file. To create
                         * such hole, we append one byte, and truncate the
                         * last block. This is necessary because ftruncate(3C)
                         * alone allocates one block on the end of file.
                         */
                        rv = write_zeros(ofd, buf, 1, maxwrite, data_in_info);
                        if (rv != 0)
                                goto errout;
                        (void) ftruncate(ofd, hl->hl_data);
                        break;
                }
                rv = rdwr_bytes(ifd, ofd, buf, datasize, maxwrite,
                    blocksize, data_in_info);
                if (rv != 0) {
errout:
                        /*
                         * Return if we got a read error or in pass mode,
                         * or failed with partial write. Otherwise, we'll
                         * read through the input stream till next file.
                         */
                        if (rv < 0 || (Args & OCp) ||
                            data_in_info->data_in_wr_part) {
                                free(buf);
                                return (rv);
                        }
                        error = 1;
                        hl = hl->hl_next;
                        break;
                }
                curpos += datasize;
        }

        /*
         * We should read through the input data to go to the next
         * header when non-fatal error occured.
         */
        if (error && !(Args & OCp)) {
                data_in_info->data_in_proc_mode = P_SKIP;
                while (hl != NULL) {
                        datasize = hl->hl_hole - hl->hl_data;
                        rv = rdwr_bytes(ifd, ofd, buf, datasize, maxwrite,
                            blocksize, data_in_info);
                        if (rv != 0)
                                break;
                        hl = hl->hl_next;
                }
        }

        free(buf);
        return (error);
}

/*
 * Strip off the sparse file information that is prepended to
 * the compressed sparse file. The information is in the following
 * format:
 *      <prepended info size><SP><orig file size><SP><holes info>
 * where prepended info size is long right justified in 10 bytes.
 * Holesdata consists of the series of offset pairs:
 *      <data offset><SP><hole offset><SP><data offset><SP><hole offset>...
 * prepended info size and original file size have been read in gethdr().
 * We read the rest of holes information here in this function.
 */
static int
read_holesdata(holes_info_t *holes, off_t *fileszp,
    char *nam_p, data_in_t *data_in_info)
{
        char            *holesdata;
        size_t          holesdata_sz;

        /* We've already read the header. */
        holesdata_sz = holes->holesdata_sz - MIN_HOLES_HDRSIZE;

        if ((holesdata = e_zalloc(E_NORMAL, holesdata_sz)) == NULL) {
                msg(ERRN, "Could not allocate memory for "
                    "sparse file information", nam_p);
                return (1);
        }
        /*
         * This function is called only in OCi mode. Therefore,
         * read_bytes() won't fail, and won't return if error occurs in
         * input stream. See rstbuf().
         */
        (void) read_bytes(-1, holesdata, holesdata_sz, CPIOBSZ, data_in_info);
        *fileszp -= holesdata_sz;

        /* The string should be terminated. */
        if (holesdata[holesdata_sz - 1] != '\0') {
invalid:
                free(holesdata);
                msg(ERR, "invalid sparse file information", nam_p);
                return (1);
        }
        if (parse_holesdata(holes, holesdata) != 0)
                goto invalid;

        /* sanity check */
        if (*fileszp != holes->data_size)
                goto invalid;

        free(holesdata);
        return (0);
}

/*
 * data_in:  If proc_mode == P_PROC, bread() the file's data from the archive
 * and write(2) it to the open fdes gotten from openout().  If proc_mode ==
 * P_SKIP, or becomes P_SKIP (due to errors etc), bread(2) the file's data
 * and ignore it.  If the user specified any of the "swap" options (b, s or S),
 * and the length of the file is not appropriate for that action, do not
 * perform the "swap", otherwise perform the action on a buffer by buffer basis.
 * If the CRC header was selected, calculate a running checksum as each buffer
 * is processed.
 */
static void
data_in(int proc_mode)
{
        char *nam_p;
        int pad, rv;
        int error = 0;
        int swapfile = 0;
        int cstatus = 0;
        off_t   filesz;
        data_in_t *data_in_info;

        if (G_p->g_attrnam_p != NULL) {
                nam_p = G_p->g_attrnam_p;
        } else {
                nam_p = G_p->g_nam_p;
        }

        if (((G_p->g_mode & Ftype) == S_IFLNK && proc_mode != P_SKIP) ||
            (Hdr_type == BAR && bar_linkflag == '2' && proc_mode != P_SKIP)) {
                proc_mode = P_SKIP;
                VERBOSE((Args & (OCv | OCV)), nam_p);
        }
        if (Args & (OCb | OCs | OCS)) { /* verify that swapping is possible */
                swapfile = 1;
                if (Args & (OCs | OCb) && G_p->g_filesz % 2) {
                        msg(ERR,
                            "Cannot swap bytes of \"%s\", odd number of bytes",
                            nam_p);
                        swapfile = 0;
                }
                if (Args & (OCS | OCb) && G_p->g_filesz % 4) {
                        msg(ERR,
                            "Cannot swap halfwords of \"%s\", odd number "
                            "of halfwords", nam_p);
                        swapfile = 0;
                }
        }

        data_in_info = e_zalloc(E_EXIT, sizeof (data_in_t));
        data_in_info->data_in_swapfile = swapfile;
        data_in_info->data_in_proc_mode = proc_mode;

        filesz = G_p->g_filesz;

        if (S_ISSPARSE(G_p->g_mode) && G_p->g_holes != NULL) {
                /* We've already read the header in gethdr() */
                filesz -= MIN_HOLES_HDRSIZE;

                /*
                 * Strip rest of the sparse file information. This includes
                 * the data/hole offset pairs which will be used to restore
                 * the holes in the file.
                 */
                if (proc_mode == P_SKIP) {
                        /* holes info isn't necessary to skip file */
                        free_holes_info(G_p->g_holes);
                        G_p->g_holes = NULL;
                } else {
                        rv = read_holesdata(G_p->g_holes, &filesz,
                            nam_p, data_in_info);
                        if (rv != 0) {
                                /*
                                 * We got an error. Skip this file. holes info
                                 * is no longer necessary.
                                 */
                                free_holes_info(G_p->g_holes);
                                G_p->g_holes = NULL;

                                data_in_info->data_in_proc_mode = P_SKIP;
                                error = 1;
                        }
                }
        }

        if (G_p->g_holes != NULL) {
                rv = data_copy_with_holes(-1, Ofile,
                    (G_p->g_attrnam_p == NULL) ? 0 : G_p->g_rw_sysattr,
                    G_p->g_holes->orig_size,
                    CPIOBSZ, data_in_info, G_p->g_holes);

                free_holes_info(G_p->g_holes);
                G_p->g_holes = NULL;
        } else {
                rv = data_copy(-1, Ofile,
                    (G_p->g_attrnam_p == NULL) ? 0 : G_p->g_rw_sysattr,
                    filesz, CPIOBSZ, data_in_info);
        }

        /* This writes out the file from the archive */
        if (rv != 0 || error) {
                errno = data_in_info->data_in_errno;

                if (!error) {
                        msg(data_in_info->data_in_wr_part ? EXTN : ERRN,
                            "Cannot write \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "), nam_p);
                }
                /*
                 * We've failed to write to the file, and input data
                 * has been skiped to the next file. We'll need to restore
                 * the original file, and skip the rest of work.
                 */
                proc_mode = P_SKIP;
                rstfiles(U_KEEP, G_p->g_dirfd);
                cstatus = close(Ofile);
                Ofile = 0;
                if (cstatus != 0) {
                        msg(EXTN, "close error");
                }
        }

        /* we must use g_filesz for the amount of padding */
        pad = (Pad_val + 1 - (G_p->g_filesz & Pad_val)) & Pad_val;
        if (pad != 0) {
                FILL(pad);
                Buffr.b_out_p += pad;
                Buffr.b_cnt -= pad;
        }
        if (proc_mode != P_SKIP) {
                if (Hdr_type == CRC &&
                    Gen.g_cksum != data_in_info->data_in_cksumval) {
                        msg(ERR, "\"%s\" - checksum error", nam_p);
                        rstfiles(U_KEEP, G_p->g_dirfd);
                } else
                        rstfiles(U_OVER, G_p->g_dirfd);
                if (Hdr_type == BAR && data_in_info->data_in_compress_flag) {
                        (void) pclose(data_in_info->data_in_pipef);
                } else {
                        cstatus = close(Ofile);
                }
                Ofile = 0;
                if (cstatus != 0) {
                        msg(EXTN, "close error");
                }
        }
        (void) free(data_in_info);

        VERBOSE((proc_mode != P_SKIP && (Args & (OCv | OCV))),
            (G_p->g_attrparent_p == NULL) ? G_p->g_nam_p : G_p->g_attrpath_p);
        Finished = 1;
}

/*
 * Read regular file. Return number of bytes which weren't read.
 * Upon return, real_filesz will be real file size of input file.
 * When read_exact is specified, read size is adjusted to the given
 * file size.
 */
static off_t
read_file(char *nam_p, off_t file_size, off_t *real_filesz,
    boolean_t read_exact)
{
        int     amount_read;
        off_t   amt_to_read;
        off_t   readsz;

        if (file_size == 0)
                return (0);

        amt_to_read = file_size;
        do {
                if (read_exact && amt_to_read < CPIOBSZ)
                        readsz = amt_to_read;
                else
                        readsz = CPIOBSZ;

                FLUSH(readsz);
                errno = 0;

                if ((amount_read = read(Ifile, Buffr.b_in_p, readsz)) < 0) {
                        msg(EXTN, "Cannot read \"%s%s%s\"",
                            (Gen.g_attrnam_p == NULL) ?
                            nam_p : Gen.g_attrfnam_p,
                            (Gen.g_attrnam_p == NULL) ? "" : Gen.g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (Gen.g_attrnam_p == NULL) ? "" : nam_p);
                        break;
                }

                if (amount_read == 0) {
                        /* got EOF. the file has shrunk */
                        *real_filesz = file_size - amt_to_read;
                        break;
                } else if (amount_read > amt_to_read) {
                        /* the file has grown */
                        *real_filesz = file_size +
                            (amount_read - amt_to_read);
                        amount_read = amt_to_read;
                } else if (amount_read == amt_to_read) {
                        /* the file is the same size */
                        *real_filesz = file_size;
                }

                Buffr.b_in_p += amount_read;
                Buffr.b_cnt += amount_read;

                amt_to_read -= (off_t)amount_read;
                if (!read_exact &&
                    amt_to_read == 0 && amount_read == CPIOBSZ) {
                        /*
                         * If the file size is multiple of CPIOBSZ, we may
                         * be able to read more from the file even though
                         * amt_to_read already gets 0.
                         */
                        FLUSH(CPIOBSZ);
                        amount_read = read(Ifile, Buffr.b_in_p, CPIOBSZ);
                        if (amount_read != 0) {
                                /* the file has grown */
                                *real_filesz = file_size + amount_read;
                        }
                }
        } while (amt_to_read != 0);

        return (amt_to_read);
}

/*
 * Read through the data in files skipping holes.
 */
static off_t
read_compress_holes(char *nam_p, off_t file_size, off_t *real_filesz,
    holes_info_t *holes, int *hole_changed)
{
        off_t           left;
        off_t           datasize, realsz;
        off_t           curpos, npos;
        holes_list_t    *hl = holes->holes_list;

        curpos = 0;
        for (hl = holes->holes_list; hl != NULL; hl = hl->hl_next) {
                datasize = hl->hl_hole - hl->hl_data;

                npos = lseek(Ifile, curpos, SEEK_DATA);
                if (npos == -1 && errno == ENXIO) {
                        /*
                         * No more data. There are two cases.
                         * - we have a hole toward the end of file.
                         * - file has been shrunk, and we've reached EOF.
                         */
                        *real_filesz = lseek(Ifile, 0, SEEK_END);
                        if (hl->hl_data == file_size)
                                return (0);
                        /*
                         * File has been shrunk. Check the amount of data
                         * left.
                         */
                        left = 0;
                        while (hl != NULL) {
                                left += (hl->hl_hole - hl->hl_data);
                                hl = hl->hl_next;
                        }
                        return (left);
                }

                /* found data */
                curpos = npos;
                if (curpos != hl->hl_data) {
                        /*
                         * File has been changed. We shouldn't read data
                         * from different offset since we've already put
                         * the holes data.
                         */
                        *hole_changed = 1;
                        (void) lseek(Ifile, hl->hl_data, SEEK_SET);
                        curpos = hl->hl_data;
                }
                left = read_file(nam_p, datasize, &realsz, B_TRUE);
                if (left != 0) {
                        /* file has been shrunk */
                        *real_filesz = curpos + datasize - left;
                        left = file_size - *real_filesz;
                        return (left);
                }
                curpos += datasize;
        }
        /*
         * We've read exact size of holes. We need to make sure
         * that file hasn't grown by reading from the EOF.
         */
        realsz = 0;
        (void) read_file(nam_p, CPIOBSZ, &realsz, B_FALSE);

        *real_filesz = curpos + realsz;
        return (0);
}

/*
 * data_out:  open(2) the file to be archived, compute the checksum
 * of it's data if the CRC header was specified and write the header.
 * read(2) each block of data and bwrite() it to the archive.  For TARTYP (TAR
 * and USTAR) archives, pad the data with NULLs to the next 512 byte boundary.
 */
static void
data_out(void)
{
        char            *nam_p;
        int             cnt, pad;
        off_t           amt_to_read;
        off_t           real_filesz;
        int             errret = 0;
        int             hole_changed = 0;
        off_t           orig_filesz;
        holes_info_t    *holes = NULL;

        nam_p = G_p->g_nam_p;
        if (Aspec) {
                if (Pflag && aclp != NULL) {
                        char    *secinfo = NULL;
                        int     len = 0;

                        /* append security attributes */
                        if (append_secattr(&secinfo, &len, aclp) == -1) {
                                msg(ERR,
                                    "can create security information");
                        }
                        /* call append_secattr() if more than one */

                        if (len > 0) {
                        /* write ancillary only if there is sec info */
                                write_hdr(ARCHIVE_ACL, (off_t)len);
                                write_ancillary(secinfo, len, B_TRUE);
                        }
                }
                write_hdr(ARCHIVE_NORMAL, (off_t)0);
                rstfiles(U_KEEP, G_p->g_dirfd);
                VERBOSE((Args & (OCv | OCV)), nam_p);
                return;
        }
        if ((G_p->g_mode & Ftype) == S_IFLNK && (Hdr_type !=
            USTAR && Hdr_type != TAR)) { /* symbolic link */
                int size;
                write_hdr(ARCHIVE_NORMAL, (off_t)0);

                FLUSH(G_p->g_filesz);
                errno = 0;

                /* Note that "size" and G_p->g_filesz are the same number */

                if ((size = readlink(nam_p, Buffr.b_in_p, G_p->g_filesz)) <
                    0) {
                        msg(ERRN, "Cannot read symbolic link \"%s\"", nam_p);
                        return;
                }

                /*
                 * Note that it is OK not to add the NUL after the name read by
                 * readlink, because it is not being used subsequently.
                 */

                Buffr.b_in_p += size;
                Buffr.b_cnt += size;
                pad = (Pad_val + 1 - (size & Pad_val)) & Pad_val;
                if (pad != 0) {
                        FLUSH(pad);
                        (void) memset(Buffr.b_in_p, 0, pad);
                        Buffr.b_in_p += pad;
                        Buffr.b_cnt += pad;
                }
                VERBOSE((Args & (OCv | OCV)), nam_p);
                return;
        } else if ((G_p->g_mode & Ftype) == S_IFLNK &&
            (Hdr_type == USTAR || Hdr_type == TAR)) {
                int size;

                /*
                 * G_p->g_filesz is the length of the right-hand side of
                 * the symlink "x -> y".
                 * The tar link field is only NAMSIZ long.
                 */

                if (G_p->g_filesz > NAMSIZ) {
                        msg(ERRN,
                            "Symbolic link too long \"%s\"", nam_p);
                        return;
                }
                if ((size = readlink(nam_p, T_lname, G_p->g_filesz)) < 0) {
                        msg(ERRN,
                            "Cannot read symbolic link \"%s\"", nam_p);
                        return;
                }
                T_lname[size] = '\0';
                G_p->g_filesz = (off_t)0;
                write_hdr(ARCHIVE_NORMAL, (off_t)0);
                VERBOSE((Args & (OCv | OCV)), nam_p);
                return;
        }
        if ((Ifile = openfile(O_RDONLY)) < 0) {
                msg(ERR, "\"%s%s%s\" ?",
                    (Gen.g_attrnam_p == NULL) ? nam_p : Gen.g_attrfnam_p,
                    (Gen.g_attrnam_p == NULL) ? "" : Gen.g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (Gen.g_attrnam_p == NULL) ? "" :
                    (Gen.g_attrparent_p == NULL) ? Gen.g_attrnam_p :
                    Gen.g_attrparent_p);
                return;
        }

        /* save original file size */
        orig_filesz = G_p->g_filesz;

        /*
         * Calculate the new compressed file size of a sparse file
         * before any of the header information is written
         * to the archive.
         */
        if (Compress_sparse && S_ISREG(G_p->g_mode)) {
                /*
                 * If the file being processed is a sparse file, gather the
                 * hole information and the compressed file size.
                 * G_p->g_filesz will need to be changed to be the size of
                 * the compressed sparse file plus the the size of the hole
                 * information that will be prepended to the compressed file
                 * in the archive.
                 */
                holes = get_holes_info(Ifile, G_p->g_filesz, B_FALSE);
                if (holes != NULL)
                        G_p->g_filesz = holes->holesdata_sz + holes->data_size;

                if (G_p->g_filesz > Max_offset) {
                        msg(ERR, "%s%s%s: too large to archive "
                            "in current mode",
                            G_p->g_nam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" :
                            ((G_p->g_attrparent_p == NULL) ?
                            G_p->g_attrnam_p:
                            G_p->g_attrpath_p));

                        (void) close(Ifile);
                        if (holes != NULL)
                                free_holes_info(holes);
                        return; /* do not archive if it's too big */
                }
        }

        /*
         * Dump extended attribute header.
         */

        if (Gen.g_attrnam_p != NULL) {
                write_xattr_hdr();
        }

        if (Hdr_type == CRC) {
                uint_t csum = cksum(CRC, 0, &errret);
                if (errret != 0) {
                        G_p->g_cksum = UINT_MAX;
                        msg(POST, "\"%s%s%s\" skipped",
                            (Gen.g_attrnam_p == NULL) ?
                            nam_p : Gen.g_attrfnam_p,
                            (Gen.g_attrnam_p == NULL) ? "" : Gen.g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (Gen.g_attrnam_p == NULL) ? "" : nam_p);
                        if (holes != NULL)
                                free_holes_info(holes);
                        (void) close(Ifile);
                        return;
                }
                G_p->g_cksum = csum;
        } else {
                G_p->g_cksum = 0;
        }

        /*
         * ACL has been retrieved in getname().
         */
        if (Pflag) {
                char    *secinfo = NULL;
                int     len = 0;

                /* append security attributes */
                if ((append_secattr(&secinfo, &len, aclp)) == -1)
                        msg(ERR, "can create security information");

                /* call append_secattr() if more than one */

                if (len > 0) {
                /* write ancillary only if there is sec info */
                        write_hdr(ARCHIVE_ACL, (off_t)len);
                        write_ancillary(secinfo, len, B_TRUE);
                }
        }

        if (holes != NULL) {
                /*
                 * Write the header info with a modified c_mode field to
                 * indicate a compressed sparse file is being archived,
                 * as well as the new file size, including the size of the
                 * compressed file as well as all the prepended data.
                 */
                write_hdr(ARCHIVE_SPARSE, (off_t)0);
                /* Prepend sparse file info */
                write_ancillary(holes->holesdata,
                    holes->holesdata_sz, B_FALSE);
        } else {
                write_hdr(ARCHIVE_NORMAL, (off_t)0);
        }

        real_filesz = 0;

        if (holes != NULL) {
                amt_to_read = read_compress_holes(nam_p, G_p->g_filesz,
                    &real_filesz, holes, &hole_changed);
        } else {
                amt_to_read = read_file(nam_p, G_p->g_filesz,
                    &real_filesz, B_FALSE);
        }

        while (amt_to_read > 0) {
                cnt = (amt_to_read > CPIOBSZ) ? CPIOBSZ : (int)amt_to_read;
                FLUSH(cnt);
                (void) memset(Buffr.b_in_p, 0, cnt);
                Buffr.b_in_p += cnt;
                Buffr.b_cnt += cnt;
                amt_to_read -= cnt;
        }

        pad = (Pad_val + 1 - (G_p->g_filesz & Pad_val)) & Pad_val;
        if (pad != 0) {
                FLUSH(pad);
                (void) memset(Buffr.b_in_p, 0, pad);
                Buffr.b_in_p += pad;
                Buffr.b_cnt += pad;
        }

        if (hole_changed == 1) {
                msg(ERR,
                    "File data and hole offsets of \"%s%s%s\" have changed",
                    (Gen.g_attrnam_p == NULL) ?
                    G_p->g_nam_p : Gen.g_attrfnam_p,
                    (Gen.g_attrnam_p == NULL) ? "" : Gen.g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (Gen.g_attrnam_p == NULL) ? "" : G_p->g_nam_p);
        }
        if (real_filesz > orig_filesz) {
                msg(ERR, "File size of \"%s%s%s\" has increased by %lld",
                    (Gen.g_attrnam_p == NULL) ?
                    G_p->g_nam_p : Gen.g_attrfnam_p,
                    (Gen.g_attrnam_p == NULL) ? "" : Gen.g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (Gen.g_attrnam_p == NULL) ? "" : G_p->g_nam_p,
                    (real_filesz - orig_filesz));
        }
        if (real_filesz < orig_filesz) {
                msg(ERR, "File size of \"%s%s%s\" has decreased by %lld",
                    (Gen.g_attrnam_p == NULL) ?
                    G_p->g_nam_p : Gen.g_attrfnam_p,
                    (Gen.g_attrnam_p == NULL) ? "" : Gen.g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (Gen.g_attrnam_p == NULL) ? "" : G_p->g_nam_p,
                    (orig_filesz - real_filesz));
        }

        if (holes != NULL)
                free_holes_info(holes);

        (void) close(Ifile);
        rstfiles(U_KEEP, G_p->g_dirfd);
        VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
}

/*
 * data_pass:  If not a special file (Aspec), open(2) the file to be
 * transferred, read(2) each block of data and write(2) it to the output file
 * Ofile, which was opened in file_pass().
 */
static void
data_pass(void)
{
        int rv;
        int cstatus;
        char *namep = Nam_p;
        holes_info_t *holes = NULL;
        data_in_t *data_in_info;

        if (G_p->g_attrnam_p != NULL) {
                namep = G_p->g_attrnam_p;
        }
        if (Aspec) {
                rstfiles(U_KEEP, G_p->g_passdirfd);
                cstatus = close(Ofile);
                Ofile = 0;
                VERBOSE((Args & (OCv | OCV)), Nam_p);
                if (cstatus != 0) {
                        msg(EXTN, "close error");
                }
                return;
        }
        if ((Ifile = openat(G_p->g_dirfd, get_component(namep), 0)) < 0) {
                msg(ERRN, "Cannot open \"%s%s%s\", skipped",
                    (G_p->g_attrnam_p == NULL) ? Nam_p : G_p->g_attrfnam_p,
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p);
                rstfiles(U_KEEP, G_p->g_passdirfd);
                cstatus = close(Ofile);
                Ofile = 0;
                if (cstatus != 0) {
                        msg(EXTN, "close error");
                }
                return;
        }

        data_in_info = e_zalloc(E_EXIT, sizeof (data_in_t));
        data_in_info->data_in_proc_mode = P_PROC;

        if (S_ISREG(G_p->g_mode))
                holes = get_holes_info(Ifile, G_p->g_filesz, B_TRUE);

        if (holes != NULL) {
                rv = data_copy_with_holes(Ifile, Ofile,
                    (G_p->g_attrnam_p == NULL) ? 0 : G_p->g_rw_sysattr,
                    G_p->g_filesz, Bufsize, data_in_info, holes);

                free_holes_info(holes);
        } else {
                rv = data_copy(Ifile, Ofile,
                    (G_p->g_attrnam_p == NULL) ? 0 : G_p->g_rw_sysattr,
                    G_p->g_filesz, Bufsize, data_in_info);
        }

        if (rv < 0) {
                /* read error or unexpected EOF */
                if (data_in_info->data_in_rd_eof) {
                        /*
                         * read has reached EOF unexpectedly, but this isn't
                         * an error since it's the latest shape of the file.
                         */
                        msg(EPOST, "File size of \"%s%s%s\" has decreased",
                            (G_p->g_attrnam_p == NULL) ?
                            Nam_p : G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p);

                        /* It's not error. We'll use the new file */
                        rv = 0;
                } else {
                        /* read error */
                        msg(ERRN, "Cannot read \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ?
                            Nam_p : G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p);
                }
        } else if (rv > 0) {
                /* write error */
                if (Do_rename) {
                        msg(ERRN, "Cannot write \"%s%s%s\"", Over_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : Over_p);
                } else {
                        msg(ERRN, "Cannot write \"%s%s%s\"",
                            Fullnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p);
                }
        }

        free(data_in_info);

        if (rv == 0) {
                rstfiles(U_OVER, G_p->g_passdirfd);
        } else {
                rstfiles(U_KEEP, G_p->g_passdirfd);
        }

        (void) close(Ifile);
        cstatus = close(Ofile);
        Ofile = 0;
        if (cstatus != 0) {
                msg(EXTN, "close error");
        }
        VERBOSE((Args & (OCv | OCV)), Fullnam_p);
        Finished = 1;
}

/*
 * file_in:  Process an object from the archive.  If a TARTYP (TAR or USTAR)
 * archive and g_nlink == 1, link this file to the file name in t_linkname
 * and return.  Handle linked files in one of two ways.  If Onecopy == 0, this
 * is an old style (binary or -c) archive, create and extract the data for the
 * first link found, link all subsequent links to this file and skip their data.
 * If Oncecopy == 1, save links until all have been processed, and then
 * process the links first to last checking their names against the patterns
 * and/or asking the user to rename them.  The first link that is accepted
 * for xtraction is created and the data is read from the archive.
 * All subsequent links that are accepted are linked to this file.
 */
static void
file_in(void)
{
        struct Lnk *l_p, *tl_p;
        int lnkem = 0, cleanup = 0;
        int proc_file;
        struct Lnk *ttl_p;
        int typeflag;
        char savacl;
        int cwd;

        G_p = &Gen;

        /*
         * Now that we've read the extended header,
         * determine if we should restore attributes.
         * Don't restore the attribute if we are extracting
         * a file from an archive (as opposed to doing a table of
         * contents) and any of the following are true:
         * 1. neither -@ or -/ was specified.
         * 2. -@ was specified, -/ wasn't specified, and we're
         * processing a hidden attribute directory of an attribute
         * or we're processing a read-write system attribute file.
         * 3.  -@ wasn't specified, -/ was specified, and the file
         * we're processing it not a read-write system attribute file,
         * or we're processing the hidden attribute directory of an
         * attribute.
         *
         * We always process the attributes if we're just generating
         * generating a table of contents, or if both -@ and -/ were
         * specified.
         */
        if (G_p->g_attrnam_p != NULL) {
                if (((Args & OCt) == 0) &&
                    ((!Atflag && !SysAtflag) ||
                    (Atflag && !SysAtflag && ((G_p->g_attrparent_p != NULL) ||
                    G_p->g_rw_sysattr)) ||
                    (!Atflag && SysAtflag && ((G_p->g_attrparent_p != NULL) ||
                    !G_p->g_rw_sysattr)))) {
                        proc_file = F_SKIP;
                        data_in(P_SKIP);
                        return;
                }
        }

        /*
         * Open target directory if this isn't a skipped file
         * and g_nlink == 1
         *
         * Links are handled further down in this function.
         */

        proc_file = ckname(0);

        if (proc_file == F_SKIP && G_p->g_nlink == 1) {
                /*
                 * Normally ckname() prints out the file as a side
                 * effect except for table of contents listing
                 * when its parameter is zero and Onecopy isn't
                 * Zero.  Due to this we need to force the name
                 * to be printed here.
                 */
                if (Onecopy == 1) {
                        VERBOSE((Args & OCt), G_p->g_nam_p);
                }
                data_in(P_SKIP);
                return;
        }

        if (proc_file != F_SKIP && open_dirfd() != 0) {
                data_in(P_SKIP);
                return;
        }

        if (Hdr_type == BAR) {
                bar_file_in();
                close_dirfd();
                return;
        }

        /*
         * For archives in USTAR format, the files are extracted according
         * to the typeflag.
         */
        if (Hdr_type == USTAR || Hdr_type == TAR) {
                typeflag = Thdr_p->tbuf.t_typeflag;
                if (G_p->g_nlink == 1) {                /* hard link */
                        if (proc_file != F_SKIP) {
                                int i;
                                char lname[NAMSIZ+1];
                                (void) memset(lname, '\0', sizeof (lname));

                                (void) strncpy(lname, Thdr_p->tbuf.t_linkname,
                                    NAMSIZ);
                                for (i = 0; i <= NAMSIZ && lname[i] != 0; i++)
                                        ;

                                lname[i] = 0;
                                (void) creat_lnk(G_p->g_dirfd,
                                    &lname[0], G_p->g_nam_p);
                        }
                        close_dirfd();
                        return;
                }
                if (typeflag == '3' || typeflag == '4' || typeflag == '5' ||
                    typeflag == '6') {
                        if (proc_file != F_SKIP &&
                            creat_spec(G_p->g_dirfd) > 0) {
                                VERBOSE((Args & (OCv | OCV)),
                                    (G_p->g_attrparent_p == NULL) ?
                                    G_p->g_nam_p : G_p->g_attrpath_p);
                        }
                        close_dirfd();
                        return;
                } else if (Adir || Aspec) {
                        if ((proc_file == F_SKIP) ||
                            (Ofile = openout(G_p->g_dirfd)) < 0) {
                                data_in(P_SKIP);
                        } else {
                                data_in(P_PROC);
                        }
                        close_dirfd();
                        return;
                }
        }

        if (Adir) {
                if (proc_file != F_SKIP && creat_spec(G_p->g_dirfd) > 0) {
                        VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
                }
                close_dirfd();
                if (Onecopy == 1) {
                        VERBOSE((Args & OCt), G_p->g_nam_p);
                }
                return;
        }
        if (G_p->g_nlink == 1 || (Hdr_type == TAR ||
            Hdr_type == USTAR)) {
                if (Aspec) {
                        if (proc_file != F_SKIP && creat_spec(G_p->g_dirfd) > 0)
                                VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
                } else {
                        if ((proc_file == F_SKIP) ||
                            (Ofile = openout(G_p->g_dirfd)) < 0) {
                                data_in(P_SKIP);
                        } else {
                                data_in(P_PROC);
                        }
                }
                close_dirfd();
                return;
        }
        close_dirfd();

        tl_p = add_lnk(&ttl_p);
        l_p = ttl_p;
        if (l_p->L_cnt == l_p->L_gen.g_nlink)
                cleanup = 1;
        if (!Onecopy || G_p->g_attrnam_p != NULL) {
                lnkem = (tl_p != l_p) ? 1 : 0;
                G_p = &tl_p->L_gen;
                if (proc_file == F_SKIP) {
                        data_in(P_SKIP);
                } else {
                        if (open_dirfd() != 0)
                                return;
                        if (!lnkem) {
                                if (Aspec) {
                                        if (creat_spec(G_p->g_dirfd) > 0)
                                                VERBOSE((Args & (OCv | OCV)),
                                                    G_p->g_nam_p);
                                } else if ((Ofile =
                                    openout(G_p->g_dirfd)) < 0) {
                                        data_in(P_SKIP);
                                        close_dirfd();
                                        reclaim(l_p);
                                } else {
                                        data_in(P_PROC);
                                        close_dirfd();
                                }
                        } else {
                                /*
                                 * Are we linking an attribute?
                                 */
                                cwd = -1;
                                if (l_p->L_gen.g_attrnam_p != NULL) {
                                        (void) strcpy(Lnkend_p,
                                            l_p->L_gen.g_attrnam_p);
                                        (void) strcpy(Full_p,
                                            tl_p->L_gen.g_attrnam_p);
                                        cwd = save_cwd();
                                        (void) fchdir(G_p->g_dirfd);
                                } else {
                                        (void) strcpy(Lnkend_p,
                                            l_p->L_gen.g_nam_p);
                                        (void) strcpy(Full_p,
                                            tl_p->L_gen.g_nam_p);
                                }
                                (void) creat_lnk(G_p->g_dirfd,
                                    Lnkend_p, Full_p);
                                data_in(P_SKIP);
                                close_dirfd();
                                l_p->L_lnk_p = NULL;
                                free(tl_p->L_gen.g_nam_p);
                                free(tl_p);
                                if (cwd != -1)
                                        rest_cwd(cwd);
                        }
                }
        } else { /* Onecopy */
                if (tl_p->L_gen.g_filesz)
                        cleanup = 1;
                if (!cleanup) {
                        close_dirfd();
                        return; /* don't do anything yet */
                }
                tl_p = l_p;
                /*
                 * ckname will clear aclchar. We need to keep aclchar for
                 * all links.
                 */
                savacl = aclchar;
                while (tl_p != NULL) {
                        G_p = &tl_p->L_gen;
                        aclchar = savacl;
                        if ((proc_file = ckname(1)) != F_SKIP) {
                                if (open_dirfd() != 0) {
                                        return;
                                }
                                if (l_p->L_data) {
                                        (void) creat_lnk(G_p->g_dirfd,
                                            l_p->L_gen.g_nam_p,
                                            G_p->g_nam_p);
                                } else if (Aspec) {
                                        (void) creat_spec(G_p->g_dirfd);
                                        l_p->L_data = 1;
                                        VERBOSE((Args & (OCv | OCV)),
                                            G_p->g_nam_p);
                                } else if ((Ofile =
                                    openout(G_p->g_dirfd)) < 0) {
                                        proc_file = F_SKIP;
                                } else {
                                        data_in(P_PROC);
                                        l_p->L_data = 1;
                                }
                        } /* (proc_file = ckname(1)) != F_SKIP */

                        tl_p = tl_p->L_lnk_p;

                        close_dirfd();

                        if (proc_file == F_SKIP && !cleanup) {
                                tl_p->L_nxt_p = l_p->L_nxt_p;
                                tl_p->L_bck_p = l_p->L_bck_p;
                                l_p->L_bck_p->L_nxt_p = tl_p;
                                l_p->L_nxt_p->L_bck_p = tl_p;
                                free(l_p->L_gen.g_nam_p);
                                free(l_p);
                        }
                } /* tl_p->L_lnk_p != NULL */
                if (l_p->L_data == 0) {
                        data_in(P_SKIP);
                }
        }
        if (cleanup) {
                reclaim(l_p);
        }
}

/*
 * file_out:  If the current file is not a special file (!Aspec) and it
 * is identical to the archive, skip it (do not archive the archive if it
 * is a regular file).  If creating a TARTYP (TAR or USTAR) archive, the first
 * time a link to a file is encountered, write the header and file out normally.
 * Subsequent links to this file put this file name in their t_linkname field.
 * Otherwise, links are handled in one of two ways, for the old headers
 * (i.e. binary and -c), linked files are written out as they are encountered.
 * For the new headers (ASC and CRC), links are saved up until all the links
 * to each file are found.  For a file with n links, write n - 1 headers with
 * g_filesz set to 0, write the final (nth) header with the correct g_filesz
 * value and write the data for the file to the archive.
 */
static
int
file_out(void)
{
        struct Lnk *l_p, *tl_p;
        int cleanup = 0;
        struct Lnk *ttl_p;

        G_p = &Gen;
        if (!Aspec && IDENT(SrcSt, ArchSt))
                return (1); /* do not archive the archive if it's a reg file */
        /*
         * If compressing sparse files, wait until the compressed file size
         * is known to check if file size is too big.
         */
        if (Compress_sparse == 0 && G_p->g_filesz > Max_offset) {
                msg(ERR, "%s%s%s: too large to archive in current mode",
                    G_p->g_nam_p,
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (G_p->g_attrnam_p == NULL) ? "" :
                    ((G_p->g_attrparent_p == NULL) ? G_p->g_attrnam_p:
                    G_p->g_attrpath_p));
                return (1); /* do not archive if it's too big */
        }
        if (Hdr_type == TAR || Hdr_type == USTAR) { /* TAR and USTAR */
                if (Adir) {
                        if (Gen.g_attrnam_p != NULL) {
                                write_xattr_hdr();
                        }
                        write_hdr(ARCHIVE_NORMAL, 0);
                        return (0);
                }
                if (G_p->g_nlink == 1) {
                        data_out();
                        return (0);
                }
                tl_p = add_lnk(&ttl_p);
                l_p = ttl_p;
                if (tl_p == l_p) { /* first link to this file encountered */
                        data_out();
                        return (0);
                }
                (void) strncpy(T_lname, l_p->L_gen.g_nam_p,
                    l_p->L_gen.g_namesz);

                /*
                 * check if linkname is greater than 100 characters
                 */
                if (strlen(T_lname) > NAMSIZ) {
                        msg(EPOST, "cpio: %s: linkname %s is greater than %d",
                            G_p->g_nam_p, T_lname, NAMSIZ);
                        return (1);
                }

                write_hdr(ARCHIVE_NORMAL, (off_t)0);
                VERBOSE((Args & (OCv | OCV)), tl_p->L_gen.g_nam_p);

                /* find the lnk entry in sublist, unlink it, and free it */
                for (; ttl_p->L_lnk_p != NULL;
                    ttl_p = ttl_p->L_lnk_p) {
                        if (ttl_p->L_lnk_p == tl_p) {
                                ttl_p->L_lnk_p = tl_p->L_lnk_p;
                                free(tl_p->L_gen.g_nam_p);
                                free(tl_p);
                                break;
                        }
                }

                return (0);
        }
        if (Adir) {
                /*
                 * ACL has been retrieved in getname().
                 */
                if (Pflag) {
                        char    *secinfo = NULL;
                        int     len = 0;

                        /* append security attributes */
                        if ((append_secattr(&secinfo, &len, aclp)) == -1)
                                msg(ERR, "can create security information");

                        /* call append_secattr() if more than one */

                        if (len > 0) {
                        /* write ancillary */
                                write_hdr(ARCHIVE_ACL, (off_t)len);
                                write_ancillary(secinfo, len, B_TRUE);
                        }
                }

                if (Gen.g_attrnam_p != NULL) {
                        write_xattr_hdr();
                }
                write_hdr(ARCHIVE_NORMAL, (off_t)0);
                VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
                return (0);
        }
        if (G_p->g_nlink == 1) {
                data_out();
                return (0);
        } else {
                tl_p = add_lnk(&ttl_p);
                l_p = ttl_p;

                if (l_p->L_cnt == l_p->L_gen.g_nlink)
                        cleanup = 1;
                else if (Onecopy && G_p->g_attrnam_p == NULL) {
                        return (0); /* don't process data yet */
                }
        }
        if (Onecopy && G_p->g_attrnam_p == NULL) {
                tl_p = l_p;
                while (tl_p->L_lnk_p != NULL) {
                        G_p = &tl_p->L_gen;
                        G_p->g_filesz = (off_t)0;
                        /* one link with the acl is sufficient */
                        write_hdr(ARCHIVE_NORMAL, (off_t)0);
                        VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
                        tl_p = tl_p->L_lnk_p;
                }
                G_p = &tl_p->L_gen;
                if (open_dirfd() != 0)
                        return (1);
        }
        /* old style: has acl and data for every link */
        data_out();
        if (cleanup)
                reclaim(l_p);
        return (0);
}

/*
 * Verify the underlying file system supports the attribute type.
 * Only archive extended attribute files when '-@' was specified.
 * Only archive system extended attribute files if '-/' was specified.
 */
#if defined(O_XATTR)
static attr_status_t
verify_attr_support(char *filename, int attrflg, arc_action_t actflag,
    int *ext_attrflg)
{
        /*
         * Verify extended attributes are supported/exist.  We only
         * need to check if we are processing a base file, not an
         * extended attribute.
         */
        if (attrflg) {
                *ext_attrflg = (pathconf(filename, (actflag == ARC_CREATE) ?
                    _PC_XATTR_EXISTS : _PC_XATTR_ENABLED) == 1);
        }
        if (Atflag) {
#if defined(_PC_SATTR_ENABLED)
                if (!*ext_attrflg) {
                        if (SysAtflag) {
                                /* Verify system attributes are supported */
                                if (sysattr_support(filename,
                                    (actflag == ARC_CREATE) ?_PC_SATTR_EXISTS :
                                    _PC_SATTR_ENABLED) != 1) {
                                        return (ATTR_SATTR_ERR);
                                }
                        } else
                                return (ATTR_XATTR_ERR);
#else
                                return (ATTR_XATTR_ERR);
#endif  /* _PC_SATTR_ENABLED */
                }

#if defined(_PC_SATTR_ENABLED)
        } else if (SysAtflag) {
                /* Verify system attributes are supported */
                if (sysattr_support(filename, (actflag == ARC_CREATE) ?
                    _PC_SATTR_EXISTS : _PC_SATTR_ENABLED) != 1) {
                        return (ATTR_SATTR_ERR);
        }
#endif  /* _PC_SATTR_ENABLED */
        } else {
                return (ATTR_SKIP);
        }

        return (ATTR_OK);
}
#endif

#if defined(O_XATTR)
/*
 * Verify the attribute, attrname, is an attribute we want to restore.
 * Never restore read-only system attribute files.  Only restore read-write
 * system attributes files when -/ was specified, and only traverse into
 * the 2nd level attribute directory containing only system attributes if
 * -@ was specified.  This keeps us from archiving
 *      <attribute name>/<read-write system attribute file>
 * when -/ was specified without -@.
 *
 * attrname             - attribute file name
 * attrparent           - attribute's parent name within the base file's
 *                      attribute digrectory hierarchy
 * arc_rwsysattr        - flag that indicates that read-write system attribute
 *                      file should be archived as it contains other than
 *                      the default system attributes.
 * rw_sysattr           - on return, flag will indicate if attrname is a
 *                      read-write system attribute file.
 */
static attr_status_t
verify_attr(char *attrname, char *attrparent, int arc_rwsysattr,
    int *rw_sysattr)
{
#if defined(_PC_SATTR_ENABLED)
        int     attr_supported;

        /* Never restore read-only system attribute files */
        if ((attr_supported = sysattr_type(attrname)) == _RO_SATTR) {
                *rw_sysattr = 0;
                return (ATTR_SKIP);
        } else {
                *rw_sysattr = (attr_supported == _RW_SATTR);
        }

        /*
         * Don't archive a read-write system attribute file if
         * it contains only the default system attributes.
         */
        if (*rw_sysattr && !arc_rwsysattr) {
                return (ATTR_SKIP);
        }

#else
        /* Never restore read-only system attribute files */
        if ((*rw_sysattr = is_sysattr(attrname)) == 1) {
                return (ATTR_SKIP);
        }
#endif  /* _PC_SATTR_ENABLED */

        /*
         * Only restore read-write system attribute files
         * when -/ was specified.  Only restore extended
         * attributes when -@ was specified.
         */
        if (Atflag) {
                if (!SysAtflag) {
                        /*
                         * Only archive/restore the hidden directory "." if
                         * we're processing the top level hidden attribute
                         * directory.  We don't want to process the
                         * hidden attribute directory of the attribute
                         * directory that contains only extended system
                         * attributes.
                         */
                        if (*rw_sysattr || (Hiddendir &&
                            (attrparent != NULL))) {
                                return (ATTR_SKIP);
                        }
                }
        } else if (SysAtflag) {
                /*
                 * Only archive/restore read-write extended system attribute
                 * files of the base file.
                 */
                if (!*rw_sysattr || (attrparent != NULL)) {
                        return (ATTR_SKIP);
                }
        } else {
                return (ATTR_SKIP);
        }

        return (ATTR_OK);
}
#endif

#if defined(O_XATTR)
static int
retry_open_attr(int pdirfd, int cwd, char *fullname, char *pattr, char *name,
    int oflag, mode_t mode)
{
        int dirfd;
        int ofilefd = -1;
        struct timeval times[2];
        mode_t newmode;
        struct stat parentstat;
        acl_t *aclp = NULL;
        int error;

        /*
         * We couldn't get to attrdir. See if its
         * just a mode problem on the parent file.
         * for example: a mode such as r-xr--r--
         * on a ufs file system without extended
         * system attribute support won't let us
         * create an attribute dir if it doesn't
         * already exist, and on a ufs file system
         * with extended system attribute support
         * won't let us open the attribute for
         * write.
         *
         * If file has a non-trivial ACL, then save it
         * off so that we can place it back on after doing
         * chmod's.
         */
        if ((dirfd = openat(cwd, (pattr == NULL) ? fullname : pattr,
            O_RDONLY)) == -1) {
                return (-1);
        }
        if (fstat(dirfd, &parentstat) == -1) {
                msg(ERRN, "Cannot stat %sfile %s",
                    (pdirfd == -1) ? "" : gettext("parent of "),
                    (pdirfd == -1) ? fullname : name);
                (void) close(dirfd);
                return (-1);
        }
        if ((error = facl_get(dirfd, ACL_NO_TRIVIAL, &aclp)) != 0) {
                msg(ERRN, "Failed to retrieve ACL on %sfile %s",
                    (pdirfd == -1) ? "" : gettext("parent of "),
                    (pdirfd == -1) ? fullname : name);
                (void) close(dirfd);
                return (-1);
        }

        newmode = S_IWUSR | parentstat.st_mode;
        if (fchmod(dirfd, newmode) == -1) {
                msg(ERRN, "Cannot change mode of %sfile %s to %o",
                    (pdirfd == -1) ? "" : gettext("parent of "),
                    (pdirfd == -1) ? fullname : name, newmode);
                if (aclp)
                        acl_free(aclp);
                (void) close(dirfd);
                return (-1);
        }


        if (pdirfd == -1) {
                /*
                 * We weren't able to create the attribute directory before.
                 * Now try again.
                 */
                ofilefd = attropen(fullname, ".", oflag);
        } else {
                /*
                 * We weren't able to create open the attribute before.
                 * Now try again.
                 */
                ofilefd = openat(pdirfd, name, oflag, mode);
        }

        /*
         * Put mode back to original
         */
        if (fchmod(dirfd, parentstat.st_mode) == -1) {
                msg(ERRN, "Cannot restore permissions of %sfile %s to %o",
                    (pdirfd == -1) ? "" : gettext("parent of "),
                    (pdirfd == -1) ? fullname : name, newmode);
        }

        if (aclp) {
                error = facl_set(dirfd, aclp);
                if (error) {
                        msg(ERRN, "failed to set acl entries on %sfile %s\n",
                            (pdirfd == -1) ? "" : gettext("parent of "),
                            (pdirfd == -1) ? fullname : name);
                }
                acl_free(aclp);
        }

        /*
         * Put back time stamps
         */

        times[0].tv_sec = parentstat.st_atime;
        times[0].tv_usec = 0;
        times[1].tv_sec = parentstat.st_mtime;
        times[1].tv_usec = 0;

        (void) futimesat(cwd, (pattr == NULL) ? fullname : pattr, times);

        (void) close(dirfd);

        return (ofilefd);
}
#endif

#if defined(O_XATTR)
/*
 * Recursively open attribute directories until the attribute directory
 * containing the specified attribute, attrname, is opened.
 *
 * Currently, only 2 directory levels of attributes are supported, (i.e.,
 * extended system attributes on extended attributes).  The following are
 * the possible input combinations:
 *      1.  Open the attribute directory of the base file (don't change
 *          into it).
 *              attr_parent = NULL
 *              attrname = '.'
 *      2.  Open the attribute directory of the base file and change into it.
 *              attr_parent = NULL
 *              attrname = <attr> | <sys_attr>
 *      3.  Open the attribute directory of the base file, change into it,
 *          then recursively call open_attr_dir() to open the attribute's
 *          parent directory (don't change into it).
 *              attr_parent = <attr>
 *              attrname = '.'
 *      4.  Open the attribute directory of the base file, change into it,
 *          then recursively call open_attr_dir() to open the attribute's
 *          parent directory and change into it.
 *              attr_parent = <attr>
 *              attrname = <attr> | <sys_attr>
 *
 * An attribute directory will be opened only if the underlying file system
 * supports the attribute type, and if the command line specifications
 * (f_extended_attr and f_sys_attr) enable the processing of the attribute
 * type.
 *
 * On succesful return, attr_parentfd will be the file descriptor of the
 * opened attribute directory.  In addition, if the attribute is a read-write
 * extended system attribute, rw_sysattr will be set to 1, otherwise
 * it will be set to 0.
 *
 * Possible return values:
 *      ATTR_OK         Successfully opened and, if needed, changed into the
 *                      attribute directory containing attrname.
 *      ATTR_SKIP       The command line specifications don't enable the
 *                      processing of the attribute type.
 *      ATTR_CHDIR_ERR  An error occurred while trying to change into an
 *                      attribute directory.
 *      ATTR_OPEN_ERR   An error occurred while trying to open an
 *                      attribute directory.
 *      ATTR_XATTR_ERR  The underlying file system doesn't support extended
 *                      attributes.
 *      ATTR_SATTR_ERR  The underlying file system doesn't support extended
 *                      system attributes.
 */
static int
open_attr_dir(char *attrname, char *dirp, int cwd, char *attr_parent,
    int *attr_parentfd, int *rw_sysattr)
{
        attr_status_t   rc;
        int             firsttime = (*attr_parentfd == -1);
        int             saveerrno;
        int             ext_attr;

        /*
         * open_attr_dir() was recursively called (input combination number 4),
         * close the previously opened file descriptor as we've already changed
         * into it.
         */
        if (!firsttime) {
                (void) close(*attr_parentfd);
                *attr_parentfd = -1;
        }

        /*
         * Verify that the underlying file system supports the restoration
         * of the attribute.
         */
        if ((rc = verify_attr_support(dirp, firsttime, ARC_RESTORE,
            &ext_attr)) != ATTR_OK) {
                return (rc);
        }

        /* Open the base file's attribute directory */
        if ((*attr_parentfd = attropen(dirp, ".", O_RDONLY)) == -1) {
                /*
                 * Save the errno from the attropen so it can be reported
                 * if the retry of the attropen fails.
                 */
                saveerrno = errno;
                if ((*attr_parentfd = retry_open_attr(-1, cwd, dirp,
                    NULL, ".", O_RDONLY, 0)) == -1) {
                        (void) close(*attr_parentfd);
                        *attr_parentfd = -1;
                        errno = saveerrno;
                        return (ATTR_OPEN_ERR);
                }
        }

        /*
         * Change into the parent attribute's directory unless we are
         * processing the hidden attribute directory of the base file itself.
         */
        if ((Hiddendir == 0) || (firsttime && (attr_parent != NULL))) {
                if (fchdir(*attr_parentfd) != 0) {
                        saveerrno = errno;
                        (void) close(*attr_parentfd);
                        *attr_parentfd = -1;
                        errno = saveerrno;
                        return (ATTR_CHDIR_ERR);
                }
        }

        /* Determine if the attribute should be processed */
        if ((rc = verify_attr(attrname, attr_parent, 1,
            rw_sysattr)) != ATTR_OK) {
                saveerrno = errno;
                (void) close(*attr_parentfd);
                *attr_parentfd = -1;
                errno = saveerrno;
                return (rc);
        }

        /*
         * If the attribute is an extended system attribute of an attribute
         * (i.e., <attr>/<sys_attr>), then recursively call open_attr_dir() to
         * open the attribute directory of the parent attribute.
         */
        if (firsttime && (attr_parent != NULL)) {
                return (open_attr_dir(attrname, attr_parent, *attr_parentfd,
                    attr_parent, attr_parentfd, rw_sysattr));
        }

        return (ATTR_OK);
}
#endif

/*
 * file_pass:  If the -l option is set (link files when possible), and the
 * source and destination file systems are the same, link the source file
 * (G_p->g_nam_p) to the destination file (Fullnam) and return.  If not a
 * linked file, transfer the data.  Otherwise, the first link to a file
 * encountered is transferred normally and subsequent links are linked to it.
 */

static int
file_pass(void)
{
        struct Lnk *l_p, *tl_p;
        struct Lnk *ttl_p;
        char *save_name;
        int size;
        int cwd;
        char *lfrom, *lto;

        G_p = &Gen;

        if (Adir && !(Args & OCd)) {
                msg(ERR, "Use -d option to copy \"%s\"", G_p->g_nam_p);
                return (FILE_PASS_ERR);
        }

        save_name = G_p->g_nam_p;

        while (*(G_p->g_nam_p) == '/') {
                G_p->g_nam_p++;
        }

        (void) strcpy(Full_p, (G_p->g_attrfnam_p == NULL) ?
            G_p->g_nam_p : G_p->g_attrfnam_p);

        if (G_p->g_attrnam_p == NULL) {
                G_p->g_passdirfd = open_dir(Fullnam_p);

                if (G_p->g_passdirfd == -1) {
                        msg(ERRN,
                            "Cannot open/create \"%s\"", Fullnam_p);
                        return (FILE_PASS_ERR);
                }
        } else {
                int     rw_sysattr;

                /*
                 * Open the file's attribute directory.
                 * Change into the base file's starting directory then call
                 * open_attr_dir() to open the attribute directory of either
                 * the base file (if G_p->g_attrparent_p is NULL) or the
                 * attribute (if G_p->g_attrparent_p is set) of the base file.
                 */

                G_p->g_passdirfd = -1;
                (void) fchdir(G_p->g_baseparent_fd);
                (void) open_attr_dir(G_p->g_attrnam_p, Fullnam_p,
                    G_p->g_baseparent_fd, (G_p->g_attrparent_p == NULL) ? NULL :
                    G_p->g_attrparent_p, &G_p->g_passdirfd, &rw_sysattr);
                if (G_p->g_passdirfd == -1) {
                        msg(ERRN,
                            "Cannot open attribute directory of "
                            "%s%s%sfile \"%s\"",
                            (G_p->g_attrparent_p == NULL) ? "" :
                            gettext("attribute \""),
                            (G_p->g_attrparent_p == NULL) ? "" :
                            G_p->g_attrparent_p,
                            (G_p->g_attrparent_p == NULL) ? "" :
                            gettext("\" of "), Fullnam_p);
                        return (FILE_PASS_ERR);
                }
        }

        if (Args & OCl) {
                /* We are linking back to the source directory. */

                if (!Adir) {
                        char *existingfile = save_name;

                        if ((Args & OCL) && issymlink) {
                                /* We are chasing symlinks. */

                                if ((size = readlink(save_name, Symlnk_p,
                                    MAXPATHLEN)) < 0) {
                                        msg(ERRN,
                                            "Cannot read symbolic link \"%s\"",
                                            save_name);
                                        return (FILE_PASS_ERR);
                                }

                                Symlnk_p[size] = '\0';
                                existingfile = Symlnk_p;
                        }

                        if (G_p->g_attrnam_p == NULL) {
                                if (creat_lnk(G_p->g_passdirfd,
                                    existingfile, Fullnam_p) == 0) {
                                        return (FILE_LINKED);
                                }
                        }
                }
        }

        if ((G_p->g_mode & Ftype) == S_IFLNK && !(Args & OCL)) {
                /* The archive file is a symlink. */

                errno = 0;

                if ((size = readlink(save_name, Symlnk_p, MAXPATHLEN)) < 0) {
                        msg(ERRN,
                            "Cannot read symbolic link \"%s\"", save_name);
                        return (FILE_PASS_ERR);
                }

                errno = 0;
                (void) missdir(Fullnam_p);
                *(Symlnk_p + size) = '\0';

                if (symlink(Symlnk_p, Fullnam_p) < 0) {
                        if (errno == EEXIST) {
                                if (openout(G_p->g_passdirfd) < 0) {
                                        if (errno != EEXIST) {
                                                msg(ERRN,
                                                    "Cannot create \"%s\"",
                                                    Fullnam_p);
                                        }
                                        return (FILE_PASS_ERR);
                                }
                        } else {
                                msg(ERRN, "Cannot create \"%s\"", Fullnam_p);
                                return (FILE_PASS_ERR);
                        }
                } else {
                        if (Args & OCR) {
                                if (lchown(Fullnam_p, (int)Rpw_p->pw_uid,
                                    (int)Rpw_p->pw_gid) < 0) {
                                        msg(ERRN,
                                            "Error during chown() of \"%s\"",
                                            Fullnam_p);
                                }
                        } else if ((lchown(Fullnam_p, (int)G_p->g_uid,
                            (int)G_p->g_gid) < 0) && privileged) {
                                msg(ERRN,
                                    "Error during chown() of \"%s\"",
                                    Fullnam_p);
                        }
                }

                VERBOSE((Args & (OCv | OCV)), Fullnam_p);
                return (FILE_PASS_ERR);
        }

        if (!Adir && G_p->g_nlink > 1) {
                /* The archive file has hard links. */

                tl_p = add_lnk(&ttl_p);
                l_p = ttl_p;

                if (tl_p == l_p) {
                        /* The archive file was not found. */

                        G_p = &tl_p->L_gen;
                } else {
                        /* The archive file was found. */

                        cwd = -1;

                        if (l_p->L_gen.g_attrnam_p != NULL) {
                                /* We are linking an attribute */

                                (void) strcpy(Lnkend_p, l_p->L_gen.g_attrnam_p);
                                cwd = save_cwd();
                                (void) fchdir(G_p->g_passdirfd);
                                lfrom = get_component(Lnknam_p);
                                lto = tl_p->L_gen.g_attrnam_p;
                        } else {
                                /* We are not linking an attribute */

                                (void) strcpy(Lnkend_p, l_p->L_gen.g_nam_p);
                                (void) strcpy(Full_p, tl_p->L_gen.g_nam_p);
                                lfrom = Lnknam_p;
                                lto = Fullnam_p;
                        }

                        (void) creat_lnk(G_p->g_passdirfd, lfrom, lto);

                        if (cwd) {
                                rest_cwd(cwd);
                        }

                        l_p->L_lnk_p = NULL;
                        free(tl_p->L_gen.g_nam_p);
                        free(tl_p);

                        if (l_p->L_cnt == G_p->g_nlink) {
                                reclaim(l_p);
                        }

                        return (FILE_LINKED);
                }
        }

        if (Adir || Aspec) {
                /*
                 * The archive file is a directory,  block special, char
                 * special or a fifo.
                 */

                if (creat_spec(G_p->g_passdirfd) > 0) {
                        VERBOSE((Args & (OCv | OCV)), Fullnam_p);
                }
        } else if ((Ofile = openout(G_p->g_passdirfd)) > 0) {
                data_pass();
        }

        return (FILE_COPIED);
}

/*
 * flush_lnks: With new linked file handling, linked files are not archived
 * until all links have been collected.  When the end of the list of filenames
 * to archive has been reached, all files that did not encounter all their links
 * are written out with actual (encountered) link counts.  A file with n links
 * (that are archived) will be represented by n headers (one for each link (the
 * first n - 1 have g_filesz set to 0)) followed by the data for the file.
 */

static void
flush_lnks(void)
{
        struct Lnk *l_p, *tl_p;
        off_t tfsize;

        l_p = Lnk_hd.L_nxt_p;
        while (l_p != &Lnk_hd) {
                (void) strcpy(Gen.g_nam_p, l_p->L_gen.g_nam_p);
                if (stat(Gen.g_nam_p, &SrcSt) == 0) { /* check if file exists */
                        tl_p = l_p;
                        (void) creat_hdr();
                        Gen.g_nlink = l_p->L_cnt; /* "actual" link count */
                        tfsize = Gen.g_filesz;
                        Gen.g_filesz = (off_t)0;
                        G_p = &Gen;
                        while (tl_p != NULL) {
                                Gen.g_nam_p = tl_p->L_gen.g_nam_p;
                                Gen.g_namesz = tl_p->L_gen.g_namesz;
                                if (tl_p->L_lnk_p == NULL) {
                                        Gen.g_filesz = tfsize;
                                        if (open_dirfd() != 0) {
                                                break;
                                        }
                                        data_out();
                                        break;
                                }
                                write_hdr(ARCHIVE_NORMAL,
                                    (off_t)0); /* header only */
                                VERBOSE((Args & (OCv | OCV)), Gen.g_nam_p);
                                tl_p = tl_p->L_lnk_p;
                        }
                        Gen.g_nam_p = Nam_p;
                } else /* stat(Gen.g_nam_p, &SrcSt) == 0 */
                        msg(ERR, "\"%s%s%s\" has disappeared",
                            (Gen.g_attrnam_p == NULL) ?
                            Gen.g_nam_p : Gen.g_attrfnam_p,
                            (Gen.g_attrnam_p == NULL) ?
                            "" : Gen.g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (Gen.g_attrnam_p == NULL) ?
                            "" : Gen.g_attrnam_p);
                tl_p = l_p;
                l_p = l_p->L_nxt_p;
                reclaim(tl_p);
        } /* l_p != &Lnk_hd */
}

#if defined(O_XATTR)
static int
is_sysattr(char *name)
{
        return ((strcmp(name, VIEW_READONLY) == 0) ||
            (strcmp(name, VIEW_READWRITE) == 0));
}
#endif

/*
 * gethdr: Get a header from the archive, validate it and check for the trailer.
 * Any user specified Hdr_type is ignored (set to NONE in main).  Hdr_type is
 * set appropriately after a valid header is found.  Unless the -k option is
 * set a corrupted header causes an exit with an error.  I/O errors during
 * examination of any part of the header cause gethdr to throw away any current
 * data and start over.  Other errors during examination of any part of the
 * header cause gethdr to advance one byte and continue the examination.
 */

static int
gethdr(void)
{
        ushort_t ftype;
        int hit = NONE, cnt = 0;
        int goodhdr, hsize = 0, offset;
        int bswap = 0;
        char *preptr;
        int k = 0;
        int j;
        int error;
        int aclcnt;

        Gen.g_nam_p = Nam_p;
        do { /* hit == NONE && (Args & OCk) && Buffr.b_cnt > 0 */
                FILL(Hdrsz);
                switch (Hdr_type) {
                case NONE:
                case BIN:
                        Binmag.b_byte[0] = Buffr.b_out_p[0];
                        Binmag.b_byte[1] = Buffr.b_out_p[1];
                        if ((Binmag.b_half == CMN_BIN) ||
                            (Binmag.b_half == CMN_BBS)) {
                                hit = read_hdr(BIN);
                                if (Hdr_type == NONE)
                                        bswap = 1;
                                hsize = HDRSZ + Gen.g_namesz;
                                break;
                        }
                        if (Hdr_type != NONE)
                                break;
                        /*FALLTHROUGH*/
                case CHR:
                        if (!(strncmp(Buffr.b_out_p, CMS_CHR, CMS_LEN))) {
                                hit = read_hdr(CHR);
                                hsize = CHRSZ + Gen.g_namesz;
                                break;
                        }
                        if (Hdr_type != NONE)
                                break;
                        /*FALLTHROUGH*/
                case ASC:
                        if (!(strncmp(Buffr.b_out_p, CMS_ASC, CMS_LEN))) {
                                hit = read_hdr(ASC);
                                hsize = ASCSZ + Gen.g_namesz;
                                Max_namesz = APATH;
                                break;
                        }
                        if (Hdr_type != NONE)
                                break;
                        /*FALLTHROUGH*/
                case CRC:
                        if (!(strncmp(Buffr.b_out_p, CMS_CRC, CMS_LEN))) {
                                hit = read_hdr(CRC);
                                hsize = ASCSZ + Gen.g_namesz;
                                Max_namesz = APATH;
                                break;
                        }
                        if (Hdr_type != NONE)
                                break;
                        /*FALLTHROUGH*/

                case BAR:
                        if (Hdr_p != NULL && strcmp(Hdr_p, "bar") == 0) {
                                Hdrsz = BARSZ;
                                FILL(Hdrsz);
                                if ((hit = read_hdr(BAR)) == NONE) {
                                        Hdrsz = ASCSZ;
                                        break;
                                }
                                hit = BAR;
                                hsize = BARSZ;
                                break;
                        }
                        /*FALLTHROUGH*/

                case USTAR:
                        if (Hdr_p != NULL && strcmp(Hdr_p, "ustar") == 0) {
                                Hdrsz = TARSZ;
                                FILL(Hdrsz);
                                if ((hit = read_hdr(USTAR)) == NONE) {
                                        Hdrsz = ASCSZ;
                                        break;
                                }
                                hit = USTAR;
                                hsize = TARSZ;
                                break;
                        }
                        /*FALLTHROUGH*/
                case TAR:
                        if (Hdr_p != NULL && strcmp(Hdr_p, "tar") == 0) {
                                Hdrsz = TARSZ;
                                FILL(Hdrsz);
                                if ((hit = read_hdr(TAR)) == NONE) {
                                        Hdrsz = ASCSZ;
                                        break;
                                }
                                hit = TAR;
                                hsize = TARSZ;
                                break;
                        }
                        /*FALLTHROUGH*/
                default:
                        msg(EXT, "Impossible header type.");
                } /* Hdr_type */

                if (hit == TAR || hit == USTAR) {
                        Gen.g_nam_p = &nambuf[0];
                }

                if (hit != NONE) {
                        FILL(hsize);
                        goodhdr = 1;
                        if (Gen.g_filesz < (off_t)0 || Gen.g_namesz < 1)
                                goodhdr = 0;
                        if ((hit != USTAR) && (hit != TAR))
                                if (Gen.g_namesz - 1 > Max_namesz)
                                        goodhdr = 0;
                        /* TAR and USTAR */
                        if ((hit == USTAR) || (hit == TAR)) {
                                if (*Gen.g_nam_p == '\0') { /* tar trailer */
                                        goodhdr = 1;
                                } else {

                                        G_p = &Gen;
                                        if (G_p->g_cksum !=
                                            cksum(TARTYP, 0, NULL)) {
                                                goodhdr = 0;
                                                msg(ERR,
                                                    "Bad header - checksum "
                                                    "error.");
                                        }
                                }
                        } else if (hit != BAR) { /* binary, -c, ASC and CRC */
                                if (Gen.g_nlink <= 0)
                                        goodhdr = 0;
                                if (*(Buffr.b_out_p + hsize - 1) != '\0')
                                        goodhdr = 0;
                        }
                        if (!goodhdr) {
                                hit = NONE;
                                if (!(Args & OCk))
                                        break;
                                msg(ERR,
                                    "Corrupt header, file(s) may be lost.");
                        } else {
                                FILL(hsize);
                        }
                } /* hit != NONE */
                if (hit == NONE) {
                        Buffr.b_out_p++;
                        Buffr.b_cnt--;
                        if (!(Args & OCk))
                                break;
                        if (!cnt++)
                                msg(ERR, "Searching for magic number/header.");
                }
        } while (hit == NONE);
        if (hit == NONE) {
                if (Hdr_type == NONE)
                        msg(EXT, "Not a cpio file, bad header.");
                else
                        msg(EXT, "Bad magic number/header.");
        } else if (cnt > 0) {
                msg(EPOST, "Re-synchronized on magic number/header.");
        }
        if (Hdr_type == NONE) {
                Hdr_type = hit;
                switch (Hdr_type) {
                case BIN:
                        if (bswap)
                                Args |= BSM;
                        Hdrsz = HDRSZ;
                        Max_namesz = CPATH;
                        Pad_val = HALFWD;
                        Onecopy = 0;
                        break;
                case CHR:
                        Hdrsz = CHRSZ;
                        Max_namesz = CPATH;
                        Pad_val = 0;
                        Onecopy = 0;
                        break;
                case ASC:
                case CRC:
                        Hdrsz = ASCSZ;
                        Max_namesz = APATH;
                        Pad_val = FULLWD;
                        Onecopy = 1;
                        break;
                case USTAR:
                        Hdrsz = TARSZ;
                        Max_namesz = HNAMLEN - 1;
                        Pad_val = FULLBK;
                        Onecopy = 0;
                        break;
                case BAR:
                case TAR:
                        Hdrsz = TARSZ;
                        Max_namesz = TNAMLEN - 1;
                        Pad_val = FULLBK;
                        Onecopy = 0;
                        break;
                default:
                        msg(EXT, "Impossible header type.");
                } /* Hdr_type */
        } /* Hdr_type == NONE */
        if ((Hdr_type == USTAR) || (Hdr_type == TAR) ||
            (Hdr_type == BAR)) {                        /* TAR, USTAR, BAR */
                Gen.g_namesz = 0;
                if (Gen.g_nam_p[0] == '\0')
                        return (0);
                else {
                        preptr = &prebuf[0];
                        if (*preptr != '\0') {
                                k = strlen(&prebuf[0]);
                                if (k < PRESIZ) {
                                        (void) strcpy(&fullnam[0], &prebuf[0]);
                                        j = 0;
                                        fullnam[k++] = '/';
                                        while ((j < NAMSIZ) && (nambuf[j] !=
                                            '\0')) {
                                                fullnam[k] = nambuf[j];
                                                k++; j++;
                                        }
                                        fullnam[k] = '\0';
                                } else if (k >= PRESIZ) {
                                        k = 0;
                                        while ((k < PRESIZ) && (prebuf[k] !=
                                            '\0')) {
                                                fullnam[k] = prebuf[k];
                                                k++;
                                        }
                                        fullnam[k++] = '/';
                                        j = 0;
                                        while ((j < NAMSIZ) && (nambuf[j] !=
                                            '\0')) {
                                                fullnam[k] = nambuf[j];
                                                k++; j++;
                                        }
                                        fullnam[k] = '\0';
                                }
                                Gen.g_nam_p = &fullnam[0];
                        } else
                                Gen.g_nam_p = &nambuf[0];

                        /*
                         * initialize the buffer so that the prefix will not
                         * applied to the next entry in the archive
                         */
                        (void) memset(prebuf, 0, sizeof (prebuf));
                }
        } else if (Hdr_type != BAR) {
                (void) memcpy(Gen.g_nam_p, Buffr.b_out_p + Hdrsz, Gen.g_namesz);
                if (!(strcmp(Gen.g_nam_p, "TRAILER!!!")))
                        return (0);
        }
        offset = ((hsize + Pad_val) & ~Pad_val);
        FILL(offset + Hdrsz);
        Thdr_p = (union tblock *)Buffr.b_out_p;
        Buffr.b_out_p += offset;
        Buffr.b_cnt -= (off_t)offset;
        ftype = Gen.g_mode & Ftype;

#if defined(O_XATTR)
        /* extended attribute support */
        if (((Gen.g_mode & S_IFMT) == _XATTR_CPIO_MODE) ||
            ((Hdr_type == USTAR || Hdr_type == TAR) &&
            Thdr_p->tbuf.t_typeflag == _XATTR_HDRTYPE)) {
                char    *aname;
                char    *attrparent = NULL;
                char    *attrpath = NULL;
                char    *tapath;
                char    *taname;

                if (xattrp != NULL) {
                        if (xattrbadhead) {
                                free(xattrhead);
                                xattrp = NULL;
                                xattr_linkp = NULL;
                                xattrhead = NULL;
                                return (1);
                        }

                        /*
                         * At this point, the attribute path contains
                         * the path to the attribute rooted at the hidden
                         * attribute directory of the base file.  This can
                         * be a simple attribute or extended attribute name,
                         * or it can be something like <attr>/<sys attr> if
                         * we are processing a system attribute of an attribute.
                         * Determine the attribute name and attribute parent
                         * (if there is one).  When we are processing a simple
                         * attribute or extended attribute name, the attribute
                         * parent will be set to NULL.  When we are processing
                         * something like <attr>/<sys attr>, the attribute
                         * parent will be contain <attr>, and the attribute
                         * name will contain <sys attr>.
                         */
                        tapath = xattrp->h_names +
                            strlen(xattrp->h_names) + 1;
                        attrpath = e_strdup(E_EXIT, tapath);
                        if ((taname = strpbrk(tapath, "/")) != NULL) {
                                aname = taname + 1;
                                *taname = '\0';
                                attrparent = tapath;
                        } else {
                                aname = tapath;
                        }

                        Gen.g_rw_sysattr = is_sysattr(aname);
                        Gen.g_baseparent_fd = attr_baseparent_fd;

                        if (Gen.g_attrfnam_p != NULL) {
                                free(Gen.g_attrfnam_p);
                                Gen.g_attrfnam_p = NULL;
                        }
                        if (Gen.g_attrnam_p != NULL) {
                                free(Gen.g_attrnam_p);
                                Gen.g_attrnam_p = NULL;
                        }
                        if (Gen.g_attrparent_p != NULL) {
                                free(Gen.g_attrparent_p);
                                Gen.g_attrparent_p = NULL;
                        }
                        if (Gen.g_attrpath_p != NULL) {
                                free(Gen.g_attrpath_p);
                                Gen.g_attrpath_p = NULL;
                        }
                        if (Renam_p && Renam_p[0] != '\0') {
                                Gen.g_attrfnam_p = e_strdup(E_EXIT, Renam_p);
                        } else {
                                Gen.g_attrfnam_p = e_strdup(E_EXIT,
                                    xattrp->h_names);
                        }
                        Gen.g_attrnam_p = e_strdup(E_EXIT, aname);

                        if (attrparent != NULL) {
                                if (Renam_attr_p && Renam_attr_p[0] != '\0') {
                                        size_t  apathlen = strlen(attrparent) +
                                            strlen(aname) + 2;
                                        Gen.g_attrparent_p = e_strdup(E_EXIT,
                                            Renam_attr_p);
                                        Gen.g_attrpath_p = e_zalloc(E_EXIT,
                                            apathlen);
                                        (void) snprintf(Gen.g_attrpath_p,
                                            apathlen, "%s/%s", Renam_attr_p,
                                            aname);
                                        (void) free(attrparent);
                                        (void) free(attrpath);
                                } else {
                                        Gen.g_attrparent_p = attrparent;
                                        Gen.g_attrpath_p = attrpath;
                                }
                        } else {
                                Gen.g_attrpath_p = attrpath;
                        }

                        if (xattr_linkp != NULL) {
                                if (Gen.g_linktoattrfnam_p != NULL) {
                                        free(Gen.g_linktoattrfnam_p);
                                        Gen.g_linktoattrfnam_p = NULL;
                                }
                                if (Gen.g_linktoattrnam_p != NULL) {
                                        free(Gen.g_linktoattrnam_p);
                                        Gen.g_linktoattrnam_p = NULL;
                                }
                                if (Renam_attr_p && Renam_attr_p[0] != '\0') {
                                        Gen.g_linktoattrfnam_p = e_strdup(
                                            E_EXIT, Renam_attr_p);
                                } else {
                                        Gen.g_linktoattrfnam_p = e_strdup(
                                            E_EXIT, xattr_linkp->h_names);
                                }
                                Gen.g_linktoattrnam_p = e_strdup(E_EXIT,
                                    aname);
                                xattr_linkp = NULL;
                        }
                        if (Hdr_type != USTAR && Hdr_type != TAR) {
                                Gen.g_mode = Gen.g_mode & (~_XATTR_CPIO_MODE);
                                Gen.g_mode |= attrmode(xattrp->h_typeflag);
                        } else if (Hdr_type == USTAR || Hdr_type == TAR) {
                                Thdr_p->tbuf.t_typeflag = xattrp->h_typeflag;
                        }

                        ftype = Gen.g_mode & Ftype;
                        Adir = ftype == S_IFDIR;
                        Aspec = (ftype == S_IFBLK || ftype == S_IFCHR ||
                            ftype == S_IFIFO || ftype == S_IFSOCK);

                        if (Gen.g_attrnam_p[0] == '.' &&
                            Gen.g_attrnam_p[1] == '\0' &&
                            xattrp->h_typeflag == DIRTYPE) {
                                Hiddendir = 1;
                        } else {
                                Hiddendir = 0;
                        }

                        free(xattrhead);
                        xattrhead = NULL;
                        xattrp = NULL;
                } else {
                        if (xattrbadhead == 0) {
                                (void) read_xattr_hdr();
                                return (2);
                        }
                }
        } else {
                Hiddendir = 0;
        }
#endif /* O_XATTR */

        /* acl support: grab acl info */
        if ((Gen.g_mode == SECMODE) || ((Hdr_type == USTAR ||
            Hdr_type == TAR) && Thdr_p->tbuf.t_typeflag == 'A')) {
                /* this is an ancillary file */
                off_t   bytes;
                char    *secp;
                int     pad;
                int     cnt;
                char    *tp;
                int     attrsize = 0;

                if (Pflag) {
                        bytes = Gen.g_filesz;
                        secp = e_zalloc(E_EXIT, (uint_t)bytes);
                        tp = secp;

                        while (bytes > 0) {
                                cnt = (int)(bytes > CPIOBSZ) ? CPIOBSZ : bytes;
                                FILL(cnt);
                                (void) memcpy(tp, Buffr.b_out_p, cnt);
                                tp += cnt;
                                Buffr.b_out_p += cnt;
                                Buffr.b_cnt -= (off_t)cnt;
                                bytes -= (off_t)cnt;
                        }

                        pad = (Pad_val + 1 - (Gen.g_filesz & Pad_val)) &
                            Pad_val;
                        if (pad != 0) {
                                FILL(pad);
                                Buffr.b_out_p += pad;
                                Buffr.b_cnt -= (off_t)pad;
                        }

                        /* got all attributes in secp */
                        tp = secp;
                        do {
                                attr = (struct sec_attr *)tp;
                                switch (attr->attr_type) {
                                case UFSD_ACL:
                                case ACE_ACL:
                                        (void) sscanf(attr->attr_len, "%7o",
                                            (uint_t *)&aclcnt);
                                        /* header is 8 */
                                        attrsize = 8 +
                                            strlen(&attr->attr_info[0])
                                            + 1;

                                        error =
                                            acl_fromtext(&attr->attr_info[0],
                                            &aclp);

                                        if (error != 0) {
                                                msg(ERR,
                                                    "aclfromtext failed: %s",
                                                    acl_strerror(error));
                                                bytes -= attrsize;
                                                break;
                                        }

                                        if (aclcnt != acl_cnt(aclp)) {
                                                msg(ERR, "acl count error");
                                                bytes -= attrsize;
                                                break;
                                        }
                                        bytes -= attrsize;
                                        break;

                                /* SunFed case goes here */

                                default:
                                        msg(EXT, "unrecognized attr type");
                                        break;
                                }
                                /* next attributes */
                                tp += attrsize;
                        } while (bytes > 0);
                        free(secp);
                } else {
                        /* skip security info */
                        G_p = &Gen;
                        data_in(P_SKIP);
                }
                /*
                 * We already got the file content, dont call file_in()
                 * when return. The new return code(2) is used to
                 *  indicate that.
                 */
                VERBOSE((Args & OCt), Gen.g_nam_p);
                return (2);
        } /* acl */

        /*
         * Sparse file support
         * Read header of holesdata to get original file size.
         * This is necessary because ckname() or file_in() shows file size
         * with OCt before data_in() extracts the holesdata. data_in()
         * actually doesn't extract the holesdata since proc_mode will be
         * P_SKIP in the OCt mode.
         */
        if ((Hdr_type == CHR || Hdr_type == ASC) &&
            S_ISSPARSE(Gen.g_mode) && Gen.g_filesz > MIN_HOLES_HDRSIZE) {
                char    holesdata[MIN_HOLES_HDRSIZE + 1];

                FILL(MIN_HOLES_HDRSIZE);
                (void) memcpy(holesdata, Buffr.b_out_p, MIN_HOLES_HDRSIZE);
                holesdata[MIN_HOLES_HDRSIZE] = '\0';

                Gen.g_holes = read_holes_header(holesdata, Gen.g_filesz);
                if (Gen.g_holes == NULL) {
                        msg(EXT, "invalid sparse file information");
                } else {
                        Buffr.b_out_p += MIN_HOLES_HDRSIZE;
                        Buffr.b_cnt -= MIN_HOLES_HDRSIZE;
                }
        }

        Adir = (ftype == S_IFDIR);
        Aspec = (ftype == S_IFBLK || ftype == S_IFCHR || ftype == S_IFIFO ||
            ftype == S_IFSOCK);

        /*
         * Skip any trailing slashes
         */
        chop_endslashes(Gen.g_nam_p);
        return (1);
}

/*
 * getname: Get file names for inclusion in the archive.  When end of file
 * on the input stream of file names is reached, flush the link buffer out.
 * For each filename, remove leading "./"s and multiple "/"s, and remove
 * any trailing newline "\n".  Finally, verify the existence of the file,
 * and call creat_hdr() to fill in the gen_hdr structure.
 */

static int
getname(void)
{
        int goodfile = 0, lastchar, err;
        char *s;
        char *dir;

        Gen.g_nam_p = Nam_p;
        Hiddendir = 0;

        while (!goodfile) {
                err = 0;

                while ((s = fgets(Gen.g_nam_p, APATH+1, In_p)) != NULL) {
                        lastchar = strlen(s) - 1;
                        issymlink = 0;

                        if (s[lastchar] != '\n') {
                                if (lastchar == APATH - 1) {
                                        if (!err) {
                                                msg(ERR,
                                                    "%s name too long.",
                                                    Nam_p);
                                        }
                                        goodfile = 0;
                                        err = 1;
                                } else {
                                        break;
                                }
                        } else {
                                s[lastchar] = '\0';
                                break;
                        }
                }

                if (s == NULL) {
                        if (Gen.g_dirfd != -1) {
                                (void) close(Gen.g_dirfd);
                                Gen.g_dirfd = -1;
                        }
                        if (Onecopy && (Args & OCo)) {
                                flush_lnks();
                        }
                        return (0);
                }

                while (*Gen.g_nam_p == '.' && Gen.g_nam_p[1] == '/') {
                        Gen.g_nam_p += 2;
                        while (*Gen.g_nam_p == '/')
                                Gen.g_nam_p++;
                }

                /*
                 * Skip any trailing slashes
                 */
                chop_endslashes(Gen.g_nam_p);

                /*
                 * Figure out parent directory
                 */

                if (Gen.g_attrnam_p != NULL) {
                        if (Gen.g_dirfd != -1) {
                                (void) close(Gen.g_dirfd);
                        }
                        Gen.g_dirfd = attropen(Gen.g_attrfnam_p, ".", O_RDONLY);
                        if (Gen.g_dirfd == -1) {
                                msg(ERRN,
                                    "Cannot open attribute directory"
                                    " of file %s", Gen.g_attrfnam_p);
                                continue;
                        }
                } else {
#ifdef O_XATTR
                        char dirpath[PATH_MAX];

                        get_parent(Gen.g_nam_p, dirpath);
                        if (Atflag || SysAtflag) {
                                dir = dirpath;
                                if (Gen.g_dirfd != -1) {
                                        (void) close(Gen.g_dirfd);
                                }
                                Gen.g_dirfd = open(dir, O_RDONLY);
                                if (Gen.g_dirfd == -1) {
                                        msg(ERRN,
                                            "Cannot open directory %s", dir);
                                        continue;
                                }
                        } else {
                                /*
                                 * g_dirpath is the pathname cache maintaining
                                 * the dirname which is currently opened.
                                 * We first check the g_dirpath to see if the
                                 * given dirname matches. If so, we don't need
                                 * to open the dir, but we can use the g_dirfd
                                 * as is if it is still available.
                                 */
                                dir = NULL;
                                if (Gen.g_dirpath == NULL ||
                                    Gen.g_dirfd == -1) {
                                        /*
                                         * It's the first time or it has
                                         * all gone.
                                         */
                                        dir = e_strdup(E_EXIT, dirpath);
                                } else {
                                        if (strcmp(Gen.g_dirpath,
                                            dirpath) != 0) {
                                                /* different directory */
                                                dir = e_strdup(E_EXIT, dirpath);
                                        }
                                }
                                if (dir != NULL) {
                                        /*
                                         * We need to open the new directory.
                                         * discard the pathname and dirfd
                                         * for the previous directory.
                                         */
                                        if (Gen.g_dirpath != NULL) {
                                                free(Gen.g_dirpath);
                                                Gen.g_dirpath = NULL;
                                        }
                                        if (Gen.g_dirfd != -1) {
                                                (void) close(Gen.g_dirfd);
                                        }
                                        /* open the new dir */
                                        Gen.g_dirfd = open(dir, O_RDONLY);
                                        if (Gen.g_dirfd == -1) {
                                                msg(ERRN, "Cannot open "
                                                    "directory %s", dir);
                                                continue;
                                        }
                                        Gen.g_dirpath = dir;
                                }
                        }
#else
                        Gen.g_dirfd = -1;
#endif
                }

                /* creat_hdr checks for USTAR filename length */

                if (Hdr_type != USTAR && strlen(Gen.g_nam_p) >
                    Max_namesz) {
                        if (!err) {
                                msg(ERR, "%s%s%s name too long.",
                                    (Gen.g_attrnam_p == NULL) ?
                                    Nam_p : Gen.g_attrfnam_p,
                                    (Gen.g_attrnam_p == NULL) ?
                                    "" : Gen.g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (Gen.g_attrnam_p == NULL) ?
                                    "" : Gen.g_attrnam_p);
                        }
                        goodfile = 0;
                        err = 1;
                }

                if (err) {
                        continue;
                } else {
                        G_p = &Gen;
                        if (!LSTAT(Gen.g_dirfd, Gen.g_nam_p, &SrcSt)) {
                                goodfile = 1;

                                if ((SrcSt.st_mode & Ftype) == S_IFLNK) {
                                        issymlink = 1;

                                        if ((Args & OCL)) {
                                                errno = 0;
                                                if (STAT(Gen.g_dirfd,
                                                    G_p->g_nam_p,
                                                    &SrcSt) < 0) {
                                                        msg(ERRN,
                                                            "Cannot follow"
                                                            " \"%s%s%s\"",
                                                            (Gen.g_attrnam_p ==
                                                            NULL) ?
                                                            Gen.g_nam_p :
                                                            Gen.g_attrfnam_p,
                                                            (Gen.g_attrnam_p ==
                                                            NULL) ? "" :
                                                            Gen.g_rw_sysattr ?
                                                            gettext(
                                                            " System "
                                                            "Attribute ") :
                                                            gettext(
                                                            " Attribute "),
                                                            (Gen.g_attrnam_p ==
                                                            NULL) ? "" :
                                                            Gen.g_attrnam_p);
                                                        goodfile = 0;
                                                }
                                        }
                                }

                                if (Use_old_stat) {
                                        OldSt = convert_to_old_stat(&SrcSt,
                                            Gen.g_nam_p, Gen.g_attrnam_p);

                                        if (OldSt == NULL) {
                                                goodfile = 0;
                                        }
                                }
                        } else {
                                msg(ERRN,
                                    "Error with fstatat() of \"%s%s%s\"",
                                    (Gen.g_attrnam_p == NULL) ?
                                    Gen.g_nam_p : Gen.g_attrfnam_p,
                                    (Gen.g_attrnam_p == NULL) ? "" :
                                    Gen.g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (Gen.g_attrnam_p == NULL) ?
                                    "" : Gen.g_attrnam_p);
                        }
                }
        }

        /*
         * Get ACL info: dont bother allocating space if there are only
         * standard permissions, i.e. ACL count < 4
         */
        if ((SrcSt.st_mode & Ftype) != S_IFLNK && Pflag) {
                if (acl_get(Gen.g_nam_p, ACL_NO_TRIVIAL, &aclp) != 0)
                        msg(ERRN, "Error with acl() of \"%s\"", Gen.g_nam_p);
        }
        /* else: only traditional permissions, so proceed as usual */
        if (creat_hdr())
                return (1);
        else return (2);
}

/*
 * getpats: Save any filenames/patterns specified as arguments.
 * Read additional filenames/patterns from the file specified by the
 * user.  The filenames/patterns must occur one per line.
 */

static void
getpats(int largc, char **largv)
{
        char **t_pp;
        size_t len;
        unsigned numpat = largc, maxpat = largc + 2;

        Pat_pp = e_zalloc(E_EXIT, maxpat * sizeof (char *));
        t_pp = Pat_pp;
        while (*largv) {
                *t_pp = e_zalloc(E_EXIT, strlen(*largv) + 1);
                (void) strcpy(*t_pp, *largv);
                t_pp++;
                largv++;
        }
        while (fgets(Nam_p, Max_namesz + 1, Ef_p) != NULL) {
                if (numpat == maxpat - 1) {
                        maxpat += 10;
                        Pat_pp = e_realloc(E_EXIT, Pat_pp,
                            maxpat * sizeof (char *));
                        t_pp = Pat_pp + numpat;
                }
                len = strlen(Nam_p); /* includes the \n */
                *(Nam_p + len - 1) = '\0'; /* remove the \n */
                *t_pp = e_zalloc(E_EXIT, len);
                (void) strcpy(*t_pp, Nam_p);
                t_pp++;
                numpat++;
        }
        *t_pp = NULL;
}

static void
ioerror(int dir)
{
        int t_errno;

        t_errno = errno;
        errno = 0;
        if (fstat(Archive, &ArchSt) < 0)
                msg(EXTN, "Error during stat() of archive");
        errno = t_errno;
        if ((ArchSt.st_mode & Ftype) != S_IFCHR) {
                if (dir) {
                        if (errno == EFBIG)
                                msg(EXT, "ulimit reached for output file.");
                        else if (errno == ENOSPC)
                                msg(EXT, "No space left for output file.");
                        else
                                msg(EXTN, "I/O error - cannot continue");
                } else
                        msg(EXT, "Unexpected end-of-file encountered.");
        } else
                msg(EXTN, "\007I/O error on \"%s\"", dir ? "output" : "input");
}

/*
 * matched: Determine if a filename matches the specified pattern(s).  If the
 * pattern is matched (the second return), return 0 if -f was specified, else
 * return != 0.  If the pattern is not matched (the first and third
 * returns), return 0 if -f was not specified, else return != 0.
 */

static int
matched(void)
{
        char *str_p = G_p->g_nam_p;
        char **pat_pp = Pat_pp;
        int negatep, result;

        /*
         * Check for attribute
         */
        if (G_p->g_attrfnam_p != NULL)
                str_p = G_p->g_attrfnam_p;

        for (pat_pp = Pat_pp; *pat_pp; pat_pp++) {
                negatep = (**pat_pp == '!');

                result = fnmatch(negatep ? (*pat_pp+1) : *pat_pp, str_p, 0);

                if (result != 0 && result != FNM_NOMATCH) {
                        msg(POST, "error matching file %s with pattern"
                            " %s\n", str_p, *pat_pp);
                        return (Args & OCf);
                }

                if ((result == 0 && ! negatep) ||
                    (result == FNM_NOMATCH && negatep)) {
                        /* match occurred */
                        return (!(Args & OCf));
                }
        }
        return (Args & OCf); /* not matched */
}

/*
 * missdir: Create missing directories for files.
 * (Possible future performance enhancement, if missdir is called, we know
 * that at least the very last directory of the path does not exist, therefore,
 * scan the path from the end
 */

static int
missdir(char *nam_p)
{
        char *c_p;
        int cnt = 2;
        char *lastp;

        if (*(c_p = nam_p) == '/') /* skip over 'root slash' */
                c_p++;

        lastp = c_p + strlen(nam_p) - 1;
        if (*lastp == '/')
                *lastp = '\0';

        for (; *c_p; ++c_p) {
                if (*c_p == '/') {
                        *c_p = '\0';
                        if (stat(nam_p, &DesSt) < 0) {
                                if (Args & OCd) {
                                        cnt = mkdir(nam_p, Def_mode);
                                        if (cnt != 0) {
                                                *c_p = '/';
                                                return (cnt);
                                        }
                                } else {
                                        msg(ERR, "Missing -d option.");
                                        *c_p = '/';
                                        return (-1);
                                }
                        }
                        *c_p = '/';
                }
        }
        if (cnt == 2) /* the file already exists */
                cnt = 0;
        return (cnt);
}

/*
 * mklong: Convert two shorts into one long.  For VAX, Interdata ...
 */

static long
mklong(short v[])
{

        union swpbuf swp_b;

        swp_b.s_word = 1;
        if (swp_b.s_byte[0]) {
                swp_b.s_half[0] = v[1];
                swp_b.s_half[1] = v[0];
        } else {
                swp_b.s_half[0] = v[0];
                swp_b.s_half[1] = v[1];
        }
        return (swp_b.s_word);
}

/*
 * mkshort: Convert a long into 2 shorts, for VAX, Interdata ...
 */

static void
mkshort(short sval[], long v)
{
        union swpbuf *swp_p, swp_b;

        swp_p = (union swpbuf *)sval;
        swp_b.s_word = 1;
        if (swp_b.s_byte[0]) {
                swp_b.s_word = v;
                swp_p->s_half[0] = swp_b.s_half[1];
                swp_p->s_half[1] = swp_b.s_half[0];
        } else {
                swp_b.s_word = v;
                swp_p->s_half[0] = swp_b.s_half[0];
                swp_p->s_half[1] = swp_b.s_half[1];
        }
}

/*
 * msg: Print either a message (no error) (POST), an error message with or
 * without the errno (ERRN or ERR), or print an error message with or without
 * the errno and exit (EXTN or EXT).
 */
void
msg(int severity, const char *fmt, ...)
{
        FILE *file_p;
        va_list ap;

        if ((Args & OCV) && Verbcnt) { /* clear current line of dots */
                (void) fputc('\n', Out_p);
                Verbcnt = 0;
        }
        va_start(ap, fmt);
        if (severity == POST)
                file_p = Out_p;
        else
                if (severity == EPOST)
                        file_p = Err_p;
                else {
                        file_p = Err_p;
                        Error_cnt++;
                }
        (void) fflush(Out_p);
        (void) fflush(Err_p);
        if ((severity != POST) && (severity != EPOST))
                (void) fprintf(file_p, "cpio: ");

        /* gettext replaces version of string */

        (void) vfprintf(file_p, gettext(fmt), ap);
        if (severity == ERRN || severity == EXTN) {
                if (G_p && (G_p->g_attrnam_p != NULL) && G_p->g_rw_sysattr) {
                        if (errno == EPERM) {
                                (void) fprintf(file_p, ", errno %d, %s", errno,
                                    gettext("insufficient privileges\n"));
                        } else if (errno == EINVAL) {
                                (void) fprintf(file_p, ", errno %d, %s",
                                    errno, gettext(
                                    "unsupported on underlying file system\n"));
                        } else {
                                (void) fprintf(file_p, ", errno %d, ", errno);
                                perror("");
                        }
                } else {
                        (void) fprintf(file_p, ", errno %d, ", errno);
                        perror("");
                }
        } else
                (void) fprintf(file_p, "\n");
        (void) fflush(file_p);
        va_end(ap);
        if (severity == EXT || severity == EXTN) {
                (void) fprintf(file_p, gettext("%d errors\n"), Error_cnt);
                exit(EXIT_CODE);
        }
}

/*
 * openout: Open files for output and set all necessary information.
 * If the u option is set (unconditionally overwrite existing files),
 * and the current file exists, get a temporary file name from mktemp(3C),
 * link the temporary file to the existing file, and remove the existing file.
 * Finally either creat(2), mkdir(2) or mknod(2) as appropriate.
 *
 */

static int
openout(int dirfd)
{
        char *nam_p;
        int cnt, result;

        Do_rename = 0;  /* creat_tmp() may reset this */

        if (G_p->g_attrnam_p != NULL) {
                nam_p = G_p->g_attrnam_p;
        } else {
                if (Args & OCp) {
                        nam_p = Fullnam_p;
                } else {
                        nam_p = G_p->g_nam_p;
                }
        }


        if ((Max_filesz != RLIM_INFINITY) &&
            (Max_filesz < (G_p->g_filesz >> 9))) {
                /* ... divided by 512 ... */
                msg(ERR, "Skipping \"%s%s%s\": exceeds ulimit by %lld bytes",
                    (G_p->g_attrnam_p == NULL) ? nam_p : G_p->g_attrfnam_p,
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (G_p->g_attrnam_p == NULL) ? "" : nam_p,
                    (off_t)(G_p->g_filesz - (Max_filesz << 9)));
                return (-1);
        }

        if (LSTAT(dirfd, nam_p, &DesSt) == 0) {
                /*
                 * A file by the same name exists.  Move it to a temporary
                 * file unless it's a system attribute file.  If we are
                 * restoring a system attribute file on a file system that
                 * supports system attributes, then the system attribute file
                 * will already exist (a default system attribute file will
                 * get created when the file it is associated with is created).
                 * If we create a temporary system attribute file, we can't
                 * overwrite the existing system attribute file using
                 * renameat().  In addition, only system attributes can exist
                 * for an attribute of a file, therefore, a temporary file
                 * cannot be created for a system attribute of an attribute.
                 * Thus, when restoring a system attribute, we won't move it
                 * to a temporary file, but will attempt to process it as if
                 * it didn't already exist.
                 */

#if defined(_PC_SATTR_ENABLED)
                if (G_p->g_rw_sysattr == 0)
#endif  /* _PC_SATTR_ENABLED */
                        if (creat_tmp(nam_p) < 0) {
                                /*
                                 * We weren't able to create the temp file.
                                 * Report failure.
                                 */

                                return (-1);
                        }
        }

        if (Do_rename) {
                /* nam_p was changed by creat_tmp() above. */

                if (Args & OCp) {
                        if (G_p->g_attrnam_p != NULL) {
                                nam_p = Attrfile_p;
                        } else {
                                nam_p = Fullnam_p;
                        }
                } else {
                        nam_p = G_p->g_nam_p;
                }
        }

        /*
         * This pile tries to create the file directly, and, if there is a
         * problem, creates missing directories, and then tries to create the
         * file again.  Two strikes and you're out.
         *
         * On XATTR system, the directory has already been created by
         * open_dirfd(), so error shouldn't happen in the loop. However,
         * on non-XATTR system, symlink/open may fail with ENOENT. In such
         * case, we go to create missing directories.
         */

        cnt = 0;

        do {
                errno = 0;

                if (Hdr_type == TAR && Thdr_p->tbuf.t_typeflag == SYMTYPE) {
                        /* The archive file is a TAR symlink. */
                        if ((result =
                            symlink(Thdr_p->tbuf.t_linkname, nam_p)) >= 0) {
                                cnt = 0;
                                if (Over_p != NULL) {
                                        (void) unlinkat(dirfd,
                                            get_component(Over_p), 0);
                                        *Over_p = '\0';
                                }
                                break;
                        } else if (errno != ENOENT) {
                                /* The attempt to symlink failed. */
                                msg(ERRN,
                                    "Cannot create symbolic link \"%s\" -> "
                                    "\"%s\"",
                                    Thdr_p->tbuf.t_linkname, nam_p);

                                if (*Over_p != '\0') {
                                        rstfiles(U_KEEP, dirfd);
                                }
                                return (-1);
                        }
                } else if (Hdr_type == BAR && bar_linkflag == SYMTYPE) {
                        if ((result = symlink(bar_linkname, nam_p)) >= 0) {
                                cnt = 0;
                                if (Over_p != NULL) {
                                        (void) unlinkat(dirfd,
                                            get_component(Over_p), 0);
                                        *Over_p = '\0';
                                }
                                break;
                        } else if (errno != ENOENT) {
                                /* The attempt to symlink failed. */
                                msg(ERRN,
                                    "Cannot create symbolic link \"%s\" -> "
                                    "\"%s\"",
                                    bar_linkname, nam_p);
                                if (*Over_p != '\0') {
                                        rstfiles(U_KEEP, dirfd);
                                }
                                return (-1);
                        }
                } else if ((G_p->g_mode & Ftype) == S_IFLNK) {
                        if ((!(Args & OCp)) && !(Hdr_type == USTAR)) {
                                FILL(G_p->g_filesz);
                                (void) strncpy(Symlnk_p,
                                    Buffr.b_out_p, G_p->g_filesz);
                                *(Symlnk_p + G_p->g_filesz) = '\0';
                        } else if ((!(Args & OCp)) && (Hdr_type == USTAR)) {
                                Symlnk_p[NAMSIZ] = '\0';
                                (void) strncpy(Symlnk_p,
                                    &Thdr_p->tbuf.t_linkname[0], NAMSIZ);
                        }
                        if ((result = symlink(Symlnk_p, nam_p)) >= 0) {
                                cnt = 0;
                                if (Over_p != NULL) {
                                        (void) unlinkat(dirfd,
                                            get_component(Over_p), 0);
                                        *Over_p = '\0';
                                }
                                break;
                        } else if (errno != ENOENT) {
                                /* The attempt to symlink failed. */
                                msg(ERRN,
                                    "Cannot create symbolic link \"%s\" -> "
                                    "\"%s\"",
                                    Symlnk_p, nam_p);

                                if (*Over_p != '\0') {
                                        rstfiles(U_KEEP, dirfd);
                                }
                                return (-1);
                        }
                } else {
                        int     saveerrno;

                        if ((result = openat(dirfd, get_component(nam_p),
                            O_CREAT|O_RDWR|O_TRUNC, (int)G_p->g_mode)) < 0) {
                                saveerrno = errno;
                                if (G_p->g_attrnam_p != NULL)  {
                                        result = retry_open_attr(dirfd,
                                            Gen.g_baseparent_fd, Fullnam_p,
                                            (G_p->g_attrparent_p == NULL) ?
                                            NULL : G_p->g_attrparent_p, nam_p,
                                            O_CREAT|O_RDWR|O_TRUNC,
                                            (int)G_p->g_mode);
                                }
                        }
                        if (result < 0) {
                                errno = saveerrno;
                                if (errno != ENOENT) {
                                        /* The attempt to open failed. */
                                        msg(ERRN, "Cannot open file \"%s\"",
                                            nam_p);
                                        if (*Over_p != '\0') {
                                                rstfiles(U_KEEP, dirfd);
                                        }
                                        return (-1);
                                }
                        } else {
                                /* acl support */
                                acl_is_set = 0;
                                if (Pflag && aclp != NULL) {
                                        if (facl_set(result, aclp) < 0) {
                                                msg(ERRN,
                                                    "\"%s\": failed to set acl",
                                                    nam_p);
                                        } else {
                                                acl_is_set = 1;
                                        }
                                        acl_free(aclp);
                                        aclp = NULL;
                                }
                                cnt = 0;
                                break;
                        }
                }
                cnt++;
        } while (cnt < 2 && missdir(nam_p) == 0);

        switch (cnt) {
        case 0:
                if ((Args & OCi) && (Hdr_type == USTAR)) {
                        setpasswd(nam_p);
                }
                if ((G_p->g_mode & Ftype) == S_IFLNK ||
                    (Hdr_type == BAR && bar_linkflag == SYMTYPE)) {
                        if (Args & OCR) {
                                if (fchownat(dirfd,
                                    get_component(nam_p),
                                    (int)Rpw_p->pw_uid,
                                    (int)Rpw_p->pw_gid,
                                    AT_SYMLINK_NOFOLLOW) < 0) {
                                        msg(ERRN,
                                            "Error during chown() of "
                                            "\"%s%s%s\"",
                                            (G_p->g_attrnam_p == NULL) ?
                                            nam_p : G_p->g_attrfnam_p,
                                            (G_p->g_attrnam_p == NULL) ?
                                            "" : G_p->g_rw_sysattr ?
                                            gettext(" System Attribute ") :
                                            gettext(" Attribute "),
                                            (G_p->g_attrnam_p == NULL) ?
                                            "" : nam_p);
                                }
                        } else if ((fchownat(dirfd, get_component(nam_p),
                            (int)G_p->g_uid, (int)G_p->g_gid,
                            AT_SYMLINK_NOFOLLOW) < 0) && privileged) {
                                msg(ERRN,
                                    "Error during chown() of \"%s%s%s\"",
                                    (G_p->g_attrnam_p == NULL) ?
                                    nam_p : G_p->g_attrfnam_p,
                                    (G_p->g_attrnam_p == NULL) ? "" :
                                    G_p->g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (G_p->g_attrnam_p == NULL) ? "" : nam_p);
                        }
                }
                break;

        case 1:
                if (Do_rename) {
                        msg(ERRN, "Cannot create directory for \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? Over_p :
                            G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : Over_p);
                } else {
                        msg(ERRN, "Cannot create directory for \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? nam_p :
                            G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : nam_p);
                }
                break;

        case 2:
                if (Do_rename) {
                        msg(ERRN, "Cannot create \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? Over_p :
                            G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" :
                            Over_p);
                } else {
                        msg(ERRN, "Cannot create \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? nam_p :
                            G_p->g_attrfnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ?
                            gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : nam_p);
                }
                break;

        default:
                msg(EXT, "Impossible case.");
        }

        Finished = 0;
        return (result);
}

/*
 * read_hdr: Transfer headers from the selected format
 * in the archive I/O buffer to the generic structure.
 */

static
int
read_hdr(int hdr)
{
        int rv = NONE;
        major_t maj, rmaj;
        minor_t min, rmin;
        char tmpnull = '\0';
        static int bar_read_cnt = 0;

        if (hdr != BAR) {
                if (Buffr.b_end_p != (Buffr.b_out_p + Hdrsz)) {
                        tmpnull = *(Buffr.b_out_p + Hdrsz);
                        *(Buffr.b_out_p + Hdrsz) = '\0';
                }
        }

        switch (hdr) {
        case BIN:
                (void) memcpy(&Hdr, Buffr.b_out_p, HDRSZ);
                if (Hdr.h_magic == (short)CMN_BBS) {
                        swap((char *)&Hdr, HDRSZ);
                }
                Gen.g_magic = Hdr.h_magic;
                Gen.g_mode = Hdr.h_mode;
                Gen.g_uid = Hdr.h_uid;
                Gen.g_gid = Hdr.h_gid;
                Gen.g_nlink = Hdr.h_nlink;
                Gen.g_mtime = mklong(Hdr.h_mtime);
                Gen.g_ino = Hdr.h_ino;
                Gen.g_dev = Hdr.h_dev;
                Gen.g_rdev = Hdr.h_rdev;
                maj = SVR3_MAJOR(Gen.g_dev);
                rmaj = SVR3_MAJOR(Gen.g_rdev);
                min = SVR3_MINOR(Gen.g_dev);
                rmin = SVR3_MINOR(Gen.g_rdev);
                Gen.g_dev = makedev(maj, min);
                Gen.g_rdev = makedev(rmaj, rmin);
                Gen.g_cksum = 0L;
                Gen.g_filesz = (off_t)mklong(Hdr.h_filesize);
                Gen.g_namesz = Hdr.h_namesize;
                rv = BIN;
                break;
        case CHR:
                if (sscanf(Buffr.b_out_p,
                    "%6o%6o%6o%6o"
                    "%6" _SCNoID "%6" _SCNoID
                    "%6o%6o%11o%6o%11lo",
                    &Gen.g_magic, &Gen.g_dev, &Gen.g_ino, &Gen.g_mode,
                    &Gen.g_uid, &Gen.g_gid,
                    &Gen.g_nlink, &Gen.g_rdev,
                    (uint_t *)&Gen.g_mtime, (uint_t *)&Gen.g_namesz,
                    (u_off_t *)&Gen.g_filesz) == CHR_CNT) {
                        rv = CHR;
                        maj = SVR3_MAJOR(Gen.g_dev);
                        rmaj = SVR3_MAJOR(Gen.g_rdev);
                        min = SVR3_MINOR(Gen.g_dev);
                        rmin = SVR3_MINOR(Gen.g_rdev);
                        Gen.g_dev = makedev(maj, min);
                        Gen.g_rdev = makedev(rmaj, rmin);
                }
                break;
        case ASC:
        case CRC:
                if (sscanf(Buffr.b_out_p,
                    "%6x%8x%8x"
                    "%8" _SCNxID "%8" _SCNxID
                    "%8x%8x%8lx%8x%8x%8x%8x%8x%8x",
                    &Gen.g_magic, &Gen.g_ino, &Gen.g_mode,
                    &Gen.g_uid, &Gen.g_gid,
                    &Gen.g_nlink, &Gen.g_mtime,
                    (u_off_t *)&Gen.g_filesz, (uint_t *)&maj, (uint_t *)&min,
                    (uint_t *)&rmaj, (uint_t *)&rmin, (uint_t *)&Gen.g_namesz,
                    &Gen.g_cksum) == ASC_CNT) {
                        Gen.g_dev = makedev(maj, min);
                        Gen.g_rdev = makedev(rmaj, rmin);
                        rv = hdr;
                }
                break;
        case USTAR: /* TAR and USTAR */
                if (*Buffr.b_out_p == '\0') {
                        *Gen.g_nam_p = '\0';
                        nambuf[0] = '\0';
                } else {
                        Thdr_p = (union tblock *)Buffr.b_out_p;
                        Gen.g_nam_p[0] = '\0';
                        (void) strncpy((char *)&nambuf,
                            Thdr_p->tbuf.t_name, NAMSIZ);
                        (void) sscanf(Thdr_p->tbuf.t_mode, "%8o",
                            &Gen.g_mode);
                        (void) sscanf(Thdr_p->tbuf.t_uid, "%8" _SCNoID,
                            &Gen.g_uid);
                        (void) sscanf(Thdr_p->tbuf.t_gid, "%8" _SCNoID,
                            &Gen.g_gid);
                        (void) sscanf(Thdr_p->tbuf.t_size, "%12lo",
                            (u_off_t *)&Gen.g_filesz);
                        (void) sscanf(Thdr_p->tbuf.t_mtime, "%12o",
                            (uint_t *)&Gen.g_mtime);
                        (void) sscanf(Thdr_p->tbuf.t_cksum, "%8o",
                            (uint_t *)&Gen.g_cksum);
                        if (Thdr_p->tbuf.t_linkname[0] != '\0')
                                Gen.g_nlink = 1;
                        else
                                Gen.g_nlink = 0;

                        switch (Thdr_p->tbuf.t_typeflag) {
                        case SYMTYPE:
                                /* Symbolic Link */
                                Gen.g_nlink = 2;
                                break;
                        case CHRTYPE:
                                Gen.g_mode |= (S_IFMT & S_IFCHR);
                                break;
                        case BLKTYPE:
                                Gen.g_mode |= (S_IFMT & S_IFBLK);
                                break;
                        case DIRTYPE:
                                Gen.g_mode |= (S_IFMT & S_IFDIR);
                                break;
                        case FIFOTYPE:
                                Gen.g_mode |= (S_IFMT & S_IFIFO);
                                break;
                        }

                        (void) sscanf(Thdr_p->tbuf.t_magic, "%6o",
                            (uint_t *)&Gen.g_tmagic);
                        (void) sscanf(Thdr_p->tbuf.t_version, "%2o",
                            (uint_t *)&Gen.g_version);
                        (void) sscanf(Thdr_p->tbuf.t_uname, "%32s",
                            (char *)&Gen.g_uname);
                        (void) sscanf(Thdr_p->tbuf.t_gname, "%32s",
                            (char *)&Gen.g_gname);
                        (void) sscanf(Thdr_p->tbuf.t_devmajor, "%8o", &maj);
                        (void) sscanf(Thdr_p->tbuf.t_devminor, "%8o", &min);
                        (void) strncpy((char *)&prebuf,
                            Thdr_p->tbuf.t_prefix, PRESIZ);
                        Gen.g_namesz = strlen(Gen.g_nam_p) + 1;
                        Gen.g_rdev = makedev(maj, min);
                }
                rv = USTAR;
                break;
        case TAR:
                if (*Buffr.b_out_p == '\0') {
                        *Gen.g_nam_p = '\0';
                        nambuf[0] = '\0';
                } else {
                        Thdr_p = (union tblock *)Buffr.b_out_p;
                        Gen.g_nam_p[0] = '\0';
                        (void) sscanf(Thdr_p->tbuf.t_mode, "%8o", &Gen.g_mode);
                        (void) sscanf(Thdr_p->tbuf.t_uid, "%8" _SCNoID,
                            &Gen.g_uid);
                        (void) sscanf(Thdr_p->tbuf.t_gid, "%8" _SCNoID,
                            &Gen.g_gid);
                        (void) sscanf(Thdr_p->tbuf.t_size, "%12" SCNo64,
                            (u_off_t *)&Gen.g_filesz);
                        (void) sscanf(Thdr_p->tbuf.t_mtime, "%12o",
                            &Gen.g_mtime);
                        (void) sscanf(Thdr_p->tbuf.t_cksum, "%8o",
                            &Gen.g_cksum);
                        if (Thdr_p->tbuf.t_typeflag == '1')     /* hardlink */
                                Gen.g_nlink = 1;
                        else
                                Gen.g_nlink = 0;
                        (void) strncpy(Gen.g_nam_p,
                            Thdr_p->tbuf.t_name, NAMSIZ);
                        Gen.g_namesz = strlen(Gen.g_nam_p) + 1;
                        (void) strcpy(nambuf, Gen.g_nam_p);
                }
                rv = TAR;
                break;
        case BAR:
                if (Bar_vol_num == 0 && bar_read_cnt == 0) {
                        read_bar_vol_hdr();
                        bar_read_cnt++;
                } else {
                        read_bar_file_hdr();
                }
                rv = BAR;
                break;
        default:
                msg(EXT, "Impossible header type.");
        }

        if (hdr != BAR) {
                if (Buffr.b_end_p != (Buffr.b_out_p + Hdrsz))
                        *(Buffr.b_out_p + Hdrsz) = tmpnull;
        }

        return (rv);
}

/*
 * reclaim: Reclaim linked file structure storage.
 */

static void
reclaim(struct Lnk *p)
{
        p->L_bck_p->L_nxt_p = p->L_nxt_p;
        p->L_nxt_p->L_bck_p = p->L_bck_p;

        while (p != NULL) {
                struct Lnk *new_p = p->L_lnk_p;

                free(p->L_gen.g_nam_p);
                free(p);
                p = new_p;
        }
}

/*
 * rstbuf: Reset the I/O buffer, move incomplete potential headers to
 * the front of the buffer and force bread() to refill the buffer.  The
 * return value from bread() is returned (to identify I/O errors).  On the
 * 3B2, reads must begin on a word boundary, therefore, with the -i option,
 * any remaining bytes in the buffer must be moved to the base of the buffer
 * in such a way that the destination locations of subsequent reads are
 * word aligned.
 */

static void
rstbuf(void)
{
        int pad;

        if ((Args & OCi) || Append) {
                if (Buffr.b_out_p != Buffr.b_base_p) {
                        pad = ((Buffr.b_cnt + FULLWD) & ~FULLWD);
                        Buffr.b_in_p = Buffr.b_base_p + pad;
                        pad -= Buffr.b_cnt;
                        (void) memcpy(Buffr.b_base_p + pad, Buffr.b_out_p,
                            (int)Buffr.b_cnt);
                        Buffr.b_out_p = Buffr.b_base_p + pad;
                }
                if (bfill() < 0)
                        msg(EXT, "Unexpected end-of-archive encountered.");
        } else { /* OCo */
                (void) memcpy(Buffr.b_base_p, Buffr.b_out_p, (int)Buffr.b_cnt);
                Buffr.b_out_p = Buffr.b_base_p;
                Buffr.b_in_p = Buffr.b_base_p + Buffr.b_cnt;
        }
}

static void
setpasswd(char *nam)
{
        if ((dpasswd = getpwnam(&Gen.g_uname[0])) == NULL) {
                msg(EPOST, "cpio: problem reading passwd entry");
                msg(EPOST, "cpio: %s: owner not changed", nam);
                if (Gen.g_uid == UID_NOBODY && S_ISREG(Gen.g_mode))
                        Gen.g_mode &= ~S_ISUID;
        } else
                Gen.g_uid = dpasswd->pw_uid;

        if ((dgroup = getgrnam(&Gen.g_gname[0])) == NULL) {
                msg(EPOST, "cpio: problem reading group entry");
                msg(EPOST, "cpio: %s: group not changed", nam);
                if (Gen.g_gid == GID_NOBODY && S_ISREG(Gen.g_mode))
                        Gen.g_mode &= ~S_ISGID;
        } else
                Gen.g_gid = dgroup->gr_gid;
        G_p = &Gen;
}

/*
 * rstfiles:  Perform final changes to the file.  If the -u option is set,
 * and overwrite == U_OVER, remove the temporary file, else if overwrite
 * == U_KEEP, unlink the current file, and restore the existing version
 * of the file.  In addition, where appropriate, set the access or modification
 * times, change the owner and change the modes of the file.
 *
 * Note that if Do_rename is set, then the roles of original and temporary
 * file are reversed. If all went well, we will rename() the temporary file
 * over the original in order to accommodate potentially executing files.
 */
static void
rstfiles(int over, int dirfd)
{
        char *inam_p, *onam_p, *nam_p;
        int error;

#if defined(_PC_SATTR_ENABLED)
        /* Time or permissions cannot be set on system attribute files */
        if ((Gen.g_attrnam_p != NULL) && (Gen.g_rw_sysattr == 1)) {
                return;
        }
#endif  /* _PC_SATTR_ENABLED */

        if (Args & OCp) {
                if (G_p->g_attrnam_p == NULL) {
                        nam_p = Fullnam_p;
                } else {
                        nam_p = G_p->g_attrnam_p;
                }
        } else {
                if (Gen.g_nlink > 0) {
                        nam_p = G_p->g_nam_p;
                } else {
                        nam_p = Gen.g_nam_p;
                }
        }
        if (Gen.g_attrnam_p != NULL) {
                nam_p = Gen.g_attrnam_p;
        }

        if ((Args & OCi) && (Hdr_type == USTAR)) {
                setpasswd(nam_p);
        }
        if (over == U_KEEP && *Over_p != '\0') {
                if (Do_rename) {
                        msg(POST, "Restoring existing \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? Over_p : Fullnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : Over_p);
                } else {
                        msg(POST, "Restoring existing \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? nam_p : Fullnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : nam_p);
                }

                /* delete what we just built */
                (void) unlinkat(dirfd, get_component(nam_p), 0);

                /* If the old file needs restoring, do the necessary links */
                if (Do_rename) {
                        char *tmp_ptr;

                        if (Args & OCp) {
                                tmp_ptr = Fullnam_p;
                                Fullnam_p = Over_p;
                        } else {
                                tmp_ptr = G_p->g_nam_p;
                                G_p->g_nam_p = Over_p;
                        }
                        Over_p = tmp_ptr;

                        Do_rename = 0;  /* names now have original values */
                } else {
                        if (rename(Over_p, nam_p) < 0) {
                                if (link(Over_p, nam_p) < 0) {
                                        msg(EXTN,
                                            "Cannot recover original version"
                                            " of \"%s%s%s\"",
                                            (G_p->g_attrnam_p == NULL) ?
                                            nam_p : Fullnam_p,
                                            (G_p->g_attrnam_p == NULL) ? "" :
                                            G_p->g_rw_sysattr ?
                                            gettext(" System Attribute ") :
                                            gettext(" Attribute "),
                                            (G_p->g_attrnam_p == NULL) ?
                                            "" : nam_p);
                                }
                                if (unlinkat(dirfd, get_component(Over_p), 0)) {
                                        msg(ERRN,
                                            "Cannot remove temp file "
                                            "\"%s%s%s\"",
                                            (G_p->g_attrnam_p == NULL) ?
                                            Over_p : Fullnam_p,
                                            (G_p->g_attrnam_p == NULL) ? "" :
                                            G_p->g_rw_sysattr ?
                                            gettext(" System Attribute ") :
                                            gettext(" Attribute "),
                                            (G_p->g_attrnam_p == NULL) ?
                                            "" : Over_p);
                                }
                        }
                }
                *Over_p = '\0';
                return;
        } else if (over == U_OVER && *Over_p != '\0') {
                if (Do_rename) {
                        char *tmp_ptr;

                        (void) renameat(dirfd, get_component(nam_p),
                            dirfd, get_component(Over_p));
                        if (Args & OCp) {
                                if (G_p->g_attrnam_p == NULL) {
                                        tmp_ptr = Fullnam_p;
                                        Fullnam_p = Over_p;
                                        Over_p = tmp_ptr;
                                } else {
                                        /*
                                         * Over_p is pointing at g_attrnam_p
                                         * which must be preserved.
                                         *
                                         * We don't want the tmp_ptr and so
                                         * on to throw away our only copy of
                                         * the name.
                                         */
                                        Over_p = Attrfile_p;
                                }
                        } else {
                                tmp_ptr = G_p->g_nam_p;
                                G_p->g_nam_p = Over_p;
                                Over_p = tmp_ptr;
                        }
                        Do_rename = 0;  /* names now have original values */
                } else {
                        if (unlinkat(dirfd, get_component(Over_p), 0) < 0) {
                                msg(ERRN,
                                    "Cannot unlink() temp file \"%s%s%s\"",
                                    (G_p->g_attrnam_p == NULL) ?
                                    Over_p : Fullnam_p,
                                    (G_p->g_attrnam_p == NULL) ? "" :
                                    G_p->g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (G_p->g_attrnam_p == NULL) ? "" : Over_p);
                        }
                }
                *Over_p = '\0';
        }
        if (Args & OCp) {
                if (G_p->g_attrnam_p != NULL) {
                        inam_p = G_p->g_attrfnam_p;
                        onam_p = G_p->g_attrnam_p;
                } else {
                        inam_p = Nam_p;
                        onam_p = Fullnam_p;
                }
        } else /* OCi only uses onam_p, OCo only uses inam_p */
                if (G_p->g_attrnam_p != NULL) {
                        inam_p = onam_p = G_p->g_attrnam_p;
                } else {
                        inam_p = onam_p = G_p->g_nam_p;
                }

        /*
         * Change the owner, time, and mode to those of the file
         * originally created in the archive.  Note: time and
         * mode do not need to be restored for a symbolic link
         * since rstfiles() is not called when the archived file
         * is a symlink.
         */
        if (!(Args & OCo)) {
                if (Args & OCR) {
                        if (fchownat(dirfd, get_component(onam_p),
                            Rpw_p->pw_uid, Rpw_p->pw_gid,
                            AT_SYMLINK_NOFOLLOW) < 0) {
                                msg(ERRN, "Cannot chown() \"%s%s%s\"",
                                    onam_p,
                                    (G_p->g_attrnam_p == NULL) ? "" :
                                    G_p->g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (G_p->g_attrnam_p == NULL) ? "" : onam_p);
                        }
                } else {
                        if ((fchownat(dirfd, get_component(onam_p),
                            G_p->g_uid, G_p->g_gid,
                            AT_SYMLINK_NOFOLLOW) < 0) && privileged) {
                                msg(ERRN, "Cannot chown() \"%s%s%s\"",
                                    onam_p,
                                    (G_p->g_attrnam_p == NULL) ? "" :
                                    G_p->g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (G_p->g_attrnam_p == NULL) ? "" : onam_p);
                        }
                }

                if (Args & OCm) {
                        set_tym(dirfd, get_component(onam_p),
                            G_p->g_mtime, G_p->g_mtime);
                }

                /* Acl was not set, so we must chmod */
                if (!acl_is_set) {
                        mode_t orig_mask = 0, new_mask;

                        /*
                         * use fchmod for attributes, since
                         * we known they are always regular
                         * files, whereas when it isn't an
                         * attribute it could be for a fifo
                         * or something other that we don't
                         * open and don't have a valid Ofile
                         * for.
                         */
                        if (privileged) {
                                new_mask = G_p->g_mode;
                        } else {
                                orig_mask = umask(0);
                                new_mask = G_p->g_mode & ~orig_mask;
                        }

                        if (G_p->g_attrnam_p != NULL) {
                                error = fchmod(Ofile, new_mask);
                        } else {
                                error = chmod(onam_p, new_mask);
                        }
                        if (error < 0) {
                                msg(ERRN,
                                    "Cannot chmod() \"%s%s%s\"",
                                    (G_p->g_attrnam_p == NULL) ?
                                    onam_p : G_p->g_attrfnam_p,
                                    (G_p->g_attrnam_p == NULL) ? "" :
                                    G_p->g_rw_sysattr ?
                                    gettext(" System Attribute ") :
                                    gettext(" Attribute "),
                                    (G_p->g_attrnam_p == NULL) ? "" : onam_p);
                        }
                        if (!privileged) {
                                (void) umask(orig_mask);
                        }
                }
        }

        if (!(Args & OCi) && (Args & OCa)) {
                /*
                 * Use dirfd since we are updating original file
                 * and not just created file
                 */
                set_tym(G_p->g_dirfd, get_component(inam_p),
                    (uint_t)SrcSt.st_atime, (uint_t)SrcSt.st_mtime);
        }
}

/*
 * scan4trail: Scan the archive looking for the trailer.
 * When found, back the archive up over the trailer and overwrite
 * the trailer with the files to be added to the archive.
 */

static void
scan4trail(void)
{
        int rv;
        off_t off1, off2;

        Append = 1;
        Hdr_type = NONE;
        G_p = NULL;
        while (gethdr()) {
                G_p = &Gen;
                data_in(P_SKIP);
        }
        off1 = Buffr.b_cnt;
        off2 = Bufsize - (Buffr.b_cnt % Bufsize);
        Buffr.b_out_p = Buffr.b_in_p = Buffr.b_base_p;
        Buffr.b_cnt = (off_t)0;
        if (lseek(Archive, -(off1 + off2), SEEK_REL) < 0)
                msg(EXTN, "Unable to append to this archive");
        if ((rv = g_read(Device, Archive, Buffr.b_in_p, Bufsize)) < 0)
                msg(EXTN, "Cannot append to this archive");
        if (lseek(Archive, (off_t)-rv, SEEK_REL) < 0)
                msg(EXTN, "Unable to append to this archive");
        Buffr.b_cnt = off2;
        Buffr.b_in_p = Buffr.b_base_p + Buffr.b_cnt;
        Append = 0;
}

/*
 * setup:  Perform setup and initialization functions.  Parse the options
 * using getopt(3C), call ckopts to check the options and initialize various
 * structures and pointers.  Specifically, for the -i option, save any
 * patterns, for the -o option, check (via stat(2)) the archive, and for
 * the -p option, validate the destination directory.
 */

static void
setup(int largc, char **largv)
{
        extern int optind;
        extern char *optarg;

#if defined(O_XATTR)
#if defined(_PC_SATTR_ENABLED)
#ifdef WAITAROUND
        char    *opts_p = "zabcdfiklmopqrstuvABC:DE:H:I:LM:O:PR:SV6@/";
#else
        char    *opts_p = "abcdfiklmopqrstuvABC:DE:H:I:LM:O:PR:SV6@/";
#endif  /* WAITAROUND */

#else   /* _PC_SATTR_ENABLED */
#ifdef WAITAROUND
        char    *opts_p = "zabcdfiklmopqrstuvABC:DE:H:I:LM:O:PR:SV6@";
#else
        char    *opts_p = "abcdfiklmopqrstuvABC:DE:H:I:LM:O:PR:SV6@";
#endif  /* WAITAROUND */
#endif  /* _PC_SATTR_ENABLED */

#else   /* O_XATTR */
#ifdef WAITAROUND
        char    *opts_p = "zabcdfiklmopqrstuvABC:DE:H:I:LM:O:PR:SV6";
#else
        char    *opts_p = "abcdfiklmopqrstuvABC:DE:H:I:LM:O:PR:SV6";
#endif  /* WAITAROUND */
#endif  /* O_XATTR */

        char   *dupl_p = "Only one occurrence of -%c allowed";
        int option;
        int blk_cnt, blk_cnt_max;
        struct rlimit rlim;

        /* Remember the native page size. */

        PageSize = sysconf(_SC_PAGESIZE);

        if (PageSize == -1) {
                /*
                 * This sysconf call will almost certainly never fail.  The
                 * symbol PAGESIZE itself resolves to the above sysconf call,
                 * so we should go ahead and define our own constant.
                 */
                PageSize = 8192;
        }

        Hdr_type = BIN;
        Max_offset = (off_t)(BIN_OFFSET_MAX);
        Efil_p = Hdr_p = Own_p = IOfil_p = NULL;
        while ((option = getopt(largc, largv, opts_p)) != EOF) {
                switch (option) {
#ifdef WAITAROUND
                case 'z':
                        /* rendezvous with the debugger */
                        waitaround = 1;
                        break;
#endif
                case 'a':       /* reset access time */
                        Args |= OCa;
                        break;
                case 'b':       /* swap bytes and halfwords */
                        Args |= OCb;
                        break;
                case 'c':       /* select character header */
                        Args |= OCc;
                        Hdr_type = ASC;
                        Max_namesz = APATH;
                        Onecopy = 1;
                        break;
                case 'd':       /* create directories as needed */
                        Args |= OCd;
                        break;
                case 'f':       /* select files not in patterns */
                        Args |= OCf;
                        break;
                case 'i':       /* "copy in" */
                        Args |= OCi;
                        Archive = 0;
                        break;
                case 'k':       /* retry after I/O errors */
                        Args |= OCk;
                        break;
                case 'l':       /* link files when possible */
                        Args |= OCl;
                        break;
                case 'm':       /* retain modification time */
                        Args |= OCm;
                        break;
                case 'o':       /* "copy out" */
                        Args |= OCo;
                        Archive = 1;
                        break;
                case 'p':       /* "pass" */
                        Max_namesz = APATH;
                        Args |= OCp;
                        break;
                case 'q':       /* "quiet" */
                        Args |= OCq;
                        break;
                case 'r':       /* rename files interactively */
                        Args |= OCr;
                        break;
                case 's':       /* swap bytes */
                        Args |= OCs;
                        break;
                case 't':       /* table of contents */
                        Args |= OCt;
                        break;
                case 'u':       /* copy unconditionally */
                        Args |= OCu;
                        break;
                case 'v':       /* verbose - print file names */
                        Args |= OCv;
                        break;
                case 'A':       /* append to existing archive */
                        Args |= OCA;
                        break;
                case 'B':       /* set block size to 5120 bytes */
                        Args |= OCB;
                        Bufsize = 5120;
                        break;
                case 'C':       /* set arbitrary block size */
                        if (Args & OCC)
                                msg(ERR, dupl_p, 'C');
                        else {
                                Args |= OCC;
                                Bufsize = atoi(optarg);
                        }
                        break;
                case 'D':
                        Dflag = 1;
                        break;
                case 'E':       /* alternate file for pattern input */
                        if (Args & OCE)
                                msg(ERR, dupl_p, 'E');
                        else {
                                Args |= OCE;
                                Efil_p = optarg;
                        }
                        break;
                case 'H':       /* select header type */
                        if (Args & OCH)
                                msg(ERR, dupl_p, 'H');
                        else {
                                Args |= OCH;
                                Hdr_p = optarg;
                        }
                        break;
                case 'I':       /* alternate file for archive input */
                        if (Args & OCI)
                                msg(ERR, dupl_p, 'I');
                        else {
                                Args |= OCI;
                                IOfil_p = optarg;
                        }
                        break;
                case 'L':       /* follow symbolic links */
                        Args |= OCL;
                        break;
                case 'M':       /* specify new end-of-media message */
                        if (Args & OCM)
                                msg(ERR, dupl_p, 'M');
                        else {
                                Args |= OCM;
                                Eom_p = optarg;
                        }
                        break;
                case 'O':       /* alternate file for archive output */
                        if (Args & OCO)
                                msg(ERR, dupl_p, 'O');
                        else {
                                Args |= OCO;
                                IOfil_p = optarg;
                        }
                        break;
                case 'P':       /* preserve acls */
                        Args |= OCP;
                        Pflag++;
                        break;
                case 'R':       /* change owner/group of files */
                        if (Args & OCR)
                                msg(ERR, dupl_p, 'R');
                        else {
                                Args |= OCR;
                                Own_p = optarg;
                        }
                        break;
                case 'S':       /* swap halfwords */
                        Args |= OCS;
                        break;
                case 'V':       /* print a dot '.' for each file */
                        Args |= OCV;
                        break;
                case '6':       /* for old, sixth-edition files */
                        Args |= OC6;
                        Ftype = SIXTH;
                        break;
#if defined(O_XATTR)
                case '@':
                        Atflag++;
                        break;
#if defined(_PC_SATTR_ENABLED)
                case '/':
                        SysAtflag++;
                        break;
#endif  /* _PC_SATTR_ENABLED */
#endif  /* O_XATTR */
                default:
                        Error_cnt++;
                } /* option */
        } /* (option = getopt(largc, largv, opts_p)) != EOF */

#ifdef WAITAROUND
        if (waitaround) {
                (void) fprintf(stderr, gettext("Rendezvous with cpio on pid"
                    " %d\n"), getpid());

                while (waitaround) {
                        (void) sleep(10);
                }
        }
#endif

        largc -= optind;
        largv += optind;
        ckopts(Args);
        if (!Error_cnt) {
                if (Args & OCr) {
                        Renam_p = e_zalloc(E_EXIT, APATH + 1);
                        Renametmp_p = e_zalloc(E_EXIT, APATH + 1);
#if defined(_PC_SATTR_ENABLED)
                        Renam_attr_p = e_zalloc(E_EXIT, APATH + 1);
#endif
                }
                Symlnk_p = e_zalloc(E_EXIT, APATH);
                Over_p = e_zalloc(E_EXIT, APATH);
                Nam_p = e_zalloc(E_EXIT, APATH + 1);
                if (Args & OCp) {
                        Savenam_p = e_zalloc(E_EXIT, APATH + 1);
                }
                Fullnam_p = e_zalloc(E_EXIT, APATH);
                Lnknam_p = e_zalloc(E_EXIT, APATH);
                Gen.g_nam_p = Nam_p;
                if ((Fullnam_p = getcwd(NULL, APATH)) == NULL)
                        msg(EXT, "Unable to determine current directory.");
                if (Args & OCi) {
                        if (largc > 0) /* save patterns for -i option, if any */
                                Pat_pp = largv;
                        if (Args & OCE)
                                getpats(largc, largv);
                } else if (Args & OCo) {
                        if (largc != 0) /* error if arguments left with -o */
                                Error_cnt++;
                        else if (fstat(Archive, &ArchSt) < 0)
                                msg(ERRN, "Error during stat() of archive");
                        switch (Hdr_type) {
                        case BIN:
                                Hdrsz = HDRSZ;
                                Pad_val = HALFWD;
                                break;
                        case CHR:
                                Hdrsz = CHRSZ;
                                Pad_val = 0;
                                Max_offset = (off_t)(CHAR_OFFSET_MAX);
                                break;
                        case ASC:
                        case CRC:
                                Hdrsz = ASCSZ;
                                Pad_val = FULLWD;
                                Max_offset = (off_t)(ASC_OFFSET_MAX);
                                break;
                        case TAR:
                        /* FALLTHROUGH */
                        case USTAR: /* TAR and USTAR */
                                Hdrsz = TARSZ;
                                Pad_val = FULLBK;
                                Max_offset = (off_t)(CHAR_OFFSET_MAX);
                                break;
                        default:
                                msg(EXT, "Impossible header type.");
                        }
                } else { /* directory must be specified */
                        if (largc != 1)
                                Error_cnt++;
                        else if (access(*largv, 2) < 0 && (errno != EACCES))
                                /*
                                 * EACCES is ignored here as it may occur
                                 * when any directory component of the path
                                 * does not have write permission, even though
                                 * the destination subdirectory has write
                                 * access. Writing to a read only directory
                                 * is handled later, as in "copy in" mode.
                                 */
                                msg(ERRN,
                                    "Error during access() of \"%s\"", *largv);
                }
        }
        if (Error_cnt)
                usage(); /* exits! */
        if (Args & (OCi | OCo)) {
                if (!Dflag) {
                        if (Args & (OCB | OCC)) {
                                if (g_init(&Device, &Archive) < 0)
                                        msg(EXTN,
                                            "Error during initialization");
                        } else {
                                if ((Bufsize = g_init(&Device, &Archive)) < 0)
                                        msg(EXTN,
                                            "Error during initialization");
                        }
                }

                blk_cnt_max = _20K / Bufsize;
                if (blk_cnt_max < MX_BUFS) {
                        blk_cnt_max = MX_BUFS;
                }

                Buffr.b_base_p = NULL;

                for (blk_cnt = blk_cnt_max; blk_cnt > 1; blk_cnt--) {
                        Buffr.b_size = (size_t)(Bufsize * blk_cnt);
                        Buffr.b_base_p = e_valloc(E_NORMAL, Buffr.b_size);
                        if (Buffr.b_base_p != NULL) {
                                break;
                        }
                }
                if (Buffr.b_base_p == NULL || Buffr.b_size < (2 * CPIOBSZ)) {
                        msg(EXT, "Out of memory");
                }

                Buffr.b_out_p = Buffr.b_in_p = Buffr.b_base_p;
                Buffr.b_cnt = 0L;
                Buffr.b_end_p = Buffr.b_base_p + Buffr.b_size;
        }

        /*
         * Now that Bufsize has stabilized, we can allocate our i/o buffer
         */
        Buf_p = e_valloc(E_EXIT, Bufsize);

        if (Args & OCp) { /* get destination directory */
                (void) strcpy(Fullnam_p, *largv);
                if (stat(Fullnam_p, &DesSt) < 0)
                        msg(EXTN, "Error during stat() of \"%s\"", Fullnam_p);
                if ((DesSt.st_mode & Ftype) != S_IFDIR)
                        msg(EXT, "\"%s\" is not a directory", Fullnam_p);
        }
        Full_p = Fullnam_p + strlen(Fullnam_p) - 1;
        if (*Full_p != '/') {
                Full_p++;
                *Full_p = '/';
        }
        Full_p++;
        *Full_p = '\0';
        (void) strcpy(Lnknam_p, Fullnam_p);
        Lnkend_p = Lnknam_p + strlen(Lnknam_p);
        (void) getrlimit(RLIMIT_FSIZE, &rlim);
        Max_filesz = (off_t)rlim.rlim_cur;
        Lnk_hd.L_nxt_p = Lnk_hd.L_bck_p = &Lnk_hd;
        Lnk_hd.L_lnk_p = NULL;
}

/*
 * set_tym: Set the access and/or modification times for a file.
 */

static void
set_tym(int dirfd, char *nam_p, time_t atime, time_t mtime)
{
        struct timeval times[2];

        times[0].tv_sec = atime;
        times[0].tv_usec = 0;
        times[1].tv_sec = mtime;
        times[1].tv_usec = 0;

        if (futimesat(dirfd, nam_p, times) < 0) {
                if (Args & OCa) {
                        msg(ERRN,
                            "Unable to reset access time for \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? nam_p : Fullnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : nam_p);
                } else {
                        msg(ERRN,
                            "Unable to reset modification time for \"%s%s%s\"",
                            (G_p->g_attrnam_p == NULL) ? nam_p : Fullnam_p,
                            (G_p->g_attrnam_p == NULL) ? "" :
                            G_p->g_rw_sysattr ? gettext(" System Attribute ") :
                            gettext(" Attribute "),
                            (G_p->g_attrnam_p == NULL) ? "" : nam_p);
                }
        }
}

/*
 * sigint:  Catch interrupts.  If an interrupt occurs during the extraction
 * of a file from the archive with the -u option set, and the filename did
 * exist, remove the current file and restore the original file.  Then exit.
 */

/*ARGSUSED*/
static void
sigint(int sig)
{
        char *nam_p;

        (void) signal(SIGINT, SIG_IGN); /* block further signals */
        if (!Finished) {
                if (Args & OCi)
                        nam_p = G_p->g_nam_p;
                else /* OCp */
                        nam_p = Fullnam_p;
                if (*Over_p != '\0') { /* There is a temp file */
                        if (unlink(nam_p) < 0) {
                                msg(ERRN,
                                    "Cannot remove incomplete \"%s\"", nam_p);
                        }
                        if (rename(Over_p, nam_p) < 0) {
                                if (link(Over_p, nam_p) < 0) {
                                        msg(ERRN,
                                            "Cannot recover original \"%s\"",
                                            nam_p);
                                }
                                if (unlink(Over_p)) {
                                        msg(ERRN,
                                            "Cannot remove temp file \"%s\"",
                                            Over_p);
                                }
                        }
                } else if (unlink(nam_p))
                        msg(ERRN, "Cannot remove incomplete \"%s\"", nam_p);
                *Over_p = '\0';
        }
        exit(EXIT_CODE);
}

/*
 * swap: Swap bytes (-s), halfwords (-S) or or both halfwords and bytes (-b).
 */

static void
swap(char *buf_p, int cnt)
{
        unsigned char tbyte;
        int tcnt;
        int rcnt;
        ushort_t thalf;

        rcnt = cnt % 4;
        cnt /= 4;
        if (Args & (OCb | OCs | BSM)) {
                tcnt = cnt;
                Swp_p = (union swpbuf *)buf_p;
                while (tcnt-- > 0) {
                        tbyte = Swp_p->s_byte[0];
                        Swp_p->s_byte[0] = Swp_p->s_byte[1];
                        Swp_p->s_byte[1] = tbyte;
                        tbyte = Swp_p->s_byte[2];
                        Swp_p->s_byte[2] = Swp_p->s_byte[3];
                        Swp_p->s_byte[3] = tbyte;
                        Swp_p++;
                }
                if (rcnt >= 2) {
                tbyte = Swp_p->s_byte[0];
                Swp_p->s_byte[0] = Swp_p->s_byte[1];
                Swp_p->s_byte[1] = tbyte;
                tbyte = Swp_p->s_byte[2];
                }
        }
        if (Args & (OCb | OCS)) {
                tcnt = cnt;
                Swp_p = (union swpbuf *)buf_p;
                while (tcnt-- > 0) {
                        thalf = Swp_p->s_half[0];
                        Swp_p->s_half[0] = Swp_p->s_half[1];
                        Swp_p->s_half[1] = thalf;
                        Swp_p++;
                }
        }
}

/*
 * usage: Print the usage message on stderr and exit.
 */

static void
usage(void)
{

        (void) fflush(stdout);
#if defined(O_XATTR)
        (void) fprintf(stderr, gettext("USAGE:\n"
            "\tcpio -i[bcdfkmqrstuv@BSV6] [-C size] "
            "[-E file] [-H hdr] [-I file [-M msg]] "
            "[-R id] [patterns]\n"
            "\tcpio -o[acv@ABLV] [-C size] "
            "[-H hdr] [-O file [-M msg]]\n"
            "\tcpio -p[adlmuv@LV] [-R id] directory\n"));
#else
        (void) fprintf(stderr, gettext("USAGE:\n"
            "\tcpio -i[bcdfkmqrstuvBSV6] [-C size] "
            "[-E file] [-H hdr] [-I file [-M msg]] "
            "[-R id] [patterns]\n"
            "\tcpio -o[acvABLV] [-C size] "
            "[-H hdr] [-O file [-M msg]]\n"
            "\tcpio -p[adlmuvLV] [-R id] directory\n"));
#endif
        (void) fflush(stderr);
        exit(EXIT_CODE);
}

/*
 * verbose: For each file, print either the filename (-v) or a dot (-V).
 * If the -t option (table of contents) is set, print either the filename,
 * or if the -v option is also set, print an "ls -l"-like listing.
 */

static void
verbose(char *nam_p)
{
        int i, j, temp;
        mode_t mode;
        char modestr[12];
        time_t  ttime;

        /*
         * The printf format and associated arguments to print the current
         * filename.  Normally, just nam_p.  If we're processing an extended
         * attribute, these are overridden.
         */
        char *name_fmt = "%s";
        const char *name = nam_p;
        const char *attribute = NULL;

        if (Gen.g_attrnam_p != NULL) {
                /*
                 * Translation note:
                 * 'attribute' is a noun.
                 */

                if (Gen.g_rw_sysattr) {
                        name_fmt = gettext("%s system attribute %s");
                } else if ((Args & OCt) &&
                    (is_sysattr(basename(Gen.g_attrnam_p)))) {
                        name_fmt = gettext("%s system attribute %s");
                } else {
                        name_fmt = gettext("%s attribute %s");
                }

                name = (Args & OCp) ? nam_p : Gen.g_attrfnam_p;
                if (Gen.g_attrparent_p == NULL) {
                        attribute = Gen.g_attrnam_p;
                } else {
                        attribute = Gen.g_attrpath_p;
                }
        }

        if ((Gen.g_mode == SECMODE) || ((Hdr_type == USTAR ||
            Hdr_type == TAR) && Thdr_p->tbuf.t_typeflag == 'A')) {
                /* dont print ancillary file */
                aclchar = '+';
                return;
        }
        for (i = 0; i < 11; i++)
                modestr[i] = '-';
        modestr[i] = '\0';
        modestr[i-1] = aclchar;
        aclchar = ' ';

        if ((Args & OCt) && (Args & OCv)) {
                mode = Gen.g_mode;
                for (i = 0; i < 3; i++) {
                        temp = (mode >> (6 - (i * 3)));
                        j = (i * 3) + 1;
                        if (S_IROTH & temp)
                                modestr[j] = 'r';
                        if (S_IWOTH & temp)
                                modestr[j + 1] = 'w';
                        if (S_IXOTH & temp)
                                modestr[j + 2] = 'x';
                }

                if (Hdr_type != BAR) {
                        temp = Gen.g_mode & Ftype;
                        switch (temp) {
                        case (S_IFIFO):
                                modestr[0] = 'p';
                                break;
                        case (S_IFSOCK):
                                modestr[0] = 's';
                                break;
                        case (S_IFCHR):
                                modestr[0] = 'c';
                                break;
                        case (S_IFDIR):
                                modestr[0] = 'd';
                                break;
                        case (S_IFBLK):
                                modestr[0] = 'b';
                                break;
                        case (S_IFREG): /* was initialized to '-' */
                                break;
                        case (S_IFLNK):
                                modestr[0] = 'l';
                                break;
                        default:
                                msg(ERR, "Impossible file type");
                        }
                } else {                /* bar */
                        temp = Gen.g_mode & Ftype;
                        switch (temp) {
                        case (S_IFIFO):
                                modestr[0] = 'p';
                                break;
                        case (S_IFSOCK):
                                modestr[0] = 's';
                                break;
                        case (S_IFCHR):
                                modestr[0] = 'c';
                                break;
                        case (S_IFDIR):
                                modestr[0] = 'd';
                                break;
                        case (S_IFBLK):
                                modestr[0] = 'b';
                                break;
                        }
                        if (bar_linkflag == SYMTYPE)
                                modestr[0] = 'l';
                }
                if ((S_ISUID & Gen.g_mode) == S_ISUID)
                        modestr[3] = 's';
                if ((S_ISVTX & Gen.g_mode) == S_ISVTX)
                        modestr[9] = 't';
                if ((S_ISGID & G_p->g_mode) == S_ISGID && modestr[6] == 'x')
                        modestr[6] = 's';
                else if ((S_ENFMT & Gen.g_mode) == S_ENFMT && modestr[6] != 'x')
                        modestr[6] = 'l';
                if ((Hdr_type == TAR || Hdr_type == USTAR) && Gen.g_nlink == 0)
                        (void) printf("%s%4d ", modestr, (int)Gen.g_nlink+1);
                else
                        (void) printf("%s%4d ", modestr, (int)Gen.g_nlink);
                if (Lastuid == (uid_t)Gen.g_uid) {
                        if (Lastuid == (uid_t)-1)
                                (void) printf("-1       ");
                        else
                                (void) printf("%-9s", Curpw_p->pw_name);
                } else {
                        if ((Curpw_p = getpwuid((int)Gen.g_uid)) != NULL) {
                                (void) printf("%-9s", Curpw_p->pw_name);
                                Lastuid = (uid_t)Gen.g_uid;
                        } else {
                                (void) printf("%-9d", (int)Gen.g_uid);
                                Lastuid = (uid_t)-1;
                        }
                }
                if (Lastgid == (gid_t)Gen.g_gid) {
                        if (Lastgid == (gid_t)-1)
                                (void) printf("-1       ");
                        else
                                (void) printf("%-9s", Curgr_p->gr_name);
                } else {
                        if ((Curgr_p = getgrgid((int)Gen.g_gid)) != NULL) {
                                (void) printf("%-9s", Curgr_p->gr_name);
                                Lastgid = (gid_t)Gen.g_gid;
                        } else {
                                (void) printf("%-9d", (int)Gen.g_gid);
                                Lastgid = (gid_t)-1;
                        }
                }

                /* print file size */
                if (!Aspec || ((Gen.g_mode & Ftype) == S_IFIFO) ||
                    ((Gen.g_mode & Ftype) == S_IFSOCK) ||
                    (Hdr_type == BAR && bar_linkflag == SYMTYPE)) {
                        off_t filesz = Gen.g_filesz;

                        if (S_ISSPARSE(Gen.g_mode) && Gen.g_holes != NULL)
                                filesz = Gen.g_holes->orig_size;

                        (void) printf("%*" PRId64 " ",
                            filesz < (1L << 31) ? 7 : 11, filesz);
                } else {
                        (void) printf("%3" PRId32 ",%3" PRId32 " ",
                            major(Gen.g_rdev), minor(Gen.g_rdev));
                }
                ttime = Gen.g_mtime;
                (void) strftime(Time, sizeof (Time),
                    dcgettext(NULL, FORMAT, LC_TIME), localtime(&ttime));
                (void) printf("%s, ", Time);
                str_fprintf(stdout, name_fmt, name, attribute);
                if ((Gen.g_mode & Ftype) == S_IFLNK) {
                        if (Hdr_type == USTAR || Hdr_type == TAR) {
                                (void) strcpy(Symlnk_p,
                                    Thdr_p->tbuf.t_linkname);
                        } else {
                                FILL(Gen.g_filesz);
                                (void) strncpy(Symlnk_p, Buffr.b_out_p,
                                    Gen.g_filesz);
                                *(Symlnk_p + Gen.g_filesz) = '\0';
                        }
                        (void) printf(" -> %s", Symlnk_p);
                }
                if (Hdr_type == BAR) {
                        if (bar_linkflag == SYMTYPE)
                                (void) printf(gettext(" symbolic link to %s"),
                                    bar_linkname);
                        else if (bar_linkflag == '1')
                                (void) printf(gettext(" linked to %s"),
                                    bar_linkname);
                }
                if ((Hdr_type == USTAR || Hdr_type == TAR) &&
                    Thdr_p->tbuf.t_typeflag == '1') {
                        (void) printf(gettext(" linked to %s%s%s"),
                            (Gen.g_attrnam_p == NULL) ?
                            Thdr_p->tbuf.t_linkname : Gen.g_attrfnam_p,
                            (Gen.g_attrnam_p == NULL) ? "" :
                            gettext(" attribute "),
                            (Gen.g_attrnam_p == NULL) ?
                            "" : Gen.g_linktoattrnam_p);
                }
                (void) printf("\n");
        } else if ((Args & OCt) || (Args & OCv)) {
                str_fprintf(Out_p, name_fmt, name, attribute);
                (void) fputc('\n', Out_p);
        } else { /* OCV */
                (void) fputc('.', Out_p);
                if (Verbcnt++ >= 49) { /* start a new line of dots */
                        Verbcnt = 0;
                        (void) fputc('\n', Out_p);
                }
        }
        (void) fflush(Out_p);
}

/*
 * write_hdr: Transfer header information for the generic structure
 * into the format for the selected header and bwrite() the header.
 */

static void
write_hdr(int arcflag, off_t len)
{
        int cnt = 0, pad;
        mode_t mode = 0;
        major_t maj;
        minor_t min;
        uid_t uid;
        gid_t gid;
        const char warnfmt[] = "%s%s%s : %s";

        switch (arcflag) {
        case ARCHIVE_ACL:
                mode = SECMODE;
                break;

        case ARCHIVE_XATTR:
        case ARCHIVE_NORMAL:
                /*
                 * If attribute is being archived in cpio format then
                 * zap off the file type bits since those are truly a
                 * mask and reset them with _XATTR_CPIO_MODE
                 */
                /*
                 * len is the value of g_filesz for normal files
                 * and the length of the special header buffer in
                 * the case of acl and xattr headers.
                 */
                if (G_p->g_attrnam_p != NULL && Hdr_type != USTAR &&
                    Hdr_type != TAR) {
                        mode = (G_p->g_mode & POSIXMODES) | _XATTR_CPIO_MODE;
                } else {
                        mode = G_p->g_mode;
                }
                if (arcflag != ARCHIVE_XATTR) {
                        len = G_p->g_filesz;
                }
                break;

        case ARCHIVE_SPARSE:
                mode = G_p->g_mode | C_ISSPARSE;
                len = G_p->g_filesz;
                break;
        }

        uid = G_p->g_uid;
        gid = G_p->g_gid;
        /*
         * Handle EFT uids and gids.  If they get too big
         * to be represented in a particular format, force 'em to 'nobody'.
         */
        switch (Hdr_type) {
        case BIN:                       /* 16-bits of u_short */
                if ((uint_t)uid > (uint_t)USHRT_MAX)
                        uid = UID_NOBODY;
                if ((uint_t)gid > (uint_t)USHRT_MAX)
                        gid = GID_NOBODY;
                break;
        case CHR:                       /* %.6o => 262143 base 10 */
                if ((uint_t)uid > (uint_t)0777777)
                        uid = UID_NOBODY;
                if ((uint_t)gid > (uint_t)0777777)
                        gid = GID_NOBODY;
                break;
        case ASC:                       /* %.8x => full 32 bits */
        case CRC:
                break;
        case USTAR:
        case TAR:                       /* %.7o => 2097151 base 10 */
                if ((uint_t)uid > (uint_t)07777777)
                        uid = UID_NOBODY;
                if ((uint_t)gid > (uint_t)07777777)
                        gid = GID_NOBODY;
                break;
        default:
                msg(EXT, "Impossible header type.");
        }

        /*
         * Since cpio formats -don't- encode the symbolic names, print
         * a warning message when we map the uid or gid this way.
         * Also, if the ownership just changed, clear set[ug]id bits
         *
         * (Except for USTAR format of course, where we have a string
         * representation of the username embedded in the header)
         */
        if (uid != G_p->g_uid && Hdr_type != USTAR) {
                msg(ERR, warnfmt,
                    (G_p->g_attrnam_p == NULL) ?
                    G_p->g_nam_p : G_p->g_attrfnam_p,
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p,
                    gettext("uid too large for archive format"));
                if (S_ISREG(mode))
                        mode &= ~S_ISUID;
        }
        if (gid != G_p->g_gid && Hdr_type != USTAR) {
                msg(ERR, warnfmt,
                    (G_p->g_attrnam_p == NULL) ?
                    G_p->g_nam_p : G_p->g_attrfnam_p,
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_rw_sysattr ?
                    gettext(" System Attribute ") : gettext(" Attribute "),
                    (G_p->g_attrnam_p == NULL) ? "" : G_p->g_attrnam_p,
                    gettext("gid too large for archive format"));
                if (S_ISREG(mode))
                        mode &= ~S_ISGID;
        }

        switch (Hdr_type) {
        case BIN:
        case CHR:
        case ASC:
        case CRC:
                cnt = Hdrsz + G_p->g_namesz;
                break;
        case TAR:
                /*FALLTHROUGH*/
        case USTAR: /* TAR and USTAR */
                cnt = TARSZ;
                break;
        default:
                msg(EXT, "Impossible header type.");
        }
        FLUSH(cnt);

        switch (Hdr_type) {
        case BIN:
                Hdr.h_magic = (short)G_p->g_magic;
                Hdr.h_dev = G_p->g_dev;
                Hdr.h_ino = G_p->g_ino;
                Hdr.h_uid = uid;
                Hdr.h_gid = gid;
                Hdr.h_mode = mode;
                Hdr.h_nlink = G_p->g_nlink;
                maj = major(G_p->g_rdev);
                min = minor(G_p->g_rdev);
                if (maj > (uint_t)OMAXMAJ)
                        maj = 0;
                if (min > (uint_t)OMAXMIN)
                        min = 0;
                Hdr.h_rdev = TO_SVR3(maj, min);
                mkshort(Hdr.h_mtime, (long)G_p->g_mtime);
                Hdr.h_namesize = (short)G_p->g_namesz;
                mkshort(Hdr.h_filesize, (long)len);
                (void) strcpy(Hdr.h_name, G_p->g_nam_p);
                (void) memcpy(Buffr.b_in_p, &Hdr, cnt);
                break;
        case CHR:
                (void) sprintf(Buffr.b_in_p,
                    "%.6o%.6o%.6o%.6o"
                    "%.6" _PRIoID "%.6" _PRIoID "%.6o"
                    "%.6" PRIo16
                    "%.11o%.6o%.11" PRIo64
                    "%s",
                    G_p->g_magic, G_p->g_dev, G_p->g_ino, mode,
                    uid, gid, G_p->g_nlink,
                    (unsigned short)(G_p->g_rdev & 0xffff),
                    G_p->g_mtime, G_p->g_namesz, len,
                    G_p->g_nam_p);
                break;
        case ASC:
        case CRC:
                (void) sprintf(Buffr.b_in_p,
                    "%.6x%.8x%.8x"
                    "%.8" _PRIxID "%.8" _PRIxID
                    "%.8x%.8x%.8x"
                    "%.8" PRIx32 "%.8" PRIx32 "%.8" PRIx32 "%.8" PRIx32
                    "%.8x%.8x%s",
                    G_p->g_magic, G_p->g_ino, mode,
                    G_p->g_uid, G_p->g_gid,
                    G_p->g_nlink, G_p->g_mtime, (uint_t)len,
                    major(G_p->g_dev), minor(G_p->g_dev),
                    major(G_p->g_rdev), minor(G_p->g_rdev),
                    G_p->g_namesz, G_p->g_cksum, G_p->g_nam_p);
                break;
        case USTAR:
                Thdr_p = (union tblock *)Buffr.b_in_p;
                (void) memset(Thdr_p, 0, TARSZ);
                (void) strncpy(Thdr_p->tbuf.t_name, G_p->g_tname,
                    strlen(G_p->g_tname));
                (void) sprintf(Thdr_p->tbuf.t_mode, "%07o", mode);
                (void) sprintf(Thdr_p->tbuf.t_uid, "%07" _PRIoID, uid);
                (void) sprintf(Thdr_p->tbuf.t_gid, "%07" _PRIoID, gid);
                (void) sprintf(Thdr_p->tbuf.t_size, "%011" PRIo64, len);
                (void) sprintf(Thdr_p->tbuf.t_mtime, "%011o", G_p->g_mtime);
                if (arcflag == ARCHIVE_ACL) {
                        Thdr_p->tbuf.t_typeflag = 'A';  /* ACL file type */
                } else if (arcflag == ARCHIVE_XATTR ||
                    (G_p->g_attrnam_p != NULL)) {
                        Thdr_p->tbuf.t_typeflag = _XATTR_HDRTYPE;
                } else {
                        Thdr_p->tbuf.t_typeflag = G_p->g_typeflag;
                }
                if (T_lname[0] != '\0') {
                        /*
                         * if not a symbolic link
                         */
                        if (((G_p->g_mode & Ftype) != S_IFLNK) &&
                            (G_p->g_attrnam_p == NULL)) {
                                Thdr_p->tbuf.t_typeflag = LNKTYPE;
                                (void) sprintf(Thdr_p->tbuf.t_size,
                                    "%011lo", 0L);
                        }
                        (void) strncpy(Thdr_p->tbuf.t_linkname, T_lname,
                            strlen(T_lname));
                }
                (void) strcpy(Thdr_p->tbuf.t_magic, TMAGIC);
                (void) memcpy(Thdr_p->tbuf.t_version, TVERSION, 2);
                (void) strcpy(Thdr_p->tbuf.t_uname, G_p->g_uname);
                (void) strcpy(Thdr_p->tbuf.t_gname, G_p->g_gname);
                (void) sprintf(Thdr_p->tbuf.t_devmajor, "%07" PRIo32,
                    major(G_p->g_rdev));
                (void) sprintf(Thdr_p->tbuf.t_devminor, "%07" PRIo32,
                    minor(G_p->g_rdev));
                if (Gen.g_prefix) {
                        (void) strcpy(Thdr_p->tbuf.t_prefix, Gen.g_prefix);
                        free(Gen.g_prefix);
                        Gen.g_prefix = NULL;
                } else {
                        Thdr_p->tbuf.t_prefix[0] = '\0';
                }
                (void) sprintf(Thdr_p->tbuf.t_cksum, "%07o",
                    cksum(TARTYP, 0, NULL));
                break;
        case TAR:
                Thdr_p = (union tblock *)Buffr.b_in_p;
                (void) memset(Thdr_p, 0, TARSZ);
                (void) strncpy(Thdr_p->tbuf.t_name, G_p->g_nam_p,
                    G_p->g_namesz);
                (void) sprintf(Thdr_p->tbuf.t_mode, "%07o ", mode);
                (void) sprintf(Thdr_p->tbuf.t_uid, "%07" _PRIoID " ", uid);
                (void) sprintf(Thdr_p->tbuf.t_gid, "%07" _PRIoID " ", gid);
                (void) sprintf(Thdr_p->tbuf.t_size, "%011" PRIo64 " ", len);
                (void) sprintf(Thdr_p->tbuf.t_mtime, "%011o ", G_p->g_mtime);
                if (T_lname[0] != '\0') {
                        Thdr_p->tbuf.t_typeflag = '1';
                } else {
                        Thdr_p->tbuf.t_typeflag = '\0';
                }
                (void) strncpy(Thdr_p->tbuf.t_linkname, T_lname,
                    strlen(T_lname));
                break;
        default:
                msg(EXT, "Impossible header type.");
        } /* Hdr_type */

        Buffr.b_in_p += cnt;
        Buffr.b_cnt += cnt;
        pad = ((cnt + Pad_val) & ~Pad_val) - cnt;
        if (pad != 0) {
                FLUSH(pad);
                (void) memset(Buffr.b_in_p, 0, pad);
                Buffr.b_in_p += pad;
                Buffr.b_cnt += pad;
        }
}

/*
 * write_trail: Create the appropriate trailer for the selected header type
 * and bwrite the trailer.  Pad the buffer with nulls out to the next Bufsize
 * boundary, and force a write.  If the write completes, or if the trailer is
 * completely written (but not all of the padding nulls (as can happen on end
 * of medium)) return.  Otherwise, the trailer was not completely written out,
 * so re-pad the buffer with nulls and try again.
 */

static void
write_trail(void)
{
        int cnt, need;

        switch (Hdr_type) {
        case BIN:
                Gen.g_magic = CMN_BIN;
                break;
        case CHR:
                Gen.g_magic = CMN_BIN;
                break;
        case ASC:
                Gen.g_magic = CMN_ASC;
                break;
        case CRC:
                Gen.g_magic = CMN_CRC;
                break;
        }

        switch (Hdr_type) {
        case BIN:
        case CHR:
        case ASC:
        case CRC:
                Gen.g_mode = Gen.g_uid = Gen.g_gid = 0;
                Gen.g_nlink = 1;
                Gen.g_mtime = Gen.g_ino = Gen.g_dev = 0;
                Gen.g_rdev = Gen.g_cksum = 0;
                Gen.g_filesz = (off_t)0;
                Gen.g_namesz = strlen("TRAILER!!!") + 1;
                (void) strcpy(Gen.g_nam_p, "TRAILER!!!");
                G_p = &Gen;
                write_hdr(ARCHIVE_NORMAL, (off_t)0);
                break;
        case TAR:
        /*FALLTHROUGH*/
        case USTAR: /* TAR and USTAR */
                for (cnt = 0; cnt < 3; cnt++) {
                        FLUSH(TARSZ);
                        (void) memset(Buffr.b_in_p, 0, TARSZ);
                        Buffr.b_in_p += TARSZ;
                        Buffr.b_cnt += TARSZ;
                }
                break;
        default:
                msg(EXT, "Impossible header type.");
        }
        need = Bufsize - (Buffr.b_cnt % Bufsize);
        if (need == Bufsize)
                need = 0;

        while (Buffr.b_cnt > 0) {
                while (need > 0) {
                        cnt = (need < TARSZ) ? need : TARSZ;
                        need -= cnt;
                        FLUSH(cnt);
                        (void) memset(Buffr.b_in_p, 0, cnt);
                        Buffr.b_in_p += cnt;
                        Buffr.b_cnt += cnt;
                }
                bflush();
        }
}

/*
 * if archives in USTAR format, check if typeflag == '5' for directories
 */
static int
ustar_dir(void)
{
        if (Hdr_type == USTAR || Hdr_type == TAR) {
                if (Thdr_p->tbuf.t_typeflag == '5')
                        return (1);
        }
        return (0);
}

/*
 * if archives in USTAR format, check if typeflag == '3' || '4' || '6'
 * for character, block, fifo special files
 */
static int
ustar_spec(void)
{
        int typeflag;

        if (Hdr_type == USTAR || Hdr_type == TAR) {
                typeflag = Thdr_p->tbuf.t_typeflag;
                if (typeflag == '3' || typeflag == '4' || typeflag == '6')
                        return (1);
        }
        return (0);
}

/*
 * The return value is a pointer to a converted copy of the information in
 * FromStat if the file is representable in -Hodc format, and NULL otherwise.
 */

static struct stat *
convert_to_old_stat(struct stat *FromStat, char *namep, char *attrp)
{
        static struct stat ToSt;
        cpioinfo_t TmpSt;

        (void) memset(&TmpSt, 0, sizeof (cpioinfo_t));
        stat_to_svr32_stat(&TmpSt, FromStat);
        (void) memset(&ToSt, 0, sizeof (ToSt));

        if (TmpSt.st_rdev == (o_dev_t)NODEV &&
            (((TmpSt.st_mode & Ftype) == S_IFCHR) ||
            ((TmpSt.st_mode & Ftype) == S_IFBLK))) {
                /*
                 * Encountered a problem representing the rdev information.
                 * Don't archive it.
                 */

                msg(ERR, "Error -Hodc format can't support expanded"
                    "types on %s%s%s",
                    namep,
                    (attrp == NULL) ? "" : gettext(" Attribute"),
                    (attrp == NULL) ? "" : attrp);
                return (NULL);
        }

        if (TmpSt.st_dev == (o_dev_t)NODEV) {
                /*
                 * Having trouble representing the device/inode pair.  We can't
                 * track links in this case; break them all into separate
                 * files.
                 */

                TmpSt.st_ino = 0;

                if (((TmpSt.st_mode & Ftype) != S_IFDIR) &&
                    TmpSt.st_nlink > 1)
                        msg(POST,
                            "Warning: file %s%s%s has large "
                            "device number - linked "
                            "files will be restored as "
                            "separate files",
                            namep,
                            (attrp == NULL) ? "" : gettext(" Attribute"),
                            (attrp == NULL) ? "" : attrp);

                /* ensure no links */

                TmpSt.st_nlink = 1;
        }

        /* Start converting values */

        if (TmpSt.st_dev < 0) {
                ToSt.st_dev = 0;
        } else {
                ToSt.st_dev = (dev_t)TmpSt.st_dev;
        }

        /* -actual- not truncated uid */

        ToSt.st_uid = TmpSt.st_uid;

        /* -actual- not truncated gid */

        ToSt.st_gid = TmpSt.st_gid;
        ToSt.st_ino = (ino_t)TmpSt.st_ino;
        ToSt.st_mode = (mode_t)TmpSt.st_mode;
        ToSt.st_mtime = (uint_t)TmpSt.st_modtime;
        ToSt.st_nlink = (nlink_t)TmpSt.st_nlink;
        ToSt.st_size = (off_t)TmpSt.st_size;
        ToSt.st_rdev = (dev_t)TmpSt.st_rdev;

        return (&ToSt);
}

/*
 * In the beginning of each bar archive, there is a header which describes the
 * current volume being created, followed by a header which describes the
 * current file being created, followed by the file itself.  If there is
 * more than one file to be created, a separate header will be created for
 * each additional file.  This structure may be repeated if the bar archive
 * contains multiple volumes.  If a file spans across volumes, its header
 * will not be repeated in the next volume.
 *               +------------------+
 *               |    vol header    |
 *               |------------------|
 *               |   file header i  |     i = 0
 *               |------------------|
 *               |     <file i>     |
 *               |------------------|
 *               |  file header i+1 |
 *               |------------------|
 *               |    <file i+1>    |
 *               |------------------|
 *               |        .         |
 *               |        .         |
 *               |        .         |
 *               +------------------+
 */

/*
 * read in the header that describes the current volume of the bar archive
 * to be extracted.
 */
static void
read_bar_vol_hdr(void)
{
        union b_block *tmp_hdr;

        tmp_hdr = (union b_block *)Buffr.b_out_p;
        if (tmp_hdr->dbuf.bar_magic[0] == BAR_VOLUME_MAGIC) {

                if (bar_Vhdr == NULL) {
                        bar_Vhdr = e_zalloc(E_EXIT, TBLOCK);
                }
                (void) memcpy(&(bar_Vhdr->dbuf), &(tmp_hdr->dbuf), TBLOCK);
        } else {
                (void) fprintf(stderr, gettext(
                    "bar error: cannot read volume header\n"));
                exit(1);
        }

        (void) sscanf(bar_Vhdr->dbuf.mode, "%8l", &Gen_bar_vol.g_mode);
        (void) sscanf(bar_Vhdr->dbuf.uid, "%8" _SCNdID, &Gen_bar_vol.g_uid);
        (void) sscanf(bar_Vhdr->dbuf.gid, "%8" _SCNdID, &Gen_bar_vol.g_gid);
        (void) sscanf(bar_Vhdr->dbuf.size, "%12" SCNo64,
            (u_off_t *)&Gen_bar_vol.g_filesz);
        (void) sscanf(bar_Vhdr->dbuf.mtime, "%12o", &Gen_bar_vol.g_mtime);
        (void) sscanf(bar_Vhdr->dbuf.chksum, "%8o", &Gen_bar_vol.g_cksum);

        /* set the compress flag */
        if (bar_Vhdr->dbuf.compressed == '1')
                Compressed = 1;
        else
                Compressed = 0;

        Buffr.b_out_p += 512;
        Buffr.b_cnt -= 512;

        /*
         * not the first volume; exit
         */
        if (strcmp(bar_Vhdr->dbuf.volume_num, "1") != 0) {
                (void) fprintf(stderr,
                    gettext("error: This is not volume 1.  "));
                (void) fprintf(stderr, gettext("This is volume %s.  "),
                    bar_Vhdr->dbuf.volume_num);
                (void) fprintf(stderr, gettext("Please insert volume 1.\n"));
                exit(1);
        }

        read_bar_file_hdr();
}

/*
 * read in the header that describes the current file to be extracted
 */
static void
read_bar_file_hdr(void)
{
        union b_block *tmp_hdr;
        char *start_of_name, *name_p;
        char *tmp;
        major_t maj;
        minor_t min;

        if (*Buffr.b_out_p == '\0') {
                *Gen.g_nam_p = '\0';
                exit(0);
        }

        tmp_hdr = (union b_block *)Buffr.b_out_p;

        tmp = &tmp_hdr->dbuf.mode[1];
        (void) sscanf(tmp, "%8o", &Gen.g_mode);
        (void) sscanf(tmp_hdr->dbuf.uid, "%8" _SCNoID, &Gen.g_uid);
        (void) sscanf(tmp_hdr->dbuf.gid, "%8" _SCNoID, &Gen.g_gid);
        (void) sscanf(tmp_hdr->dbuf.size, "%12" SCNo64,
            (u_off_t *)&Gen.g_filesz);
        (void) sscanf(tmp_hdr->dbuf.mtime, "%12o", &Gen.g_mtime);
        (void) sscanf(tmp_hdr->dbuf.chksum, "%8o", &Gen.g_cksum);
        (void) sscanf(tmp_hdr->dbuf.rdev, "%8o", &Gen.g_rdev);

        maj = SVR3_MAJOR(Gen.g_rdev);
        min = SVR3_MINOR(Gen.g_rdev);
        Gen.g_rdev = makedev(maj, min);
        bar_linkflag = tmp_hdr->dbuf.linkflag;
        start_of_name = &tmp_hdr->dbuf.start_of_name;


        name_p = Gen.g_nam_p;
        while ((*name_p++ = *start_of_name++) != '\0')
                ;
        *name_p = '\0';
        if (bar_linkflag == LNKTYPE || bar_linkflag == SYMTYPE)
                (void) strcpy(bar_linkname, start_of_name);

        Gen.g_namesz = strlen(Gen.g_nam_p) + 1;
        (void) strcpy(nambuf, Gen.g_nam_p);
}

/*
 * if the bar archive is compressed, set up a pipe and do the de-compression
 * as the compressed file is read in.
 */
static void
setup_uncompress(FILE **pipef)
{
        char *cmd_buf;
        size_t cmdlen;

        cmd_buf = e_zalloc(E_EXIT, MAXPATHLEN * 2);

        if (access(Gen.g_nam_p, W_OK) != 0) {
                cmdlen = snprintf(cmd_buf, MAXPATHLEN * 2,
                    "chmod +w '%s'; uncompress -c > '%s'; "
                    "chmod 0%o '%s'",
                    Gen.g_nam_p, Gen.g_nam_p, G_p->g_mode, Gen.g_nam_p);
        } else {
                cmdlen = snprintf(cmd_buf, MAXPATHLEN * 2,
                    "uncompress -c > '%s'", Gen.g_nam_p);
        }

        if (cmdlen >= MAXPATHLEN * 2 ||
            (*pipef = popen(cmd_buf, "w")) == NULL) {
                (void) fprintf(stderr, gettext("error\n"));
                exit(1);
        }

        if (close(Ofile) != 0)
                msg(EXTN, "close error");
        Ofile = fileno(*pipef);

        free(cmd_buf);
}

/*
 * if the bar archive spans multiple volumes, read in the header that
 * describes the next volume.
 */
static void
skip_bar_volhdr(void)
{
        char *buff;
        union b_block *tmp_hdr;

        buff = e_zalloc(E_EXIT, (uint_t)Bufsize);

        if (g_read(Device, Archive, buff, Bufsize) < 0) {
                (void) fprintf(stderr, gettext(
                    "error in skip_bar_volhdr\n"));
        } else {

                tmp_hdr = (union b_block *)buff;
                if (tmp_hdr->dbuf.bar_magic[0] == BAR_VOLUME_MAGIC) {

                        if (bar_Vhdr == NULL) {
                                bar_Vhdr = e_zalloc(E_EXIT, TBLOCK);
                        }
                        (void) memcpy(&(bar_Vhdr->dbuf),
                            &(tmp_hdr->dbuf), TBLOCK);
                } else {
                        (void) fprintf(stderr,
                            gettext("cpio error: cannot read bar volume "
                            "header\n"));
                        exit(1);
                }

                (void) sscanf(bar_Vhdr->dbuf.mode, "%8o",
                    &Gen_bar_vol.g_mode);
                (void) sscanf(bar_Vhdr->dbuf.uid, "%8" _SCNoID,
                    &Gen_bar_vol.g_uid);
                (void) sscanf(bar_Vhdr->dbuf.gid, "%8" _SCNoID,
                    &Gen_bar_vol.g_gid);
                (void) sscanf(bar_Vhdr->dbuf.size, "%12" SCNo64,
                    (u_off_t *)&Gen_bar_vol.g_filesz);
                (void) sscanf(bar_Vhdr->dbuf.mtime, "%12o",
                    &Gen_bar_vol.g_mtime);
                (void) sscanf(bar_Vhdr->dbuf.chksum, "%8o",
                    &Gen_bar_vol.g_cksum);
                if (bar_Vhdr->dbuf.compressed == '1')
                        Compressed = 1;
                else
                        Compressed = 0;
        }

        /*
         * Now put the rest of the bytes read in into the data buffer.
         */
        (void) memcpy(Buffr.b_in_p, &buff[512], (Bufsize - 512));
        Buffr.b_in_p += (Bufsize - 512);
        Buffr.b_cnt += (long)(Bufsize - 512);

        free(buff);
}

/*
 * check the linkflag which indicates the type of the file to be extracted,
 * invoke the corresponding routine to extract the file.
 */
static void
bar_file_in(void)
{
        /*
         * the file is a directory
         */
        if (Adir) {
                if (ckname(1) != F_SKIP && creat_spec(G_p->g_dirfd) > 0) {
                        VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
                }
                return;
        }

        switch (bar_linkflag) {
        case REGTYPE:
                /* regular file */
                if ((ckname(1) == F_SKIP) ||
                    (Ofile = openout(G_p->g_dirfd)) < 0) {
                        data_in(P_SKIP);
                } else {
                        data_in(P_PROC);
                }
                break;
        case LNKTYPE:
                /* hard link */
                if (ckname(1) == F_SKIP) {
                        break;
                }
                (void) creat_lnk(G_p->g_dirfd, bar_linkname, G_p->g_nam_p);
                break;
        case SYMTYPE:
                /* symbolic link */
                if ((ckname(1) == F_SKIP) ||
                    (Ofile = openout(G_p->g_dirfd)) < 0) {
                        data_in(P_SKIP);
                } else {
                        data_in(P_PROC);
                }
                break;
        case CHRTYPE:
                /* character device or FIFO */
                if (ckname(1) != F_SKIP && creat_spec(G_p->g_dirfd) > 0) {
                        VERBOSE((Args & (OCv | OCV)), G_p->g_nam_p);
                }
                break;
        default:
                (void) fprintf(stderr, gettext("error: unknown file type\n"));
                break;
        }
}


/*
 * This originally came from libgenIO/g_init.c
 * XXX  And it is very broken.
 */

/* #include <sys/statvfs.h> */
#include <ftw.h>
/* #include <libgenIO.h> */
#define G_TM_TAPE       1       /* Tapemaster controller    */
#define G_XY_DISK       3       /* xy disks             */
#define G_SD_DISK       7       /* scsi sd disk         */
#define G_XT_TAPE       8       /* xt tapes             */
#define G_SF_FLOPPY     9       /* sf floppy            */
#define G_XD_DISK       10      /* xd disks             */
#define G_ST_TAPE       11      /* scsi tape            */
#define G_NS            12      /* noswap pseudo-dev    */
#define G_RAM           13      /* ram pseudo-dev       */
#define G_FT            14      /* tftp                 */
#define G_HD            15      /* 386 network disk     */
#define G_FD            16      /* 386 AT disk          */
#define G_FILE          28      /* file, not a device   */
#define G_NO_DEV        29      /* device does not require special treatment */
#define G_DEV_MAX       30      /* last valid device type */

/*
 * g_init: Determine the device being accessed, set the buffer size,
 * and perform any device specific initialization. Since at this point
 * Sun has no system call to read the configuration, the major numbers
 * are assumed to be static and types are figured out as such. However,
 * as a rough estimate, the buffer size for all types is set to 512
 * as a default.
 */

static int
g_init(int *devtype, int *fdes)
{
        int bufsize;
        struct stat st_buf;
        struct statvfs stfs_buf;

        *devtype = G_NO_DEV;
        bufsize = -1;
        if (fstat(*fdes, &st_buf) == -1)
                return (-1);
        if (!S_ISCHR(st_buf.st_mode) && !S_ISBLK(st_buf.st_mode)) {
                if (S_ISFIFO(st_buf.st_mode)) {
                        bufsize = 512;
                } else {
                        /* find block size for this file system */
                        *devtype = G_FILE;
                        if (fstatvfs(*fdes, &stfs_buf) < 0) {
                                        bufsize = -1;
                                        errno = ENODEV;
                        } else
                                bufsize = stfs_buf.f_bsize;
                }

                return (bufsize);

        /*
         * We'll have to add a remote attribute to stat but this
         * should work for now.
         */
        } else if (st_buf.st_dev & 0x8000)      /* if remote  rdev */
                return (512);

        bufsize = 512;

        if (Hdr_type == BAR) {
                if (is_tape(*fdes)) {
                        bufsize = BAR_TAPE_SIZE;
                        msg(EPOST, "Archiving to tape blocking factor 126");
                } else if (is_floppy(*fdes)) {
                        bufsize = BAR_FLOPPY_SIZE;
                        msg(EPOST, "Archiving to floppy blocking factor 18");
                }
        }

        return (bufsize);
}

/*
 * This originally came from libgenIO/g_read.c
 */

/*
 * g_read: Read nbytes of data from fdes (of type devtype) and place
 * data in location pointed to by buf.  In case of end of medium,
 * translate (where necessary) device specific EOM indications into
 * the generic EOM indication of rv = -1, errno = ENOSPC.
 */

static int
g_read(int devtype, int fdes, char *buf, unsigned nbytes)
{
        int rv;

        if (devtype < 0 || devtype >= G_DEV_MAX) {
                errno = ENODEV;
                return (-1);
        }

        rv = read(fdes, buf, nbytes);

        /* st devices return 0 when no space left */
        if ((rv == 0 && errno == 0 && Hdr_type != BAR) ||
            (rv == -1 && errno == EIO)) {
                errno = 0;
                rv = 0;
        }

        return (rv);
}

/*
 * This originally came from libgenIO/g_write.c
 */

/*
 * g_write: Write nbytes of data to fdes (of type devtype) from
 * the location pointed to by buf.  In case of end of medium,
 * translate (where necessary) device specific EOM indications into
 * the generic EOM indication of rv = -1, errno = ENOSPC.
 */

static int
g_write(int devtype, int fdes, char *buf, unsigned nbytes)
{
        int rv;

        if (devtype < 0 || devtype >= G_DEV_MAX) {
                errno = ENODEV;
                return (-1);
        }

        rv = write(fdes, buf, nbytes);

        /* st devices return 0 when no more space left */
        if ((rv == 0 && errno == 0) || (rv == -1 && errno == EIO)) {
                errno = ENOSPC;
                rv = -1;
        }

        return (rv);
}

/*
 * Test for tape
 */

static int
is_tape(int fd)
{
        struct mtget stuff;

        /*
         * try to do a generic tape ioctl, just to see if
         * the thing is in fact a tape drive(er).
         */
        if (ioctl(fd, MTIOCGET, &stuff) != -1) {
                /* the ioctl succeeded, must have been a tape */
                return (1);
        }
        return (0);
}

/*
 * Test for floppy
 */

static int
is_floppy(int fd)
{
        struct fd_char stuff;

        /*
         * try to get the floppy drive characteristics, just to see if
         * the thing is in fact a floppy drive(er).
         */
        if (ioctl(fd, FDIOGCHAR, &stuff) != -1) {
                /* the ioctl succeeded, must have been a floppy */
                return (1);
        }

        return (0);
}

/*
 * New functions for ACLs and other security attributes
 */

/*
 * The function appends the new security attribute info to the end of
 * existing secinfo.
 */
static int
append_secattr(char     **secinfo,      /* existing security info */
    int                 *secinfo_len,   /* length of existing security info */
    acl_t               *aclp)  /* new attribute data pointer */
{
        char    *new_secinfo;
        char    *attrtext;
        size_t  newattrsize;
        int     oldsize;

        /* no need to add */
        if (aclp == NULL) {
                return (0);
        }

        switch (acl_type(aclp)) {
        case ACLENT_T:
        case ACE_T:
                attrtext = acl_totext(aclp, ACL_APPEND_ID | ACL_COMPACT_FMT |
                    ACL_SID_FMT);
                if (attrtext == NULL) {
                        msg(EPOST, "acltotext failed");
                        return (-1);
                }
                /* header: type + size = 8 */
                newattrsize = 8 + strlen(attrtext) + 1;
                attr = e_zalloc(E_NORMAL, newattrsize);
                if (attr == NULL) {
                        msg(EPOST, "can't allocate memory");
                        return (-1);
                }
                attr->attr_type = (acl_type(aclp) == ACLENT_T) ?
                    UFSD_ACL : ACE_ACL;
                /* acl entry count */
                (void) sprintf(attr->attr_len, "%06o", acl_cnt(aclp));
                (void) strcpy((char *)&attr->attr_info[0], attrtext);
                free(attrtext);
                break;

                /* SunFed's case goes here */

        default:
                msg(EPOST, "unrecognized attribute type");
                return (-1);
        }

        /* old security info + new attr header(8) + new attr */
        oldsize = *secinfo_len;
        *secinfo_len += newattrsize;
        new_secinfo = e_zalloc(E_NORMAL, (uint_t)*secinfo_len);
        if (new_secinfo == NULL) {
                msg(EPOST, "can't allocate memory");
                *secinfo_len -= newattrsize;
                return (-1);
        }

        (void) memcpy(new_secinfo, *secinfo, oldsize);
        (void) memcpy(new_secinfo + oldsize, attr, newattrsize);

        free(*secinfo);
        *secinfo = new_secinfo;
        return (0);
}

/*
 * Append size amount of data from buf to the archive.
 */
static void
write_ancillary(char *buf, size_t len, boolean_t padding)
{
        int     pad, cnt;

        if (len == 0)
                return;

        while (len > 0) {
                cnt = (unsigned)(len > CPIOBSZ) ? CPIOBSZ : len;
                FLUSH(cnt);
                errno = 0;
                (void) memcpy(Buffr.b_in_p, buf, (unsigned)cnt);
                Buffr.b_in_p += cnt;
                Buffr.b_cnt += cnt;
                len -= cnt;
                buf += cnt;
        }
        if (padding) {
                pad = (Pad_val + 1 - (cnt & Pad_val)) & Pad_val;
                if (pad != 0) {
                        FLUSH(pad);
                        (void) memset(Buffr.b_in_p, 0, pad);
                        Buffr.b_in_p += pad;
                        Buffr.b_cnt += pad;
                }
        }
}

static int
remove_dir(char *path)
{
        DIR             *name;
        struct dirent   *direct;
        struct stat     sbuf;
        char            *path_copy;

#define MSG1    "remove_dir() failed to stat(\"%s\") "
#define MSG2    "remove_dir() failed to remove_dir(\"%s\") "
#define MSG3    "remove_dir() failed to unlink(\"%s\") "

        /*
         * Open the directory for reading.
         */
        if ((name = opendir(path)) == NULL) {
                msg(ERRN, "remove_dir() failed to opendir(\"%s\") ", path);
                return (-1);
        }

        if (chdir(path) == -1) {
                msg(ERRN, "remove_dir() failed to chdir(\"%s\") ", path);
                return (-1);
        }

        /*
         * Read every directory entry.
         */
        while ((direct = readdir(name)) != NULL) {
                /*
                 * Ignore "." and ".." entries.
                 */
                if (strcmp(direct->d_name, ".") == 0 ||
                    strcmp(direct->d_name, "..") == 0)
                        continue;

                if (lstat(direct->d_name, &sbuf) == -1) {
                        msg(ERRN, MSG1, direct->d_name);
                        (void) closedir(name);
                        return (-1);
                }

                if (S_ISDIR(sbuf.st_mode)) {
                        if (remove_dir(direct->d_name) == -1) {
                                msg(ERRN, MSG2, direct->d_name);
                                (void) closedir(name);
                                return (-1);
                        }
                } else {
                        if (unlink(direct->d_name) == -1) {
                                msg(ERRN, MSG3, direct->d_name);
                                (void) closedir(name);
                                return (-1);
                        }
                }

        }

        /*
         * Close the directory we just finished reading.
         */
        (void) closedir(name);

        /*
         * Change directory to the parent directory...
         */
        if (chdir("..") == -1) {
                msg(ERRN, "remove_dir() failed to chdir(\"..\") ");
                return (-1);
        }

        /*
         * ...and finally remove the directory; note we have to
         * make a copy since basename is free to modify its input.
         */
        path_copy = e_strdup(E_NORMAL, path);
        if (path_copy == NULL) {
                msg(ERRN, "cannot strdup() the directory pathname ");
                return (-1);
        }

        if (rmdir(basename(path_copy)) == -1) {
                free(path_copy);
                msg(ERRN, "remove_dir() failed to rmdir(\"%s\") ", path);
                return (-1);
        }

        free(path_copy);
        return (0);

}

static int
save_cwd(void)
{
        return (open(".", O_RDONLY));
}

static void
rest_cwd(int cwd)
{
        (void) fchdir(cwd);
        (void) close(cwd);
}

#if defined(O_XATTR)
static void
xattrs_out(int (*func)())
{
        int dirpfd;
        int filefd;
        int arc_rwsysattr = 0;
        int rw_sysattr = 0;
        int ext_attr = 0;
        DIR *dirp;
        struct dirent *dp;
        int slen;
        int plen;
        char *namep, *savenamep;
        char *apathp;
        char *attrparent = Gen.g_attrparent_p;
        char *filename;

        if (attrparent == NULL) {
                filename = Gen.g_nam_p;
        } else {
                filename = Gen.g_attrnam_p;
        }

        /*
         * If the underlying file system supports it, then
         * archive the extended attributes if -@ was specified,
         * and the extended system attributes if -/ was
         * specified.
         */
        if (verify_attr_support(filename, (attrparent == NULL), ARC_CREATE,
            &ext_attr) != ATTR_OK) {
                return;
        }

#if defined(_PC_SATTR_ENABLED)
        if (SysAtflag) {
                int             filefd;
                nvlist_t        *slist = NULL;

                /*
                 * Determine if there are non-transient system
                 * attributes.
                 */
                errno = 0;
                if ((filefd = open(filename, O_RDONLY)) == -1) {
                        if (attrparent == NULL) {
                                msg(EXTN,
                                    "unable to open %s%s%sfile %s",
                                    (attrparent == NULL) ? "" :
                                    gettext("attribute "),
                                    (attrparent == NULL) ? "" : attrparent,
                                    (attrparent == NULL) ? "" : gettext(" of "),
                                    (attrparent == NULL) ? G_p->g_nam_p :
                                    G_p->g_attrfnam_p);
                        }
                }
                if (((slist = sysattr_list(myname, filefd,
                    filename)) != NULL) || (errno != 0)) {
                        arc_rwsysattr = 1;
                }
                if (slist != NULL) {
                        (void) nvlist_free(slist);
                        slist = NULL;
                }
                (void) close(filefd);
        }

        /*
         * If we aren't archiving extended system attributes, and we are
         * processing an attribute, or if we are archiving extended system
         * attributes, and there are are no extended attributes, then there's
         * no need to open up the attribute directory of the file unless the
         * extended system attributes are not transient (i.e, the system
         * attributes are not the default values).
         */
        if ((arc_rwsysattr == 0) && ((attrparent != NULL) ||
            (SysAtflag && !ext_attr))) {
                return;
        }

#endif  /* _PC_SATTR_ENABLED */

        /*
         * If aclp still exists then free it since it is was set when base
         * file was extracted.
         */
        if (aclp != NULL) {
                acl_free(aclp);
                aclp = NULL;
                acl_is_set = 0;
        }

        Gen.g_dirfd = attropen(filename, ".", O_RDONLY);
        if (Gen.g_dirfd == -1) {
                msg(ERRN, "Cannot open attribute directory of file \"%s%s%s\"",
                    (attrparent == NULL) ? "" : gettext("attribute "),
                    (attrparent == NULL) ? "" : attrparent,
                    (attrparent == NULL) ? "" : gettext(" of "), filename);
                return;

        }

        if (attrparent == NULL) {
                savenamep = G_p->g_nam_p;
        } else {
                savenamep = G_p->g_attrfnam_p;
        }

        if ((dirpfd = dup(Gen.g_dirfd)) == -1)  {
                msg(ERRN, "Cannot dup(2) attribute directory descriptor");
                return;
        }

        if ((dirp = fdopendir(dirpfd)) == NULL) {
                msg(ERRN, "Cannot fdopendir(3C) directory file descriptor");
                return;
        }

        if (attrparent == NULL) {
                Gen.g_baseparent_fd = save_cwd();
        }

        while ((dp = readdir(dirp)) != NULL) {
                if (strcmp(dp->d_name, "..") == 0) {
                        continue;
                }
                if (verify_attr(dp->d_name, attrparent,
                    arc_rwsysattr, &rw_sysattr) != ATTR_OK) {
                        continue;
                }

                if (strcmp(dp->d_name, ".") == 0) {
                        Hiddendir = 1;
                } else {
                        Hiddendir = 0;
                }

                Gen.g_rw_sysattr = rw_sysattr;
                Gen.g_attrnam_p = dp->d_name;

                if (STAT(Gen.g_dirfd, Gen.g_nam_p, &SrcSt) == -1) {
                        msg(ERRN,
                            "Could not fstatat(2) attribute \"%s\" of"
                            " file \"%s\"", dp->d_name, (attrparent == NULL) ?
                            savenamep : Gen.g_attrfnam_p);
                        continue;
                }

                if (Use_old_stat) {
                        Savedev = SrcSt.st_dev;
                        OldSt = convert_to_old_stat(&SrcSt,
                            Gen.g_nam_p, Gen.g_attrnam_p);

                        if (OldSt == NULL) {
                                msg(ERRN,
                                    "Could not convert to old stat format");
                                continue;
                        }
                }

                Gen.g_attrfnam_p = savenamep;

                /*
                 * Set up dummy header name
                 *
                 * One piece is written with .hdr, which
                 * contains the actual xattr hdr or pathing information
                 * then the name is updated to drop the .hdr off
                 * and the actual file itself is archived.
                 */
                slen = strlen(Gen.g_attrnam_p) + strlen(DEVNULL) +
                    strlen(XATTRHDR) + 2;       /* add one for '/' */
                if ((namep = e_zalloc(E_NORMAL, slen)) == NULL) {
                        msg(ERRN, "Could not calloc memory for attribute name");
                        continue;
                }
                (void) snprintf(namep, slen, "%s/%s%s",
                    DEVNULL, Gen.g_attrnam_p, XATTRHDR);
                Gen.g_nam_p = namep;

                plen = strlen(Gen.g_attrnam_p) + 1;
                if (Gen.g_attrparent_p != NULL) {
                        plen += strlen(Gen.g_attrparent_p) + 1;
                }
                if ((apathp = e_zalloc(E_NORMAL, plen)) == NULL) {
                        msg(ERRN, "Could not calloc memory for attribute name");
                        continue;
                }
                (void) snprintf(apathp, plen, "%s%s%s",
                    (Gen.g_attrparent_p == NULL) ? "" : Gen.g_attrparent_p,
                    (Gen.g_attrparent_p == NULL) ? "" : "/", Gen.g_attrnam_p);

                if (Gen.g_attrpath_p != NULL) {
                        free(Gen.g_attrpath_p);
                }
                Gen.g_attrpath_p = apathp;

                /*
                 * Get attribute's ACL info: don't bother allocating space
                 * if there are only standard permissions, i.e. ACL count < 4
                 */
                if (Pflag) {
                        filefd = openat(Gen.g_dirfd, dp->d_name, O_RDONLY);
                        if (filefd == -1) {
                                msg(ERRN,
                                    "Could not open attribute \"%s\" of"
                                    " file \"%s\"", dp->d_name, savenamep);
                                free(namep);
                                continue;
                        }
                        if (facl_get(filefd, ACL_NO_TRIVIAL, &aclp) != 0) {
                                msg(ERRN,
                                    "Error with acl() on %s",
                                    Gen.g_nam_p);
                        }
                        (void) close(filefd);
                }

                (void) creat_hdr();
                (void) (*func)();

#if defined(_PC_SATTR_ENABLED)
                /*
                 * Recursively call xattrs_out() to process the attribute's
                 * hidden attribute directory and read-write system attributes.
                 */
                if (SysAtflag && !Hiddendir && !rw_sysattr) {
                        int     savedirfd = Gen.g_dirfd;

                        (void) fchdir(Gen.g_dirfd);
                        Gen.g_attrparent_p = dp->d_name;
                        xattrs_out(func);
                        Gen.g_dirfd = savedirfd;
                        Gen.g_attrparent_p = NULL;
                }
#endif  /* _PC_SATTR_ENABLED */

                if (Gen.g_passdirfd != -1) {
                        (void) close(Gen.g_passdirfd);
                        Gen.g_passdirfd = -1;
                }
                Gen.g_attrnam_p = NULL;
                Gen.g_attrfnam_p = NULL;
                Gen.g_linktoattrfnam_p = NULL;
                Gen.g_linktoattrnam_p = NULL;
                Gen.g_rw_sysattr = 0;
                if (Gen.g_attrpath_p != NULL) {
                        free(Gen.g_attrpath_p);
                        Gen.g_attrpath_p = NULL;
                }

                if (aclp != NULL) {
                        acl_free(aclp);
                        aclp = NULL;
                        acl_is_set = 0;
                }
                free(namep);
        }

        (void) closedir(dirp);
        (void) close(Gen.g_dirfd);
        if (attrparent == NULL) {
                rest_cwd(Gen.g_baseparent_fd);
                Gen.g_dirfd = -1;
        }
        Hiddendir = 0;
}
#else
static void
xattrs_out(int (*func)())
{
}
#endif

/*
 * Return the parent directory of a given path.
 *
 * Examples:
 * /usr/tmp return /usr
 * /usr/tmp/file return /usr/tmp
 * /  returns .
 * /usr returns /
 * file returns .
 *
 * dir is assumed to be at least as big as path.
 */
static void
get_parent(char *path, char *dir)
{
        char *s;
        char tmpdir[PATH_MAX + 1];

        if (strlen(path) > PATH_MAX) {
                msg(EXT, "pathname is too long");
        }
        (void) strcpy(tmpdir, path);
        chop_endslashes(tmpdir);

        if ((s = strrchr(tmpdir, '/')) == NULL) {
                (void) strcpy(dir, ".");
        } else {
                s = skipslashes(s, tmpdir);
                *s = '\0';
                if (s == tmpdir)
                        (void) strcpy(dir, "/");
                else
                        (void) strcpy(dir, tmpdir);
        }
}

#if defined(O_XATTR)
#define ROUNDTOTBLOCK(a)                ((a + (TBLOCK -1)) & ~(TBLOCK -1))

static void
prepare_xattr_hdr(
        char            **attrbuf,
        char            *filename,
        char            *attrpath,
        char            typeflag,
        struct Lnk      *linkinfo,
        int             *rlen)
{
        char                    *bufhead;       /* ptr to full buffer */
        char                    *aptr;
        struct xattr_hdr        *hptr;          /* ptr to header in bufhead */
        struct xattr_buf        *tptr;          /* ptr to pathing pieces */
        int                     totalen;        /* total buffer length */
        int                     len;            /* length returned to user */
        int                     stringlen;      /* length of filename + attr */
                                                /*
                                                 * length of filename + attr
                                                 * in link section
                                                 */
        int                     linkstringlen = 0;
        int                     complen;        /* length of pathing section */
        int                     linklen;        /* length of link section */
        int                     attrnames_index; /* attrnames starting index */

        /*
         * Release previous buffer if any.
         */

        if (*attrbuf != NULL) {
                free(*attrbuf);
                *attrbuf = NULL;
        }

        /*
         * First add in fixed size stuff
         */
        len = sizeof (struct xattr_hdr) + sizeof (struct xattr_buf);

        /*
         * Add space for two nulls
         */
        stringlen = strlen(attrpath) + strlen(filename) + 2;
        complen = stringlen + sizeof (struct xattr_buf);

        len += stringlen;

        /*
         * Now add on space for link info if any
         */

        if (linkinfo != NULL) {
                /*
                 * Again add space for two nulls
                 */
                linkstringlen = strlen(linkinfo->L_gen.g_attrfnam_p) +
                    strlen(linkinfo->L_gen.g_attrnam_p) + 2;
                linklen = linkstringlen + sizeof (struct xattr_buf);
                len += linklen;
        } else {
                linklen = 0;
        }

        /*
         * Now add padding to end to fill out TBLOCK
         *
         * Function returns size of real data and not size + padding.
         */

        totalen = ROUNDTOTBLOCK(len);
        bufhead = e_zalloc(E_EXIT, totalen);

        /*
         * Now we can fill in the necessary pieces
         */

        /*
         * first fill in the fixed header
         */
        hptr = (struct xattr_hdr *)bufhead;
        (void) strcpy(hptr->h_version, XATTR_ARCH_VERS);
        (void) sprintf(hptr->h_component_len, "%0*d",
            sizeof (hptr->h_component_len) - 1, complen);
        (void) sprintf(hptr->h_link_component_len, "%0*d",
            sizeof (hptr->h_link_component_len) - 1, linklen);
        (void) sprintf(hptr->h_size, "%0*d", sizeof (hptr->h_size) - 1, len);

        /*
         * Now fill in the filename + attrnames section
         * The filename and attrnames section can be composed of two or more
         * path segments separated by a null character.  The first segment
         * is the path to the parent file that roots the entire sequence in
         * the normal name space. The remaining segments describes a path
         * rooted at the hidden extended attribute directory of the leaf file of
         * the previous segment, making it possible to name attributes on
         * attributes.  Thus, if we are just archiving an extended attribute,
         * the second segment will contain the attribute name.  If we are
         * archiving a system attribute of an extended attribute, then the
         * second segment will contain the attribute name, and a third segment
         * will contain the system attribute name.  The attribute pathing
         * information is obtained from 'attrpath'.
         */

        tptr = (struct xattr_buf *)(bufhead + sizeof (struct xattr_hdr));
        (void) sprintf(tptr->h_namesz, "%0*d", sizeof (tptr->h_namesz) - 1,
            stringlen);
        (void) strcpy(tptr->h_names, filename);
        attrnames_index = strlen(filename) + 1;
        (void) strcpy(&tptr->h_names[attrnames_index], attrpath);
        tptr->h_typeflag = typeflag;

        /*
         * Split the attrnames section into two segments if 'attrpath'
         * contains pathing information for a system attribute of an
         * extended attribute.  We split them by replacing the '/' with
         * a '\0'.
         */
        if ((aptr = strpbrk(&tptr->h_names[attrnames_index], "/")) != NULL) {
                *aptr = '\0';
        }

        /*
         * Now fill in the optional link section if we have one
         */

        if (linkinfo != NULL) {
                tptr = (struct xattr_buf *)(bufhead +
                    sizeof (struct xattr_hdr) + complen);

                (void) sprintf(tptr->h_namesz, "%0*d",
                    sizeof (tptr->h_namesz) - 1, linkstringlen);
                (void) strcpy(tptr->h_names, linkinfo->L_gen.g_attrfnam_p);
                (void) strcpy(
                    &tptr->h_names[strlen(linkinfo->L_gen.g_attrfnam_p) + 1],
                    linkinfo->L_gen.g_attrnam_p);
                tptr->h_typeflag = typeflag;
        }
        *attrbuf = (char *)bufhead;
        *rlen = len;
}
#endif  /* O_XATTR */

static char
tartype(int type)
{
        switch (type) {

        case S_IFDIR:
                return (DIRTYPE);

        case S_IFLNK:
                return (SYMTYPE);

        case S_IFIFO:
                return (FIFOTYPE);

        case S_IFCHR:
                return (CHRTYPE);

        case S_IFBLK:
                return (BLKTYPE);

        case S_IFREG:
                return (REGTYPE);

        default:
                return ('\0');
        }
}

#if defined(O_XATTR)
static int
openfile(int omode)
{
        if (G_p->g_attrnam_p != NULL) {
                return (openat(G_p->g_dirfd, G_p->g_attrnam_p, omode));
        } else {
                return (openat(G_p->g_dirfd,
                    get_component(G_p->g_nam_p), omode));
        }
}
#else
static int
openfile(int omode)
{
        return (openat(G_p->g_dirfd, get_component(G_p->g_nam_p), omode));
}
#endif

#if defined(O_XATTR)
static int
read_xattr_hdr()
{
        off_t           bytes;
        int             comp_len, link_len;
        int             namelen;
        int             asz;
        int             cnt;
        char            *tp;
        char            *xattrapath;
        int             pad;
        int             parentfilelen;

        /*
         * Include any padding in the read.  We need to be positioned
         * at beginning of next header.
         */

        bytes = Gen.g_filesz;

        if ((xattrhead = e_zalloc(E_NORMAL, (size_t)bytes)) == NULL) {
                (void) fprintf(stderr, gettext(
                    "Insufficient memory for extended attribute\n"));
                return (1);
        }

        tp = (char *)xattrhead;
        while (bytes > 0) {
                cnt = (int)(bytes > CPIOBSZ) ? CPIOBSZ : bytes;
                FILL(cnt);
                (void) memcpy(tp, Buffr.b_out_p, cnt);
                tp += cnt;
                Buffr.b_out_p += cnt;
                Buffr.b_cnt -= (off_t)cnt;
                bytes -= (off_t)cnt;
        }

        pad = (Pad_val + 1 - (Gen.g_filesz & Pad_val)) &
            Pad_val;
        if (pad != 0) {
                FILL(pad);
                Buffr.b_out_p += pad;
                Buffr.b_cnt -= (off_t)pad;
        }

        /*
         * Validate that we can handle header format
         */

        if (strcmp(xattrhead->h_version, XATTR_ARCH_VERS) != 0) {
                (void) fprintf(stderr,
                    gettext("Unknown extended attribute format encountered\n"));
                (void) fprintf(stderr,
                    gettext("Disabling extended attribute header parsing\n"));
                xattrbadhead = 1;
                return (1);
        }
        (void) sscanf(xattrhead->h_component_len, "%10d", &comp_len);
        (void) sscanf(xattrhead->h_link_component_len, "%10d", &link_len);
        xattrp = (struct xattr_buf *)(((char *)xattrhead) +
            sizeof (struct xattr_hdr));
        (void) sscanf(xattrp->h_namesz, "%7d", &namelen);
        if (link_len > 0) {
                xattr_linkp = (struct xattr_buf *)((intptr_t)xattrp +
                    (int)comp_len);
        } else {
                xattr_linkp = NULL;
        }

        /*
         * Gather the attribute path from the filename and attrnames section.
         * The filename and attrnames section can be composed of two or more
         * path segments separated by a null character.  The first segment
         * is the path to the parent file that roots the entire sequence in
         * the normal name space. The remaining segments describes a path
         * rooted at the hidden extended attribute directory of the leaf file of
         * the previous segment, making it possible to name attributes on
         * attributes.
         */
        parentfilelen = strlen(xattrp->h_names);
        xattrapath = xattrp->h_names + parentfilelen + 1;
        asz = strlen(xattrapath);
        if ((asz + parentfilelen + 2) < namelen) {
                /*
                 * The attrnames section contains a system attribute on an
                 * attribute.  Save the name of the attribute for use later,
                 * and replace the null separating the attribute name from
                 * the system attribute name with a '/' so that xattrapath can
                 * be used to display messages with the full attribute path name
                 * rooted at the hidden attribute directory of the base file
                 * in normal name space.
                 */
                xattrapath[asz] = '/';
        }

        return (0);
}
#endif

static mode_t
attrmode(char type)
{
        mode_t mode;

        switch (type) {
        case '\0':
        case REGTYPE:
        case LNKTYPE:
                mode = S_IFREG;
                break;

        case SYMTYPE:
                mode = S_IFLNK;
                break;

        case CHRTYPE:
                mode = S_IFCHR;
                break;
        case BLKTYPE:
                mode = S_IFBLK;
                break;
        case DIRTYPE:
                mode = S_IFDIR;
                break;
        case FIFOTYPE:
                mode = S_IFIFO;
                break;
        case CONTTYPE:
        default:
                mode = 0;
        }

        return (mode);
}

#if defined(O_XATTR)
static char *
get_component(char *path)
{
        char *ptr;

        ptr = strrchr(path, '/');
        if (ptr == NULL) {
                return (path);
        } else {
                /*
                 * Handle trailing slash
                 */
                if (*(ptr + 1) == '\0')
                        return (ptr);
                else
                        return (ptr + 1);
        }
}
#else
static char *
get_component(char *path)
{
        return (path);
}
#endif

static int
open_dir(char *name)
{
        int fd = -1;
        int cnt = 0;
        char *dir;

        dir = e_zalloc(E_EXIT, strlen(name) + 1);

        /*
         * open directory; creating missing directories along the way.
         */
        get_parent(name, dir);
        do {
                fd = open(dir, O_RDONLY);
                if (fd != -1) {
                        free(dir);
                        return (fd);
                }
                cnt++;
        } while (cnt <= 1 && missdir(name) == 0);

        free(dir);
        return (-1);
}

static int
open_dirfd()
{
#ifdef O_XATTR
        if ((Args & OCt) == 0) {
                close_dirfd();
                if (G_p->g_attrnam_p != NULL) {
                        int     rw_sysattr;

                        /*
                         * Open the file's attribute directory.
                         * Change into the base file's starting directory then
                         * call open_attr_dir() to open the attribute directory
                         * of either the base file (if G_p->g_attrparent_p is
                         * NULL) or the attribute (if G_p->g_attrparent_p is
                         * set) of the base file.
                         */
                        (void) fchdir(G_p->g_baseparent_fd);
                        (void) open_attr_dir(G_p->g_attrnam_p,
                            G_p->g_attrfnam_p, G_p->g_baseparent_fd,
                            (G_p->g_attrparent_p == NULL) ? NULL :
                            G_p->g_attrparent_p, &G_p->g_dirfd, &rw_sysattr);
                        if (Args & OCi) {
                                int     saveerrno = errno;

                                (void) fchdir(G_p->g_baseparent_fd);
                                errno = saveerrno;
                        }
                        if ((G_p->g_dirfd == -1) && (Args & (OCi | OCp))) {
                                msg(ERRN,
                                    "Cannot open attribute directory "
                                    "of %s%s%sfile \"%s\"",
                                    (G_p->g_attrparent_p == NULL) ? "" :
                                    gettext("attribute \""),
                                    (G_p->g_attrparent_p == NULL) ? "" :
                                    G_p->g_attrparent_p,
                                    (G_p->g_attrparent_p == NULL) ? "" :
                                    gettext("\" of "),
                                    G_p->g_attrfnam_p);
                                return (FILE_PASS_ERR);
                        }
                } else {
                        G_p->g_dirfd = open_dir(G_p->g_nam_p);
                        if (G_p->g_dirfd == -1) {
                                msg(ERRN,
                                    "Cannot open/create %s", G_p->g_nam_p);
                                return (1);
                        }
                }
        } else {
                G_p->g_dirfd = -1;
        }
#else
        G_p->g_dirfd = -1;
#endif
        return (0);
}

static void
close_dirfd()
{
        if (G_p->g_dirfd != -1) {
                (void) close(G_p->g_dirfd);
                G_p->g_dirfd = -1;
        }
}

static void
write_xattr_hdr()
{
        char *attrbuf = NULL;
        int  attrlen = 0;
        char *namep;
        struct Lnk *tl_p, *linkinfo;

        /*
         * namep was allocated in xattrs_out.  It is big enough to hold
         * either the name + .hdr on the end or just the attr name
         */

#if defined(O_XATTR)
        namep = Gen.g_nam_p;
        (void) creat_hdr();

        if (Args & OCo) {
                linkinfo = NULL;
                tl_p = Lnk_hd.L_nxt_p;
                while (tl_p != &Lnk_hd) {
                        if (tl_p->L_gen.g_ino == G_p->g_ino &&
                            tl_p->L_gen.g_dev == G_p->g_dev) {
                                        linkinfo = tl_p;
                                        break; /* found */
                        }
                        tl_p = tl_p->L_nxt_p;
                }
                prepare_xattr_hdr(&attrbuf, Gen.g_attrfnam_p,
                    Gen.g_attrpath_p,
                    (linkinfo == NULL) ?
                    tartype(Gen.g_mode & Ftype) : LNKTYPE,
                    linkinfo, &attrlen);
                Gen.g_filesz = attrlen;
                write_hdr(ARCHIVE_XATTR, (off_t)attrlen);
                (void) sprintf(namep, "%s/%s", DEVNULL, Gen.g_attrnam_p);
                write_ancillary(attrbuf, attrlen, B_TRUE);
        }

        (void) creat_hdr();
#endif
}

/*
 * skip over extra slashes in string.
 *
 * For example:
 * /usr/tmp/////
 *
 * would return pointer at
 * /usr/tmp/////
 *         ^
 */
static char *
skipslashes(char *string, char *start)
{
        while ((string > start) && *(string - 1) == '/') {
                string--;
        }

        return (string);
}

static sl_info_t *
sl_info_alloc(void)
{
        static int num_left;
        static sl_info_t *slipool;

        if (num_left > 0) {
                return (&slipool[--num_left]);
        }
        num_left = SL_INFO_ALLOC_CHUNK;
        slipool = e_zalloc(E_EXIT, sizeof (sl_info_t) * num_left);
        return (&slipool[--num_left]);
}

/*
 * If a match for the key values was found in the tree, return a pointer to it.
 * If a match was not found, insert it and return a pointer to it.  This is
 * based on Knuth's Algorithm A in Vol 3, section 6.2.3.
 */

sl_info_t *
sl_insert(dev_t device, ino_t inode, int ftype)
{
        sl_info_t *p;           /* moves down the tree */
        sl_info_t *q = NULL;    /* scratch */
        sl_info_t *r;           /* scratch */
        sl_info_t *s;           /* pt where rebalancing may be needed */
        sl_info_t *t;           /* father of s */
        sl_info_t *head;

        int a;                  /* used to hold balance factors */
        int done;               /* loop control */
        int cmpflg;             /* used to hold the result of a comparison */

        /* initialize */

        head = sl_devhash_lookup(device);

        if (head == NULL) {
                head = sl_info_alloc();
                head->llink = NULL;
                head->bal = 0;

                p = head->rlink = sl_info_alloc();
                p->sl_ino = inode;
                p->sl_ftype = ftype;
                p->sl_count = 0;
                p->bal = 0;
                p->llink = NULL;
                p->rlink = NULL;
                sl_devhash_insert(device, head);
                return (p);
        }

        t = head;
        s = p = head->rlink;

        /* compare */

        for (done = 0; ! done; ) {
                switch (sl_compare(inode, ftype, p->sl_ino, p->sl_ftype)) {
                        case -1:
                                /* move left */

                                q = p->llink;

                                if (q == NULL) {
                                        q = sl_info_alloc();
                                        p->llink = q;
                                        done = 1;
                                        continue;
                                }

                                break;

                        case 0:
                                /* found it */
                                return (p);

                        case 1:
                                /* move right */

                                q = p->rlink;

                                if (q == NULL) {
                                        q = sl_info_alloc();
                                        p->rlink = q;
                                        done = 1;
                                        continue;
                                }

                                break;
                }

                if (q->bal != 0) {
                        t = p;
                        s = q;
                }

                p = q;
        }

        /* insert */

        q->sl_ino = inode;
        q->sl_ftype = ftype;
        q->sl_count = 0;
        q->llink = q->rlink = NULL;
        q->bal = 0;

        /* adjust balance factors */

        if ((cmpflg = sl_compare(inode, ftype, s->sl_ino, s->sl_ftype)) < 0) {
                r = p = s->llink;
        } else {
                r = p = s->rlink;
        }

        while (p != q) {
                switch (sl_compare(inode, ftype, p->sl_ino, p->sl_ftype)) {
                        case -1:
                                p->bal = -1;
                                p = p->llink;
                                break;

                        case 0:
                                break;

                        case 1:
                                p->bal = 1;
                                p = p->rlink;
                                break;
                }
        }

        /* balancing act */

        if (cmpflg < 0) {
                a = -1;
        } else {
                a = 1;
        }

        if (s->bal == 0) {
                s->bal = a;
                head->llink = (sl_info_t *)((intptr_t)head->llink + 1);
                return (q);
        } else if (s->bal == -a) {
                s->bal = 0;
                return (q);
        }

        /*
         * (s->bal == a)
         */

        if (r->bal == a) {
                /* single rotation */

                p = r;

                if (a == -1) {
                        s->llink = r->rlink;
                        r->rlink = s;
                } else if (a == 1) {
                        s->rlink = r->llink;
                        r->llink = s;
                }

                s->bal = r->bal = 0;

        } else if (r->bal == -a) {
                /* double rotation */

                if (a == -1) {
                        p = r->rlink;
                        r->rlink = p->llink;
                        p->llink = r;
                        s->llink = p->rlink;
                        p->rlink = s;
                } else if (a == 1) {
                        p = r->llink;
                        r->llink = p->rlink;
                        p->rlink = r;
                        s->rlink = p->llink;
                        p->llink = s;
                }

                if (p->bal == 0) {
                        s->bal = 0;
                        r->bal = 0;
                } else if (p->bal == -a) {
                        s->bal = 0;
                        r->bal = a;
                } else if (p->bal == a) {
                        s->bal = -a;
                        r->bal = 0;
                }

                p->bal = 0;
        }

        /* finishing touch */

        if (s == t->rlink) {
                t->rlink = p;
        } else {
                t->llink = p;
        }

        return (q);
}

/*
 * sl_numlinks: return the number of links that we saw during our preview.
 */

static uint_t
sl_numlinks(dev_t device, ino_t inode, int ftype)
{
        sl_info_t *p = sl_search(device, inode, ftype);

        if (p) {
                return (p->sl_count);
        } else {
                return (1);
        }
}

/*
 * Preview extended and extended system attributes.
 *
 * Return 0 if successful, otherwise return 1.
 */
#if defined(O_XATTR)
static int
preview_attrs(char *s, char *attrparent)
{
        char            *filename = (attrparent == NULL) ? s : attrparent;
        int             dirfd;
        int             tmpfd;
        int             islnk;
        int             rc = 0;
        int             arc_rwsysattr = 0;
        int             rw_sysattr = 0;
        int             ext_attr = 0;
        DIR             *dirp;
        struct dirent   *dp;
        struct stat     sb;

        /*
         * If the underlying file system supports it, then
         * archive the extended attributes if -@ was specified,
         * and the extended system attributes if -/ was
         * specified.
         */
        if (verify_attr_support(filename, (attrparent == NULL), ARC_CREATE,
            &ext_attr) != ATTR_OK) {
                return (1);
        }

#if defined(_PC_SATTR_ENABLED)
        if (SysAtflag) {
                int             filefd;
                nvlist_t        *slist = NULL;

                /* Determine if there are non-transient system attributes. */
                errno = 0;
                if ((filefd = open(filename, O_RDONLY)) < 0) {
                        return (1);
                }
                if (((slist = sysattr_list(myname, filefd,
                    filename)) != NULL) || (errno != 0)) {
                        arc_rwsysattr = 1;
                }
                if (slist != NULL) {
                        (void) nvlist_free(slist);
                        slist = NULL;
                }
                (void) close(filefd);
        }

        if ((arc_rwsysattr == 0) && ((attrparent != NULL) ||
            (SysAtflag && !ext_attr))) {
                return (1);
        }
#endif  /* _PC_SATTR_ENABLED */
        /*
         * We need to open the attribute directory of the
         * file, and preview all of the file's attributes as
         * attributes of the file can be hard links to other
         * attributes of the file.
         */
        dirfd = attropen(filename, ".", O_RDONLY);
        if (dirfd == -1)
                return (1);

        tmpfd = dup(dirfd);
        if (tmpfd == -1) {
                (void) close(dirfd);
                return (1);
        }
        dirp = fdopendir(tmpfd);
        if (dirp == NULL) {
                (void) close(dirfd);
                (void) close(tmpfd);
                return (1);
        }

        while ((dp = readdir(dirp)) != NULL) {
                if (dp->d_name[0] == '.') {
                        if (dp->d_name[1] == '\0') {
                                Hiddendir = 1;
                        } else if ((dp->d_name[1] == '.') &&
                            (dp->d_name[2] == '\0')) {
                                continue;
                        } else {
                                Hiddendir = 0;
                        }
                } else {
                        Hiddendir = 0;
                }

                if (fstatat(dirfd, dp->d_name, &sb,
                    AT_SYMLINK_NOFOLLOW) < 0) {
                        continue;
                }

                if (verify_attr(dp->d_name, attrparent,
                    arc_rwsysattr, &rw_sysattr) != ATTR_OK) {
                        continue;
                }

                islnk = 0;
                if (S_ISLNK(sb.st_mode)) {
                        islnk = 1;
                        if (Args & OCL) {
                                if (fstatat(dirfd, dp->d_name,
                                    &sb, 0) < 0) {
                                        continue;
                                }
                        }
                }
                sl_remember_tgt(&sb, islnk, rw_sysattr);

                /*
                 * Recursively call preview_attrs() to preview extended
                 * system attributes of attributes.
                 */
                if (SysAtflag && !Hiddendir && !rw_sysattr) {
                        int     my_cwd = save_cwd();

                        (void) fchdir(dirfd);
                        rc = preview_attrs(s, dp->d_name);
                        rest_cwd(my_cwd);
                }
        }
        (void) closedir(dirp);
        (void) close(dirfd);
        return (rc);
}
#endif  /* O_XATTR */

/*
 * sl_preview_synonyms:  Read the file list from the input stream, remembering
 * each reference to each file.
 */

static void
sl_preview_synonyms(void)
{
        char buf [APATH+1];
        char *s;

        char *suffix = "/cpioXXXXXX";
        char *tmpdir = getenv("TMPDIR");
        int    tmpfd, islnk;
        FILE *tmpfile;
        char *tmpfname;

        if (tmpdir == NULL || *tmpdir == '\0' ||
            (strlen(tmpdir) + strlen(suffix)) > APATH) {
                struct statvfs tdsb;

                tmpdir = "/var/tmp";

                /* /var/tmp is read-only in the mini-root environment */

                if (statvfs(tmpdir, &tdsb) == -1 || tdsb.f_flag & ST_RDONLY) {
                        tmpdir = "/tmp";
                }
        }

        tmpfname = e_zalloc(E_EXIT, strlen(tmpdir) + strlen(suffix) + 1);

        (void) strcpy(tmpfname, tmpdir);
        (void) strcat(tmpfname, suffix);

        if ((tmpfd = mkstemp(tmpfname)) == -1) {
                msg(EXTN, "cannot open tmpfile %s%s", tmpdir, suffix);
        }

        if (unlink(tmpfname) == -1) {
                msg(EXTN, "cannot unlink tmpfile %s", tmpfname);
        }

        if ((tmpfile = fdopen(tmpfd, "w+")) == NULL) {
                msg(EXTN, "cannot fdopen tmpfile %s", tmpfname);
        }

        while ((s = fgets(buf, APATH+1, In_p)) != NULL) {
                size_t lastchar;
                struct stat sb;

                if (fputs(buf, tmpfile) == EOF) {
                        msg(EXTN, "problem writing to tmpfile %s", tmpfname);
                }

                /* pre-process the name */

                lastchar = strlen(s) - 1;

                if (s[lastchar] != '\n' && lastchar == APATH - 1) {
                        continue;
                } else {
                        s[lastchar] = '\0';
                }

                while (s[0] == '.' && s[1] == '/') {
                        s += 2;
                        while (s[0] == '/') {
                                s++;
                        }
                }

                if (lstat(s, &sb) < 0) {
                        continue;
                }
                islnk = 0;
                if (S_ISLNK(sb.st_mode)) {
                        islnk = 1;
                        if (Args & OCL) {
                                if (stat(s, &sb) < 0) {
                                        continue;
                                }
                        }
                }
                sl_remember_tgt(&sb, islnk, 0);

#if defined(O_XATTR)
                if (Atflag || SysAtflag) {
                        (void) preview_attrs(s, NULL);
                }
#endif  /* O_XATTR */
        }

        if (ferror(In_p)) {
                msg(EXTN, "error reading stdin");
        }

        if (fseek(tmpfile, 0L, SEEK_SET) == -1) {
                msg(EXTN, "cannot fseek on tmpfile %s", tmpfname);
        }

        In_p = tmpfile;
        free(tmpfname);
}

/*
 * sl_remember_tgt: Add the device/inode for lstat or stat info to the list of
 * those we've seen before.
 *
 * This tree (rooted under head) is keyed by the device/inode of the file
 * being pointed to.  A count is kept of the number of references encountered
 * so far.
 */

static void
sl_remember_tgt(const struct stat *sbp, int isSymlink, int is_sysattr)
{
        sl_info_t *p;
        dev_t device;
        ino_t inode;
        int ftype;

        device = sbp->st_dev;
        inode  = sbp->st_ino;
        ftype  = sbp->st_mode & Ftype;

        /* Determine whether we've seen this one before */

        p = sl_insert(device, inode, ftype);

        if (p->sl_count > 0) {
                /*
                 * It appears as if have seen this file before as we found a
                 * matching device, inode, and file type as a file already
                 * processed.  Since there can possibly be files with the
                 * same device, inode, and file type, but aren't hard links
                 * (e.g., read-write system attribute files will always have
                 * the same inode), we need to only attempt to add one to the
                 * link count if the file we are processing is a hard link
                 * (i.e., st_nlink > 1).
                 *
                 * Note that if we are not chasing symlinks, and this one is a
                 * symlink, it is identically the one we saw before (you cannot
                 * have hard links to symlinks); in this case, we leave the
                 * count alone, so that we don't wind up archiving a symlink to
                 * itself.
                 */

                if (((Args & OCL) || (! isSymlink)) && !is_sysattr) {
                        p->sl_count++;
                }
        } else {
                /* We have not seen this file before */

                p->sl_count = 1;

                if (Use_old_stat) {
                        /* -Hodc: remap inode (-1 on overflow) */

                        sl_remap_t  *q;

                        for (q = sl_remap_head; q && (q->dev != device);
                            q = q->next) {
                                /* do nothing */
                        }

                        if (q == NULL) {
                                q = e_zalloc(E_EXIT, sizeof (sl_remap_t));
                                q->dev = device;
                                p->sl_ino2 = q->inode_count = 1;

                                q->next = (sl_remap_head) ?
                                    sl_remap_head->next : NULL;
                                sl_remap_head = q;
                        } else {
                                if ((size_t)q->inode_count <=
                                    ((1 << (sizeof (o_ino_t) * 8)) - 1)) {
                                        /* fits in o_ino_t */
                                        p->sl_ino2 = ++(q->inode_count);
                                } else {
                                        p->sl_ino2 = (ino_t)-1;
                                }
                        }
                }
        }
}

/*
 * A faster search, which does not insert the key values into the tree.
 * If the a match was found in the tree, return a pointer to it.  If it was not
 * found, return NULL.
 */

sl_info_t *
sl_search(dev_t device, ino_t inode, int ftype)
{
        sl_info_t *p;           /* moves down the tree */
        int c;                  /* comparison value */
        sl_info_t *retval = NULL; /* return value */
        sl_info_t *head;

        head = sl_devhash_lookup(device);
        if (head != NULL) {
                for (p = head->rlink; p; ) {
                        if ((c = sl_compare(inode, ftype, p->sl_ino,
                            p->sl_ftype)) == 0) {
                                retval = p;
                                break;
                        } else if (c < 0) {
                                p = p->llink;
                        } else {
                                p = p->rlink;
                        }
                }
        }

        return (retval);
}

static sl_info_t *
sl_devhash_lookup(dev_t device)
{
        int key;
        sl_info_link_t *lp;
        static sl_info_link_t *devcache;

        if (devcache != NULL && devcache->dev == device) {
                return (devcache->head);
        }

        key = DEV_HASHKEY(device);
        for (lp = sl_devhash[key]; lp; lp = lp->next) {
                if (lp->dev == device) {
                        devcache = lp;
                        return (lp->head);
                }
        }
        return (NULL);
}

static void
sl_devhash_insert(dev_t device, sl_info_t *head)
{
        int key = DEV_HASHKEY(device);
        sl_info_link_t *lp;

        lp = e_zalloc(E_EXIT, sizeof (sl_info_link_t));
        lp->dev = device;
        lp->head = head;
        lp->next = sl_devhash[key];
        sl_devhash[key] = lp;
}

static void
chop_endslashes(char *path)
{
        char *end, *ptr;

        end = &path[strlen(path) -1];
        if (*end == '/' && end != path) {
                ptr = skipslashes(end, path);
                if (ptr != NULL && ptr != path) {
                        *ptr = '\0';
                }
        }
}

#if !defined(O_XATTR)
int
openat64(int fd, char *name, int oflag, mode_t cmode)
{
        return (open64(name, oflag, cmode));
}

int
openat(int fd, char *name, int oflag, mode_t cmode)
{
        return (open(name, oflag, cmode));
}

int
fchownat(int fd, char *name, uid_t owner, gid_t group, int flag)
{
        if (flag == AT_SYMLINK_NOFOLLOW)
                return (lchown(name, owner, group));
        else
                return (chown(name, owner, group));
}

int
renameat(int fromfd, char *old, int tofd, char *new)
{
        return (rename(old, new));
}

int
futimesat(int fd, char *path, struct timeval times[2])
{
        return (utimes(path, times));
}

int
unlinkat(int dirfd, char *path, int flag)
{
        if (flag == AT_REMOVEDIR) {
                return (rmdir(path));
        } else {
                return (unlink(path));
        }
}

int
fstatat(int fd, char *path, struct stat *buf, int flag)
{
        if (flag == AT_SYMLINK_NOFOLLOW)
                return (lstat(path, buf));
        else
                return (stat(path, buf));
}

int
attropen(char *file, char *attr, int omode, mode_t cmode)
{
        errno = ENOTSUP;
        return (-1);
}
#endif