/*
 * cpio - copy file archives in and out
 *
 * Gunnar Ritter, Freiburg i. Br., Germany, April 2003.
 */
/*
 * Copyright (c) 2003 Gunnar Ritter
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute
 * it freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 *
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 *
 * 3. This notice may not be removed or altered from any source distribution.
 */

/*
 * Sccsid @(#)cpio.c	1.304 (gritter) 2/14/09
 */

#include <sys/types.h>
#include <sys/stat.h>
#ifdef	__linux__
#if !defined (__UCLIBC__) && !defined (__dietlibc__)
#include <linux/fs.h>
#endif	/* !__UCLIBC__, !__dietlibc__ */
#include <linux/fd.h>
#undef	WNOHANG
#undef	WUNTRACED
#undef	P_ALL
#undef	P_PID
#undef	P_PGID
#ifdef	__dietlibc__
#undef	NR_OPEN
#undef	PATH_MAX
#endif	/* __dietlibc__ */
#endif	/* __linux__ */
#include <sys/wait.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include "sigset.h"
#include <time.h>
#include <utime.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#include <stdio.h>
#include <libgen.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <locale.h>
#include <ctype.h>
#include "memalign.h"

int	sysv3;

#if USE_ZLIB
#include <zlib.h>
#endif	/* USE_ZLIB */

#if USE_BZLIB
#include <bzlib.h>
#endif	/* USE_BZLIB */

#include <sys/ioctl.h>

#if defined (__linux__) || defined (__sun) || defined (__FreeBSD__) || \
	defined (__hpux) || defined (_AIX) || defined (__NetBSD__) || \
	defined (__OpenBSD__) || defined (__DragonFly__) || \
	defined (__CYGWIN__)
#include <sys/mtio.h>
#endif

#include <iblok.h>
#include <sfile.h>
#include <atoll.h>

#ifdef	_AIX
#include <sys/sysmacros.h>
#endif	/* _AIX */

#ifndef	major
#include <sys/mkdev.h>
#endif	/* !major */

#include "cpio.h"
#include "blast.h"

#ifdef	__GLIBC__
#ifdef	_IO_putc_unlocked
#undef	putc
#define	putc(c, f)	_IO_putc_unlocked(c, f)
#undef	putchar
#define	putchar(c)	_IO_putc_unlocked(c, stdout)
#endif	/* _IO_putc_unlocked */
#endif	/* __GLIBC__ */

/*
 * The cpio code assumes that all following S_IFMT bits are the same as
 * those of the mode fields in cpio headers. The only real Unix system
 * known to deviate from this de facto standard is UNICOS which uses
 * 0130000 for S_IFLNK. But this software does not run on UNICOS for
 * a variety of other reasons anyway, so this should not be of much
 * concern.
 */
#if	S_IFIFO	!= 0010000 || \
	S_IFCHR	!= 0020000 || \
	S_IFDIR	!= 0040000 || \
	S_IFBLK	!= 0060000 || \
	S_IFREG	!= 0100000 || \
	S_IFLNK	!= 0120000 || \
	S_IFSOCK!= 0140000 || \
	S_IFMT	!= 0170000
#error non-standard S_IFMT bits
#endif

/*
 * File types that are not present on all systems but that we want to
 * recognize nevertheless.
 */
#ifndef	S_IFDOOR
#define	S_IFDOOR	0150000		/* Solaris door */
#endif
#ifndef	S_IFNAM
#define	S_IFNAM		0050000		/* XENIX special named file */
#endif
#ifndef	S_INSEM
#define	S_INSEM		0x1		/* XENIX semaphore subtype of IFNAM */
#endif
#ifndef	S_INSHD
#define	S_INSHD		0x2		/* XENIX shared data subtype of IFNAM */
#endif
#ifndef	S_IFNWK
#define	S_IFNWK		0110000		/* HP-UX network special file */
#endif

#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) || \
	defined (__DragonFly__) || defined (__APPLE__)
/*
 * For whatever reason, FreeBSD casts the return values of major() and
 * minor() to signed values so that normal limit comparisons will fail.
 */
static unsigned long
mymajor(long dev)
{
	return major(dev) & 0xFFFFFFFFUL;
}
#undef	major
#define	major(a)	mymajor(a)
static unsigned long
myminor(long dev)
{
	return minor(dev) & 0xFFFFFFFFUL;
}
#undef	minor
#define	minor(a)	myminor(a)
#endif	/* __FreeBSD__, __NetBSD__, __OpenBSD__, __DragonFly__, __APPLE__ */

/*
 * Device and inode counts in cpio formats are too small to store the
 * information used to detect hard links on today's systems. Keep track
 * of all devices and inodes and store fake counts in the archive.
 */
struct ilink {
	struct ilink	*l_nxt;	/* next link to same i-node */
	char		*l_nam;	/* link name */
	size_t		l_siz;	/* link name size */
};

struct islot {
	struct islot	*i_lln;	/* left link */
	struct islot 	*i_rln;	/* right link */
	struct ilink	*i_lnk;	/* links list */
	struct stat	*i_st;	/* stat information */
	char		*i_name;/* name of first link encountered */
	ino_t		i_ino;	/* real inode number */
	uint32_t	i_fino;	/* fake inode number */
	nlink_t		i_nlk;	/* number of remaining links */
};

struct dslot {
	struct dslot	*d_nxt;	/* next device */
	struct islot	*d_isl;	/* inode slots */
	uint32_t	d_cnt;	/* used inode number count */
	uint32_t	d_fake;	/* faked device id */
	dev_t		d_dev;	/* real device id */
};

union types2 {
	uint8_t		byte[2];
	uint16_t	sword;
};

union types4 {
	uint8_t		byte[4];
	uint16_t	sword[2];
	uint32_t	lword;
};

/*
 * Store and retrieve integers in a defined endian order.
 */
static uint16_t
ple16(const char *cp)
{
	return (uint16_t)(cp[0]&0377) +
		((uint16_t)(cp[1]&0377) << 8);
}

static uint16_t
pbe16(const char *cp)
{
	return (uint16_t)(cp[1]&0377) +
		((uint16_t)(cp[0]&0377) << 8);
}

static uint32_t
ple32(const char *cp)
{
	return (uint32_t)(cp[0]&0377) +
		((uint32_t)(cp[1]&0377) << 8) +
		((uint32_t)(cp[2]&0377) << 16) +
		((uint32_t)(cp[3]&0377) << 24);
}

static uint32_t
pbe32(const char *cp)
{
	return (uint32_t)(cp[3]&0377) +
		((uint32_t)(cp[2]&0377) << 8) +
		((uint32_t)(cp[1]&0377) << 16) +
		((uint32_t)(cp[0]&0377) << 24);
}

static uint32_t
pme32(const char *cp)
{
	return (uint32_t)(cp[2]&0377) +
		((uint32_t)(cp[3]&0377) << 8) +
		((uint32_t)(cp[0]&0377) << 16) +
		((uint32_t)(cp[1]&0377) << 24);
}

static uint64_t
ple64(const char *cp)
{
	return (uint64_t)(cp[0]&0377) +
		((uint64_t)(cp[1]&0377) << 8) +
		((uint64_t)(cp[2]&0377) << 16) +
		((uint64_t)(cp[3]&0377) << 24) +
		((uint64_t)(cp[4]&0377) << 32) +
		((uint64_t)(cp[5]&0377) << 40) +
		((uint64_t)(cp[6]&0377) << 48) +
		((uint64_t)(cp[7]&0377) << 56);
}

static uint64_t
pbe64(const char *cp)
{
	return (uint64_t)(cp[7]&0377) +
		((uint64_t)(cp[6]&0377) << 8) +
		((uint64_t)(cp[5]&0377) << 16) +
		((uint64_t)(cp[4]&0377) << 24) +
		((uint64_t)(cp[3]&0377) << 32) +
		((uint64_t)(cp[2]&0377) << 40) +
		((uint64_t)(cp[1]&0377) << 48) +
		((uint64_t)(cp[0]&0377) << 56);
}

static void
le16p(uint16_t n, char *cp)
{
	cp[0] = (n&0x00ff);
	cp[1] = (n&0xff00) >> 8;
}

static void
be16p(uint16_t n, char *cp)
{
	cp[1] = (n&0x00ff);
	cp[0] = (n&0xff00) >> 8;
}

static void
le32p(uint32_t n, char *cp)
{
	cp[0] = (n&0x000000ff);
	cp[1] = (n&0x0000ff00) >> 8;
	cp[2] = (n&0x00ff0000) >> 16;
	cp[3] = (n&0xff000000) >> 24;
}

static void
be32p(uint32_t n, char *cp)
{
	cp[3] = (n&0x000000ff);
	cp[2] = (n&0x0000ff00) >> 8;
	cp[1] = (n&0x00ff0000) >> 16;
	cp[0] = (n&0xff000000) >> 24;
}

static void
me32p(uint32_t n, char *cp)
{
	cp[2] = (n&0x000000ff);
	cp[3] = (n&0x0000ff00) >> 8;
	cp[0] = (n&0x00ff0000) >> 16;
	cp[1] = (n&0xff000000) >> 24;
}

static void
le64p(uint64_t n, char *cp)
{
	cp[0] = (n&0x00000000000000ffLL);
	cp[1] = (n&0x000000000000ff00LL) >> 8;
	cp[2] = (n&0x0000000000ff0000LL) >> 16;
	cp[3] = (n&0x00000000ff000000LL) >> 24;
	cp[4] = (n&0x000000ff00000000LL) >> 32;
	cp[5] = (n&0x0000ff0000000000LL) >> 40;
	cp[6] = (n&0x00ff000000000000LL) >> 48;
	cp[7] = (n&0xff00000000000000LL) >> 56;
}

static void
be64p(uint64_t n, char *cp)
{
	cp[7] = (n&0x00000000000000ffLL);
	cp[6] = (n&0x000000000000ff00LL) >> 8;
	cp[5] = (n&0x0000000000ff0000LL) >> 16;
	cp[4] = (n&0x00000000ff000000LL) >> 24;
	cp[3] = (n&0x000000ff00000000LL) >> 32;
	cp[2] = (n&0x0000ff0000000000LL) >> 40;
	cp[1] = (n&0x00ff000000000000LL) >> 48;
	cp[0] = (n&0xff00000000000000LL) >> 56;
}

#define	TNAMSIZ	100
#define	TPFXSIZ	155
#define	TMAGSIZ	6
#define	TTIMSIZ	12

/*
 * Structure of an archive header.
 */
