1
0
Files
irix-657m-src/eoe/cmd/tar/tar.c
2022-09-29 17:59:04 +03:00

3900 lines
89 KiB
C

/* from tar.c 4.19 (Berkeley) 9/22/83 */
#ident "tar/tar.c: $Revision: 1.122 $"
/*
* Modified 1/84 by Dave Yost
* - At end of archive, tar now tries two more reads to find end of file
* so that mag tape is properly positioned at the beginning of the next
* tape file.
* - Recognize premature end of file as such. These last two
* called for changes to getdir(), readtape(), passtape(), endtape(),
* and the places where they are called.
* - Add 'e' option to continue after tape read errors.
* - Add 'X' option, which is like 'x' except it compares
* against another tree and links to there on files from tape
* which are identical to their counterparts in the specified tree.
* - Add 'C' option to compare the tape against the file system.
* - Changed the linkbuf struct, putting the filename string at the
* end and allocating just enough space for it, instead of making
* it a fixed-size array which is hoped to be big enough.
* - Replaced some of the rflag, tflag, etc. variables with a 'work'
* variable that is set to the type of work to be done.
* - Enforce blocking factor limit of 20. If tar can't extract it, why
* let it write it?
* - The 'cannot link' message now says what it cannot link to.
* - The 'x filename' message printed when extracting now holds back the
* newline until the file is finished extracting so you can know when
* it is done if you are only extracting one file and want to kill it.
* - Print out group owner %-2d instead of %d.
* - Changed default device to the logical device /dev/tar instead of
* /dev/rmt0. Let'em make the appropriate device for their system.
* - Complain and exit if tarfile specified with 'f' option is in /dev
* and either doesn't exist or is a regular file.
* - Put in some ifdefs so this source will work on more vanilla systems.
* - Buffer stderr and flush it after each write. (4.2 line buffering not
* used because it reverts to block buffering when tar output log is
* redirected to a file and because it is not portable.)
* - Clean up file names before writing them on the directory block:
* no leading '//' or './' garbage.
* - If a filename on tape has a leading './', pretend it doesn't.
* - Added 'R' option to ignore leading '/' when extracting.
* - Fixed bug: tar got mixed up on what its parent directory was if you
* tried to archive a directory starting with '/'.
* - Added 'U' option to unlink before each creat.
* - Fix bug where file descriptor was not freed up when file name too
* long in putfile in the IF_REG case.
* - Tar passes lint now.
*
* Modified 7/84 by Bob Toxen of Silicon Graphics, Inc.
*
* 1. Tar CAN NOW BACK UP CHAR/BLOCK DEVICES AND NAMED PIPES!!!
* 2. Renamed '-o' option to '-d'.
* 3. Added '-o' option to not do CHOWN or CHGRP on extracted files.
* 4. Default blocking factor for our cartridge tape is now 400.
* 5. Fix bug so Tar will cope with pwd returning excessively long path.
* 6. Added '-V' option to allow variably sized last block to avoid large
* write for last block for hugely blocked tapes.
* 7. Can now have huge blocking factors.
* 8. Fixed bug whereby Tar hung on device and named pipe files (see #1).
* 9. Fixed bug where Tar stripped set-U/Gid & sticky bit from modes.
* 10. Fixed bug whereby a compiler warning about NULL being redefined.
* 11. Changed default archive file back to /dev/rmt1.
* 12. Added support for System III and System V.
* 13. Jazzed up conditional code & added comments.
* 14. Allow specifying all drives from 1 to 9, don't have to have 4.2BSD MT.
* 15. In verbose mode tells when symbolic links are changed to hard links.
* 16. Files in directories are written to tape alphabetically!!!!!
* 17. Fixed bug whereby if "/" is to be backed up tar tried to open ""
* which was interpreted as "." on V7 but is illegal on System III & V.
* 18. Can now handle multiple tape volumes. If -e (continue on errors)
* is NOT specified then Tar will consider any error return to mean
* end of tape. Otherwise any error except EIO will mean end of tape.
* 19. Don't try to read past end of tape if OLDMAGTAPE.
* 20. If file name "-" is on command line then read list of files from
* standard input (thanks to Steve Hartwell).
* 21. Added -a to preserve access time on files read (10/10/84).
*/
/*
* Tape Archival Program
*/
#define OLDMAGTAPE
#define CART
#define FASTDIE
#define DEVTAR "/dev/tape"
#define DEVTAR2 "/dev/tape1"
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/sysmacros.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <dirent.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <utime.h>
#include <unistd.h>
#include <bstring.h>
#include <stdlib.h>
#include <time.h>
#include <malloc.h>
#include <pwd.h> /* POSIX */
#include <grp.h> /* POSIX */
/*
* SGI: getbsize is unconditionally part of tar, so we need this.
*/
#include <sys/mtio.h>
#include <sys/tpsc.h> /* for audio turn off */
#include <tar.h>
#ifdef TRUSTEDIRIX
#include <sys/mac.h>
#include <sys/acl.h>
#endif /* TRUSTEDIRIX */
#define YES 1
#define NO 0
#undef MIN
#define MIN(a,b) (((a) < (b))? (a): (b))
#define NBLOCK 20 /* default blocking factor */
#ifdef CART
#define NBLOCKC 400 /* default SGI cartridge blocking factor */
#else
#define NBLOCKC 20 /* default blocking factor */
#endif
#define DFUDGE 20 /* fudge factor on growing directories */
#define S_PERMMASK (~S_IFMT) /* mask for permission part of modes */
#define TBLOCK 512 /* size of a tape block */
#define FCHR 0020000
#define FDIR 0040000
#define FBLK 0060000
#define FIFO 0010000
/* even for SVR3 must keep NAMSPACE 100 and not MAXPATHLEN because TAR
* tape block size written in stone (TBLOCK)
*/
#define NAMSPACE 100 /* total storage, including terminating NULL */
#define NAMSIZ (NAMSPACE-1) /* max # of 'usable' chars */
#define PREFIXSPACE 156 /* POSIX: prefix space including NULL */
#define PREFIXSIZ (PREFIXSPACE-1)
/*
* Added for xfs: Block size for large file transfers
*/
#define BIGBLOCK ((long long)1<<((sizeof(long)*CHAR_BIT)-1))
union hblock {
char dummy[TBLOCK];
struct header {
char name[NAMSPACE];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char linkflag;
char linkname[NAMSPACE];
char rdev[12];
} dbuf;
struct pheader { /* POSIX */
char name[NAMSPACE]; /* POSIX */
char mode[8]; /* POSIX */
char uid[8]; /* POSIX */
char gid[8]; /* POSIX */
char size[12]; /* POSIX */
char mtime[12]; /* POSIX */
char chksum[8]; /* POSIX */
char typeflag; /* POSIX */
char linkname[NAMSPACE]; /* POSIX */
char magic[6]; /* POSIX */
char version[2]; /* POSIX */
char uname[32]; /* POSIX */
char gname[32]; /* POSIX */
char devmajor[8]; /* POSIX */
char devminor[8]; /* POSIX */
char prefix[PREFIXSPACE]; /* POSIX */
} pdbuf; /* POSIX */
};
/* #define linkflag typeflag */
struct linkbuf {
struct linkbuf *nextp;
dev_t devnum;
ino64_t inum;
short count;
char pathname[1]; /* actually alloced larger */
};
union hblock dblock;
union hblock *tbuf;
char *dblockname; /* pointer to name */
char *dblocklnname; /* pointer to link name*/
char *dblocksymname; /* pointer to symbolic linkname*/
struct linkbuf *ihead;
struct stat64 stbuf;
#ifdef TRUSTEDIRIX
#define TIRIX_PREFIX "/tmp/TRUSTEDIRIX-label-data."
#define ATTRFILE "attribute"
char tirix_file[NAMSIZ];
char *g_attrbuf;
char g_lname[256]; /* to go with stbuf */
char g_aclstr[256]; /* to go with stbuf */
mac_t g_lp; /* to go with g_lname */
acl_t g_aclp;
void attr_from_tape(void);
void clear_gacl(void);
void clear_glabel(void);
void clear_attr(void);
#endif /* TRUSTEDIRIX */
/*
* The kind of work we are going to do is determined
* by the value of the 'work' variable. Other aspects
* of the work are influenced by other flags.
*/
typedef void (*funcaddr_t)(char **); /* Pointer to a function */
funcaddr_t work;
#define NOWORK (funcaddr_t)0
int vflag;
int cflag;
int mflag;
int fflag;
int dflag; /* don't backup directory files */
int pflag;
int wflag;
int Lflag;
int Bflag;
int Kflag; /* Added for xfs */
int Eflag; /* SGI: Exclude any non-local files/dirs (including symlinks
* to remote, if L also specified. This includes dirs given on the
* command line. Used primarily for full system backups */
int Fflag; /* undocumented 4.2 flag */
int Nflag; /* extract only if it doesn't exist */
int continflag; /* continue on read errs */
char lnflag; /* -X link from linkdir if identical */
int Rflag; /* ignore leading '/' when extracting */
int Uflag; /* unlink before creating each file */
int Vflag; /* SGI: Variable blocking */
int oflag; /* SGI: don't chown files */
int bflag; /* SGI: user set blocking */
int Dflag; /* SGI: don't backup device/pipe files */
int aflag; /* SGI: restore access time */
int Pflag; /* SGI: use POSIX header format */
int Sflag; /* SGI: 3.3.* and prior chown behavior */
#ifdef TRUSTEDIRIX
int Mflag; /* store/expect phantom label files */
#endif /* TRUSTEDIRIX */
/* the max filename length for POSIX format allows 255 chars plus one
* terminating NULL char (NAMSPACE+PREFIXSPACE). In the non-POSIX
* format, the only filename array is NAMSPACE (100) bytes, which
* must store the NULL too. Therefore, in the POSIX format maxnamlen
* == NAMSPACE+PREFIXSIZ, otherwise it is NAMSIZ, where PREFIXSIZ and
* NAMSIZ are defined to be one less than their *SPACE counterparts. */
int maxnamlen = NAMSPACE + PREFIXSIZ; /* default to POSIX */
int infrompipe;
int mt;
int term;
int chksum;
int recno;
int first;
int linkerrok;
char *linkdir; /* link from this directory if identical */
int freemem = 1;
int nblock;
int isatape = 0; /* output to a device (as opposed to pipe or file).
used to decide whether we should check for certain types of tape
errors, and also multi-vol archive stuff. */
int debug; /* activate with -q */
off64_t low;
off64_t high;
FILE *tfile;
char tname[] = "/tmp/tarXXXXXX";
char *usefile;
char *defaultdev = DEVTAR;
char *defdev2 = DEVTAR2;
int atstrcmp(char **a, char **b);
void backtape(void);
int bread(int fd, char *buf, int size, char *eot);
off64_t bsrch(char *s, int n, off64_t l, off64_t h);
int bufcmp(register char *cp1, register char *cp2, register int num);
int bwrite(int fd, char *buf, int size, char *eot);
int checkdir(register char *name);
int checkf(char *name, mode_t mode, int howmuch);
int checksum(void);
int checkupdate(char *arg);
int checkw(int c, char *name);
void chkandfixaudio(int mt);
void choose(int *pairp, struct stat64 *st);
int cmp(char *b, char *s, int n);
void cmpname(char type, int do_nl);
int cmprd(int ifile, char *buf, long num);
void cond_unlink(char *name);
int dirpart(char *str);
void docompare(char *argv[]);
void done(int n);
void dorep(char *argv[]);
void dotable(char *argv[]);
void doxtract(char *argv[]);
void endread(void);
int endtape(void);
gid_t findgid(struct pheader *hp);
char *findgname(gid_t gid);
uid_t finduid(struct pheader *hp);
char *finduname(uid_t uid);
void flushtape(void);
int getbsize(int fd);
int getdir(void);
int gotit(register char **list, register char *name);
char *getlink(struct pheader *hp);
char *getname(struct pheader *hp);
void longt(register struct stat64 *st, char *name);
off64_t lookup(char *s);
int myio(int (*fn)(), int fd, char *ptr, int n, char *eot);
void onhup(void);
void onintr(void);
void onquit(void);
int openmt(char *tape, int writing);
static char parseaction(char *cp);
void passtape(void);
void pmode(register struct stat64 *st);
int prefix(register char *s1, register char *s2);
void premature_eof(void);
void putempty(void);
void putfile(char *longname, char *shortname, char *parent);
int putname(struct pheader *hp, char *wholename);
int readtape(char *buffer);
int response(void);
int signedchecksum(void);
void terror(int isread, int exval);
void tomodes(register struct stat64 *sp);
void usage(void);
void writetape(char *buffer);
void zeroendtbuf(void);
#ifdef RMT
int rmtopen(char *, mode_t);
int rmtcreat(char *, mode_t);
int rmtclose(int);
int rmtlseek(int, off_t, int);
int rmtread(int, char *, size_t);
int rmtwrite(int, char *, size_t);
int rmtioctl(int, int, ...);
#define ioctl rmtioctl /* all ioctls are done on device only,
reduces ifdefs. Olson */
#endif /* RMT */
char action = 0; /* this char holds what tar will do */
int minusok = 0; /* only allow '-' (piped filelist) w/ r,u,& c */
int imroot = 0; /* is tar running as superuser? */
static mode_t Oumask = 0; /* old umask value */
main(int argc, char *argv[])
{
char stderrbuf[BUFSIZ];
char *usefil1;
char *cp;
#ifdef TRUSTEDIRIX
sprintf(tirix_file, "%s%d", TIRIX_PREFIX, getpid());
#endif /* TRUSTEDIRIX */
usefil1 = DEVTAR2;
Pflag++; /* by default use POSIX format */
setbuf(stderr, stderrbuf);
if (geteuid() == (uid_t)0)
imroot++;
if (argc < 2)
usage();
tfile = NULL;
argv[argc] = 0; /* may be illegal! */
argv++; /* bump over progname */
cp = *argv++;
/* skip leading '-' only if preceeds a real option */
if ((*cp == '-') && (cp[1] != '\0')) {
if (debug)
printf("skipping %c\n",*cp);
cp++;
}
action = parseaction(cp);
switch(action) { /* get the operation for tar to perform */
case 'u': /* update archive */
mktemp(tname);
if ((tfile = fopen(tname, "w")) == NULL) {
fprintf(stderr,
"tar: cannot create temporary file (%s)\n",
tname);
done(1);
}
fprintf(tfile, "!!!!!/!/!/!/!/!/!/! 000\n");
/* FALL THRU */
case 'r': /* append files to end of archive */
work = dorep;
minusok++;
#ifdef TRUSTEDIRIX
Mflag = -1;
#endif /* TRUSTEDIRIX */
break;
case 'X': /* extract files from archive with linking */
if (*argv == 0) {
fprintf(stderr,
"tar: linkdir must be specified with 'X' option\n");
usage();
}
linkdir = *argv++;
lnflag++;
#ifdef TRUSTEDIRIX
Mflag = -1;
#endif /* TRUSTEDIRIX */
/* FALL THRU */
case 'x': /* extract files from archive */
work = doxtract;
break;
case 't': /* list file names */
work = dotable;
break;
case 'c': /* create a new archive */
cflag++;
work = dorep;
minusok++;
break;
case 'C': /* Compare files */
work = docompare;
#ifdef TRUSTEDIRIX
Mflag = -1;
#endif /* TRUSTEDIRIX */
break;
default: /* can never happen now that parseaction checks */
fprintf(stderr,"First flag must be an function specifier:\n");
usage();
}
if (debug)
fprintf(stderr,"action = %c\n",action);
/* Now fetch remaining opts and/or parms. */
for (; *cp; cp++)
switch (*cp) {
case 'r':
case 'u':
case 'x':
case 'X':
case 't':
case 'c':
case 'C': break; /* already seen by parseaction() */
case 'f':
if (*argv == 0) {
fprintf(stderr,
"tar: tapefile must be specified with 'f' option\n");
usage();
}
usefile = *argv++;
fflag++;
break;
case 'b': /* this case must come after 'f'; mandated
* by parseaction() and the man page */
if (*argv == 0) {
fprintf(stderr,
"tar: blocksize must be specified with 'b' option\n");
usage();
}
nblock = atoi(*argv);
if (nblock <= 0) {
fprintf(stderr, "tar: invalid blocksize.\n");
done(1);
}
bflag++;
argv++;
break;
case 'e':
continflag++;
break;
case 'd':
dflag++;
Dflag++;
break;
case 'p':
pflag++;
break;
case 'v':
vflag++;
break;
case 'w':
wflag++;
break;
case 'R':
Rflag++;
break;
case 'U':
Uflag++;
break;
#ifdef TRUSTEDIRIX
case 'M':
if (!sysconf(_SC_MAC) && !sysconf(_SC_ACL)) {
fprintf(stderr,
"tar: 'M' option may only be specified with Trusted Systems\n");
usage();
}
if (Mflag < 0) {
fprintf(stderr,
"tar: 'M' option may only be specified with 'c', 't', or 'x' option\n");
usage();
}
Mflag = 1;
break;
#endif /* TRUSTEDIRIX */
case 'm':
mflag++;
break;
case 'a':
aflag++;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
fprintf( stderr,
"tar: Use -f [tapefile] to specify a device other than %s\n", DEVTAR );
usage();
/* NOTREACHED */
case 'l':
linkerrok++;
break;
case 'H':
case 'h':
Lflag = 0;
break;
case 'L':
Lflag++;
break;
case 'B':
Bflag++;
break;
case 'E':
Eflag++;
break;
case 'F':
Fflag++;
break;
case 'N':
Nflag++;
break;
case 'K':
if (!Pflag) {
fprintf(stderr,
"'K' and 'O' flags are incompatible:\n");
usage();
}
Kflag++;
break;
case 'V': /* SGI: Variable blocking */
Vflag++;
break;
case 'o': /* SGI (pre 5.2): don't chown */
if (Sflag) {
fprintf(stderr,
"'o' and 'S' flags are incompatible:\n");
usage();
}
oflag++;
break;
case 'S': /* SGI (pre SVR4): chown to tape user */
if (oflag) {
fprintf(stderr,
"'o' and 'S' flags are incompatible:\n");
usage();
}
Sflag++;
if (pathconf(NULL,_PC_CHOWN_RESTRICTED) == 1) {
fprintf(stderr,
"\n\n\t\tWARNING: Restricted chown enabled:\n\n\tIRIX configuration ");
fprintf(stderr,
"won't allow 'S' option to chmod\n\n\n");
fflush(stderr);
}
break;
case 'D': /* SGI: don't back devs/pipes */
Dflag++;
break;
case 'q': /* SGI: bump up debugging level */
debug++;
break;
case 'O': /* SGI: write pre-POSIX format */
if (Kflag) {
fprintf(stderr,
"'K' and 'O' flags are incompatible:\n");
usage();
}
Pflag = 0;
maxnamlen = NAMSIZ;
break;
default:
fprintf(stderr,
"tar: %c: unknown or illegal option\n",*cp);
usage();
}
if (!usefile) {
/* we've had an RFE on this for along time, and svr4EA5 does it,
* so we'll add it. I'm not adding the whole default table stuff
* EA5 has though! 'f' option overrides. Tell them about it with 'q'
*/
usefile = getenv("TAPE");
if(usefile) {
if(debug)
fprintf(stderr, "Use $TAPE as archive name: %s\n", usefile);
}
else
usefile = defaultdev;
}
if (!strncmp("/dev/", usefile, 5)) {
struct stat64 stbuf;
if (stat64(usefile, &stbuf) < 0
|| (stbuf.st_mode & S_IFMT) == S_IFREG) {
if (!fflag) {
if (stat64(usefil1, &stbuf) < 0)
{
/* IFREG check appears to be here so we don't output to
/dev/tape if 'f' not given, and it happens to be a file. */
/* Also added invalid blocksize in error message -- hack
over a hack!!
*/
fprintf(stderr,
"tar: archive file %s does not exist or is a regular file or invalid blocksize\n",
usefile);
done(1);
}
else {
usefile = usefil1;
}
}
else {
/* IFREG check is here so we don't output to a file in
* /dev even if 'f' given, and it happens to be a file. */
/* Also added invalid blocksize in error message -- hack
over a hack!!
*/
fprintf(stderr,
"tar: archive file %s does not exist or is a regular file or invalid blocksize\n",
usefile);
done(1);
}
}
isatape = 1;
}
if (work == dorep) {
mt = openmt(usefile, 1);
dorep(argv);
}
else if (work != NOWORK) {
mt = openmt(usefile, 0);
(*work) (argv);
} else
usage();
done(0);
/* NOTREACHED */
return 0;
}
void
usage(void)
{
fprintf(stderr,
"usage: tar [-][{ruxXtcC}acdefhlm{o|S}pqvwLUBDRV{O|K}fb] [dir] [tapefile]\
[blocksize] file ...\n");
done(1);
}
openmt(char *tape, int writing)
{
int pgsize;
if (strcmp(tape, "-") == 0) {
/*
* Read from standard input or write to standard output.
*/
if (writing) {
if (cflag == 0) {
fprintf(stderr,
"tar: can only create standard output archives\n");
done(1);
}
mt = dup(1);
if (!Vflag)
nblock = 1;
} else {
infrompipe = 1;
mt = dup(0);
if (!Vflag)
nblock = 1;
}
} else {
/*
* Use file or tape on local machine.
*/
if (writing) {
int saverr = 0;
mt = -1;
/* If -c then truncate regular files! */
#ifdef RMT
if (cflag) {
mt = rmtcreat(usefile, 0666);
if(mt == -1) saverr = errno;
}
if(mt < 0)
mt = rmtopen(usefile, cflag?1:2);
#else
if (cflag) {
mt = creat(usefile, 0666);
if(mt == -1) saverr = errno;
}
if (mt < 0)
mt = open(usefile, cflag?1:2);
#endif /* RMT */
if(mt == -1 && saverr && errno == ENOENT) errno = saverr;
#ifdef RMT
} else
mt = rmtopen(tape, 0);
#else
} else
mt = open(tape, 0);
#endif /* RMT */
if (mt <0) {
fprintf(stderr, "tar: %s: %s\n",tape,strerror(errno));
done(1);
}
else
chkandfixaudio(mt);
}
if (!bflag || (nblock == 0) )
nblock = getbsize(mt);
if((pgsize=getpagesize())<=0) {
fprintf(stderr, "Illegal getpagesize() %d\n", pgsize);
done(1);
}
pgsize--; /* make it a mask */
tbuf = (union hblock *)malloc((unsigned int)(nblock*TBLOCK+pgsize));
if (tbuf == NULL) {
fprintf(stderr,
"tar: block size %d too big, can't get memory\n",
nblock * TBLOCK);
done(1);
}
/* align to page boundary for best performance */
tbuf = (union hblock *)(((unsigned int)tbuf+pgsize)&~pgsize);
return(mt);
}
/*
* Try to figure out the block size using a mag tape ioctl. Sketch:
* if (using standard input/output archive)
* dup stdin or stdout;
* else if (open() fails && not creating a named archive)
* complain and die;
* if (get block size via ioctl() fails)
* return default block size;
* else
* return ioctl() block size;
*/
int
getbsize(int fd)
{
register int bsize;
bsize = 0;
if (fd >= 0) {
auto int blksize;
if (ioctl(fd, MTIOCGETBLKSIZE, &blksize) == 0)
bsize = blksize;
}
if (bsize <= 0)
bsize = NBLOCK;
/* The stat() in main doesn't set isatape for remote
tapes, because it always fails for remote devices as of 1/89
(even if you use rmtstat(). So, if the ioctl above succeeded,
we are clearly dealing with a tape, and we set isatape. This
also catches the case where the 'f' option was given, and
the pathname didn't start with /dev. Olson, 1/89. */
else
isatape = 1;
return bsize;
}
void
dorep(char *argv[])
{
char wdir[MAXPATHLEN];
int anywritten = 0;
if (cflag && tfile != NULL)
usage();
#ifndef FASTDIE
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, onintr);
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
signal(SIGHUP, onhup);
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
signal(SIGQUIT, onquit);
#endif /* FASTDIE */
if (!cflag) { /* 'r' mode */
while (getdir()) {
passtape();
if (term) {
if (debug)
fprintf(stderr,"Exiting from dorep\n");
done(0);
}
}
backtape();
if (tfile != NULL) { /* 'u' mode */
char buf[200];
sprintf(buf,
"sort +0 -1 +1nr %s -o %s; awk '$1 != prev {print; prev=$1}' %s >%sX; mv %sX %s",
tname, tname, tname, tname, tname, tname);
fflush(tfile);
system(buf);
freopen(tname, "r", tfile);
fstat64(fileno(tfile), &stbuf);
if ((stbuf.st_mode & S_IFMT) != S_IFREG)
stbuf.st_size = 0;
high = stbuf.st_size;
}
}
/*
* Modified by Bob to do Steve Hartwell's
* enhancement of reading list of files to
* backup from stdin if file name is "-".
*/
(void) getwd(wdir);
while (*argv && ! term) {
register char *cp, *cp2;
char tempdir[MAXPATHLEN], *parent;
char stdinfname[MAXPATHLEN+2], *filearg;
if (!strcmp(*argv, "-C") && argv[1]) {
argv++;
if (chdir(*argv) < 0)
fprintf(stderr,"%s: %s\n",*argv,
strerror(errno));
else
(void) getwd(wdir);
argv++;
continue;
}
if (!strcmp(*argv,"-")) {
if (gets(stdinfname) == NULL) {
argv++;
continue;
}
filearg = stdinfname;
} else
filearg = *argv++;
cp2 = 0;
/* deal with trailing /'s in names, such as
* as tar cv /usr/ Otherwise we pass an emptry
* string to putfile as 2nd arg, producing bogus errors. */
for (cp = &filearg[strlen(filearg)-1]; cp>filearg && *cp=='/'; cp--)
*cp = '\0';
for (cp = filearg; *cp; cp++)
if (*cp == '/')
cp2 = cp;
if (cp2) {
*cp2 = '\0';
if (chdir(filearg[0] ? filearg : "/") < 0) {
fprintf(stderr,"%s: %s\n",filearg,
strerror(errno));
continue;
}
parent = getwd(tempdir);
*cp2 = '/';
if(cp2[1])
cp2++;
} else {
cp2 = filearg;
parent = wdir;
}
putfile(filearg, cp2, parent);
anywritten = 1;
if (chdir(wdir) < 0) {
fprintf(stderr, "cannot change back?: ");
fprintf(stderr,"%s: %s\n",wdir,strerror(errno));
fflush(stderr);
}
}
if(anywritten) { /* this is so a mistaken c option when they
meant t or C won't destroy an archive (if no files were
specified). */
putempty();
if(nblock>1)
putempty();
if ( Pflag )
zeroendtbuf();
flushtape();
}
if (linkerrok == 0)
return;
for (; ihead != NULL; ihead = ihead->nextp) {
if (ihead->count == 0)
continue;
fprintf(stderr, "tar: missing links to %s\n", ihead->pathname);
fflush(stderr);
}
}
int
endtape(void)
{
if (!continflag && dblock.dbuf.name[0] == '\0')
return YES;
/*
* Assume constant expression in if causes dead code elimination.
*/
if(TBLOCK % sizeof(int)) {
register char *cp;
register char *endp;
endp = &dblock.dummy[TBLOCK];
for (cp = dblock.dummy; cp < endp; cp++)
if (*cp != '\0')
return NO;
}
else {
register int *ip;
register int *endp;
endp = (int *) &dblock.dummy[TBLOCK];
for (ip = (int *) dblock.dummy; ip < endp; ip++)
if (*ip != 0)
return NO;
}
return YES;
}
int
getdir(void)
{
register struct stat64 *sp;
register int continuing;
int i, firstdir=1, isposix = 0, signedsum;
static hadsignedsum = 0;
char cbuf[sizeof(dblock.dbuf.chksum)];
continuing = NO;
for (;;) {
int cnt;
if ((cnt = readtape((char *) &dblock)) < 0) {
fflush(stdout);
if (!continflag) {
fprintf(stderr,
"tar: tape error reading dir-block\n");
done(2);
}
if (!continuing) {
fprintf(stderr,
"tar: tape error reading dir-block. Continuing.");
fflush(stderr);
continuing = YES;
} else {
fprintf(stderr, ".");
fflush(stderr);
}
continue;
}
if (cnt == 0)
premature_eof();
if (endtape() && !continuing)
/* if endtape and continuing, do NOT
* return NO, since it is common to find
* blocks of nulls in binary files on the
* backup. Keep going until we re-sync,
* we get a read error, or reach EOT/EOF */
return NO;
sp = &stbuf;
if ( strcmp(dblock.pdbuf.magic, TMAGIC) == 0 ) {
isposix = 1;
sp->st_mode = strtol(dblock.pdbuf.mode, 0, 8);
sp->st_uid = finduid(&dblock.pdbuf);
sp->st_gid = findgid(&dblock.pdbuf);
sp->st_size = 0; /* default val */
switch ( dblock.pdbuf.typeflag ) {
case '3': /* CHR */
sp->st_mode |= FCHR;
sp->st_rdev = makedev(
strtol(dblock.pdbuf.devmajor, 0, 8),
strtol(dblock.pdbuf.devminor, 0, 8));
break;
case '4': /* BLK */
sp->st_mode |= FBLK;
sp->st_rdev = makedev(
strtol(dblock.pdbuf.devmajor, 0, 8),
strtol(dblock.pdbuf.devminor, 0, 8));
break;
case '5': /* DIR */
sp->st_mode |= FDIR;
break;
case '6': /* FIFO */
sp->st_mode |= FIFO;
sp->st_rdev = makedev(
strtol(dblock.pdbuf.devmajor, 0, 8),
strtol(dblock.pdbuf.devminor, 0, 8));
break;
default:
sp->st_size = (long)strtoul(dblock.pdbuf.size, 0, 8);
break;
}
sp->st_mtime = strtol(dblock.pdbuf.mtime, 0, 8);
chksum = strtol(dblock.pdbuf.chksum, 0, 8);
} else { /* not POSIX */
isposix = 0;
sscanf(dblock.dbuf.mode, "%o", &i);
sp->st_mode = i;
sscanf(dblock.dbuf.uid, "%o", &i);
sp->st_uid = i;
sscanf(dblock.dbuf.gid, "%o", &i);
sp->st_gid = i;
sscanf(dblock.dbuf.size, "%llo", &(sp->st_size));
sscanf(dblock.dbuf.mtime, "%lo", &sp->st_mtime);
sscanf(dblock.dbuf.chksum, "%o", &chksum);
sscanf(dblock.dbuf.rdev, "%o", &sp->st_rdev);
switch ((int)(sp->st_mode & ~07777)) {
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
sp->st_size = 0;
break;
default:
if (dblock.dbuf.name[
strlen(dblock.dbuf.name)] == '/')
sp->st_size = 0;
}
} /* not POSIX */
if(firstdir) /* preserve for byteswap chk */
strncpy(cbuf, dblock.dbuf.chksum, sizeof(cbuf));
if (chksum == (i=checksum())) {
if (continuing) {
fprintf(stderr, "OK\n");
fflush(stderr);
}
break;
}
else {
signedsum = signedchecksum();
if(chksum == signedsum) {
if(!hadsignedsum) {
fprintf(stderr,
"tar: archive apparently built with signed checksums\n");
hadsignedsum = 1;
}
break;
}
}
if (!continflag) {
fprintf(stderr, "tar: directory checksum error\n");
if(firstdir) {
/* check to see if byte-swapped; if so print message,
* then fall through to normal message, in case of
* scripts that know about the error message. */
swab(cbuf, dblock.dbuf.chksum, sizeof(cbuf));
sscanf(dblock.dbuf.chksum, "%o", &chksum);
if(chksum == i)
fprintf(stderr, "tar: this appears to be a byte-swapped archive\n");
else if(chksum == signedsum)
fprintf(stderr, "tar: this appears to be a byte-swapped"
" archive, with signed checksums\n");
}
done(2);
} else {
fflush(stdout);
if (!continuing) {
fprintf(stderr,
"tar: Directory checksum error. Continuing.");
fflush(stderr);
continuing = YES;
} else {
fprintf(stderr, ".");
fflush(stderr);
}
}
firstdir = 0;
}
{
register char *cp;
register char *lcp;
if ( isposix ) {
cp = getname(&dblock.pdbuf);
lcp = getlink(&dblock.pdbuf);
}
else {
cp = &dblock.dbuf.name[0];
lcp = &dblock.dbuf.linkname[0];
}
if (Rflag > 2) {
/* strip directory from filename:
first, find end of directory specification
if found, change directory to ./
*/
char *op;
char *np;
np = op = cp;
while (*op) {
if (*op == '/')
np = op + 1;
op++;
}
if (np != cp) {
op = cp;
*op++ = '.';
*op++ = '/';
strcpy(op,np);
}
np = op = lcp;
while (*op) {
if (*op == '/')
np = op + 1;
op++;
}
if (np != lcp) {
op = lcp;
*op++ = '.';
*op++ = '/';
strcpy(op,np);
}
}
else for ( ;; ) {
if (cp[0] == '.' && cp[1] == '/') {
if (cp[2] == '\0')
break;
cp += 2;
} else if (Rflag && cp[0] == '/') {
if (cp[1] == '\0')
break;
cp++;
} else
break;
}
dblockname = cp;
dblocksymname = lcp;
/* Rflag applies even if archive doesn't have full pathnames.
* this will be a nop for hardlinks (because of the way tar works
* they will be relative also), but *DOES* have an effect for
* symlinks. Since this requires doubling R, and that's a new
* feature, I think this is worth keeping, even on relative
* archives. */
if(Rflag) /* if R given, need to make links relative also */
while(lcp[0] == '/' && lcp[1])
lcp++;
dblocklnname = lcp;
if(Rflag>1) /* symlinks also? */
dblocksymname = lcp;
}
if (tfile != NULL)
fprintf(tfile, "%s %s\n", dblockname, dblock.dbuf.mtime);
return YES;
}
void
passtape(void)
{
off64_t blocks;
char buf[TBLOCK];
switch ( dblock.pdbuf.typeflag ) { /* POSIX */
case '1': case '2': case '3': case '4': case '5': case '6':
return;
}
if (stbuf.st_size < 0)
blocks = -stbuf.st_size*BIGBLOCK;
else
blocks = stbuf.st_size;
blocks += TBLOCK-1;
blocks /= TBLOCK;
while (blocks-- > 0) {
int cnt;
if ((cnt = readtape(buf)) < 0) {
fflush(stdout);
fprintf(stderr, "tar: tape read error while skipping data\n");
if (continflag)
break;
done(2);
}
if (cnt == 0)
premature_eof();
}
}
void
putfile(char *longname, char *shortname, char *parent)
{
int infile = 0;
off_t leftover;
int bigflag=0;
off64_t blocks;
char buf[TBLOCK];
register char *cp;
dirent64_t *dp;
DIR *dirp;
int i;
int j;
char newparent[NAMSPACE+PREFIXSPACE+64]; /* +PREFIXSPACE POSIX */
extern int errno;
int special;
unsigned int dirents; /* number of entries in directory */
char **dirnms;
struct statvfs vfs;
extern int atstrcmp();
#ifdef TRUSTEDIRIX
mac_t flabel; /* label of the file */
char *flabelstr; /* string representation of label */
acl_t acl; /* acl of the file */
char *aclstr = NULL; /* string representation of acl */
#endif /* TRUSTEDIRIX */
/* strip off trailing slashes */
for (cp = &longname[strlen(longname)]; *--cp == '/'; *cp = '\0')
if (cp == longname)
break;
/* strip leading ./ or extra leading slashes */
for (;;)
if (longname[0] == '.' && longname[1] == '/') {
longname += 2;
while (*longname == '/')
longname++;
} else if (longname[0] == '/' && longname[1] == '/')
longname++;
else
break;
if ( Pflag && strlen(longname) > maxnamlen ) {
fprintf(stderr,
"tar(1): name too long (%d: %d max), not archiving: %s\n",
strlen(longname),maxnamlen,longname);
return;
}
#ifdef S_IFLNK
if (!Lflag)
i = lstat64(shortname, &stbuf);
else
#endif /* S_IFLNK */
i = stat64(shortname, &stbuf);
if (i < 0) {
switch (errno) {
case EACCES:
fprintf(stderr, "tar: %s: cannot access file\n",
longname);
break;
case ENOENT:
fprintf(stderr, "tar: %s: no such file or directory\n",
longname);
break;
default:
fprintf(stderr, "tar: %s: cannot stat file\n",
longname);
break;
}
fflush(stderr);
return;
}
if(Eflag && !S_ISLNK(stbuf.st_mode) && statvfs(shortname, &vfs)==0 &&
!(vfs.f_flag&ST_LOCAL)) {
if(vflag > 1)
fprintf(stderr, "%s: skipped; not local\n", longname);
return;
}
/*
* Added for xfs
*/
if ( stbuf.st_size > LONG_MAX && !Kflag ){
fprintf(stderr,"-K option not specified. Skipping file -> %s\n",
longname);
fflush(stderr);
return;
}
if ( stbuf.st_size > LONG_MAX && Kflag ){
fprintf(stderr,"Warning: Inclusion of file -> %s will create a non-portable archive\n",longname);
fflush(stderr);
}
if (tfile != NULL && checkupdate(longname) == 0)
return;
if (checkw('r', longname) == 0)
return;
if (Fflag && checkf(shortname, stbuf.st_mode, Fflag) == 0)
return;
#ifdef TRUSTEDIRIX
if (Mflag > 0) {
FILE *fp; /* descriptor for phantom file */
struct stat64 sbuf; /* to check on stat file */
/*
* Skip directories named "attribute" owned by root.
*/
if ( (strcmp(shortname, ATTRFILE) == 0) &&
S_ISDIR(stbuf.st_mode) && (stbuf.st_uid == 0) )
return;
/*
* In truth, getlabel should never fail unless the preceeding
* stat() failed.
*/
if ((flabel = mac_get_file(shortname)) == NULL) {
fprintf(stderr, "tar: %s: cannot get file label\n", longname);
fflush(stderr);
return;
}
if ((flabelstr = mac_to_text(flabel, (size_t *) NULL)) == NULL) {
fprintf(stderr, "tar: %s: cannot get label name\n", longname);
fflush(stderr);
mac_free(flabel);
return;
}
mac_free(flabel);
aclstr = NULL;
if ((acl = acl_get_file(shortname, ACL_TYPE_ACCESS)) != (acl_t) NULL) {
if (acl->acl_cnt > NACLBASE) {
if ((aclstr = acl_to_text(acl, (size_t *) NULL)) == (char *) NULL) {
fprintf(stderr, "tar: %s: cannot convert ACL to text\n", longname);
fflush(stderr);
acl_free(acl);
mac_free(flabelstr);
return;
}
}
acl_free(acl);
}
if (stat64(tirix_file, &sbuf) >= 0) {
fprintf(stderr,
"tar: %s: label tmp file collision\n", tirix_file);
fflush(stderr);
mac_free(flabelstr);
if (aclstr != NULL) acl_free(aclstr);
return;
}
if ((fp = fopen(tirix_file, "w")) == NULL) {
fprintf(stderr,
"tar: %s: cannot create label tmp file\n", tirix_file);
fflush(stderr);
mac_free(flabelstr);
if (aclstr != NULL) acl_free(aclstr);
return;
}
fchmod(fp, 0600);
fprintf(fp, "mac=%s\n", flabelstr);
mac_free(flabelstr);
if (aclstr != NULL) {
fprintf(fp, "acl=%s\n", aclstr);
acl_free(aclstr);
}
fclose(fp);
/*
* Get clever here.
* Turn off Mflag, then call putfile recursivly to
* dump this on the tape. Save stbuf as this function is not
* (Yetch!) reentrant.
*/
sbuf = stbuf;
Mflag = 0;
putfile(tirix_file, tirix_file, "/");
unlink(tirix_file);
stbuf = sbuf;
Mflag = 1;
}
#endif /* TRUSTEDIRIX */
switch ((int)(stbuf.st_mode & S_IFMT)) {
case S_IFCHR:
special = FCHR;
break;
case S_IFBLK:
special = FBLK;
break;
#ifdef S_IFIFO
case S_IFIFO:
special = FIFO;
break;
#endif /* S_IFIFO */
default:
special = 0;
}
if ( Pflag )
bzero((char *)&dblock, TBLOCK);
switch ((int)(stbuf.st_mode & S_IFMT)) {
case S_IFDIR:
for (i = 0, cp = buf; *cp++ = longname[i++];)
continue;
/* add a trailing '/' if it doesn't already have one */
if (cp[-2] != '/') {
cp[-1] = '/';
cp[0] = '\0';
} else
--cp;
if (!dflag) {
if ((cp - buf) > maxnamlen) {
fprintf(stderr,
"tar(2): %s: file name too long (%d), %d max\n",
buf,(cp - buf),maxnamlen);
fflush(stderr);
return;
}
stbuf.st_size = 0;
tomodes(&stbuf);
if ( Pflag ) {
if ( putname(&dblock.pdbuf, buf) ) {
fprintf(stderr,
"tar(3): %s: file name too long (%d), %d max\n",
buf,strlen(buf),maxnamlen);
fflush(stderr);
return;
}
dblock.pdbuf.typeflag = '5';
sprintf(dblock.dbuf.chksum, "%06o", checksum());
} else {
strncpy(dblock.dbuf.name, buf, sizeof(dblock.dbuf.name));
sprintf(dblock.dbuf.chksum, "%6o", checksum());
}
writetape((char *)&dblock);
}
sprintf(newparent, "%s/%s", parent, shortname);
if (chdir(shortname) < 0) {
fprintf(stderr,"%s: %s\n",shortname,strerror(errno));
return;
}
if ((dirp = opendir(".")) == NULL) {
fprintf(stderr, "tar: %s: directory read error\n",
longname);
fflush(stderr);
if (chdir(parent) < 0) {
fprintf(stderr, "cannot change back?: ");
fprintf(stderr,"%s: %s\n",parent,
strerror(errno));
fflush(stderr);
}
return;
}
dirents = 0;
while ((dp = readdir64(dirp)) != NULL) {
if (dp->d_ino == 0)
continue;
if (!strcmp(".", dp->d_name) ||
!strcmp("..", dp->d_name))
continue;
dirents++;
}
if (!dirents)
goto cleanup;
closedir(dirp);
dirents += DFUDGE;
dirnms = (char **) calloc(dirents, sizeof (char *));
if (dirnms == NULL) {
fprintf(stderr, "tar: out of memory\n");
fflush(stderr);
goto cleanup;
}
if ((dirp = opendir(".")) == NULL) {
fprintf(stderr, "tar: %s: directory read error\n",
longname);
fflush(stderr);
return;
}
j = 0;
while ((dp = readdir64(dirp)) != NULL && !term) {
if (dp->d_ino == 0)
continue;
if (!strcmp(".", dp->d_name) ||
!strcmp("..", dp->d_name))
continue;
if (j >= dirents) {
fprintf(stderr,"tar: directory changed size\n");
fflush(stderr);
break;
}
dirnms[j] = malloc(strlen(dp->d_name)+1);
if (dirnms[j] == NULL) {
fprintf(stderr, "tar: out of memory\n");
fflush(stderr);
goto cleanup;
}
strcpy(dirnms[j++], dp->d_name);
}
if (j != dirents-DFUDGE) {
fprintf(stderr,"tar: directory changed size\n");
fflush(stderr);
}
dirents = j;
qsort((char *) dirnms, dirents, sizeof (*dirnms), atstrcmp);
for (j=0; j<dirents; j++) {
strcpy(cp, dirnms[j]);
putfile(buf, cp, newparent);
free(dirnms[j]);
}
free(dirnms);
cleanup:
closedir(dirp);
if (chdir(parent) < 0) {
fprintf(stderr, "cannot change back?: ");
fprintf(stderr,"%s: %s\n",parent,strerror(errno));
fflush(stderr);
}
break;
#ifdef S_IFLNK
case S_IFLNK:
tomodes(&stbuf);
if ( Pflag ) {
if ( putname(&dblock.pdbuf, longname) ) {
fprintf(stderr,
"tar(4): %s total path too long (%d, %d max), or filename > %d\n",
longname,strlen(longname),maxnamlen,NAMSIZ);
fflush(stderr);
return;
}
} else {
if(strlen(longname) > NAMSIZ) {
fprintf(stderr,
"tar(5): %s: file name too long (%d), %d max\n",
longname,strlen(longname),NAMSIZ);
fflush(stderr);
return;
}
strcpy(dblock.dbuf.name, longname);
}
if (stbuf.st_size > NAMSIZ) { /* st_size doesn't count NULL */
fprintf(stderr,
"tar(6): %s: destination filename of symbolic link too long (%d), %d max\n",
longname,(int)stbuf.st_size,NAMSIZ);
fflush(stderr);
return;
}
i = readlink(shortname, dblock.dbuf.linkname, NAMSIZ);
if (i < 0) {
fprintf(stderr,"%s: %s\n",longname,strerror(errno));
return;
}
if ( Pflag ) {
dblock.pdbuf.linkname[i] = '\0';
dblock.pdbuf.typeflag = '2';
} else {
dblock.dbuf.linkname[i] = '\0';
dblock.dbuf.linkflag = '2';
}
if (vflag) {
fprintf(stderr, "a %s ", longname);
fprintf(stderr, "symbolic link to %s\n",
dblock.dbuf.linkname);
fflush(stderr);
}
if ( Pflag ) {
sprintf(dblock.pdbuf.size, "%011o", 0);
sprintf(dblock.pdbuf.chksum, "%06o", checksum());
} else {
sprintf(dblock.dbuf.size, "%11lo", 0);
sprintf(dblock.dbuf.chksum, "%6o", checksum());
}
writetape((char *)&dblock);
break;
#endif /* S_IFLNK */
case S_IFCHR:
case S_IFBLK:
#ifdef S_IFIFO
case S_IFIFO:
#endif /* S_IFIFO */
if (Dflag) {
fprintf(stderr,
"tar: %s is not a regular file. Not dumped\n",
longname);
fflush(stderr);
break;
}
stbuf.st_size = 0;
/* Fall Through */
case S_IFREG:
if (!special && (infile = open(shortname, 0)) < 0) {
fprintf(stderr, "tar: %s: cannot open file\n",
longname);
fflush(stderr);
return;
}
tomodes(&stbuf);
if ( Pflag ) {
if ( !special )
dblock.pdbuf.typeflag = '0';
if ( putname(&dblock.pdbuf, longname) ) {
fprintf(stderr,
"tar(7): %s: total path too long (%d, %d max), or filename > %d\n",
longname,strlen(longname),maxnamlen,NAMSIZ);
fflush(stderr);
if (!special)
close(infile);
return;
}
} else {
if ( strlen(longname) > NAMSIZ ) {
fprintf(stderr,
"tar(8): %s: file name too long (%d), %d max\n",
longname,strlen(longname),NAMSIZ);
fflush(stderr);
if (!special)
close(infile);
return;
}
strcpy(dblock.dbuf.name, longname);
}
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 ( Pflag ) {
if ( strlen(lp->pathname) > NAMSIZ ) {
fprintf(stderr,
"tar(8): %s: link name (%s) too long (%d), %d max\n",
longname, lp->pathname,
strlen(lp->pathname),
NAMSIZ);
fflush(stderr);
if (!special)
close(infile);
return;
}
strcpy(dblock.pdbuf.linkname,
lp->pathname);
dblock.pdbuf.typeflag = '1';
sprintf(dblock.pdbuf.size,
"%011o", 0);
sprintf(dblock.pdbuf.chksum,
"%06o", checksum());
} else {
strcpy(dblock.dbuf.linkname,
lp->pathname);
dblock.dbuf.linkflag = '1';
sprintf(dblock.dbuf.chksum,
"%6o", checksum());
}
writetape( (char *) &dblock);
if (vflag) {
fprintf(stderr, "a %s ", longname);
fprintf(stderr, "link to %s\n",
lp->pathname);
fflush(stderr);
}
lp->count--;
if (!special)
close(infile);
return;
}
lp = (struct linkbuf *)
malloc((unsigned)
(strlen(longname) + sizeof *lp));
if (lp == NULL) {
if (freemem) {
fprintf(stderr,
"tar: out of memory, link information lost\n");
fflush(stderr);
freemem = 0;
}
} else {
lp->nextp = ihead;
ihead = lp;
lp->inum = stbuf.st_ino;
lp->devnum = stbuf.st_dev;
lp->count = (short)stbuf.st_nlink - 1;
strcpy(lp->pathname, longname);
}
}
/* size was set to 0 for specials */
blocks = (stbuf.st_size + (TBLOCK-1)) / TBLOCK;
if (vflag) {
fprintf(stderr, "a %s ", longname);
switch (special) {
case S_IFCHR:
case S_IFBLK:
fprintf(stderr,"%s special (%d/%d)\n",
special == S_IFCHR ? "char" : "block",
major((int) stbuf.st_rdev),
minor((int) stbuf.st_rdev));
break;
case S_IFIFO:
fprintf(stderr,"pipe\n");
break;
default:
fprintf(stderr, "%lld block%s\n",
blocks,blocks!=1?"s":"");
}
fflush(stderr);
}
/* Added for xfs support */
if ( stbuf.st_size >= BIGBLOCK ){
sprintf(dblock.pdbuf.size, "%011o ", (long)(-stbuf.st_size/BIGBLOCK));
sprintf(dblock.pdbuf.mtime, "%011o ", stbuf.st_mtime);
leftover = stbuf.st_size%BIGBLOCK;
blocks = ((stbuf.st_size - leftover) + (TBLOCK-1)) / TBLOCK;
bigflag++;
}
if ( Pflag )
sprintf(dblock.pdbuf.chksum, "%06o", checksum());
else
sprintf(dblock.dbuf.chksum, "%6o", checksum());
writetape((char *)&dblock);
/*
* special files are 0 bytes long
*/
if (!special) {
int err;
struct utimbuf utimbuf;
i=1;
while (i > 0 && blocks > 0) {
i = read(infile, buf, TBLOCK);
writetape(buf);
blocks--;
}
err = errno;
if (blocks != 0 || i < 0 || (!bigflag && i == 0)) {
if (i < 0)
fprintf(stderr, "tar: error reading %s: %s\n",
longname, strerror(err));
else
fprintf(stderr,
"tar: %s: file changed size\n",
longname);
fflush(stderr);
while (--blocks >= 0)
putempty();
}
if ( bigflag-- ){
sprintf(dblock.pdbuf.size, "%011o ", (int) leftover);
sprintf(dblock.pdbuf.mtime, "%011o ", stbuf.st_mtime);
sprintf(dblock.pdbuf.chksum,"%06o", checksum());
writetape((char *)&dblock);
blocks = (leftover + (TBLOCK-1))/TBLOCK;
while ((i = read(infile, buf, TBLOCK)) > 0
&& blocks > 0) {
writetape(buf);
blocks--;
}
err = errno;
if (blocks != 0 || i != 0) {
if (i < 0)
fprintf(stderr, "tar: error reading %s: %s\n",
longname, strerror(err));
else
fprintf(stderr,
"tar: %s: file changed size\n",
longname);
fflush(stderr);
while (--blocks >= 0)
putempty();
}
}
close(infile);
/* assumes order of members in stat struct */
if (aflag) {
utimbuf.actime = stbuf.st_atime;
utimbuf.modtime = stbuf.st_mtime;
utime(shortname, &utimbuf);
}
}
break;
default:
fprintf(stderr, "tar: %s is not a file. Not dumped\n",
longname);
fflush(stderr);
break;
}
}
/* unlink a file, but first test to see if it is a directory if
* we are the superuser. Otherwise mess up dir trees...
* Don't report errors, unless debug set; subsequent extracts fail
* with the 'right' error message.
*/
void
cond_unlink(char *name)
{
if(imroot) {
struct stat64 dst;
if(lstat64(name, &dst))
return; /* doesn't exist */
if(S_ISDIR(dst.st_mode)) {
if(debug)
fprintf(stderr,
"request to unlink dir %s as superuser ignored\n",
name);
return;
}
}
if(unlink(name) && debug)
fprintf(stderr, "Can't unlink %s: %s\n", name, strerror(errno));
}
/* print error message on tape i/o error; if exval != 0, exit.
* check to see if error caused by no tape present, to give a
* more helpful error to the user.
*/
void
terror(int isread, int exval)
{
struct mtget mtg;
int saverrno = errno;
fprintf(stderr,"tar: tape %s error: ", isread?"read":"write");
if(isatape && ioctl(mt, MTIOCGET, &mtg) == 0 && !(mtg.mt_dposn&MT_ONL))
fprintf(stderr,"no tape in drive\n");
else if(saverrno == EROFS) /* so we don't get readonly filesystem */
fprintf(stderr,"write protected tape\n");
else
fprintf(stderr,"%s\n", strerror(saverrno));
if(exval)
done(exval);
}
void
doxtract(char *argv[])
{
off64_t blocks, bytes, tempbytes, tempblocks;
int Bigflag=0;
char buf[TBLOCK];
int ifile;
int ofile;
char cmpfile[200 + NAMSPACE];
char *fcmpfile;
char tmpfile[200 + NAMSPACE];
int same;
mode_t special;
int typeflag, isposix = 0;
int once = 1;
#ifdef TRUSTEDIRIX
int attrset = 0;
#endif /* TRUSTEDIRIX */
/* reading stdin for file list valid only during archiving */
if (*argv && (!strcmp(*argv,"-")) && !minusok) {
fprintf(stderr,
"tar: '-' (read filelist from stdin) valid only with r,u,c:\n");
usage();
}
if (lnflag) {
strcpy(cmpfile, linkdir);
fcmpfile = &cmpfile[strlen(cmpfile)];
strcpy(fcmpfile, "/");
fcmpfile++;
}
while (getdir()) {
int res;
if ((! *dblockname) && (Rflag > 2)) {
/* skip directories if RRR */
passtape();
continue;
}
#ifdef TRUSTEDIRIX
if (Mflag > 0) {
if ( (strncmp(dblockname, TIRIX_PREFIX,
strlen(TIRIX_PREFIX)) == 0) ||
(strncmp(dblockname, &(TIRIX_PREFIX[1]),
strlen(TIRIX_PREFIX) - 1) == 0) ) {
attr_from_tape();
continue;
}
}
#endif /* TRUSTEDIRIX */
if (*argv && !gotit(argv, dblockname)) {
if(vflag>1)
fprintf(stderr, "P not in list, skip %s\n", dblockname);
passtape();
continue;
}
if (checkw('x', dblockname) == 0) {
passtape();
continue;
}
if (Pflag && once) {
if (!strncmp(dblock.pdbuf.magic, "ustar", 5)) {
if (geteuid() == (uid_t) 0) {
pflag = 1;
} else {
Oumask = umask(0); /* get file creation mask */
(void) umask(Oumask);
}
once = 0;
} else {
if (geteuid() == (uid_t) 0) {
pflag = 1;
}
if (!pflag) {
Oumask = umask(0); /* get file creation mask */
(void) umask(Oumask);
}
once = 0;
}
}
if (Nflag) {
struct stat s;
if(!stat(dblockname, &s)) {
if(vflag)
fprintf(stderr, "N skip %s\n", dblockname);
passtape();
continue;
}
}
if (Fflag) {
char *s;
if ((s = strrchr(dblockname, '/')) == 0)
s = dblockname;
else
s++;
if (checkf(s, stbuf.st_mode, Fflag) == 0) {
passtape();
continue;
}
}
if ((res = checkdir(dblockname)) == -1) {
fprintf(stderr,"tar extract: failed mkdir of %s\n",
dblockname);
done(2);
} else if (res) /* it's a dir and made; next hdr */
continue;
if ( strcmp(dblock.pdbuf.magic, "ustar") == 0 )
isposix = 1;
if ( isposix )
typeflag = dblock.pdbuf.typeflag;
else
typeflag = dblock.dbuf.linkflag;
if (typeflag == '2') {
#ifndef S_IFLNK
goto linkit;
#else /* S_IFLNK */
cond_unlink(dblockname);
if (symlink(dblocksymname, dblockname)<0) {
fprintf(stderr, "tar: %s: symbolic link failed\n",
dblockname);
fflush(stderr);
continue;
}
if (vflag) {
#ifdef TRUSTEDIRIX
if (Mflag > 0)
fprintf(stderr,
"x %s symbolic link to %s\t[%s]\n",
dblockname, dblocksymname,
g_lname);
else
#endif /* TRUSTEDIRIX */
fprintf(stderr, "x %s symbolic link to %s\n",
dblockname, dblocksymname);
fflush(stderr);
}
/* see below for chown explanation */
if (Sflag || (!oflag && imroot))
lchown(dblockname, stbuf.st_uid, stbuf.st_gid);
#ifdef TRUSTEDIRIX
if (Mflag > 0) {
attrset = 0;
if (g_lp != NULL) {
if (mac_set_file(dblockname, g_lp) == -1)
fprintf(stderr,
"tar: cannot set %s to label \"%s\"\n",
dblockname, g_lname);
/*
* Having used the label once it
* must be discarded so as not to be
* incorrectly used again.
*/
attrset = 1;
clear_glabel();
}
if (g_aclp != NULL) {
if (acl_set_file(dblockname,
ACL_TYPE_ACCESS, g_aclp) == -1) {
fprintf(stderr,
"tar: cannot set %s to acl %s\n",
dblockname, g_aclstr);
}
attrset = 1;
clear_gacl();
}
if (attrset == 0) {
fprintf(stderr, "tar: No label for %s\n", dblockname);
}
}
#endif /* TRUSTEDIRIX */
continue;
#endif /* S_IFLNK */
} else if (typeflag == '1') { /* XXX POSIX */
#ifndef S_IFLNK
linkit:
#endif /* S_IFLNK */
cond_unlink(dblockname);
if (link(dblocklnname, dblockname) < 0) {
fprintf(stderr, "tar: %s: cannot link to %s\n",
dblocklnname, dblockname);
fflush(stderr);
continue;
}
if (vflag) {
fprintf(stderr, "%s %s to %s\n",
dblockname,
typeflag == '2'
? "symlink changed to hard link"
: "linked",
dblocklnname);
fflush(stderr);
}
continue;
}
ifile = -1;
same = YES;
if (lnflag) {
static char tmpf[] = "TarXXXXXX";
strcpy(fcmpfile, dblockname);
if ((ifile = open(cmpfile, 0)) >= 0) {
tmpfile[0] = '\0';
strncat(tmpfile, dblockname,
dirpart(dblockname));
strcat(tmpfile, tmpf);
mktemp(tmpfile);
}
}
if (ifile >= 0) {
ofile = creat(tmpfile, (int) stbuf.st_mode & 0xfff);
} else {
if (Uflag)
cond_unlink(dblockname);
switch ((int)(stbuf.st_mode & S_IFMT)) {
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
special = stbuf.st_mode & S_IFMT;
break;
default:
special = 0;
}
if (special) {
ofile = mknod(dblockname,
(int) (stbuf.st_mode & S_PERMMASK) | special,
stbuf.st_rdev);
} else if (dblockname[strlen(dblockname)] != '/')
ofile = creat(dblockname,
(int) stbuf.st_mode & S_PERMMASK);
}
if (ofile < 0) {
fprintf(stderr,"tar: %s - cannot %s -- %s\n",
dblockname, special ? "mknod" : "create",
strerror(errno));
fflush(stderr);
passtape();
continue;
}
/*
* Added for xfs
*/
if (stbuf.st_size < 0){
tempblocks = blocks = ((tempbytes = bytes = -stbuf.st_size*BIGBLOCK) + TBLOCK-1)/TBLOCK;
Bigflag = 1;
}
else
blocks = ((bytes = stbuf.st_size) + TBLOCK-1)/TBLOCK;
#ifndef IGNORE_SCCSID
if (ifile >= 0) {
struct stat64 scstbuf;
if (fstat64(ifile, &scstbuf) < 0 || bytes != scstbuf.st_size)
same = NO;
}
#endif /* IGNORE_SCCSID */
if (vflag && !Bigflag) {
switch ((int)special) {
case 0:
fprintf(stderr,
"x %s, %lld bytes, %lld block%s",
dblockname, bytes, blocks, blocks!=1?"s":"");
break;
case S_IFCHR:
fprintf(stderr, "x %s, char special (%d/%d)",
dblockname,
major(stbuf.st_rdev),
minor(stbuf.st_rdev));
break;
case S_IFBLK:
fprintf(stderr, "x %s, char special (%d/%d)",
dblockname,
major(stbuf.st_rdev),
minor(stbuf.st_rdev));
break;
#ifdef S_IFIFO
case S_IFIFO:
fprintf(stderr, "x %s, pipe", dblockname);
break;
#endif /* S_IFIFO */
default:
fprintf(stderr,
"unrecognized case %d: doxtract\n",
special);
}
#ifdef TRUSTEDIRIX
if (Mflag > 0)
fprintf(stderr, "\t[%s]", g_lname);
#endif /* TRUSTEDIRIX */
fflush(stderr);
}
while (blocks-- > 0) {
int nw;
if ((nw = readtape(buf)) < 0) {
fflush(stdout);
if (vflag)
fprintf(stderr, "\n");
if (!continflag)
terror(1, 2);
fprintf(stderr, "tar: **Omitting data block**\n");
fprintf(stderr, "tar: Discard file: %s\n\n",
dblockname);
fflush(stderr);
continue;
}
if (nw == 0) {
if (vflag)
fprintf(stderr, "\n");
premature_eof();
}
nw = MIN(bytes, TBLOCK);
if (write(ofile, buf, nw) < nw) {
if (vflag)
fprintf(stderr, "\n");
fprintf(stderr, "tar: %s: HELP - extract write error\n",
dblockname);
done(2);
}
#ifndef IGNORE_SCCSID
if (ifile >= 0 && same)
same = cmprd(ifile, buf, (long)nw);
#endif /* IGNORE_SCCSID */
bytes -= TBLOCK;
}
/*
* Added for xfs
*/
if (Bigflag){
Bigflag = 0;
getdir();
blocks = ((bytes = stbuf.st_size) + TBLOCK-1)/TBLOCK;
tempbytes += bytes;
tempblocks += blocks;
while (blocks-- > 0) {
int nw;
if ((nw = readtape(buf)) < 0) {
fflush(stdout);
if (vflag)
fprintf(stderr, "\n");
if (!continflag)
terror(1, 2);
fprintf(stderr, "tar: **Omitting data block**\n");
fprintf(stderr, "tar: Discard file: %s\n\n",
dblockname);
fflush(stderr);
continue;
}
if (nw == 0) {
if (vflag)
fprintf(stderr, "\n");
premature_eof();
}
nw = MIN(bytes, TBLOCK);
if (write(ofile, buf, nw) < nw) {
if (vflag)
fprintf(stderr, "\n");
fprintf(stderr, "tar: %s: HELP - extract write error\n",
dblockname);
done(2);
}
#ifndef IGNORE_SCCSID
if (ifile >= 0 && same)
same = cmprd(ifile, buf, (long)nw);
#endif /* IGNORE_SCCSID */
bytes -= TBLOCK;
}
if (vflag) {
fprintf(stderr,
"x %s, %lld bytes, %lld block%s",
dblockname, tempbytes, tempblocks, tempblocks!=1?"s":"");
}
}
if (!special)
close(ofile);
if (ifile >= 0) {
#ifdef IGNORE_SCCSID
same = cmpsccsid(tmpfile, cmpfile);
#endif /* IGNORE_SCCSID */
close(ifile);
cond_unlink(dblockname);
if (vflag) {
fprintf(stderr, " (%s)\n", same ? "same" : "new");
fflush(stderr);
}
if (link(same ? cmpfile : tmpfile, dblockname) < 0) {
fprintf(stderr, "tar: %s - cannot link\n",
dblockname);
fflush(stderr);
if (same && link(tmpfile, dblockname) < 0) {
fprintf(stderr,
"tar: %s - cannot link\n",
dblockname);
fflush(stderr);
}
}
unlink(tmpfile);
} else if (vflag) {
fprintf(stderr, "\n");
fflush(stderr);
}
if (ifile < 0 || !same) {
if (mflag == 0) {
time_t tv[2];
tv[0] = time((time_t *)0);
tv[1] = stbuf.st_mtime;
utime(dblockname, (struct utimbuf *)tv);
}
if (pflag) /* symlinks don't get down here?? */
chmod(dblockname,(int)stbuf.st_mode&S_PERMMASK);
/* POSIX tar chown behavior:
* Sflag acts exactly like 3.3* and prior--chown
* to tape owner.
* Else, do as SVR4 does:
* - if user specified oflag, no chown regardless.
* - if no oflag and not root, no chown regardless.
* - if no oflag and root:
* + if posix hdr, try stored uname (pdbuf.uname)
* + else use old-style stored uid string (dbuf.uid)
* : these last two cases are same in our tar
* cuz getdir() set {u/g}id as per header type
*/
if (Sflag || (!oflag && imroot))
chown(dblockname, stbuf.st_uid, stbuf.st_gid);
}
#ifdef TRUSTEDIRIX
if (Mflag > 0) {
attrset = 0;
if (g_lp != NULL) {
if (mac_set_file(dblockname, g_lp) == -1)
fprintf(stderr,
"tar: cannot set %s to label \"%s\"\n",
dblockname, g_lname);
/*
* Having used the label once it
* must be discarded so as not to be
* incorrectly used again.
*/
attrset = 1;
clear_glabel();
}
if (g_aclp != NULL) {
if (acl_set_file(dblockname, ACL_TYPE_ACCESS,
g_aclp) == -1) {
fprintf(stderr,
"tar: cannot set %s to acl %s\n",
dblockname, g_aclstr);
}
attrset = 1;
clear_gacl();
}
if (attrset == 0) {
fprintf(stderr, "tar: No label for %s\n", dblockname);
}
}
#endif /* TRUSTEDIRIX */
}
endread();
}
/* having this a separate routine makes docompare cleaner */
void
cmpname(char type, int do_nl)
{
printf("%c ", type);
if (vflag)
longt(&stbuf,dblockname);
printf(do_nl?"%s\n":"%s", dblockname);
}
void
docompare(char *argv[])
{
off64_t blocks, bytes, tempbytes;
mode_t smode;
int Bigflag=0,doagain=0;
char buf[TBLOCK];
char typinfo;
int ifile;
int same;
int typeflag, isposix = 0;
struct stat64 st;
/* reading stdin for file list valid only during archiving */
if (*argv && (!strcmp(*argv,"-")) && !minusok) {
fprintf(stderr,
"tar: '-' (read filelist from stdin) valid only with r,u,c:\n");
usage();
}
while (getdir()) {
if (*argv && !gotit(argv, dblockname)) {
passtape();
continue;
}
if ( strcmp(dblock.pdbuf.magic, "ustar") == 0 )
isposix = 1;
if ( isposix )
typeflag = dblock.pdbuf.typeflag;
else
typeflag = dblock.dbuf.linkflag;
/* check if it is a hard (1) or soft(2) link */
if (typeflag == '1' || typeflag == '2') {
cmpname(typeflag=='1' ? 'L' : 'S', 0);
printf(" linked to %s\n",
typeflag=='1' ? dblocklnname : dblocksymname);
continue;
}
smode = stbuf.st_mode & S_IFMT;
if(smode && smode != S_IFREG) {
switch ((int)smode) {
case S_IFCHR:
typinfo = 'C';
break;
case S_IFBLK:
typinfo = 'B';
break;
case S_IFIFO:
typinfo = 'P';
break;
case S_IFDIR:
typinfo = 'D';
break;
}
cmpname(typinfo, 1);
continue;
}
/*
IFMT bits not kept for old non-posix extension tar dirs...
The posix tar should use the above switch statement since
stbuf.st_mode for posix tar should mark directories
(in getdir routine).
*/
if(dblockname[strlen(dblockname)-1] == '/') {
cmpname('D', 1);
continue;
}
if ((ifile = open(dblockname, 0)) < 0) {
cmpname((stat64(dblockname, &st) >= 0) ? '?' : '>', 1);
passtape();
continue;
}
same = YES;
/*
* Added for xfs
*/
if (stbuf.st_size < 0){
blocks = ((tempbytes = bytes = -stbuf.st_size*BIGBLOCK) + TBLOCK-1)/TBLOCK;
Bigflag = 1;
}
else
blocks = ((bytes = stbuf.st_size) + TBLOCK-1)/TBLOCK;
if (fstat64(ifile, &st) < 0 || (bytes != st.st_size && !Bigflag))
same = NO;
if (same){
doagain = 1;
while(doagain--){
while (blocks-- > 0) {
int nw;
if ((nw = readtape(buf)) < 0) {
fflush(stdout);
if (!continflag)
terror(1, 2);
fprintf(stderr,
"tar: **Omitting data block**\n");
fprintf(stderr,
"tar: Discard file: %s\n\n",
dblockname);
fflush(stderr);
continue;
}
if (nw == 0)
premature_eof();
nw = MIN(bytes, TBLOCK);
if (ifile >= 0 && same)
same = cmprd(ifile, buf, (long)nw);
bytes -= TBLOCK;
}
if (Bigflag){
Bigflag = 0;
getdir();
if (same){
blocks = ((bytes = stbuf.st_size) + TBLOCK-1)/TBLOCK;
stbuf.st_size += tempbytes;
doagain++;
}
else
passtape();
}
}
}
else
passtape();
close(ifile);
cmpname(same ? '=' : '!' , 1);
}
endread();
}
void
dotable(char *argv[])
{
int typeflag, bigflag = 0;
off64_t tmpsize,tmpsiz2;
/* reading stdin for file list valid only during archiving */
if (*argv && (!strcmp(*argv,"-")) && !minusok) {
fprintf(stderr,
"tar: '-' (read filelist from stdin) valid only with r,u,c:\n");
usage();
}
while (getdir()) {
#ifdef TRUSTEDIRIX
if (Mflag > 0) {
if (strncmp(dblockname, TIRIX_PREFIX,
strlen(TIRIX_PREFIX)) == 0) {
attr_from_tape();
continue;
}
if (vflag)
printf("[%s]\t", g_lname);
}
#endif /* TRUSTEDIRIX */
if (*argv && !gotit(argv, dblockname)) {
passtape();
continue;
}
if (Nflag) {
struct stat s;
if(!stat(dblockname, &s)) {
passtape();
continue;
}
}
/*
* Added for xfs
*/
if (stbuf.st_size < 0 ){
tmpsize= -stbuf.st_size*BIGBLOCK;
passtape();
getdir();
tmpsiz2 = stbuf.st_size;
stbuf.st_size += tmpsize;
bigflag++;
}
if (vflag)
longt(&stbuf,dblockname);
printf("%s", dblockname);
if ( strcmp(dblock.pdbuf.magic, "ustar") == 0 )
typeflag = dblock.pdbuf.typeflag;
else
typeflag = dblock.dbuf.linkflag;
switch (typeflag) {
case '1':
printf(" linked to %s", dblocklnname);
break;
case '2':
printf(" symbolic link to %s", dblocksymname);
}
printf("\n");
if (bigflag){
stbuf.st_size = tmpsiz2;
bigflag=0;
}
passtape();
}
endread();
}
void
putempty(void)
{
static char zerobuf[TBLOCK];
writetape(zerobuf);
}
void
longt(struct stat64 *st, char *name)
{
register char *cp;
char info[32];
pmode(st);
printf(" %3d/%-2d ", st->st_uid, st->st_gid);
switch ((int)(st->st_mode & S_IFMT)) {
case 0:
/* AIX tar puts the IFREG bit in their st_mode, bug #316112 */
case S_IFREG:
if (name[strlen(name)-1] != '/') {
sprintf(info, "%13lld", st->st_size);
break;
}
/* fall through */
case S_IFDIR:
strcpy(info, "dir");
break;
case S_IFCHR:
sprintf(info, "c:%d/%d",
major(st->st_rdev), minor(st->st_rdev));
break;
case S_IFBLK:
sprintf(info, "b:%d/%d",
major(st->st_rdev), minor(st->st_rdev));
break;
case S_IFIFO:
strcpy(info, "pipe");
break;
default:
#ifdef DEBUG
fprintf(stderr,
"debug: unrecognized case: longt(st,name)\n");
fflush(stderr);
#endif /* DEBUG */
strcpy(info, "?");
break;
}
cp = ctime(&st->st_mtime);
printf("%-13s %-12.12s %-4.4s ", info, cp+4, cp+20);
}
#define SUID 04000
#define SGID 02000
#define ROWN 0400
#define WOWN 0200
#define XOWN 0100
#define RGRP 040
#define WGRP 020
#define XGRP 010
#define ROTH 04
#define WOTH 02
#define XOTH 01
#define STXT 01000
int m1[] = { 1, ROWN, 'r', '-' };
int m2[] = { 1, WOWN, 'w', '-' };
int m3[] = { 2, SUID, 's', XOWN, 'x', '-' };
int m4[] = { 1, RGRP, 'r', '-' };
int m5[] = { 1, WGRP, 'w', '-' };
int m6[] = { 2, SGID, 's', XGRP, 'x', '-' };
int m7[] = { 1, ROTH, 'r', '-' };
int m8[] = { 1, WOTH, 'w', '-' };
int m9[] = { 2, STXT, 't', XOTH, 'x', '-' };
int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
void
pmode(struct stat64 *st)
{
register int **mp;
for (mp = &m[0]; mp < &m[9];)
choose(*mp++, st);
}
void
choose(int *pairp, struct stat64 *st)
{
register int n, *ap;
ap = pairp;
n = *ap++;
while (--n>=0 && (st->st_mode&*ap++)==0)
ap++;
printf("%c", *ap);
}
checkdir(char *name)
{
register char *cp;
extern int errno;
if (dblock.pdbuf.typeflag != '5') {
/*
* Quick check for existence of directory.
*/
if ((cp = strrchr(name, '/')) == 0)
return (0);
*cp = '\0';
} else if ((cp = strrchr(name, '/')) == 0) {
if (access(name, 0) < 0) {
if(mkdir(name, 0777) < 0) {
goto mkdir_error;
}
return(1);
}
return(0);
}
if (access(name, 0) >= 0) {
*cp = '/';
return (cp[1] == '\0'); /* return (lastchar == '/') */
}
*cp = '/';
/*
* No luck, try to make all directories in path.
* Assume "/" exists. Otherwise we will test "" which fails on
* System V, breaking tar.
*/
for (cp = name+1; *cp; cp++) {
if (*cp != '/')
continue;
*cp = '\0';
if (access(name, 0) < 0) {
errno = 0;
if (mkdir(name, 0777) < 0) { /* umask controls */
mkdir_error:
fflush(stdout);
fprintf(stderr,"tar:can't mkdir ");
if (errno)
fprintf(stderr,"%s: %s\n",name,
strerror(errno));
else
fprintf(stderr,"%s\n",name);
*cp = '/';
/*
* Nonzero to suppress creation of
* zero-length file preventing
* subsequent mkdir by hand after
* fixing problem.
*/
return -1;
}
/* .../me/ */
if (pflag && cp[1] == '\0')
chmod(name, (int)stbuf.st_mode & S_PERMMASK);
/* see doxtract() for chown explanation */
if (Sflag || (!oflag && imroot))
chown(name, stbuf.st_uid, stbuf.st_gid);
#ifdef TRUSTEDIRIX
if (Mflag > 0 && (g_lp != NULL)) {
if (mac_set_file(name, g_lp) == -1)
fprintf(stderr,
"tar: cannot set %s to label \"%s\"\n",
name, g_lname);
clear_glabel();
}
if (Mflag > 0 && (g_aclp != NULL)) {
if (acl_set_file(dblockname, ACL_TYPE_ACCESS,
g_aclp) == -1) {
fprintf(stderr,
"tar: cannot set %s to acl %s\n",
dblockname, g_aclstr);
}
clear_gacl();
}
#endif /* TRUSTEDIRIX */
}
*cp = '/';
}
if (((dblock.pdbuf.typeflag == '5') || (cp[-1]=='/'))
&& access(name, 0) < 0) {
if (mkdir(name, 0777) < 0) {
goto mkdir_error;
}
return(1);
}
return (cp[-1]=='/'); /* TRUE if only directory (*?/) */
}
void
onintr()
{
signal(SIGINT, SIG_IGN);
term++;
}
void
onquit()
{
signal(SIGQUIT, SIG_IGN);
term++;
}
void
onhup()
{
signal(SIGHUP, SIG_IGN);
term++;
}
void
tomodes(struct stat64 *sp)
{
static int uid_overflow = 0;
register char *cp;
int i;
for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
*cp = '\0';
switch ((int)(sp->st_mode & S_IFMT)) {
case S_IFCHR:
i = FCHR;
if ( Pflag )
dblock.pdbuf.typeflag = '3';
break;
case S_IFBLK:
i = FBLK;
if ( Pflag )
dblock.pdbuf.typeflag = '4';
break;
#ifdef S_IFIFO
case S_IFIFO:
i = FIFO;
if ( Pflag )
dblock.pdbuf.typeflag = '6';
break;
#endif /* S_IFIFO */
default:
i = 0;
break;
}
if (i)
sp->st_size = 0;
if ( Pflag ) {
sprintf(dblock.pdbuf.devmajor, "%06o ",
i ? major(sp->st_rdev) : 0);
sprintf(dblock.pdbuf.devminor, "%06o ",
i ? minor(sp->st_rdev) : 0);
sprintf(dblock.pdbuf.mode,"%06o ",(sp->st_mode&S_PERMMASK) | i);
/* N.B. We don't print a warning for uid overflow (as we do
* in pre-POSIX mode (below) because we're still writing the
* uname, so chances are we'll be okay at extraction time.
*/
sprintf(dblock.pdbuf.uid, "%06o ",
((sp->st_uid > 0777777) ? UID_NOBODY : sp->st_uid));
sprintf(dblock.pdbuf.gid, "%06o ",
((sp->st_gid > 0777777) ? GID_NOBODY : sp->st_gid));
sprintf(dblock.pdbuf.size, "%011o ", (long)sp->st_size);
sprintf(dblock.pdbuf.mtime, "%011o ", sp->st_mtime);
sprintf(dblock.pdbuf.magic, "%s", TMAGIC);
sprintf(dblock.pdbuf.version, "%2s", TVERSION);
sprintf(dblock.pdbuf.uname, "%s", finduname(sp->st_uid));
sprintf(dblock.pdbuf.gname, "%s", findgname(sp->st_gid));
return;
}
sprintf(dblock.dbuf.mode, "%6o ", (sp->st_mode & S_PERMMASK) | i);
sprintf(dblock.dbuf.uid, "%6o ",
((sp->st_uid > 0777777) ? UID_NOBODY : sp->st_uid));
sprintf(dblock.dbuf.gid, "%6o ",
((sp->st_gid > 0777777) ? GID_NOBODY : sp->st_gid));
if (!uid_overflow && (sp->st_uid > 0777777 || sp->st_gid > 0777777))
{
uid_overflow = 1;
fprintf(stderr,
"Warning: file(s) have user id or group id > 262143, "
"writing \"nobody\" instead\n");
}
/* Each sprintf writes the NUL one past the buffer boundary,
* but that should be okay (even for rdev).
*/
sprintf(dblock.dbuf.size, "%11lo ", (unsigned long)sp->st_size);
sprintf(dblock.dbuf.mtime, "%11lo ", sp->st_mtime);
sprintf(dblock.dbuf.rdev, "%11o ", (unsigned int) sp->st_rdev);
}
int
checksum(void)
{
register i; /* still signed? */
register unsigned char *cp;
for (cp = (unsigned char *)dblock.dbuf.chksum;
cp < (unsigned char *)&dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)];
cp++)
*cp = ' ';
i = 0;
for (cp = (unsigned char *)dblock.dummy;
cp < (unsigned char *)&dblock.dummy[TBLOCK]; cp++)
i += *cp;
return (i);
}
/* check with signed chars. POSIX calls for unsigned chars, but
* some systems use signed, because that is the default for their
* chars. Can show up as a problem if filenames have high bit set
*/
int
signedchecksum(void)
{
register i;
register signed char *cp;
for (cp = (signed char *)dblock.dbuf.chksum;
cp < (signed char *)&dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)];
cp++)
*cp = ' ';
i = 0;
for (cp = (signed char *)dblock.dummy;
cp < (signed char *)&dblock.dummy[TBLOCK]; cp++)
i += *cp;
return (i);
}
int
checkw(int c, char *name)
{
if (!wflag)
return (1);
printf("%c ", c);
if (vflag)
longt(&stbuf, name);
printf("%s: ", name);
return (response() == 'y');
}
int
response(void)
{
char c;
int i;
c = getchar();
if (c != '\n')
while ((i=getchar()) != '\n' && i != EOF)
continue;
else
c = 'n';
return (c);
}
int
checkf(char *name, mode_t mode, int howmuch)
{
int l;
if ((mode & S_IFMT) == S_IFDIR)
return (strcmp(name, "SCCS") != 0);
if ((l = strlen(name)) < 3)
return (1);
if (howmuch > 1 && name[l-2] == '.' && name[l-1] == 'o')
return (0);
if (strcmp(name, "core") == 0 ||
strcmp(name, "errs") == 0 ||
(howmuch > 1 && strcmp(name, "a.out") == 0))
return (0);
/* SHOULD CHECK IF IT IS EXECUTABLE */
return (1);
}
int
checkupdate(arg)
char *arg;
{
char name[100];
long mtime;
off64_t seekp;
off64_t lookup();
rewind(tfile);
if ((seekp = lookup(arg)) < 0)
return (1);
fseek64(tfile, seekp, 0);
fscanf(tfile, "%s %lo", name, &mtime);
return (stbuf.st_mtime > mtime);
}
void
done(int n)
{
if (debug)
fprintf(stderr,"exiting from done, n %d\n",n);
fflush(stderr);
fflush(stdin);
#ifdef RMT
rmtclose(mt);
#endif /* RMT */
unlink(tname);
exit(n);
}
gotit(char **list, char *name)
{
for (; *list; ++list)
if (prefix(*list, name))
return YES;
return NO;
}
prefix(char *s1, char *s2)
{
while (*s1)
if (*s1++ != *s2++)
return (0);
if (*s2)
return (*s2 == '/');
return (1);
}
#define N 200
int njab;
off64_t
lookup(char *s)
{
register i;
off64_t a;
for (i=0; s[i]; i++)
if (s[i] == ' ')
break;
a = bsrch(s, i, low, high);
return (a);
}
off64_t
bsrch(char *s, int n, off64_t l, off64_t h)
{
register i, j;
char b[N];
off64_t m, m1;
njab = 0;
loop:
if (l >= h)
return (-1L);
m = l + (h-l)/2 - N/2;
if (m < l)
m = l;
fseek64(tfile, m, 0);
fread(b, 1, N, tfile);
njab++;
for (i=0; i<N; i++) {
if (b[i] == '\n')
break;
m++;
}
if (m >= h)
return (-1L);
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;
}
return (m);
}
cmp(char *b, char *s, int n)
{
register i;
if (b[0] != '\n') {
if (debug)
fprintf(stderr,"exiting from cmp, b[0] = %c\n",b[0]);
exit(2);
}
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);
}
void
endread(void)
{
register int cnt;
if (infrompipe) {
while ((cnt = readtape(NULL)) > 0)
;
if (cnt == -1) {
fflush(stdout);
fprintf(stderr, "tar: pipe read error at end\n");
fflush(stderr);
}
}
#ifndef OLDMAGTAPE
/* try to find end of file. Give up after two tries */
if ((cnt = readtape((char *) NULL)) != 0
&& (cnt < 0 || readtape((char *) NULL) < 0)) {
fflush(stdout);
fprintf(stderr, "tar: tape read error at end\n");
fflush(stderr);
}
#endif /* OLDMAGTAPE */
}
void
premature_eof(void)
{
fflush(stdout);
fprintf(stderr, "tar: tape premature EOF; may need to use -b option\n");
done(2);
}
/* SGI: Variable blocking */
readtape(char *buffer)
{
register int i;
static int haderr;
if (recno >= nblock || first == 0 || buffer == (char *) NULL) {
if ((i = bread(mt, (char *) tbuf,
TBLOCK*(nblock?nblock:NBLOCK), buffer)) < 0) {
fflush(stdout);
terror(1, 0);
fflush(stderr);
if (continflag && !haderr) {
haderr = 1;
return -1;
}
done(3);
}
haderr = 0;
if (i == 0)
return 0;
if (buffer == (char *) NULL)
return i;
if ((i % TBLOCK) != 0) {
fprintf(stderr, "tar: tape blocksize error\n");
done(3);
}
i /= TBLOCK;
if (first == 0) {
if (i != nblock && i != 1) {
fprintf(stderr, "tar: blocksize = %d\n", i);
fflush(stderr);
nblock = i;
}
}
nblock = i;
recno = 0;
}
first = 1;
bcopy((char *)&tbuf[recno++], buffer, TBLOCK);
return (TBLOCK);
}
void
writetape(char *buffer)
{
first = 1;
if (nblock == 0)
nblock = 1;
if (recno >= nblock) {
if (bwrite(mt, (char *) tbuf,
TBLOCK*nblock, buffer) != TBLOCK*nblock)
terror(0, 2);
recno = 0;
}
bcopy(buffer, (char *)&tbuf[recno++], TBLOCK);
if (recno >= nblock) {
if (bwrite(mt, (char *) tbuf,
TBLOCK*nblock, buffer) != TBLOCK*nblock)
terror(0, 2);
recno = 0;
}
}
/* if eot == NULL -> don't change tapes */
myio(int (*fn)(), int fd, char *ptr, int n, char *eot)
{
int i;
int tty;
char what;
static int firsttape = 1;
#ifdef RMT
int isread = (fn == rmtread);
#else
int isread = (fn == read);
#endif /* RMT */
i = (*fn)(fd, ptr, n);
if (i != n && eot != NULL) {
fflush(stdout);
if (debug) {
fprintf(stderr,"myio(%s): wanted %d bytes, got %d\n",
isread ? "read" : "write", n, i);
if (i < 0) {
fprintf(stderr,"\terrno is %d: %s",errno,
strerror(errno));
}
}
fflush(stderr);
if (!isatape)
return i; /* return short count if not a device */
if ( i == -1 && errno != ENOSPC )
{
terror(isread, 0);
if(firsttape) {
/* if failed at start of first tape, or
* in 'middle' of any tape, then quit,
* else prompt for new tape. I.e.,
* always give up unless we just had
* a tape change, with no OK i/o. */
if (debug)
fprintf(stderr,
"exiting myio, firsttape %d\n",
firsttape);
done(2);
}
}
else if ( i > 0 ) {
/* short read/write is OK */
return(i);
}
else if(firsttape==1) /* if failed at start of first tape with
ENOSPC then don't prompt for new tape; return 0 to print
premature EOF for reads; or perror msg for writes. */
return isread ? 0 : -1;
else if(!i) {
/* 9 track, and DAT multivolume; this is what cpio has
* always allowed, and tar and bru now do starting with
* IRIX 4.0, since filemarks are now always written at the
* end of a tape for 9 track, DAT, and any new tape devices.
* Writes from tpsc.c will return 0 on first write after
* EW/EOM is encountered, and therefore reads will
* return 0 when the FM is encountered. We talked about
* adding an mtstat check for EOT or EW, but that seems
* like overkill. May still get -1 and ENOSPC on
* multivol writes due to deferred error reporting on
* SCSI tapes */
if (debug)
fprintf(stderr, "i/o returned 0 bytes, assume multivolume\n");
}
/* else -1 and ENOSPC, which is 'normal' for QIC and 8mm
* tapes when doing multivolume the older SGI way. */
if ((tty = open("/dev/tty", 2)) < 0 && !isatty(tty = 0)) {
fprintf(stderr, "tar: cannot prompt for new tape\n");
done(2);
}
#ifdef RMT
rmtclose(mt);
#else
close(mt);
#endif /* RMT */
for (;;) {
/* !*!*!*! NOTE NOTE NOTE NOTE NOTE NOTE !*!*!*!
* This prompt is known to the GUI backup and restore
* tool. If it is changed or i18n'ed, the GUI tool
* must be changed to match
* (usrenv/sysadm/lib/libbackuprestore/
* is where the source lives as of 7/96)
*/
static char prompt[] =
"\007Change tape and press the Enter key:";
/* Prompt the user to replace the tape. */
fflush(stdout);
write(tty, prompt, sizeof(prompt));
do {
errno = 0;
if (read(tty, &what, 1) != 1) {
if (errno > 0)
fprintf(stderr,
"tar: no new tape: %s\n",
strerror(errno));
else {
fprintf(stderr,
"tar: no new tape\n");
}
fprintf(stderr, "tar: tape %s error\n",
isread ? "read" : "write");
done(2);
}
if (debug > 1) {
fprintf(stderr,"Read '%c'\n", what);
fflush(stderr);
}
} while (what != '\n');
/* Try to open the device with the new tape in it. */
if (!strcmp(usefile,"-"))
mt = dup(isread ? 0 : 1);
else {
#ifdef RMT
if (isread) {
if (work == dorep)
mt = rmtopen(usefile,2);
else
mt = rmtopen(usefile,0);
}
else {
mt = rmtopen(usefile,1);
if (mt < 0)
mt = rmtcreat(usefile,0666);
}
#else
if (isread)
mt = open(usefile,0);
else {
mt = open(usefile,1);
if (mt < 0)
mt = creat(usefile,0666);
}
#endif /* RMT */
}
if (mt < 0) {
fprintf(stderr,"tar: can't %s ",
isread ? "open" : "creat");
fprintf(stderr,"%s: %s\n",usefile,
strerror(errno));
fflush(stderr);
} else {
chkandfixaudio(mt);
firsttape = 0; /* we are at start of a tape */
if((i = myio(fn, mt, ptr, n, eot)) > 0)
break;
/* else very first i/o fails; write protected
* when writing, or some similar error; reprompt,
* rather than giving up in middle of multivol
* backup set */
if(debug)
fprintf(stderr, "First %s on new tape failed\n",
isread?"read":"write");
}
}
if (tty > 0)
(void) close(tty);
}
if(i == n)
firsttape = -1; /* we're in 'middle' of a tape */
return i;
}
void
backtape(void)
{
static int mtdev = 1;
static struct mtop mtop1 = {MTBSR, 1};
struct mtget mtget;
if (mtdev == 1) /* verify that it's a tape */
mtdev = ioctl(mt, MTIOCGET, &mtget);
if (mtdev == 0) {
/* a change was made at one point to use MTAFILE first, so
* we got an error if drive didn't support overwrite. The
* problem with that idea is that it can *never* work reliably,
* since the two putempty() records could be split over a block
* boundary, or not (except at nblock==1, and then it works reliably
* if you did bsr 2. Better to simply rely on getting an error on
* the next write, if the drive doesn't support overrite. */
if (ioctl(mt, MTIOCTOP, &mtop1) < 0)
goto backerr;
}
else
#ifdef RMT
rmtlseek(mt, (off_t) -TBLOCK*nblock, 1);
#else
lseek(mt, (off_t) -TBLOCK*nblock, 1);
#endif /* RMT */
recno--;
return;
backerr:
fprintf(stderr,"tar: can not append to existing archive on tape\n");
done(4);
}
/*
* zero the remaining blocks in the tbuf
* keeps POSIX happy
*/
void
zeroendtbuf(void)
{
while ( recno < nblock )
bzero(&tbuf[recno++], TBLOCK);
}
/* SGI: Variable blocking */
void
flushtape(void)
{
int amt;
char oink[3];
amt = TBLOCK*(Vflag?recno:nblock);
if (recno || !Vflag)
{
#ifdef RMT
if (myio(rmtwrite, mt, (char *) tbuf,
#else
if (myio(write, mt, (char *) tbuf,
#endif /* RMT */
amt, oink) != amt) {
terror(0, 2);
}
}
}
/* if eot == NULL -> don't switch tapes on error */
bread(int fd, char *buf, int size, char *eot)
{
int count;
static int lastread = 0;
if (!Bflag && !isatape)
#ifdef RMT
return (myio(rmtread, fd, buf, size, eot));
#else
return (myio(read, fd, buf, size, eot));
#endif /* RMT */
for (count = 0; count < size; count += lastread) {
if (lastread < 0) {
if (count > 0)
return (count);
return (lastread);
}
#ifdef RMT
lastread = myio(rmtread, fd, buf, size - count, eot);
#else
lastread = myio(read, fd, buf, size - count, eot);
#endif /* RMT */
if (debug)
if (debug > 1 || lastread <= 0)
fprintf(stderr,
"bread(fd=%d,buf=%u,bytes=%d,eot=%d) returned %d\n",
fd, buf, size - count, eot, lastread);
if (lastread <= 0)
if (lastread < 0)
return lastread;
else
return count;
buf += lastread;
}
return (count);
}
/* if eot == NULL -> don't switch tapes on error */
bwrite(int fd, char *buf, int size, char *eot)
{
int count;
static int lastwrite = 0;
if (!isatape)
#ifdef RMT
return (myio(rmtwrite, fd, buf, size, eot));
#else
return (myio(write, fd, buf, size, eot));
#endif /* RMT */
for (count = 0; count < size; count += lastwrite) {
#ifdef RMT
lastwrite = myio(rmtwrite, fd, buf, size - count, eot);
#else
lastwrite = myio(write, fd, buf, size - count, eot);
#endif /* RMT */
if (debug)
if (debug > 1 || lastwrite <= 0)
fprintf(stderr,
"bwrite(fd=%d,buf=%u,bytes=%d,eot=%d) returned %d\n",
fd, buf, size - count, eot, lastwrite);
if (lastwrite <= 0)
if (lastwrite < 0)
return lastwrite;
else
return count;
buf += lastwrite;
}
return (count);
}
/* Compare the next 'nw' characters of ifile with buf.
* return 1 if same, else 0
*/
cmprd(int ifile, char *buf, long num)
{
register int nr;
char ibuf[BUFSIZ];
for (; (nr = MIN(num, BUFSIZ)) > 0; num -= nr) {
if (read(ifile, ibuf, nr) < nr)
return NO;
if (bufcmp(buf, ibuf, nr))
return NO;
}
return YES;
}
bufcmp(char *cp1, char *cp2, int num)
{
if (num <= 0)
return 0;
do
if (*cp1++ != *cp2++)
return *--cp2 - *--cp1;
while (--num);
return 0;
}
dirpart(char *str)
{
register char *cp;
if (cp = strrchr(str, '/'))
return cp - str + 1;
else
return 0;
}
#ifdef IGNORE_SCCSID
cmpsccsid(char *f1, char *f2)
{
char buf[1000];
static char tmplate[] = "/var/tmp/cmpXXXXXX";
static char tmpbuf[sizeof tmplate];
static char *tmpfile;
int retval;
if (!tmpfile) {
tmpfile = tmpbuf;
strcpy(tmpfile, tmplate);
mktemp(tmpfile);
}
sprintf(buf, "grep -v '(#)' '%s' > '%s' ;\
grep -v '(#)' '%s' | cmp -s - '%s'", f1, tmpfile, f2, tmpfile);
retval = !system(buf);
unlink(tmpfile);
return retval;
}
#endif /* IGNORE_SCCSID */
atstrcmp(char **a, char **b)
{
int i;
i = strcmp(*a,*b);
return i;
}
/* ------------------------------------------------------------------------ */
/* these functions are more or less borrowed from pax */
#define UNAMELEN 31 /* make this 31 because we need a NULL */
#define GNAMELEN 31 /* make this 31 because we need a NULL */
uid_t saveuid = -993; /* presumed to be an invalid uid */
gid_t savegid = -993; /* presumed to be an invalid gid */
char saveuname[UNAMELEN];
char savegname[GNAMELEN];
char *
finduname(uid_t uid)
{
struct passwd *pw;
if ( uid != saveuid ) {
saveuid = uid;
if ( pw = getpwuid(uid) ) {
strncpy(saveuname, pw->pw_name, UNAMELEN);
} else
sprintf(saveuname,"unknown%d",uid);
}
return saveuname;
}
char *
findgname(gid_t gid)
{
struct group *gp;
if ( gid != savegid ) {
savegid = gid;
if ( gp = getgrgid(gid) ) {
strncpy(savegname, gp->gr_name, GNAMELEN);
} else
sprintf(savegname,"unknown%d",gid);
}
return savegname;
}
uid_t
finduid(struct pheader *hp)
{
struct passwd *pw;
if ( strncmp(hp->uname, saveuname, UNAMELEN) == 0 )
return saveuid;
strncpy(saveuname, hp->uname, UNAMELEN);
if ( pw = getpwnam(hp->uname) ) {
saveuid = pw->pw_uid;
} else {
saveuid = strtol(hp->uid, 0, 8);
}
return saveuid;
}
gid_t
findgid(struct pheader *hp)
{
struct group *gp;
if ( strncmp(hp->gname, savegname, GNAMELEN) == 0 )
return savegid;
strncpy(savegname, hp->gname, GNAMELEN);
if ( gp = getgrnam(hp->gname) ) {
savegid = gp->gr_gid;
} else {
savegid = strtol(hp->gid, 0, 8);
}
return savegid;
}
/* ------------------------------------------------------------------------ */
/*
* Copy 'wholename' into header block, posix style:
* if 'wholename' > 100 break up into prefix and name
* Returns -1 if name too long
* Returns 0 on success
*/
int
putname(struct pheader *hp, char *wholename)
{
int len = strlen(wholename);
if ( len > maxnamlen )
return -1;
if ( len > NAMSIZ ) { /* if shortname-format, must count NULL */
char *cp = wholename;
cp += (PREFIXSIZ > len) ? len : PREFIXSIZ;
smore:
while ( cp > wholename && *cp != '/' ) /* find a '/' */
cp--;
if ( cp == wholename ) /* no '/' found */
return -1;
if ( strlen(cp) > NAMSIZ ) /* "a/x<99chars>x" */
return -1;
if ( *(cp+1) == '\0' ) { /* BSD tar dirs end with '/' */
--cp;
goto smore;
}
strncpy(hp->prefix, wholename, cp - wholename);
strncpy(hp->name, cp+1, len - (cp - wholename) - 1);
} else {
strncpy(hp->name, wholename, sizeof(hp->name));
}
return 0;
}
/*
* Retrieve 'wholename' from header block, posix style:
* concatenate prefix '/' name
*/
char *
getname(struct pheader *hp)
{
static char wholename[NAMSPACE + PREFIXSPACE+1];
wholename[0] = '\0';
if ( hp->prefix[0] != '\0' ) {
strcpy(wholename, hp->prefix);
strcat(wholename, "/");
}
strncat(wholename, hp->name,100);
wholename[NAMSPACE + PREFIXSPACE] = '\0';
return wholename;
}
char *
getlink(struct pheader *hp)
{
static char wholelink[NAMSPACE + 1];
wholelink[0] = '\0';
strncat(wholelink, hp->linkname,NAMSPACE);
wholelink[NAMSPACE] = '\0';
return wholelink;
}
/* parseaction: looks at opt string: extracts action, disallowing duplicate
* actions, also disallows duplicates of options that take args. */
static char
parseaction(char *cp)
{
char c;
char action = 0x00;
int bseen = 0;
int fseen = 0;
while(c = *cp++)
switch (c) {
case '\n' :
case ' ' : break;
case 'c' :
case 'C' :
case 'r' :
case 't' :
case 'u' :
case 'x' :
case 'X' :
if (action) goto duperr;
action = c;
continue;
case 'f' : if (fseen) goto dferror;
fseen = 1;
continue;
case 'b' : if (bseen) goto dberror;
bseen = 1;
continue;
default : continue;
};
if (!action)
{
fprintf(stderr, "tar: no function specifier.\n");
usage();
}
return (action);
duperr:
fprintf(stderr, "tar: duplicate function specifiers %c and %c.\n", action, c);
usage();
dberror:
fprintf(stderr, "tar: duplicate option specifier 'b'. \n");
usage();
dferror:
fprintf(stderr, "tar: duplicate option specifier 'f'. \n");
usage();
/* NOTREACHED */
}
/* check to see if it's a drive, and is in audio mode; if it is, then
* try to fix it, and also notify them. Otherwise we can write in
* audio mode, but they won't be able to get the data back
*/
void
chkandfixaudio(int mt)
{
static struct mtop mtop = {MTAUD, 0};
struct mtget mtget;
unsigned status;
if(ioctl(mt, MTIOCGET, &mtget))
return;
status = mtget.mt_dsreg | ((unsigned)mtget.mt_erreg<<16);
if(status & CT_AUDIO) {
fprintf(stderr, "tar: Warning: drive was in audio mode, turning audio mode off\n");
if(ioctl(mt, MTIOCTOP, &mtop) < 0)
fprintf(stderr, "tar: Warning: unable to disable audio mode. Tape may not be readable\n");
}
}
#ifdef TRUSTEDIRIX
void
attr_from_tape(void)
{
int red = 0;
long blocks;
char *attrstrp, *bufp;
char buf[400];
clear_attr();
blocks = stbuf.st_size;
blocks += TBLOCK-1;
blocks /= TBLOCK;
g_attrbuf = malloc(blocks * TBLOCK + 1);
g_attrbuf[0] = '\0';
while (blocks-- > 0) {
int cnt;
if ((cnt = readtape(g_attrbuf + (red++ * TBLOCK))) < 0) {
fflush(stdout);
fprintf(stderr, "tar: tape read error while skipping data\n");
if (continflag)
break;
done(2);
}
if (cnt == 0)
premature_eof();
}
g_attrbuf[stbuf.st_size] = '\0';
attrstrp = g_attrbuf;
bufp = buf;
while (sscanf(attrstrp, "%s", buf) != EOF) {
if (!strncmp(bufp, "mac=", 4)) {
strncpy(g_lname, bufp + 4, sizeof(g_lname)-4);
g_lp = mac_from_text(g_lname);
} else
if (!strncmp(bufp, "acl=", 4)) {
strncpy(g_aclstr, bufp + 4, sizeof(g_aclstr)-4);
g_aclp = acl_from_text(g_aclstr);
}
attrstrp += strlen(buf) + 1;
}
}
void
clear_attr(void)
{
if (g_attrbuf != NULL)
free(g_attrbuf);
}
void
clear_glabel(void)
{
if (g_lp)
mac_free(g_lp);
g_lname[0] = '\0';
}
void
clear_gacl(void)
{
if (g_aclp)
acl_free(g_aclp);
g_aclstr[0] = '\0';
}
#endif /* TRUSTEDIRIX */