blob: c35cc61cb06ed8b25adc716d7960a6271d68f297 [file] [log] [blame]
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/mkdev.h>
#include <sys/wait.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
#include <locale.h>
#include <nl_types.h>
#include <langinfo.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#include <utime.h>
#include <stdlib.h>
#include <stdarg.h>
#include <widec.h>
#include <sys/mtio.h>
#include <libintl.h>
#include <sys/acl.h>
#include <strings.h>
#include <deflt.h>
#include <limits.h>
#include <iconv.h>
#include <assert.h>
#include <aclutils.h>
#if defined(__SunOS_5_6) || defined(__SunOS_5_7)
extern int defcntl();
#endif
#include <archives.h>
/* Trusted Extensions */
#include <zone.h>
#include <tsol/label.h>
#include <sys/tsol/label_macro.h>
/*
* Source compatibility
*/
/*
* These constants come from archives.h and sys/fcntl.h
* and were introduced by the extended attributes project
* in Solaris 9.
*/
#if !defined(O_XATTR)
#define AT_SYMLINK_NOFOLLOW 0x1000
#define AT_REMOVEDIR 0x1
#define AT_FDCWD 0xffd19553
#define _XATTR_HDRTYPE 'E'
static int attropen();
static int fstatat();
static int renameat();
static int unlinkat();
static int openat();
static int fchownat();
static int futimesat();
#endif
/*
* Compiling with -D_XPG4_2 gets this but produces other problems, so
* instead of including sys/time.h and compiling with -D_XPG4_2, I'm
* explicitly doing the declaration here.
*/
int utimes(const char *path, const struct timeval timeval_ptr[]);
#ifndef MINSIZE
#define MINSIZE 250
#endif
#define DEF_FILE "/etc/default/tar"
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
/* -DDEBUG ONLY for debugging */
#ifdef DEBUG
#undef DEBUG
#define DEBUG(a, b, c)\
(void) fprintf(stderr, "DEBUG - "), (void) fprintf(stderr, a, b, c)
#endif
#define TBLOCK 512 /* tape block size--should be universal */
#ifdef BSIZE
#define SYS_BLOCK BSIZE /* from sys/param.h: secondary block size */
#else /* BSIZE */
#define SYS_BLOCK 512 /* default if no BSIZE in param.h */
#endif /* BSIZE */
#define NBLOCK 20
#define NAMSIZ 100
#define PRESIZ 155
#define MAXNAM 256
#define MODEMASK 0777777 /* file creation mode mask */
#define POSIXMODES 07777 /* mask for POSIX mode bits */
#define MAXEXT 9 /* reasonable max # extents for a file */
#define EXTMIN 50 /* min blks left on floppy to split a file */
/* max value dblock.dbuf.efsize can store */
#define TAR_EFSIZE_MAX 0777777777
/*
* Symbols which specify the values at which the use of the 'E' function
* modifier is required to properly store a file.
*
* TAR_OFFSET_MAX - the largest file size we can archive
* OCTAL7CHAR - the limit for ustar gid, uid, dev
*/
#ifdef XHDR_DEBUG
/* tiny values which force the creation of extended header entries */
#define TAR_OFFSET_MAX 9
#define OCTAL7CHAR 2
#else
/* normal values */
#define TAR_OFFSET_MAX 077777777777ULL
#define OCTAL7CHAR 07777777
#endif
#define TBLOCKS(bytes) (((bytes) + TBLOCK - 1) / TBLOCK)
#define K(tblocks) ((tblocks+1)/2) /* tblocks to Kbytes for printing */
#define MAXLEV (PATH_MAX / 2)
#define LEV0 1
#define SYMLINK_LEV0 0
#define TRUE 1
#define FALSE 0
#define XATTR_FILE 1
#define NORMAL_FILE 0
#define PUT_AS_LINK 1
#define PUT_NOTAS_LINK 0
#if _FILE_OFFSET_BITS == 64
#define FMT_off_t "lld"
#define FMT_off_t_o "llo"
#define FMT_blkcnt_t "lld"
#else
#define FMT_off_t "ld"
#define FMT_off_t_o "lo"
#define FMT_blkcnt_t "ld"
#endif
/* ACL support */
static
struct sec_attr {
char attr_type;
char attr_len[7];
char attr_info[1];
} *attr;
/*
*
* Tar has been changed to support extended attributes.
*
* As part of this change tar now uses 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 tar 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 Format
*
* 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.
*
* 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 |
* | |
* --------------------------------
*
*/
/*
* xattrhead is a pointer to the xattr_hdr
*
* xattrp is a pointer to the xattr_buf structure
* which contains the "pathing" steps to get to attributes
*
* xattr_linkp is a pointer to another xattr_buf structure that is
* only used when an attribute is actually linked to another attribute
*
*/
static struct xattr_hdr *xattrhead;
static struct xattr_buf *xattrp;
static struct xattr_buf *xattr_linkp; /* pointer to link info, if any */
static char *xattraname; /* attribute name */
static char *xattr_linkaname; /* attribute attribute is linked to */
static char Hiddendir; /* are we processing hidden xattr dir */
static char xattrbadhead;
/* Was statically allocated tbuf[NBLOCK] */
static
union hblock {
char dummy[TBLOCK];
struct header {
char name[NAMSIZ]; /* If non-null prefix, path is */
/* <prefix>/<name>; otherwise */
/* <name> */
char mode[8];
char uid[8];
char gid[8];
char size[12]; /* size of this extent if file split */
char mtime[12];
char chksum[8];
char typeflag;
char linkname[NAMSIZ];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[PRESIZ]; /* Together with "name", the path of */
/* the file: <prefix>/<name> */
char extno; /* extent #, null if not split */
char extotal; /* total extents */
char efsize[10]; /* size of entire file */
} dbuf;
} dblock, *tbuf, xhdr_buf;
static
struct xtar_hdr {
uid_t x_uid, /* Uid of file */
x_gid; /* Gid of file */
major_t x_devmajor; /* Device major node */
minor_t x_devminor; /* Device minor node */
off_t x_filesz; /* Length of file */
char *x_uname, /* Pointer to name of user */
*x_gname, /* Pointer to gid of user */
*x_linkpath, /* Path for a hard/symbolic link */
*x_path; /* Path of file */
timestruc_t x_mtime; /* Seconds and nanoseconds */
} Xtarhdr;
static
struct gen_hdr {
ulong_t g_mode; /* Mode of file */
uid_t g_uid, /* Uid of file */
g_gid; /* Gid of file */
off_t g_filesz; /* Length of file */
time_t g_mtime; /* Modification time */
uint_t g_cksum; /* Checksum of file */
ulong_t g_devmajor, /* File system of file */
g_devminor; /* Major/minor of special files */
} Gen;
static
struct linkbuf {
ino_t inum;
dev_t devnum;
int count;
char pathname[MAXNAM+1]; /* added 1 for last NULL */
char attrname[MAXNAM+1];
struct linkbuf *nextp;
} *ihead;
/* see comments before build_table() */
#define TABLE_SIZE 512
struct file_list {
char *name; /* Name of file to {in,ex}clude */
struct file_list *next; /* Linked list */
};
static struct file_list *exclude_tbl[TABLE_SIZE],
*include_tbl[TABLE_SIZE];
static int append_secattr(char **, int *, int, char *, char);
static void write_ancillary(union hblock *, char *, int, char);
static void add_file_to_table(struct file_list *table[], char *str);
static void assert_string(char *s, char *msg);
static int istape(int fd, int type);
static void backtape(void);
static void build_table(struct file_list *table[], char *file);
static void check_prefix(char **namep, char **dirp, char **compp);
static void closevol(void);
static void copy(void *dst, void *src);
static int convtoreg(off_t);
static void delete_target(int fd, char *namep);
static void doDirTimes(char *name, timestruc_t modTime);
static void done(int n);
static void dorep(char *argv[]);
#ifdef _iBCS2
static void dotable(char *argv[], int cnt);
static void doxtract(char *argv[], int cnt);
#else
static void dotable(char *argv[]);
static void doxtract(char *argv[]);
#endif
static void fatal(char *format, ...);
static void vperror(int exit_status, char *fmt, ...);
static void flushtape(void);
static void getdir(void);
static void *getmem(size_t);
static void longt(struct stat *st, char aclchar);
static int makeDir(char *name);
static void mterr(char *operation, int i, int exitcode);
static void newvol(void);
static void passtape(void);
static void putempty(blkcnt_t n);
static int putfile(char *longname, char *shortname, char *parent,
int filetype, int lev, int symlink_lev);
static void readtape(char *buffer);
static void seekdisk(blkcnt_t blocks);
static void setPathTimes(int dirfd, char *path, timestruc_t modTime);
static void splitfile(char *longname, int ifd, char *name,
char *prefix, int filetype);
static void tomodes(struct stat *sp);
static void usage(void);
static void xblocks(off_t bytes, int ofile);
static void xsfile(int ofd);
static void resugname(int dirfd, char *name, int symflag);
static int bcheck(char *bstr);
static int checkdir(char *name);
static int checksum(union hblock *dblockp);
#ifdef EUC
static int checksum_signed(union hblock *dblockp);
#endif /* EUC */
static int checkupdate(char *arg);
static int checkw(char c, char *name);
static int cmp(char *b, char *s, int n);
static int defset(char *arch);
static int endtape(void);
static int is_in_table(struct file_list *table[], char *str);
static int notsame(void);
static int is_prefix(char *s1, char *s2);
static int response(void);
static int build_dblock(const char *, const char *, const char,
const int filetype, const struct stat *, const dev_t, const char *);
static wchar_t yesnoresponse(void);
static unsigned int hash(char *str);
#ifdef _iBCS2
static void initarg(char *argv[], char *file);
static char *nextarg();
#endif
static blkcnt_t kcheck(char *kstr);
static off_t bsrch(char *s, int n, off_t l, off_t h);
static void onintr(int sig);
static void onquit(int sig);
static void onhup(int sig);
static uid_t getuidbyname(char *);
static gid_t getgidbyname(char *);
static char *getname(gid_t);
static char *getgroup(gid_t);
static int checkf(char *name, int mode, int howmuch);
static int writetbuf(char *buffer, int n);
static int wantit(char *argv[], char **namep, char **dirp, char **comp);
static void append_ext_attr(char *shortname, char **secinfo, int *len);
static int get_xdata(void);
static void gen_num(const char *keyword, const u_longlong_t number);
static void gen_date(const char *keyword, const timestruc_t time_value);
static void gen_string(const char *keyword, const char *value);
static void get_xtime(char *value, timestruc_t *xtime);
static int chk_path_build(char *name, char *longname, char *linkname,
char *prefix, char type, int filetype);
static int gen_utf8_names(const char *filename);
static int utf8_local(char *option, char **Xhdr_ptrptr, char *target,
const char *src, int max_val);
static int local_utf8(char **Xhdr_ptrptr, char *target, const char *src,
iconv_t iconv_cd, int xhdrflg, int max_val);
static int c_utf8(char *target, const char *source);
static int getstat(int dirfd, char *longname, char *shortname);
static void xattrs_put(char *, char *, char *);
static void prepare_xattr(char **, char *, char *,
char, struct linkbuf *, int *);
static int put_link(char *name, char *longname, char *component, char *prefix,
int filetype, char typeflag);
static int put_extra_attributes(char *longname, char *shortname,
char *prefix, int filetype, char typeflag);
static int put_xattr_hdr(char *longname, char *shortname, char *prefix,
int typeflag, int filetype, struct linkbuf *lp);
static int read_xattr_hdr();
/* Trusted Extensions */
#define AUTO_ZONE "/zone"
static void extract_attr(char **file_ptr, struct sec_attr *);
static int check_ext_attr(char *filename);
static void rebuild_comp_path(char *str, char **namep);
static int rebuild_lk_comp_path(char *str, char **namep);
static void get_parent(char *path, char *dir);
static char *get_component(char *path);
static int retry_attrdir_open(char *name);
static char *skipslashes(char *string, char *start);
static void chop_endslashes(char *path);
static struct stat stbuf;
static int checkflag = 0;
#ifdef _iBCS2
static int Fileflag;
char *sysv3_env;
#endif
static int Xflag, Fflag, iflag, hflag, Bflag, Iflag;
static int rflag, xflag, vflag, tflag, mt, cflag, mflag, pflag;
static int uflag;
static int eflag, errflag, qflag;
static int oflag;
static int bflag, kflag, Aflag;
static int Pflag; /* POSIX conformant archive */
static int Eflag; /* Allow files greater than 8GB */
static int atflag; /* traverse extended attributes */
static int Dflag; /* Data change flag */
/* Trusted Extensions */
static int Tflag; /* Trusted Extensions attr flags */
static int dir_flag; /* for attribute extract */
static int mld_flag; /* for attribute extract */
static char *orig_namep; /* original namep - unadorned */
static int rpath_flag; /* MLD real path is rebuilt */
static char real_path[MAXPATHLEN]; /* MLD real path */
static int lk_rpath_flag; /* linked to real path is rebuilt */
static char lk_real_path[MAXPATHLEN]; /* linked real path */
static bslabel_t bs_label; /* for attribute extract */
static bslabel_t admin_low;
static bslabel_t admin_high;
static int ignored_aprivs = 0;
static int ignored_fprivs = 0;
static int ignored_fattrs = 0;
static int term, chksum, wflag,
first = TRUE, defaults_used = FALSE, linkerrok;
static blkcnt_t recno;
static int freemem = 1;
static int nblock = NBLOCK;
static int Errflg = 0;
static int exitflag = 0;
static dev_t mt_dev; /* device containing output file */
static ino_t mt_ino; /* inode number of output file */
static int mt_devtype; /* dev type of archive, from stat structure */
static int update = 1; /* for `open' call */
static off_t low;
static off_t high;
static FILE *tfile;
static FILE *vfile = stdout;
static char tname[] = "/tmp/tarXXXXXX";
static char archive[] = "archive0=";
static char *Xfile;
static char *usefile;
static char *Filefile;
static int mulvol; /* multi-volume option selected */
static blkcnt_t blocklim; /* number of blocks to accept per volume */
static blkcnt_t tapepos; /* current block number to be written */
static int NotTape; /* true if tape is a disk */
static int dumping; /* true if writing a tape or other archive */
static int extno; /* number of extent: starts at 1 */
static int extotal; /* total extents in this file */
static off_t extsize; /* size of current extent during extraction */
static ushort_t Oumask = 0; /* old umask value */
static int is_posix; /* true if archive we're reading is POSIX-conformant */
static const char *magic_type = "ustar";
static size_t xrec_size = 8 * PATH_MAX; /* extended rec initial size */
static char *xrec_ptr;
static off_t xrec_offset = 0;
static int Xhdrflag;
static int charset_type = 0;
static u_longlong_t xhdr_flgs; /* Bits set determine which items */
/* need to be in extended header. */
#define _X_DEVMAJOR 0x1
#define _X_DEVMINOR 0x2
#define _X_GID 0x4
#define _X_GNAME 0x8
#define _X_LINKPATH 0x10
#define _X_PATH 0x20
#define _X_SIZE 0x40
#define _X_UID 0x80
#define _X_UNAME 0x100
#define _X_ATIME 0x200
#define _X_CTIME 0x400
#define _X_MTIME 0x800
#define _X_LAST 0x40000000
#define PID_MAX_DIGITS (10 * sizeof (pid_t) / 4)
#define TIME_MAX_DIGITS (10 * sizeof (time_t) / 4)
#define LONG_MAX_DIGITS (10 * sizeof (long) / 4)
#define ULONGLONG_MAX_DIGITS (10 * sizeof (u_longlong_t) / 4)
/*
* UTF_8 encoding requires more space than the current codeset equivalent.
* Currently a factor of 2-3 would suffice, but it is possible for a factor
* of 6 to be needed in the future, so for saftey, we use that here.
*/
#define UTF_8_FACTOR 6
static u_longlong_t xhdr_count = 0;
static char xhdr_dirname[PRESIZ + 1];
static char pidchars[PID_MAX_DIGITS + 1];
static char *tchar = ""; /* null linkpath */
static char local_path[UTF_8_FACTOR * PATH_MAX + 1];
static char local_linkpath[UTF_8_FACTOR * PATH_MAX + 1];
static char local_gname[UTF_8_FACTOR * _POSIX_NAME_MAX + 1];
static char local_uname[UTF_8_FACTOR * _POSIX_NAME_MAX + 1];
/*
* The following mechanism is provided to allow us to debug tar 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' function modifier to the
* target tar invocation, eg. "tar czf tarfile file". 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
int
main(int argc, char *argv[])
{
char *cp;
char *tmpdirp;
pid_t thispid;
#ifdef _iBCS2
int tbl_cnt = 0;
sysv3_env = getenv("SYSV3");
#endif
(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);
if (argc < 2)
usage();
tfile = NULL;
/*
* For XPG4 compatibility, we must be able to accept the "--"
* argument normally recognized by getopt; it is used to delimit
* the end opt the options section, and so can only appear in
* the position of the first argument. We simply skip it.
*/
if (strcmp(argv[1], "--") == 0) {
argv++;
argc--;
if (argc < 3)
usage();
}
argv[argc] = NULL;
argv++;
/*
* Set up default values.
* Search the operand string looking for the first digit or an 'f'.
* If you find a digit, use the 'archive#' entry in DEF_FILE.
* If 'f' is given, bypass looking in DEF_FILE altogether.
* If no digit or 'f' is given, still look in DEF_FILE but use '0'.
*/
if ((usefile = getenv("TAPE")) == (char *)NULL) {
for (cp = *argv; *cp; ++cp)
if (isdigit(*cp) || *cp == 'f')
break;
if (*cp != 'f') {
archive[7] = (*cp)? *cp: '0';
if (!(defaults_used = defset(archive))) {
usefile = NULL;
nblock = 1;
blocklim = 0;
NotTape = 0;
}
}
}
for (cp = *argv++; *cp; cp++)
switch (*cp) {
#ifdef WAITAROUND
case 'z':
/* rendezvous with the debugger */
waitaround = 1;
break;
#endif
case 'f':
assert_string(*argv, gettext(
"tar: tarfile must be specified with 'f' "
"function modifier\n"));
usefile = *argv++;
break;
case 'F':
#ifdef _iBCS2
if (sysv3_env) {
assert_string(*argv, gettext(
"tar: 'F' requires a file name\n"));
Filefile = *argv++;
Fileflag++;
} else
#endif /* _iBCS2 */
Fflag++;
break;
case 'c':
cflag++;
rflag++;
update = 1;
break;
#if defined(O_XATTR)
case '@':
atflag++;
break;
#endif
case 'u':
uflag++; /* moved code after signals caught */
rflag++;
update = 2;
break;
case 'r':
rflag++;
update = 2;
break;
case 'v':
vflag++;
break;
case 'w':
wflag++;
break;
case 'x':
xflag++;
break;
case 'X':
assert_string(*argv, gettext(
"tar: exclude file must be specified with 'X' "
"function modifier\n"));
Xflag = 1;
Xfile = *argv++;
build_table(exclude_tbl, Xfile);
break;
case 't':
tflag++;
break;
case 'm':
mflag++;
break;
case 'p':
pflag++;
break;
case 'D':
Dflag++;
break;
case '-':
/* ignore this silently */
break;
case '0': /* numeric entries used only for defaults */
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
break;
case 'b':
assert_string(*argv, gettext(
"tar: blocking factor must be specified "
"with 'b' function modifier\n"));
bflag++;
nblock = bcheck(*argv++);
break;
case 'q':
qflag++;
break;
case 'k':
assert_string(*argv, gettext(
"tar: size value must be specified with 'k' "
"function modifier\n"));
kflag++;
blocklim = kcheck(*argv++);
break;
case 'n': /* not a magtape (instead of 'k') */
NotTape++; /* assume non-magtape */
break;
case 'l':
linkerrok++;
break;
case 'e':
#ifdef _iBCS2
/* If sysv3 IS set, don't be as verbose */
if (!sysv3_env)
#endif /* _iBCS2 */
errflag++;
eflag++;
break;
case 'o':
oflag++;
break;
case 'h':
hflag++;
break;
case 'i':
iflag++;
break;
case 'B':
Bflag++;
break;
case 'P':
Pflag++;
break;
case 'E':
Eflag++;
Pflag++; /* Only POSIX archive made */
break;
case 'T':
Tflag++; /* Handle Trusted Extensions attrs */
pflag++; /* also set flag for ACL */
break;
default:
(void) fprintf(stderr, gettext(
"tar: %c: unknown function modifier\n"), *cp);
usage();
}
#ifdef _iBCS2
if (Xflag && Fileflag) {
(void) fprintf(stderr, gettext(
"tar: specify only one of X or F.\n"));
usage();
}
#endif /* _iBCS2 */
if (!rflag && !xflag && !tflag)
usage();
if ((rflag && xflag) || (xflag && tflag) || (rflag && tflag)) {
(void) fprintf(stderr, gettext(
"tar: specify only one of [ctxru].\n"));
usage();
}
/* Trusted Extensions attribute handling */
if (Tflag && ((getzoneid() != GLOBAL_ZONEID) ||
!is_system_labeled())) {
(void) fprintf(stderr, gettext(
"tar: the 'T' option is only available with "
"Trusted Extensions\nand must be run from "
"the global zone.\n"));
usage();
}
if (cflag && *argv == NULL && Filefile == NULL)
fatal(gettext("Missing filenames"));
if (usefile == NULL)
fatal(gettext("device argument required"));
/* alloc a buffer of the right size */
if ((tbuf = (union hblock *)
calloc(sizeof (union hblock) * nblock, sizeof (char))) ==
(union hblock *)NULL) {
(void) fprintf(stderr, gettext(
"tar: cannot allocate physio buffer\n"));
exit(1);
}
if ((xrec_ptr = malloc(xrec_size)) == NULL) {
(void) fprintf(stderr, gettext(
"tar: cannot allocate extended header buffer\n"));
exit(1);
}
#ifdef WAITAROUND
if (waitaround) {
(void) fprintf(stderr, gettext("Rendezvous with tar on pid"
" %d\n"), getpid());
while (waitaround) {
(void) sleep(10);
}
}
#endif
thispid = getpid();
(void) sprintf(pidchars, "%ld", thispid);
thispid = strlen(pidchars);
if ((tmpdirp = getenv("TMPDIR")) == (char *)NULL)
(void) strcpy(xhdr_dirname, "/tmp");
else {
/*
* Make sure that dir is no longer than what can
* fit in the prefix part of the header.
*/
if (strlen(tmpdirp) > (size_t)(PRESIZ - thispid - 12)) {
(void) strcpy(xhdr_dirname, "/tmp");
if ((vflag > 0) && (Eflag > 0))
(void) fprintf(stderr, gettext(
"Ignoring TMPDIR\n"));
} else
(void) strcpy(xhdr_dirname, tmpdirp);
}
(void) strcat(xhdr_dirname, "/PaxHeaders.");
(void) strcat(xhdr_dirname, pidchars);
if (rflag) {
if (cflag && tfile != NULL)
usage();
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
(void) signal(SIGINT, onintr);
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
(void) signal(SIGHUP, onhup);
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
(void) signal(SIGQUIT, onquit);
if (uflag) {
int tnum;
if ((tnum = mkstemp(tname)) == -1)
vperror(1, "%s", tname);
if ((tfile = fdopen(tnum, "w")) == NULL)
vperror(1, "%s", tname);
}
if (strcmp(usefile, "-") == 0) {
if (cflag == 0)
fatal(gettext(
"can only create standard output archives."));
vfile = stderr;
mt = dup(1);
++bflag;
} else {
if (cflag)
mt = open(usefile,
O_RDWR|O_CREAT|O_TRUNC, 0666);
else
mt = open(usefile, O_RDWR);
if (mt < 0) {
if (cflag == 0 || (mt = creat(usefile, 0666))
< 0)
vperror(1, "%s", usefile);
}
}
/* Get inode and device number of output file */
(void) fstat(mt, &stbuf);
mt_ino = stbuf.st_ino;
mt_dev = stbuf.st_dev;
mt_devtype = stbuf.st_mode & S_IFMT;
NotTape = !istape(mt, mt_devtype);
if (rflag && !cflag && (mt_devtype == S_IFIFO))
fatal(gettext("cannot append to pipe or FIFO."));
if (Aflag && vflag)
(void) printf(
gettext("Suppressing absolute pathnames\n"));
dorep(argv);
} else if (xflag || tflag) {
/*
* for each argument, check to see if there is a "-I file" pair.
* if so, move the 3rd argument into "-I"'s place, build_table()
* using "file"'s name and increment argc one (the second
* increment appears in the for loop) which removes the two
* args "-I" and "file" from the argument vector.
*/
for (argc = 0; argv[argc]; argc++) {
if (strcmp(argv[argc], "-I") == 0) {
if (!argv[argc+1]) {
(void) fprintf(stderr, gettext(
"tar: missing argument for -I flag\n"));
done(2);
} else {
Iflag = 1;
argv[argc] = argv[argc+2];
build_table(include_tbl, argv[++argc]);
#ifdef _iBCS2
if (Fileflag) {
(void) fprintf(stderr, gettext(
"tar: only one of I or F.\n"));
usage();
}
#endif /* _iBCS2 */
}
}
}
if (strcmp(usefile, "-") == 0) {
mt = dup(0);
++bflag;
/* try to recover from short reads when reading stdin */
++Bflag;
} else if ((mt = open(usefile, 0)) < 0)
vperror(1, "%s", usefile);
if (xflag) {
if (Aflag && vflag)
(void) printf(gettext(
"Suppressing absolute pathnames.\n"));
#ifdef _iBCS2
doxtract(argv, tbl_cnt);
#else
doxtract(argv);
#endif
} else if (tflag)
#ifdef _iBCS2
dotable(argv, tbl_cnt);
#else
dotable(argv);
#endif
}
else
usage();
done(Errflg);
/* Not reached: keep compiler quiet */
return (1);
}
static void
usage(void)
{
#ifdef _iBCS2
if (sysv3_env) {
(void) fprintf(stderr, gettext(
#if defined(O_XATTR)
"Usage: tar {c|r|t|u|x}[BDeEhilmnopPqTvw@[0-7]][bfFk][X...] "
#else
"Usage: tar {c|r|t|u|x}[BDeEhilmnopPqTvw[0-7]][bfFk][X...] "
#endif
"[blocksize] [tarfile] [filename] [size] [exclude-file...] "
"{file | -I include-file | -C directory file}...\n"));
} else
#endif /* _iBCS2 */
{
(void) fprintf(stderr, gettext(
#if defined(O_XATTR)
"Usage: tar {c|r|t|u|x}[BDeEFhilmnopPqTvw@[0-7]][bfk][X...] "
#else
"Usage: tar {c|r|t|u|x}[BDeEFhilmnopPqTvw[0-7]][bfk][X...] "
#endif
"[blocksize] [tarfile] [size] [exclude-file...] "
"{file | -I include-file | -C directory file}...\n"));
}
done(1);
}
/*
* dorep - do "replacements"
*
* Dorep is responsible for creating ('c'), appending ('r')
* and updating ('u');
*/
static void
dorep(char *argv[])
{
char *cp, *cp2, *p;
char wdir[PATH_MAX+2], tempdir[PATH_MAX+2], *parent;
char file[PATH_MAX*2], origdir[PATH_MAX+1];
FILE *fp = (FILE *)NULL;
FILE *ff = (FILE *)NULL;
int archtype;
if (!cflag) {
xhdr_flgs = 0;
getdir(); /* read header for next file */
if (Xhdrflag > 0) {
if (!Eflag)
fatal(gettext("Archive contains extended"
" header. -E flag required.\n"));
(void) get_xdata(); /* Get extended header items */
/* and regular header */
} else {
if (Eflag)
fatal(gettext("Archive contains no extended"
" header. -E flag not allowed.\n"));
}
while (!endtape()) { /* changed from a do while */
passtape(); /* skip the file data */
if (term)
done(Errflg); /* received signal to stop */
xhdr_flgs = 0;
getdir();
if (Xhdrflag > 0)
(void) get_xdata();
}
backtape(); /* was called by endtape */
if (tfile != NULL) {
char buf[200];
(void) sprintf(buf, "sort +0 -1 +1nr %s -o %s; awk '$1 "
"!= prev {print; prev=$1}' %s >%sX;mv %sX %s",
tname, tname, tname, tname, tname, tname);
(void) fflush(tfile);
(void) system(buf);
(void) freopen(tname, "r", tfile);
(void) fstat(fileno(tfile), &stbuf);
high = stbuf.st_size;
}
}
dumping = 1;
if (mulvol) { /* SP-1 */
if (nblock && (blocklim%nblock) != 0)
fatal(gettext(
"Volume size not a multiple of block size."));
blocklim -= 2; /* for trailer records */
if (vflag)
(void) fprintf(vfile, gettext("Volume ends at %"
FMT_blkcnt_t "K, blocking factor = %dK\n"),
K((blocklim - 1)), K(nblock));
}
#ifdef _iBCS2
if (Fileflag) {
if (Filefile != NULL) {
if ((ff = fopen(Filefile, "r")) == NULL)
vperror(0, "%s", Filefile);
} else {
(void) fprintf(stderr, gettext(
"tar: F requires a file name.\n"));
usage();
}
}
#endif /* _iBCS2 */
/*
* Save the original directory before it gets
* changed.
*/
if (getcwd(origdir, (PATH_MAX+1)) == NULL) {
vperror(0, gettext("A parent directory cannot be read"));
exit(1);
}
(void) strcpy(wdir, origdir);
while ((*argv || fp || ff) && !term) {
if (fp || (strcmp(*argv, "-I") == 0)) {
#ifdef _iBCS2
if (Fileflag) {
(void) fprintf(stderr, gettext(
"tar: only one of I or F.\n"));
usage();
}
#endif /* _iBCS2 */
if (fp == NULL) {
if (*++argv == NULL)
fatal(gettext(
"missing file name for -I flag."));
else if ((fp = fopen(*argv++, "r")) == NULL)
vperror(0, "%s", argv[-1]);
continue;
} else if ((fgets(file, PATH_MAX-1, fp)) == NULL) {
(void) fclose(fp);
fp = NULL;
continue;
} else {
cp = cp2 = file;
if ((p = strchr(cp2, '\n')))
*p = 0;
}
} else if ((strcmp(*argv, "-C") == 0) && argv[1]) {
#ifdef _iBCS2
if (Fileflag) {
(void) fprintf(stderr, gettext(
"tar: only one of F or C\n"));
usage();
}
#endif /* _iBCS2 */
if (chdir(*++argv) < 0)
vperror(0, gettext(
"can't change directories to %s"), *argv);
else
(void) getcwd(wdir, (sizeof (wdir)));
argv++;
continue;
#ifdef _iBCS2
} else if (Fileflag && (ff != NULL)) {
if ((fgets(file, PATH_MAX-1, ff)) == NULL) {
(void) fclose(ff);
ff = NULL;
continue;
} else {
cp = cp2 = file;
if (p = strchr(cp2, '\n'))
*p = 0;
}
#endif /* _iBCS2 */
} else
cp = cp2 = strcpy(file, *argv++);
/*
* point cp2 to the last '/' in file, but not
* to a trailing '/'
*/
for (; *cp; cp++) {
if (*cp == '/') {
while (*(cp+1) == '/') {
++cp;
}
if (*(cp+1) != '\0') {
/* not trailing slash */
cp2 = cp;
}
}
}
if (cp2 != file) {
*cp2 = '\0';
if (chdir(file) < 0) {
vperror(0, gettext(
"can't change directories to %s"), file);
continue;
}
*cp2 = '/';
cp2++;
}
parent = getcwd(tempdir, (sizeof (tempdir)));
archtype = putfile(file, cp2, parent, NORMAL_FILE,
LEV0, SYMLINK_LEV0);
#if defined(O_XATTR)
if (!exitflag) {
if (atflag && archtype == PUT_NOTAS_LINK) {
xattrs_put(file, cp2, parent);
}
}
#endif
if (chdir(origdir) < 0)
vperror(0, gettext("cannot change back?: %s"), origdir);
if (exitflag) {
/*
* If e function modifier has been specified
* write the files (that are listed before the
* file causing the error) to tape. exitflag is
* used because only some of the error conditions
* in putfile() recognize the e function modifier.
*/
break;
}
}
putempty((blkcnt_t)2);
flushtape();
closevol(); /* SP-1 */
if (linkerrok == 1)
for (; ihead != NULL; ihead = ihead->nextp) {
if (ihead->count == 0)
continue;
(void) fprintf(stderr, gettext(
"tar: missing links to %s\n"), ihead->pathname);
if (errflag)
done(1);
else
Errflg = 1;
}
}
/*
* endtape - check for tape at end
*
* endtape checks the entry in dblock.dbuf to see if its the
* special EOT entry. Endtape is usually called after getdir().
*
* endtape used to call backtape; it no longer does, he who
* wants it backed up must call backtape himself
* RETURNS: 0 if not EOT, tape position unaffected
* 1 if EOT, tape position unaffected
*/
static int
endtape(void)
{
if (dblock.dbuf.name[0] == '\0') { /* null header = EOT */
return (1);
} else
return (0);
}
/*
* getdir - get directory entry from tar tape
*
* getdir reads the next tarblock off the tape and cracks
* it as a directory. The checksum must match properly.
*
* If tfile is non-null getdir writes the file name and mod date
* to tfile.
*/
static void
getdir(void)
{
struct stat *sp;
#ifdef EUC
static int warn_chksum_sign = 0;
#endif /* EUC */
top:
readtape((char *)&dblock);
if (dblock.dbuf.name[0] == '\0')
return;
sp = &stbuf;
(void) sscanf(dblock.dbuf.mode, "%8lo", &Gen.g_mode);
(void) sscanf(dblock.dbuf.uid, "%8lo", (ulong_t *)&Gen.g_uid);
(void) sscanf(dblock.dbuf.gid, "%8lo", (ulong_t *)&Gen.g_gid);
(void) sscanf(dblock.dbuf.size, "%12" FMT_off_t_o, &Gen.g_filesz);
(void) sscanf(dblock.dbuf.mtime, "%12lo", (ulong_t *)&Gen.g_mtime);
(void) sscanf(dblock.dbuf.chksum, "%8o", &Gen.g_cksum);
(void) sscanf(dblock.dbuf.devmajor, "%8lo", &Gen.g_devmajor);
(void) sscanf(dblock.dbuf.devminor, "%8lo", &Gen.g_devminor);
is_posix = (strcmp(dblock.dbuf.magic, magic_type) == 0);
sp->st_mode = Gen.g_mode;
if (is_posix && (sp->st_mode & S_IFMT) == 0)
switch (dblock.dbuf.typeflag) {
case '0': case 0: case _XATTR_HDRTYPE:
sp->st_mode |= S_IFREG;
break;
case '1': /* hard link */
break;
case '2':
sp->st_mode |= S_IFLNK;
break;
case '3':
sp->st_mode |= S_IFCHR;
break;
case '4':
sp->st_mode |= S_IFBLK;
break;
case '5':
sp->st_mode |= S_IFDIR;
break;
case '6':
sp->st_mode |= S_IFIFO;
break;
default:
if (convtoreg(Gen.g_filesz))
sp->st_mode |= S_IFREG;
break;
}
if (dblock.dbuf.typeflag == 'X')
Xhdrflag = 1; /* Currently processing extended header */
else
Xhdrflag = 0;
sp->st_uid = Gen.g_uid;
sp->st_gid = Gen.g_gid;
sp->st_size = Gen.g_filesz;
sp->st_mtime = Gen.g_mtime;
chksum = Gen.g_cksum;
if (dblock.dbuf.extno != '\0') { /* split file? */
extno = dblock.dbuf.extno;
extsize = Gen.g_filesz;
extotal = dblock.dbuf.extotal;
} else {
extno = 0; /* tell others file not split */
extsize = 0;
extotal = 0;
}
#ifdef EUC
if (chksum != checksum(&dblock)) {
if (chksum != checksum_signed(&dblock)) {
(void) fprintf(stderr, gettext(
"tar: directory checksum error\n"));
if (iflag)
goto top;
done(2);
} else {
if (! warn_chksum_sign) {
warn_chksum_sign = 1;
(void) fprintf(stderr, gettext(
"tar: warning: tar file made with signed checksum\n"));
}
}
}
#else
if (chksum != checksum(&dblock)) {
(void) fprintf(stderr, gettext(
"tar: directory checksum error\n"));
if (iflag)
goto top;
done(2);
}
#endif /* EUC */
if (tfile != NULL && Xhdrflag == 0) {
/*
* If an extended header is present, then time is available
* in nanoseconds in the extended header data, so set it.
* Otherwise, give an invalid value so that checkupdate will
* not test beyond seconds.
*/
if ((xhdr_flgs & _X_MTIME))
sp->st_mtim.tv_nsec = Xtarhdr.x_mtime.tv_nsec;
else
sp->st_mtim.tv_nsec = -1;
if (xhdr_flgs & _X_PATH)
(void) fprintf(tfile, "%s %10ld.%9.9ld\n",
Xtarhdr.x_path, sp->st_mtim.tv_sec,
sp->st_mtim.tv_nsec);
else
(void) fprintf(tfile, "%.*s %10ld.%9.9ld\n",
NAMSIZ, dblock.dbuf.name, sp->st_mtim.tv_sec,
sp->st_mtim.tv_nsec);
}
#if defined(O_XATTR)
Hiddendir = 0;
if (xattrp && dblock.dbuf.typeflag == _XATTR_HDRTYPE) {
if (xattrbadhead) {
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
} else {
if (xattraname[0] == '.' && xattraname[1] == '\0' &&
xattrp->h_typeflag == '5') {
Hiddendir = 1;
sp->st_mode =
(S_IFDIR | (sp->st_mode & POSIXMODES));
}
dblock.dbuf.typeflag = xattrp->h_typeflag;
}
}
#endif
}
/*
* passtape - skip over a file on the tape
*
* passtape skips over the next data file on the tape.
* The tape directory entry must be in dblock.dbuf. This
* routine just eats the number of blocks computed from the
* directory size entry; the tape must be (logically) positioned
* right after thee directory info.
*/
static void
passtape(void)
{
blkcnt_t blocks;
char buf[TBLOCK];
/*
* Types link(1), sym-link(2), char special(3), blk special(4),
* directory(5), and FIFO(6) do not have data blocks associated
* with them so just skip reading the data block.
*/
if (dblock.dbuf.typeflag == '1' || dblock.dbuf.typeflag == '2' ||
dblock.dbuf.typeflag == '3' || dblock.dbuf.typeflag == '4' ||
dblock.dbuf.typeflag == '5' || dblock.dbuf.typeflag == '6')
return;
blocks = TBLOCKS(stbuf.st_size);
/* if operating on disk, seek instead of reading */
if (NotTape)
seekdisk(blocks);
else
while (blocks-- > 0)
readtape(buf);
}
static int
putfile(char *longname, char *shortname, char *parent,
int filetype, int lev, int symlink_lev)
{
int infile = -1; /* deliberately invalid */
blkcnt_t blocks;
char buf[PATH_MAX + 2]; /* Add trailing slash and null */
char *bigbuf;
int maxread;
int hint; /* amount to write to get "in sync" */
char filetmp[PATH_MAX + 1];
char *cp;
char *name;
struct dirent *dp;
DIR *dirp;
int i;
long l;
int split;
int dirfd = -1;
int rc = PUT_NOTAS_LINK;
int archtype = 0;
char newparent[PATH_MAX + MAXNAMLEN + 1];
char *prefix = "";
char *tmpbuf;
char goodbuf[PRESIZ + 2];
char junkbuf[MAXNAM+1];
char *lastslash;
int j;
struct stat sbuf;
int readlink_max;
(void) memset(goodbuf, '\0', sizeof (goodbuf));
(void) memset(junkbuf, '\0', sizeof (junkbuf));
xhdr_flgs = 0;
if (filetype == XATTR_FILE) {
dirfd = attropen(get_component(longname), ".", O_RDONLY);
} else {
dirfd = open(".", O_RDONLY);
}
if (dirfd == -1) {
(void) fprintf(stderr, gettext(
"tar: unable to open%sdirectory %s\n"),
(filetype == XATTR_FILE) ? gettext(" attribute ") : " ",
(filetype == XATTR_FILE) ? longname : parent);
goto out;
}
if (filetype == XATTR_FILE) {
if (fchdir(dirfd) < 0) {
(void) fprintf(stderr, gettext(
"tar: unable to fchdir into attribute directory"
" of file %s\n"), longname);
goto out;
}
}
if (lev > MAXLEV) {
(void) fprintf(stderr,
gettext("tar: directory nesting too deep, %s not dumped\n"),
longname);
goto out;
}
if (getstat(dirfd, longname, shortname))
goto out;
if (hflag) {
/*
* Catch nesting where a file is a symlink to its directory.
*/
j = fstatat(dirfd, shortname, &sbuf, AT_SYMLINK_NOFOLLOW);
if (S_ISLNK(sbuf.st_mode)) {
if (symlink_lev++ >= MAXSYMLINKS) {
(void) fprintf(stderr, gettext(
"tar: %s: Number of symbolic links "
"encountered during path name traversal "
"exceeds MAXSYMLINKS\n"), longname);
Errflg = 1;
goto out;
}
}
}
/*
* Check if the input file is the same as the tar file we
* are creating
*/
if ((mt_ino == stbuf.st_ino) && (mt_dev == stbuf.st_dev)) {
(void) fprintf(stderr, gettext(
"tar: %s same as archive file\n"), longname);
Errflg = 1;
goto out;
}
/*
* Check size limit - we can't archive files that
* exceed TAR_OFFSET_MAX bytes because of header
* limitations. Exclude file types that set
* st_size to zero below because they take no
* archive space to represent contents.
*/
if ((stbuf.st_size > (off_t)TAR_OFFSET_MAX) &&
!S_ISDIR(stbuf.st_mode) &&
!S_ISCHR(stbuf.st_mode) &&
!S_ISBLK(stbuf.st_mode) &&
(Eflag == 0)) {
(void) fprintf(stderr, gettext(
"tar: %s too large to archive. "
"Use E function modifier.\n"), longname);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
if (tfile != NULL && checkupdate(longname) == 0) {
goto out;
}
if (checkw('r', longname) == 0) {
goto out;
}
if (Fflag && checkf(shortname, stbuf.st_mode, Fflag) == 0)
goto out;
if (Xflag) {
if (is_in_table(exclude_tbl, longname)) {
if (vflag) {
(void) fprintf(vfile, gettext(
"a %s excluded\n"), longname);
}
goto out;
}
}
/*
* If the length of the fullname is greater than MAXNAM,
* print out a message and return (unless extended headers are used,
* in which case fullname is limited to PATH_MAX).
*/
if ((((split = (int)strlen(longname)) > MAXNAM) && (Eflag == 0)) ||
(split > PATH_MAX)) {
(void) fprintf(stderr, gettext(
"tar: %s: file name too long\n"), longname);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
/*
* We split the fullname into prefix and name components if any one
* of three conditions holds:
* -- the length of the fullname exceeds NAMSIZ,
* -- the length of the fullname equals NAMSIZ, and the shortname
* is less than NAMSIZ, (splitting in this case preserves
* compatibility with 5.6 and 5.5.1 tar), or
* -- the length of the fullname equals NAMSIZ, the file is a
* directory and we are not in POSIX-conformant mode (where
* trailing slashes are removed from directories).
*/
if ((split > NAMSIZ) ||
(split == NAMSIZ && strlen(shortname) < NAMSIZ) ||
(split == NAMSIZ && S_ISDIR(stbuf.st_mode) && !Pflag)) {
/*
* Since path is limited to PRESIZ characters, look for the
* last slash within PRESIZ + 1 characters only.
*/
(void) strncpy(&goodbuf[0], longname, min(split, PRESIZ + 1));
tmpbuf = goodbuf;
lastslash = strrchr(tmpbuf, '/');
if (lastslash == NULL) {
i = split; /* Length of name */
j = 0; /* Length of prefix */
goodbuf[0] = '\0';
} else {
*lastslash = '\0'; /* Terminate the prefix */
j = strlen(tmpbuf);
i = split - j - 1;
}
/*
* If the filename is greater than NAMSIZ we can't
* archive the file unless we are using extended headers.
*/
if ((i > NAMSIZ) || (i == NAMSIZ && S_ISDIR(stbuf.st_mode) &&
!Pflag)) {
/* Determine which (filename or path) is too long. */
lastslash = strrchr(longname, '/');
if (lastslash != NULL)
i = strlen(lastslash + 1);
if (Eflag > 0) {
xhdr_flgs |= _X_PATH;
Xtarhdr.x_path = longname;
if (i <= NAMSIZ)
(void) strcpy(junkbuf, lastslash + 1);
else
(void) sprintf(junkbuf, "%llu",
xhdr_count + 1);
if (split - i - 1 > PRESIZ)
(void) strcpy(goodbuf, xhdr_dirname);
} else {
if ((i > NAMSIZ) || (i == NAMSIZ &&
S_ISDIR(stbuf.st_mode) && !Pflag))
(void) fprintf(stderr, gettext(
"tar: %s: filename is greater than "
"%d\n"), lastslash == NULL ?
longname : lastslash + 1, NAMSIZ);
else
(void) fprintf(stderr, gettext(
"tar: %s: prefix is greater than %d"
"\n"), longname, PRESIZ);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
} else
(void) strncpy(&junkbuf[0], longname + j + 1,
strlen(longname + j + 1));
name = junkbuf;
prefix = goodbuf;
} else {
name = longname;
}
if (Aflag) {
if ((prefix != NULL) && (*prefix != '\0'))
while (*prefix == '/')
++prefix;
else
while (*name == '/')
++name;
}
switch (stbuf.st_mode & S_IFMT) {
case S_IFDIR:
stbuf.st_size = (off_t)0;
blocks = TBLOCKS(stbuf.st_size);
if (filetype != XATTR_FILE && Hiddendir == 0) {
i = 0;
cp = buf;
while ((*cp++ = longname[i++]))
;
*--cp = '/';
*++cp = 0;
}
if (!oflag) {
tomodes(&stbuf);
if (build_dblock(name, tchar, '5', filetype,
&stbuf, stbuf.st_dev, prefix) != 0) {
goto out;
}
if (!Pflag) {
/*
* Old archives require a slash at the end
* of a directory name.
*
* XXX
* If directory name is too long, will
* slash overfill field?
*/
if (strlen(name) > (unsigned)NAMSIZ-1) {
(void) fprintf(stderr, gettext(
"tar: %s: filename is greater "
"than %d\n"), name, NAMSIZ);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
} else {
if (strlen(name) == (NAMSIZ - 1)) {
(void) memcpy(dblock.dbuf.name,
name, NAMSIZ);
dblock.dbuf.name[NAMSIZ-1]
= '/';
} else
(void) sprintf(dblock.dbuf.name,
"%s/", name);
/*
* need to recalculate checksum
* because the name changed.
*/
(void) sprintf(dblock.dbuf.chksum,
"%07o", checksum(&dblock));
}
}
if (put_extra_attributes(longname, shortname, prefix,
filetype, '5') != 0)
goto out;
#if defined(O_XATTR)
/*
* Reset header typeflag when archiving directory, since
* build_dblock changed it on us.
*/
if (filetype == XATTR_FILE) {
dblock.dbuf.typeflag = _XATTR_HDRTYPE;
} else {
dblock.dbuf.typeflag = '5';
}
#else
dblock.dbuf.typeflag = '5';
#endif
(void) sprintf(dblock.dbuf.chksum, "%07o",
checksum(&dblock));
(void) writetbuf((char *)&dblock, 1);
}
if (vflag) {
#ifdef DEBUG
if (NotTape)
DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
0);
#endif
if (filetype == XATTR_FILE && Hiddendir) {
(void) fprintf(vfile, "a %s attribute . ",
longname);
} else {
(void) fprintf(vfile, "a %s/ ", longname);
}
if (NotTape)
(void) fprintf(vfile, "%" FMT_blkcnt_t "K\n",
K(blocks));
else
(void) fprintf(vfile, gettext("%" FMT_blkcnt_t
" tape blocks\n"), blocks);
}
/*
* If hidden dir then break now since xattrs_put() will do
* the iterating of the directory.
*
* At the moment, there can't be attributes on attributes
* or directories within the attributes hidden directory
* hierarchy.
*/
if (filetype == XATTR_FILE)
break;
if (*shortname != '/')
(void) sprintf(newparent, "%s/%s", parent, shortname);
else
(void) sprintf(newparent, "%s", shortname);
if (chdir(shortname) < 0) {
vperror(0, "%s", newparent);
goto out;
}
if ((dirp = opendir(".")) == NULL) {
vperror(0, gettext(
"can't open directory %s"), longname);
if (chdir(parent) < 0)
vperror(0, gettext("cannot change back?: %s"),
parent);
goto out;
}
while ((dp = readdir(dirp)) != NULL && !term) {
if ((strcmp(".", dp->d_name) == 0) ||
(strcmp("..", dp->d_name) == 0))
continue;
(void) strcpy(cp, dp->d_name);
if (stat(dp->d_name, &sbuf) < 0 ||
(sbuf.st_mode & S_IFMT) == S_IFDIR) {
l = telldir(dirp);
(void) closedir(dirp);
} else
l = -1;
archtype = putfile(buf, cp, newparent,
NORMAL_FILE, lev + 1, symlink_lev);
if (!exitflag) {
if (atflag && archtype == PUT_NOTAS_LINK) {
xattrs_put(buf, cp, newparent);
}
}
if (exitflag)
break;
/*
* If the directory was not closed, then it does
* not need to be reopened.
*/
if (l < 0)
continue;
if ((dirp = opendir(".")) == NULL) {
vperror(0, gettext(
"can't open directory %s"), longname);
if (chdir(parent) < 0)
vperror(0,
gettext("cannot change back?: %s"),
parent);
goto out;
}
seekdir(dirp, l);
}
(void) closedir(dirp);
if (chdir(parent) < 0) {
vperror(0, gettext("cannot change back?: %s"), parent);
}
break;
case S_IFLNK:
readlink_max = NAMSIZ;
if (stbuf.st_size > NAMSIZ) {
if (Eflag > 0) {
xhdr_flgs |= _X_LINKPATH;
readlink_max = PATH_MAX;
} else {
(void) fprintf(stderr, gettext(
"tar: %s: symbolic link too long\n"),
longname);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
}
/*
* Sym-links need header size of zero since you
* don't store any data for this type.
*/
stbuf.st_size = (off_t)0;
tomodes(&stbuf);
i = readlink(shortname, filetmp, readlink_max);
if (i < 0) {
vperror(0, gettext(
"can't read symbolic link %s"), longname);
goto out;
} else {
filetmp[i] = 0;
}
if (vflag)
(void) fprintf(vfile, gettext(
"a %s symbolic link to %s\n"),
longname, filetmp);
if (xhdr_flgs & _X_LINKPATH) {
Xtarhdr.x_linkpath = filetmp;
if (build_dblock(name, tchar, '2', filetype, &stbuf,
stbuf.st_dev, prefix) != 0)
goto out;
} else
if (build_dblock(name, filetmp, '2', filetype, &stbuf,
stbuf.st_dev, prefix) != 0)
goto out;
(void) writetbuf((char *)&dblock, 1);
/*
* No acls for symlinks: mode is always 777
* dont call write ancillary
*/
rc = PUT_AS_LINK;
break;
case S_IFREG:
if ((infile = openat(dirfd, shortname, 0)) < 0) {
vperror(0, "%s%s%s", longname,
(filetype == XATTR_FILE) ?
gettext(" attribute ") : "",
(filetype == XATTR_FILE) ?
shortname : "");
goto out;
}
blocks = TBLOCKS(stbuf.st_size);
if (put_link(name, longname, shortname,
prefix, filetype, '1') == 0) {
(void) close(infile);
rc = PUT_AS_LINK;
goto out;
}
tomodes(&stbuf);
/* correctly handle end of volume */
while (mulvol && tapepos + blocks + 1 > blocklim) {
/* file won't fit */
if (eflag) {
if (blocks <= blocklim) {
newvol();
break;
}
(void) fprintf(stderr, gettext(
"tar: Single file cannot fit on volume\n"));
done(3);
}
/* split if floppy has some room and file is large */
if (((blocklim - tapepos) >= EXTMIN) &&
((blocks + 1) >= blocklim/10)) {
splitfile(longname, infile,
name, prefix, filetype);
(void) close(dirfd);
(void) close(infile);
goto out;
}
newvol(); /* not worth it--just get new volume */
}
#ifdef DEBUG
DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
#endif
if (build_dblock(name, tchar, '0', filetype,
&stbuf, stbuf.st_dev, prefix) != 0) {
goto out;
}
if (vflag) {
#ifdef DEBUG
if (NotTape)
DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
0);
#endif
(void) fprintf(vfile, "a %s%s%s ", longname,
(filetype == XATTR_FILE) ?
gettext(" attribute ") : "",
(filetype == XATTR_FILE) ?
shortname : "");
if (NotTape)
(void) fprintf(vfile, "%" FMT_blkcnt_t "K\n",
K(blocks));
else
(void) fprintf(vfile,
gettext("%" FMT_blkcnt_t " tape blocks\n"),
blocks);
}
if (put_extra_attributes(longname, shortname, prefix,
filetype, '0') != 0)
goto out;
/*
* No need to reset typeflag for extended attribute here, since
* put_extra_attributes already set it and we haven't called
* build_dblock().
*/
(void) sprintf(dblock.dbuf.chksum, "%07o", checksum(&dblock));
hint = writetbuf((char *)&dblock, 1);
maxread = max(min(stbuf.st_blksize, stbuf.st_size),
(nblock * TBLOCK));
if ((bigbuf = calloc((unsigned)maxread, sizeof (char))) == 0) {
maxread = TBLOCK;
bigbuf = buf;
}
while (((i = (int)
read(infile, bigbuf, min((hint*TBLOCK), maxread))) > 0) &&
blocks) {
blkcnt_t nblks;
nblks = ((i-1)/TBLOCK)+1;
if (nblks > blocks)
nblks = blocks;
hint = writetbuf(bigbuf, nblks);
blocks -= nblks;
}
(void) close(infile);
if (bigbuf != buf)
free(bigbuf);
if (i < 0)
vperror(0, gettext("Read error on %s"), longname);
else if (blocks != 0 || i != 0) {
(void) fprintf(stderr, gettext(
"tar: %s: file changed size\n"), longname);
if (errflag) {
exitflag = 1;
Errflg = 1;
} else if (!Dflag) {
Errflg = 1;
}
}
putempty(blocks);
break;
case S_IFIFO:
blocks = TBLOCKS(stbuf.st_size);
stbuf.st_size = (off_t)0;
if (put_link(name, longname, shortname,
prefix, filetype, '6') == 0) {
rc = PUT_AS_LINK;
goto out;
}
tomodes(&stbuf);
while (mulvol && tapepos + blocks + 1 > blocklim) {
if (eflag) {
if (blocks <= blocklim) {
newvol();
break;
}
(void) fprintf(stderr, gettext(
"tar: Single file cannot fit on volume\n"));
done(3);
}
if (((blocklim - tapepos) >= EXTMIN) &&
((blocks + 1) >= blocklim/10)) {
splitfile(longname, infile, name,
prefix, filetype);
(void) close(dirfd);
(void) close(infile);
goto out;
}
newvol();
}
#ifdef DEBUG
DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
#endif
if (vflag) {
#ifdef DEBUG
if (NotTape)
DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
0);
#endif
if (NotTape)
(void) fprintf(vfile, gettext("a %s %"
FMT_blkcnt_t "K\n "), longname, K(blocks));
else
(void) fprintf(vfile, gettext(
"a %s %" FMT_blkcnt_t " tape blocks\n"),
longname, blocks);
}
if (build_dblock(name, tchar, '6', filetype,
&stbuf, stbuf.st_dev, prefix) != 0)
goto out;
if (put_extra_attributes(longname, shortname, prefix,
filetype, '6') != 0)
goto out;
(void) sprintf(dblock.dbuf.chksum, "%07o", checksum(&dblock));
dblock.dbuf.typeflag = '6';
(void) writetbuf((char *)&dblock, 1);
break;
case S_IFCHR:
stbuf.st_size = (off_t)0;
blocks = TBLOCKS(stbuf.st_size);
if (put_link(name, longname,
shortname, prefix, filetype, '3') == 0) {
rc = PUT_AS_LINK;
goto out;
}
tomodes(&stbuf);
while (mulvol && tapepos + blocks + 1 > blocklim) {
if (eflag) {
if (blocks <= blocklim) {
newvol();
break;
}
(void) fprintf(stderr, gettext(
"tar: Single file cannot fit on volume\n"));
done(3);
}
if (((blocklim - tapepos) >= EXTMIN) &&
((blocks + 1) >= blocklim/10)) {
splitfile(longname, infile, name,
prefix, filetype);
(void) close(dirfd);
goto out;
}
newvol();
}
#ifdef DEBUG
DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
#endif
if (vflag) {
#ifdef DEBUG
if (NotTape)
DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
0);
#endif
if (NotTape)
(void) fprintf(vfile, gettext("a %s %"
FMT_blkcnt_t "K\n"), longname, K(blocks));
else
(void) fprintf(vfile, gettext("a %s %"
FMT_blkcnt_t " tape blocks\n"), longname,
blocks);
}
if (build_dblock(name, tchar, '3',
filetype, &stbuf, stbuf.st_rdev, prefix) != 0)
goto out;
if (put_extra_attributes(longname, shortname,
prefix, filetype, '3') != 0)
goto out;
(void) sprintf(dblock.dbuf.chksum, "%07o", checksum(&dblock));
dblock.dbuf.typeflag = '3';
(void) writetbuf((char *)&dblock, 1);
break;
case S_IFBLK:
stbuf.st_size = (off_t)0;
blocks = TBLOCKS(stbuf.st_size);
if (put_link(name, longname,
shortname, prefix, filetype, '4') == 0) {
rc = PUT_AS_LINK;
goto out;
}
tomodes(&stbuf);
while (mulvol && tapepos + blocks + 1 > blocklim) {
if (eflag) {
if (blocks <= blocklim) {
newvol();
break;
}
(void) fprintf(stderr, gettext(
"tar: Single file cannot fit on volume\n"));
done(3);
}
if (((blocklim - tapepos) >= EXTMIN) &&
((blocks + 1) >= blocklim/10)) {
splitfile(longname, infile,
name, prefix, filetype);
(void) close(dirfd);
goto out;
}
newvol();
}
#ifdef DEBUG
DEBUG("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
#endif
if (vflag) {
#ifdef DEBUG
if (NotTape)
DEBUG("seek = %" FMT_blkcnt_t "K\t", K(tapepos),
0);
#endif
(void) fprintf(vfile, "a %s ", longname);
if (NotTape)
(void) fprintf(vfile, "%" FMT_blkcnt_t "K\n",
K(blocks));
else
(void) fprintf(vfile, gettext("%"
FMT_blkcnt_t " tape blocks\n"), blocks);
}
if (build_dblock(name, tchar, '4',
filetype, &stbuf, stbuf.st_rdev, prefix) != 0)
goto out;
if (put_extra_attributes(longname, shortname,
prefix, filetype, '4') != 0)
goto out;
(void) sprintf(dblock.dbuf.chksum, "%07o", checksum(&dblock));
dblock.dbuf.typeflag = '4';
(void) writetbuf((char *)&dblock, 1);
break;
default:
(void) fprintf(stderr, gettext(
"tar: %s is not a file. Not dumped\n"), longname);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
out:
if (dirfd != -1) {
if (filetype == XATTR_FILE)
(void) chdir(parent);
(void) close(dirfd);
}
return (rc);
}
/*
* splitfile dump a large file across volumes
*
* splitfile(longname, fd);
* char *longname; full name of file
* int ifd; input file descriptor
*
* NOTE: only called by putfile() to dump a large file.
*/
static void
splitfile(char *longname, int ifd, char *name, char *prefix, int filetype)
{
blkcnt_t blocks;
off_t bytes, s;
char buf[TBLOCK];
int i, extents;
blocks = TBLOCKS(stbuf.st_size); /* blocks file needs */
/*
* # extents =
* size of file after using up rest of this floppy
* blocks - (blocklim - tapepos) + 1 (for header)
* plus roundup value before divide by blocklim-1
* + (blocklim - 1) - 1
* all divided by blocklim-1 (one block for each header).
* this gives
* (blocks - blocklim + tapepos + 1 + blocklim - 2)/(blocklim-1)
* which reduces to the expression used.
* one is added to account for this first extent.
*
* When one is dealing with extremely large archives, one may want
* to allow for a large number of extents. This code should be
* revisited to determine if extents should be changed to something
* larger than an int.
*/
extents = (int)((blocks + tapepos - 1ULL)/(blocklim - 1ULL) + 1);
if (extents < 2 || extents > MAXEXT) { /* let's be reasonable */
(void) fprintf(stderr, gettext(
"tar: %s needs unusual number of volumes to split\n"
"tar: %s not dumped\n"), longname, longname);
return;
}
if (build_dblock(name, tchar, '0', filetype,
&stbuf, stbuf.st_dev, prefix) != 0)
return;
dblock.dbuf.extotal = extents;
bytes = stbuf.st_size;
/*
* The value contained in dblock.dbuf.efsize was formerly used when the
* v flag was specified in conjunction with the t flag. Although it is
* no longer used, older versions of tar will expect the former
* behaviour, so we must continue to write it to the archive.
*
* Since dblock.dbuf.efsize is 10 chars in size, the maximum value it
* can store is TAR_EFSIZE_MAX. If bytes exceeds that value, simply
* store 0.
*/
if (bytes <= TAR_EFSIZE_MAX)
(void) sprintf(dblock.dbuf.efsize, "%9" FMT_off_t_o, bytes);
else
(void) sprintf(dblock.dbuf.efsize, "%9" FMT_off_t_o, (off_t)0);
(void) fprintf(stderr, gettext(
"tar: large file %s needs %d extents.\n"
"tar: current device seek position = %" FMT_blkcnt_t "K\n"),
longname, extents, K(tapepos));
s = (off_t)(blocklim - tapepos - 1) * TBLOCK;
for (i = 1; i <= extents; i++) {
if (i > 1) {
newvol();
if (i == extents)
s = bytes; /* last ext. gets true bytes */
else
s = (off_t)(blocklim - 1)*TBLOCK; /* all */
}
bytes -= s;
blocks = TBLOCKS(s);
(void) sprintf(dblock.dbuf.size, "%011" FMT_off_t_o, s);
dblock.dbuf.extno = i;
(void) sprintf(dblock.dbuf.chksum, "%07o", checksum(&dblock));
(void) writetbuf((char *)&dblock, 1);
if (vflag)
(void) fprintf(vfile,
"+++ a %s %" FMT_blkcnt_t "K [extent #%d of %d]\n",
longname, K(blocks), i, extents);
while (blocks && read(ifd, buf, TBLOCK) > 0) {
blocks--;
(void) writetbuf(buf, 1);
}
if (blocks != 0) {
(void) fprintf(stderr, gettext(
"tar: %s: file changed size\n"), longname);
(void) fprintf(stderr, gettext(
"tar: aborting split file %s\n"), longname);
(void) close(ifd);
return;
}
}
(void) close(ifd);
if (vflag)
(void) fprintf(vfile, gettext("a %s %" FMT_off_t "K (in %d "
"extents)\n"), longname, K(TBLOCKS(stbuf.st_size)),
extents);
}
/*
* convtoreg - determines whether the file should be converted to a
* regular file when extracted
*
* Returns 1 when file size > 0 and typeflag is not recognized
* Otherwise returns 0
*/
static int
convtoreg(off_t size)
{
if ((size > 0) && (dblock.dbuf.typeflag != '0') &&
(dblock.dbuf.typeflag != NULL) && (dblock.dbuf.typeflag != '1') &&
(dblock.dbuf.typeflag != '2') && (dblock.dbuf.typeflag != '3') &&
(dblock.dbuf.typeflag != '4') && (dblock.dbuf.typeflag != '5') &&
(dblock.dbuf.typeflag != '6') && (dblock.dbuf.typeflag != 'A') &&
(dblock.dbuf.typeflag != _XATTR_HDRTYPE) &&
(dblock.dbuf.typeflag != 'X')) {
return (1);
}
return (0);
}
static void
#ifdef _iBCS2
doxtract(char *argv[], int tbl_cnt)
#else
doxtract(char *argv[])
#endif
{
struct stat xtractbuf; /* stat on file after extracting */
blkcnt_t blocks;
off_t bytes;
int ofile;
int newfile; /* Does the file already exist */
int xcnt = 0; /* count # files extracted */
int fcnt = 0; /* count # files in argv list */
int dir;
int dirfd = -1;
uid_t Uid;
char *namep, *dirp, *comp, *linkp; /* for removing absolute paths */
char dirname[PATH_MAX+1];
char templink[PATH_MAX+1]; /* temp link with terminating NULL */
char origdir[PATH_MAX+1];
int once = 1;
int error;
int symflag;
int want;
acl_t *aclp = NULL; /* acl info */
char dot[] = "."; /* dirp for using realpath */
timestruc_t time_zero; /* used for call to doDirTimes */
int dircreate;
int convflag;
time_zero.tv_sec = 0;
time_zero.tv_nsec = 0;
/* reset Trusted Extensions variables */
rpath_flag = 0;
lk_rpath_flag = 0;
dir_flag = 0;
mld_flag = 0;
bslundef(&bs_label);
bsllow(&admin_low);
bslhigh(&admin_high);
orig_namep = 0;
dumping = 0; /* for newvol(), et al: we are not writing */
/*
* Count the number of files that are to be extracted
*/
Uid = getuid();
#ifdef _iBCS2
initarg(argv, Filefile);
while (nextarg() != NULL)
++fcnt;
fcnt += tbl_cnt;
#endif /* _iBCS2 */
for (;;) {
convflag = 0;
symflag = 0;
dir = 0;
ofile = -1;
/* namep is set by wantit to point to the full name */
if ((want = wantit(argv, &namep, &dirp, &comp)) == 0) {
#if defined(O_XATTR)
if (xattrp != (struct xattr_buf *)NULL) {
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
}
#endif
continue;
}
if (want == -1)
break;
/* Trusted Extensions */
/*
* During tar extract (x):
* If the pathname of the restored file has been
* reconstructed from the ancillary file,
* use it to process the normal file.
*/
if (mld_flag) { /* Skip over .MLD. directory */
mld_flag = 0;
passtape();
continue;
}
orig_namep = namep; /* save original */
if (rpath_flag) {
namep = real_path; /* use zone path */
comp = real_path; /* use zone path */
dirp = dot; /* work from the top */
rpath_flag = 0; /* reset */
}
if (dirfd != -1)
(void) close(dirfd);
(void) strcpy(&dirname[0], namep);
dircreate = checkdir(&dirname[0]);
#if defined(O_XATTR)
if (xattrp != (struct xattr_buf *)NULL) {
dirfd = attropen(dirp, ".", O_RDONLY);
} else {
dirfd = open(dirp, O_RDONLY);
}
#else
dirfd = open(dirp, O_RDONLY);
#endif
if (dirfd == -1) {
#if defined(O_XATTR)
if (xattrp) {
dirfd = retry_attrdir_open(dirp);
}
#endif
if (dirfd == -1) {
#if defined(O_XATTR)
if (xattrp) {
(void) fprintf(vfile,
gettext("tar: cannot open "
"attribute %s of file %s: %s\n"),
xattraname, dirp, strerror(errno));
/*
* Reset typeflag back to real
* value so passtape will skip
* ahead correctly.
*/
dblock.dbuf.typeflag = _XATTR_HDRTYPE;
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
} else {
(void) fprintf(vfile,
gettext("tar: cannot open %s %s\n"),
dirp, strerror(errno));
}
#else
(void) fprintf(vfile,
gettext("tar: cannot open %s %s\n"),
dirp, strerror(errno));
#endif
passtape();
continue;
}
}
if (xhdr_flgs & _X_LINKPATH)
(void) strcpy(templink, Xtarhdr.x_linkpath);
else {
#if defined(O_XATTR)
if (xattrp && dblock.dbuf.typeflag == '1') {
(void) sprintf(templink, "%.*s", NAMSIZ,
xattrp->h_names);
} else {
(void) sprintf(templink, "%.*s", NAMSIZ,
dblock.dbuf.linkname);
}
#else
(void) sprintf(templink, "%.*s", NAMSIZ,
dblock.dbuf.linkname);
#endif
}
if (Fflag) {
char *s;
if ((s = strrchr(namep, '/')) == 0)
s = namep;
else
s++;
if (checkf(s, stbuf.st_mode, Fflag) == 0) {
passtape();
continue;
}
}
if (checkw('x', namep) == 0) {
passtape();
continue;
}
if (once) {
if (strcmp(dblock.dbuf.magic, magic_type) == 0) {
if (geteuid() == (uid_t)0) {
checkflag = 1;
pflag = 1;
} else {
/* get file creation mask */
Oumask = umask(0);
(void) umask(Oumask);
}
once = 0;
} else {
if (geteuid() == (uid_t)0) {
pflag = 1;
checkflag = 2;
}
if (!pflag) {
/* get file creation mask */
Oumask = umask(0);
(void) umask(Oumask);
}
once = 0;
}
}
#if defined(O_XATTR)
/*
* Handle extraction of hidden attr dir.
* Dir is automatically created, we only
* need to update mode and perm's.
*/
if ((xattrp != (struct xattr_buf *)NULL) && Hiddendir == 1) {
if (fchownat(dirfd, ".", stbuf.st_uid,
stbuf.st_gid, 0) != 0) {
vperror(0, gettext(
"%s: failed to set ownership of attribute"
" directory"), namep);
}
if (fchmod(dirfd, stbuf.st_mode) != 0) {
vperror(0, gettext(
"%s: failed to set permissions of"
" attribute directory"), namep);
}
goto filedone;
}
#endif
if (dircreate && (!is_posix || dblock.dbuf.typeflag == '5')) {
dir = 1;
if (vflag) {
(void) fprintf(vfile, "x %s, 0 bytes, ",
&dirname[0]);
if (NotTape)
(void) fprintf(vfile, "0K\n");
else
(void) fprintf(vfile, gettext("%"
FMT_blkcnt_t " tape blocks\n"),
(blkcnt_t)0);
}
goto filedone;
}
if (dblock.dbuf.typeflag == '6') { /* FIFO */
if (rmdir(namep) < 0) {
if (errno == ENOTDIR)
(void) unlink(namep);
}
linkp = templink;
if (*linkp != NULL) {
if (Aflag && *linkp == '/')
linkp++;
if (link(linkp, namep) < 0) {
(void) fprintf(stderr, gettext(
"tar: %s: c