union bincpio {
	char		data[4096];
#define	SIZEOF_hdr_cpio	26
	struct hdr_cpio {
		char		c_magic[2];
		char		c_dev[2];
		char		c_ino[2];
		char		c_mode[2];
		char		c_uid[2];
		char		c_gid[2];
		char		c_nlink[2];
		char		c_rdev[2];
		char		c_mtime[4];
		char		c_namesize[2];
		char		c_filesize[4];
	} Hdr;
#define	SIZEOF_cray_hdr	152
	struct cray_hdr {	/* with thanks to Cray-Cyber.org */
		char		C_magic[8];
		char		C_dev[8];
		char		C_ino[8];
		char		C_mode[8];
		char		C_uid[8];
		char		C_gid[8];
		char		C_nlink[8];
		char		C_rdev[8];
				/*
				 * The C_param field was introduced with
				 * UNICOS 6 and is simply not present in
				 * the earlier format. Following fields
				 * are the same for both revisions again.
				 */
#define	CRAY_PARAMSZ	(8*8)
		char		C_param[CRAY_PARAMSZ];
		char		C_mtime[8];
		char		C_namesize[8];
		char		C_filesize[8];
	} Crayhdr;
#define	SIZEOF_c_hdr	76
	struct c_hdr {
		char		c_magic[6];
		char		c_dev[6];
		char		c_ino[6];
		char		c_mode[6];
		char		c_uid[6];
		char		c_gid[6];
		char		c_nlink[6];
		char		c_rdev[6];
		char		c_mtime[11];
		char		c_namesz[6];
		char		c_filesz[11];
	} Cdr;
#define	SIZEOF_d_hdr	86
	struct d_hdr {
		char		d_magic[6];
		char		d_dev[6];
		char		d_ino[6];
		char		d_mode[6];
		char		d_uid[6];
		char		d_gid[6];
		char		d_nlink[6];
		char		d_rmaj[8];
		char		d_rmin[8];
		char		d_mtime[11];
		char		d_namesz[6];
		char		d_filesz[11];
	} Ddr;
#define	SIZEOF_Exp_cpio_hdr	110
	struct Exp_cpio_hdr {
		char		E_magic[6];
		char		E_ino[8];
		char		E_mode[8];
		char		E_uid[8];
		char		E_gid[8];
		char		E_nlink[8];
		char		E_mtime[8];
		char		E_filesize[8];
		char		E_maj[8];
		char		E_min[8];
		char		E_rmaj[8];
		char		E_rmin[8];
		char		E_namesize[8];
		char		E_chksum[8];
	} Edr;
	struct tar_header {
		char		t_name[TNAMSIZ];
		char		t_mode[8];
		char		t_uid[8];
		char		t_gid[8];
		char		t_size[12];
		char		t_mtime[TTIMSIZ];
		char		t_chksum[8];
		char		t_linkflag;
		char		t_linkname[TNAMSIZ];
		char		t_magic[TMAGSIZ];
		char		t_version[2];
		char		t_uname[32];
		char		t_gname[32];
		char		t_devmajor[8];
		char		t_devminor[8];
		char		t_prefix[TPFXSIZ];
	} Tdr;
#define	SIZEOF_bar_header	84
	struct bar_header {
		char		b_mode[8];
		char		b_uid[8];
		char		b_gid[8];
		char		b_size[12];
		char		b_mtime[12];
		char		b_chksum[8];
		char		b_rdev[8];
		char		b_linkflag;
		char		b_bar_magic[2];
		char		b_volume_num[4];
		char		b_compressed;
		char		b_date[12];
	} Bdr;
#define	SIZEOF_zip_header	30
	struct zip_header {
		char		z_signature[4];
		char		z_version[2];
		char		z_gflag[2];
		char		z_cmethod[2];
		char		z_modtime[2];
		char		z_moddate[2];
		char		z_crc32[4];
		char		z_csize[4];
		char		z_nsize[4];
		char		z_namelen[2];
		char		z_extralen[2];
	} Zdr;
};

#define	BCUT	0177777
#define	OCUT	0777777
#define	ECUT	0xFFFFFFFFUL

static const char	trailer[] = "TRAILER!!!";

/*
 * Structure of per-file extra data for zip format.
 */
union zextra {
	char	data[1];
#define	SIZEOF_zextra_gn	4
	struct	zextra_gn {
		char	ze_gn_tag[2];
		char	ze_gn_tsize[2];
	} Ze_gn;
#define	SIZEOF_zextra_64	32		/* regular size */
#define	SIZEOF_zextra_64_a	28		/* size without startn field */
#define	SIZEOF_zextra_64_b	20		/* size without reloff field */
	struct	zextra_64 {
		char	ze_64_tag[2];
		char	ze_64_tsize[2];
		char	ze_64_nsize[8];
		char	ze_64_csize[8];
		char	ze_64_reloff[8];
		char	ze_64_startn[4];
	} Ze_64;
#define	SIZEOF_zextra_pk	16
	struct	zextra_pk {
		char	ze_pk_tag[2];
		char	ze_pk_tsize[2];
		char	ze_pk_atime[4];
		char	ze_pk_mtime[4];
		char	ze_pk_uid[2];
		char	ze_pk_gid[2];
	} Ze_pk;
#define	SIZEOF_zextra_ek	17
	struct	zextra_et {
		char	ze_et_tag[2];
		char	ze_et_tsize[2];
		char	ze_et_flags[1];
		char	ze_et_mtime[4];
		char	ze_et_atime[4];
		char	ze_et_ctime[4];
	} Ze_et;
#define	SIZEOF_zextra_i1	16
	struct	zextra_i1 {
	char	ze_i1_tag[2];
		char	ze_i1_tsize[2];
		char	ze_i1_atime[4];
		char	ze_i1_mtime[4];
		char	ze_i1_uid[2];
		char	ze_i1_gid[2];
	} Ze_i1;
#define	SIZEOF_zextra_i2	8
	struct	zextra_i2 {
		char	ze_i2_tag[2];
		char	ze_i2_tsize[2];
		char	ze_i2_uid[2];
		char	ze_i2_gid[2];
	} Ze_i2;
#define	SIZEOF_zextra_as	16
	struct	zextra_as {
		char	ze_as_tag[2];
		char	ze_as_tsize[2];
		char	ze_as_crc[4];
		char	ze_as_mode[2];
		char	ze_as_sizdev[4];
		char	ze_as_uid[2];
		char	ze_as_gid[2];
	} Ze_as;
#define	SIZEOF_zextra_cp	40
	struct	zextra_cp {
		char	ze_cp_tag[2];
		char	ze_cp_tsize[2];
		char	ze_cp_dev[4];
		char	ze_cp_ino[4];
		char	ze_cp_mode[4];
		char	ze_cp_uid[4];
		char	ze_cp_gid[4];
		char	ze_cp_nlink[4];
		char	ze_cp_rdev[4];
		char	ze_cp_mtime[4];
		char	ze_cp_atime[4];
	} Ze_cp;
};

static struct	zipstuff {		/* stuff for central directory at EOF */
	struct zipstuff	*zs_next;
	char	*zs_name;		/* file name */
	long long	zs_size;	/* file size */
	long long	zs_relative;	/* offset of local header */
	long long	zs_csize;	/* compressed size */
	uint32_t	zs_crc32;	/* CRC */
	time_t		zs_mtime;	/* modification time */
	enum cmethod	zs_cmethod;	/* compression method */
	int		zs_gflag;	/* general flag */
	mode_t		zs_mode;	/* file mode */
} *zipbulk;

/*
 * Structure of the central zip directory at the end of the file. This
 * (obligatory) part of a zip file is written by this implementation,
 * but is completely ignored on copy-in. This means that we miss the
 * mode_t stored in zc_extralen if zc_versionmade[1] is 3 (Unix). We
 * have to do this since it contains the S_IFMT bits, thus telling us
 * whether something is a symbolic link and resulting in different
 * behavior - but as the input had to be seekable in order to do this,
 * we had to store entire archives in temporary files if input came
 * from a pipe to be consistent.
 */
#define	SIZEOF_zipcentral	46
struct	zipcentral {
	char	zc_signature[4];
	char	zc_versionmade[2];
	char	zc_versionextr[2];
	char	zc_gflag[2];
	char	zc_cmethod[2];
	char	zc_modtime[2];
	char	zc_moddate[2];
	char	zc_crc32[4];
	char	zc_csize[4];
	char	zc_nsize[4];
	char	zc_namelen[2];
	char	zc_extralen[2];
	char	zc_commentlen[2];
	char	zc_disknstart[2];
	char	zc_internal[2];
	char	zc_external[4];
	char	zc_relative[4];
};

#define	SIZEOF_zip64end		56
struct	zip64end {
	char	z6_signature[4];
	char	z6_recsize[8];
	char	z6_versionmade[2];
	char	z6_versionextr[2];
	char	z6_thisdiskn[4];
	char	z6_alldiskn[4];
	char	z6_thisentries[8];
	char	z6_allentries[8];
	char	z6_dirsize[8];
	char	z6_startsize[8];
};

#define	SIZEOF_zip64loc		20
struct	zip64loc {
	char	z4_signature[4];
	char	z4_startno[4];
	char	z4_reloff[8];
	char	z4_alldiskn[4];
};

#define	SIZEOF_zipend		22
struct	zipend {
	char	ze_signature[4];
	char	ze_thisdiskn[2];
	char	ze_alldiskn[2];
	char	ze_thisentries[2];
	char	ze_allentries[2];
	char	ze_dirsize[4];
	char	ze_startsize[4];
	char	ze_commentlen[2];
};

#define	SIZEOF_zipddesc		16
struct	zipddesc {
	char	zd_signature[4];
	char	zd_crc32[4];
	char	zd_csize[4];
	char	zd_nsize[4];
};

#define	SIZEOF_zipddesc64	24
struct	zipddesc64 {
	char	zd_signature[4];
	char	zd_crc32[4];
	char	zd_csize[8];
	char	zd_nsize[8];
};

/*
 * Magic numbers and strings.
 */
static const uint16_t	mag_bin = 070707;
/*static const uint16_t	mag_bbs = 0143561;*/
static const char	mag_asc[6] = "070701";
static const char	mag_crc[6] = "070702";
static const char	mag_odc[6] = "070707";
static const long	mag_sco = 0x7ffffe00;

static const char	mag_ustar[6] = "ustar\0";
static const char	mag_gnutar[8] = "ustar  \0";
static const char	mag_bar[2] = "V\0";

static const char	mag_zipctr[4] = "PK\1\2";
static const char	mag_zipsig[4] = "PK\3\4";
static const char	mag_zipend[4] = "PK\5\6";
static const char	mag_zip64e[4] = "PK\6\6";
static const char	mag_zip64l[4] = "PK\6\7";
static const char	mag_zipdds[4] = "PK\7\10";
#define			mag_zip64f	0x0001
#define			mag_zipcpio	0x0707

/*
 * Fields for the extended pax header.
 */
static enum paxrec {
	PR_NONE		= 0000,
	PR_ATIME	= 0001,
	PR_GID		= 0002,
	PR_LINKPATH	= 0004,
	PR_MTIME	= 0010,
	PR_PATH		= 0020,
	PR_SIZE		= 0040,
	PR_UID		= 0100,
	PR_SUN_DEVMAJOR	= 0200,
	PR_SUN_DEVMINOR	= 0400
} paxrec, globrec;

/*
 * Prototype structure, collecting user-defined information
 * about a file.
 */
