/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright 2015 Joyent, Inc.
*/
/* 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.
*/
#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 <sys/acl.h>
#include <strings.h>
#include <deflt.h>
#include <limits.h>
#include <iconv.h>
#include <assert.h>
#include <libgen.h>
#include <libintl.h>
#include <aclutils.h>
#include <libnvpair.h>
#include <archives.h>
#if defined(__SunOS_5_6) || defined(__SunOS_5_7)
extern int defcntl();
#endif
#if defined(_PC_SATTR_ENABLED)
#include <attr.h>
#include <libcmdutils.h>
#endif
/* Trusted Extensions */
#include <zone.h>
#include <tsol/label.h>
#include <sys/tsol/label_macro.h>
#include "getresponse.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))
#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
#ifndef VIEW_READONLY
#define VIEW_READONLY "SUNWattr_ro"
#endif
#ifndef VIEW_READWRITE
#define VIEW_READWRITE "SUNWattr_rw"
#endif
#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;
#if defined(O_XATTR)
typedef enum {
ATTR_OK,
ATTR_SKIP,
ATTR_CHDIR_ERR,
ATTR_OPEN_ERR,
ATTR_XATTR_ERR,
ATTR_SATTR_ERR
} attr_status_t;
#endif
#if defined(O_XATTR)
typedef enum {
ARC_CREATE,
ARC_RESTORE
} arc_action_t;
#endif
typedef struct attr_data {
char *attr_parent;
char *attr_path;
int attr_parentfd;
int attr_rw_sysattr;
} attr_data_t;
/*
*
* 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 *xattrapath; /* 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
typedef struct file_list {
char *name; /* Name of file to {in,ex}clude */
struct file_list *next; /* Linked list */
} file_list_t;
static file_list_t *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(file_list_t *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(file_list_t *table[], char *file);
static int 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 *comp, char *namep);
static void doDirTimes(char *name, timestruc_t modTime);
static void done(int n);
static void dorep(char *argv[]);
static void dotable(char *argv[]);
static void doxtract(char *argv[]);
static int tar_chdir(const char *path);
static int is_directory(char *name);
static int has_dot_dot(char *name);
static int is_absolute(char *name);
static char *make_relative_name(char *name, char **stripped_prefix);
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 void load_info_from_xtarhdr(u_longlong_t flag, struct xtar_hdr *xhdrp);
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,
attr_data_t *attrinfo, 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 setbytes_to_skip(struct stat *st, int err);
static void splitfile(char *longname, int ifd, char *name,
char *prefix, int filetype);
static void tomodes(struct stat *sp);
static void usage(void);
static int xblocks(int issysattr, off_t bytes, int ofile);
static int xsfile(int issysattr, 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 boolean_t endtape(void);
static int is_in_table(file_list_t *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 unsigned int hash(char *str);
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,
attr_data_t **attrinfo);
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,
char *attrparent);
static void xattrs_put(char *, 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 *longattrname, char *prefix, int filetype, char typeflag);
static int put_extra_attributes(char *longname, char *shortname,
char *longattrname, char *prefix, int filetype, char typeflag);
static int put_xattr_hdr(char *longname, char *shortname, char *longattrname,
char *prefix, int typeflag, int filetype, struct linkbuf *lp);
static int read_xattr_hdr(attr_data_t **attrinfo);
/* 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_open_attr(int pdirfd, int cwd, char *dirp, char *pattr,
char *name, int oflag, mode_t mode);
static char *skipslashes(char *string, char *start);
static void chop_endslashes(char *path);
static pid_t compress_file(void);
static void compress_back(void);
static void decompress_file(void);
static pid_t uncompress_file(void);
static void *compress_malloc(size_t);
static void check_compression(void);
static char *bz_suffix(void);
static char *gz_suffix(void);
static char *xz_suffix(void);
static char *add_suffix();
static void wait_pid(pid_t);
static void verify_compress_opt(const char *t);
static void detect_compress(void);
static void dlog(const char *, ...);
static boolean_t should_enable_debug(void);
static struct stat stbuf;
static char *myname;
static char *xtract_chdir = NULL;
static int checkflag = 0;
static int Xflag, Fflag, iflag, hflag, Bflag, Iflag;
static int rflag, xflag, vflag, tflag, mt, cflag, mflag, pflag;
static int uflag;
static int errflag;
static int oflag;
static int bflag, Aflag;
static int Pflag; /* POSIX conformant archive */
static int Eflag; /* Allow files greater than 8GB */
static int atflag; /* traverse extended attributes */
static int saflag; /* traverse extended sys attributes */
static int Dflag; /* Data change flag */
static int jflag; /* flag to use 'bzip2' */
static int zflag; /* flag to use 'gzip' */
static int Zflag; /* flag to use 'compress' */
static int Jflag; /* flag to use 'xz' */
static int aflag; /* flag to use autocompression */
/* 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 *tmpdir;
static char *tmp_suffix = "/tarXXXXXX";
static char *tname;
static char archive[] = "archive0=";
static char *Xfile;
static char *usefile;
static char tfname[1024];
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 boolean_t is_posix; /* true if archive 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. */
static pid_t comp_pid = 0;
static boolean_t debug_output = B_FALSE;
#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_XHDR 0x1000 /* Bit flag that determines whether 'X' */
/* typeflag was followed by 'A' or non 'A' */
/* typeflag. */
#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 'D' function modifier to the
* target tar invocation, eg. "tar cDf 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
#define BZIP "/usr/bin/bzip2"
#define GZIP "/usr/bin/gzip"
#define COMPRESS "/usr/bin/compress"
#define XZ "/usr/bin/xz"
#define BZCAT "/usr/bin/bzcat"
#define GZCAT "/usr/bin/gzcat"
#define ZCAT "/usr/bin/zcat"
#define XZCAT "/usr/bin/xzcat"
#define GSUF 8 /* number of valid 'gzip' sufixes */
#define BSUF 4 /* number of valid 'bzip2' sufixes */
#define XSUF 1 /* number of valid 'xz' suffixes */
static char *compress_opt; /* compression type */
static char *gsuffix[] = {".gz", "-gz", ".z", "-z", "_z", ".Z",
".tgz", ".taz"};
static char *bsuffix[] = {".bz2", ".bz", ".tbz2", ".tbz"};
static char *xsuffix[] = {".xz"};
static char *suffix;
int
main(int argc, char *argv[])
{
char *cp;
char *tmpdirp;
pid_t thispid;
(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();
debug_output = should_enable_debug();
tfile = NULL;
if ((myname = strdup(argv[0])) == NULL) {
(void) fprintf(stderr, gettext(
"tar: cannot allocate program name\n"));
exit(1);
}
if (init_yes() < 0) {
(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
strerror(errno));
exit(2);
}
/*
* 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 'D':
/* 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':
Fflag++;
break;
case 'c':
cflag++;
rflag++;
update = 1;
break;
#if defined(O_XATTR)
case '@':
atflag++;
break;
#endif /* O_XATTR */
#if defined(_PC_SATTR_ENABLED)
case '/':
saflag++;
break;
#endif /* _PC_SATTR_ENABLED */
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 'n': /* not a magtape (instead of 'k') */
NotTape++; /* assume non-magtape */
break;
case 'l':
linkerrok++;
break;
case 'e':
errflag++;
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;
case 'j': /* compession "bzip2" */
jflag = 1;
break;
case 'z': /* compression "gzip" */
zflag = 1;
break;
case 'Z': /* compression "compress" */
Zflag = 1;
break;
case 'J': /* compression "xz" */
Jflag = 1;
break;
case 'a':
aflag = 1; /* autocompression */
break;
default:
(void) fprintf(stderr, gettext(
"tar: %c: unknown function modifier\n"), *cp);
usage();
}
if (!rflag && !xflag && !tflag)
usage();
if ((rflag && xflag) || (xflag && tflag) || (rflag && tflag)) {
(void) fprintf(stderr, gettext(
"tar: specify only one of [ctxru].\n"));
usage();
}
if (cflag) {
if ((jflag + zflag + Zflag + Jflag + aflag) > 1) {
(void) fprintf(stderr, gettext(
"tar: specify only one of [ajJzZ] to "
"create a compressed file.\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)
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 && usefile != NULL) {
/* Set the compression type */
if (aflag)
detect_compress();
if (jflag) {
compress_opt = compress_malloc(strlen(BZIP)
+ 1);
(void) strcpy(compress_opt, BZIP);
} else if (zflag) {
compress_opt = compress_malloc(strlen(GZIP)
+ 1);
(void) strcpy(compress_opt, GZIP);
} else if (Zflag) {
compress_opt =
compress_malloc(strlen(COMPRESS) + 1);
(void) strcpy(compress_opt, COMPRESS);
} else if (Jflag) {
compress_opt = compress_malloc(strlen(XZ) + 1);
(void) strcpy(compress_opt, XZ);
}
} else {
/*
* Decompress if the file is compressed for
* an update or replace.
*/
if (strcmp(usefile, "-") != 0) {
check_compression();
if (compress_opt != NULL) {
decompress_file();
}
}
}
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;
struct stat sbuf;
tmpdir = getenv("TMPDIR");
/*
* If the name is invalid or this isn't a directory,
* or the directory is not writable, then reset to
* a default temporary directory.
*/
if (tmpdir == NULL || *tmpdir == '\0' ||
(strlen(tmpdir) + strlen(tmp_suffix)) > PATH_MAX) {
tmpdir = "/tmp";
} else if (stat(tmpdir, &sbuf) < 0 ||
(sbuf.st_mode & S_IFMT) != S_IFDIR ||
(sbuf.st_mode & S_IWRITE) == 0) {
tmpdir = "/tmp";
}
if ((tname = calloc(1, strlen(tmpdir) +
strlen(tmp_suffix) + 1)) == NULL) {
vperror(1, gettext("tar: out of memory, "
"cannot create temporary file\n"));
}
(void) strcpy(tname, tmpdir);
(void) strcat(tname, tmp_suffix);
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"));
if (cflag && compress_opt != NULL)
comp_pid = compress_file();
dorep(argv);
if (rflag && !cflag && (compress_opt != NULL))
compress_back();
} 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]);
}
} else if (strcmp(argv[argc], "-C") == 0) {
if (!argv[argc+1]) {
(void) fprintf(stderr, gettext("tar: "
"missing argument for -C flag\n"));
done(2);
} else if (xtract_chdir != NULL) {
(void) fprintf(stderr, gettext("tar: "
"extract should have only one -C "
"flag\n"));
done(2);
} else {
argv[argc] = argv[argc+2];
xtract_chdir = argv[++argc];
}
}
}
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);
/* Decompress if the file is compressed */
if (strcmp(usefile, "-") != 0) {
check_compression();
if (compress_opt != NULL)
comp_pid = uncompress_file();
}
if (xflag) {
if (xtract_chdir != NULL) {
if (tar_chdir(xtract_chdir) < 0) {
vperror(1, gettext("can't change "
"directories to %s"), xtract_chdir);
}
}
if (Aflag && vflag)
(void) printf(gettext(
"Suppressing absolute pathnames.\n"));
doxtract(argv);
} else if (tflag)
dotable(argv);
}
else
usage();
done(Errflg);
/* Not reached: keep compiler quiet */
return (1);
}
static boolean_t
should_enable_debug(void)
{
const char *val;
const char *truth[] = {
"true",
"1",
"yes",
"y",
"please",
NULL
};
unsigned int i;
if ((val = getenv("DEBUG_TAR")) == NULL) {
return (B_FALSE);
}
for (i = 0; truth[i] != NULL; i++) {
if (strcmp(val, truth[i]) == 0) {
return (B_TRUE);
}
}
return (B_FALSE);
}
/*PRINTFLIKE1*/
static void
dlog(const char *format, ...)
{
va_list ap;
if (!debug_output) {
return;
}
va_start(ap, format);
(void) fprintf(stderr, "tar: DEBUG: ");
(void) vfprintf(stderr, format, ap);
va_end(ap);
}
static void
usage(void)
{
(void) fprintf(stderr, gettext(
#if defined(O_XATTR)
#if defined(_PC_SATTR_ENABLED)
"Usage: tar {c|r|t|u|x}[BDeEFhilmnopPTvw@/[0-7]][bf][X...] "
#else
"Usage: tar {c|r|t|u|x}[BDeEFhilmnopPTvw@[0-7]][bf][X...] "
#endif /* _PC_SATTR_ENABLED */
#else
"Usage: tar {c|r|t|u|x}[BDeEFhilmnopPTvw[0-7]][bf][X...] "
#endif /* O_XATTR */
"[j|J|z|Z] "
"[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;
int archtype;
int ret;
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"));
ret = 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 */
setbytes_to_skip(&stbuf, ret);
passtape(); /* skip the file data */
if (term)
done(Errflg); /* received signal to stop */
xhdr_flgs = 0;
getdir();
if (Xhdrflag > 0)
ret = get_xdata();
}
if (ret == 0) {
if ((dblock.dbuf.typeflag != 'A') &&
(xhdr_flgs != 0)) {
load_info_from_xtarhdr(xhdr_flgs,
&Xtarhdr);
xhdr_flgs |= _X_XHDR;
}
}
backtape(); /* was called by endtape */
if (tfile != NULL) {
/*
* Buffer size is calculated to be the size of the
* tmpdir string, plus 6 times the size of the tname
* string, plus a value that is known to be greater
* than the command pipeline string.
*/
int buflen = strlen(tmpdir) + (6 * strlen(tname)) + 100;
char *buf;
if ((buf = (char *)calloc(1, buflen)) == NULL) {
vperror(1, gettext("tar: out of memory, "
"cannot create sort command file\n"));
}
(void) snprintf(buf, buflen, "env 'TMPDIR=%s' "
"sort +0 -1 +1nr %s -o %s; awk '$1 "
"!= prev {print; prev=$1}' %s >%sX;mv %sX %s",
tmpdir, tname, tname, tname, tname, tname, tname);
(void) fflush(tfile);
(void) system(buf);
free(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));
}
/*
* 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) && !term) {
if (fp || (strcmp(*argv, "-I") == 0)) {
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]) {
if (tar_chdir(*++argv) < 0)
vperror(0, gettext(
"can't change directories to %s"), *argv);
else
(void) getcwd(wdir, (sizeof (wdir)));
argv++;
continue;
} 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 (tar_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, NULL, NORMAL_FILE,
LEV0, SYMLINK_LEV0);
#if defined(O_XATTR)
if (!exitflag) {
if ((atflag || saflag) &&
(archtype == PUT_NOTAS_LINK)) {
xattrs_put(file, cp2, parent, NULL);
}
}
#endif
if (tar_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 boolean_t
endtape(void)
{
if (dblock.dbuf.name[0] != '\0') {
/*
* The name field is populated.
*/
return (B_FALSE);
}
if (is_posix && dblock.dbuf.prefix[0] != '\0') {
/*
* This is a ustar/POSIX archive, and although the name
* field is empty the prefix field is not.
*/
return (B_FALSE);
}
dlog("endtape(): found null header; EOT\n");
return (B_TRUE);
}
/*
* 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') || (dblock.dbuf.typeflag == 'L')) {
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) {
Errflg = 2;
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) {
Errflg = 2;
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 {
char *aname = basename(xattrapath);
size_t xindex = aname - xattrapath;
if (xattrapath[xindex] == '.' &&
xattrapath[xindex + 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 the directory info.
*/
static void
passtape(void)
{
blkcnt_t blocks;
char buf[TBLOCK];
/*
* Print some debugging information about the directory entry
* we are skipping over:
*/
dlog("passtape: typeflag \"%c\"\n", dblock.dbuf.typeflag);
if (dblock.dbuf.name[0] != '\0') {
dlog("passtape: name \"%s\"\n", dblock.dbuf.name);
}
if (is_posix && dblock.dbuf.prefix[0] != '\0') {
dlog("passtape: prefix \"%s\"\n", dblock.dbuf.prefix);
}
/*
* 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);
dlog("passtape: block count %" FMT_blkcnt_t "\n", blocks);
/* if operating on disk, seek instead of reading */
if (NotTape)
seekdisk(blocks);
else
while (blocks-- > 0)
readtape(buf);
}
#if defined(O_XATTR)
static int
is_sysattr(char *name)
{
return ((strcmp(name, VIEW_READONLY) == 0) ||
(strcmp(name, VIEW_READWRITE) == 0));
}
#endif
#if defined(O_XATTR)
/*
* Verify the attribute, attrname, is an attribute we want to restore.
* Never restore read-only system attribute files. Only restore read-write
* system attributes files when -/ was specified, and only traverse into
* the 2nd level attribute directory containing only system attributes if
* -@ was specified. This keeps us from archiving
* <attribute name>/<read-write system attribute file>
* when -/ was specified without -@.
*
* attrname - attribute file name
* attrparent - attribute's parent name within the base file's attribute
* directory hierarchy
*/
static attr_status_t
verify_attr(char *attrname, char *attrparent, int arc_rwsysattr,
int *rw_sysattr)
{
#if defined(_PC_SATTR_ENABLED)
int attr_supported;
/* Never restore read-only system attribute files */
if ((attr_supported = sysattr_type(attrname)) == _RO_SATTR) {
*rw_sysattr = 0;
return (ATTR_SKIP);
} else {
*rw_sysattr = (attr_supported == _RW_SATTR);
}
#else
/*
* Only need to check if this attribute is an extended system
* attribute.
*/
if (*rw_sysattr = is_sysattr(attrname)) {
return (ATTR_SKIP);
} else {
return (ATTR_OK);
}
#endif /* _PC_SATTR_ENABLED */
/*
* If the extended system attribute file is specified with the
* arc_rwsysattr flag, as being transient (default extended
* attributes), then don't archive it.
*/
if (*rw_sysattr && !arc_rwsysattr) {
return (ATTR_SKIP);
}
/*
* Only restore read-write system attribute files
* when -/ was specified. Only restore extended
* attributes when -@ was specified.
*/
if (atflag) {
if (!saflag) {
/*
* Only archive/restore the hidden directory "." if
* we're processing the top level hidden attribute
* directory. We don't want to process the
* hidden attribute directory of the attribute
* directory that contains only extended system
* attributes.
*/
if (*rw_sysattr || (Hiddendir &&
(attrparent != NULL))) {
return (ATTR_SKIP);
}
}
} else if (saflag) {
/*
* Only archive/restore read-write extended system attribute
* files of the base file.
*/
if (!*rw_sysattr || (attrparent != NULL)) {
return (ATTR_SKIP);
}
} else {
return (ATTR_SKIP);
}
return (ATTR_OK);
}
#endif
static void
free_children(file_list_t *children)
{
file_list_t *child = children;
file_list_t *cptr;
while (child != NULL) {
cptr = child->next;
if (child->name != NULL) {
free(child->name);
}
child = cptr;
}
}
static int
putfile(char *longname, char *shortname, char *parent, attr_data_t *attrinfo,
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;
char *attrparent = NULL;
char *longattrname = NULL;
file_list_t *child = NULL;
file_list_t *child_end = NULL;
file_list_t *cptr;
struct dirent *dp;
DIR *dirp;
int i;
int split;
int dirfd = -1;
int rc = PUT_NOTAS_LINK;
int archtype = 0;
int rw_sysattr = 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) {
attrparent = attrinfo->attr_parent;
longattrname = attrinfo->attr_path;
dirfd = attrinfo->attr_parentfd;
rw_sysattr = attrinfo->attr_rw_sysattr;
} else {
dirfd = open(".", O_RDONLY);
}
if (dirfd == -1) {
(void) fprintf(stderr, gettext(
"tar: unable to open%sdirectory %s%s%s%s\n"),
(filetype == XATTR_FILE) ? gettext(" attribute ") : " ",
(attrparent == NULL) ? "" : gettext("of attribute "),
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : gettext(" of "),
(filetype == XATTR_FILE) ? longname : parent);
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, attrparent))
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%s%s%s%s same as archive file\n"),
rw_sysattr ? gettext("system ") : "",
(longattrname == NULL) ? "" : gettext("attribute "),
(longattrname == NULL) ? "" : longattrname,
(longattrname == NULL) ? "" : gettext(" of "),
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%s%s%s%s too large to archive. "
"Use E function modifier.\n"),
rw_sysattr ? gettext("system ") : "",
(longattrname == NULL) ? "" : gettext("attribute "),
(longattrname == NULL) ? "" : longattrname,
(longattrname == NULL) ? "" : gettext(" of "),
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(longname, (stbuf.st_mode & S_IFMT) == S_IFDIR, 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,
longattrname, 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) {
if (NotTape) {
dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
}
if (filetype == XATTR_FILE && Hiddendir) {
(void) fprintf(vfile,
gettext("a %s attribute %s "),
longname, longattrname);
} 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 only be system attributes on
* attributes. There can be no 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 (tar_chdir(shortname) < 0) {
vperror(0, "%s", newparent);
goto out;
}
if ((dirp = opendir(".")) == NULL) {
vperror(0, gettext(
"can't open directory %s"), longname);
if (tar_chdir(parent) < 0)
vperror(0, gettext("cannot change back?: %s"),
parent);
goto out;
}
/*
* Create a list of files (children) in this directory to avoid
* having to perform telldir()/seekdir().
*/
while ((dp = readdir(dirp)) != NULL && !term) {
if ((strcmp(".", dp->d_name) == 0) ||
(strcmp("..", dp->d_name) == 0))
continue;
if (((cptr = (file_list_t *)calloc(sizeof (char),
sizeof (file_list_t))) == NULL) ||
((cptr->name = strdup(dp->d_name)) == NULL)) {
vperror(1, gettext(
"Insufficient memory for directory "
"list entry %s/%s\n"),
newparent, dp->d_name);
}
/* Add the file to the list */
if (child == NULL) {
child = cptr;
} else {
child_end->next = cptr;
}
child_end = cptr;
}
(void) closedir(dirp);
/*
* Archive each of the files in the current directory.
* If a file is a directory, putfile() is called
* recursively to archive the file hierarchy of the
* directory before archiving the next file in the
* current directory.
*/
while ((child != NULL) && !term) {
(void) strcpy(cp, child->name);
archtype = putfile(buf, cp, newparent, NULL,
NORMAL_FILE, lev + 1, symlink_lev);
if (!exitflag) {
if ((atflag || saflag) &&
(archtype == PUT_NOTAS_LINK)) {
xattrs_put(buf, cp, newparent, NULL);
}
}
if (exitflag)
break;
/* Free each child as we are done processing it. */
cptr = child;
child = child->next;
free(cptr->name);
free(cptr);
}
if ((child != NULL) && !term) {
free_children(child);
}
if (tar_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, gettext("unable to open %s%s%s%s"), longname,
rw_sysattr ? gettext(" system") : "",
(filetype == XATTR_FILE) ?
gettext(" attribute ") : "",
(filetype == XATTR_FILE) ? (longattrname == NULL) ?
shortname : longattrname : "");
goto out;
}
blocks = TBLOCKS(stbuf.st_size);
if (put_link(name, longname, shortname, longattrname,
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) {
/* 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 */
}
dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
if (build_dblock(name, tchar, '0', filetype,
&stbuf, stbuf.st_dev, prefix) != 0) {
goto out;
}
if (vflag) {
if (NotTape) {
dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
}
(void) fprintf(vfile, "a %s%s%s%s ", longname,
rw_sysattr ? gettext(" system") : "",
(filetype == XATTR_FILE) ? gettext(
" attribute ") : "",
(filetype == XATTR_FILE) ?
longattrname : "");
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, longattrname,
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, longattrname,
prefix, filetype, '6') == 0) {
rc = PUT_AS_LINK;
goto out;
}
tomodes(&stbuf);
while (mulvol && tapepos + blocks + 1 > blocklim) {
if (((blocklim - tapepos) >= EXTMIN) &&
((blocks + 1) >= blocklim/10)) {
splitfile(longname, infile, name,
prefix, filetype);
(void) close(dirfd);
(void) close(infile);
goto out;
}
newvol();
}
dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
if (vflag) {
if (NotTape) {
dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
(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, longattrname,
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, longattrname,
prefix, filetype, '3') == 0) {
rc = PUT_AS_LINK;
goto out;
}
tomodes(&stbuf);
while (mulvol && tapepos + blocks + 1 > blocklim) {
if (((blocklim - tapepos) >= EXTMIN) &&
((blocks + 1) >= blocklim/10)) {
splitfile(longname, infile, name,
prefix, filetype);
(void) close(dirfd);
goto out;
}
newvol();
}
dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
if (vflag) {
if (NotTape) {
dlog("seek = %" FMT_blkcnt_t "K\t", K(tapepos));
(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, longattrname,
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, longattrname,
prefix, filetype, '4') == 0) {
rc = PUT_AS_LINK;
goto out;
}
tomodes(&stbuf);
while (mulvol && tapepos + blocks + 1 > blocklim) {
if (((blocklim - tapepos) >= EXTMIN) &&
((blocks + 1) >= blocklim/10)) {
splitfile(longname, infile,
name, prefix, filetype);
(void) close(dirfd);
goto out;
}
newvol();
}
dlog("putfile: %s wants %" FMT_blkcnt_t " blocks\n", longname,
blocks);
if (vflag) {
if (NotTape) {
dlog("seek = %" FMT_blkcnt_t "K\n", K(tapepos));
}
(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, longattrname,
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) && (filetype != XATTR_FILE)) {
(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,
gettext("+++ 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 != 'L') &&
(dblock.dbuf.typeflag != _XATTR_HDRTYPE) &&
(dblock.dbuf.typeflag != 'X')) {
return (1);
}
return (0);
}
#if defined(O_XATTR)
static int
save_cwd(void)
{
return (open(".", O_RDONLY));
}
#endif
#if defined(O_XATTR)
static void
rest_cwd(int *cwd)
{
if (*cwd != -1) {
if (fchdir(*cwd) < 0) {
vperror(0, gettext(
"Cannot fchdir to attribute directory"));
exit(1);
}
(void) close(*cwd);
*cwd = -1;
}
}
#endif
/*
* Verify the underlying file system supports the attribute type.
* Only archive extended attribute files when '-@' was specified.
* Only archive system extended attribute files if '-/' was specified.
*/
#if defined(O_XATTR)
static attr_status_t
verify_attr_support(char *filename, int attrflg, arc_action_t actflag,
int *ext_attrflg)
{
/*
* Verify extended attributes are supported/exist. We only
* need to check if we are processing a base file, not an
* extended attribute.
*/
if (attrflg) {
*ext_attrflg = (pathconf(filename, (actflag == ARC_CREATE) ?
_PC_XATTR_EXISTS : _PC_XATTR_ENABLED) == 1);
}
if (atflag) {
if (!*ext_attrflg) {
#if defined(_PC_SATTR_ENABLED)
if (saflag) {
/* Verify system attributes are supported */
if (sysattr_support(filename,
(actflag == ARC_CREATE) ? _PC_SATTR_EXISTS :
_PC_SATTR_ENABLED) != 1) {
return (ATTR_SATTR_ERR);
}
} else
return (ATTR_XATTR_ERR);
#else
return (ATTR_XATTR_ERR);
#endif /* _PC_SATTR_ENABLED */
}
#if defined(_PC_SATTR_ENABLED)
} else if (saflag) {
/* Verify system attributes are supported */
if (sysattr_support(filename, (actflag == ARC_CREATE) ?
_PC_SATTR_EXISTS : _PC_SATTR_ENABLED) != 1) {
return (ATTR_SATTR_ERR);
}
#endif /* _PC_SATTR_ENABLED */
} else {
return (ATTR_SKIP);
}
return (ATTR_OK);
}
#endif
#if defined(O_XATTR)
/*
* Recursively open attribute directories until the attribute directory
* containing the specified attribute, attrname, is opened.
*
* Currently, only 2 directory levels of attributes are supported, (i.e.,
* extended system attributes on extended attributes). The following are
* the possible input combinations:
* 1. Open the attribute directory of the base file (don't change
* into it).
* attrinfo->parent = NULL
* attrname = '.'
* 2. Open the attribute directory of the base file and change into it.
* attrinfo->parent = NULL
* attrname = <attr> | <sys_attr>
* 3. Open the attribute directory of the base file, change into it,
* then recursively call open_attr_dir() to open the attribute's
* parent directory (don't change into it).
* attrinfo->parent = <attr>
* attrname = '.'
* 4. Open the attribute directory of the base file, change into it,
* then recursively call open_attr_dir() to open the attribute's
* parent directory and change into it.
* attrinfo->parent = <attr>
* attrname = <attr> | <sys_attr>
*
* An attribute directory will be opened only if the underlying file system
* supports the attribute type, and if the command line specifications (atflag
* and saflag) enable the processing of the attribute type.
*
* On succesful return, attrinfo->parentfd will be the file descriptor of the
* opened attribute directory. In addition, if the attribute is a read-write
* extended system attribute, attrinfo->rw_sysattr will be set to 1, otherwise
* it will be set to 0.
*
* Possible return values:
* ATTR_OK Successfully opened and, if needed, changed into the
* attribute directory containing attrname.
* ATTR_SKIP The command line specifications don't enable the
* processing of the attribute type.
* ATTR_CHDIR_ERR An error occurred while trying to change into an
* attribute directory.
* ATTR_OPEN_ERR An error occurred while trying to open an
* attribute directory.
* ATTR_XATTR_ERR The underlying file system doesn't support extended
* attributes.
* ATTR_SATTR_ERR The underlying file system doesn't support extended
* system attributes.
*/
static int
open_attr_dir(char *attrname, char *dirp, int cwd, attr_data_t *attrinfo)
{
attr_status_t rc;
int firsttime = (attrinfo->attr_parentfd == -1);
int saveerrno;
int ext_attr;
/*
* open_attr_dir() was recursively called (input combination number 4),
* close the previously opened file descriptor as we've already changed
* into it.
*/
if (!firsttime) {
(void) close(attrinfo->attr_parentfd);
attrinfo->attr_parentfd = -1;
}
/*
* Verify that the underlying file system supports the restoration
* of the attribute.
*/
if ((rc = verify_attr_support(dirp, firsttime, ARC_RESTORE,
&ext_attr)) != ATTR_OK) {
return (rc);
}
/* Open the base file's attribute directory */
if ((attrinfo->attr_parentfd = attropen(dirp, ".", O_RDONLY)) == -1) {
/*
* Save the errno from the attropen so it can be reported
* if the retry of the attropen fails.
*/
saveerrno = errno;
if ((attrinfo->attr_parentfd = retry_open_attr(-1, cwd, dirp,
NULL, ".", O_RDONLY, 0)) == -1) {
/*
* Reset typeflag back to real value so passtape
* will skip ahead correctly.
*/
dblock.dbuf.typeflag = _XATTR_HDRTYPE;
(void) close(attrinfo->attr_parentfd);
attrinfo->attr_parentfd = -1;
errno = saveerrno;
return (ATTR_OPEN_ERR);
}
}
/*
* Change into the parent attribute's directory unless we are
* processing the hidden attribute directory of the base file itself.
*/
if ((Hiddendir == 0) || (firsttime && attrinfo->attr_parent != NULL)) {
if (fchdir(attrinfo->attr_parentfd) != 0) {
saveerrno = errno;
(void) close(attrinfo->attr_parentfd);
attrinfo->attr_parentfd = -1;
errno = saveerrno;
return (ATTR_CHDIR_ERR);
}
}
/* Determine if the attribute should be processed */
if ((rc = verify_attr(attrname, attrinfo->attr_parent, 1,
&attrinfo->attr_rw_sysattr)) != ATTR_OK) {
saveerrno = errno;
(void) close(attrinfo->attr_parentfd);
attrinfo->attr_parentfd = -1;
errno = saveerrno;
return (rc);
}
/*
* If the attribute is an extended attribute, or extended system
* attribute, of an attribute (i.e., <attr>/<sys_attr>), then
* recursively call open_attr_dir() to open the attribute directory
* of the parent attribute.
*/
if (firsttime && (attrinfo->attr_parent != NULL)) {
return (open_attr_dir(attrname, attrinfo->attr_parent,
attrinfo->attr_parentfd, attrinfo));
}
return (ATTR_OK);
}
#endif
static void
doxtract(char *argv[])
{
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;
int cwd = -1;
int rw_sysattr;
int saveerrno;
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 */
int once = 1;
int error;
int symflag;
int want;
attr_data_t *attrinfo = NULL; /* attribute info */
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 */
Uid = getuid();
for (;;) {
convflag = 0;
symflag = 0;
dir = 0;
Hiddendir = 0;
rw_sysattr = 0;
ofile = -1;
if (dirfd != -1) {
(void) close(dirfd);
dirfd = -1;
}
if (ofile != -1) {
if (close(ofile) != 0)
vperror(2, gettext("close error"));
}
#if defined(O_XATTR)
if (cwd != -1) {
rest_cwd(&cwd);
}
#endif
/* namep is set by wantit to point to the full name */
if ((want = wantit(argv, &namep, &dirp, &comp,
&attrinfo)) == 0) {
#if defined(O_XATTR)
if (xattrp != 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 != NULL) {
int rc;
if (((cwd = save_cwd()) == -1) ||
((rc = open_attr_dir(comp, dirp, cwd,
attrinfo)) != ATTR_OK)) {
if (cwd == -1) {
vperror(0, gettext(
"unable to save current working "
"directory while processing "
"attribute %s of %s"),
dirp, attrinfo->attr_path);
} else if (rc != ATTR_SKIP) {
(void) fprintf(vfile,
gettext("tar: cannot open "
"%sattribute %s of file %s: %s\n"),
attrinfo->attr_rw_sysattr ? gettext(
"system ") : "",
comp, dirp, strerror(errno));
}
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
passtape();
continue;
} else {
dirfd = attrinfo->attr_parentfd;
rw_sysattr = attrinfo->attr_rw_sysattr;
}
} else {
dirfd = open(dirp, O_RDONLY);
}
#else
dirfd = open(dirp, O_RDONLY);
#endif
if (dirfd == -1) {
(void) fprintf(vfile, gettext(
"tar: cannot open %s: %s\n"),
dirp, strerror(errno));
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) {
if (checkf(namep, is_directory(namep), 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 != NULL) && Hiddendir == 1) {
bytes = stbuf.st_size;
blocks = TBLOCKS(bytes);
if (vflag) {
(void) fprintf(vfile,
"x %s%s%s, %" FMT_off_t " %s, ", namep,
gettext(" attribute "),
xattrapath, bytes,
gettext("bytes"));
if (NotTape)
(void) fprintf(vfile,
"%" FMT_blkcnt_t "K\n", K(blocks));
else
(void) fprintf(vfile, gettext("%"
FMT_blkcnt_t " tape blocks\n"),
blocks);
}
/*
* Set the permissions and mode of the attribute
* unless the attribute is a system attribute (can't
* successfully do this) or the hidden attribute
* directory (".") of an attribute (when the attribute
* is restored, the hidden attribute directory of an
* attribute is transient). Note: when the permissions
* and mode are set for the hidden attribute directory
* of a file on a system supporting extended system
* attributes, even though it returns successfully, it
* will not have any affect since the attribute
* directory is transient.
*/
if (attrinfo->attr_parent == NULL) {
if (fchownat(dirfd, ".", stbuf.st_uid,
stbuf.st_gid, 0) != 0) {
vperror(0, gettext(
"%s%s%s: failed to set ownership "
"of attribute directory"), namep,
gettext(" attribute "), xattrapath);
}
if (fchmod(dirfd, stbuf.st_mode) != 0) {
vperror(0, gettext(
"%s%s%s: failed to set permissions "
"of attribute directory"), namep,
gettext(" attribute "), xattrapath);
}
}
goto filedone;
}
#endif
if (dircreate && (!is_posix || dblock.dbuf.typeflag == '5')) {
dir = 1;
if (vflag) {
(void) fprintf(vfile, "x %s, 0 %s, ",
&dirname[0], gettext("bytes"));
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: cannot link\n"), namep);
continue;
}
if (vflag)
(void) fprintf(vfile, gettext(
"x %s linked to %s\n"), namep,
linkp);
xcnt++; /* increment # files extracted */
continue;
}
if (mknod(namep, (int)(Gen.g_mode|S_IFIFO),
(int)Gen.g_devmajor) < 0) {
vperror(0, gettext("%s: mknod failed"), namep);
continue;
}
bytes = stbuf.st_size;
blocks = TBLOCKS(bytes);
if (vflag) {
(void) fprintf(vfile, "x %s, %" FMT_off_t
" %s, ", namep, bytes, gettext("bytes"));
if (NotTape)
(void) fprintf(vfile, "%" FMT_blkcnt_t
"K\n", K(blocks));
else
(void) fprintf(vfile, gettext("%"
FMT_blkcnt_t " tape blocks\n"),
blocks);
}
goto filedone;
}
if (dblock.dbuf.typeflag == '3' && !Uid) { /* CHAR SPECIAL */
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: cannot link\n"), namep);
continue;
}
if (vflag)
(void) fprintf(vfile, gettext(
"x %s linked to %s\n"), namep,
linkp);
xcnt++; /* increment # files extracted */
continue;
}
if (mknod(namep, (int)(Gen.g_mode|S_IFCHR),
(int)makedev(Gen.g_devmajor, Gen.g_devminor)) < 0) {
vperror(0, gettext(
"%s: mknod failed"), namep);
continue;
}
bytes = stbuf.st_size;
blocks = TBLOCKS(bytes);
if (vflag) {
(void) fprintf(vfile, "x %s, %" FMT_off_t
" %s, ", namep, bytes, gettext("bytes"));
if (NotTape)
(void) fprintf(vfile, "%" FMT_blkcnt_t
"K\n", K(blocks));
else
(void) fprintf(vfile, gettext("%"
FMT_blkcnt_t " tape blocks\n"),
blocks);
}
goto filedone;
} else if (dblock.dbuf.typeflag == '3' && Uid) {
(void) fprintf(stderr, gettext(
"Can't create special %s\n"), namep);
continue;
}
/* BLOCK SPECIAL */
if (dblock.dbuf.typeflag == '4' && !Uid) {
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: cannot link\n"), namep);
continue;
}
if (vflag)
(void) fprintf(vfile, gettext(
"x %s linked to %s\n"), namep,
linkp);
xcnt++; /* increment # files extracted */
continue;
}
if (mknod(namep, (int)(Gen.g_mode|S_IFBLK),
(int)makedev(Gen.g_devmajor, Gen.g_devminor)) < 0) {
vperror(0, gettext("%s: mknod failed"), namep);
continue;
}
bytes = stbuf.st_size;
blocks = TBLOCKS(bytes);
if (vflag) {
(void) fprintf(vfile, gettext("x %s, %"
FMT_off_t " bytes, "), namep, bytes);
if (NotTape)
(void) fprintf(vfile, "%" FMT_blkcnt_t
"K\n", K(blocks));
else
(void) fprintf(vfile, gettext("%"
FMT_blkcnt_t " tape blocks\n"),
blocks);
}
goto filedone;
} else if (dblock.dbuf.typeflag == '4' && Uid) {
(void) fprintf(stderr,
gettext("Can't create special %s\n"), namep);
continue;
}
if (dblock.dbuf.typeflag == '2') { /* symlink */
if ((Tflag) && (lk_rpath_flag == 1))
linkp = lk_real_path;
else
linkp = templink;
if (Aflag && *linkp == '/')
linkp++;
if (rmdir(namep) < 0) {
if (errno == ENOTDIR)
(void) unlink(namep);
}
if (symlink(linkp, namep) < 0) {
vperror(0, gettext("%s: symbolic link failed"),
namep);
continue;
}
if (vflag)
(void) fprintf(vfile, gettext(
"x %s symbolic link to %s\n"),
namep, linkp);
symflag = AT_SYMLINK_NOFOLLOW;
goto filedone;
}
if (dblock.dbuf.typeflag == '1') {
linkp = templink;
if (Aflag && *linkp == '/')
linkp++;
if (unlinkat(dirfd, comp, AT_REMOVEDIR) < 0) {
if (errno == ENOTDIR)
(void) unlinkat(dirfd, comp, 0);
}
#if defined(O_XATTR)
if (xattrp && xattr_linkp) {
if (fchdir(dirfd) < 0) {
vperror(0, gettext(
"Cannot fchdir to attribute "
"directory %s"),
(attrinfo->attr_parent == NULL) ?
dirp : attrinfo->attr_parent);
exit(1);
}
error = link(xattr_linkaname, xattrapath);
} else {
error = link(linkp, namep);
}
#else
error = link(linkp, namep);
#endif
if (error < 0) {
(void) fprintf(stderr, gettext(
"tar: %s%s%s: cannot link\n"),
namep, (xattr_linkp != NULL) ?
gettext(" attribute ") : "",
(xattr_linkp != NULL) ?
xattrapath : "");
continue;
}
if (vflag)
(void) fprintf(vfile, gettext(
"x %s%s%s linked to %s%s%s\n"), namep,
(xattr_linkp != NULL) ?
gettext(" attribute ") : "",
(xattr_linkp != NULL) ?
xattr_linkaname : "",
linkp,
(xattr_linkp != NULL) ?
gettext(" attribute ") : "",
(xattr_linkp != NULL) ? xattrapath : "");
xcnt++; /* increment # files extracted */
#if defined(O_XATTR)
if (xattrp != NULL) {
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
}
#endif
continue;
}
/* REGULAR FILES */
if (convtoreg(stbuf.st_size)) {
convflag = 1;
if (errflag) {
(void) fprintf(stderr, gettext(
"tar: %s: typeflag '%c' not recognized\n"),
namep, dblock.dbuf.typeflag);
done(1);
} else {
(void) fprintf(stderr, gettext(
"tar: %s: typeflag '%c' not recognized, "
"converting to regular file\n"), namep,
dblock.dbuf.typeflag);
Errflg = 1;
}
}
if (dblock.dbuf.typeflag == '0' ||
dblock.dbuf.typeflag == NULL || convflag) {
delete_target(dirfd, comp, namep);
linkp = templink;
if (*linkp != NULL) {
if (Aflag && *linkp == '/')
linkp++;
if (link(linkp, comp) < 0) {
(void) fprintf(stderr, gettext(
"tar: %s: cannot link\n"), namep);
continue;
}
if (vflag)
(void) fprintf(vfile, gettext(
"x %s linked to %s\n"), comp,
linkp);
xcnt++; /* increment # files extracted */
#if defined(O_XATTR)
if (xattrp != NULL) {
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
}
#endif
continue;
}
newfile = ((fstatat(dirfd, comp,
&xtractbuf, 0) == -1) ? TRUE : FALSE);
ofile = openat(dirfd, comp, O_RDWR|O_CREAT|O_TRUNC,
stbuf.st_mode & MODEMASK);
saveerrno = errno;
#if defined(O_XATTR)
if (xattrp != NULL) {
if (ofile < 0) {
ofile = retry_open_attr(dirfd, cwd,
dirp, attrinfo->attr_parent, comp,
O_RDWR|O_CREAT|O_TRUNC,
stbuf.st_mode & MODEMASK);
}
}
#endif
if (ofile < 0) {
errno = saveerrno;
(void) fprintf(stderr, gettext(
"tar: %s%s%s%s - cannot create\n"),
(xattrp == NULL) ? "" : (rw_sysattr ?
gettext("system attribute ") :
gettext("attribute ")),
(xattrp == NULL) ? "" : xattrapath,
(xattrp == NULL) ? "" : gettext(" of "),
(xattrp == NULL) ? comp : namep);
if (errflag)
done(1);
else
Errflg = 1;
#if defined(O_XATTR)
if (xattrp != NULL) {
dblock.dbuf.typeflag = _XATTR_HDRTYPE;
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
}
#endif
passtape();
continue;
}
if (Tflag && (check_ext_attr(namep) == 0)) {
if (errflag)
done(1);
else
Errflg = 1;
passtape();
continue;
}
if (extno != 0) { /* file is in pieces */
if (extotal < 1 || extotal > MAXEXT)
(void) fprintf(stderr, gettext(
"tar: ignoring bad extent info for "
"%s%s%s%s\n"),
(xattrp == NULL) ? "" : (rw_sysattr ?
gettext("system attribute ") :
gettext("attribute ")),
(xattrp == NULL) ? "" : xattrapath,
(xattrp == NULL) ? "" : gettext(" of "),
(xattrp == NULL) ? comp : namep);
else {
/* extract it */
(void) xsfile(rw_sysattr, ofile);
}
}
extno = 0; /* let everyone know file is not split */
bytes = stbuf.st_size;
blocks = TBLOCKS(bytes);
if (vflag) {
(void) fprintf(vfile,
"x %s%s%s, %" FMT_off_t " %s, ",
(xattrp == NULL) ? "" : dirp,
(xattrp == NULL) ? "" : (rw_sysattr ?
gettext(" system attribute ") :
gettext(" attribute ")),
(xattrp == NULL) ? namep : xattrapath, bytes,
gettext("bytes"));
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 (xblocks(rw_sysattr, bytes, ofile) != 0) {
#if defined(O_XATTR)
if (xattrp != NULL) {
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
}
#endif
continue;
}
filedone:
if (mflag == 0 && !symflag) {
if (dir)
doDirTimes(namep, stbuf.st_mtim);
else
#if defined(O_XATTR)
if (xattrp != NULL) {
/*
* Set the time on the attribute unless
* the attribute is a system attribute
* (can't successfully do this) or the
* hidden attribute directory, "." (the
* time on the hidden attribute
* directory will be updated when
* attributes are restored, otherwise
* it's transient).
*/
if (!rw_sysattr && (Hiddendir == 0)) {
setPathTimes(dirfd, comp,
stbuf.st_mtim);
}
} else
setPathTimes(dirfd, comp,
stbuf.st_mtim);
#else
setPathTimes(dirfd, comp, stbuf.st_mtim);
#endif
}
/* moved this code from above */
if (pflag && !symflag && Hiddendir == 0) {
if (xattrp != NULL)
(void) fchmod(ofile, stbuf.st_mode & MODEMASK);
else
(void) chmod(namep, stbuf.st_mode & MODEMASK);
}
/*
* Because ancillary file preceeds the normal file,
* acl info may have been retrieved (in aclp).
* All file types are directed here (go filedone).
* Always restore ACLs if there are ACLs.
*/
if (aclp != NULL) {
int ret;
#if defined(O_XATTR)
if (xattrp != NULL) {
if (Hiddendir)
ret = facl_set(dirfd, aclp);
else
ret = facl_set(ofile, aclp);
} else {
ret = acl_set(namep, aclp);
}
#else
ret = acl_set(namep, aclp);
#endif
if (ret < 0) {
if (pflag) {
(void) fprintf(stderr, gettext(
"%s%s%s%s: failed to set acl "
"entries\n"), namep,
(xattrp == NULL) ? "" :
(rw_sysattr ? gettext(
" system attribute ") :
gettext(" attribute ")),
(xattrp == NULL) ? "" :
xattrapath);
}
/* else: silent and continue */
}
acl_free(aclp);
aclp = NULL;
}
if (!oflag)
/* set file ownership */
resugname(dirfd, comp, symflag);
if (pflag && newfile == TRUE && !dir &&
(dblock.dbuf.typeflag == '0' ||
dblock.dbuf.typeflag == NULL ||
convflag || dblock.dbuf.typeflag == '1')) {
if (fstat(ofile, &xtractbuf) == -1)
(void) fprintf(stderr, gettext(
"tar: cannot stat extracted file "
"%s%s%s%s\n"),
(xattrp == NULL) ? "" : (rw_sysattr ?
gettext("system attribute ") :
gettext("attribute ")),
(xattrp == NULL) ? "" : xattrapath,
(xattrp == NULL) ? "" :
gettext(" of "), namep);
else if ((xtractbuf.st_mode & (MODEMASK & ~S_IFMT))
!= (stbuf.st_mode & (MODEMASK & ~S_IFMT))) {
(void) fprintf(stderr, gettext(
"tar: warning - file permissions have "
"changed for %s%s%s%s (are 0%o, should be "
"0%o)\n"),
(xattrp == NULL) ? "" : (rw_sysattr ?
gettext("system attribute ") :
gettext("attribute ")),
(xattrp == NULL) ? "" : xattrapath,
(xattrp == NULL) ? "" :
gettext(" of "), namep,
xtractbuf.st_mode, stbuf.st_mode);
}
}
#if defined(O_XATTR)
if (xattrp != NULL) {
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
}
#endif
if (ofile != -1) {
(void) close(dirfd);
dirfd = -1;
if (close(ofile) != 0)
vperror(2, gettext("close error"));
ofile = -1;
}
xcnt++; /* increment # files extracted */
}
/*
* Process ancillary file.
*
*/
if (dblock.dbuf.typeflag == 'A') { /* acl info */
char buf[TBLOCK];
char *secp;
char *tp;
int attrsize;
int cnt;
/* reset Trusted Extensions flags */
dir_flag = 0;
mld_flag = 0;
lk_rpath_flag = 0;
rpath_flag = 0;
if (pflag) {
bytes = stbuf.st_size;
if ((secp = malloc((int)bytes)) == NULL) {
(void) fprintf(stderr, gettext(
"Insufficient memory for acl\n"));
passtape();
continue;
}
tp = secp;
blocks = TBLOCKS(bytes);
/*
* Display a line for each ancillary file.
*/
if (vflag && Tflag)
(void) fprintf(vfile, "x %s(A), %"
FMT_blkcnt_t " %s, %"
FMT_blkcnt_t " %s\n",
namep, bytes, gettext("bytes"),
blocks, gettext("tape blocks"));
while (blocks-- > 0) {
readtape(buf);
if (bytes <= TBLOCK) {
(void) memcpy(tp, buf,
(size_t)bytes);
break;
} else {
(void) memcpy(tp, buf,
TBLOCK);
tp += TBLOCK;
}
bytes -= TBLOCK;
}
bytes = stbuf.st_size;
/* got all attributes in secp */
tp = secp;
do {
attr = (struct sec_attr *)tp;
switch (attr->attr_type) {
case UFSD_ACL:
case ACE_ACL:
(void) sscanf(attr->attr_len,
"%7o",
(uint_t *)
&cnt);
/* header is 8 */
attrsize = 8 + (int)strlen(
&attr->attr_info[0]) + 1;
error =
acl_fromtext(
&attr->attr_info[0], &aclp);
if (error != 0) {
(void) fprintf(stderr,
gettext(
"aclfromtext "
"failed: %s\n"),
acl_strerror(
error));
bytes -= attrsize;
break;
}
if (acl_cnt(aclp) != cnt) {
(void) fprintf(stderr,
gettext(
"aclcnt error\n"));
bytes -= attrsize;
break;
}
bytes -= attrsize;
break;
/* Trusted Extensions */
case DIR_TYPE:
case LBL_TYPE:
case APRIV_TYPE:
case FPRIV_TYPE:
case COMP_TYPE:
case LK_COMP_TYPE:
case ATTR_FLAG_TYPE:
attrsize =
sizeof (struct sec_attr) +
strlen(&attr->attr_info[0]);
bytes -= attrsize;
if (Tflag)
extract_attr(&namep,
attr);
break;
default:
(void) fprintf(stderr, gettext(
"unrecognized attr"
" type\n"));
bytes = (off_t)0;
break;
}
/* next attributes */
tp += attrsize;
} while (bytes != 0);
free(secp);
} else {
passtape();
}
} /* acl */
} /* for */
/*
* Ensure that all the directories still on the directory stack
* get their modification times set correctly by flushing the
* stack.
*/
doDirTimes(NULL, time_zero);
#if defined(O_XATTR)
if (xattrp != NULL) {
free(xattrhead);
xattrp = NULL;
xattr_linkp = NULL;
xattrhead = NULL;
}
#endif
/*
* Check if the number of files extracted is different from the
* number of files listed on the command line
*/
if (fcnt > xcnt) {
(void) fprintf(stderr,
gettext("tar: %d file(s) not extracted\n"),
fcnt-xcnt);
Errflg = 1;
}
}
/*
* xblocks extract file/extent from tape to output file
*
* xblocks(issysattr, bytes, ofile);
*
* issysattr flag set if the files being extracted
* is an extended system attribute file.
* unsigned long long bytes size of extent or file to be extracted
* ofile output file
*
* called by doxtract() and xsfile()
*/
static int
xblocks(int issysattr, off_t bytes, int ofile)
{
char *buf;
char tempname[NAMSIZ+1];
size_t maxwrite;
size_t bytesread;
size_t piosize; /* preferred I/O size */
struct stat tsbuf;
/* Don't need to do anything if this is a zero size file */
if (bytes <= 0) {
return (0);
}
/*
* To figure out the size of the buffer used to accumulate data
* from readtape() and to write to the file, we need to determine
* the largest chunk of data to be written to the file at one time.
* This is determined based on the smallest of the following two
* things:
* 1) The size of the archived file.
* 2) The preferred I/O size of the file.
*/
if (issysattr || (bytes <= TBLOCK)) {
/*
* Writes to system attribute files must be
* performed in one operation.
*/
maxwrite = bytes;
} else {
/*
* fstat() the file to get the preferred I/O size.
* If it fails, then resort back to just writing
* one block at a time.
*/
if (fstat(ofile, &tsbuf) == 0) {
piosize = tsbuf.st_blksize;
} else {
piosize = TBLOCK;
}
maxwrite = min(bytes, piosize);
}
/*
* The buffer used to accumulate the data for the write operation
* needs to be the maximum number of bytes to be written rounded up
* to the nearest TBLOCK since readtape reads one block at a time.
*/
if ((buf = malloc(TBLOCKS(maxwrite) * TBLOCK)) == NULL) {
fatal(gettext("cannot allocate buffer"));
}
while (bytes > 0) {
/*
* readtape() obtains one block (TBLOCK) of data at a time.
* Accumulate as many blocks of data in buf as we can write
* in one operation.
*/
for (bytesread = 0; bytesread < maxwrite; bytesread += TBLOCK) {
readtape(buf + bytesread);
}
if (write(ofile, buf, maxwrite) < 0) {
int saveerrno = errno;
if (xhdr_flgs & _X_PATH)
(void) strlcpy(tempname, Xtarhdr.x_path,
sizeof (tempname));
else
(void) sprintf(tempname, "%.*s", NAMSIZ,
dblock.dbuf.name);
/*
* If the extended system attribute being extracted
* contains attributes that the user needs privileges
* for, then just display a warning message, skip
* the extraction of this file, and return.
*/
if ((saveerrno == EPERM) && issysattr) {
(void) fprintf(stderr, gettext(
"tar: unable to extract system attribute "
"%s: insufficient privileges\n"), tempname);
Errflg = 1;
(void) free(buf);
return (1);
} else {
(void) fprintf(stderr, gettext(
"tar: %s: HELP - extract write error\n"),
tempname);
done(2);
}
}
bytes -= maxwrite;
/*
* If we've reached this point and there is still data
* to be written, maxwrite had to have been determined
* by the preferred I/O size. If the number of bytes
* left to write is smaller than the preferred I/O size,
* then we're about to do our final write to the file, so
* just set maxwrite to the number of bytes left to write.
*/
if ((bytes > 0) && (bytes < maxwrite)) {
maxwrite = bytes;
}
}
free(buf);
return (0);
}
/*
* xsfile extract split file
*
* xsfile(ofd); ofd = output file descriptor
*
* file extracted and put in ofd via xblocks()
*
* NOTE: only called by doxtract() to extract one large file
*/
static union hblock savedblock; /* to ensure same file across volumes */
static int
xsfile(int issysattr, int ofd)
{
int i, c;
int sysattrerr = 0;
char name[PATH_MAX+1]; /* holds name for diagnostics */
int extents, totalext;
off_t bytes, totalbytes;
if (xhdr_flgs & _X_PATH)
(void) strcpy(name, Xtarhdr.x_path);
else
(void) sprintf(name, "%.*s", NAMSIZ, dblock.dbuf.name);
totalbytes = (off_t)0; /* in case we read in half the file */
totalext = 0; /* these keep count */
(void) fprintf(stderr, gettext(
"tar: %s split across %d volumes\n"), name, extotal);
/* make sure we do extractions in order */
if (extno != 1) { /* starting in middle of file? */
(void) printf(gettext(
"tar: first extent read is not #1\n"
"OK to read file beginning with extent #%d (%s/%s) ? "),
extno, yesstr, nostr);
if (yes() == 0) {
canit:
passtape();
if (close(ofd) != 0)
vperror(2, gettext("close error"));
if (sysattrerr) {
return (1);
} else {
return (0);
}
}
}
extents = extotal;
i = extno;
/*CONSTCOND*/
while (1) {
if (xhdr_flgs & _X_SIZE) {
bytes = extsize;
} else {
bytes = stbuf.st_size;
}
if (vflag)
(void) fprintf(vfile, "+++ x %s [%s #%d], %"
FMT_off_t " %s, %ldK\n",
name, gettext("extent"), extno,
bytes, gettext("bytes"),
(long)K(TBLOCKS(bytes)));
if (xblocks(issysattr, bytes, ofd) != 0) {
sysattrerr = 1;
goto canit;
}
totalbytes += bytes;
totalext++;
if (++i > extents)
break;
/* get next volume and verify it's the right one */
copy(&savedblock, &dblock);
tryagain:
newvol();
xhdr_flgs = 0;
getdir();
if (Xhdrflag > 0)
(void) get_xdata(); /* Get x-header & regular hdr */
if ((dblock.dbuf.typeflag != 'A') && (xhdr_flgs != 0)) {
load_info_from_xtarhdr(xhdr_flgs, &Xtarhdr);
xhdr_flgs |= _X_XHDR;
}
if (endtape()) { /* seemingly empty volume */
(void) fprintf(stderr, gettext(
"tar: first record is null\n"));
asknicely:
(void) fprintf(stderr, gettext(
"tar: need volume with extent #%d of %s\n"),
i, name);
goto tryagain;
}
if (notsame()) {
(void) fprintf(stderr, gettext(
"tar: first file on that volume is not "
"the same file\n"));
goto asknicely;
}
if (i != extno) {
(void) fprintf(stderr, gettext(
"tar: extent #%d received out of order\ntar: "
"should be #%d\n"), extno, i);
(void) fprintf(stderr, gettext(
"Ignore error, Abort this file, or "
"load New volume (i/a/n) ? "));
c = response();
if (c == 'a')
goto canit;
if (c != 'i') /* default to new volume */
goto asknicely;
i = extno; /* okay, start from there */
}
}
if (vflag)
(void) fprintf(vfile, gettext(
"x %s (in %d extents), %" FMT_off_t " bytes, %ldK\n"),
name, totalext, totalbytes, (long)K(TBLOCKS(totalbytes)));
return (0);
}
/*
* notsame() check if extract file extent is invalid
*
* returns true if anything differs between savedblock and dblock
* except extno (extent number), checksum, or size (extent size).
* Determines if this header belongs to the same file as the one we're
* extracting.
*
* NOTE: though rather bulky, it is only called once per file
* extension, and it can withstand changes in the definition
* of the header structure.
*
* WARNING: this routine is local to xsfile() above
*/
static int
notsame(void)
{
return (
(strncmp(savedblock.dbuf.name, dblock.dbuf.name, NAMSIZ)) ||
(strcmp(savedblock.dbuf.mode, dblock.dbuf.mode)) ||
(strcmp(savedblock.dbuf.uid, dblock.dbuf.uid)) ||
(strcmp(savedblock.dbuf.gid, dblock.dbuf.gid)) ||
(strcmp(savedblock.dbuf.mtime, dblock.dbuf.mtime)) ||
(savedblock.dbuf.typeflag != dblock.dbuf.typeflag) ||
(strncmp(savedblock.dbuf.linkname, dblock.dbuf.linkname, NAMSIZ)) ||
(savedblock.dbuf.extotal != dblock.dbuf.extotal) ||
(strcmp(savedblock.dbuf.efsize, dblock.dbuf.efsize)));
}
static void
dotable(char *argv[])
{
int tcnt = 0; /* count # files tabled */
int fcnt = 0; /* count # files in argv list */
char *namep, *dirp, *comp;
int want;
char aclchar = ' '; /* either blank or '+' */
char templink[PATH_MAX+1];
attr_data_t *attrinfo = NULL;
dumping = 0;
/* if not on magtape, maximize seek speed */
if (NotTape && !bflag) {
#if SYS_BLOCK > TBLOCK
nblock = SYS_BLOCK / TBLOCK;
#else
nblock = 1;
#endif
}
for (;;) {
/* namep is set by wantit to point to the full name */
if ((want = wantit(argv, &namep, &dirp, &comp, &attrinfo)) == 0)
continue;
if (want == -1)
break;
if (dblock.dbuf.typeflag != 'A')
++tcnt;
if (Fflag) {
if (checkf(namep, is_directory(namep), Fflag) == 0) {
passtape();
continue;
}
}
/*
* ACL support:
* aclchar is introduced to indicate if there are
* acl entries. longt() now takes one extra argument.
*/
if (vflag) {
if (dblock.dbuf.typeflag == 'A') {
aclchar = '+';
passtape();
continue;
}
longt(&stbuf, aclchar);
aclchar = ' ';
}
#if defined(O_XATTR)
if (xattrp != NULL) {
int issysattr;
char *bn = basename(attrinfo->attr_path);
/*
* We could use sysattr_type() to test whether or not
* the attribute we are processing is really an
* extended system attribute, which as of this writing
* just does a strcmp(), however, sysattr_type() may
* be changed to issue a pathconf() call instead, which
* would require being changed into the parent attribute
* directory. So instead, just do simple string
* comparisons to see if we are processing an extended
* system attribute.
*/
issysattr = is_sysattr(bn);
(void) printf(gettext("%s %sattribute %s"),
xattrp->h_names,
issysattr ? gettext("system ") : "",
attrinfo->attr_path);
} else {
(void) printf("%s", namep);
}
#else
(void) printf("%s", namep);
#endif
if (extno != 0) {
if (vflag) {
/* keep the '\n' for backwards compatibility */
(void) fprintf(vfile, gettext(
"\n [extent #%d of %d]"), extno, extotal);
} else {
(void) fprintf(vfile, gettext(
" [extent #%d of %d]"), extno, extotal);
}
}
if (xhdr_flgs & _X_LINKPATH) {
(void) strcpy(templink, Xtarhdr.x_linkpath);
} else {
#if defined(O_XATTR)
if (xattrp != NULL) {
(void) sprintf(templink,
"file %.*s", NAMSIZ, xattrp->h_names);
} else {
(void) sprintf(templink, "%.*s", NAMSIZ,
dblock.dbuf.linkname);
}
#else
(void) sprintf(templink, "%.*s", NAMSIZ,
dblock.dbuf.linkname);
#endif
templink[NAMSIZ] = '\0';
}
if (dblock.dbuf.typeflag == '1') {
/*
* TRANSLATION_NOTE
* Subject is omitted here.
* Translate this as if
* <subject> linked to %s
*/
#if defined(O_XATTR)
if (xattrp != NULL) {
(void) printf(
gettext(" linked to attribute %s"),
xattr_linkp->h_names +
strlen(xattr_linkp->h_names) + 1);
} else {
(void) printf(
gettext(" linked to %s"), templink);
}
#else
(void) printf(
gettext(" linked to %s"), templink);
#endif
}
if (dblock.dbuf.typeflag == '2')
(void) printf(gettext(
/*
* TRANSLATION_NOTE
* Subject is omitted here.
* Translate this as if
* <subject> symbolic link to %s
*/
" symbolic link to %s"), templink);
(void) printf("\n");
#if defined(O_XATTR)
if (xattrp != NULL) {
free(xattrhead);
xattrp = NULL;
xattrhead = NULL;
}
#endif
passtape();
}
/*
* Check if the number of files tabled is different from the
* number of files listed on the command line
*/
if (fcnt > tcnt) {
(void) fprintf(stderr, gettext(
"tar: %d file(s) not found\n"), fcnt-tcnt);
Errflg = 1;
}
}
static void
putempty(blkcnt_t n)
{
char buf[TBLOCK];
char *cp;
for (cp = buf; cp < &buf[TBLOCK]; )
*cp++ = '\0';
while (n-- > 0)
(void) writetbuf(buf, 1);
}
static ushort_t Ftype = S_IFMT;
static void
verbose(struct stat *st, char aclchar)
{
int i, j, temp;
mode_t mode;
char modestr[12];
for (i = 0; i < 11; i++)
modestr[i] = '-';
modestr[i] = '\0';
/* a '+' sign is printed if there is ACL */
modestr[i-1] = aclchar;
mode = st->st_mode;
for (i = 0; i < 3; i++) {
temp = (mode >> (6 - (i * 3)));
j = (i * 3) + 1;
if (S_IROTH & temp)
modestr[j] = 'r';
if (S_IWOTH & temp)
modestr[j + 1] = 'w';
if (S_IXOTH & temp)
modestr[j + 2] = 'x';
}
temp = st->st_mode & Ftype;
switch (temp) {
case (S_IFIFO):
modestr[0] = 'p';
break;
case (S_IFCHR):
modestr[0] = 'c';
break;
case (S_IFDIR):
modestr[0] = 'd';
break;
case (S_IFBLK):
modestr[0] = 'b';
break;
case (S_IFREG): /* was initialized to '-' */
break;
case (S_IFLNK):
modestr[0] = 'l';
break;
default:
/* This field may be zero in old archives. */
if (is_posix && dblock.dbuf.typeflag != '1') {
/*
* For POSIX compliant archives, the mode field
* consists of 12 bits, ie: the file type bits
* are not stored in dblock.dbuf.mode.
* For files other than hard links, getdir() sets
* the file type bits in the st_mode field of the
* stat structure based upon dblock.dbuf.typeflag.
*/
(void) fprintf(stderr, gettext(
"tar: impossible file type"));
}
}
if ((S_ISUID & Gen.g_mode) == S_ISUID)
modestr[3] = 's';
if ((S_ISVTX & Gen.g_mode) == S_ISVTX)
modestr[9] = 't';
if ((S_ISGID & Gen.g_mode) == S_ISGID && modestr[6] == 'x')
modestr[6] = 's';
else if ((S_ENFMT & Gen.g_mode) == S_ENFMT && modestr[6] != 'x')
modestr[6] = 'l';
(void) fprintf(vfile, "%s", modestr);
}
static void
longt(struct stat *st, char aclchar)
{
char fileDate[30];
struct tm *tm;
verbose(st, aclchar);
(void) fprintf(vfile, "%3ld/%-3ld", st->st_uid, st->st_gid);
if (dblock.dbuf.typeflag == '2') {
if (xhdr_flgs & _X_LINKPATH)
st->st_size = (off_t)strlen(Xtarhdr.x_linkpath);
else
st->st_size = (off_t)(memchr(dblock.dbuf.linkname,
'\0', NAMSIZ) ?
(strlen(dblock.dbuf.linkname)) : (NAMSIZ));
}
(void) fprintf(vfile, " %6" FMT_off_t, st->st_size);
tm = localtime(&(st->st_mtime));
(void) strftime(fileDate, sizeof (fileDate),
dcgettext((const char *)0, "%b %e %R %Y", LC_TIME), tm);
(void) fprintf(vfile, " %s ", fileDate);
}
/*
* checkdir - Attempt to ensure that the path represented in name
* exists, and return 1 if this is true and name itself is a
* directory.
* Return 0 if this path cannot be created or if name is not
* a directory.
*/
static int
checkdir(char *name)
{
char lastChar; /* the last character in name */
char *cp; /* scratch pointer into name */
char *firstSlash = NULL; /* first slash in name */
char *lastSlash = NULL; /* last slash in name */
int nameLen; /* length of name */
int trailingSlash; /* true if name ends in slash */
int leadingSlash; /* true if name begins with slash */
int markedDir; /* true if name denotes a directory */
int success; /* status of makeDir call */
/*
* Scan through the name, and locate first and last slashes.
*/
for (cp = name; *cp; cp++) {
if (*cp == '/') {
if (! firstSlash) {
firstSlash = cp;
}
lastSlash = cp;
}
}
/*
* Determine what you can from the proceeds of the scan.
*/
lastChar = *(cp - 1);
nameLen = (int)(cp - name);
trailingSlash = (lastChar == '/');
leadingSlash = (*name == '/');
markedDir = (dblock.dbuf.typeflag == '5' || trailingSlash);
if (! lastSlash && ! markedDir) {
/*
* The named file does not have any subdrectory
* structure; just bail out.
*/
return (0);
}
/*
* Make sure that name doesn`t end with slash for the loop.
* This ensures that the makeDir attempt after the loop is
* meaningful.
*/
if (trailingSlash) {
name[nameLen-1] = '\0';
}
/*
* Make the path one component at a time.
*/
for (cp = strchr(leadingSlash ? name+1 : name, '/');
cp;
cp = strchr(cp+1, '/')) {
*cp = '\0';
success = makeDir(name);
*cp = '/';
if (!success) {
name[nameLen-1] = lastChar;
return (0);
}
}
/*
* This makes the last component of the name, if it is a
* directory.
*/
if (markedDir) {
if (! makeDir(name)) {
name[nameLen-1] = lastChar;
return (0);
}
}
name[nameLen-1] = (lastChar == '/') ? '\0' : lastChar;
return (markedDir);
}
/*
* resugname - Restore the user name and group name. Search the NIS
* before using the uid and gid.
* (It is presumed that an archive entry cannot be
* simultaneously a symlink and some other type.)
*/
static void
resugname(int dirfd, /* dir fd file resides in */
char *name, /* name of the file to be modified */
int symflag) /* true if file is a symbolic link */
{
uid_t duid;
gid_t dgid;
struct stat *sp = &stbuf;
char *u_g_name;
if (checkflag == 1) { /* Extended tar format and euid == 0 */
/*
* Try and extract the intended uid and gid from the name
* service before believing the uid and gid in the header.
*
* In the case where we archived a setuid or setgid file
* owned by someone with a large uid, then it will
* have made it into the archive with a uid of nobody. If
* the corresponding username doesn't appear to exist, then we
* want to make sure it *doesn't* end up as setuid nobody!
*
* Our caller will print an error message about the fact
* that the restore didn't work out quite right ..
*/
if (xhdr_flgs & _X_UNAME)
u_g_name = Xtarhdr.x_uname;
else
u_g_name = dblock.dbuf.uname;
if ((duid = getuidbyname(u_g_name)) == -1) {
if (S_ISREG(sp->st_mode) && sp->st_uid == UID_NOBODY &&
(sp->st_mode & S_ISUID) == S_ISUID)
(void) chmod(name,
MODEMASK & sp->st_mode & ~S_ISUID);
duid = sp->st_uid;
}
/* (Ditto for gids) */
if (xhdr_flgs & _X_GNAME)
u_g_name = Xtarhdr.x_gname;
else
u_g_name = dblock.dbuf.gname;
if ((dgid = getgidbyname(u_g_name)) == -1) {
if (S_ISREG(sp->st_mode) && sp->st_gid == GID_NOBODY &&
(sp->st_mode & S_ISGID) == S_ISGID)
(void) chmod(name,
MODEMASK & sp->st_mode & ~S_ISGID);
dgid = sp->st_gid;
}
} else if (checkflag == 2) { /* tar format and euid == 0 */
duid = sp->st_uid;
dgid = sp->st_gid;
}
if ((checkflag == 1) || (checkflag == 2))
(void) fchownat(dirfd, name, duid, dgid, symflag);
}
/*ARGSUSED*/
static void
onintr(int sig)
{
(void) signal(SIGINT, SIG_IGN);
term++;
}
/*ARGSUSED*/
static void
onquit(int sig)
{
(void) signal(SIGQUIT, SIG_IGN);
term++;
}
/*ARGSUSED*/
static void
onhup(int sig)
{
(void) signal(SIGHUP, SIG_IGN);
term++;
}
static void
tomodes(struct stat *sp)
{
uid_t uid;
gid_t gid;
bzero(dblock.dummy, TBLOCK);
/*
* If the uid or gid is too large, we can't put it into
* the archive. We could fail to put anything in the
* archive at all .. but most of the time the name service
* will save the day when we do a lookup at restore time.
*
* Instead we choose a "safe" uid and gid, and fix up whether
* or not the setuid and setgid bits are left set to extraction
* time.
*/
if (Eflag) {
if ((ulong_t)(uid = sp->st_uid) > (ulong_t)OCTAL7CHAR) {
xhdr_flgs |= _X_UID;
Xtarhdr.x_uid = uid;
}
if ((ulong_t)(gid = sp->st_gid) > (ulong_t)OCTAL7CHAR) {
xhdr_flgs |= _X_GID;
Xtarhdr.x_gid = gid;
}
if (sp->st_size > TAR_OFFSET_MAX) {
xhdr_flgs |= _X_SIZE;
Xtarhdr.x_filesz = sp->st_size;
(void) sprintf(dblock.dbuf.size, "%011" FMT_off_t_o,
(off_t)0);
} else
(void) sprintf(dblock.dbuf.size, "%011" FMT_off_t_o,
sp->st_size);
} else {
(void) sprintf(dblock.dbuf.size, "%011" FMT_off_t_o,
sp->st_size);
}
if ((ulong_t)(uid = sp->st_uid) > (ulong_t)OCTAL7CHAR)
uid = UID_NOBODY;
if ((ulong_t)(gid = sp->st_gid) > (ulong_t)OCTAL7CHAR)
gid = GID_NOBODY;
(void) sprintf(dblock.dbuf.gid, "%07lo", gid);
(void) sprintf(dblock.dbuf.uid, "%07lo", uid);
(void) sprintf(dblock.dbuf.mode, "%07lo", sp->st_mode & POSIXMODES);
(void) sprintf(dblock.dbuf.mtime, "%011lo", sp->st_mtime);
}
static int
#ifdef EUC
/*
* Warning: the result of this function depends whether 'char' is a
* signed or unsigned data type. This a source of potential
* non-portability among heterogeneous systems. It is retained here
* for backward compatibility.
*/
checksum_signed(union hblock *dblockp)
#else
checksum(union hblock *dblockp)
#endif /* EUC */
{
int i;
char *cp;
for (cp = dblockp->dbuf.chksum;
cp < &dblockp->dbuf.chksum[sizeof (dblockp->dbuf.chksum)]; cp++)
*cp = ' ';
i = 0;
for (cp = dblockp->dummy; cp < &(dblockp->dummy[TBLOCK]); cp++)
i += *cp;
return (i);
}
#ifdef EUC
/*
* Generate unsigned checksum, regardless of what C compiler is
* used. Survives in the face of arbitrary 8-bit clean filenames,
* e.g., internationalized filenames.
*/
static int
checksum(union hblock *dblockp)
{
unsigned i;
unsigned char *cp;
for (cp = (unsigned char *) dblockp->dbuf.chksum;
cp < (unsigned char *)
&(dblockp->dbuf.chksum[sizeof (dblockp->dbuf.chksum)]); cp++)
*cp = ' ';
i = 0;
for (cp = (unsigned char *) dblockp->dummy;
cp < (unsigned char *) &(dblockp->dummy[TBLOCK]); cp++)
i += *cp;
return (i);
}
#endif /* EUC */
/*
* If the w flag is set, output the action to be taken and the name of the
* file. Perform the action if the user response is affirmative.
*/
static int
checkw(char c, char *name)
{
if (wflag) {
(void) fprintf(vfile, "%c ", c);
if (vflag)
longt(&stbuf, ' '); /* do we have acl info here */
(void) fprintf(vfile, "%s: ", name);
if (yes() == 1) {
return (1);
}
return (0);
}
return (1);
}
/*
* When the F flag is set, exclude RCS and SCCS directories (and any files
* or directories under them). If F is set twice, also exclude .o files,
* and files names errs, core, and a.out.
*
* Return 0 if file should be excluded, 1 otherwise.
*/
static int
checkf(char *longname, int is_dir, int howmuch)
{
static char fullname[PATH_MAX + 1];
char *dir, *name;
#if defined(O_XATTR)
/*
* If there is an xattr_buf structure associated with this file,
* always return 1.
*/
if (xattrp) {
return (1);
}
#endif
/*
* First check to see if the base name is an RCS or SCCS directory.
*/
if (strlcpy(fullname, longname, sizeof (fullname)) >= sizeof (fullname))
return (1);
name = basename(fullname);
if (is_dir) {
if ((strcmp(name, "SCCS") == 0) || (strcmp(name, "RCS") == 0))
return (0);
}
/*
* If two -F command line options were given then exclude .o files,
* and files named errs, core, and a.out.
*/
if (howmuch > 1 && !is_dir) {
size_t l = strlen(name);
if (l >= 3 && name[l - 2] == '.' && name[l - 1] == 'o')
return (0);
if (strcmp(name, "core") == 0 || strcmp(name, "errs") == 0 ||
strcmp(name, "a.out") == 0)
return (0);
}
/*
* At this point, check to see if this file has a parent directory
* named RCS or SCCS. If so, then this file should be excluded too.
* The strcpy() operation is done again, because basename(3C) may
* modify the path string passed to it.
*/
if (strlcpy(fullname, longname, sizeof (fullname)) >= sizeof (fullname))
return (1);
dir = dirname(fullname);
while (strcmp(dir, ".") != 0) {
name = basename(dir);
if ((strcmp(name, "SCCS") == 0) || (strcmp(name, "RCS") == 0))
return (0);
dir = dirname(dir);
}
return (1);
}
static int
response(void)
{
int c;
c = getchar();
if (c != '\n')
while (getchar() != '\n')
;
else c = 'n';
return ((c >= 'A' && c <= 'Z') ? c + ('a'-'A') : c);
}
/* Has file been modified since being put into archive? If so, return > 0. */
static off_t lookup(char *);
static int
checkupdate(char *arg)
{
char name[PATH_MAX+1];
time_t mtime;
long nsecs;
off_t seekp;
rewind(tfile);
if ((seekp = lookup(arg)) < 0)
return (1);
(void) fseek(tfile, seekp, 0);
(void) fscanf(tfile, "%s %ld.%ld", name, &mtime, &nsecs);
/*
* Unless nanoseconds were stored in the file, only use seconds for
* comparison of time. Nanoseconds are stored when -E is specified.
*/
if (Eflag == 0)
return (stbuf.st_mtime > mtime);
if ((stbuf.st_mtime < mtime) ||
((stbuf.st_mtime == mtime) && (stbuf.st_mtim.tv_nsec <= nsecs)))
return (0);
return (1);
}
/*
* newvol get new floppy (or tape) volume
*
* newvol(); resets tapepos and first to TRUE, prompts for
* for new volume, and waits.
* if dumping, end-of-file is written onto the tape.
*/
static void
newvol(void)
{
int c;
if (dumping) {
dlog("newvol called with 'dumping' set\n");
putempty((blkcnt_t)2); /* 2 EOT marks */
closevol();
flushtape();
sync();
tapepos = 0;
} else
first = TRUE;
if (close(mt) != 0)
vperror(2, gettext("close error"));
mt = 0;
(void) fprintf(stderr, gettext(
"tar: \007please insert new volume, then press RETURN."));
(void) fseek(stdin, (off_t)0, 2); /* scan over read-ahead */
while ((c = getchar()) != '\n' && ! term)
if (c == EOF)
done(Errflg);
if (term)
done(Errflg);
errno = 0;
if (strcmp(usefile, "-") == 0) {
mt = dup(1);
} else {
mt = open(usefile, dumping ? update : 0);
}
if (mt < 0) {
(void) fprintf(stderr, gettext(
"tar: cannot reopen %s (%s)\n"),
dumping ? gettext("output") : gettext("input"), usefile);
dlog("update=%d, usefile=%s ", update, usefile);
dlog("mt=%d, [%s]\n", mt, strerror(errno));
done(2);
}
}
/*
* Write a trailer portion to close out the current output volume.
*/
static void
closevol(void)
{
if (mulvol) {
/*
* blocklim does not count the 2 EOT marks;
* tapepos does count the 2 EOT marks;
* therefore we need the +2 below.
*/
putempty(blocklim + (blkcnt_t)2 - tapepos);
}
}
static void
done(int n)
{
/*
* If we were terminated in some way, and we would otherwise have
* exited with a value of 0, adjust to 1, so that external callers
* can determine this by looking at the exit status.
*/
if (term && n == 0)
n = 1;
if (tfile != NULL)
(void) unlink(tname);
if (compress_opt != NULL)
(void) free(compress_opt);
if (mt > 0) {
if ((close(mt) != 0) || (fclose(stdout) != 0)) {
perror(gettext("tar: close error"));
exit(2);
}
}
/*
* If we have a compression child, we should have a child process that
* we're waiting for to finish compressing or uncompressing the tar
* stream.
*/
if (comp_pid != 0)
wait_pid(comp_pid);
exit(n);
}
/*
* Determine if s1 is a prefix portion of s2 (or the same as s2).
*/
static int
is_prefix(char *s1, char *s2)
{
while (*s1)
if (*s1++ != *s2++)
return (0);
if (*s2)
return (*s2 == '/');
return (1);
}
/*
* lookup and bsrch look through tfile entries to find a match for a name.
* The name can be up to PATH_MAX bytes. bsrch compares what it sees between
* a pair of newline chars, so the buffer it uses must be long enough for
* two lines: name and modification time as well as period, newline and space.
*
* A kludge was added to bsrch to take care of matching on the first entry
* in the file--there is no leading newline. So, if we are reading from the
* start of the file, read into byte two and set the first byte to a newline.
* Otherwise, the first entry cannot be matched.
*
*/
#define N (2 * (PATH_MAX + TIME_MAX_DIGITS + LONG_MAX_DIGITS + 3))
static off_t
lookup(char *s)
{
int i;
off_t a;
for (i = 0; s[i]; i++)
if (s[i] == ' ')
break;
a = bsrch(s, i, low, high);
return (a);
}
static off_t
bsrch(char *s, int n, off_t l, off_t h)
{
int i, j;
char b[N];
off_t m, m1;
loop:
if (l >= h)
return ((off_t)-1);
m = l + (h-l)/2 - N/2;
if (m < l)
m = l;
(void) fseek(tfile, m, 0);
if (m == 0) {
(void) fread(b+1, 1, N-1, tfile);
b[0] = '\n';
m--;
} else
(void) fread(b, 1, N, tfile);
for (i = 0; i < N; i++) {
if (b[i] == '\n')
break;
m++;
}
if (m >= h)
return ((off_t)-1);
m1 = m;
j = i;
for (i++; i < N; i++) {
m1++;
if (b[i] == '\n')
break;
}
i = cmp(b+j, s, n);
if (i < 0) {
h = m;
goto loop;
}
if (i > 0) {
l = m1;
goto loop;
}
if (m < 0)
m = 0;
return (m);
}
static int
cmp(char *b, char *s, int n)
{
int i;
assert(b[0] == '\n');
for (i = 0; i < n; i++) {
if (b[i+1] > s[i])
return (-1);
if (b[i+1] < s[i])
return (1);
}
return (b[i+1] == ' '? 0 : -1);
}
/*
* seekdisk seek to next file on archive
*
* called by passtape() only
*
* WARNING: expects "nblock" to be set, that is, readtape() to have
* already been called. Since passtape() is only called
* after a file header block has been read (why else would
* we skip to next file?), this is currently safe.
*
* changed to guarantee SYS_BLOCK boundary
*/
static void
seekdisk(blkcnt_t blocks)
{
off_t seekval;
#if SYS_BLOCK > TBLOCK
/* handle non-multiple of SYS_BLOCK */
blkcnt_t nxb; /* # extra blocks */
#endif
tapepos += blocks;
dlog("seekdisk(%" FMT_blkcnt_t ") called\n", blocks);
if (recno + blocks <= nblock) {
recno += blocks;
return;
}
if (recno > nblock)
recno = nblock;
seekval = (off_t)blocks - (nblock - recno);
recno = nblock; /* so readtape() reads next time through */
#if SYS_BLOCK > TBLOCK
nxb = (blkcnt_t)(seekval % (off_t)(SYS_BLOCK / TBLOCK));
dlog("xtrablks=%" FMT_blkcnt_t " seekval=%" FMT_blkcnt_t " blks\n",
nxb, seekval);
if (nxb && nxb > seekval) /* don't seek--we'll read */
goto noseek;
seekval -= nxb; /* don't seek quite so far */
#endif
if (lseek(mt, (off_t)(TBLOCK * seekval), 1) == (off_t)-1) {
(void) fprintf(stderr, gettext(
"tar: device seek error\n"));
done(3);
}
#if SYS_BLOCK > TBLOCK
/* read those extra blocks */
noseek:
if (nxb) {
dlog("reading extra blocks\n");
if (read(mt, tbuf, TBLOCK*nblock) < 0) {
(void) fprintf(stderr, gettext(
"tar: read error while skipping file\n"));
done(8);
}
recno = nxb; /* so we don't read in next readtape() */
}
#endif
}
static void
readtape(char *buffer)
{
int i, j;
++tapepos;
if (recno >= nblock || first) {
if (first) {
/*
* set the number of blocks to read initially, based on
* the defined defaults for the device, or on the
* explicit block factor given.
*/
if (bflag || defaults_used || NotTape)
j = nblock;
else
j = NBLOCK;
} else
j = nblock;
if ((i = read(mt, tbuf, TBLOCK*j)) < 0) {
(void) fprintf(stderr, gettext(
"tar: tape read error\n"));
done(3);
/*
* i == 0 and !rflag means that EOF is reached and we are
* trying to update or replace an empty tar file, so exit
* with an error.
*
* If i == 0 and !first and NotTape, it means the pointer
* has gone past the EOF. It could happen if two processes
* try to update the same tar file simultaneously. So exit
* with an error.
*/
} else if (i == 0) {
if (first && !rflag) {
(void) fprintf(stderr, gettext(
"tar: blocksize = %d\n"), i);
done(Errflg);
} else if (!first && (!rflag || NotTape)) {
mterr("read", 0, 2);
}
} else if ((!first || Bflag) && i != TBLOCK*j) {
/*
* Short read - try to get the remaining bytes.
*/
int remaining = (TBLOCK * j) - i;
char *b = (char *)tbuf + i;
int r;
do {
if ((r = read(mt, b, remaining)) < 0) {
(void) fprintf(stderr,
gettext("tar: tape read error\n"));
done(3);
}
b += r;
remaining -= r;
i += r;
} while (remaining > 0 && r != 0);
}
if (first) {
if ((i % TBLOCK) != 0) {
(void) fprintf(stderr, gettext(
"tar: tape blocksize error\n"));
done(3);
}
i /= TBLOCK;
if (vflag && i != nblock && i != 1) {
if (!NotTape)
(void) fprintf(stderr, gettext(
"tar: blocksize = %d\n"), i);
}
/*
* If we are reading a tape, then a short read is
* understood to signify that the amount read is
* the tape's actual blocking factor. We adapt
* nblock accordingly. There is no reason to do
* this when the device is not blocked.
*/
if (!NotTape)
nblock = i;
}
recno = 0;
}
first = FALSE;
copy(buffer, &tbuf[recno++]);
}
/*
* replacement for writetape.
*/
static int
writetbuf(char *buffer, int n)
{
int i;
tapepos += n; /* output block count */
if (recno >= nblock) {
i = write(mt, (char *)tbuf, TBLOCK*nblock);
if (i != TBLOCK*nblock)
mterr("write", i, 2);
recno = 0;
}
/*
* Special case: We have an empty tape buffer, and the
* users data size is >= the tape block size: Avoid
* the bcopy and dma direct to tape. BIG WIN. Add the
* residual to the tape buffer.
*/
while (recno == 0 && n >= nblock) {
i = (int)write(mt, buffer, TBLOCK*nblock);
if (i != TBLOCK*nblock)
mterr("write", i, 2);
n -= nblock;
buffer += (nblock * TBLOCK);
}
while (n-- > 0) {
(void) memcpy((char *)&tbuf[recno++], buffer, TBLOCK);
buffer += TBLOCK;
if (recno >= nblock) {
i = (int)write(mt, (char *)tbuf, TBLOCK*nblock);
if (i != TBLOCK*nblock)
mterr("write", i, 2);
recno = 0;
}
}
/* Tell the user how much to write to get in sync */
return (nblock - recno);
}
/*
* backtape - reposition tape after reading soft "EOF" record
*
* Backtape tries to reposition the tape back over the EOF
* record. This is for the 'u' and 'r' function letters so that the
* tape can be extended. This code is not well designed, but
* I'm confident that the only callers who care about the
* backspace-over-EOF feature are those involved in 'u' and 'r'.
*
* The proper way to backup the tape is through the use of mtio.
* Earlier spins used lseek combined with reads in a confusing
* maneuver that only worked on 4.x, but shouldn't have, even
* there. Lseeks are explicitly not supported for tape devices.
*/
static void
backtape(void)
{
struct mtop mtcmd;
dlog("backtape() called, recno=%" FMT_blkcnt_t " nblock=%d\n", recno,
nblock);
/*
* Backup to the position in the archive where the record
* currently sitting in the tbuf buffer is situated.
*/
if (NotTape) {
/*
* For non-tape devices, this means lseeking to the
* correct position. The absolute location tapepos-recno
* should be the beginning of the current record.
*/
if (lseek(mt, (off_t)(TBLOCK*(tapepos-recno)), SEEK_SET) ==
(off_t)-1) {
(void) fprintf(stderr,
gettext("tar: lseek to end of archive failed\n"));
done(4);
}
} else {
/*
* For tape devices, we backup over the most recently
* read record.
*/
mtcmd.mt_op = MTBSR;
mtcmd.mt_count = 1;
if (ioctl(mt, MTIOCTOP, &mtcmd) < 0) {
(void) fprintf(stderr,
gettext("tar: backspace over record failed\n"));
done(4);
}
}
/*
* Decrement the tape and tbuf buffer indices to prepare for the
* coming write to overwrite the soft EOF record.
*/
recno--;
tapepos--;
}
/*
* flushtape write buffered block(s) onto tape
*
* recno points to next free block in tbuf. If nonzero, a write is done.
* Care is taken to write in multiples of SYS_BLOCK when device is
* non-magtape in case raw i/o is used.
*
* NOTE: this is called by writetape() to do the actual writing
*/
static void
flushtape(void)
{
dlog("flushtape() called, recno=%" FMT_blkcnt_t "\n", recno);
if (recno > 0) { /* anything buffered? */
if (NotTape) {
#if SYS_BLOCK > TBLOCK
int i;
/*
* an odd-block write can only happen when
* we are at the end of a volume that is not a tape.
* Here we round recno up to an even SYS_BLOCK
* boundary.
*/
if ((i = recno % (SYS_BLOCK / TBLOCK)) != 0) {
dlog("flushtape() %d rounding blocks\n", i);
recno += i; /* round up to even SYS_BLOCK */
}
#endif
if (recno > nblock)
recno = nblock;
}
dlog("writing out %" FMT_blkcnt_t " blocks of %" FMT_blkcnt_t
" bytes\n", (blkcnt_t)(NotTape ? recno : nblock),
(blkcnt_t)(NotTape ? recno : nblock) * TBLOCK);
if (write(mt, tbuf,
(size_t)(NotTape ? recno : nblock) * TBLOCK) < 0) {
(void) fprintf(stderr, gettext(
"tar: tape write error\n"));
done(2);
}
recno = 0;
}
}
static void
copy(void *dst, void *src)
{
(void) memcpy(dst, src, TBLOCK);
}
/*
* kcheck()
* - checks the validity of size values for non-tape devices
* - if size is zero, mulvol tar is disabled and size is
* assumed to be infinite.
* - returns volume size in TBLOCKS
*/
static blkcnt_t
kcheck(char *kstr)
{
blkcnt_t kval;
kval = strtoll(kstr, NULL, 0);
if (kval == (blkcnt_t)0) { /* no multi-volume; size is infinity. */
mulvol = 0; /* definitely not mulvol, but we must */
return (0); /* took out setting of NotTape */
}
if (kval < (blkcnt_t)MINSIZE) {
(void) fprintf(stderr, gettext(
"tar: sizes below %luK not supported (%" FMT_blkcnt_t
").\n"), (ulong_t)MINSIZE, kval);
(void) fprintf(stderr, gettext(
"bad size entry for %s in %s.\n"),
archive, DEF_FILE);
done(1);
}
mulvol++;
NotTape++; /* implies non-tape */
return (kval * 1024 / TBLOCK); /* convert to TBLOCKS */
}
/*
* bcheck()
* - checks the validity of blocking factors
* - returns blocking factor
*/
static int
bcheck(char *bstr)
{
blkcnt_t bval;
bval = strtoll(bstr, NULL, 0);
if ((bval <= 0) || (bval > INT_MAX / TBLOCK)) {
(void) fprintf(stderr, gettext(
"tar: invalid blocksize \"%s\".\n"), bstr);
if (!bflag)
(void) fprintf(stderr, gettext(
"bad blocksize entry for '%s' in %s.\n"),
archive, DEF_FILE);
done(1);
}
return ((int)bval);
}
/*
* defset()
* - reads DEF_FILE for the set of default values specified.
* - initializes 'usefile', 'nblock', and 'blocklim', and 'NotTape'.
* - 'usefile' points to static data, so will be overwritten
* if this routine is called a second time.
* - the pattern specified by 'arch' must be followed by four
* blank-separated fields (1) device (2) blocking,
* (3) size(K), and (4) tape
* for example: archive0=/dev/fd 1 400 n
*/
static int
defset(char *arch)
{
char *bp;
if (defopen(DEF_FILE) != 0)
return (FALSE);
if (defcntl(DC_SETFLAGS, (DC_STD & ~(DC_CASE))) == -1) {
(void) fprintf(stderr, gettext(
"tar: error setting parameters for %s.\n"), DEF_FILE);
return (FALSE); /* & following ones too */
}
if ((bp = defread(arch)) == NULL) {
(void) fprintf(stderr, gettext(
"tar: missing or invalid '%s' entry in %s.\n"),
arch, DEF_FILE);
return (FALSE);
}
if ((usefile = strtok(bp, " \t")) == NULL) {
(void) fprintf(stderr, gettext(
"tar: '%s' entry in %s is empty!\n"), arch, DEF_FILE);
return (FALSE);
}
if ((bp = strtok(NULL, " \t")) == NULL) {
(void) fprintf(stderr, gettext(
"tar: block component missing in '%s' entry in %s.\n"),
arch, DEF_FILE);
return (FALSE);
}
nblock = bcheck(bp);
if ((bp = strtok(NULL, " \t")) == NULL) {
(void) fprintf(stderr, gettext(
"tar: size component missing in '%s' entry in %s.\n"),
arch, DEF_FILE);
return (FALSE);
}
blocklim = kcheck(bp);
if ((bp = strtok(NULL, " \t")) != NULL)
NotTape = (*bp == 'n' || *bp == 'N');
else
NotTape = (blocklim != 0);
(void) defopen(NULL);
dlog("defset: archive='%s'; usefile='%s'\n", arch, usefile);
dlog("defset: nblock='%d'; blocklim='%" FMT_blkcnt_t "'\n",
nblock, blocklim);
dlog("defset: not tape = %d\n", NotTape);
return (TRUE);
}
/*
* Following code handles excluded and included files.
* A hash table of file names to be {in,ex}cluded is built.
* For excluded files, before writing or extracting a file
* check to see if it is in the exclude_tbl.
* For included files, the wantit() procedure will check to
* see if the named file is in the include_tbl.
*/
static void
build_table(file_list_t *table[], char *file)
{
FILE *fp;
char buf[PATH_MAX + 1];
if ((fp = fopen(file, "r")) == (FILE *)NULL)
vperror(1, gettext("could not open %s"), file);
while (fgets(buf, sizeof (buf), fp) != NULL) {
if (buf[strlen(buf) - 1] == '\n')
buf[strlen(buf) - 1] = '\0';
/* Only add to table if line has something in it */
if (strspn(buf, " \t") != strlen(buf))
add_file_to_table(table, buf);
}
(void) fclose(fp);
}
/*
* Add a file name to the the specified table, if the file name has any
* trailing '/'s then delete them before inserting into the table
*/
static void
add_file_to_table(file_list_t *table[], char *str)
{
char name[PATH_MAX + 1];
unsigned int h;
file_list_t *exp;
(void) strcpy(name, str);
while (name[strlen(name) - 1] == '/') {
name[strlen(name) - 1] = NULL;
}
h = hash(name);
if ((exp = (file_list_t *)calloc(sizeof (file_list_t),
sizeof (char))) == NULL) {
(void) fprintf(stderr, gettext(
"tar: out of memory, exclude/include table(entry)\n"));
exit(1);
}
if ((exp->name = strdup(name)) == NULL) {
(void) fprintf(stderr, gettext(
"tar: out of memory, exclude/include table(file name)\n"));
exit(1);
}
exp->next = table[h];
table[h] = exp;
}
/*
* See if a file name or any of the file's parent directories is in the
* specified table, if the file name has any trailing '/'s then delete
* them before searching the table
*/
static int
is_in_table(file_list_t *table[], char *str)
{
char name[PATH_MAX + 1];
unsigned int h;
file_list_t *exp;
char *ptr;
(void) strcpy(name, str);
while (name[strlen(name) - 1] == '/') {
name[strlen(name) - 1] = NULL;
}
/*
* check for the file name in the passed list
*/
h = hash(name);
exp = table[h];
while (exp != NULL) {
if (strcmp(name, exp->name) == 0) {
return (1);
}
exp = exp->next;
}
/*
* check for any parent directories in the file list
*/
while ((ptr = strrchr(name, '/'))) {
*ptr = NULL;
h = hash(name);
exp = table[h];
while (exp != NULL) {
if (strcmp(name, exp->name) == 0) {
return (1);
}
exp = exp->next;
}
}
return (0);
}
/*
* Compute a hash from a string.
*/
static unsigned int
hash(char *str)
{
char *cp;
unsigned int h;
h = 0;
for (cp = str; *cp; cp++) {
h += *cp;
}
return (h % TABLE_SIZE);
}
static void *
getmem(size_t size)
{
void *p = calloc((unsigned)size, sizeof (char));
if (p == NULL && freemem) {
(void) fprintf(stderr, gettext(
"tar: out of memory, link and directory modtime "
"info lost\n"));
freemem = 0;
if (errflag)
done(1);
else
Errflg = 1;
}
return (p);
}
/*
* vperror() --variable argument perror.
* Takes 3 args: exit_status, formats, args. If exit_status is 0, then
* the errflag (exit on error) is checked -- if it is non-zero, tar exits
* with the value of whatever "errno" is set to. If exit_status is not
* zero, then tar exits with that error status. If errflag and exit_status
* are both zero, the routine returns to where it was called and sets Errflg
* to errno.
*/
static void
vperror(int exit_status, char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
(void) fputs("tar: ", stderr);
(void) vfprintf(stderr, fmt, ap);
(void) fprintf(stderr, ": %s\n", strerror(errno));
va_end(ap);
if (exit_status)
done(exit_status);
else
if (errflag)
done(errno);
else
Errflg = errno;
}
static void
fatal(char *format, ...)
{
va_list ap;
va_start(ap, format);
(void) fprintf(stderr, "tar: ");
(void) vfprintf(stderr, format, ap);
(void) fprintf(stderr, "\n");
va_end(ap);
done(1);
}
/*
* Check to make sure that argument is a char * ptr.
* Actually, we just check to see that it is non-null.
* If it is null, print out the message and call usage(), bailing out.
*/
static void
assert_string(char *s, char *msg)
{
if (s == NULL) {
(void) fprintf(stderr, msg);
usage();
}
}
static void
mterr(char *operation, int i, int exitcode)
{
(void) fprintf(stderr, gettext(
"tar: %s error: "), operation);
if (i < 0)
perror("");
else
(void) fprintf(stderr, gettext("unexpected EOF\n"));
done(exitcode);
}
static int
wantit(char *argv[], char **namep, char **dirp, char **component,
attr_data_t **attrinfo)
{
char **cp;
int gotit; /* true if we've found a match */
int ret;
top:
if (xhdr_flgs & _X_XHDR) {
xhdr_flgs = 0;
}
getdir();
if (Xhdrflag > 0) {
ret = get_xdata();
if (ret != 0) { /* Xhdr items and regular header */
setbytes_to_skip(&stbuf, ret);
passtape();
return (0); /* Error--don't want to extract */
}
}
/*
* If typeflag is not 'A' and xhdr_flgs is set, then processing
* of ancillary file is either over or ancillary file
* processing is not required, load info from Xtarhdr and set
* _X_XHDR bit in xhdr_flgs.
*/
if ((dblock.dbuf.typeflag != 'A') && (xhdr_flgs != 0)) {
load_info_from_xtarhdr(xhdr_flgs, &Xtarhdr);
xhdr_flgs |= _X_XHDR;
}
#if defined(O_XATTR)
if (dblock.dbuf.typeflag == _XATTR_HDRTYPE && xattrbadhead == 0) {
/*
* Always needs to read the extended header. If atflag, saflag,
* or tflag isn't set, then we'll have the correct info for
* passtape() later.
*/
(void) read_xattr_hdr(attrinfo);
goto top;
}
/*
* Now that we've read the extended header, call passtape()
* if we don't want to restore attributes or system attributes.
* Don't restore the attribute if we are extracting
* a file from an archive (as opposed to doing a table of
* contents) and any of the following are true:
* 1. neither -@ or -/ was specified.
* 2. -@ was specified, -/ wasn't specified, and we're
* processing a hidden attribute directory of an attribute
* or we're processing a read-write system attribute file.
* 3. -@ wasn't specified, -/ was specified, and the file
* we're processing is not a read-write system attribute file,
* or we're processing the hidden attribute directory of an
* attribute.
*
* We always process the attributes if we're just generating
* generating a table of contents, or if both -@ and -/ were
* specified.
*/
if (xattrp != NULL) {
attr_data_t *ainfo = *attrinfo;
if (!tflag &&
((!atflag && !saflag) ||
(atflag && !saflag && ((ainfo->attr_parent != NULL) ||
ainfo->attr_rw_sysattr)) ||
(!atflag && saflag && ((ainfo->attr_parent != NULL) ||
!ainfo->attr_rw_sysattr)))) {
passtape();
return (0);
}
}
#endif
/* sets *namep to point at the proper name */
if (check_prefix(namep, dirp, component) != 0) {
passtape();
return (0);
}
if (endtape()) {
if (Bflag) {
ssize_t sz;
size_t extra_blocks = 0;
/*
* Logically at EOT - consume any extra blocks
* so that write to our stdin won't fail and
* emit an error message; otherwise something
* like "dd if=foo.tar | (cd bar; tar xvf -)"
* will produce a bogus error message from "dd".
*/
while ((sz = read(mt, tbuf, TBLOCK*nblock)) > 0) {
extra_blocks += sz;
}
dlog("wantit(): %d bytes of extra blocks\n",
extra_blocks);
}
dlog("wantit(): at end of tape.\n");
return (-1);
}
gotit = 0;
if ((Iflag && is_in_table(include_tbl, *namep)) ||
(! Iflag && *argv == NULL)) {
gotit = 1;
} else {
for (cp = argv; *cp; cp++) {
if (is_prefix(*cp, *namep)) {
gotit = 1;
break;
}
}
}
if (! gotit) {
passtape();
return (0);
}
if (Xflag && is_in_table(exclude_tbl, *namep)) {
if (vflag) {
(void) fprintf(stderr, gettext("%s excluded\n"),
*namep);
}
passtape();
return (0);
}
return (1);
}
static void
setbytes_to_skip(struct stat *st, int err)
{
/*
* In a scenario where a typeflag 'X' was followed by
* a typeflag 'A' and typeflag 'O', then the number of
* bytes to skip should be the size of ancillary file,
* plus the dblock for regular file, and the size
* from Xtarhdr. However, if the typeflag was just 'X'
* followed by typeflag 'O', then the number of bytes
* to skip should be the size from Xtarhdr.
*/
if ((err != 0) && (dblock.dbuf.typeflag == 'A') &&
(xhdr_flgs & _X_SIZE)) {
st->st_size += TBLOCK + Xtarhdr.x_filesz;
xhdr_flgs |= _X_XHDR;
} else if ((dblock.dbuf.typeflag != 'A') &&
(xhdr_flgs & _X_SIZE)) {
st->st_size += Xtarhdr.x_filesz;
xhdr_flgs |= _X_XHDR;
}
}
static int
fill_in_attr_info(char *attr, char *longname, char *attrparent, int atparentfd,
int rw_sysattr, attr_data_t **attrinfo)
{
size_t pathlen;
char *tpath;
char *tparent;
/* parent info */
if (attrparent != NULL) {
if ((tparent = strdup(attrparent)) == NULL) {
vperror(0, gettext(
"unable to allocate memory for attribute parent "
"name for %sattribute %s/%s of %s"),
rw_sysattr ? gettext("system ") : "",
attrparent, attr, longname);
return (1);
}
} else {
tparent = NULL;
}
/* path info */
pathlen = strlen(attr) + 1;
if (attrparent != NULL) {
pathlen += strlen(attrparent) + 1; /* add 1 for '/' */
}
if ((tpath = calloc(1, pathlen)) == NULL) {
vperror(0, gettext(
"unable to allocate memory for full "
"attribute path name for %sattribute %s%s%s of %s"),
rw_sysattr ? gettext("system ") : "",
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : "/",
attr, longname);
if (tparent != NULL) {
free(tparent);
}
return (1);
}
(void) snprintf(tpath, pathlen, "%s%s%s",
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : "/",
attr);
/* fill in the attribute info */
if (*attrinfo == NULL) {
if ((*attrinfo = malloc(sizeof (attr_data_t))) == NULL) {
vperror(0, gettext(
"unable to allocate memory for attribute "
"information for %sattribute %s%s%s of %s"),
rw_sysattr ? gettext("system ") : "",
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : gettext("/"),
attr, longname);
if (tparent != NULL) {
free(tparent);
}
free(tpath);
return (1);
}
} else {
if ((*attrinfo)->attr_parent != NULL) {
free((*attrinfo)->attr_parent);
}
if ((*attrinfo)->attr_path != NULL) {
free((*attrinfo)->attr_path);
}
/*
* The parent file descriptor is passed in, so don't
* close it here as it should be closed by the function
* that opened it.
*/
}
(*attrinfo)->attr_parent = tparent;
(*attrinfo)->attr_path = tpath;
(*attrinfo)->attr_rw_sysattr = rw_sysattr;
(*attrinfo)->attr_parentfd = atparentfd;
return (0);
}
/*
* Test to see if name is a directory.
*
* Return 1 if true, 0 otherwise.
*/
static int
is_directory(char *name)
{
#if defined(O_XATTR)
/*
* If there is an xattr_buf structure associated with this file,
* then the directory test is based on whether the name has a
* trailing slash.
*/
if (xattrp)
return (name[strlen(name) - 1] == '/');
#endif
if (is_posix)
return (dblock.dbuf.typeflag == '5');
else
return (name[strlen(name) - 1] == '/');
}
/*
* Version of chdir that handles directory pathnames of greater than PATH_MAX
* length, by changing the working directory to manageable portions of the
* complete directory pathname. If any of these attempts fail, then it exits
* non-zero.
*
* If a segment (i.e. a portion of "path" between two "/"'s) of the overall
* pathname is greater than PATH_MAX, then this still won't work, and this
* routine will return -1 with errno set to ENAMETOOLONG.
*
* NOTE: this routine is semantically different to the system chdir in
* that it is remotely possible for the currently working directory to be
* changed to a different directory, if a chdir call fails when processing
* one of the segments of a path that is greater than PATH_MAX. This isn't
* a problem as this is tar's own specific version of chdir.
*/
static int
tar_chdir(const char *path)
{
const char *sep = "/";
char *path_copy = NULL;
char *ptr = NULL;
/* The trivial case. */
if (chdir(path) == 0) {
return (0);
}
if (errno == ENAMETOOLONG) {
if (path[0] == '/' && chdir(sep) != 0)
return (-1);
/* strtok(3C) modifies the string, so make a copy. */
if ((path_copy = strdup(path)) == NULL) {
return (-1);
}
/* chdir(2) for every path element. */
for (ptr = strtok(path_copy, sep);
ptr != NULL;
ptr = strtok(NULL, sep)) {
if (chdir(ptr) != 0) {
free(path_copy);
return (-1);
}
}
free(path_copy);
return (0);
}
/* If chdir fails for any reason except ENAMETOOLONG. */
return (-1);
}
/*
* Test if name has a '..' sequence in it.
*
* Return 1 if found, 0 otherwise.
*/
static int
has_dot_dot(char *name)
{
char *s;
size_t name_len = strlen(name);
for (s = name; s < (name + name_len - 2); s++) {
if (s[0] == '.' && s[1] == '.' && ((s[2] == '/') || !s[2]))
return (1);
while (! (*s == '/')) {
if (! *s++)
return (0);
}
}
return (0);
}
/*
* Test if name is an absolute path name.
*
* Return 1 if true, 0 otherwise.
*/
static int
is_absolute(char *name)
{
#if defined(O_XATTR)
/*
* If this is an extended attribute (whose name will begin with
* "/dev/null/", always return 0 as they should be extracted with
* the name intact, to allow other tar archiving programs that
* don't understand extended attributes, to correctly throw them away.
*/
if (xattrp)
return (0);
#endif
return (name[0] == '/');
}
/*
* Adjust the pathname to make it a relative one. Strip off any leading
* '/' characters and if the pathname contains any '..' sequences, strip
* upto and including the last occurance of '../' (or '..' if found at
* the very end of the pathname).
*
* Return the relative pathname. stripped_prefix will also return the
* portion of name that was stripped off and should be freed by the
* calling routine when no longer needed.
*/
static char *
make_relative_name(char *name, char **stripped_prefix)
{
char *s;
size_t prefix_len = 0;
size_t name_len = strlen(name);
for (s = name + prefix_len; s < (name + name_len - 2); ) {
if (s[0] == '.' && s[1] == '.' && ((s[2] == '/') || !s[2]))
prefix_len = s + 2 - name;
do {
char c = *s++;
if (c == '/')
break;
} while (*s);
}
for (s = name + prefix_len; *s == '/'; s++)
continue;
prefix_len = s - name;
/* Create the portion of the name that was stripped off. */
s = malloc(prefix_len + 1);
memcpy(s, name, prefix_len);
s[prefix_len] = 0;
*stripped_prefix = s;
s = &name[prefix_len];
return (s);
}
/*
* Return through *namep a pointer to the proper fullname (i.e "<name> |
* <prefix>/<name>"), as represented in the header entry dblock.dbuf.
*
* Returns 0 if successful, otherwise returns 1.
*/
static int
check_prefix(char **namep, char **dirp, char **compp)
{
static char fullname[PATH_MAX + 1];
static char dir[PATH_MAX + 1];
static char component[PATH_MAX + 1];
static char savename[PATH_MAX + 1];
char *s;
(void) memset(dir, 0, sizeof (dir));
(void) memset(component, 0, sizeof (component));
if (xhdr_flgs & _X_PATH) {
(void) strcpy(fullname, Xtarhdr.x_path);
} else {
if (dblock.dbuf.prefix[0] != '\0')
(void) sprintf(fullname, "%.*s/%.*s", PRESIZ,
dblock.dbuf.prefix, NAMSIZ, dblock.dbuf.name);
else
(void) sprintf(fullname, "%.*s", NAMSIZ,
dblock.dbuf.name);
}
/*
* If we are printing a table of contents or extracting an archive,
* make absolute pathnames relative and prohibit the unpacking of
* files contain ".." in their name (unless the user has supplied
* the -P option).
*/
if ((tflag || xflag) && !Pflag) {
if (is_absolute(fullname) || has_dot_dot(fullname)) {
char *stripped_prefix;
(void) strcpy(savename, fullname);
strcpy(fullname,
make_relative_name(savename, &stripped_prefix));
(void) fprintf(stderr,
gettext("tar: Removing leading '%s' from '%s'\n"),
stripped_prefix, savename);
free(stripped_prefix);
}
}
/*
* Set dir and component names
*/
get_parent(fullname, dir);
#if defined(O_XATTR)
if (xattrp == NULL) {
#endif
/*
* Save of real name since were going to chop off the
* trailing slashes.
*/
(void) strcpy(savename, fullname);
/*
* first strip of trailing slashes.
*/
chop_endslashes(savename);
s = get_component(savename);
(void) strcpy(component, s);
#if defined(O_XATTR)
} else {
(void) strcpy(fullname, xattrp->h_names);
(void) strcpy(dir, fullname);
(void) strcpy(component, basename(xattrp->h_names +
strlen(xattrp->h_names) + 1));
}
#endif
*namep = fullname;
*dirp = dir;
*compp = component;
return (0);
}
/*
* Return true if the object indicated by the file descriptor and type
* is a tape device, false otherwise
*/
static int
istape(int fd, int type)
{
int result = 0;
if (S_ISCHR(type)) {
struct mtget mtg;
if (ioctl(fd, MTIOCGET, &mtg) != -1) {
result = 1;
}
}
return (result);
}
#include <utmpx.h>
struct utmpx utmpx;
#define NMAX (sizeof (utmpx.ut_name))
typedef struct cachenode { /* this struct must be zeroed before using */
struct cachenode *next; /* next in hash chain */
int val; /* the uid or gid of this entry */
int namehash; /* name's hash signature */
char name[NMAX+1]; /* the string that val maps to */
} cachenode_t;
#define HASHSIZE 256
static cachenode_t *names[HASHSIZE];
static cachenode_t *groups[HASHSIZE];
static cachenode_t *uids[HASHSIZE];
static cachenode_t *gids[HASHSIZE];
static int
hash_byname(char *name)
{
int i, c, h = 0;
for (i = 0; i < NMAX; i++) {
c = name[i];
if (c == '\0')
break;
h = (h << 4) + h + c;
}
return (h);
}
static cachenode_t *
hash_lookup_byval(cachenode_t *table[], int val)
{
int h = val;
cachenode_t *c;
for (c = table[h & (HASHSIZE - 1)]; c != NULL; c = c->next) {
if (c->val == val)
return (c);
}
return (NULL);
}
static cachenode_t *
hash_lookup_byname(cachenode_t *table[], char *name)
{
int h = hash_byname(name);
cachenode_t *c;
for (c = table[h & (HASHSIZE - 1)]; c != NULL; c = c->next) {
if (c->namehash == h && strcmp(c->name, name) == 0)
return (c);
}
return (NULL);
}
static cachenode_t *
hash_insert(cachenode_t *table[], char *name, int value)
{
cachenode_t *c;
int signature;
c = calloc(1, sizeof (cachenode_t));
if (c == NULL) {
perror("malloc");
exit(1);
}
if (name != NULL) {
(void) strncpy(c->name, name, NMAX);
c->namehash = hash_byname(name);
}
c->val = value;
if (table == uids || table == gids)
signature = c->val;
else
signature = c->namehash;
c->next = table[signature & (HASHSIZE - 1)];
table[signature & (HASHSIZE - 1)] = c;
return (c);
}
static char *
getname(uid_t uid)
{
cachenode_t *c;
if ((c = hash_lookup_byval(uids, uid)) == NULL) {
struct passwd *pwent = getpwuid(uid);
c = hash_insert(uids, pwent ? pwent->pw_name : NULL, uid);
}
return (c->name);
}
static char *
getgroup(gid_t gid)
{
cachenode_t *c;
if ((c = hash_lookup_byval(gids, gid)) == NULL) {
struct group *grent = getgrgid(gid);
c = hash_insert(gids, grent ? grent->gr_name : NULL, gid);
}
return (c->name);
}
static uid_t
getuidbyname(char *name)
{
cachenode_t *c;
if ((c = hash_lookup_byname(names, name)) == NULL) {
struct passwd *pwent = getpwnam(name);
c = hash_insert(names, name, pwent ? (int)pwent->pw_uid : -1);
}
return ((uid_t)c->val);
}
static gid_t
getgidbyname(char *group)
{
cachenode_t *c;
if ((c = hash_lookup_byname(groups, group)) == NULL) {
struct group *grent = getgrnam(group);
c = hash_insert(groups, group, grent ? (int)grent->gr_gid : -1);
}
return ((gid_t)c->val);
}
/*
* Build the header.
* Determine whether or not an extended header is also needed. If needed,
* create and write the extended header and its data.
* Writing of the extended header assumes that "tomodes" has been called and
* the relevant information has been placed in the header block.
*/
static int
build_dblock(
const char *name,
const char *linkname,
const char typeflag,
const int filetype,
const struct stat *sp,
const dev_t device,
const char *prefix)
{
int nblks;
major_t dev;
const char *filename;
const char *lastslash;
if (filetype == XATTR_FILE)
dblock.dbuf.typeflag = _XATTR_HDRTYPE;
else
dblock.dbuf.typeflag = typeflag;
(void) memset(dblock.dbuf.name, '\0', NAMSIZ);
(void) memset(dblock.dbuf.linkname, '\0', NAMSIZ);
(void) memset(dblock.dbuf.prefix, '\0', PRESIZ);
if (xhdr_flgs & _X_PATH)
filename = Xtarhdr.x_path;
else
filename = name;
if ((dev = major(device)) > OCTAL7CHAR) {
if (Eflag) {
xhdr_flgs |= _X_DEVMAJOR;
Xtarhdr.x_devmajor = dev;
} else {
(void) fprintf(stderr, gettext(
"Device major too large for %s. Use -E flag."),
filename);
if (errflag)
done(1);
else
Errflg = 1;
}
dev = 0;
}
(void) sprintf(dblock.dbuf.devmajor, "%07lo", dev);
if ((dev = minor(device)) > OCTAL7CHAR) {
if (Eflag) {
xhdr_flgs |= _X_DEVMINOR;
Xtarhdr.x_devminor = dev;
} else {
(void) fprintf(stderr, gettext(
"Device minor too large for %s. Use -E flag."),
filename);
if (errflag)
done(1);
else
Errflg = 1;
}
dev = 0;
}
(void) sprintf(dblock.dbuf.devminor, "%07lo", dev);
(void) strncpy(dblock.dbuf.name, name, NAMSIZ);
(void) strncpy(dblock.dbuf.linkname, linkname, NAMSIZ);
(void) sprintf(dblock.dbuf.magic, "%.5s", magic_type);
(void) sprintf(dblock.dbuf.version, "00");
(void) sprintf(dblock.dbuf.uname, "%.31s", getname(sp->st_uid));
(void) sprintf(dblock.dbuf.gname, "%.31s", getgroup(sp->st_gid));
(void) strncpy(dblock.dbuf.prefix, prefix, PRESIZ);
(void) sprintf(dblock.dbuf.chksum, "%07o", checksum(&dblock));
if (Eflag) {
(void) bcopy(dblock.dummy, xhdr_buf.dummy, TBLOCK);
(void) memset(xhdr_buf.dbuf.name, '\0', NAMSIZ);
lastslash = strrchr(name, '/');
if (lastslash == NULL)
lastslash = name;
else
lastslash++;
(void) strcpy(xhdr_buf.dbuf.name, lastslash);
(void) memset(xhdr_buf.dbuf.linkname, '\0', NAMSIZ);
(void) memset(xhdr_buf.dbuf.prefix, '\0', PRESIZ);
(void) strcpy(xhdr_buf.dbuf.prefix, xhdr_dirname);
xhdr_count++;
xrec_offset = 0;
gen_date("mtime", sp->st_mtim);
xhdr_buf.dbuf.typeflag = 'X';
if (gen_utf8_names(filename) != 0)
return (1);
#ifdef XHDR_DEBUG
Xtarhdr.x_uname = dblock.dbuf.uname;
Xtarhdr.x_gname = dblock.dbuf.gname;
xhdr_flgs |= (_X_UNAME | _X_GNAME);
#endif
if (xhdr_flgs) {
if (xhdr_flgs & _X_DEVMAJOR)
gen_num("SUN.devmajor", Xtarhdr.x_devmajor);
if (xhdr_flgs & _X_DEVMINOR)
gen_num("SUN.devminor", Xtarhdr.x_devminor);
if (xhdr_flgs & _X_GID)
gen_num("gid", Xtarhdr.x_gid);
if (xhdr_flgs & _X_UID)
gen_num("uid", Xtarhdr.x_uid);
if (xhdr_flgs & _X_SIZE)
gen_num("size", Xtarhdr.x_filesz);
if (xhdr_flgs & _X_PATH)
gen_string("path", Xtarhdr.x_path);
if (xhdr_flgs & _X_LINKPATH)
gen_string("linkpath", Xtarhdr.x_linkpath);
if (xhdr_flgs & _X_GNAME)
gen_string("gname", Xtarhdr.x_gname);
if (xhdr_flgs & _X_UNAME)
gen_string("uname", Xtarhdr.x_uname);
}
(void) sprintf(xhdr_buf.dbuf.size,
"%011" FMT_off_t_o, xrec_offset);
(void) sprintf(xhdr_buf.dbuf.chksum, "%07o",
checksum(&xhdr_buf));
(void) writetbuf((char *)&xhdr_buf, 1);
nblks = TBLOCKS(xrec_offset);
(void) writetbuf(xrec_ptr, nblks);
}
return (0);
}
/*
* makeDir - ensure that a directory with the pathname denoted by name
* exists, and return 1 on success, and 0 on failure (e.g.,
* read-only file system, exists but not-a-directory).
*/
static int
makeDir(char *name)
{
struct stat buf;
if (access(name, 0) < 0) { /* name doesn't exist */
if (mkdir(name, 0777) < 0) {
vperror(0, "%s", name);
return (0);
}
} else { /* name exists */
if (stat(name, &buf) < 0) {
vperror(0, "%s", name);
return (0);
}
return ((buf.st_mode & S_IFMT) == S_IFDIR);
}
return (1);
}
/*
* Save this directory and its mtime on the stack, popping and setting
* the mtimes of any stacked dirs which aren't parents of this one.
* A null name causes the entire stack to be unwound and set.
*
* Since all the elements of the directory "stack" share a common
* prefix, we can make do with one string. We keep only the current
* directory path, with an associated array of mtime's. A negative
* mtime means no mtime.
*
* This stack algorithm is not guaranteed to work for tapes created
* with the 'r' function letter, but the vast majority of tapes with
* directories are not. This avoids saving every directory record on
* the tape and setting all the times at the end.
*
* (This was borrowed from the 4.1.3 source, and adapted to the 5.x
* environment)
*/
static void
doDirTimes(char *name, timestruc_t modTime)
{
static char dirstack[PATH_MAX+2];
/* Add spaces for the last slash and last NULL */
static timestruc_t modtimes[PATH_MAX+1]; /* hash table */
char *p = dirstack;
char *q = name;
char *savp;
if (q) {
/*
* Find common prefix
*/
while (*p == *q && *p) {
p++; q++;
}
}
savp = p;
while (*p) {
/*
* Not a child: unwind the stack, setting the times.
* The order we do this doesn't matter, so we go "forward."
*/
if (*p == '/')
if (modtimes[p - dirstack].tv_sec >= 0) {
*p = '\0'; /* zap the slash */
setPathTimes(AT_FDCWD, dirstack,
modtimes[p - dirstack]);
*p = '/';
}
++p;
}
p = savp;
/*
* Push this one on the "stack"
*/
if (q) {
/*
* Since the name parameter points the dir pathname
* which is limited only to contain PATH_MAX chars
* at maximum, we can ignore the overflow case of p.
*/
while ((*p = *q++)) { /* append the rest of the new dir */
modtimes[p - dirstack].tv_sec = -1;
p++;
}
/*
* If the tar file had used 'P' or 'E' function modifier,
* append the last slash.
*/
if (*(p - 1) != '/') {
*p++ = '/';
*p = '\0';
}
/* overwrite the last one */
modtimes[p - dirstack - 1] = modTime;
}
}
/*
* setPathTimes - set the modification time for given path. Return 1 if
* successful and 0 if not successful.
*/
static void
setPathTimes(int dirfd, char *path, timestruc_t modTime)
{
struct timeval timebuf[2];
/*
* futimesat takes an array of two timeval structs.
* The first entry contains access time.
* The second entry contains modification time.
* Unlike a timestruc_t, which uses nanoseconds, timeval uses
* microseconds.
*/
timebuf[0].tv_sec = time((time_t *)0);
timebuf[0].tv_usec = 0;
timebuf[1].tv_sec = modTime.tv_sec;
/* Extended header: use microseconds */
timebuf[1].tv_usec = (xhdr_flgs & _X_MTIME) ? modTime.tv_nsec/1000 : 0;
if (futimesat(dirfd, path, timebuf) < 0)
vperror(0, gettext("can't set time on %s"), path);
}
/*
* If hflag is set then delete the symbolic link's target.
* If !hflag then delete the target.
*/
static void
delete_target(int fd, char *comp, char *namep)
{
struct stat xtractbuf;
char buf[PATH_MAX + 1];
int n;
if (unlinkat(fd, comp, AT_REMOVEDIR) < 0) {
if (errno == ENOTDIR && !hflag) {
(void) unlinkat(fd, comp, 0);
} else if (errno == ENOTDIR && hflag) {
if (!lstat(namep, &xtractbuf)) {
if ((xtractbuf.st_mode & S_IFMT) != S_IFLNK) {
(void) unlinkat(fd, comp, 0);
} else if ((n = readlink(namep, buf,
PATH_MAX)) != -1) {
buf[n] = (char)NULL;
(void) unlinkat(fd, buf,
AT_REMOVEDIR);
if (errno == ENOTDIR)
(void) unlinkat(fd, buf, 0);
} else {
(void) unlinkat(fd, comp, 0);
}
} else {
(void) unlinkat(fd, comp, 0);
}
}
}
}
/*
* ACL changes:
* putfile():
* Get acl info after stat. Write out ancillary file
* before the normal file, i.e. directory, regular, FIFO,
* link, special. If acl count is less than 4, no need to
* create ancillary file. (i.e. standard permission is in
* use.
* doxtract():
* Process ancillary file. Read it in and set acl info.
* watch out for 'o' function modifier.
* 't' function letter to display table
*/
/*
* New functions for ACLs and other security attributes
*/
/*
* The function appends the new security attribute info to the end of
* existing secinfo.
*/
int
append_secattr(
char **secinfo, /* existing security info */
int *secinfo_len, /* length of existing security info */
int size, /* new attribute size: unit depends on type */
char *attrtext, /* new attribute text */
char attr_type) /* new attribute type */
{
char *new_secinfo;
int newattrsize;
int oldsize;
struct sec_attr *attr;
/* no need to add */
if (attr_type != DIR_TYPE) {
if (attrtext == NULL)
return (0);
}
switch (attr_type) {
case UFSD_ACL:
case ACE_ACL:
if (attrtext == NULL) {
(void) fprintf(stderr, gettext("acltotext failed\n"));
return (-1);
}
/* header: type + size = 8 */
newattrsize = 8 + (int)strlen(attrtext) + 1;
attr = (struct sec_attr *)malloc(newattrsize);
if (attr == NULL) {
(void) fprintf(stderr,
gettext("can't allocate memory\n"));
return (-1);
}
attr->attr_type = attr_type;
(void) sprintf(attr->attr_len,
"%06o", size); /* acl entry count */
(void) strcpy((char *)&attr->attr_info[0], attrtext);
free(attrtext);
break;
/* Trusted Extensions */
case DIR_TYPE:
case LBL_TYPE:
newattrsize = sizeof (struct sec_attr) + strlen(attrtext);
attr = (struct sec_attr *)malloc(newattrsize);
if (attr == NULL) {
(void) fprintf(stderr,
gettext("can't allocate memory\n"));
return (-1);
}
attr->attr_type = attr_type;
(void) sprintf(attr->attr_len,
"%06d", size); /* len of attr data */
(void) strcpy((char *)&attr->attr_info[0], attrtext);
break;
default:
(void) fprintf(stderr,
gettext("unrecognized attribute type\n"));
return (-1);
}
/* old security info + new attr header(8) + new attr */
oldsize = *secinfo_len;
*secinfo_len += newattrsize;
new_secinfo = (char *)malloc(*secinfo_len);
if (new_secinfo == NULL) {
(void) fprintf(stderr, gettext("can't allocate memory\n"));
*secinfo_len -= newattrsize;
free(attr);
return (-1);
}
(void) memcpy(new_secinfo, *secinfo, oldsize);
(void) memcpy(new_secinfo + oldsize, attr, newattrsize);
free(*secinfo);
free(attr);
*secinfo = new_secinfo;
return (0);
}
/*
* write_ancillary(): write out an ancillary file.
* The file has the same header as normal file except the type and size
* fields. The type is 'A' and size is the sum of all attributes
* in bytes.
* The body contains a list of attribute type, size and info. Currently,
* there is only ACL info. This file is put before the normal file.
*/
void
write_ancillary(union hblock *dblockp, char *secinfo, int len, char hdrtype)
{
long blocks;
int savflag;
int savsize;
/* Just tranditional permissions or no security attribute info */
if (len == 0 || secinfo == NULL)
return;
/* save flag and size */
savflag = (dblockp->dbuf).typeflag;
(void) sscanf(dblockp->dbuf.size, "%12o", (uint_t *)&savsize);
/* special flag for ancillary file */
if (hdrtype == _XATTR_HDRTYPE)
dblockp->dbuf.typeflag = _XATTR_HDRTYPE;
else
dblockp->dbuf.typeflag = 'A';
/* for pre-2.5 versions of tar, need to make sure */
/* the ACL file is readable */
(void) sprintf(dblock.dbuf.mode, "%07lo",
(stbuf.st_mode & POSIXMODES) | 0000200);
(void) sprintf(dblockp->dbuf.size, "%011o", len);
(void) sprintf(dblockp->dbuf.chksum, "%07o", checksum(dblockp));
/* write out the header */
(void) writetbuf((char *)dblockp, 1);
/* write out security info */
blocks = TBLOCKS(len);
(void) writetbuf((char *)secinfo, (int)blocks);
/* restore mode, flag and size */
(void) sprintf(dblock.dbuf.mode, "%07lo", stbuf.st_mode & POSIXMODES);
dblockp->dbuf.typeflag = savflag;
(void) sprintf(dblockp->dbuf.size, "%011o", savsize);
}
/*
* Read the data record for extended headers and then the regular header.
* The data are read into the buffer and then null-terminated. Entries
* for typeflag 'X' extended headers are of the format:
* "%d %s=%s\n"
*
* When an extended header record is found, the extended header must
* be processed and its values used to override the values in the
* normal header. The way this is done is to process the extended
* header data record and set the data values, then call getdir
* to process the regular header, then then to reconcile the two
* sets of data.
*/
static int
get_xdata(void)
{
struct keylist_pair {
int keynum;
char *keylist;
} keylist_pair[] = { _X_DEVMAJOR, "SUN.devmajor",
_X_DEVMINOR, "SUN.devminor",
_X_GID, "gid",
_X_GNAME, "gname",
_X_LINKPATH, "linkpath",
_X_PATH, "path",
_X_SIZE, "size",
_X_UID, "uid",
_X_UNAME, "uname",
_X_MTIME, "mtime",
_X_LAST, "NULL" };
char *lineloc;
int length, i;
char *keyword, *value;
blkcnt_t nblocks;
int bufneeded;
int errors;
(void) memset(&Xtarhdr, 0, sizeof (Xtarhdr));
xhdr_count++;
errors = 0;
nblocks = TBLOCKS(stbuf.st_size);
bufneeded = nblocks * TBLOCK;
if (bufneeded >= xrec_size) {
free(xrec_ptr);
xrec_size = bufneeded + 1;
if ((xrec_ptr = malloc(xrec_size)) == NULL)
fatal(gettext("cannot allocate buffer"));
}
lineloc = xrec_ptr;
while (nblocks-- > 0) {
readtape(lineloc);
lineloc += TBLOCK;
}
lineloc = xrec_ptr;
xrec_ptr[stbuf.st_size] = '\0';
while (lineloc < xrec_ptr + stbuf.st_size) {
if (dblock.dbuf.typeflag == 'L') {
length = xrec_size;
keyword = "path";
value = lineloc;
} else {
length = atoi(lineloc);
*(lineloc + length - 1) = '\0';
keyword = strchr(lineloc, ' ') + 1;
value = strchr(keyword, '=') + 1;
*(value - 1) = '\0';
}
i = 0;
lineloc += length;
while (keylist_pair[i].keynum != (int)_X_LAST) {
if (strcmp(keyword, keylist_pair[i].keylist) == 0)
break;
i++;
}
errno = 0;
switch (keylist_pair[i].keynum) {
case _X_DEVMAJOR:
Xtarhdr.x_devmajor = (major_t)strtoul(value, NULL, 0);
if (errno) {
(void) fprintf(stderr, gettext(
"tar: Extended header major value error "
"for file # %llu.\n"), xhdr_count);
errors++;
} else
xhdr_flgs |= _X_DEVMAJOR;
break;
case _X_DEVMINOR:
Xtarhdr.x_devminor = (minor_t)strtoul(value, NULL, 0);
if (errno) {
(void) fprintf(stderr, gettext(
"tar: Extended header minor value error "
"for file # %llu.\n"), xhdr_count);
errors++;
} else
xhdr_flgs |= _X_DEVMINOR;
break;
case _X_GID:
xhdr_flgs |= _X_GID;
Xtarhdr.x_gid = strtol(value, NULL, 0);
if ((errno) || (Xtarhdr.x_gid > UID_MAX)) {
(void) fprintf(stderr, gettext(
"tar: Extended header gid value error "
"for file # %llu.\n"), xhdr_count);
Xtarhdr.x_gid = GID_NOBODY;
}
break;
case _X_GNAME:
if (utf8_local("gname", &Xtarhdr.x_gname,
local_gname, value, _POSIX_NAME_MAX) == 0)
xhdr_flgs |= _X_GNAME;
break;
case _X_LINKPATH:
if (utf8_local("linkpath", &Xtarhdr.x_linkpath,
local_linkpath, value, PATH_MAX) == 0)
xhdr_flgs |= _X_LINKPATH;
else
errors++;
break;
case _X_PATH:
if (utf8_local("path", &Xtarhdr.x_path,
local_path, value, PATH_MAX) == 0)
xhdr_flgs |= _X_PATH;
else
errors++;
break;
case _X_SIZE:
Xtarhdr.x_filesz = strtoull(value, NULL, 0);
if (errno) {
(void) fprintf(stderr, gettext(
"tar: Extended header invalid filesize "
"for file # %llu.\n"), xhdr_count);
errors++;
} else
xhdr_flgs |= _X_SIZE;
break;
case _X_UID:
xhdr_flgs |= _X_UID;
Xtarhdr.x_uid = strtol(value, NULL, 0);
if ((errno) || (Xtarhdr.x_uid > UID_MAX)) {
(void) fprintf(stderr, gettext(
"tar: Extended header uid value error "
"for file # %llu.\n"), xhdr_count);
Xtarhdr.x_uid = UID_NOBODY;
}
break;
case _X_UNAME:
if (utf8_local("uname", &Xtarhdr.x_uname,
local_uname, value, _POSIX_NAME_MAX) == 0)
xhdr_flgs |= _X_UNAME;
break;
case _X_MTIME:
get_xtime(value, &(Xtarhdr.x_mtime));
if (errno)
(void) fprintf(stderr, gettext(
"tar: Extended header modification time "
"value error for file # %llu.\n"),
xhdr_count);
else
xhdr_flgs |= _X_MTIME;
break;
default:
(void) fprintf(stderr,
gettext("tar: unrecognized extended"
" header keyword '%s'. Ignored.\n"), keyword);
break;
}
}
getdir(); /* get regular header */
if (errors && errflag)
done(1);
else
if (errors)
Errflg = 1;
return (errors);
}
/*
* load_info_from_xtarhdr - sets Gen and stbuf variables from
* extended header
* load_info_from_xtarhdr(flag, xhdrp);
* u_longlong_t flag; xhdr_flgs
* struct xtar_hdr *xhdrp; pointer to extended header
* NOTE: called when typeflag is not 'A' and xhdr_flgs
* is set.
*/
static void
load_info_from_xtarhdr(u_longlong_t flag, struct xtar_hdr *xhdrp)
{
if (flag & _X_DEVMAJOR) {
Gen.g_devmajor = xhdrp->x_devmajor;
}
if (flag & _X_DEVMINOR) {
Gen.g_devminor = xhdrp->x_devminor;
}
if (flag & _X_GID) {
Gen.g_gid = xhdrp->x_gid;
stbuf.st_gid = xhdrp->x_gid;
}
if (flag & _X_UID) {
Gen.g_uid = xhdrp->x_uid;
stbuf.st_uid = xhdrp->x_uid;
}
if (flag & _X_SIZE) {
Gen.g_filesz = xhdrp->x_filesz;
stbuf.st_size = xhdrp->x_filesz;
}
if (flag & _X_MTIME) {
Gen.g_mtime = xhdrp->x_mtime.tv_sec;
stbuf.st_mtim.tv_sec = xhdrp->x_mtime.tv_sec;
stbuf.st_mtim.tv_nsec = xhdrp->x_mtime.tv_nsec;
}
}
/*
* gen_num creates a string from a keyword and an usigned long long in the
* format: %d %s=%s\n
* This is part of the extended header data record.
*/
void
gen_num(const char *keyword, const u_longlong_t number)
{
char save_val[ULONGLONG_MAX_DIGITS + 1];
int len;
char *curr_ptr;
(void) sprintf(save_val, "%llu", number);
/*
* len = length of entire line, including itself. len will be
* two digits. So, add the string lengths plus the length of len,
* plus a blank, an equal sign, and a newline.
*/
len = strlen(save_val) + strlen(keyword) + 5;
if (xrec_offset + len > xrec_size) {
if (((curr_ptr = realloc(xrec_ptr, 2 * xrec_size)) == NULL))
fatal(gettext(
"cannot allocate extended header buffer"));
xrec_ptr = curr_ptr;
xrec_size *= 2;
}
(void) sprintf(&xrec_ptr[xrec_offset],
"%d %s=%s\n", len, keyword, save_val);
xrec_offset += len;
}
/*
* gen_date creates a string from a keyword and a timestruc_t in the
* format: %d %s=%s\n
* This is part of the extended header data record.
* Currently, granularity is only microseconds, so the low-order three digits
* will be truncated.
*/
void
gen_date(const char *keyword, const timestruc_t time_value)
{
/* Allow for <seconds>.<nanoseconds>\n */
char save_val[TIME_MAX_DIGITS + LONG_MAX_DIGITS + 2];
int len;
char *curr_ptr;
(void) sprintf(save_val, "%ld", time_value.tv_sec);
len = strlen(save_val);
save_val[len] = '.';
(void) sprintf(&save_val[len + 1], "%9.9ld", time_value.tv_nsec);
/*
* len = length of entire line, including itself. len will be
* two digits. So, add the string lengths plus the length of len,
* plus a blank, an equal sign, and a newline.
*/
len = strlen(save_val) + strlen(keyword) + 5;
if (xrec_offset + len > xrec_size) {
if (((curr_ptr = realloc(xrec_ptr, 2 * xrec_size)) == NULL))
fatal(gettext(
"cannot allocate extended header buffer"));
xrec_ptr = curr_ptr;
xrec_size *= 2;
}
(void) sprintf(&xrec_ptr[xrec_offset],
"%d %s=%s\n", len, keyword, save_val);
xrec_offset += len;
}
/*
* gen_string creates a string from a keyword and a char * in the
* format: %d %s=%s\n
* This is part of the extended header data record.
*/
void
gen_string(const char *keyword, const char *value)
{
int len;
char *curr_ptr;
/*
* len = length of entire line, including itself. The character length
* of len must be 1-4 characters, because the maximum size of the path
* or the name is PATH_MAX, which is 1024. So, assume 1 character
* for len, one for the space, one for the "=", and one for the newline.
* Then adjust as needed.
*/
/* LINTED constant expression */
assert(PATH_MAX <= 9996);
len = strlen(value) + strlen(keyword) + 4;
if (len > 997)
len += 3;
else if (len > 98)
len += 2;
else if (len > 9)
len += 1;
if (xrec_offset + len > xrec_size) {
if (((curr_ptr = realloc(xrec_ptr, 2 * xrec_size)) == NULL))
fatal(gettext(
"cannot allocate extended header buffer"));
xrec_ptr = curr_ptr;
xrec_size *= 2;
}
#ifdef XHDR_DEBUG
if (strcmp(keyword+1, "name") != 0)
#endif
(void) sprintf(&xrec_ptr[xrec_offset],
"%d %s=%s\n", len, keyword, value);
#ifdef XHDR_DEBUG
else {
len += 11;
(void) sprintf(&xrec_ptr[xrec_offset],
"%d %s=%snametoolong\n", len, keyword, value);
}
#endif
xrec_offset += len;
}
/*
* Convert time found in the extended header data to seconds and nanoseconds.
*/
void
get_xtime(char *value, timestruc_t *xtime)
{
char nanosec[10];
char *period;
int i;
(void) memset(nanosec, '0', 9);
nanosec[9] = '\0';
period = strchr(value, '.');
if (period != NULL)
period[0] = '\0';
xtime->tv_sec = strtol(value, NULL, 10);
if (period == NULL)
xtime->tv_nsec = 0;
else {
i = strlen(period +1);
(void) strncpy(nanosec, period + 1, min(i, 9));
xtime->tv_nsec = strtol(nanosec, NULL, 10);
}
}
/*
* Check linkpath for length.
* Emit an error message and return 1 if too long.
*/
int
chk_path_build(
char *name,
char *longname,
char *linkname,
char *prefix,
char type,
int filetype)
{
if (strlen(linkname) > (size_t)NAMSIZ) {
if (Eflag > 0) {
xhdr_flgs |= _X_LINKPATH;
Xtarhdr.x_linkpath = linkname;
} else {
(void) fprintf(stderr, gettext(
"tar: %s: linked to %s\n"), longname, linkname);
(void) fprintf(stderr, gettext(
"tar: %s: linked name too long\n"), linkname);
if (errflag)
done(1);
else
Errflg = 1;
return (1);
}
}
if (xhdr_flgs & _X_LINKPATH)
return (build_dblock(name, tchar, type,
filetype, &stbuf, stbuf.st_dev,
prefix));
else
return (build_dblock(name, linkname, type,
filetype, &stbuf, stbuf.st_dev, prefix));
}
/*
* Convert from UTF-8 to local character set.
*/
static int
utf8_local(
char *option,
char **Xhdr_ptrptr,
char *target,
const char *source,
int max_val)
{
static iconv_t iconv_cd;
char *nl_target;
const char *iconv_src;
char *iconv_trg;
size_t inlen;
size_t outlen;
if (charset_type == -1) { /* iconv_open failed in earlier try */
(void) fprintf(stderr, gettext(
"tar: file # %llu: (%s) UTF-8 conversion failed.\n"),
xhdr_count, source);
return (1);
} else if (charset_type == 0) { /* iconv_open has not yet been done */
nl_target = nl_langinfo(CODESET);
if (strlen(nl_target) == 0) /* locale using 7-bit codeset */
nl_target = "646";
if (strcmp(nl_target, "646") == 0)
charset_type = 1;
else if (strcmp(nl_target, "UTF-8") == 0)
charset_type = 3;
else {
if (strncmp(nl_target, "ISO", 3) == 0)
nl_target += 3;
charset_type = 2;
errno = 0;
if ((iconv_cd = iconv_open(nl_target, "UTF-8")) ==
(iconv_t)-1) {
if (errno == EINVAL)
(void) fprintf(stderr, gettext(
"tar: conversion routines not "
"available for current locale. "));
(void) fprintf(stderr, gettext(
"file # %llu: (%s) UTF-8 conversion"
" failed.\n"), xhdr_count, source);
charset_type = -1;
return (1);
}
}
}
/* locale using 7-bit codeset or UTF-8 locale */
if (charset_type == 1 || charset_type == 3) {
if (strlen(source) > max_val) {
(void) fprintf(stderr, gettext(
"tar: file # %llu: Extended header %s too long.\n"),
xhdr_count, option);
return (1);
}
if (charset_type == 3)
(void) strcpy(target, source);
else if (c_utf8(target, source) != 0) {
(void) fprintf(stderr, gettext(
"tar: file # %llu: (%s) UTF-8 conversion"
" failed.\n"), xhdr_count, source);
return (1);
}
*Xhdr_ptrptr = target;
return (0);
}
iconv_src = source;
iconv_trg = target;
inlen = strlen(source);
outlen = max_val * UTF_8_FACTOR;
if (iconv(iconv_cd, &iconv_src, &inlen, &iconv_trg, &outlen) ==
(size_t)-1) { /* Error occurred: didn't convert */
(void) fprintf(stderr, gettext(
"tar: file # %llu: (%s) UTF-8 conversion failed.\n"),
xhdr_count, source);
/* Get remaining output; reinitialize conversion descriptor */
iconv_src = (const char *)NULL;
inlen = 0;
(void) iconv(iconv_cd, &iconv_src, &inlen, &iconv_trg, &outlen);
return (1);
}
/* Get remaining output; reinitialize conversion descriptor */
iconv_src = (const char *)NULL;
inlen = 0;
if (iconv(iconv_cd, &iconv_src, &inlen, &iconv_trg, &outlen) ==
(size_t)-1) { /* Error occurred: didn't convert */
(void) fprintf(stderr, gettext(
"tar: file # %llu: (%s) UTF-8 conversion failed.\n"),
xhdr_count, source);
return (1);
}
*iconv_trg = '\0'; /* Null-terminate iconv output string */
if (strlen(target) > max_val) {
(void) fprintf(stderr, gettext(
"tar: file # %llu: Extended header %s too long.\n"),
xhdr_count, option);
return (1);
}
*Xhdr_ptrptr = target;
return (0);
}
/*
* Check gname, uname, path, and linkpath to see if they need to go in an
* extended header. If they are already slated to be in an extended header,
* or if they are not ascii, then they need to be in the extended header.
* Then, convert all extended names to UTF-8.
*/
int
gen_utf8_names(const char *filename)
{
static iconv_t iconv_cd;
char *nl_target;
char tempbuf[MAXNAM + 1];
int nbytes;
int errors;
if (charset_type == -1) { /* Previous failure to open. */
(void) fprintf(stderr, gettext(
"tar: file # %llu: UTF-8 conversion failed.\n"),
xhdr_count);
return (1);
}
if (charset_type == 0) { /* Need to get conversion descriptor */
nl_target = nl_langinfo(CODESET);
if (strlen(nl_target) == 0) /* locale using 7-bit codeset */
nl_target = "646";
if (strcmp(nl_target, "646") == 0)
charset_type = 1;
else if (strcmp(nl_target, "UTF-8") == 0)
charset_type = 3;
else {
if (strncmp(nl_target, "ISO", 3) == 0)
nl_target += 3;
charset_type = 2;
errno = 0;
#ifdef ICONV_DEBUG
(void) fprintf(stderr,
gettext("Opening iconv_cd with target %s\n"),
nl_target);
#endif
if ((iconv_cd = iconv_open("UTF-8", nl_target)) ==
(iconv_t)-1) {
if (errno == EINVAL)
(void) fprintf(stderr, gettext(
"tar: conversion routines not "
"available for current locale. "));
(void) fprintf(stderr, gettext(
"file (%s): UTF-8 conversion failed.\n"),
filename);
charset_type = -1;
return (1);
}
}
}
errors = 0;
errors += local_utf8(&Xtarhdr.x_gname, local_gname,
dblock.dbuf.gname, iconv_cd, _X_GNAME, _POSIX_NAME_MAX);
errors += local_utf8(&Xtarhdr.x_uname, local_uname,
dblock.dbuf.uname, iconv_cd, _X_UNAME, _POSIX_NAME_MAX);
if ((xhdr_flgs & _X_LINKPATH) == 0) { /* Need null-terminated str. */
(void) strncpy(tempbuf, dblock.dbuf.linkname, NAMSIZ);
tempbuf[NAMSIZ] = '\0';
}
errors += local_utf8(&Xtarhdr.x_linkpath, local_linkpath,
tempbuf, iconv_cd, _X_LINKPATH, PATH_MAX);
if ((xhdr_flgs & _X_PATH) == 0) { /* Concatenate prefix & name */
(void) strncpy(tempbuf, dblock.dbuf.prefix, PRESIZ);
tempbuf[PRESIZ] = '\0';
nbytes = strlen(tempbuf);
if (nbytes > 0) {
tempbuf[nbytes++] = '/';
tempbuf[nbytes] = '\0';
}
(void) strncat(tempbuf + nbytes, dblock.dbuf.name,
(MAXNAM - nbytes));
tempbuf[MAXNAM] = '\0';
}
errors += local_utf8(&Xtarhdr.x_path, local_path,
tempbuf, iconv_cd, _X_PATH, PATH_MAX);
if (errors > 0)
(void) fprintf(stderr, gettext(
"tar: file (%s): UTF-8 conversion failed.\n"), filename);
if (errors && errflag)
done(1);
else
if (errors)
Errflg = 1;
return (errors);
}
static int
local_utf8(
char **Xhdr_ptrptr,
char *target,
const char *source,
iconv_t iconv_cd,
int xhdrflg,
int max_val)
{
const char *iconv_src;
const char *starting_src;
char *iconv_trg;
size_t inlen;
size_t outlen;
#ifdef ICONV_DEBUG
unsigned char c_to_hex;
#endif
/*
* If the item is already slated for extended format, get the string
* to convert from the extended header record. Otherwise, get it from
* the regular (dblock) area.
*/
if (xhdr_flgs & xhdrflg) {
if (charset_type == 3) { /* Already UTF-8, just copy */
(void) strcpy(target, *Xhdr_ptrptr);
*Xhdr_ptrptr = target;
return (0);
} else
iconv_src = (const char *) *Xhdr_ptrptr;
} else {
if (charset_type == 3) /* Already in UTF-8 format */
return (0); /* Don't create xhdr record */
iconv_src = source;
}
starting_src = iconv_src;
iconv_trg = target;
if ((inlen = strlen(iconv_src)) == 0)
return (0);
if (charset_type == 1) { /* locale using 7-bit codeset */
if (c_utf8(target, starting_src) != 0) {
(void) fprintf(stderr,
gettext("tar: invalid character in"
" UTF-8 conversion of '%s'\n"), starting_src);
return (1);
}
return (0);
}
outlen = max_val * UTF_8_FACTOR;
errno = 0;
if (iconv(iconv_cd, &iconv_src, &inlen, &iconv_trg, &outlen) ==
(size_t)-1) {
/* An error occurred, or not all characters were converted */
if (errno == EILSEQ)
(void) fprintf(stderr,
gettext("tar: invalid character in"
" UTF-8 conversion of '%s'\n"), starting_src);
else
(void) fprintf(stderr, gettext(
"tar: conversion to UTF-8 aborted for '%s'.\n"),
starting_src);
/* Get remaining output; reinitialize conversion descriptor */
iconv_src = (const char *)NULL;
inlen = 0;
(void) iconv(iconv_cd, &iconv_src, &inlen, &iconv_trg, &outlen);
return (1);
}
/* Get remaining output; reinitialize conversion descriptor */
iconv_src = (const char *)NULL;
inlen = 0;
if (iconv(iconv_cd, &iconv_src, &inlen, &iconv_trg, &outlen) ==
(size_t)-1) { /* Error occurred: didn't convert */
if (errno == EILSEQ)
(void) fprintf(stderr,
gettext("tar: invalid character in"
" UTF-8 conversion of '%s'\n"), starting_src);
else
(void) fprintf(stderr, gettext(
"tar: conversion to UTF-8 aborted for '%s'.\n"),
starting_src);
return (1);
}
*iconv_trg = '\0'; /* Null-terminate iconv output string */
if (strcmp(starting_src, target) != 0) {
*Xhdr_ptrptr = target;
xhdr_flgs |= xhdrflg;
#ifdef ICONV_DEBUG
(void) fprintf(stderr, "*** inlen: %d %d; outlen: %d %d\n",
strlen(starting_src), inlen, max_val, outlen);
(void) fprintf(stderr, "Input string:\n ");
for (inlen = 0; inlen < strlen(starting_src); inlen++) {
c_to_hex = (unsigned char)starting_src[inlen];
(void) fprintf(stderr, " %2.2x", c_to_hex);
if (inlen % 20 == 19)
(void) fprintf(stderr, "\n ");
}
(void) fprintf(stderr, "\nOutput string:\n ");
for (inlen = 0; inlen < strlen(target); inlen++) {
c_to_hex = (unsigned char)target[inlen];
(void) fprintf(stderr, " %2.2x", c_to_hex);
if (inlen % 20 == 19)
(void) fprintf(stderr, "\n ");
}
(void) fprintf(stderr, "\n");
#endif
}
return (0);
}
/*
* Function to test each byte of the source string to make sure it is
* in within bounds (value between 0 and 127).
* If valid, copy source to target.
*/
int
c_utf8(char *target, const char *source)
{
size_t len;
const char *thischar;
len = strlen(source);
thischar = source;
while (len-- > 0) {
if (!isascii((int)(*thischar++)))
return (1);
}
(void) strcpy(target, source);
return (0);
}
#if defined(O_XATTR)
#define ROUNDTOTBLOCK(a) ((a + (TBLOCK -1)) & ~(TBLOCK -1))
static void
prepare_xattr(
char **attrbuf,
char *filename,
char *attrpath,
char typeflag,
struct linkbuf *linkinfo,
int *rlen)
{
char *bufhead; /* ptr to full buffer */
char *aptr;
struct xattr_hdr *hptr; /* ptr to header in bufhead */
struct xattr_buf *tptr; /* ptr to pathing pieces */
int totalen; /* total buffer length */
int len; /* length returned to user */
int stringlen; /* length of filename + attr */
/*
* length of filename + attr
* in link section
*/
int linkstringlen;
int complen; /* length of pathing section */
int linklen; /* length of link section */
int attrnames_index; /* attrnames starting index */
/*
* Release previous buffer
*/
if (*attrbuf != (char *)NULL) {
free(*attrbuf);
*attrbuf = NULL;
}
/*
* First add in fixed size stuff
*/
len = sizeof (struct xattr_hdr) + sizeof (struct xattr_buf);
/*
* Add space for two nulls
*/
stringlen = strlen(attrpath) + strlen(filename) + 2;
complen = stringlen + sizeof (struct xattr_buf);
len += stringlen;
/*
* Now add on space for link info if any
*/
if (linkinfo != NULL) {
/*
* Again add space for two nulls
*/
linkstringlen = strlen(linkinfo->pathname) +
strlen(linkinfo->attrname) + 2;
linklen = linkstringlen + sizeof (struct xattr_buf);
len += linklen;
} else {
linklen = 0;
}
/*
* Now add padding to end to fill out TBLOCK
*
* Function returns size of real data and not size + padding.
*/
totalen = ROUNDTOTBLOCK(len);
if ((bufhead = calloc(1, totalen)) == NULL) {
fatal(gettext("Out of memory."));
}
/*
* Now we can fill in the necessary pieces
*/
/*
* first fill in the fixed header
*/
hptr = (struct xattr_hdr *)bufhead;
(void) sprintf(hptr->h_version, "%s", XATTR_ARCH_VERS);
(void) sprintf(hptr->h_component_len, "%0*d",
sizeof (hptr->h_component_len) - 1, complen);
(void) sprintf(hptr->h_link_component_len, "%0*d",
sizeof (hptr->h_link_component_len) - 1, linklen);
(void) sprintf(hptr->h_size, "%0*d", sizeof (hptr->h_size) - 1, len);
/*
* Now fill in the filename + attrnames section
* The filename and attrnames section can be composed of two or more
* path segments separated by a null character. The first segment
* is the path to the parent file that roots the entire sequence in
* the normal name space. The remaining segments describes a path
* rooted at the hidden extended attribute directory of the leaf file of
* the previous segment, making it possible to name attributes on
* attributes. Thus, if we are just archiving an extended attribute,
* the second segment will contain the attribute name. If we are
* archiving a system attribute of an extended attribute, then the
* second segment will contain the attribute name, and a third segment
* will contain the system attribute name. The attribute pathing
* information is obtained from 'attrpath'.
*/
tptr = (struct xattr_buf *)(bufhead + sizeof (struct xattr_hdr));
(void) sprintf(tptr->h_namesz, "%0*d", sizeof (tptr->h_namesz) - 1,
stringlen);
(void) strcpy(tptr->h_names, filename);
attrnames_index = strlen(filename) + 1;
(void) strcpy(&tptr->h_names[attrnames_index], attrpath);
tptr->h_typeflag = typeflag;
/*
* Split the attrnames section into two segments if 'attrpath'
* contains pathing information for a system attribute of an
* extended attribute. We split them by replacing the '/' with
* a '\0'.
*/
if ((aptr = strpbrk(&tptr->h_names[attrnames_index], "/")) != NULL) {
*aptr = '\0';
}
/*
* Now fill in the optional link section if we have one
*/
if (linkinfo != (struct linkbuf *)NULL) {
tptr = (struct xattr_buf *)(bufhead +
sizeof (struct xattr_hdr) + complen);
(void) sprintf(tptr->h_namesz, "%0*d",
sizeof (tptr->h_namesz) - 1, linkstringlen);
(void) strcpy(tptr->h_names, linkinfo->pathname);
(void) strcpy(
&tptr->h_names[strlen(linkinfo->pathname) + 1],
linkinfo->attrname);
tptr->h_typeflag = typeflag;
}
*attrbuf = (char *)bufhead;
*rlen = len;
}
#else
static void
prepare_xattr(
char **attrbuf,
char *filename,
char *attrname,
char typeflag,
struct linkbuf *linkinfo,
int *rlen)
{
*attrbuf = NULL;
*rlen = 0;
}
#endif
int
getstat(int dirfd, char *longname, char *shortname, char *attrparent)
{
int i, j;
int printerr;
int slnkerr;
struct stat symlnbuf;
if (!hflag)
i = fstatat(dirfd, shortname, &stbuf, AT_SYMLINK_NOFOLLOW);
else
i = fstatat(dirfd, shortname, &stbuf, 0);
if (i < 0) {
/* Initialize flag to print error mesg. */
printerr = 1;
/*
* If stat is done, then need to do lstat
* to determine whether it's a sym link
*/
if (hflag) {
/* Save returned error */
slnkerr = errno;
j = fstatat(dirfd, shortname,
&symlnbuf, AT_SYMLINK_NOFOLLOW);
/*
* Suppress error message when file is a symbolic link
* and function modifier 'l' is off. Exception: when
* a symlink points to a symlink points to a
* symlink ... and we get past MAXSYMLINKS. That
* error will cause a file not to be archived, and
* needs to be printed.
*/
if ((j == 0) && (!linkerrok) && (slnkerr != ELOOP) &&
(S_ISLNK(symlnbuf.st_mode)))
printerr = 0;
/*
* Restore errno in case the lstat
* on symbolic link change
*/
errno = slnkerr;
}
if (printerr) {
(void) fprintf(stderr, gettext(
"tar: %s%s%s%s: %s\n"),
(attrparent == NULL) ? "" : gettext("attribute "),
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : gettext(" of "),
longname, strerror(errno));
Errflg = 1;
}
return (1);
}
return (0);
}
/*
* Recursively archive the extended attributes and/or extended system attributes
* of the base file, longname. Note: extended system attribute files will be
* archived only if the extended system attributes are not transient (i.e. the
* extended system attributes are other than the default values).
*
* If -@ was specified and the underlying file system supports it, archive the
* extended attributes, and if there is a system attribute associated with the
* extended attribute, then recursively call xattrs_put() to archive the
* hidden attribute directory and the extended system attribute. If -/ was
* specified and the underlying file system supports it, archive the extended
* system attributes. Read-only extended system attributes are never archived.
*
* Currently, there cannot be attributes on attributes; only system
* attributes on attributes. In addition, there cannot be attributes on
* system attributes. A file and it's attribute directory hierarchy looks as
* follows:
* longname ----> . ("." is the hidden attribute directory)
* |
* ----------------------------
* | |
* <sys_attr_name> <attr_name> ----> .
* |
* <sys_attr_name>
*
*/
#if defined(O_XATTR)
static void
xattrs_put(char *longname, char *shortname, char *parent, char *attrparent)
{
char *filename = (attrparent == NULL) ? shortname : attrparent;
int arc_rwsysattr = 0;
int dirfd;
int fd = -1;
int rw_sysattr = 0;
int ext_attr = 0;
int rc;
DIR *dirp;
struct dirent *dp;
attr_data_t *attrinfo = NULL;
/*
* If the underlying file system supports it, then archive the extended
* attributes if -@ was specified, and the extended system attributes
* if -/ was specified.
*/
if (verify_attr_support(filename, (attrparent == NULL), ARC_CREATE,
&ext_attr) != ATTR_OK) {
return;
}
/*
* Only want to archive a read-write extended system attribute file
* if it contains extended system attribute settings that are not the
* default values.
*/
#if defined(_PC_SATTR_ENABLED)
if (saflag) {
int filefd;
nvlist_t *slist = NULL;
/* Determine if there are non-transient system attributes */
errno = 0;
if ((filefd = open(filename, O_RDONLY)) == -1) {
if (attrparent == NULL) {
vperror(0, gettext(
"unable to open file %s"), longname);
}
return;
}
if (((slist = sysattr_list(basename(myname), filefd,
filename)) != NULL) || (errno != 0)) {
arc_rwsysattr = 1;
}
if (slist != NULL) {
(void) nvlist_free(slist);
slist = NULL;
}
(void) close(filefd);
}
/*
* If we aren't archiving extended system attributes, and we are
* processing an attribute, or if we are archiving extended system
* attributes, and there are are no extended attributes, then there's
* no need to open up the attribute directory of the file unless the
* extended system attributes are not transient (i.e, the system
* attributes are not the default values).
*/
if ((arc_rwsysattr == 0) && ((attrparent != NULL) ||
(saflag && !ext_attr))) {
return;
}
#endif /* _PC_SATTR_ENABLED */
/* open the parent attribute directory */
fd = attropen(filename, ".", O_RDONLY);
if (fd < 0) {
vperror(0, gettext(
"unable to open attribute directory for %s%s%sfile %s"),
(attrparent == NULL) ? "" : gettext("attribute "),
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : gettext(" of "),
longname);
return;
}
/*
* We need to change into the parent's attribute directory to determine
* if each of the attributes should be archived.
*/
if (fchdir(fd) < 0) {
vperror(0, gettext(
"cannot change to attribute directory of %s%s%sfile %s"),
(attrparent == NULL) ? "" : gettext("attribute "),
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : gettext(" of "),
longname);
(void) close(fd);
return;
}
if (((dirfd = dup(fd)) == -1) ||
((dirp = fdopendir(dirfd)) == NULL)) {
(void) fprintf(stderr, gettext(
"tar: unable to open dir pointer for %s%s%sfile %s\n"),
(attrparent == NULL) ? "" : gettext("attribute "),
(attrparent == NULL) ? "" : attrparent,
(attrparent == NULL) ? "" : gettext(" of "),
longname);
if (fd > 0) {
(void) close(fd);
}
return;
}
while ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name, "..") == 0) {
continue;
} else if (strcmp(dp->d_name, ".") == 0) {
Hiddendir = 1;
} else {
Hiddendir = 0;
}
/* Determine if this attribute should be archived */
if (verify_attr(dp->d_name, attrparent, arc_rwsysattr,
&rw_sysattr) != ATTR_OK) {
continue;
}
/* gather the attribute's information to pass to putfile() */
if ((fill_in_attr_info(dp->d_name, longname, attrparent,
fd, rw_sysattr, &attrinfo)) == 1) {
continue;
}
/* add the attribute to the archive */
rc = putfile(longname, dp->d_name, parent, attrinfo,
XATTR_FILE, LEV0, SYMLINK_LEV0);
if (exitflag) {
break;
}
#if defined(_PC_SATTR_ENABLED)
/*
* If both -/ and -@ were specified, then archive the
* attribute's extended system attributes and hidden directory
* by making a recursive call to xattrs_put().
*/
if (!rw_sysattr && saflag && atflag && (rc != PUT_AS_LINK) &&
(Hiddendir == 0)) {
xattrs_put(longname, shortname, parent, dp->d_name);
/*
* Change back to the parent's attribute directory
* to process any further attributes.
*/
if (fchdir(fd) < 0) {
vperror(0, gettext(
"cannot change back to attribute directory "
"of file %s"), longname);
break;
}
}
#endif /* _PC_SATTR_ENABLED */
}
if (attrinfo != NULL) {
if (attrinfo->attr_parent != NULL) {
free(attrinfo->attr_parent);
}
free(attrinfo->attr_path);
free(attrinfo);
}
(void) closedir(dirp);
if (fd != -1) {
(void) close(fd);
}
/* Change back to the parent directory of the base file */
if (attrparent == NULL) {
(void) tar_chdir(parent);
}
Hiddendir = 0;
}
#else
static void
xattrs_put(char *longname, char *shortname, char *parent, char *attrppath)
{
}
#endif /* O_XATTR */
static int
put_link(char *name, char *longname, char *component, char *longattrname,
char *prefix, int filetype, char type)
{
if (stbuf.st_nlink > 1) {
struct linkbuf *lp;
int found = 0;
for (lp = ihead; lp != NULL; lp = lp->nextp)
if (lp->inum == stbuf.st_ino &&
lp->devnum == stbuf.st_dev) {
found++;
break;
}
if (found) {
#if defined(O_XATTR)
if (filetype == XATTR_FILE)
if (put_xattr_hdr(longname, component,
longattrname, prefix, type, filetype, lp)) {
goto out;
}
#endif
stbuf.st_size = (off_t)0;
if (filetype != XATTR_FILE) {
tomodes(&stbuf);
if (chk_path_build(name, longname, lp->pathname,
prefix, type, filetype) > 0) {
goto out;
}
}
if (mulvol && tapepos + 1 >= blocklim)
newvol();
(void) writetbuf((char *)&dblock, 1);
/*
* write_ancillary() is not needed here.
* The first link is handled in the following
* else statement. No need to process ACLs
* for other hard links since they are the
* same file.
*/
if (vflag) {
if (NotTape)
dlog("seek = %" FMT_blkcnt_t
"K\n", K(tapepos));
if (filetype == XATTR_FILE) {
(void) fprintf(vfile, gettext(
"a %s attribute %s link to "
"%s attribute %s\n"),
name, component, name,
lp->attrname);
} else {
(void) fprintf(vfile, gettext(
"a %s link to %s\n"),
longname, lp->pathname);
}
}
lp->count--;
return (0);
} else {
lp = (struct linkbuf *)getmem(sizeof (*lp));
if (lp != (struct linkbuf *)NULL) {
lp->nextp = ihead;
ihead = lp;
lp->inum = stbuf.st_ino;
lp->devnum = stbuf.st_dev;
lp->count = stbuf.st_nlink - 1;
if (filetype == XATTR_FILE) {
(void) strcpy(lp->pathname, longname);
(void) strcpy(lp->attrname,
component);
} else {
(void) strcpy(lp->pathname, longname);
(void) strcpy(lp->attrname, "");
}
}
}
}
out:
return (1);
}
static int
put_extra_attributes(char *longname, char *shortname, char *longattrname,
char *prefix, int filetype, char typeflag)
{
static acl_t *aclp = NULL;
int error;
if (aclp != NULL) {
acl_free(aclp);
aclp = NULL;
}
#if defined(O_XATTR)
if ((atflag || saflag) && (filetype == XATTR_FILE)) {
if (put_xattr_hdr(longname, shortname, longattrname, prefix,
typeflag, filetype, NULL)) {
return (1);
}
}
#endif
/* ACL support */
if (pflag) {
char *secinfo = NULL;
int len = 0;
/* ACL support */
if (((stbuf.st_mode & S_IFMT) != S_IFLNK)) {
/*
* Get ACL info: dont bother allocating space if
* there is only a trivial ACL.
*/
if ((error = acl_get(shortname, ACL_NO_TRIVIAL,
&aclp)) != 0) {
(void) fprintf(stderr, gettext(
"%s: failed to retrieve acl : %s\n"),
longname, acl_strerror(error));
return (1);
}
}
/* append security attributes if any */
if (aclp != NULL) {
(void) append_secattr(&secinfo, &len, acl_cnt(aclp),
acl_totext(aclp, ACL_APPEND_ID | ACL_COMPACT_FMT |
ACL_SID_FMT), (acl_type(aclp) == ACLENT_T) ?
UFSD_ACL : ACE_ACL);
}
if (Tflag) {
/* append Trusted Extensions extended attributes */
append_ext_attr(shortname, &secinfo, &len);
(void) write_ancillary(&dblock, secinfo, len, ACL_HDR);
} else if (aclp != NULL) {
(void) write_ancillary(&dblock, secinfo, len, ACL_HDR);
}
}
return (0);
}
#if defined(O_XATTR)
static int
put_xattr_hdr(char *longname, char *shortname, char *longattrname, char *prefix,
int typeflag, int filetype, struct linkbuf *lp)
{
char *lname = NULL;
char *sname = NULL;
int error = 0;
static char *attrbuf = NULL;
int attrlen;
lname = malloc(sizeof (char) * strlen("/dev/null") + 1 +
strlen(shortname) + strlen(".hdr") + 1);
if (lname == NULL) {
fatal(gettext("Out of Memory."));
}
sname = malloc(sizeof (char) * strlen(shortname) +
strlen(".hdr") + 1);
if (sname == NULL) {
fatal(gettext("Out of Memory."));
}
(void) sprintf(sname, "%s.hdr", shortname);
(void) sprintf(lname, "/dev/null/%s", sname);
if (strlcpy(dblock.dbuf.name, lname, sizeof (dblock.dbuf.name)) >=
sizeof (dblock.dbuf.name)) {
fatal(gettext(
"Buffer overflow writing extended attribute file name"));
}
/*
* dump extended attr lookup info
*/
prepare_xattr(&attrbuf, longname, longattrname, typeflag, lp, &attrlen);
write_ancillary(&dblock, attrbuf, attrlen, _XATTR_HDRTYPE);
(void) sprintf(lname, "/dev/null/%s", shortname);
(void) strncpy(dblock.dbuf.name, sname, NAMSIZ);
/*
* Set up filename for attribute
*/
error = build_dblock(lname, tchar, '0', filetype,
&stbuf, stbuf.st_dev, prefix);
free(lname);
free(sname);
return (error);
}
#endif
#if defined(O_XATTR)
static int
read_xattr_hdr(attr_data_t **attrinfo)
{
char buf[TBLOCK];
char *attrparent = NULL;
blkcnt_t blocks;
char *tp;
off_t bytes;
int comp_len, link_len;
int namelen;
int attrparentlen;
int parentfilelen;
if (dblock.dbuf.typeflag != _XATTR_HDRTYPE)
return (1);
bytes = stbuf.st_size;
if ((xattrhead = calloc(1, (int)bytes)) == NULL) {
(void) fprintf(stderr, gettext(
"Insufficient memory for extended attribute\n"));
return (1);
}
tp = (char *)xattrhead;
blocks = TBLOCKS(bytes);
while (blocks-- > 0) {
readtape(buf);
if (bytes <= TBLOCK) {
(void) memcpy(tp, buf, (size_t)bytes);
break;
} else {
(void) memcpy(tp, buf, TBLOCK);
tp += TBLOCK;
}
bytes -= TBLOCK;
}
/*
* Validate that we can handle header format
*/
if (strcmp(xattrhead->h_version, XATTR_ARCH_VERS) != 0) {
(void) fprintf(stderr,
gettext("Unknown extended attribute format encountered\n"));
(void) fprintf(stderr,
gettext("Disabling extended attribute parsing\n"));
xattrbadhead = 1;
return (0);
}
(void) sscanf(xattrhead->h_component_len, "%10d", &comp_len);
(void) sscanf(xattrhead->h_link_component_len, "%10d", &link_len);
xattrp = (struct xattr_buf *)(((char *)xattrhead) +
sizeof (struct xattr_hdr));
(void) sscanf(xattrp->h_namesz, "%7d", &namelen);
if (link_len > 0)
xattr_linkp = (struct xattr_buf *)
((int)xattrp + (int)comp_len);
else
xattr_linkp = NULL;
/*
* Gather the attribute path from the filename and attrnames section.
* The filename and attrnames section can be composed of two or more
* path segments separated by a null character. The first segment
* is the path to the parent file that roots the entire sequence in
* the normal name space. The remaining segments describes a path
* rooted at the hidden extended attribute directory of the leaf file of
* the previous segment, making it possible to name attributes on
* attributes.
*/
parentfilelen = strlen(xattrp->h_names);
xattrapath = xattrp->h_names + parentfilelen + 1;
if ((strlen(xattrapath) + parentfilelen + 2) < namelen) {
/*
* The attrnames section contains a system attribute on an
* attribute. Save the name of the attribute for use later,
* and replace the null separating the attribute name from
* the system attribute name with a '/' so that xattrapath can
* be used to display messages with the full attribute path name
* rooted at the hidden attribute directory of the base file
* in normal name space.
*/
attrparent = strdup(xattrapath);
attrparentlen = strlen(attrparent);
xattrapath[attrparentlen] = '/';
}
if ((fill_in_attr_info((attrparent == NULL) ? xattrapath :
xattrapath + attrparentlen + 1, xattrapath, attrparent,
-1, 0, attrinfo)) == 1) {
free(attrparent);
return (1);
}
/* Gather link info */
if (xattr_linkp) {
xattr_linkaname = xattr_linkp->h_names +
strlen(xattr_linkp->h_names) + 1;
} else {
xattr_linkaname = NULL;
}
return (0);
}
#else
static int
read_xattr_hdr(attr_data_t **attrinfo)
{
return (0);
}
#endif
/*
* skip over extra slashes in string.
*
* For example:
* /usr/tmp/////
*
* would return pointer at
* /usr/tmp/////
* ^
*/
static char *
skipslashes(char *string, char *start)
{
while ((string > start) && *(string - 1) == '/') {
string--;
}
return (string);
}
/*
* Return the parent directory of a given path.
*
* Examples:
* /usr/tmp return /usr
* /usr/tmp/file return /usr/tmp
* / returns .
* /usr returns /
* file returns .
*
* dir is assumed to be at least as big as path.
*/
static void
get_parent(char *path, char *dir)
{
char *s;
char tmpdir[PATH_MAX + 1];
if (strlen(path) > PATH_MAX) {
fatal(gettext("pathname is too long"));
}
(void) strcpy(tmpdir, path);
chop_endslashes(tmpdir);
if ((s = strrchr(tmpdir, '/')) == NULL) {
(void) strcpy(dir, ".");
} else {
s = skipslashes(s, tmpdir);
*s = '\0';
if (s == tmpdir)
(void) strcpy(dir, "/");
else
(void) strcpy(dir, tmpdir);
}
}
#if defined(O_XATTR)
static char *
get_component(char *path)
{
char *ptr;
ptr = strrchr(path, '/');
if (ptr == NULL) {
return (path);
} else {
/*
* Handle trailing slash
*/
if (*(ptr + 1) == '\0')
return (ptr);
else
return (ptr + 1);
}
}
#else
static char *
get_component(char *path)
{
return (path);
}
#endif
#if defined(O_XATTR)
static int
retry_open_attr(int pdirfd, int cwd, char *dirp, char *pattr, char *name,
int oflag, mode_t mode)
{
int dirfd;
int ofilefd = -1;
struct timeval times[2];
mode_t newmode;
struct stat parentstat;
acl_t *aclp = NULL;
int error;
/*
* We couldn't get to attrdir. See if its
* just a mode problem on the parent file.
* for example: a mode such as r-xr--r--
* on a ufs file system without extended
* system attribute support won't let us
* create an attribute dir if it doesn't
* already exist, and on a ufs file system
* with extended system attribute support
* won't let us open the attribute for
* write.
*
* If file has a non-trivial ACL, then save it
* off so that we can place it back on after doing
* chmod's.
*/
if ((dirfd = openat(cwd, (pattr == NULL) ? dirp : pattr,
O_RDONLY)) == -1) {
return (-1);
}
if (fstat(dirfd, &parentstat) == -1) {
(void) fprintf(stderr, gettext(
"tar: cannot stat %sfile %s: %s\n"),
(pdirfd == -1) ? "" : gettext("parent of "),
(pdirfd == -1) ? dirp : name, strerror(errno));
return (-1);
}
if ((error = facl_get(dirfd, ACL_NO_TRIVIAL, &aclp)) != 0) {
(void) fprintf(stderr, gettext(
"tar: failed to retrieve ACL on %sfile %s: %s\n"),
(pdirfd == -1) ? "" : gettext("parent of "),
(pdirfd == -1) ? dirp : name, strerror(errno));
return (-1);
}
newmode = S_IWUSR | parentstat.st_mode;
if (fchmod(dirfd, newmode) == -1) {
(void) fprintf(stderr,
gettext(
"tar: cannot fchmod %sfile %s to %o: %s\n"),
(pdirfd == -1) ? "" : gettext("parent of "),
(pdirfd == -1) ? dirp : name, newmode, strerror(errno));
if (aclp)
acl_free(aclp);
return (-1);
}
if (pdirfd == -1) {
/*
* We weren't able to create the attribute directory before.
* Now try again.
*/
ofilefd = attropen(dirp, ".", oflag);
} else {
/*
* We weren't able to create open the attribute before.
* Now try again.
*/
ofilefd = openat(pdirfd, name, oflag, mode);
}
/*
* Put mode back to original
*/
if (fchmod(dirfd, parentstat.st_mode) == -1) {
(void) fprintf(stderr,
gettext("tar: cannot chmod %sfile %s to %o: %s\n"),
(pdirfd == -1) ? "" : gettext("parent of "),
(pdirfd == -1) ? dirp : name, newmode, strerror(errno));
}
if (aclp) {
error = facl_set(dirfd, aclp);
if (error) {
(void) fprintf(stderr,
gettext("tar: failed to set acl entries on "
"%sfile %s\n"),
(pdirfd == -1) ? "" : gettext("parent of "),
(pdirfd == -1) ? dirp : name);
}
acl_free(aclp);
}
/*
* Put back time stamps
*/
times[0].tv_sec = parentstat.st_atime;
times[0].tv_usec = 0;
times[1].tv_sec = parentstat.st_mtime;
times[1].tv_usec = 0;
(void) futimesat(cwd, (pattr == NULL) ? dirp : pattr, times);
(void) close(dirfd);
return (ofilefd);
}
#endif
#if !defined(O_XATTR)
static int
openat64(int fd, const char *name, int oflag, mode_t cmode)
{
return (open64(name, oflag, cmode));
}
static int
openat(int fd, const char *name, int oflag, mode_t cmode)
{
return (open(name, oflag, cmode));
}
static int
fchownat(int fd, const char *name, uid_t owner, gid_t group, int flag)
{
if (flag == AT_SYMLINK_NOFOLLOW)
return (lchown(name, owner, group));
else
return (chown(name, owner, group));
}
static int
renameat(int fromfd, char *old, int tofd, char *new)
{
return (rename(old, new));
}
static int
futimesat(int fd, char *path, struct timeval times[2])
{
return (utimes(path, times));
}
static int
unlinkat(int dirfd, char *path, int flag)
{
if (flag == AT_REMOVEDIR)
return (rmdir(path));
else
return (unlink(path));
}
static int
fstatat(int fd, char *path, struct stat *buf, int flag)
{
if (flag == AT_SYMLINK_NOFOLLOW)
return (lstat(path, buf));
else
return (stat(path, buf));
}
static int
attropen(char *file, char *attr, int omode, mode_t cmode)
{
errno = ENOTSUP;
return (-1);
}
#endif
static void
chop_endslashes(char *path)
{
char *end, *ptr;
/*
* Chop of slashes, but not if all we have is slashes
* for example: ////
* should make no changes, otherwise it will screw up
* checkdir
*/
end = &path[strlen(path) -1];
if (*end == '/' && end != path) {
ptr = skipslashes(end, path);
if (ptr != NULL && ptr != path) {
*ptr = '\0';
}
}
}
/* Trusted Extensions */
/*
* append_ext_attr():
*
* Append extended attributes and other information into the buffer
* that gets written to the ancillary file.
*
* With option 'T', we create a tarfile which
* has an ancillary file each corresponding archived file.
* Each ancillary file contains 1 or more of the
* following attributes:
*
* attribute type attribute process procedure
* ---------------- ---------------- --------------------------
* DIR_TYPE = 'D' directory flag append if a directory
* LBL_TYPE = 'L' SL[IL] or SL append ascii label
*
*
*/
static void
append_ext_attr(char *shortname, char **secinfo, int *len)
{
bslabel_t b_slabel; /* binary sensitvity label */
char *ascii = NULL; /* ascii label */
/*
* For each attribute type, append it if it is
* relevant to the file type.
*/
/*
* For attribute type DIR_TYPE,
* append it to the following file type:
*
* S_IFDIR: directories
*/
/*
* For attribute type LBL_TYPE,
* append it to the following file type:
*
* S_IFDIR: directories (including mld, sld)
* S_IFLNK: symbolic link
* S_IFREG: regular file but not hard link
* S_IFIFO: FIFO file but not hard link
* S_IFCHR: char special file but not hard link
* S_IFBLK: block special file but not hard link
*/
switch (stbuf.st_mode & S_IFMT) {
case S_IFDIR:
/*
* append DIR_TYPE
*/
(void) append_secattr(secinfo, len, 1,
"\0", DIR_TYPE);
/*
* Get and append attribute types LBL_TYPE.
* For directories, LBL_TYPE contains SL.
*/
/* get binary sensitivity label */
if (getlabel(shortname, &b_slabel) != 0) {
(void) fprintf(stderr,
gettext("tar: can't get sensitvity label for "
" %s, getlabel() error: %s\n"),
shortname, strerror(errno));
} else {
/* get ascii SL */
if (bsltos(&b_slabel, &ascii,
0, 0) <= 0) {
(void) fprintf(stderr,
gettext("tar: can't get ascii SL for"
" %s\n"), shortname);
} else {
/* append LBL_TYPE */
(void) append_secattr(secinfo, len,
strlen(ascii) + 1, ascii,
LBL_TYPE);
/* free storage */
if (ascii != NULL) {
free(ascii);
ascii = (char *)0;
}
}
}
break;
case S_IFLNK:
case S_IFREG:
case S_IFIFO:
case S_IFCHR:
case S_IFBLK:
/* get binary sensitivity label */
if (getlabel(shortname, &b_slabel) != 0) {
(void) fprintf(stderr,
gettext("tar: can't get sensitivty label for %s, "
"getlabel() error: %s\n"),
shortname, strerror(errno));
} else {
/* get ascii IL[SL] */
if (bsltos(&b_slabel, &ascii, 0, 0) <= 0) {
(void) fprintf(stderr,
gettext("tar: can't translate sensitivity "
" label for %s\n"), shortname);
} else {
char *cmw_label;
size_t cmw_length;
cmw_length = strlen("ADMIN_LOW [] ") +
strlen(ascii);
if ((cmw_label = malloc(cmw_length)) == NULL) {
(void) fprintf(stderr, gettext(
"Insufficient memory for label\n"));
exit(1);
}
/* append LBL_TYPE */
(void) snprintf(cmw_label, cmw_length,
"ADMIN_LOW [%s]", ascii);
(void) append_secattr(secinfo, len,
strlen(cmw_label) + 1, cmw_label,
LBL_TYPE);
/* free storage */
if (ascii != NULL) {
free(cmw_label);
free(ascii);
ascii = (char *)0;
}
}
}
break;
default:
break;
} /* end switch for LBL_TYPE */
/* DONE !! */
return;
} /* end of append_ext_attr */
/*
* Name: extract_attr()
*
* Description:
* Process attributes from the ancillary file due to
* the T option.
*
* Call by doxtract() as part of the switch case structure.
* Making this a separate routine because the nesting are too
* deep in doxtract, thus, leaving very little space
* on each line for instructions.
*
* With option 'T', we extract from a TS 8 or TS 2.5 ancillary file
*
* For option 'T', following are possible attributes in
* a TS 8 ancillary file: (NOTE: No IL support)
*
* attribute type attribute process procedure
* ---------------- ---------------- -------------------------
* # LBL_TYPE = 'L' SL construct binary label
* # APRIV_TYPE = 'P' allowed priv construct privileges
* # FPRIV_TYPE = 'p' forced priv construct privileges
* # COMP_TYPE = 'C' path component construct real path
* # DIR_TYPE = 'D' directory flag note it is a directory
* $ UFSD_ACL = '1' ACL data construct ACL entries
* ATTR_FLAG_TYPE = 'F' file attr flags construct binary flags
* LK_COMP_TYPE = 'K' linked path comp construct linked real path
*
* note: # = attribute names common between TS 8 & TS 2.5 ancillary
* files.
* $ = ACL attribute is processed for the option 'p', it doesn't
* need option 'T'.
*
* Trusted Extensions ignores APRIV_TYPE, FPRIV_TYPE, and ATTR_FLAG_TYPE
*
*/
static void
extract_attr(char **file_ptr, struct sec_attr *attr)
{
int reterr, err;
char *dummy_buf; /* for attribute extract */
dummy_buf = attr->attr_info;
switch (attr->attr_type) {
case DIR_TYPE:
dir_flag++;
break;
case LBL_TYPE:
/*
* LBL_TYPE is used to indicate SL for directory, and
* CMW label for other file types.
*/
if (!dir_flag) { /* not directory */
/* Skip over IL portion */
char *sl_ptr = strchr(dummy_buf, '[');
if (sl_ptr == NULL)
err = 0;
else
err = stobsl(sl_ptr, &bs_label,
NEW_LABEL, &reterr);
} else { /* directory */
err = stobsl(dummy_buf, &bs_label,
NEW_LABEL, &reterr);
}
if (err == 0) {
(void) fprintf(stderr, gettext("tar: "
"can't convert %s to binary label\n"),
dummy_buf);
bslundef(&bs_label);
} else if (!blequal(&bs_label, &admin_low) &&
!blequal(&bs_label, &admin_high)) {
bslabel_t *from_label;
char *buf;
char tempbuf[MAXPATHLEN];
if (*orig_namep != '/') {
/* got relative linked to path */
(void) getcwd(tempbuf, (sizeof (tempbuf)));
(void) strncat(tempbuf, "/", MAXPATHLEN);
} else
*tempbuf = '\0';
buf = real_path;
(void) strncat(tempbuf, orig_namep, MAXPATHLEN);
from_label = getlabelbypath(tempbuf);
if (from_label != NULL) {
if (blequal(from_label, &admin_low)) {
if ((getpathbylabel(tempbuf, buf,
MAXPATHLEN, &bs_label) == NULL)) {
(void) fprintf(stderr,
gettext("tar: "
"can't get zone root path for "
"%s\n"), tempbuf);
} else
rpath_flag = 1;
}
free(from_label);
}
}
break;
case COMP_TYPE:
rebuild_comp_path(dummy_buf, file_ptr);
break;
case LK_COMP_TYPE:
if (rebuild_lk_comp_path(dummy_buf, file_ptr)
== 0) {
lk_rpath_flag = 1;
} else {
(void) fprintf(stderr, gettext("tar: warning: link's "
"target pathname might be invalid.\n"));
lk_rpath_flag = 0;
}
break;
case APRIV_TYPE:
ignored_aprivs++;
break;
case FPRIV_TYPE:
ignored_fprivs++;
break;
case ATTR_FLAG_TYPE:
ignored_fattrs++;
break;
default:
break;
}
/* done */
return;
} /* end extract_attr */
/*
* Name: rebuild_comp_path()
*
* Description:
* Take the string of components passed down by the calling
* routine and parse the values and rebuild the path.
* This routine no longer needs to produce a new real_path
* string because it is produced when the 'L' LABEL_TYPE is
* interpreted. So the only thing done here is to distinguish
* between an SLD and an MLD entry. We only want one, so we
* ignore the MLD entry by setting the mld_flag.
*
* return value:
* none
*/
static void
rebuild_comp_path(char *str, char **namep)
{
char *cp;
while (*str != '\0') {
switch (*str) {
case MLD_TYPE:
str++;
if ((cp = strstr(str, ";;")) != NULL) {
*cp = '\0';
str = cp + 2;
*cp = ';';
}
mld_flag = 1;
break;
case SLD_TYPE:
str++;
if ((cp = strstr(str, ";;")) != NULL) {
*cp = '\0';
str = cp + 2;
*cp = ';';
}
mld_flag = 0;
break;
case PATH_TYPE:
str++;
if ((cp = strstr(str, ";;")) != NULL) {
*cp = '\0';
str = cp + 2;
*cp = ';';
}
break;
}
}
if (rpath_flag)
*namep = real_path;
return;
} /* end rebuild_comp_path() */
/*
* Name: rebuild_lk_comp_path()
*
* Description:
* Take the string of components passed down by the calling
* routine and parse the values and rebuild the path.
*
* return value:
* 0 = succeeded
* -1 = failed
*/
static int
rebuild_lk_comp_path(char *str, char **namep)
{
char *cp;
int reterr;
bslabel_t bslabel;
char *buf;
char pbuf[MAXPATHLEN];
char *ptr1, *ptr2;
int plen;
int use_pbuf;
char tempbuf[MAXPATHLEN];
int mismatch;
bslabel_t *from_label;
char zonename[ZONENAME_MAX];
zoneid_t zoneid;
/* init stuff */
use_pbuf = 0;
mismatch = 0;
/*
* For linked to pathname (LK_COMP_TYPE):
* - If the linked to pathname is absolute (start with /), we
* will use it as is.
* - If it is a relative pathname then it is relative to 1 of 2
* directories. For a hardlink, it is relative to the current
* directory. For a symbolic link, it is relative to the
* directory the symbolic link is in. For the symbolic link
* case, set a flag to indicate we need to use the prefix of
* the restored file's pathname with the linked to pathname.
*
* NOTE: At this point, we have no way to determine if we have
* a hardlink or a symbolic link. We will compare the 1st
* component in the prefix portion of the restore file's
* pathname to the 1st component in the attribute data
* (the linked pathname). If they are the same, we will assume
* the link pathname to reconstruct is relative to the current
* directory. Otherwise, we will set a flag indicate we need
* to use a prefix with the reconstructed name. Need to compare
* both the adorned and unadorned version before deciding a
* mismatch.
*/
buf = lk_real_path;
if (*(str + 1) != '/') { /* got relative linked to path */
ptr1 = orig_namep;
ptr2 = strrchr(ptr1, '/');
plen = ptr2 - ptr1;
if (plen > 0) {
pbuf[0] = '\0';
plen++; /* include '/' */
(void) strncpy(pbuf, ptr1, plen);
*(pbuf + plen) = '\0';
ptr2 = strchr(pbuf, '/');
if (strncmp(pbuf, str + 1, ptr2 - pbuf) != 0)
mismatch = 1;
}
if (mismatch == 1)
use_pbuf = 1;
}
buf[0] = '\0';
while (*str != '\0') {
switch (*str) {
case MLD_TYPE:
str++;
if ((cp = strstr(str, ";;")) != NULL) {
*cp = '\0';
/*
* Ignore attempts to backup over .MLD.
*/
if (strcmp(str, "../") != 0)
(void) strncat(buf, str, MAXPATHLEN);
str = cp + 2;
*cp = ';';
}
break;
case SLD_TYPE:
str++;
if ((cp = strstr(str, ";;")) != NULL) {
*cp = '\0';
/*
* Use the path name in the header if
* error occurs when processing the
* SLD type.
*/
if (!stobsl(str, &bslabel,
NO_CORRECTION, &reterr)) {
(void) fprintf(stderr, gettext(
"tar: can't translate to binary"
"SL for SLD, stobsl() error:"
" %s\n"), strerror(errno));
return (-1);
}
str = cp + 2;
*cp = ';';
if (use_pbuf == 1) {
if (*pbuf != '/') {
/* relative linked to path */
(void) getcwd(tempbuf,
(sizeof (tempbuf)));
(void) strncat(tempbuf, "/",
MAXPATHLEN);
(void) strncat(tempbuf, pbuf,
MAXPATHLEN);
}
else
(void) strcpy(tempbuf, pbuf);
} else if (*buf != '/') {
/* got relative linked to path */
(void) getcwd(tempbuf,
(sizeof (tempbuf)));
(void) strncat(tempbuf, "/",
MAXPATHLEN);
} else
*tempbuf = '\0';
(void) strncat(tempbuf, buf, MAXPATHLEN);
*buf = '\0';
if (blequal(&bslabel, &admin_high)) {
bslabel = admin_low;
}
/*
* Check for cross-zone symbolic links
*/
from_label = getlabelbypath(real_path);
if (rpath_flag && (from_label != NULL) &&
!blequal(&bslabel, from_label)) {
if ((zoneid =
getzoneidbylabel(&bslabel)) == -1) {
(void) fprintf(stderr,
gettext("tar: can't get "
"zone ID for %s\n"),
tempbuf);
return (-1);
}
if (zone_getattr(zoneid, ZONE_ATTR_NAME,
&zonename, ZONENAME_MAX) == -1) {
/* Badly configured zone info */
(void) fprintf(stderr,
gettext("tar: can't get "
"zonename for %s\n"),
tempbuf);
return (-1);
}
(void) strncpy(buf, AUTO_ZONE,
MAXPATHLEN);
(void) strncat(buf, "/",
MAXPATHLEN);
(void) strncat(buf, zonename,
MAXPATHLEN);
}
if (from_label != NULL)
free(from_label);
(void) strncat(buf, tempbuf, MAXPATHLEN);
break;
}
mld_flag = 0;
break;
case PATH_TYPE:
str++;
if ((cp = strstr(str, ";;")) != NULL) {
*cp = '\0';
(void) strncat(buf, str, MAXPATHLEN);
str = cp + 2;
*cp = ';';
}
break;
default:
(void) fprintf(stderr, gettext(
"tar: error rebuilding path %s\n"),
*namep);
*buf = '\0';
str++;
return (-1);
}
}
/*
* Done for LK_COMP_TYPE
*/
return (0); /* component path is rebuilt successfully */
} /* end rebuild_lk_comp_path() */
/*
* Name: check_ext_attr()
*
* Description:
* Check the extended attributes for a file being extracted.
* The attributes being checked here are CMW labels.
* ACLs are not set here because they are set by the
* pflag in doxtract().
*
* If the label doesn't match, return 0
* else return 1
*/
static int
check_ext_attr(char *filename)
{
bslabel_t currentlabel; /* label from zone */
if (bltype(&bs_label, SUN_SL_UN)) {
/* No label check possible */
return (0);
}
if (getlabel(filename, &currentlabel) != 0) {
(void) fprintf(stderr,
gettext("tar: can't get label for "
" %s, getlabel() error: %s\n"),
filename, strerror(errno));
return (0);
} else if ((blequal(&currentlabel, &bs_label)) == 0) {
char *src_label = NULL; /* ascii label */
/* get current src SL */
if (bsltos(&bs_label, &src_label, 0, 0) <= 0) {
(void) fprintf(stderr,
gettext("tar: can't interpret requested label for"
" %s\n"), filename);
} else {
(void) fprintf(stderr,
gettext("tar: can't apply label %s to %s\n"),
src_label, filename);
free(src_label);
}
(void) fprintf(stderr,
gettext("tar: %s not restored\n"), filename);
return (0);
}
return (1);
} /* end check_ext_attr */
/* Compressing a tar file using compression method provided in 'opt' */
static void
compress_back()
{
pid_t pid;
if (vflag) {
(void) fprintf(vfile,
gettext("Compressing '%s' with '%s'...\n"),
usefile, compress_opt);
}
if ((pid = fork()) == 0) {
verify_compress_opt(compress_opt);
(void) execlp(compress_opt, compress_opt,
usefile, NULL);
} else if (pid == -1) {
vperror(1, "%s", gettext("Could not fork"));
}
wait_pid(pid);
if (suffix == 0) {
(void) rename(tfname, usefile);
}
}
/* The magic numbers from /etc/magic */
#define GZIP_MAGIC "\037\213"
#define BZIP_MAGIC "BZh"
#define COMP_MAGIC "\037\235"
#define XZ_MAGIC "\375\067\172\130\132\000"
void
check_compression(void)
{
char magic[16];
FILE *fp;
if ((fp = fopen(usefile, "r")) != NULL) {
(void) fread(magic, sizeof (char), 6, fp);
(void) fclose(fp);
}
if (memcmp(magic, GZIP_MAGIC, 2) == 0) {
if (xflag || tflag) {
compress_opt = compress_malloc(strlen(GZCAT) + 1);
(void) strcpy(compress_opt, GZCAT);
} else if (uflag || rflag) {
compress_opt = compress_malloc(strlen(GZIP) + 1);
(void) strcpy(compress_opt, GZIP);
}
} else if (memcmp(magic, BZIP_MAGIC, 2) == 0) {
if (xflag || tflag) {
compress_opt = compress_malloc(strlen(BZCAT) + 1);
(void) strcpy(compress_opt, BZCAT);
} else if (uflag || rflag) {
compress_opt = compress_malloc(strlen(BZIP) + 1);
(void) strcpy(compress_opt, BZIP);
}
} else if (memcmp(magic, COMP_MAGIC, 2) == 0) {
if (xflag || tflag) {
compress_opt = compress_malloc(strlen(ZCAT) + 1);
(void) strcpy(compress_opt, ZCAT);
} else if (uflag || rflag) {
compress_opt = compress_malloc(strlen(COMPRESS) + 1);
(void) strcpy(compress_opt, COMPRESS);
}
} else if (memcmp(magic, XZ_MAGIC, 6) == 0) {
if (xflag || tflag) {
compress_opt = compress_malloc(strlen(XZCAT) + 1);
(void) strcpy(compress_opt, XZCAT);
} else if (uflag || rflag) {
compress_opt = compress_malloc(strlen(XZ) + 1);
(void) strcpy(compress_opt, XZ);
}
}
}
char *
add_suffix()
{
(void) strcpy(tfname, usefile);
if (strcmp(compress_opt, GZIP) == 0) {
if ((suffix = gz_suffix()) == NULL) {
strlcat(tfname, gsuffix[0], sizeof (tfname));
return (gsuffix[0]);
}
} else if (strcmp(compress_opt, COMPRESS) == 0) {
if ((suffix = gz_suffix()) == NULL) {
strlcat(tfname, gsuffix[6], sizeof (tfname));
return (gsuffix[6]);
}
} else if (strcmp(compress_opt, BZIP) == 0) {
if ((suffix = bz_suffix()) == NULL) {
strlcat(tfname, bsuffix[0], sizeof (tfname));
return (bsuffix[0]);
}
} else if (strcmp(compress_opt, XZ) == 0) {
if ((suffix = xz_suffix()) == NULL) {
strlcat(tfname, xsuffix[0], sizeof (tfname));
return (xsuffix[0]);
}
}
return (NULL);
}
/* Decompressing a tar file using compression method from the file type */
void
decompress_file(void)
{
pid_t pid;
char *added_suffix;
added_suffix = add_suffix();
if (added_suffix != NULL) {
(void) rename(usefile, tfname);
}
if ((pid = fork()) == 0) {
if (vflag) {
(void) fprintf(vfile,
gettext("Decompressing '%s' with "
"'%s'...\n"), usefile, compress_opt);
}
verify_compress_opt(compress_opt);
(void) execlp(compress_opt, compress_opt, "-df",
tfname, NULL);
vperror(1, gettext("Could not exec %s"), compress_opt);
} else if (pid == -1) {
vperror(1, gettext("Could not fork"));
}
wait_pid(pid);
if (suffix != NULL) {
/* restore the file name - original file was without suffix */
*(usefile + strlen(usefile) - strlen(suffix)) = '\0';
}
}
/* Set the archive for writing and then compress the archive */
pid_t
compress_file(void)
{
int fd[2];
pid_t pid;
if (vflag) {
(void) fprintf(vfile, gettext("Compressing '%s' with "
"'%s'...\n"), usefile, compress_opt);
}
if (pipe(fd) < 0) {
vperror(1, gettext("Could not create pipe"));
}
if ((pid = fork()) > 0) {
mt = fd[1];
(void) close(fd[0]);
return (pid);
}
/* child */
(void) dup2(fd[0], STDIN_FILENO);
(void) close(fd[1]);
(void) dup2(mt, STDOUT_FILENO);
verify_compress_opt(compress_opt);
(void) execlp(compress_opt, compress_opt, NULL);
vperror(1, gettext("Could not exec %s"), compress_opt);
return (0); /*NOTREACHED*/
}
pid_t
uncompress_file(void)
{
int fd[2];
pid_t pid;
if (vflag) {
(void) fprintf(vfile, gettext("Decompressing '%s' with "
"'%s'...\n"), usefile, compress_opt);
}
if (pipe(fd) < 0) {
vperror(1, gettext("Could not create pipe"));
}
if ((pid = fork()) > 0) {
mt = fd[0];
(void) close(fd[1]);
return (pid);
}
/* child */
(void) dup2(fd[1], STDOUT_FILENO);
(void) close(fd[0]);
(void) dup2(mt, STDIN_FILENO);
verify_compress_opt(compress_opt);
(void) execlp(compress_opt, compress_opt, NULL);
vperror(1, gettext("Could not exec %s"), compress_opt);
return (0); /*NOTREACHED*/
}
/* Checking suffix validity */
char *
check_suffix(char **suf, int size)
{
int i;
int slen;
int nlen = strlen(usefile);
for (i = 0; i < size; i++) {
slen = strlen(suf[i]);
if (nlen < slen)
return (NULL);
if (strcmp(usefile + nlen - slen, suf[i]) == 0)
return (suf[i]);
}
return (NULL);
}
/* Checking valid 'bzip2' suffix */
char *
bz_suffix(void)
{
return (check_suffix(bsuffix, BSUF));
}
/* Checking valid 'gzip' suffix */
char *
gz_suffix(void)
{
return (check_suffix(gsuffix, GSUF));
}
/* Checking valid 'xz' suffix */
char *
xz_suffix(void)
{
return (check_suffix(xsuffix, XSUF));
}
void *
compress_malloc(size_t size)
{
void *opt;
if ((opt = malloc(size)) == NULL) {
vperror(1, "%s",
gettext("Could not allocate compress buffer\n"));
}
return (opt);
}
void
wait_pid(pid_t pid)
{
int status;
while (waitpid(pid, &status, 0) == -1 && errno == EINTR)
;
}
static void
verify_compress_opt(const char *t)
{
struct stat statbuf;
if (stat(t, &statbuf) == -1)
vperror(1, "%s %s: %s\n", gettext("Could not stat"),
t, strerror(errno));
}
static void
detect_compress(void)
{
char *zsuf[] = {".Z"};
if (check_suffix(zsuf, 1) != NULL) {
Zflag = 1;
} else if (check_suffix(bsuffix, BSUF) != NULL) {
jflag = 1;
} else if (check_suffix(gsuffix, GSUF) != NULL) {
zflag = 1;
} else if (check_suffix(xsuffix, XSUF) != NULL) {
Jflag = 1;
} else {
vperror(1, "%s\n", gettext("No compression method detected"));
}
}