tar.c revision d2443e765650e70b88cd0346e67d2aee6dd1ea3a
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* Copyright (c) 1987, 1988 Microsoft Corporation */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <unistd.h>
#include <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 <libintl.h>
#include <strings.h>
#include <deflt.h>
#include <limits.h>
#include <iconv.h>
#include <assert.h>
#include <aclutils.h>
#if defined(__SunOS_5_6) || defined(__SunOS_5_7)
extern int defcntl();
#endif
#include <archives.h>
/*
* 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
* explicitly doing the declaration here.
*/
#ifndef MINSIZE
#define MINSIZE 250
#endif
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
/* -DDEBUG ONLY for debugging */
#ifdef DEBUG
#define DEBUG(a, b, c)\
#endif
#ifdef BSIZE
#else /* BSIZE */
#endif /* BSIZE */
#define NBLOCK 20
#define NAMSIZ 100
#define PRESIZ 155
#define MAXNAM 256
/* 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 LEV0 1
#define SYMLINK_LEV0 0
#define TRUE 1
#define FALSE 0
#define XATTR_FILE 1
#define NORMAL_FILE 0
#define PUT_AS_LINK 1
#define PUT_NOTAS_LINK 0
#if _FILE_OFFSET_BITS == 64
#define FMT_off_t "lld"
#define FMT_off_t_o "llo"
#define FMT_blkcnt_t "lld"
#else
#define FMT_off_t "ld"
#define FMT_off_t_o "lo"
#define FMT_blkcnt_t "ld"
#endif
/* ACL support */
static
struct sec_attr {
char attr_type;
char attr_len[7];
char attr_info[1];
} *attr;
#define ACL_HDR 'A'
/*
*
* 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.
* associated with them.
*
* The names of the header in the archive look like:
*
* The name of the attribute looks like:
*
* 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 char *xattraname; /* attribute name */
static char *xattr_linkaname; /* attribute attribute is linked to */
static char Hiddendir; /* are we processing hidden xattr dir */
static char xattrbadhead;
/* Was statically allocated tbuf[NBLOCK] */
static
union hblock {
struct header {
/* <prefix>/<name>; otherwise */
/* <name> */
char mode[8];
char uid[8];
char gid[8];
char mtime[12];
char chksum[8];
char typeflag;
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
/* the file: <prefix>/<name> */
char extno; /* extent #, null if not split */
char extotal; /* total extents */
} dbuf;
static
struct xtar_hdr {
x_gid; /* Gid 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 */
} Xtarhdr;
static
struct gen_hdr {
g_gid; /* Gid of file */
g_devminor; /* Major/minor of special files */
} Gen;
static
struct linkbuf {
int count;
} *ihead;
/* see comments before build_table() */
#define TABLE_SIZE 512
struct file_list {
char *name; /* Name of file to {in,ex}clude */
};
static int append_secattr(char **, int *, acl_t *);
static void write_ancillary(union hblock *, char *, int, char);
static void assert_string(char *s, char *msg);
static void backtape(void);
static void closevol(void);
static void done(int n);
#ifdef _iBCS2
#else
#endif
static void flushtape(void);
static void getdir(void);
static void newvol(void);
static void passtape(void);
static void usage(void);
#ifdef EUC
#endif /* EUC */
static int checkupdate(char *arg);
static int cmp(char *b, char *s, int n);
static int endtape(void);
static int notsame(void);
static int response(void);
static int build_dblock(const char *, const char *, const char,
static wchar_t yesnoresponse(void);
#ifdef _iBCS2
static char *nextarg();
#endif
static uid_t getuidbyname(char *);
static gid_t getgidbyname(char *);
static int get_xdata(void);
static int gen_utf8_names(const char *filename);
static void xattrs_put(char *, char *, char *);
static void prepare_xattr(char **, char *, char *,
char, struct linkbuf *, int *);
static int read_xattr_hdr();
static char *get_component(char *path);
static int retry_attrdir_open(char *name);
static void chop_endslashes(char *path);
static int checkflag = 0;
#ifdef _iBCS2
static int Fileflag;
char *sysv3_env;
#endif
static int uflag;
static int oflag;
static int Pflag; /* POSIX conformant archive */
static int Eflag; /* Allow files greater than 8GB */
static int atflag; /* traverse extended attributes */
static int Dflag; /* Data change flag */
static int freemem = 1;
static int Errflg = 0;
static int exitflag = 0;
static int mt_devtype; /* dev type of archive, from stat structure */
static char archive[] = "archive0=";
static char *Xfile;
static char *usefile;
static char *Filefile;
static int mulvol; /* multi-volume option selected */
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 int is_posix; /* true if archive we're reading is POSIX-conformant */
static const char *magic_type = "ustar";
static char *xrec_ptr;
static off_t xrec_offset = 0;
static int Xhdrflag;
static int charset_type = 0;
/* need to be in extended header. */
#define _X_DEVMAJOR 0x1
#define _X_DEVMINOR 0x2
#define _X_GID 0x4
#define _X_GNAME 0x8
#define _X_LINKPATH 0x10
#define _X_PATH 0x20
#define _X_SIZE 0x40
#define _X_UID 0x80
#define _X_UNAME 0x100
#define _X_ATIME 0x200
#define _X_CTIME 0x400
#define _X_MTIME 0x800
#define _X_LAST 0x40000000
/*
* 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;
/*
* The following mechanism is provided to allow us to debug tar in complicated
* situations, like when it is part of a pipe. The idea is that you compile
* with -DWAITAROUND defined, and then add the 'z' function modifier to the
* target tar invocation, eg. "tar czf tarfile file". If stderr is available,
* it will tell you to which pid to attach the debugger; otherwise, use ps to
* find it. Attach to the process from the debugger, and, *PRESTO*, you are
* there!
*
* Simply assign "waitaround = 0" once you attach to the process, and then
* proceed from there as usual.
*/
#ifdef WAITAROUND
int waitaround = 0; /* wait for rendezvous with the debugger */
#endif
int
{
char *cp;
char *tmpdirp;
#ifdef _iBCS2
int tbl_cnt = 0;
#endif
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
#endif
(void) textdomain(TEXT_DOMAIN);
if (argc < 2)
usage();
/*
* 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.
*/
argv++;
argc--;
if (argc < 3)
usage();
}
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'.
*/
break;
if (*cp != 'f') {
nblock = 1;
blocklim = 0;
NotTape = 0;
}
}
}
switch (*cp) {
#ifdef WAITAROUND
case 'z':
/* rendezvous with the debugger */
waitaround = 1;
break;
#endif
case 'f':
"tar: tarfile must be specified with 'f' "
"function modifier\n"));
break;
case 'F':
#ifdef _iBCS2
if (sysv3_env) {
"tar: 'F' requires a file name\n"));
Fileflag++;
} else
#endif /* _iBCS2 */
Fflag++;
break;
case 'c':
cflag++;
rflag++;
update = 1;
break;
#if defined(O_XATTR)
case '@':
atflag++;
break;
#endif
case 'u':
uflag++; /* moved code after signals caught */
rflag++;
update = 2;
break;
case 'r':
rflag++;
update = 2;
break;
case 'v':
vflag++;
break;
case 'w':
wflag++;
break;
case 'x':
xflag++;
break;
case 'X':
"tar: exclude file must be specified with 'X' "
"function modifier\n"));
Xflag = 1;
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':
"tar: blocking factor must be specified "
"with 'b' function modifier\n"));
bflag++;
break;
case 'q':
qflag++;
break;
case 'k':
"tar: size value must be specified with 'k' "
"function modifier\n"));
kflag++;
break;
case 'n': /* not a magtape (instead of 'k') */
NotTape++; /* assume non-magtape */
break;
case 'l':
linkerrok++;
break;
case 'e':
#ifdef _iBCS2
/* If sysv3 IS set, don't be as verbose */
if (!sysv3_env)
#endif /* _iBCS2 */
errflag++;
eflag++;
break;
case 'o':
oflag++;
break;
case 'h':
hflag++;
break;
case 'i':
iflag++;
break;
case 'B':
Bflag++;
break;
case 'P':
Pflag++;
break;
case 'E':
Eflag++;
Pflag++; /* Only POSIX archive made */
break;
default:
"tar: %c: unknown function modifier\n"), *cp);
usage();
}
#ifdef _iBCS2
"tar: specify only one of X or F.\n"));
usage();
}
#endif /* _iBCS2 */
usage();
"tar: specify only one of [ctxru].\n"));
usage();
}
/* alloc a buffer of the right size */
"tar: cannot allocate physio buffer\n"));
exit(1);
}
"tar: cannot allocate extended header buffer\n"));
exit(1);
}
#ifdef WAITAROUND
if (waitaround) {
" %d\n"), getpid());
while (waitaround) {
(void) sleep(10);
}
}
#endif
else {
/*
* Make sure that dir is no longer than what can
* fit in the prefix part of the header.
*/
"Ignoring TMPDIR\n"));
} else
}
if (rflag) {
usage();
if (uflag) {
int tnum;
}
if (cflag == 0)
"can only create standard output archives."));
++bflag;
} else {
if (cflag)
else
if (mt < 0) {
< 0)
}
}
/* Get inode and device number of output file */
(void) printf(
gettext("Suppressing absolute pathnames\n"));
/*
* 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.
*/
"tar: missing argument for -I flag\n"));
done(2);
} else {
Iflag = 1;
#ifdef _iBCS2
if (Fileflag) {
"tar: only one of I or F.\n"));
usage();
}
#endif /* _iBCS2 */
}
}
}
++bflag;
/* try to recover from short reads when reading stdin */
++Bflag;
if (xflag) {
("Suppressing absolute pathnames.\n"));
#ifdef _iBCS2
#else
#endif
} else if (tflag)
#ifdef _iBCS2
#else
#endif
}
else
usage();
/* Not reached: keep compiler quiet */
return (1);
}
static void
usage(void)
{
#ifdef _iBCS2
if (sysv3_env) {
#if defined(O_XATTR)
"Usage: tar {c|r|t|u|x}[BDeEhilmnopPqvw@[0-7]][bfFk][X...] "
#else
"Usage: tar {c|r|t|u|x}[BDeEhilmnopPqvw[0-7]][bfFk][X...] "
#endif
"[blocksize] [tarfile] [filename] [size] [exclude-file...] "
"{file | -I include-file | -C directory file}...\n"));
} else
#endif /* _iBCS2 */
{
#if defined(O_XATTR)
"Usage: tar {c|r|t|u|x}[BDeEFhilmnopPqvw@[0-7]][bfk][X...] "
#else
"Usage: tar {c|r|t|u|x}[BDeEFhilmnopPqvw[0-7]][bfk][X...] "
#endif
"[blocksize] [tarfile] [size] [exclude-file...] "
"{file | -I include-file | -C directory file}...\n"));
}
done(1);
}
/*
* dorep - do "replacements"
*
* Dorep is responsible for creating ('c'), appending ('r')
* and updating ('u');
*/
static void
{
int archtype;
if (!cflag) {
xhdr_flgs = 0;
getdir(); /* read header for next file */
if (Xhdrflag > 0) {
if (!Eflag)
" header. -E flag required.\n"));
(void) get_xdata(); /* Get extended header items */
/* and regular header */
} else {
if (Eflag)
" header. -E flag not allowed.\n"));
}
while (!endtape()) { /* changed from a do while */
passtape(); /* skip the file data */
if (term)
xhdr_flgs = 0;
getdir();
if (Xhdrflag > 0)
(void) get_xdata();
}
backtape(); /* was called by endtape */
char buf[200];
"!= prev {print; prev=$1}' %s >%sX;mv %sX %s",
}
}
dumping = 1;
if (mulvol) { /* SP-1 */
"Volume size not a multiple of block size."));
if (vflag)
FMT_blkcnt_t "K, blocking factor = %dK\n"),
}
#ifdef _iBCS2
if (Fileflag) {
} else {
"tar: F requires a file name.\n"));
usage();
}
}
#endif /* _iBCS2 */
/*
* Save the original directory before it gets
* changed.
*/
exit(1);
}
#ifdef _iBCS2
if (Fileflag) {
"tar: only one of I or F.\n"));
usage();
}
#endif /* _iBCS2 */
"missing file name for -I flag."));
continue;
continue;
} else {
*p = 0;
}
#ifdef _iBCS2
if (Fileflag) {
"tar: only one of F or C\n"));
usage();
}
#endif /* _iBCS2 */
"can't change directories to %s"), *argv);
else
argv++;
continue;
#ifdef _iBCS2
continue;
} else {
*p = 0;
}
#endif /* _iBCS2 */
} else
/*
* point cp2 to the last '/' in file, but not
* to a trailing '/'
*/
if (*cp == '/') {
++cp;
}
/* not trailing slash */
}
}
}
*cp2 = '\0';
"can't change directories to %s"), file);
continue;
}
*cp2 = '/';
cp2++;
}
LEV0, SYMLINK_LEV0);
#if defined(O_XATTR)
if (!exitflag) {
}
}
#endif
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;
}
}
flushtape();
closevol(); /* SP-1 */
if (linkerrok == 1)
continue;
if (errflag)
done(1);
else
Errflg = 1;
}
}
/*
* endtape - check for tape at end
*
* endtape checks the entry in dblock.dbuf to see if its the
* special EOT entry. Endtape is usually called after getdir().
*
* endtape used to call backtape; it no longer does, he who
* wants it backed up must call backtape himself
* RETURNS: 0 if not EOT, tape position unaffected
* 1 if EOT, tape position unaffected
*/
static int
endtape(void)
{
return (1);
} else
return (0);
}
/*
* getdir - get directory entry from tar tape
*
* getdir reads the next tarblock off the tape and cracks
* it as a directory. The checksum must match properly.
*
* If tfile is non-null getdir writes the file name and mod date
* to tfile.
*/
static void
getdir(void)
{
#ifdef EUC
static int warn_chksum_sign = 0;
#endif /* EUC */
top:
return;
case '0': case 0: case _XATTR_HDRTYPE:
break;
case '1': /* hard link */
break;
case '2':
break;
case '3':
break;
case '4':
break;
case '5':
break;
case '6':
break;
default:
break;
}
else
Xhdrflag = 0;
} else {
extno = 0; /* tell others file not split */
extsize = 0;
extotal = 0;
}
#ifdef EUC
"tar: directory checksum error\n"));
if (iflag)
goto top;
done(2);
} else {
if (! warn_chksum_sign) {
warn_chksum_sign = 1;
"tar: warning: tar file made with signed checksum\n"));
}
}
}
#else
"tar: directory checksum error\n"));
if (iflag)
goto top;
done(2);
}
#endif /* EUC */
/*
* 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.
*/
else
else
}
#if defined(O_XATTR)
Hiddendir = 0;
if (xattrbadhead) {
xattr_linkp = NULL;
} else {
Hiddendir = 1;
}
}
}
#endif
}
/*
* passtape - skip over a file on the tape
*
* passtape skips over the next data file on the tape.
* The tape directory entry must be in dblock.dbuf. This
* routine just eats the number of blocks computed from the
* directory size entry; the tape must be (logically) positioned
* right after thee directory info.
*/
static void
passtape(void)
{
/*
* 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.
*/
return;
/* if operating on disk, seek instead of reading */
if (NotTape)
else
while (blocks-- > 0)
}
static int
{
char *bigbuf;
int maxread;
int hint; /* amount to write to get "in sync" */
char *cp;
char *name;
int i;
long l;
int split;
int dirfd = -1;
int rc = PUT_NOTAS_LINK;
int archtype = 0;
char *prefix = "";
char *tmpbuf;
char *lastslash;
int j;
int readlink_max;
xhdr_flgs = 0;
if (filetype == XATTR_FILE) {
} else {
}
if (dirfd == -1) {
"tar: unable to open%sdirectory %s\n"),
goto out;
}
if (filetype == XATTR_FILE) {
"tar: unable to fchdir into attribute directory"
" of file %s\n"), longname);
goto out;
}
}
gettext("tar: directory nesting too deep, %s not dumped\n"),
longname);
goto out;
}
goto out;
if (hflag) {
/*
* Catch nesting where a file is a symlink to its directory.
*/
if (symlink_lev++ >= MAXSYMLINKS) {
"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
*/
"tar: %s same as archive file\n"), longname);
Errflg = 1;
goto out;
}
/*
* Check size limit - we can't archive files that
* exceed TAR_OFFSET_MAX bytes because of header
* limitations. Exclude file types that set
* st_size to zero below because they take no
* archive space to represent contents.
*/
(Eflag == 0)) {
"tar: %s too large to archive. "
"Use E function modifier.\n"), longname);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
goto out;
}
goto out;
}
goto out;
if (Xflag) {
if (vflag) {
"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).
*/
"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).
*/
/*
* Since path is limited to PRESIZ characters, look for the
* last slash within PRESIZ + 1 characters only.
*/
i = split; /* Length of name */
j = 0; /* Length of prefix */
goodbuf[0] = '\0';
} else {
i = split - j - 1;
}
/*
* If the filename is greater than NAMSIZ we can't
* archive the file unless we are using extended headers.
*/
!Pflag)) {
/* Determine which (filename or path) is too long. */
if (Eflag > 0) {
if (i <= NAMSIZ)
else
xhdr_count + 1);
} else {
"tar: %s: filename is greater than "
else
"tar: %s: prefix is greater than %d"
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
} else
} else {
}
if (Aflag) {
while (*prefix == '/')
++prefix;
else
while (*name == '/')
++name;
}
case S_IFDIR:
i = 0;
;
*--cp = '/';
*++cp = 0;
}
if (!oflag) {
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?
*/
"tar: %s: filename is greater "
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
} else {
= '/';
} else
"%s/", name);
/*
* need to recalculate checksum
* because the name changed.
*/
}
}
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) {
} else {
}
#else
#endif
}
if (vflag) {
#ifdef DEBUG
if (NotTape)
0);
#endif
longname);
} else {
}
if (NotTape)
K(blocks));
else
" tape blocks\n"), blocks);
}
/*
* If hidden dir then break now since xattrs_put() will do
* the iterating of the directory.
*
* At the moment, there can't be attributes on attributes
* or directories within the attributes hidden directory
* hierarchy.
*/
if (filetype == XATTR_FILE)
break;
if (*shortname != '/')
else
goto out;
}
"can't open directory %s"), longname);
parent);
goto out;
}
continue;
} else
l = -1;
if (!exitflag) {
}
}
if (exitflag)
break;
/*
* If the directory was not closed, then it does
* not need to be reopened.
*/
if (l < 0)
continue;
"can't open directory %s"), longname);
vperror(0,
gettext("cannot change back?: %s"),
parent);
goto out;
}
}
}
break;
case S_IFLNK:
if (Eflag > 0) {
xhdr_flgs |= _X_LINKPATH;
} else {
"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.
*/
if (i < 0) {
"can't read symbolic link %s"), longname);
goto out;
} else {
filetmp[i] = 0;
}
if (vflag)
"a %s symbolic link to %s\n"),
if (xhdr_flgs & _X_LINKPATH) {
goto out;
} else
goto out;
/*
* No acls for symlinks: mode is always 777
* dont call write ancillary
*/
rc = PUT_AS_LINK;
break;
case S_IFREG:
(filetype == XATTR_FILE) ?
(filetype == XATTR_FILE) ?
shortname : "");
goto out;
}
rc = PUT_AS_LINK;
goto out;
}
/* correctly handle end of volume */
/* file won't fit */
if (eflag) {
newvol();
break;
}
"tar: Single file cannot fit on volume\n"));
done(3);
}
/* split if floppy has some room and file is large */
goto out;
}
newvol(); /* not worth it--just get new volume */
}
#ifdef DEBUG
blocks);
#endif
goto out;
}
if (vflag) {
#ifdef DEBUG
if (NotTape)
0);
#endif
(filetype == XATTR_FILE) ?
(filetype == XATTR_FILE) ?
shortname : "");
if (NotTape)
K(blocks));
else
blocks);
}
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().
*/
}
while (((i = (int)
blocks) {
}
if (i < 0)
else if (blocks != 0 || i != 0) {
"tar: %s: file changed size\n"), longname);
if (errflag) {
exitflag = 1;
Errflg = 1;
} else if (!Dflag) {
Errflg = 1;
}
}
break;
case S_IFIFO:
rc = PUT_AS_LINK;
goto out;
}
if (eflag) {
newvol();
break;
}
"tar: Single file cannot fit on volume\n"));
done(3);
}
goto out;
}
newvol();
}
#ifdef DEBUG
blocks);
#endif
if (vflag) {
#ifdef DEBUG
if (NotTape)
0);
#endif
if (NotTape)
else
}
goto out;
filetype, '6') != 0)
goto out;
break;
case S_IFCHR:
rc = PUT_AS_LINK;
goto out;
}
if (eflag) {
newvol();
break;
}
"tar: Single file cannot fit on volume\n"));
done(3);
}
goto out;
}
newvol();
}
#ifdef DEBUG
blocks);
#endif
if (vflag) {
#ifdef DEBUG
if (NotTape)
0);
#endif
if (NotTape)
else
blocks);
}
goto out;
goto out;
break;
case S_IFBLK:
rc = PUT_AS_LINK;
goto out;
}
if (eflag) {
newvol();
break;
}
"tar: Single file cannot fit on volume\n"));
done(3);
}
goto out;
}
newvol();
}
#ifdef DEBUG
blocks);
#endif
if (vflag) {
#ifdef DEBUG
if (NotTape)
0);
#endif
if (NotTape)
K(blocks));
else
}
goto out;
goto out;
break;
default:
"tar: %s is not a file. Not dumped\n"), longname);
if (errflag)
exitflag = 1;
Errflg = 1;
goto out;
}
out:
if (dirfd != -1) {
if (filetype == XATTR_FILE)
}
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
{
int i, extents;
/*
* # 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.
*/
"tar: %s needs unusual number of volumes to split\n"
return;
}
return;
/*
* 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)
else
"tar: large file %s needs %d extents.\n"
for (i = 1; i <= extents; i++) {
if (i > 1) {
newvol();
if (i == extents)
s = bytes; /* last ext. gets true bytes */
else
}
bytes -= s;
if (vflag)
blocks--;
}
if (blocks != 0) {
"tar: %s: file changed size\n"), longname);
"tar: aborting split file %s\n"), longname);
return;
}
}
if (vflag)
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
{
return (1);
}
return (0);
}
static void
#ifdef _iBCS2
#else
#endif
{
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 once = 1;
int error;
int symflag;
int want;
int dircreate;
int convflag;
int cnt;
dumping = 0; /* for newvol(), et al: we are not writing */
/*
* Count the number of files that are to be extracted
*/
#ifdef _iBCS2
++fcnt;
#endif /* _iBCS2 */
for (;;) {
convflag = 0;
symflag = 0;
dir = 0;
ofile = -1;
/* namep is set by wantit to point to the full name */
#if defined(O_XATTR)
xattr_linkp = NULL;
}
#endif
continue;
}
if (want == -1)
break;
if (dirfd != -1)
#if defined(O_XATTR)
} else {
}
#else
#endif
if (dirfd == -1) {
#if defined(O_XATTR)
if (xattrp) {
}
#endif
if (dirfd == -1) {
#if defined(O_XATTR)
if (xattrp) {
gettext("tar: cannot open "
"attribute %s of file %s: %s\n"),
/*
* Reset typeflag back to real
* value so passtape will skip
* ahead correctly.
*/
xattr_linkp = NULL;
} else {
gettext("tar: cannot open %s %s\n"),
}
#else
gettext("tar: cannot open %s %s\n"),
#endif
passtape();
continue;
}
}
if (xhdr_flgs & _X_LINKPATH)
else {
#if defined(O_XATTR)
} else {
}
#else
#endif
}
if (Fflag) {
char *s;
s = namep;
else
s++;
passtape();
continue;
}
}
passtape();
continue;
}
if (once) {
checkflag = 1;
pflag = 1;
} else {
/* get file creation mask */
}
once = 0;
} else {
pflag = 1;
checkflag = 2;
}
if (!pflag) {
/* get file creation mask */
}
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.
*/
"%s: failed to set ownership of attribute"
" directory"), namep);
}
"%s: failed to set permissions of"
" attribute directory"), namep);
}
goto filedone;
}
#endif
dir = 1;
if (vflag) {
&dirname[0]);
if (NotTape)
else
FMT_blkcnt_t " tape blocks\n"),
(blkcnt_t)0);
}
goto filedone;
}
}
linkp++;
"tar: %s: cannot link\n"), namep);
continue;
}
if (vflag)
xcnt++; /* increment # files extracted */
continue;
}
(int)Gen.g_devmajor) < 0) {
continue;
}
if (vflag) {
if (NotTape)
"K\n", K(blocks));
else
FMT_blkcnt_t " tape blocks\n"),
blocks);
}
goto filedone;
}
}
linkp++;
"tar: %s: cannot link\n"), namep);
continue;
}
if (vflag)
xcnt++; /* increment # files extracted */
continue;
}
"%s: mknod failed"), namep);
continue;
}
if (vflag) {
if (NotTape)
"K\n", K(blocks));
else
FMT_blkcnt_t " tape blocks\n"),
blocks);
}
goto filedone;
"Can't create special %s\n"), namep);
continue;
}
/* BLOCK SPECIAL */
}
linkp++;
"tar: %s: cannot link\n"), namep);
continue;
}
if (vflag)
xcnt++; /* increment # files extracted */
continue;
}
continue;
}
if (vflag) {
if (NotTape)
"K\n", K(blocks));
else
FMT_blkcnt_t " tape blocks\n"),
blocks);
}
goto filedone;
continue;
}
linkp++;
}
namep);
continue;
}
if (vflag)
"x %s symbolic link to %s\n"),
goto filedone;
}
linkp++;
}
#if defined(O_XATTR)
if (xattrp && xattr_linkp) {
(char *)NULL) {
"A parent directory cannot"
" be read"));
exit(1);
}
"Cannot fchdir to attribute "
"directory"));
exit(1);
}
"Cannot chdir out of attribute "
"directory"));
exit(1);
}
} else {
}
#else
#endif
if (error < 0) {
"tar: %s%s%s: cannot link\n"),
(xattr_linkp != NULL) ?
xattraname : "");
continue;
}
if (vflag)
"%s%s%s linked to %s%s%s\n"), namep,
(xattr_linkp != NULL) ?
(xattr_linkp != NULL) ?
xattr_linkaname : "",
(xattr_linkp != NULL) ?
xattraname : "");
xcnt++; /* increment # files extracted */
#if defined(O_XATTR)
xattr_linkp = NULL;
}
#endif
continue;
}
/* REGULAR FILES */
convflag = 1;
if (errflag) {
"tar: %s: typeflag '%c' not recognized\n"),
done(1);
} else {
"tar: %s: typeflag '%c' not recognized, "
"converting to regular file\n"), namep,
Errflg = 1;
}
}
linkp++;
"tar: %s: cannot link\n"), namep);
continue;
}
if (vflag)
xcnt++; /* increment # files extracted */
continue;
}
"tar: %s - cannot create\n"), comp);
if (errflag)
done(1);
else
Errflg = 1;
passtape();
continue;
}
if (extno != 0) { /* file is in pieces */
"tar: ignoring bad extent info for %s\n"),
comp);
else {
goto filedone;
}
}
extno = 0; /* let everyone know file is not split */
if (vflag) {
if (NotTape)
K(blocks));
else
}
if (dir)
else
}
/* moved this code from above */
else
}
/*
* 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.
*/
int ret;
#if defined(O_XATTR)
if (Hiddendir)
else
} else {
}
#else
#endif
if (ret < 0) {
if (pflag) {
"%s: failed to set acl entries\n"),
namep);
}
/* else: silent and continue */
}
}
#if defined(O_XATTR)
xattr_linkp = NULL;
}
#endif
if (!oflag)
"tar: cannot stat extracted file %s\n"),
namep);
"tar: warning - file permissions have "
"changed for %s (are 0%o, should be "
"0%o)\n"),
}
}
if (ofile != -1) {
dirfd = -1;
}
xcnt++; /* increment # files extracted */
}
char *secp;
char *tp;
int attrsize;
int cnt;
if (pflag) {
"Insufficient memory for acl\n"));
passtape();
continue;
}
while (blocks-- > 0) {
break;
} else {
TBLOCK);
}
}
/* got all attributes in secp */
do {
case UFSD_ACL:
case ACE_ACL:
"%7o",
(uint_t *)
&cnt);
/* header is 8 */
error =
if (error != 0) {
"aclfromtext "
"failed: %s\n"),
error));
break;
}
"aclcnt error\n"));
break;
}
break;
/* SunFed case goes here */
default:
"unrecognized attr"
" type\n"));
break;
}
/* next attributes */
} while (bytes != 0);
} else
passtape();
} /* acl */
} /* for */
/*
* Ensure that all the directories still on the directory stack
* get their modification times set correctly by flushing the
* stack.
*/
/*
* Check if the number of files extracted is different from the
* number of files listed on the command line
*/
gettext("tar: %d file(s) not extracted\n"),
Errflg = 1;
}
}
/*
*
* xblocks(bytes, ofile);
* unsigned long long bytes; size of extent or file to be extracted
*
* called by doxtract() and xsfile()
*/
static void
{
int write_count;
while (blocks-- > 0) {
else
write_count = bytes;
else
"tar: %s: HELP - extract write error\n"), tempname);
done(2);
}
}
}
/*
* 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 void
{
int i, c;
else
totalext = 0; /* these keep count */
/* make sure we do extractions in order */
"tar: first extent read is not #1\n"
"OK to read file beginning with extent #%d (%wc/%wc) ? "),
if (yesnoresponse() != yeschar) {
passtape();
return;
}
}
i = extno;
/*CONSTCOND*/
while (1) {
} else {
}
if (vflag)
totalbytes += bytes;
totalext++;
if (++i > extents)
break;
/* get next volume and verify it's the right one */
newvol();
xhdr_flgs = 0;
getdir();
if (Xhdrflag > 0)
(void) get_xdata(); /* Get x-header & regular hdr */
if (endtape()) { /* seemingly empty volume */
"tar: first record is null\n"));
"tar: need volume with extent #%d of %s\n"),
i, name);
goto tryagain;
}
if (notsame()) {
"tar: first file on that volume is not "
"the same file\n"));
goto asknicely;
}
if (i != extno) {
"tar: extent #%d received out of order\ntar: should be #%d\n"),
extno, i);
"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)
}
/*
* 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 (
}
static void
#ifdef _iBCS2
#else
#endif
{
int tcnt; /* count # files tabled */
int fcnt; /* count # files in argv list */
int want;
char *np;
dumping = 0;
/* if not on magtape, maximize seek speed */
#else
nblock = 1;
#endif
}
/*
* Count the number of files that are to be tabled
*/
#ifdef _iBCS2
++fcnt;
#endif /* _iBCS2 */
for (;;) {
/* namep is set by wantit to point to the full name */
continue;
if (want == -1)
break;
++tcnt;
/*
* ACL support:
* aclchar is introduced to indicate if there are
* acl entries. longt() now takes one extra argument.
*/
if (vflag) {
aclchar = '+';
passtape();
continue;
}
aclchar = ' ';
}
#if defined(O_XATTR)
} else {
}
#else
#endif
if (extno != 0) {
if (vflag) {
/* keep the '\n' for backwards compatibility */
} else {
}
}
if (xhdr_flgs & _X_LINKPATH) {
} else {
#if defined(O_XATTR)
} else {
}
#else
#endif
}
/*
* TRANSLATION_NOTE
* Subject is omitted here.
* Translate this as if
* <subject> linked to %s
*/
#if defined(O_XATTR)
(void) printf(
gettext(" linked to attribute %s"),
} else {
(void) printf(
}
#else
(void) printf(
#endif
}
/*
* 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)
}
#endif
passtape();
}
/*
* Check if the number of files tabled is different from the
* number of files listed on the command line
*/
Errflg = 1;
}
}
static void
{
char *cp;
*cp++ = '\0';
while (n-- > 0)
}
static void
{
int i, j, temp;
char modestr[12];
for (i = 0; i < 11; i++)
modestr[i] = '-';
modestr[i] = '\0';
/* a '+' sign is printed if there is ACL */
for (i = 0; i < 3; i++) {
j = (i * 3) + 1;
modestr[j] = 'r';
}
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. */
/*
* 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.
*/
"tar: impossible file type"));
}
}
}
static void
{
char fileDate[30];
if (xhdr_flgs & _X_LINKPATH)
else
'\0', NAMSIZ) ?
}
}
/*
* 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
{
char lastChar; /* the last character in name */
char *cp; /* scratch pointer into 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.
*/
if (*cp == '/') {
if (! firstSlash) {
firstSlash = cp;
}
}
}
/*
* Determine what you can from the proceeds of the scan.
*/
/*
* 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) {
}
/*
* Make the path one component at a time.
*/
cp;
*cp = '\0';
*cp = '/';
if (!success) {
return (0);
}
}
/*
* This makes the last component of the name, if it is a
* directory.
*/
if (markedDir) {
return (0);
}
}
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
char *name, /* name of the file to be modified */
int symflag) /* true if file is a symbolic link */
{
char *u_g_name;
/*
* 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 ..
*/
else
}
/* (Ditto for gids) */
else
}
}
}
/*ARGSUSED*/
static void
{
term++;
}
/*ARGSUSED*/
static void
{
term++;
}
/*ARGSUSED*/
static void
{
term++;
}
static void
{
/*
* 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) {
}
}
(off_t)0);
} else
} else {
}
uid = UID_NOBODY;
gid = GID_NOBODY;
}
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.
*/
#else
#endif /* EUC */
{
int i;
char *cp;
*cp = ' ';
i = 0;
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
{
unsigned i;
unsigned char *cp;
cp < (unsigned char *)
*cp = ' ';
i = 0;
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
{
if (wflag) {
if (vflag)
if (response() == 'y') {
return (1);
}
return (0);
}
return (1);
}
/*
* When the F flag is set, exclude RCS and SCCS directories. If F is set
* twice, also exclude .o files, and files names errs, core, and a.out.
*/
static int
{
int l;
return (0);
return (1);
}
return (1);
return (0);
if (howmuch > 1) {
return (0);
}
/* SHOULD CHECK IF IT IS EXECUTABLE */
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 int
checkupdate(char *arg)
{
long nsecs;
return (1);
/*
* 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 (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) {
#ifdef DEBUG
DEBUG("newvol called with 'dumping' set\n", 0, 0);
#endif
closevol();
flushtape();
sync();
tapepos = 0;
} else
mt = 0;
"tar: \007please insert new volume, then press RETURN."));
if (c == EOF)
if (term)
errno = 0;
} else {
}
if (mt < 0) {
"tar: cannot reopen %s (%s)\n"),
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.
*/
}
}
static void
done(int n)
{
if (mt > 0) {
exit(2);
}
}
exit(n);
}
/*
* Determine if s1 is a prefix portion of s2 (or the same as s2).
*/
static int
{
while (*s1)
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.
*
*/
static off_t
lookup(char *s)
{
int i;
off_t a;
for (i = 0; s[i]; i++)
if (s[i] == ' ')
break;
return (a);
}
static off_t
{
int i, j;
char b[N];
loop:
if (l >= h)
return ((off_t)-1);
m = l + (h-l)/2 - N/2;
if (m < l)
m = l;
if (m == 0) {
b[0] = '\n';
m--;
} else
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
{
/* handle non-multiple of SYS_BLOCK */
#endif
#ifdef DEBUG
#endif
return;
}
#ifdef DEBUG
#endif
goto noseek;
#endif
"tar: device seek error\n"));
done(3);
}
/* read those extra blocks */
if (nxb) {
#ifdef DEBUG
DEBUG("reading extra blocks\n", 0, 0);
#endif
"tar: read error while skipping file\n"));
done(8);
}
}
#endif
}
static void
{
int i, j;
++tapepos;
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)
j = nblock;
else
j = NBLOCK;
} else
j = nblock;
"tar: tape read error\n"));
done(3);
/*
* i == 0 means EOF reached and !rflag means that when
* tar command uses 'r' as a function letter, we are trying
* to update or replace an empty tar file which will fail.
* So this fix is not for 'r' function letter.
*/
} else if (i == 0 && !rflag) {
if (first) {
"tar: blocksize = %d\n"), i);
}
else
/*
* Short read - try to get the remaining bytes.
*/
char *b = (char *)tbuf + i;
int r;
do {
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) {
"tar: tape blocksize error\n"));
done(3);
}
i /= TBLOCK;
if (!NotTape)
"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;
}
}
/*
* replacement for writetape.
*/
static int
{
int i;
tapepos += n; /* output block count */
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.
*/
n -= nblock;
}
while (n-- > 0) {
recno = 0;
}
}
/* Tell the user how much to write to get in sync */
}
/*
* 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)
{
#ifdef DEBUG
nblock);
#endif
/*
* 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.
*/
(off_t)-1) {
gettext("tar: lseek to end of archive failed\n"));
done(4);
}
} else {
/*
* For tape devices, we backup over the most recently
* read record.
*/
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)
{
#ifdef DEBUG
#endif
if (recno > 0) { /* anything buffered? */
if (NotTape) {
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.
*/
#ifdef DEBUG
DEBUG("flushtape() %d rounding blocks\n", i, 0);
#endif
recno += i; /* round up to even SYS_BLOCK */
}
#endif
}
#ifdef DEBUG
#endif
"tar: tape write error\n"));
done(2);
}
recno = 0;
}
}
static void
{
}
#ifdef _iBCS2
/*
* initarg -- initialize things for nextarg.
*
* argv filename list, a la argv.
* filefile name of file containing filenames. Unless doing
* a create, seeks must be allowable (e.g. no named pipes).
*
* - if filefile is non-NULL, it will be used first, and argv will
* be used when the data in filefile are exhausted.
* - otherwise argv will be used.
*/
static long seekFile = -1;
static void
{
char *p;
int nbytes;
return; /* no -F file */
/*
* need to REinitialize
*/
if (seekFile != -1)
return;
}
/*
* first time initialization
*/
"tar: %s is not a regular file\n"), filefile);
done(1);
}
seekFile = 0;
if (!xflag)
return; /* the file will be read only once anyway */
nbytes -= 20;
if (nbytes < 50) {
return; /* no room so just do plain reads */
}
if (*p == '\n')
*p = '\0';
else
}
/*
* nextarg -- get next argument of arglist.
*
* The argument is taken from wherever is appropriate.
*
* If the 'F file' function modifier has been specified, the argument
* will be taken from the file, unless EOF has been reached.
* Otherwise the argument will be taken from argv.
*
* WARNING:
* Return value may point to static data, whose contents are over-
* written on each call.
*/
static char *
nextarg(void)
{
int n;
char *p;
if (FILEFile) {
p = ptrtoFile;
while (*ptrtoFile)
++ptrtoFile;
++ptrtoFile;
return (p);
}
return (nameFile);
}
}
return (*Cmdargv++);
}
#endif /* _iBCS2 */
/*
* 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
{
mulvol = 0; /* definitely not mulvol, but we must */
return (0); /* took out setting of NotTape */
}
"tar: sizes below %luK not supported (%" FMT_blkcnt_t
if (!kflag)
"bad size entry for %s in %s.\n"),
done(1);
}
mulvol++;
NotTape++; /* implies non-tape */
}
/*
* bcheck()
* - checks the validity of blocking factors
* - returns blocking factor
*/
static int
{
"tar: invalid blocksize \"%s\".\n"), bstr);
if (!bflag)
"bad blocksize entry for '%s' in %s.\n"),
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
*/
static int
{
char *bp;
return (FALSE);
"tar: error setting parameters for %s.\n"), DEF_FILE);
return (FALSE); /* & following ones too */
}
"tar: missing or invalid '%s' entry in %s.\n"),
return (FALSE);
}
return (FALSE);
}
"tar: block component missing in '%s' entry in %s.\n"),
return (FALSE);
}
"tar: size component missing in '%s' entry in %s.\n"),
return (FALSE);
}
else
#ifdef DEBUG
#endif
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
{
/* Only add to table if line has something in it */
}
}
/*
* 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
{
unsigned int h;
}
sizeof (char))) == NULL) {
exit(1);
}
exit(1);
}
}
/*
* 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
{
unsigned int h;
char *ptr;
}
/*
* check for the file name in the passed list
*/
return (1);
}
}
/*
* check for any parent directories in the file list
*/
return (1);
}
}
}
return (0);
}
/*
* Compute a hash from a string.
*/
static unsigned int
{
char *cp;
unsigned int h;
h = 0;
h += *cp;
}
return (h % TABLE_SIZE);
}
static void *
{
"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
{
if (exit_status)
else
if (errflag)
else
}
static void
{
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) {
usage();
}
}
static void
{
"tar: %s error: "), operation);
if (i < 0)
perror("");
else
}
static int
{
char **cp;
int gotit; /* true if we've found a match */
top:
xhdr_flgs = 0;
getdir();
if (Xhdrflag > 0) {
if (get_xdata() != 0) { /* Xhdr items and regular header */
passtape();
return (0); /* Error--don't want to extract */
}
}
#if defined(O_XATTR)
(void) read_xattr_hdr();
} else {
passtape();
}
goto top;
}
#endif
/* sets *namep to point at the proper name */
if (endtape()) {
if (Bflag) {
/*
* 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".
*/
/* empty body */
}
}
return (-1);
}
gotit = 0;
gotit = 1;
} else {
gotit = 1;
break;
}
}
}
if (! gotit) {
passtape();
return (0);
}
if (vflag) {
*namep);
}
passtape();
return (0);
}
return (1);
}
/*
* Return through *namep a pointer to the proper fullname (i.e "<name> |
* <prefix>/<name>"), as represented in the header entry dblock.dbuf.
*/
static void
{
char *s;
} else {
else
}
/*
* Set dir and component names
*/
#if defined(O_XATTR)
#endif
/*
* Save of real name since were going to chop off the
* trailing slashes.
*/
/*
* first strip of trailing slashes.
*/
s = get_component(savename);
#if defined(O_XATTR)
} else {
}
#endif
}
static wchar_t
yesnoresponse(void)
{
wchar_t c;
c = getwchar();
if (c != '\n')
while (getwchar() != '\n');
else c = 0;
return (c);
}
/*
* Return true if the object indicated by the file descriptor and type
* is a tape device, false otherwise
*/
static int
{
int result = 0;
result = 1;
}
}
return (result);
}
#include <utmpx.h>
typedef struct cachenode { /* this struct must be zeroed before using */
int val; /* the uid or gid of this entry */
int namehash; /* name's hash signature */
} cachenode_t;
#define HASHSIZE 256
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 *
{
int h = val;
cachenode_t *c;
return (c);
}
return (NULL);
}
static cachenode_t *
{
int h = hash_byname(name);
cachenode_t *c;
return (c);
}
return (NULL);
}
static cachenode_t *
{
cachenode_t *c;
int signature;
if (c == NULL) {
perror("malloc");
exit(1);
}
}
else
return (c);
}
static char *
{
cachenode_t *c;
}
return (c->name);
}
static char *
{
cachenode_t *c;
}
return (c->name);
}
static uid_t
getuidbyname(char *name)
{
cachenode_t *c;
}
}
static gid_t
getgidbyname(char *group)
{
cachenode_t *c;
}
}
/*
* 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
const char *name,
const char *linkname,
const char typeflag,
const int filetype,
const char *prefix)
{
int nblks;
const char *filename;
const char *lastslash;
if (filetype == XATTR_FILE)
else
else
if (Eflag) {
xhdr_flgs |= _X_DEVMAJOR;
} else {
"Device major too large for %s. Use -E flag."),
filename);
if (errflag)
done(1);
else
Errflg = 1;
}
dev = 0;
}
if (Eflag) {
xhdr_flgs |= _X_DEVMINOR;
} else {
"Device minor too large for %s. Use -E flag."),
filename);
if (errflag)
done(1);
else
Errflg = 1;
}
dev = 0;
}
if (Eflag) {
else
lastslash++;
xhdr_count++;
xrec_offset = 0;
if (gen_utf8_names(filename) != 0)
return (1);
#ifdef XHDR_DEBUG
#endif
if (xhdr_flgs) {
if (xhdr_flgs & _X_DEVMAJOR)
if (xhdr_flgs & _X_DEVMINOR)
if (xhdr_flgs & _X_LINKPATH)
}
}
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
{
return (0);
}
} else { /* name exists */
return (0);
}
}
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
{
/* Add spaces for the last slash and last NULL */
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 == '/')
*p = '\0'; /* zap the slash */
*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 */
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 */
}
}
/*
* setPathTimes - set the modification time for given path. Return 1 if
* successful and 0 if not successful.
*/
static void
{
/*
* 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.
*/
/* Extended header: use microseconds */
}
/*
* If hflag is set then delete the symbolic link's target.
* If !hflag then delete the target.
*/
static void
{
int n;
PATH_MAX)) != -1) {
} else {
}
} else {
}
}
}
}
/*
* 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
char **secinfo, /* existing security info */
int *secinfo_len, /* length of existing security info */
{
char *new_secinfo;
char *attrtext;
int newattrsize;
int oldsize;
/* no need to add */
return (0);
case ACLENT_T:
case ACE_T:
return (-1);
}
/* header: type + size = 8 */
return (-1);
}
break;
/* SunFed's case goes here */
default:
return (-1);
}
/* old security info + new attr header(8) + new attr */
oldsize = *secinfo_len;
*secinfo_len += newattrsize;
if (new_secinfo == NULL) {
*secinfo_len -= newattrsize;
return (-1);
}
*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
{
long blocks;
int savflag;
int savsize;
/* Just tranditional permissions or no security attribute info */
return;
/* save flag and size */
/* special flag for ancillary file */
if (hdrtype == _XATTR_HDRTYPE)
else
/* for pre-2.5 versions of tar, need to make sure */
/* the ACL file is readable */
/* write out the header */
/* write out security info */
/* restore mode, flag and size */
}
/*
* Read the data record for extended headers and then the regular header.
* The data are read into the buffer and then null-terminated. Entries
* 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;
_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;
int bufneeded;
int errors;
Xtarhdr.x_devmajor = 0;
Xtarhdr.x_devminor = 0;
xhdr_count++;
errors = 0;
}
while (nblocks-- > 0) {
}
i = 0;
break;
i++;
}
errno = 0;
switch (keylist_pair[i].keynum) {
case _X_DEVMAJOR:
if (errno) {
"tar: Extended header major value error "
"for file # %llu.\n"), xhdr_count);
errors++;
} else
xhdr_flgs |= _X_DEVMAJOR;
break;
case _X_DEVMINOR:
if (errno) {
"tar: Extended header minor value error "
"for file # %llu.\n"), xhdr_count);
errors++;
} else
xhdr_flgs |= _X_DEVMINOR;
break;
case _X_GID:
"tar: Extended header gid value error "
"for file # %llu.\n"), xhdr_count);
}
break;
case _X_GNAME:
break;
case _X_LINKPATH:
xhdr_flgs |= _X_LINKPATH;
else
errors++;
break;
case _X_PATH:
else
errors++;
break;
case _X_SIZE:
if (errno) {
"tar: Extended header invalid filesize "
"for file # %llu.\n"), xhdr_count);
errors++;
} else
break;
case _X_UID:
"tar: Extended header uid value error "
"for file # %llu.\n"), xhdr_count);
}
break;
case _X_UNAME:
break;
case _X_MTIME:
if (errno)
"tar: Extended header modification time "
"value error for file # %llu.\n"),
else
break;
default:
gettext("tar: unrecognized extended"
" header keyword '%s'. Ignored.\n"), keyword);
break;
}
}
getdir(); /* get regular header */
if (xhdr_flgs & _X_DEVMAJOR) {
}
if (xhdr_flgs & _X_DEVMINOR) {
}
}
}
}
}
done(1);
else
if (errors)
Errflg = 1;
return (errors);
}
/*
* 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
{
int len;
char *curr_ptr;
/*
* 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.
*/
"cannot allocate extended header buffer"));
xrec_size *= 2;
}
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
{
/* Allow for <seconds>.<nanoseconds>\n */
int len;
char *curr_ptr;
/*
* 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.
*/
"cannot allocate extended header buffer"));
xrec_size *= 2;
}
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
{
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 */
if (len > 997)
len += 3;
else if (len > 98)
len += 2;
else if (len > 9)
len += 1;
"cannot allocate extended header buffer"));
xrec_size *= 2;
}
#ifdef XHDR_DEBUG
#endif
#ifdef XHDR_DEBUG
else {
len += 11;
}
#endif
xrec_offset += len;
}
/*
* Convert time found in the extended header data to seconds and nanoseconds.
*/
void
{
char nanosec[10];
char *period;
int i;
period[0] = '\0';
else {
}
}
/*
* Check linkpath for length.
* Emit an error message and return 1 if too long.
*/
int
char *name,
char *longname,
char *linkname,
char *prefix,
char type,
int filetype)
{
if (Eflag > 0) {
xhdr_flgs |= _X_LINKPATH;
} else {
"tar: %s: linked name too long\n"), linkname);
if (errflag)
done(1);
else
Errflg = 1;
return (1);
}
}
if (xhdr_flgs & _X_LINKPATH)
prefix));
else
}
/*
* Convert from UTF-8 to local character set.
*/
static int
char *option,
char **Xhdr_ptrptr,
char *target,
const char *source,
int max_val)
{
char *nl_target;
const char *iconv_src;
char *iconv_trg;
"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 = "646";
charset_type = 1;
charset_type = 3;
else {
nl_target += 3;
charset_type = 2;
errno = 0;
(iconv_t)-1) {
"tar: conversion routines not "
"available for current locale. "));
"file # %llu: (%s) UTF-8 conversion"
charset_type = -1;
return (1);
}
}
}
/* locale using 7-bit codeset or UTF-8 locale */
"tar: file # %llu: Extended header %s too long.\n"),
xhdr_count, option);
return (1);
}
if (charset_type == 3)
"tar: file # %llu: (%s) UTF-8 conversion"
return (1);
}
*Xhdr_ptrptr = target;
return (0);
}
"tar: file # %llu: (%s) UTF-8 conversion failed.\n"),
xhdr_count, source);
/* Get remaining output; reinitialize conversion descriptor */
inlen = 0;
return (1);
}
/* Get remaining output; reinitialize conversion descriptor */
inlen = 0;
"tar: file # %llu: (%s) UTF-8 conversion failed.\n"),
xhdr_count, source);
return (1);
}
"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)
{
char *nl_target;
int nbytes,
"tar: file # %llu: UTF-8 conversion failed.\n"),
return (1);
}
if (charset_type == 0) { /* Need to get conversion descriptor */
nl_target = "646";
charset_type = 1;
charset_type = 3;
else {
nl_target += 3;
charset_type = 2;
errno = 0;
#ifdef ICONV_DEBUG
"Opening iconv_cd with target %s\n",
#endif
(iconv_t)-1) {
"tar: conversion routines not "
"available for current locale. "));
"file (%s): UTF-8 conversion failed.\n"),
filename);
charset_type = -1;
return (1);
}
}
}
errors = 0;
}
if (nbytes > 0) {
}
}
if (errors > 0)
"tar: file (%s): UTF-8 conversion failed.\n"), filename);
done(1);
else
if (errors)
Errflg = 1;
return (errors);
}
static int
char **Xhdr_ptrptr,
char *target,
const char *source,
int xhdrflg,
int max_val)
{
const char *iconv_src;
const char *starting_src;
char *iconv_trg;
#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.
*/
*Xhdr_ptrptr = target;
return (0);
} else
iconv_src = (const char *) *Xhdr_ptrptr;
} else {
return (0); /* Don't create xhdr record */
}
return (0);
gettext("tar: invalid character in"
" UTF-8 conversion of '%s'\n"), starting_src);
return (1);
}
return (0);
}
errno = 0;
(size_t)-1) {
/* An error occurred, or not all characters were converted */
gettext("tar: invalid character in"
" UTF-8 conversion of '%s'\n"), starting_src);
else
"tar: conversion to UTF-8 aborted for '%s'.\n"),
/* Get remaining output; reinitialize conversion descriptor */
inlen = 0;
return (1);
}
/* Get remaining output; reinitialize conversion descriptor */
inlen = 0;
gettext("tar: invalid character in"
" UTF-8 conversion of '%s'\n"), starting_src);
else
"tar: conversion to UTF-8 aborted for '%s'.\n"),
return (1);
}
*Xhdr_ptrptr = target;
#ifdef ICONV_DEBUG
}
}
#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
{
const char *thischar;
while (len-- > 0) {
return (1);
}
return (0);
}
#if defined(O_XATTR)
static void
char **attrbuf,
char *filename,
char *attrname,
char typeflag,
int *rlen)
{
char *bufhead; /* ptr to full buffer */
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 */
/*
* Release previous buffer
*/
}
/*
* First add in fixed size stuff
*/
/*
* Add space for two nulls
*/
/*
* Now add on space for link info if any
*/
/*
* Again add space for two nulls
*/
len += linkstringlen;
}
/*
* Now add padding to end to fill out TBLOCK
*
* Function returns size of real data and not size + padding.
*/
}
/*
* Now we can fill in the necessary pieces
*/
} else {
linklen = 0;
}
/*
* first fill in the fixed header
*/
/*
* Now fill in the filename + attrnames section
*/
/*
* Now fill in the optional link section if we have one
*/
(void) strcpy(
}
}
#else
static void
char **attrbuf,
char *filename,
char *attrname,
char typeflag,
int *rlen)
{
*rlen = 0;
}
#endif
int
{
int i, j;
int printerr;
int slnkerr;
if (!hflag)
else
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 */
/*
* 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.
*/
printerr = 0;
/*
* Restore errno in case the lstat
* on symbolic link change
*/
}
if (printerr) {
Errflg = 1;
}
return (1);
}
return (0);
}
#if defined(O_XATTR)
static void
{
int dirfd;
return;
}
"tar: unable to open attribute directory for file %s\n"),
longname);
return;
}
"tar: unable to open dir pointer for file %s\n"), longname);
return;
}
continue;
Hiddendir = 1;
else
Hiddendir = 0;
if (exitflag)
break;
}
}
#else
static void
{
}
#endif /* O_XATTR */
static int
{
int found = 0;
found++;
break;
}
if (found) {
#if defined(O_XATTR)
if (filetype == XATTR_FILE)
goto out;
}
#endif
if (filetype != XATTR_FILE) {
goto out;
}
}
newvol();
/*
* 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) {
#ifdef DEBUG
if (NotTape)
"K\t", K(tapepos), 0);
#endif
if (filetype == XATTR_FILE) {
"a %s attribute %s link to "
"attribute %s\n"),
} else {
"a %s link to %s\n"),
}
}
return (0);
} else {
if (filetype == XATTR_FILE) {
} else {
}
}
}
}
out:
return (1);
}
static int
{
int error;
}
#if defined(O_XATTR)
return (1);
}
}
#endif
/* ACL support */
if (pflag) {
int len = 0;
/* ACL support */
/*
* Get ACL info: dont bother allocating space if
* there is only a trivial ACL.
*/
&aclp)) != 0) {
"%s: failed to retrieve acl : %s\n"),
return (1);
}
}
/* append security attributes if any */
}
}
return (0);
}
#if defined(O_XATTR)
static int
{
int error = 0;
int attrlen;
}
strlen(".hdr"));
}
"Buffer overflow writing extended attribute file name"));
}
/*
* dump extended attr lookup info
*/
/*
* Set up filename for attribute
*/
return (error);
}
#endif
#if defined(O_XATTR)
static int
{
char *tp;
int namelen;
return (1);
"Insufficient memory for extended attribute\n"));
return (1);
}
while (blocks-- > 0) {
break;
} else {
}
}
/*
* Validate that we can handle header format
*/
gettext("Unknown extended attribute format encountered\n"));
gettext("Disabling extended attribute parsing\n"));
xattrbadhead = 1;
return (0);
}
sizeof (struct xattr_hdr));
if (link_len > 0)
xattr_linkp = (struct xattr_buf *)
else
xattr_linkp = NULL;
if (xattr_linkp) {
} else {
}
return (0);
}
#else
static int
{
return (0);
}
#endif
/*
* skip over extra slashes in string.
*
* For example:
*
* would return pointer at
* ^
*/
static char *
{
string--;
}
return (string);
}
/*
* Return the parent directory of a given path.
*
* Examples:
* / returns .
* /usr returns /
* file returns .
*
* dir is assumed to be at least as big as path.
*/
static void
{
char *s;
}
} else {
s = skipslashes(s, tmpdir);
*s = '\0';
if (s == tmpdir)
else
}
}
#if defined(O_XATTR)
static char *
get_component(char *path)
{
char *ptr;
return (path);
} else {
/*
* Handle trailing slash
*/
return (ptr);
else
return (ptr + 1);
}
}
#else
static char *
get_component(char *path)
{
return (path);
}
#endif
static int
retry_attrdir_open(char *name)
{
int dirfd = -1;
struct stat parentstat;
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--
* won't let us create an attribute dir
* if it doesn't already exist.
*
* If file has a non-trivial ACL, then save it
* off so that we can place it back on after doing
* chmod's.
*/
return (-1);
}
return (-1);
}
gettext("tar: cannot chmod file %s to %o %s\n"),
if (aclp)
return (-1);
}
/*
* Don't print error message if attropen() failed,
* caller will print message.
*/
/*
* Put mode back to original
*/
gettext("tar: cannot chmod file %s to %o %s\n"),
}
if (aclp) {
if (error) {
gettext("tar: %s: failed to set acl entries\n"),
name);
}
}
/*
* Put back time stamps
*/
return (dirfd);
}
#if !defined(O_XATTR)
static int
{
}
static int
{
}
static int
{
if (flag == AT_SYMLINK_NOFOLLOW)
else
}
static int
{
}
static int
{
}
static int
{
if (flag == AT_REMOVEDIR)
else
}
static int
{
if (flag == AT_SYMLINK_NOFOLLOW)
else
}
static int
{
return (-1);
}
#endif
static void
chop_endslashes(char *path)
{
/*
* Chop of slashes, but not if all we have is slashes
* for example: ////
* should make no changes, otherwise it will screw up
* checkdir
*/
*ptr = '\0';
}
}
}