struct prototype {
	mode_t	pt_mode;	/* type and permission bits */
	uid_t	pt_uid;		/* owner */
	gid_t	pt_gid;		/* group owner */
	time_t	pt_atime;	/* time of last access */
	time_t	pt_mtime;	/* time of last modification */
	dev_t	pt_rdev;	/* device major/minor */
	enum {
		PT_NONE	 = 0000,
		PT_TYPE	 = 0001,
		PT_OWNER = 0002,
		PT_GROUP = 0004,
		PT_MODE  = 0010,
		PT_ATIME = 0020,
		PT_MTIME = 0040,
		PT_RDEV  = 0100
	}	pt_spec;	/* specified information */
};

static struct stat	globst;

/*
 * This sets a sanity check limit on path names. If a longer path name
 * occurs in an archive, it is treated as corrupt. This is because no
 * known Unix system can handle path names of arbitrary length; limits
 * are typically between 1024 and 4096. Trying to extract longer path
 * names would fail anyway and will cpio eventually fail to allocate
 * memory.
 */
#define			SANELIMIT	0177777

char			*progname;	/* argv[0] to main() */
static struct dslot	*devices;	/* devices table */
static struct dslot	*markeddevs;	/* unusable device numbers */
static char		*blkbuf;	/* block buffer */
int			blksiz;		/* block buffer size */
static int		blktop;		/* top of filled part of buffer */
static int		curpos;		/* position in blkbuf */
static uint32_t		fakedev;	/* fake device for single link inodes */
static uint32_t		fakeino;	/* fake inode for single link inodes */
static uint32_t		harddev;	/* fake device used for hard links */
static unsigned long long	maxsize;/* maximum size for format */
static unsigned long long	maxrdev;/* maximum st_rdev for format */
static unsigned long long	maxmajor;/* maximum major(st_rdev) for format */
static unsigned long long	maxminor;/* maximum minor(st_rdev) for format */
static unsigned long long	maxuid;	/* maximum user id for format */
static unsigned long long	maxgid;	/* maximum group id for format */
static unsigned long long	maxnlink;/* maximum link count for format */
static int		mt;		/* magtape file descriptor */
static int		mfl;		/* magtape flags */
static struct stat	mtst;		/* fstat() on mt */
int			aflag;		/* reset access times */
int			Aflag;		/* append to archive */
int			bflag;		/* swap bytes */
int			Bflag;		/* 5120 blocking */
int			cflag;		/* ascii format */
int			Cflag;		/* user-defined blocking */
int			dflag;		/* create directories */
int			Dflag;		/* do not ask for next device */
int			eflag;		/* DEC format */
int			cray_eflag;	/* do not archive if values too large */
const char		*Eflag;		/* filename for files to be extracted */
int			fflag;		/* pattern excludes */
int			Hflag;		/* header format */
const char		*Iflag;		/* input archive name */
int			kflag;		/* skipt corrupted parts */
int			Kflag;		/* IRIX-style large file support */
int			lflag;		/* link of possible */
int			Lflag;		/* follow symbolic links */
int			mflag;		/* retain modification times */
const char		*Mflag;		/* message when switching media */
const char		*Oflag;		/* output archive name */
int			Pflag;		/* prototype file list */
int			rflag;		/* rename files */
const char		*Rflag;		/* reassign ownerships */
static uid_t		Ruid;		/* uid to assign */
static gid_t		Rgid;		/* gid to assign */
int			sflag;		/* swap half word bytes */
int			Sflag;		/* swap word bytes */
int			tflag;		/* print toc */
int			uflag;		/* overwrite files unconditionally */
int			hp_Uflag;	/* use umask when creating files */
int			vflag;		/* verbose */
int			Vflag;		/* special verbose */
int			sixflag;	/* 6th Edition archives */
int			action;		/* -i -o -p */
long long		errcnt;		/* error status */
static unsigned long long	maxpath;/* maximum path length with -i */
static uint32_t		maxino;		/* maximum inode number with -i */
static uid_t		myuid;		/* user id of caller */
static gid_t		mygid;		/* group id of caller */
static long long	blocks;		/* copying statistics: full blocks */
static long long	bytes;		/* copying statistics: partial blocks */
static long long	nwritten;	/* bytes written to archive */
static off_t		aoffs;		/* offset in archive */
static off_t		poffs;		/* physical offset in archive */
static int		tapeblock = -1;	/* physical tape block size */
struct glist		*patterns;	/* patterns for -i */
static int		tty;		/* terminal file descriptor */
static const char	*cur_ofile;	/* current original file */
static const char	*cur_tfile;	/* current temporary file */
static mode_t		umsk;		/* user's umask */
static int		zipclevel;	/* zip compression level */
static struct islot	*inull;		/* splay tree null element */
int			printsev;	/* print message severity strings */
static int		compressed_bar;	/* this is a compressed bar archive */
static int		formatforced;	/* -k -i -Hfmt forces a format */
static long long	lineno;		/* input line number */

int			pax_dflag;	/* directory matches only itself */
int			pax_kflag;	/* do not overwrite files */
int			pax_nflag;	/* select first archive member only */
int			pax_sflag;	/* substitute file names */
int			pax_uflag;	/* add only recent files to archive */
int			pax_Xflag;	/* do not cross device boundaries */
static enum {
	PO_NONE		= 0,
	PO_LINKDATA	= 01,		/* include link data in type 2 */
	PO_TIMES	= 02,		/* create atime and mtime fields */
} pax_oflag;				/* recognized -o options */

static void	copyout(int (*)(const char *, struct stat *));
static size_t	ofiles_cpio(char **, size_t *);
static void	dooutp(void);
static int	outfile(const char *, struct stat *);
static int	addfile(const char *, struct stat *, uint32_t, uint32_t, int,
			const char *);
static void	iflush(struct islot *, uint32_t);
static void	lflush(void);
static int	bigendian(void);
static void	getbuf(char **, size_t *, size_t);
static void	prdot(int);
static void	newmedia(int);
static void	mclose(void);
static ssize_t	mwrite(int);
static void	bwrite(const char *, size_t);
static void	bflush(void);
static int	sum(int, const char *, struct stat *, char *);
static int	rstime(const char *, struct stat *, const char *);
static struct islot	*isplay(ino_t, struct islot *);
static struct islot	*ifind(ino_t, struct islot **);
static void	iput(struct islot *, struct islot **);
static struct dslot	*dfind(struct dslot **, dev_t);
static void	done(int);
static void	dopass(const char *);
static int	passdata(struct file *, const char *, int);
static int	passfile(const char *, struct stat *);
static int	filein(struct file *, int (*)(struct file *, const char *, int),
			char *);
static int	linkunlink(const char *, const char *);
static void	tunlink(char **);
static int	filet(struct file *, int (*)(struct file *, const char *, int));
static void	filev(struct file *);
static int	typec(struct stat *);
static void	permbits(mode_t);
static void	prtime_cpio(time_t);
static void	getpath(const char *, char **, char **, size_t *, size_t *);
static void	setpath(const char *, char **, char **,
			size_t, size_t *, size_t *);
static int	imdir(char *);
static int	setattr(const char *, struct stat *);
static int	setowner(const char *, struct stat *);
static int	canlink(const char *, struct stat *, int);
static void	doinp(void);
static void	storelink(struct file *);
static void	flushlinks(struct file *);
static void	flushnode(struct islot *, struct file *);
static void	flushrest(int);
static void	flushtree(struct islot *, int);
static int	inpone(struct file *, int);
static int	readhdr(struct file *, union bincpio *);
static void	whathdr(void);
static int	infile(struct file *);
static int	skipfile(struct file *);
static int	skipdata(struct file *f,
			int (*)(struct file *, const char *, int));
static int	indata(struct file *, const char *, int);
static int	totrailer(void);
static long long	rdoct(const char *, int);
static long long	rdhex(const char *, int);
static ssize_t	mread(void);
static void	mstat(void);
static int	skippad(unsigned long long, int);
static int	allzero(const char *, int);
static const char	*getuser(uid_t);
static const char	*getgroup(gid_t);
static struct glist	*want(struct file *, struct glist **);
static void	patfile(void);
static int	ckodd(long long, int, const char *, const char *);
static int	rname(char **, size_t *);
static int	redirect(const char *, const char *);
static char	*tnameof(struct tar_header *, char *);
static int	tmkname(struct tar_header *, const char *);
static void	tlinkof(struct tar_header *, struct file *);
static int	tmklink(struct tar_header *, const char *);
static int	tlflag(struct stat *);
static void	tchksum(union bincpio *);
static int	tcssum(union bincpio *, int);
static int	trdsum(union bincpio *);
static mode_t	tifmt(int);
static void	bchksum(union bincpio *);
static int	bcssum(union bincpio *);
static void	blinkof(const char *, struct file *, int);
static void	dump_barhdr(void);
static int	zcreat(const char *, mode_t);
static int	zclose(int);
static void	markdev(dev_t);
static int	marked(dev_t);
static void	cantsup(int, const char *);
static void	onint(int);
static int	zipread(struct file *, const char *, int, int);
static void	zipreaddesc(struct file *);
static int	cantunzip(struct file *, const char *);
static time_t	gdostime(const char *, const char *);
static void	mkdostime(time_t, char *, char *);
static ssize_t	ziprxtra(struct file *, struct zip_header *);
static void	ziptrailer(void);
static void	zipdefer(const char *, struct stat *, long long,
			uint32_t, long long, const struct zip_header *);
static int	zipwrite(int, const char *, struct stat *,
			union bincpio *, size_t, uint32_t, uint32_t,
			uint32_t *, long long *);
static int	zipwtemp(int, const char *, struct stat *,
			union bincpio *, size_t, uint32_t, uint32_t,
			uint32_t *, long long *);
#if USE_ZLIB
static int	zipwdesc(int, const char *, struct stat *,
			union bincpio *, size_t, uint32_t, uint32_t,
			uint32_t *, long long *);
#endif	/* USE_ZLIB */
static int	zipwxtra(const char *, struct stat *, uint32_t, uint32_t);
static void	zipinfo(struct file *);
static void	readK2hdr(struct file *);
static int	readgnuname(char **, size_t *, long);
static void	writegnuname(const char *, long, int);
static void	tgetpax(struct tar_header *, struct file *);
static enum paxrec	tgetrec(char **, char **, char **);
static void	wrpax(const char *, const char *, struct stat *);
static void	addrec(char **, long *, long *,
			const char *, const char *, long long);
static void	paxnam(struct tar_header *, const char *);
static char	*sequence(void);
static char	*joinpath(const char *, char *);
static int	utf8(const char *);
static char	*getproto(char *, struct prototype *);

size_t		(*ofiles)(char **, size_t *) = ofiles_cpio;
void		(*prtime)(time_t) = prtime_cpio;

int
main(int argc, char **argv)
{
	myuid = getuid();
	mygid = getgid();
	umask(umsk = umask(0));
	progname = basename(argv[0]);
	setlocale(LC_CTYPE, "");
	setlocale(LC_TIME, "");
	inull = scalloc(1, sizeof *inull);
	inull->i_lln = inull->i_rln = inull;
	flags(argc, argv);
	switch (action) {
	case 'i':
		if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
			sigset(SIGINT, onint);
		doinp();
		break;
	case 'o':
		dooutp();
		break;
	case 'p':
		if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
			sigset(SIGINT, onint);
		dopass(argv[optind]);
		break;
	}
	if (tflag)
		fflush(stdout);
	else if (Vflag)
		prdot(1);
	if (pax != PAX_TYPE_CPIO)
		pax_onexit();
	//fprintf(stderr, "%llu blocks\n", blocks + ((bytes + 0777) >> 9));
	mclose();
	if (errcnt && sysv3 == 0)
		fprintf(stderr, "%llu error(s)\n", errcnt);
	return errcnt ? sysv3 ? 1 : 2 : 0;
}

static size_t
ofiles_cpio(char **name, size_t *namsiz)
{
	static struct iblok	*ip;

	if (ip == NULL)
		ip = ib_alloc(0, 0);
	return ib_getlin(ip, name, namsiz, srealloc);
}

/*
 * Read the file name list for -o and -p and do initial processing
 * for each name.
 */
static void
copyout(int (*copyfn)(const char *, struct stat *))
{
	char	*name = NULL, *np;
	size_t	namsiz = 0, namlen;
	struct stat	st;
	struct prototype	pt;

	while ((namlen = ofiles(&name, &namsiz)) != 0) {
		lineno++;
		if (name[namlen-1] == '\n')
			name[--namlen] = '\0';
		if (Pflag)
			np = getproto(name, &pt);
		else
			np = name;
		while (np[0] == '.' && np[1] == '/') {
			np += 2;
			while (*np == '/')
				np++;
			if (*np == '\0') {
				np = name;
				break;
			}
		}
		if (lstat(np, &st) < 0) {
			if (Pflag && *np && ((pt.pt_spec &
			    (PT_TYPE|PT_OWNER|PT_GROUP|PT_MODE|PT_RDEV) &&
					((pt.pt_mode&S_IFMT) == S_IFBLK ||
					 (pt.pt_mode&S_IFMT) == S_IFCHR)) ||
				      (pt.pt_spec &
		            (PT_TYPE|PT_OWNER|PT_GROUP|PT_MODE) &&
			    		((pt.pt_mode&S_IFMT) == S_IFDIR ||
					 (pt.pt_mode&S_IFMT) == S_IFIFO ||
					 (pt.pt_mode&S_IFMT) == S_IFREG)))) {
				memset(&st, 0, sizeof st);
				st.st_mode = pt.pt_mode;
				st.st_blksize = 4096;
				st.st_nlink = 1;
				goto missingok;
			}
			else if (sysv3 < 0)
				msg(2, 0, "< %s > ?\n", np);
			else if (sysv3 > 0)
				msg(2, 0, "Cannot obtain information "
						"about file:  \"%s\".\n",
						np);
			else
				emsg(2, "Error with lstat of \"%s\"", np);
			errcnt++;
			continue;
		}
	missingok:
		if (Lflag && (st.st_mode&S_IFMT) == S_IFLNK) {
			if (stat(np, &st) < 0) {
				emsg(2, "Cannot follow \"%s\"", np);
				errcnt++;
				continue;
			}
		}
		/*
		 * These file types are essentially useless in an archive
		 * since they are recreated by any process that needs them.
		 * We thus ignore them and do not even issue a warning,
		 * because that would only displace more important messages
		 * on a terminal and confuse people who just want to copy
		 * directory hierarchies.--But for pax, POSIX.1-2001 requires
		 * us to fail!
		 */
		if ((st.st_mode&S_IFMT) == S_IFSOCK ||
				(st.st_mode&S_IFMT) == S_IFDOOR) {
			if (pax >= PAX_TYPE_PAX2001) {
				msg(2, 0, "Cannot handle %s \"%s\".\n",
					(st.st_mode&S_IFMT) == S_IFSOCK ?
						"socket" : "door", np);
				errcnt++;
			}
			continue;
		}
		if (Pflag) {
			if (pt.pt_spec & PT_TYPE)
				if ((st.st_mode&S_IFMT) != (pt.pt_mode&S_IFMT))
					msg(4, 0, "line %lld: types "
						"do not match\n", lineno);
			if (pt.pt_spec & PT_OWNER)
				st.st_uid = pt.pt_uid;
			if (pt.pt_spec & PT_GROUP)
				st.st_gid = pt.pt_gid;
			if (pt.pt_spec & PT_MODE) {
				st.st_mode &= ~(mode_t)07777;
				st.st_mode |= pt.pt_mode;
			}
			if (pt.pt_spec & PT_ATIME)
				st.st_atime = pt.pt_atime;
			if (pt.pt_spec & PT_MTIME)
				st.st_mtime = pt.pt_mtime;
			if (pt.pt_spec & PT_RDEV) {
				if ((st.st_mode&S_IFMT) != S_IFBLK &&
				    (st.st_mode&S_IFMT) != S_IFCHR)
					msg(4, 0, "line %lld: device type "
						"specified for non-device "
						"file\n", lineno);
				st.st_rdev = pt.pt_rdev;
			}
		}
		if (pax_track(np, st.st_mtime) == 0)
			continue;
		if ((fmttype == FMT_ZIP ||
					fmttype & TYP_BAR ||
					fmttype == FMT_GNUTAR)
				&& (st.st_mode&S_IFMT) == S_IFDIR &&
				name[namlen-1] != '/') {
			if (namlen+2 >= namsiz) {
				size_t	diff = np - name;
				name = srealloc(name, namsiz = namlen+2);
				np = &name[diff];
			}
			name[namlen++] = '/';
			name[namlen] = '\0';
		}
		errcnt += copyfn(np, &st);
	}
}

/*
 * Execution for -o.
 */
static void
dooutp(void)
{
	if (Oflag) {
		if ((mt = Aflag ? open(Oflag, O_RDWR, 0666) :
					creat(Oflag, 0666)) < 0) {
			if (sysv3) {
				emsg(013, "Cannot open <%s> for %s.", Oflag,
					Aflag ? "append" : "output");
				done(1);
			} else
				msg(3, -2, "Cannot open \"%s\" for %s\n", Oflag,
					Aflag ? "append" : "output");
		}
	} else
		mt = dup(1);
	mstat();
	blkbuf = svalloc(blksiz, 1);
	if (Aflag) {
		if (totrailer() != 0)
			return;
	} else if (fmttype == FMT_NONE)
		fmttype = bigendian() ? FMT_BINBE : FMT_BINLE;
	if (fmttype & TYP_BINARY) {
		maxino = 0177777;
		fakeino = 0177777;
		maxpath = 256;
		if (fmttype & TYP_SGI) {
			maxsize = 0x7FFFFFFFFFFFFFFFLL;
			maxmajor = 037777;
			maxminor = 0777777;
		} else {
			maxsize = 0x7FFFFFFFLL;
			maxrdev = 0177777;
		}
		maxuid = 0177777;
		maxgid = 0177777;
		maxnlink = 0177777;
	} else if (fmttype == FMT_ODC) {
		maxino = 0777777;
		fakeino = 0777777;
		maxpath = 256;
		maxsize = 077777777777LL;
		maxrdev = 0777777;
		maxuid = 0777777;
		maxgid = 0777777;
		maxnlink = 0777777;
	} else if (fmttype == FMT_DEC) {
		maxino = 0777777;
		fakeino = 0777777;
		maxpath = 256;
		maxsize = 077777777777LL;
		maxmajor = 077777777;
		maxminor = 077777777;
		maxuid = 0777777;
		maxgid = 0777777;
		maxnlink = 0777777;
	} else if (fmttype & TYP_NCPIO) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = 1024;
		maxsize = fmttype&TYP_SCO ? 0x7FFFFFFFFFFFFFFFLL : 0xFFFFFFFFUL;
		maxmajor = 0xFFFFFFFFUL;
		maxminor = 0xFFFFFFFFUL;
		maxuid = 0xFFFFFFFFUL;
		maxgid = 0xFFFFFFFFUL;
		maxnlink = 0xFFFFFFFFUL;
	} else if (fmttype & TYP_CRAY) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = SANELIMIT;
		maxsize = 0x7FFFFFFFFFFFFFFFLL;
		maxrdev = 0x7FFFFFFFFFFFFFFFLL;
		maxuid = 0x7FFFFFFFFFFFFFFFLL;
		maxgid = 0x7FFFFFFFFFFFFFFFLL;
		maxnlink = 0x7FFFFFFFFFFFFFFFLL;
	} else if (fmttype == FMT_GNUTAR) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = SANELIMIT;
		maxsize = 0x7FFFFFFFFFFFFFFFLL;
		maxmajor = 0x7FFFFFFFFFFFFFFFLL;
		maxminor = 0x7FFFFFFFFFFFFFFFLL;
		maxuid = 0x7FFFFFFFFFFFFFFFLL;
		maxgid = 0x7FFFFFFFFFFFFFFFLL;
		maxnlink = 0x7FFFFFFFFFFFFFFFLL;
	} else if (fmttype & TYP_PAX) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = SANELIMIT;
		maxsize = 0x7FFFFFFFFFFFFFFFLL;
		maxmajor = fmttype==FMT_SUN ? 0x7FFFFFFFFFFFFFFFLL : 07777777;
		maxminor = fmttype==FMT_SUN ? 0x7FFFFFFFFFFFFFFFLL : 07777777;
		maxuid = 0x7FFFFFFFFFFFFFFFLL;
		maxgid = 0x7FFFFFFFFFFFFFFFLL;
		maxnlink = 0x7FFFFFFFFFFFFFFFLL;
		if (pax_oflag & PO_TIMES)
			globrec |= PR_ATIME|PR_MTIME;
	} else if (fmttype & TYP_BAR) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = 512 - SIZEOF_bar_header - 1;
		maxsize = 077777777777LL;
		maxrdev = 07777777;
		maxuid = 07777777;
		maxgid = 07777777;
		maxnlink = 0x7FFFFFFFFFFFFFFFLL;
		if (nwritten == 0)
			dump_barhdr();
	} else if (fmttype & TYP_USTAR) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = 256;
		maxsize = 077777777777LL;
		maxmajor = 07777777;
		maxminor = 07777777;
		maxuid = 07777777;
		maxgid = 07777777;
		maxnlink = 0x7FFFFFFFFFFFFFFFLL;
	} else if (fmttype & TYP_OTAR) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = 99;
		maxsize = 077777777777LL;
		maxuid = 07777777;
		maxgid = 07777777;
		maxnlink = 0x7FFFFFFFFFFFFFFFLL;
	} else if (fmttype == FMT_ZIP) {
		maxino = 0xFFFFFFFFUL;
		fakeino = 0xFFFFFFFFUL;
		maxpath = 60000;
		maxsize = 0x7FFFFFFFFFFFFFFFLL;
		maxrdev = 0xFFFFFFFFUL;
		maxuid = 0xFFFFFFFFUL;
		maxgid = 0xFFFFFFFFUL;
		maxnlink = 0xFFFFFFFFUL;
	} else
		abort();
	fakedev = 0177777;
	harddev = 1;
	copyout(outfile);
	if (fmttype & TYP_NCPIO)
		lflush();
	if (fmttype & TYP_CPIO) {
		struct stat	st;

		memset(&st, 0, sizeof st);
		st.st_nlink = 1;
		outfile(trailer, &st);
	}
	if (fmttype & TYP_TAR) {
		char b[512];

		memset(b, 0, sizeof b);
		bwrite(b, sizeof b);
		bwrite(b, sizeof b);
	}
	if (fmttype == FMT_ZIP)
		ziptrailer();
	bflush();
}

/*
 * Handle a single file for -o, do sanity checks and detect hard links.
 */
static int
outfile(const char *file, struct stat *st)
{
	uint32_t dev, ino;
	size_t pathsz;

	if ((st->st_mode&S_IFMT) == S_IFREG)
		if (mtst.st_dev == st->st_dev && mtst.st_ino == st->st_ino)
			return 0;
	if (st->st_size > maxsize) {
		msg(2, 0, "Size of %c%s%c >%lluGB. Not dumped\n",
				sysv3 ? '<' : '"',
				file,
				sysv3 ? '>' : '"',
				(maxsize+1) / (1024*1024*1024));
		return 1;
	}
	if (((st->st_mode&S_IFMT)==S_IFBLK||(st->st_mode&S_IFMT)==S_IFCHR) &&
		(maxrdev &&
			(unsigned long long)st->st_rdev > maxrdev ||
		maxmajor &&
			(unsigned long long)major(st->st_rdev) > maxmajor ||
		maxminor &&
			(unsigned long long)minor(st->st_rdev) > maxminor)) {
		cantsup(1, file);
		return 1;
	}
	if ((unsigned long long)st->st_uid > maxuid) {
		if (cray_eflag) {
			cantsup(1, file);
			return 1;
		}
		cantsup(0, file);
		st->st_uid = 60001;
		if ((st->st_mode&S_IFMT) == S_IFREG && st->st_mode & 0111)
			st->st_mode &= ~(mode_t)S_ISUID;
		if ((unsigned long long)st->st_gid > maxgid) {
			st->st_gid = 60001;
			if ((st->st_mode&S_IFMT)==S_IFREG && st->st_mode&0010)
				st->st_mode &= ~(mode_t)S_ISGID;
		}
	} else if ((unsigned long long)st->st_gid > maxgid) {
		if (cray_eflag) {
			cantsup(1, file);
			return 1;
		}
		cantsup(0, file);
		st->st_gid = 60001;
		if ((st->st_mode&S_IFMT) == S_IFREG && st->st_mode & 0010)
			st->st_mode &= ~(mode_t)S_ISGID;
	}
	if ((pathsz = strlen(file)) > maxpath) {
		msg(2, 0, "%s: file name too long\n", file);
		return 1;
	}
	/*
	 * Detect hard links and compute fake inode counts. The mechanism
	 * is as follows: If a file has more than one link, a fake device
	 * number starting at one is used for its device, and a fake inode
	 * number is used starting at one too.
	 *
	 * The information on links of directories is useless, so it is
	 * dropped and handled like a file with a single link only: Fake
	 * devices are allocated just below the format's limit, fake
	 * i-nodes the same.
	 *
	 * This way even the binary cpio format can have up to ~4G files.
	 */
	if (maxino && st->st_nlink > 1 && (st->st_mode&S_IFMT) != S_IFDIR) {
		struct dslot *ds, *dp;
		struct islot *ip;

		dev = 1;
		ds = devices;
		dp = NULL;
nextdev:
		for (; ds; dp = ds, ds = ds->d_nxt, dev++ /* see below! */)
			if (ds->d_dev == st->st_dev)
				break;
		if (markeddevs && marked(dev)) {
			dev++;
			goto nextdev;
		}
		if (dev >= fakedev)
			msg(4, 1, "Too many devices in archive, exiting\n");
		if (ds == NULL) {
			ds = scalloc(1, sizeof *ds);
			ds->d_dev = st->st_dev;
			ds->d_fake = dev;
			if (devices == NULL)
				devices = ds;
			else
				dp->d_nxt = ds;
		}
		harddev = dev;
		if ((ip = ifind(st->st_ino, &ds->d_isl)) == NULL) {
			if (ds->d_cnt >= maxino) {
				/* corresponds to for loop above */
				dev++, dp = ds, ds = ds->d_nxt;
				goto nextdev;
			}
			ip = scalloc(1, sizeof *ip);
			ip->i_ino = st->st_ino;
			ip->i_fino = ++ds->d_cnt;
			ip->i_nlk = st->st_nlink;
			if (fmttype & TYP_TAR)
				ip->i_name = sstrdup(file);
			if (fmttype & TYP_NCPIO) {
				ip->i_st = smalloc(sizeof *ip->i_st);
				*ip->i_st = *st;
			}
			iput(ip, &ds->d_isl);
		}
		ino = ip->i_fino;
		if (fmttype & TYP_NCPIO) {
			/*
			 * In SVR4 ascii cpio format, files with multiple
			 * links are stored with a zero size except for the
			 * last link, which contains the actual file content.
			 * As one cannot know which is the last link in
			 * advance since some links may be outside the
			 * archive content, all links have to be collected
			 * and written out at once.
			 */
			struct ilink *il, *ik;

			switch (ip->i_nlk) {
			case 1:
				/*
				 * This was the last link to a file. Write
				 * all previous links and break to write
				 * the actual file content. Free the pointers
				 * in islot; islot remains within the tree
				 * with a remaining link count of zero.
				 */
				ip->i_nlk--;
				free(ip->i_st);
				ip->i_st = NULL;
				for (il = ip->i_lnk, ik = NULL; il;
						ik = il, il = il->l_nxt,
						ik ? free(ik), 0 : 0) {
					errcnt += addfile(il->l_nam, st,
							dev, ino, 1, 0);
					free(il->l_nam);
				}
				break;
			case 0:
				/*
				 * This file got a link during operation, or
				 * -L was specified and we encountered a link
				 * more than once. Start with a fresh link
				 * count again.
				 */
				ip->i_nlk = st->st_nlink;
				ip->i_lnk = NULL;
				ip->i_st = smalloc(sizeof *ip->i_st);
				*ip->i_st = *st;
				/*FALLTHRU*/
			default:
				/*
				 * There are more links to this file. Store
				 * only the name and return.
				 */
				ip->i_nlk--;
				if (ip->i_lnk) {
					for (il = ip->i_lnk; il->l_nxt;
							il = il->l_nxt);
					il->l_nxt = scalloc(1,sizeof*il->l_nxt);
					il = il->l_nxt;
				} else {
					ip->i_lnk = scalloc(1,sizeof*ip->i_lnk);
					il = ip->i_lnk;
				}
				il->l_nam = smalloc(pathsz + 1);
				strcpy(il->l_nam, file);
				return 0;
			}
		} else if (fmttype & TYP_TAR) {
			if (strcmp(ip->i_name, file))
				return addfile(file, st, dev, ino, 1,
						ip->i_name);
		}
	} else {	/* single-linked or directory */
		dev = fakedev;
		while (markeddevs && marked(dev))
			dev--;
		if ((ino = fakeino--) == 0) {
			if (--dev <= harddev)
				msg(4, 1, "Too many devices in archive, "
						"exiting\n");
			fakedev = dev;
			ino = maxino;
			fakeino = ino - 1;
		}
	}
	return addfile(file, st, dev, ino, 0, 0);
}

/*
 * Add a single file to the archive with -o.
 */
static int
addfile(const char *realfile, struct stat *st,
		uint32_t dev, uint32_t ino, int zerolink, const char *linkname)
{
	union bincpio bc;
	int fd = -1;
	long long size;
	int pad, i;
	ssize_t rsz = 0, wsz = 0, hsz, fsz, psz;
	long long remsz, relative, nlink;
	long long Kbase = 0, Krest = 0, Ksize = 0;
	struct hdr_cpio	K2hdr;
	uint32_t	crc = 0;
	long long	csize = 0;
	char	*file;
	char	*symblink = NULL;
	int	failure = 1;

	file = sstrdup(realfile);
	if (pax != PAX_TYPE_CPIO && strcmp(file, trailer)) {
		size_t	junk = 0;
		if (pax_sflag && pax_sname(&file, &junk) == 0)
			goto cleanup;
		if (rflag && rname(&file, &junk) == 0)
			goto cleanup;
	}
	fsz = strlen(file);
	relative = nwritten;
	memset(bc.data, 0, sizeof bc.data);
	if (fmttype == FMT_PAX && pax_oflag & PO_LINKDATA &&
			(st->st_mode&S_IFMT) == S_IFREG)
		size = st->st_size;
	else if (zerolink)
		size = 0;
	else if ((st->st_mode&S_IFMT) == S_IFREG)
		size = st->st_size;
	else if ((st->st_mode&S_IFMT) == S_IFLNK) {
		i = st->st_size ? st->st_size : PATH_MAX;
		symblink = smalloc(i+1);
		if ((size = readlink(realfile, symblink, i)) < 0) {
			emsg(3, "Cannot read symbolic link \"%s\"", realfile);
			goto cleanup;
		}
		symblink[size] = '\0';
	} else
		size = 0;
	nlink = ((unsigned long long)st->st_nlink>maxnlink ?
			maxnlink : st->st_nlink);
	if (fmttype & TYP_NCPIO) {
		long	size1;
		if (fmttype & TYP_SCO && size >= mag_sco) {
			char	*ofile = file;
			size1 = mag_sco;
			fsz += 22;
			file = smalloc(fsz + 1);
			snprintf(file, fsz + 1, "%s%csize=%016llx",
					ofile, 0, size);
			free(ofile);
		} else
			size1 = size;
		pad = 4;
		sprintf(bc.data, "%*.*s%08lx%08lx%08lx%08lx%08lx%08lx"
				"%08lx%08lx%08lx%08lx%08lx%08lx%08lx",
			(int)(fmttype&TYP_CRC? sizeof mag_crc:sizeof mag_asc),
			(int)(fmttype&TYP_CRC? sizeof mag_crc:sizeof mag_asc),
			fmttype & TYP_CRC ? mag_crc : mag_asc,
			(long)ino & ECUT,
			(long)st->st_mode & ECUT,
			(long)st->st_uid & ECUT,
			(long)st->st_gid & ECUT,
			(long)nlink & ECUT,
			(long)st->st_mtime & ECUT,
			(long)size1 & ECUT,
			(long)major(dev) & ECUT,
			(long)minor(dev) & ECUT,
			(long)major(st->st_rdev) & ECUT,
			(long)minor(st->st_rdev) & ECUT,
			(long)++fsz,
			0L);
		hsz = SIZEOF_Exp_cpio_hdr;
		if ((psz = (hsz + fsz) % pad) != 0)
			psz = pad - psz;
	} else if (fmttype == FMT_ODC) {
		pad = 1;
		sprintf(bc.data, "%*.*s%06lo%06lo%06lo%06lo%06lo%06lo%06lo"
				"%011lo%06lo%011lo",
			(int)sizeof mag_odc, (int)sizeof mag_odc, mag_odc,
			(long)dev & OCUT,
			(long)ino & OCUT,
			(long)st->st_mode & OCUT,
			(long)st->st_uid & OCUT,
			(long)st->st_gid & OCUT,
			(long)nlink & OCUT,
			(long)st->st_rdev & OCUT,
			(long)st->st_mtime,
			(long)++fsz,
			(long)size);
		hsz = SIZEOF_c_hdr;
		if ((psz = (hsz + fsz) % pad) != 0)
			psz = pad - psz;
	} else if (fmttype == FMT_DEC) {
		pad = 1;
		sprintf(bc.data, "%*.*s%06lo%06lo%06lo%06lo%06lo%06lo"
				"%08lo%08lo%011lo%06lo%011lo",
			(int)sizeof mag_odc, (int)sizeof mag_odc, mag_odc,
			(long)dev & OCUT,
			(long)ino & OCUT,
			(long)st->st_mode & OCUT,
			(long)st->st_uid & OCUT,
			(long)st->st_gid & OCUT,
			(long)nlink & OCUT,
			(long)major(st->st_rdev) & 077777777,
			(long)minor(st->st_rdev) & 077777777,
			(long)st->st_mtime,
			(long)++fsz,
			(long)size);
		hsz = SIZEOF_d_hdr;
		if ((psz = (hsz + fsz) % pad) != 0)
			psz = pad - psz;
	} else if (fmttype & TYP_BINARY) {
		/*
		 * To avoid gcc's stupid 'comparison is always false due to
		 * limited range of data type' warning.
		 */
		unsigned long long	gcccrap;
		pad = 2;
		if (fmttype & TYP_BE) {
			be16p(mag_bin, bc.Hdr.c_magic);
			be16p(dev, bc.Hdr.c_dev);
			be16p(ino, bc.Hdr.c_ino);
			be16p(st->st_mode, bc.Hdr.c_mode);
			be16p(st->st_uid, bc.Hdr.c_uid);
			be16p(st->st_gid, bc.Hdr.c_gid);
			be16p(nlink, bc.Hdr.c_nlink);
			be16p(st->st_rdev, bc.Hdr.c_rdev);
			be32p(st->st_mtime, bc.Hdr.c_mtime);
			be16p(++fsz, bc.Hdr.c_namesize);
		} else {
			le16p(mag_bin, bc.Hdr.c_magic);
			le16p(dev, bc.Hdr.c_dev);
			le16p(ino, bc.Hdr.c_ino);
			le16p(st->st_mode, bc.Hdr.c_mode);
			le16p(st->st_uid, bc.Hdr.c_uid);
			le16p(st->st_gid, bc.Hdr.c_gid);
			le16p(nlink, bc.Hdr.c_nlink);
			le16p(st->st_rdev, bc.Hdr.c_rdev);
			me32p(st->st_mtime, bc.Hdr.c_mtime);
			le16p(++fsz, bc.Hdr.c_namesize);
		}
		if (fmttype & TYP_SGI && size > 0x7FFFFFFFLL) {
			Krest = size & 0x7FFFFFFFLL;
			Kbase = size - Krest;
			Ksize = 0x100000000LL - (Kbase >> 31);
			if (fmttype & TYP_BE)
				be32p(Ksize, bc.Hdr.c_filesize);
			else
				me32p(Ksize, bc.Hdr.c_filesize);
			K2hdr = bc.Hdr;
			if (fmttype & TYP_BE)
				be32p(Krest, K2hdr.c_filesize);
			else
				me32p(Krest, K2hdr.c_filesize);
		} else {
			if (fmttype & TYP_BE)
				be32p(size, bc.Hdr.c_filesize);
			else
				me32p(size, bc.Hdr.c_filesize);
		}
		if (fmttype & TYP_SGI &&
				(((st->st_mode&S_IFMT) == S_IFBLK ||
				 (st->st_mode&S_IFMT) == S_IFCHR) &&
				((unsigned long long)major(st->st_rdev)>0xFF ||
				 (unsigned long long)minor(st->st_rdev)>0xFF) ||
				(gcccrap = st->st_rdev) > 0177777)) {
			uint32_t	rdev;
			rdev = (minor(st->st_rdev) & 0x0003FFFF) +
				((major(st->st_rdev)<<18) & 0xFFFC0000);
			if (fmttype & TYP_BE) {
				be16p(0xFFFF, bc.Hdr.c_rdev);
				be32p(rdev, bc.Hdr.c_filesize);
			} else {
				le16p(0xFFFF, bc.Hdr.c_rdev);
				me32p(rdev, bc.Hdr.c_filesize);
			}
		}
		hsz = SIZEOF_hdr_cpio;
		psz = (hsz + fsz) % 2;
	} else if (fmttype & TYP_CRAY) {
		int	diff5 = fmttype==FMT_CRAY5 ? CRAY_PARAMSZ : 0;
		mode_t	mo;
		pad = 1;
		be64p(mag_bin, bc.Crayhdr.C_magic);
		be64p(dev, bc.Crayhdr.C_dev);
		be64p(ino, bc.Crayhdr.C_ino);
		if ((st->st_mode&S_IFMT) == S_IFLNK)	/* non-standard */
			mo = st->st_mode&07777|0130000;	/* S_IFLNK on Cray */
		else
			mo = st->st_mode;
		be64p(mo, bc.Crayhdr.C_mode);
		be64p(st->st_uid, bc.Crayhdr.C_uid);
		be64p(st->st_gid, bc.Crayhdr.C_gid);
		be64p(nlink, bc.Crayhdr.C_nlink);
		be64p(st->st_rdev, bc.Crayhdr.C_rdev);
		be64p(st->st_mtime, bc.Crayhdr.C_mtime - diff5);
		be64p(++fsz, bc.Crayhdr.C_namesize - diff5);
		be64p(size, bc.Crayhdr.C_filesize - diff5);
		hsz = SIZEOF_cray_hdr - diff5;
		psz = 0;
	} else if (fmttype & TYP_BAR) {
		int	c, n = 0;
		pad = 512;
		sprintf(bc.Bdr.b_mode, "%7.7o",(int)st->st_mode&(07777|S_IFMT));
		sprintf(bc.Bdr.b_uid, "%7.7lo", (long)st->st_uid);
		sprintf(bc.Bdr.b_gid, "%7.7lo", (long)st->st_gid);
		sprintf(bc.Bdr.b_size, "%11.11llo",
				(st->st_mode&S_IFMT) == S_IFREG && !zerolink ?
				(long long)st->st_size&077777777777LL : 0LL);
		sprintf(bc.Bdr.b_mtime, "%11.11lo", (long)st->st_mtime);
		sprintf(bc.Bdr.b_rdev, "%7.7lo", (long)st->st_rdev);
		strcpy(&bc.data[SIZEOF_bar_header], file);
		c = tlflag(st);
		if (zerolink == 0) {
			bc.Bdr.b_linkflag = c;
			if (c == '2') {
				strncpy(&bc.data[SIZEOF_bar_header+fsz+1],
						symblink,
						512-SIZEOF_bar_header-fsz);
				n = size;
			}
		} else {
			bc.Bdr.b_linkflag = '1';
			strncpy(&bc.data[SIZEOF_bar_header+fsz+1], linkname,
					512-SIZEOF_bar_header-fsz-1);
			n = strlen(linkname);
		}
		if (n > 512-SIZEOF_bar_header-fsz-1) {
			msg(3, 0, "%s: linked name too long\n", realfile);
			goto cleanup;
		}
		bchksum(&bc);
		hsz = 512;
		psz = 0;
		fsz = 0;
	} else if (fmttype & TYP_TAR) {
		const char	*cp;
		int	c;
		/*
		 * Many SVR4 cpio derivatives expect the mode field
		 * to contain S_IFMT bits. The meaning of these bits
		 * in the mode field of the ustar header is left
		 * unspecified by IEEE Std 1003.1, 1996, 10.1.1.
		 */
		int	mmask = fmttype == FMT_USTAR || fmttype == FMT_PAX ?
				07777 : 07777|S_IFMT;

		paxrec = globrec;
		pad = 512;
		if (tmkname(&bc.Tdr, file) != 0)
			goto cleanup;
		sprintf(bc.Tdr.t_mode, "%7.7o", (int)st->st_mode & mmask);
		if (fmttype == FMT_GNUTAR && st->st_uid > 07777777) {
			be64p(st->st_uid, bc.Tdr.t_uid);
			bc.Tdr.t_uid[0] |= 0200;
		} else {
			sprintf(bc.Tdr.t_uid, "%7.7lo",
					(long)st->st_uid&07777777);
			if (fmttype & TYP_PAX && st->st_uid > 07777777)
				paxrec |= PR_UID;
		}
		if (fmttype == FMT_GNUTAR && st->st_gid > 07777777) {
			be64p(st->st_gid, bc.Tdr.t_gid);
			bc.Tdr.t_gid[0] |= 0200;
		} else {
			sprintf(bc.Tdr.t_gid, "%7.7lo",
					(long)st->st_gid&07777777);
			if (fmttype & TYP_PAX && st->st_gid > 07777777)
				paxrec |= PR_GID;
		}
		if (fmttype == FMT_GNUTAR && (st->st_mode&S_IFMT) == S_IFREG &&
				st->st_size > 077777777777LL && !zerolink) {
			bc.Tdr.t_size[0] = '\200';
			be64p(st->st_size, &bc.Tdr.t_size[4]);
		} else {
			sprintf(bc.Tdr.t_size, "%11.11llo",
				(st->st_mode&S_IFMT) == S_IFREG &&
				(!zerolink || fmttype == FMT_PAX &&
				 	pax_oflag & PO_LINKDATA) ?
				(long long)st->st_size&077777777777LL : 0LL);
			if (fmttype & TYP_PAX &&
					(st->st_mode&S_IFMT) == S_IFREG &&
					st->st_size > 077777777777LL &&
					(!zerolink || fmttype == FMT_PAX &&
					 	pax_oflag & PO_LINKDATA))
				paxrec |= PR_SIZE;
		}
		sprintf(bc.Tdr.t_mtime, "%11.11lo", (long)st->st_mtime);
		if ((c = tlflag(st)) < 0) {
			if ((st->st_mode&S_IFMT) != S_IFDIR) {
				msg(2, 0, "%s is not a file. Not dumped\n",
						realfile);
				errcnt++;
			} else
				failure = 0;
			goto cleanup;
		}
		if (zerolink == 0) {
			bc.Tdr.t_linkflag = c;
			if (c == '2') {
				if (tmklink(&bc.Tdr, symblink) != 0)
					goto cleanup;
			}
		} else {
			bc.Tdr.t_linkflag = '1';
			if (tmklink(&bc.Tdr, linkname) != 0)
				goto cleanup;
		}
		if (fmttype & TYP_USTAR) {
			if (fmttype == FMT_GNUTAR)
				strcpy(bc.Tdr.t_magic, mag_gnutar);
			else {
				strcpy(bc.Tdr.t_magic, mag_ustar);
				bc.Tdr.t_version[0] = bc.Tdr.t_version[1] = '0';
			}
			if ((cp = getuser(st->st_uid)) != NULL)
				sprintf(bc.Tdr.t_uname, "%.31s", cp);
			if ((cp = getgroup(st->st_gid)) != NULL)
				sprintf(bc.Tdr.t_gname, "%.31s", cp);
			else
				msg(1, 0, "could not get group information "
						"for %s\n", realfile);
			if (fmttype == FMT_GNUTAR &&
					(unsigned long long)major(st->st_rdev)
					> 077777777) {
				be64p(major(st->st_rdev), bc.Tdr.t_devmajor);
				bc.Tdr.t_devmajor[0] |= 0200;
			} else {
				if (fmttype == FMT_SUN &&
					(unsigned long long)major(st->st_rdev)
						> 077777777 &&
						((st->st_mode&S_IFMT)==S_IFBLK||
						 (st->st_mode&S_IFMT)==S_IFCHR))
					paxrec |= PR_SUN_DEVMAJOR;
				sprintf(bc.Tdr.t_devmajor, "%7.7o",
					(int)major(st->st_rdev)&07777777);
			}
			if (fmttype == FMT_GNUTAR &&
					(unsigned long long)minor(st->st_rdev)
					> 077777777) {
				be64p(minor(st->st_rdev), bc.Tdr.t_devminor);
				bc.Tdr.t_devminor[0] |= 0200;
			} else {
				if (fmttype == FMT_SUN &&
					(unsigned long long)minor(st->st_rdev)
						> 077777777 &&
						((st->st_mode&S_IFMT)==S_IFBLK||
						 (st->st_mode&S_IFMT)==S_IFCHR))
					paxrec |= PR_SUN_DEVMINOR;
				sprintf(bc.Tdr.t_devminor, "%7.7o",
					(int)minor(st->st_rdev)&07777777);
			}
		}
		tchksum(&bc);
		hsz = 512;
		psz = 0;
		fsz = 0;
	} else if (fmttype == FMT_ZIP) {
		pad = 1;
		memcpy(bc.Zdr.z_signature, mag_zipsig, sizeof mag_zipsig);
		bc.Zdr.z_version[0] = 10;
		mkdostime(st->st_mtime, bc.Zdr.z_modtime, bc.Zdr.z_moddate);
		if ((st->st_mode&S_IFMT) == S_IFREG ||
				(st->st_mode&S_IFMT) == S_IFLNK) {
			le32p(size, bc.Zdr.z_csize);
			le32p(size, bc.Zdr.z_nsize);
			csize = size;
		}
		le16p(fsz, bc.Zdr.z_namelen);
		le16p(SIZEOF_zextra_cp, bc.Zdr.z_extralen);
		hsz = SIZEOF_zip_header;
		psz = 0;
	} else
		abort();
	/*
	 * Start writing the file to the archive.
	 */
	if ((st->st_mode&S_IFMT) == S_IFREG && st->st_size != 0 &&
			(zerolink == 0 || fmttype == FMT_PAX &&
			 	pax_oflag & PO_LINKDATA)) {
		char	*buf;
		size_t	bufsize;
		int	readerr = 0;

		if ((fd = open(realfile, O_RDONLY)) < 0) {
			if (sysv3 < 0)
				msg(0, 0, "< %s > ?\n", realfile);
			else if (sysv3 > 0)
				fprintf(stderr, "<%s> ?\n", realfile);
			else
				msg(0, 0, "\"%s\" ?\n", realfile);
			goto cleanup;
		}
		if (fmttype == FMT_ZIP) {
			if (zipwrite(fd, file, st, &bc, fsz, dev, ino,
						&crc, &csize) < 0)
				goto cleanup2;
			goto done;
		}
		if (fmttype & TYP_CRC)
			if (sum(fd, realfile, st, bc.Edr.E_chksum) < 0)
				goto cleanup2;
		if (fmttype & TYP_PAX && paxrec != PR_NONE)
			wrpax(file, symblink?symblink:linkname, st);
		bwrite(bc.data, hsz);
		if (fsz)
			bwrite(file, fsz);
		if (psz)
			bwrite(&bc.data[hsz], psz);
		if (Kbase)
			remsz = Kbase;
		else
			remsz = st->st_size;
		getbuf(&buf, &bufsize, st->st_blksize);
	again:	while (remsz > 0) {
			if (fd < 0 || (rsz = read(fd, &buf[wsz],
							bufsize - wsz)) < 0) {
				if (readerr == 0) {
					emsg(3, "Cannot read \"%s\"", realfile);
					if (fd >= 0)
						errcnt++;
					readerr = 1;
				}
				if (fd >= 0 && lseek(fd, bufsize - wsz,
							SEEK_CUR) < 0) {
					close(fd);
					fd = -1;
				}
				rsz = bufsize - wsz;
				if (rsz > remsz)
					rsz = remsz;
				memset(&buf[wsz], 0, rsz);
			}
			if (rsz > remsz)
				rsz = remsz;
			wsz += rsz;
			remsz -= rsz;
			bwrite(buf, wsz);
			memset(buf, 0, wsz);
			size = wsz;
			wsz = 0;
		}
		wsz = size;
		if (Kbase) {
			if ((i = Ksize % pad) != 0)
				bwrite(&bc.data[hsz], i);
			bwrite((char *)&K2hdr, hsz);
			if (fsz)
				bwrite(file, fsz);
			if (psz)
				bwrite(&bc.data[hsz], psz);
			remsz = Krest;
			Kbase = 0;
			wsz = 0;
			goto again;
		} else if (Ksize)
			wsz = Krest;
	} else if ((fmttype == FMT_ZIP || fmttype & TYP_CPIO) &&
			(st->st_mode&S_IFMT) == S_IFLNK) {
		wsz = size;
		if (fmttype == FMT_ZIP) {
			crc = zipcrc(0, (unsigned char *)symblink, wsz);
			le32p(crc, bc.Zdr.z_crc32);
			bwrite(bc.data, SIZEOF_zip_header);
			bwrite(file, fsz);
			zipwxtra(file, st, dev, ino);
			bwrite(symblink, wsz);
		} else {
			bwrite(bc.data, hsz);
			if (fsz)
				bwrite(file, fsz);
			if (psz)
				bwrite(&bc.data[hsz], psz);
			bwrite(symblink, wsz);
		}
	} else {
		if (fmttype & TYP_PAX && paxrec != PR_NONE)
			wrpax(file, symblink?symblink:linkname, st);
		bwrite(bc.data, hsz);
		if (fsz)
			bwrite(file, fsz);
		if (psz)
			bwrite(&bc.data[hsz], psz);
		if (fmttype == FMT_ZIP)
			zipwxtra(file, st, dev, ino);
	}
done:	if (fmttype == FMT_ZIP) {
		zipdefer(file, st, relative, crc, csize, &bc.Zdr);
	}
	if ((i = wsz % pad) != 0)
		bwrite(&bc.data[hsz], pad - i);
	if (vflag && strcmp(file, trailer))
		fprintf(stderr, "%s\n", file);
	else if (Vflag)
		prdot(0);
	failure = 0;
cleanup2:
	if ((st->st_mode&S_IFMT) == S_IFREG) {
		if (fd >= 0)
			close(fd);
		if (aflag)
			errcnt += rstime(realfile, st, "access");
	}
cleanup:
	free(file);
	free(symblink);
	return failure;
}

/*
 * Flush a SVR4 cpio format inode tree for -o.
 */
static void
iflush(struct islot *ip, uint32_t dev)
{
	if (ip == inull)
		return;
	iflush(ip->i_lln, dev);
	iflush(ip->i_rln, dev);
	if (ip->i_nlk > 0 && ip->i_st) {
		struct ilink *il;

		for (il = ip->i_lnk; il->l_nxt; il = il->l_nxt)
			errcnt += addfile(il->l_nam, ip->i_st,
					dev, ip->i_fino, 1, 0);
		errcnt += addfile(il->l_nam, ip->i_st, dev, ip->i_fino, 0, 0);
	}
}

/*
 * Flush the SVR4 cpio link forest for -o.
 */
static void
lflush(void)
{
	struct dslot *ds;

	for (ds = devices; ds; ds = ds->d_nxt)
		iflush(ds->d_isl, ds->d_fake);
}

int
setfmt(char *s)
{
	int	i, j;

	struct {
		const char	*ucs;
		const char	*lcs;
		int	type;
		int	bits;
	} fs[] = {
		{ "NEWC",	"newc",		FMT_ASC,	00 },
		{ "SCO",	"sco",		FMT_SCOASC,	00 },
		{ "CRC",	"crc",		FMT_CRC,	00 },
		{ "SCOCRC",	"scocrc",	FMT_SCOCRC,	00 },
		{ "ODC",	"odc",		FMT_ODC,	00 },
		{ "DEC",	"dec",		FMT_DEC,	00 },
		{ "BIN",	"bin",		FMT_NONE,	00 },
		{ "BBS",	"bbs",		TYP_BE,		00 },
		{ "SGI",	"sgi",		FMT_SGIBE,	00 },
		{ "CRAY",	"cray",		FMT_CRAY,	00 },
		{ "CRAY5",	"cray5",	FMT_CRAY5,	00 },
		{ "TAR",	"tar",		FMT_TAR,	00 },
		{ "USTAR",	"ustar",	FMT_USTAR,	00 },
		{ "PAX:",	"pax:",		FMT_PAX,	00 },
		{ "SUN",	"sun",		FMT_SUN,	00 },
		{ "GNU",	"gnu",		FMT_GNUTAR,	00 },
		{ "OTAR",	"otar",		FMT_OTAR,	00 },
		{ "BAR",	"bar",		FMT_BAR,	00 },
		{ "ZIP:",	"zip:",		FMT_ZIP,	00 },
		{ NULL,		NULL,		0,		00 }
	};
	for (i = 0; fs[i].ucs; i++) {
		for (j = 0; s[j] &&
				(s[j] == fs[i].ucs[j] || s[j] == fs[i].lcs[j]);
				j++)
			if (fs[i].ucs[j] == ':')
				break;
		if (s[j] == '\0' &&
				(fs[i].ucs[j] == '\0' || fs[i].ucs[j] == ':') ||
				s[j] == ':' && fs[i].ucs[j] == ':') {
			fmttype = fs[i].type;
			if (fmttype == FMT_ZIP && s[j] == ':') {
#if	USE_ZLIB
				if (strcmp(&s[j+1], "en") == 0)
					zipclevel = 00;
				else if (strcmp(&s[j+1], "ex") == 0)
					zipclevel = 01;
				else if (strcmp(&s[j+1], "ef") == 0)
					zipclevel = 02;
				else if (strcmp(&s[j+1], "es") == 0)
					zipclevel = 03;
				else
#endif	/* USE_ZLIB */
				if (strcmp(&s[j+1], "e0") == 0)
					zipclevel = 04;
				else
#if	USE_BZLIB
				if (strcmp(&s[j+1], "bz2") == 0)
					zipclevel = 07;
				else
#endif	/* USE_BZLIB */
					continue;
			} else if (fmttype == FMT_NONE)
				fmttype = bigendian() ? FMT_BINBE : FMT_BINLE;
			else if (fmttype == TYP_BE)
				fmttype = bigendian() ? FMT_BINLE : FMT_BINBE;
			else if (fmttype == FMT_PAX && s[j] == ':') {
				if (pax_options(&s[j+1], 0) < 0)
					continue;
			}
			return 0;
		}
	}
	msg(3, 0, "Invalid header \"%s\" specified.\n", s);
	return -1;
}

static int
bigendian(void)
{
	union {
		char	u_c[2];
		int16_t	u_i;
	} u;
	u.u_i = 1;
	return u.u_c[1] == 1;
}

int
setreassign(const char *s)
{
	struct passwd	*pwd;
	int	val = 0;

	if (myuid != 0) {
		msg(3, 0, "R option only valid for super-user.\n");
		val = -1;
	}
	if ((pwd = getpwnam(s)) == NULL) {
		msg(3, 0, "\"%s\" is not a valid user id\n", s);
		val = -1;
	} else {
		Ruid = pwd->pw_uid;
		Rgid = pwd->pw_gid;
	}
	return val;
}

void *
srealloc(void *m, size_t n)
{
	if ((m = realloc(m, n)) == NULL) {
		write(2, "Out of memory.\n", 15);
		_exit(sysv3 ? 2 : 3);
	}
	return m;
}

void *
smalloc(size_t n)
{
	return srealloc(NULL, n);
}

void *
scalloc(size_t nmemb, size_t size)
{
	void	*vp;

	if ((vp = calloc(nmemb, size)) == NULL) {
		write(2, "Out of memory.\n", 15);
		_exit(sysv3 ? 2 : 3);
	}
	return vp;
}

void *
svalloc(size_t n, int force)
{
	static long	pagesize;
	void	*vp;

	if (pagesize == 0)
		if ((pagesize = sysconf(_SC_PAGESIZE)) < 0)
			pagesize = 4096;
	if ((vp = memalign(pagesize, n)) == NULL && force) {
		write(2, "Out of memory.\n", 15);
		_exit(sysv3 ? 2 : 3);
	}
	return vp;
}

/*
 * A single static buffer is used for intermediate copying from file
 * data to the tape buffer and vice versa, for creating checksums, and
 * for data transfer with -p.
 */
static void
getbuf(char **bufp, size_t *sizep, size_t best)
{
	static char	*buf;
	static size_t	size;

	if (size != best) {
		if (buf)
			free(buf);
		size = best;
		if (size == 0 || (buf = svalloc(size, 0)) == NULL)
			buf = svalloc(size = 512, 1);
	}
	*bufp = buf;
	*sizep = size;
}

static void
sevprnt(int sev)
{
	if (printsev) switch (sev) {
	case 1:
		fprintf(stderr, "INFORM: ");
		break;
	case 2:
		fprintf(stderr, "WARNING: ");
		break;
	case 3:
		fprintf(stderr, "ERROR: ");
		break;
	case 4:
		fprintf(stderr, "HALT: ");
		break;
	}
}

void
msg(int sev, int err, const char *fmt, ...)
{
	va_list	ap;

	/*
	 * The error message should appear near the file it refers to.
	 */
	if (tflag)
		fflush(stdout);
	else if (Vflag)
		prdot(1);
	if (sysv3 >= 0 && sev >= (printsev ? 0 : -1))
		fprintf(stderr, "%s: ", progname);
	sevprnt(sev);
	va_start(ap, fmt);
	vfprintf(stderr, fmt, ap);
	va_end(ap);
	if (err > 0)
		done(err);
	else if (err == -2) {
		if (sysv3)
			done(1);
		usage();
	}
}

void
emsg(int sev, const char *fmt, ...)
{
	char	_fmt[60];
	int	i, fl = sev & 030, n;
	va_list	ap;

	sev &= ~030;
	i = errno;
	if (tflag)
		fflush(stdout);
	else if (Vflag)
		prdot(1);
	fprintf(stderr, "%s: ", progname);
	sevprnt(sev);
	va_start(ap, fmt);
	if (sysv3) {
		if (fmt[(n=strlen(fmt))-1] == '"' && fmt[n-2] == 's' &&
				fmt[n-3] == '%' && fmt[n-4] == '"' &&
				n < sizeof _fmt) {
			strcpy(_fmt, fmt);
			_fmt[n-1] = '>';
			_fmt[n-4] = '<';
			fmt = _fmt;
		}
	}
	vfprintf(stderr, fmt, ap);
	va_end(ap);
	if (sysv3 > 0 && sev >= 0 && fl & 010)
		fprintf(stderr, "\n\t%s\n", strerror(i));
	else if (sysv3 < 0) {
		if (fl & 020)
			putc('\n', stderr);
		else
			fprintf(stderr, " (errno:%d)\n", i);
	} else
		fprintf(stderr, ", errno %d, %s\n", i, strerror(i));
}

static void
prdot(int flush)
{
	static int	column;

	if (flush && column != 0 || column >= 50) {
		write(action == 'o' && !Oflag ? 2 : 1, "\n", 1);
		column = 0;
	}
	if (!flush) {
		write(action == 'o' && !Oflag ? 2 : 1, ".", 1);
		column++;
	}
}

/*
 * Ask the user for new media if applicable, or exit.
 */
static void
newmedia(int err)
{
	static int	mediacnt = 1;
	static char answer[PATH_MAX+1];
	const char	*mesf = action == 'i' ?
		(Iflag && !sysv3 ? Iflag : "input") :
		(Oflag && !sysv3 ? Oflag : "output");
	char c;
	int i, j;

	if (mfl == 0 && close(mt) < 0) {
		emsg(3, "Close error on \"%s\"", mesf);
		errcnt++;
	}
	mfl = -1;
	if ((mtst.st_mode&S_IFMT)!=S_IFCHR && (mtst.st_mode&S_IFMT)!=S_IFBLK ||
			Dflag) {
		if (action == 'o') {
			switch (err) {
			case 0:
				break;
			case EFBIG:
				msg(3, 0, "ulimit reached for output file.\n");
				break;
			case ENOSPC:
				msg(3, 0, "No space left for output file.\n");
				break;
			default:
				msg(3, 0, "I/O error - cannot continue, "
						"errno %d, %s\n",
						err, strerror(err));
			}
		}
		return;
	}
	if (err == ENOSPC || err == ENXIO || err == 0)
		msg(-2, 0, sysv3 ? "Reached end of medium on %s.\a\n" :
				"End of medium on \"%s\".\a\n", mesf);
	else
		msg(3, 0, "I/O error on \"%s\", errno %d, %s\a\n", mesf,
				err, strerror(err));
	mediacnt++;
	while (mfl < 0) {
		if (Iflag || Oflag)
			msg(-2, 0, Mflag ? Mflag :
					"Change to part %d and press "
					"RETURN key. [q] ", mediacnt);
		else
			msg(-2, 0, sysv3 ? "If you want to go on, "
					"type device/file name when ready.\n" :
					"To continue, type device/file name "
					"when ready.\n");
		if (tty == 0)
			if ((tty = open("/dev/tty", O_RDWR)) < 0 ||
					fcntl(tty, F_SETFD, FD_CLOEXEC) < 0) {
			cantrt:	errcnt++;
				msg(4, 1, "Cannot read tty.\n");
			}
		i = 0;
		while ((j = read(tty, &c, 1)) == 1 && c != '\n')
			if (i < sizeof answer - 1)
				answer[i++] = c;
		if (j != 1)
			goto cantrt;
		answer[i] = 0;
		if (Iflag || Oflag) {
			if (answer[0] == '\0')
				snprintf(answer, sizeof answer, Iflag ? Iflag :
						Oflag);
			else if (answer[0] == 'q')
				exit(errcnt != 0 ? sysv3 ? 1 : 2 : 0);
			else if (Iflag)
				Iflag = sstrdup(answer);
			else if (Oflag)
				Oflag = sstrdup(answer);
		} else if (answer[0] == '\0')
			return;
		if ((mt = action == 'i' ? open(answer, O_RDONLY) :
					creat(answer, 0666)) < 0) {
			if (sysv3)
				msg(-2, 0, "That didn't work, "
						"cannot open \"%s\"\n%s\n",
						answer, strerror(errno));
			else
				emsg(3, "Cannot open \"%s\"", answer);
		}
		else
			mfl = 0;
	}
	mstat();
}

static void
mclose(void)
{
	if (action == 'o' && mt >= 0 && close(mt) < 0) {
		emsg(3, "Close error on \"%s\"",
				Oflag && !sysv3 ? Oflag : "output");
		errcnt++;
	}
}

/*
 * Write the archive buffer to tape.
 */
static ssize_t
mwrite(int max)
{
	ssize_t wo, wt = 0;

	do {
		if ((wo = write(mt, blkbuf + wt, (max?max:blksiz) - wt)) < 0) {
			if (errno == EINTR) {
				continue;
			} else {
				newmedia(errno);
				if (mfl == 0) {
					if (fmttype & TYP_BAR)
						dump_barhdr();
					continue;
				}
				else
					done(1);
			}
		}
		poffs += wo;
		wt += wo;
	} while (wt < (max?max:blksiz));
	blocks += wt >> 9;
	bytes += wt & 0777;
	return wt;
}

/*
 * Buffered writes to tape.
 */
static void
bwrite(const char *data, size_t sz)
{
	size_t di;

	nwritten += sz;
	while (curpos + sz > blksiz) {
		di = blksiz - curpos;
		sz -= di;
		memcpy(&blkbuf[curpos], data, di);
		mwrite(0);
		data += di;
		curpos = 0;
	}
	memcpy(&blkbuf[curpos], data, sz);
	curpos += sz;
}

/*
 * Flush the tape write buffer.
 */
static void
bflush(void)
{
	if (curpos) {
		memset(&blkbuf[curpos], 0, blksiz - curpos);
		mwrite(fmttype==FMT_ZIP && (mtst.st_mode&S_IFMT) == S_IFREG ?
			curpos : 0);
	}
	curpos = 0;
}

/*
 * CRC format checksum calculation with -i.
 */
static int
sum(int fd, const char *fn, struct stat *sp, char *tg)
{
	char	*buf;
	size_t	bufsize;
	uint32_t	size = sp->st_size, sum = 0;
	ssize_t	rd;
	char	c;

	getbuf(&buf, &bufsize, sp->st_blksize);
	/*
	 * Unfortunately, SVR4 cpio derivatives (as on Solaris 8 and
	 * UnixWare 2.1) compute the checksum