2796 lines
59 KiB
C
2796 lines
59 KiB
C
static char sccsid[] = "@(#)10 1.49.1.39 src/bos/usr/bin/tar/tar.c, cmdarch, bos41J, 9524D_all 6/13/95 12:16:56";
|
|
/*
|
|
* COMPONENT_NAME: (CMDARCH) archive files
|
|
*
|
|
* FUNCTIONS: tar
|
|
*
|
|
* ORIGINS: 26, 27, 71
|
|
*
|
|
* This module contains IBM CONFIDENTIAL code. -- (IBM
|
|
* Confidential Restricted when combined with the aggregated
|
|
* modules for this product)
|
|
* SOURCE MATERIALS
|
|
* (C) COPYRIGHT International Business Machines Corp. 1985, 1995
|
|
* All Rights Reserved
|
|
*
|
|
* US Government Users Restricted Rights - Use, duplication or
|
|
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
|
|
*
|
|
* Copyright (c) 1980 Regents of the University of California.
|
|
* All rights reserved. The Berkeley software License Agreement
|
|
* specifies the terms and conditions for redistribution.
|
|
*
|
|
* (c) Copyright 1990, 1991, 1992 OPEN SOFTWARE FOUNDATION, INC.
|
|
* ALL RIGHTS RESERVED
|
|
*
|
|
* OSF/1 1.1
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <sys/limits.h>
|
|
#include <tar.h>
|
|
#include <sys/param.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/devinfo.h>
|
|
#include <sys/tape.h>
|
|
#include <sys/time.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <IN/standard.h>
|
|
#include <dirent.h>
|
|
#include <pwd.h>
|
|
#include <grp.h>
|
|
#include <ctype.h>
|
|
#include <locale.h>
|
|
#include <nl_types.h>
|
|
#include "tar_msg.h"
|
|
#include <sys/inode.h>
|
|
|
|
#define MSGSTR(num,str) catgets(catd,MS_TAR,num,str) /*MSG*/
|
|
#define PIPSIZ 4096
|
|
#define TBLOCK 512 /* tar block size */
|
|
#define NBLOCK 20 /* default num of blocks */
|
|
#define MAXBLOCK 800 /* max num of blocks */
|
|
#define TAR_PATH_LEN 257 /* max size of tar archive path with \0 */
|
|
#define NAME_SIZE 100 /* file name size in tar header */
|
|
#define PREFIX_SIZE 155 /* path size in tar header */
|
|
#define writetape(b) writetbuf(b, 1)
|
|
#define min(a,b) ((a) < (b) ? (a) : (b))
|
|
#define max(a,b) ((a) > (b) ? (a) : (b))
|
|
|
|
/* hblock modified for POSIX compliance */
|
|
#define UNAMELEN 32
|
|
#define GNAMELEN 32
|
|
|
|
union hblock {
|
|
char dummy[TBLOCK];
|
|
struct header {
|
|
char name[NAME_SIZE];
|
|
char mode[8];
|
|
char uid[8];
|
|
char gid[8];
|
|
char size[12];
|
|
char mtime[12];
|
|
char chksum[8];
|
|
char typeflag;
|
|
char linkname[NAME_SIZE];
|
|
char magic[6];
|
|
char version[2];
|
|
char uname[UNAMELEN];
|
|
char gname[GNAMELEN];
|
|
char devmajor[8];
|
|
char devminor[8];
|
|
char prefix[PREFIX_SIZE];
|
|
} dbuf;
|
|
};
|
|
|
|
struct linkbuf {
|
|
ino_t inum;
|
|
dev_t devnum;
|
|
int count;
|
|
char pathname[NAME_SIZE+1]; /* +1 to allow for \0 */
|
|
struct linkbuf *nextp;
|
|
};
|
|
|
|
static unsigned long maxint=(unsigned long)((1L << (sizeof(int) * 8)) - 1);
|
|
static int Nblock = NBLOCK ;
|
|
static union hblock dblock;
|
|
static union hblock *tbuf;
|
|
static struct linkbuf *ihead;
|
|
static struct stat stbuf;
|
|
static mode_t mask;
|
|
static char is_root;
|
|
|
|
static nl_catd catd ; /* message catalog descriptor */
|
|
|
|
static int rflag;
|
|
static int xflag;
|
|
static int vflag;
|
|
static int tflag;
|
|
static int cflag;
|
|
static int mflag;
|
|
static int fflag;
|
|
static int iflag;
|
|
static int oflag; /* xtract: don't restore uid/gid */
|
|
static int wflag;
|
|
static int hflag;
|
|
static int Bflag;
|
|
static int Fflag;
|
|
static int Cflag;
|
|
static int Lflag;
|
|
static int donly; /* used when reading from inputlist */
|
|
static int dflag;
|
|
static int sflag;
|
|
static int bflag;
|
|
static int Nflag;
|
|
static int Sflag;
|
|
static int pflag=0;
|
|
|
|
static int mt;
|
|
static int term;
|
|
static long trecs; /* number of records written to current volume */
|
|
static int recno;
|
|
static int first;
|
|
static int prtlinkerr;
|
|
static int freemem = 1;
|
|
static int nblock = 0;
|
|
static int berkeley = 0;
|
|
static int ignorechksum = 0;
|
|
static unsigned recsize; /* size of records on tape */
|
|
static int nbprset ;
|
|
static char full_name [TAR_PATH_LEN] ;
|
|
static int exit_status = 0; /* 0 - success, 1 - fatal, 2 - pos-nonfatal */
|
|
|
|
static int arch_file_type;
|
|
|
|
static int onintr(void);
|
|
static int onquit(void);
|
|
static int onhup(void);
|
|
static int onterm(void);
|
|
static char *rel_name();
|
|
|
|
static daddr_t high;
|
|
static daddr_t bsrch();
|
|
|
|
|
|
/* tape info defaults */
|
|
#define GAP 7500 /* Interrecord gap, .0001 inch units */
|
|
#define DENSITY 1200
|
|
#define TAPE_FOOT 110000L /* 11 inches */
|
|
static int density = DENSITY ;
|
|
static int tapelen ;
|
|
static int bptape ;
|
|
static int rptape ;
|
|
|
|
static FILE *vfile = stdout;
|
|
static FILE *tfile;
|
|
static FILE *list_fd ; /* fd for input list file */
|
|
static FILE *mtfp;
|
|
|
|
#define DEF_TAPE "/dev/rmt0"
|
|
static char cantopen [] = "tar: cannot open %s\n" ;
|
|
static char tname[] = "/tmp/tarXXXXXX";
|
|
static char usefile [PATH_MAX+1] = DEF_TAPE; /* +1 for \0 char */
|
|
|
|
static char *getpwd();
|
|
static char *getmem();
|
|
|
|
static FILE *response_file = stdin;
|
|
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
int cp;
|
|
|
|
(void) setlocale(LC_ALL,"");
|
|
catd = catopen(MF_TAR, NL_CAT_LOCALE);
|
|
|
|
if (argc < 2)
|
|
usage();
|
|
|
|
tfile = NULL;
|
|
is_root = !getuid();
|
|
|
|
/* Allow options to be specified w/o the '-'. (ala Berkeley) */
|
|
if (argv[1][0] != '-') {
|
|
char *bcp;
|
|
|
|
berkeley = 1;
|
|
argv[argc] = 0;
|
|
argv++;
|
|
for (bcp = *argv++; *bcp; bcp++)
|
|
switch(*bcp) {
|
|
|
|
case 'o':
|
|
oflag++;
|
|
break;
|
|
|
|
case 'L':
|
|
Lflag++ ;
|
|
if (( list_fd = fopen ( *argv , "r" )) == NULL ) {
|
|
eprintf ( MSGSTR( ETOPEN, cantopen) , *argv ) ;
|
|
done ( 1 ) ;
|
|
}
|
|
argv++;
|
|
break ;
|
|
|
|
case 'S':
|
|
{
|
|
int len ;
|
|
char *mark ;
|
|
Sflag++;
|
|
if (*argv == 0) {
|
|
usage();
|
|
}
|
|
len = strlen ( *argv ) ;
|
|
if ( (*argv)[--len] == 'b' ) {
|
|
(*argv)[len] = '\0' ;
|
|
bptape = atoi ( *argv ) ;
|
|
}
|
|
else {
|
|
tapelen = atoi ( *argv ) ;
|
|
mark = strchr ( *argv , '@' ) ;
|
|
if ( mark )
|
|
density = atoi ( ++mark ) ;
|
|
}
|
|
}
|
|
argv++;
|
|
break ;
|
|
|
|
case 'd':
|
|
dflag++ ;
|
|
break ;
|
|
|
|
case 's':
|
|
sflag++ ;
|
|
break ;
|
|
|
|
case 'f':
|
|
if (*argv == 0) {
|
|
fprintf(stderr, MSGSTR( EFFLAG,
|
|
"tar: tapefile must be specified with 'f' option\n"));
|
|
usage();
|
|
}
|
|
if ( strlen ( *argv ) > PATH_MAX ) {
|
|
fprintf (stderr, MSGSTR ( ETLONGFN ,
|
|
"tar: %s: file name too long\n") , *argv ) ;
|
|
exit (1) ;
|
|
}
|
|
strcpy( usefile, *argv);
|
|
argv++;
|
|
fflag++;
|
|
break;
|
|
|
|
case 'c':
|
|
cflag++;
|
|
rflag++;
|
|
break;
|
|
|
|
case 'p':
|
|
pflag++;
|
|
break;
|
|
|
|
case 'u':
|
|
mktemp(tname);
|
|
if ((tfile = fopen(tname, "w")) == NULL) {
|
|
fprintf(stderr, MSGSTR( ETCRTMP,
|
|
"tar: cannot create temporary file (%s)\n"),
|
|
tname);
|
|
done(1);
|
|
}
|
|
|
|
fputs("!!!!!/!/!/!/!/!/!/! 000\n",tfile);
|
|
rflag++;
|
|
break;
|
|
|
|
case 'r':
|
|
rflag++;
|
|
break;
|
|
|
|
case 'v':
|
|
vflag++;
|
|
break;
|
|
|
|
case 'w':
|
|
wflag++;
|
|
break;
|
|
|
|
case 'x':
|
|
xflag++;
|
|
break;
|
|
|
|
case 't':
|
|
tflag++;
|
|
break;
|
|
|
|
case 'm':
|
|
mflag++;
|
|
break;
|
|
|
|
case '-':
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
strcpy(usefile, DEF_TAPE);
|
|
usefile[strlen(usefile)-1] = *bcp;
|
|
break;
|
|
|
|
case 'N':
|
|
Nflag++ ;
|
|
Nblock = maxint / TBLOCK ;
|
|
|
|
case 'b':
|
|
if ( *bcp == 'b' )
|
|
bflag++ ;
|
|
if (*argv == 0) {
|
|
fprintf(stderr, MSGSTR( ETNOBSIZE,
|
|
"tar: blocksize must be specified with 'b' or 'N' options\n"));
|
|
usage();
|
|
}
|
|
nblock = atoi(*argv);
|
|
if (nblock <= 0 || nblock > Nblock) {
|
|
fprintf(stderr, MSGSTR( EBSIZE,
|
|
"tar: invalid blocksize %s.\n"), *argv);
|
|
done(1);
|
|
}
|
|
if ( *bcp == 'N' ) {
|
|
if ( nblock > MAXBLOCK ) {
|
|
nblock = MAXBLOCK ;
|
|
eprintf ( MSGSTR( CREDUCED,
|
|
"tar: Cluster size reduced to %d.\n" ),
|
|
nblock ) ;
|
|
}
|
|
Nblock = nblock ;
|
|
}
|
|
argv++;
|
|
recsize = TBLOCK * nblock;
|
|
nbprset++ ;
|
|
break;
|
|
|
|
case 'l':
|
|
prtlinkerr++;
|
|
break;
|
|
|
|
case 'h':
|
|
hflag++;
|
|
break;
|
|
|
|
case 'i':
|
|
iflag++;
|
|
ignorechksum = 1;
|
|
break;
|
|
|
|
case 'B':
|
|
Bflag++;
|
|
break;
|
|
|
|
case 'F':
|
|
Fflag++;
|
|
break;
|
|
|
|
default:
|
|
usage();
|
|
}
|
|
} else
|
|
while (!Cflag && (cp = getopt(argc, argv, "0123456789b:cdf:hilmoprstuvwxBCFN:L:S:")) != EOF)
|
|
switch(cp) {
|
|
|
|
case 'o':
|
|
oflag++;
|
|
break;
|
|
|
|
case 'C':
|
|
Cflag++ ;
|
|
if ( strncmp ( argv [optind] , "-C" , (size_t)2 ))
|
|
optind-- ; /* dorep expects "-C" */
|
|
break ;
|
|
|
|
case 'L':
|
|
Lflag++ ;
|
|
if (( list_fd = fopen ( optarg , "r" )) == NULL ) {
|
|
eprintf ( MSGSTR( ETOPEN, cantopen) , optarg ) ;
|
|
done ( 1 ) ;
|
|
}
|
|
break ;
|
|
|
|
case 'S':
|
|
{
|
|
int len ;
|
|
char *mark ;
|
|
Sflag++;
|
|
len = strlen ( optarg ) ;
|
|
if ( optarg [--len] == 'b' ) {
|
|
optarg [len] = '\0' ;
|
|
bptape = atoi ( optarg ) ;
|
|
}
|
|
else {
|
|
tapelen = atoi ( optarg ) ;
|
|
mark = strchr ( optarg , '@' ) ;
|
|
if ( mark )
|
|
density = atoi ( ++mark ) ;
|
|
}
|
|
}
|
|
break ;
|
|
|
|
case 'd':
|
|
dflag++ ;
|
|
break ;
|
|
|
|
case 's':
|
|
sflag++ ;
|
|
break ;
|
|
|
|
case 'f':
|
|
if (*optarg == 0) {
|
|
fprintf(stderr, MSGSTR( EFFLAG,
|
|
"tar: file must be specified with 'f' option\n"));
|
|
usage();
|
|
}
|
|
if ( strlen ( optarg ) > PATH_MAX ) {
|
|
fprintf (stderr, MSGSTR ( ETLONGFN ,
|
|
"tar: %s: file name too long\n") , optarg ) ;
|
|
exit (1) ;
|
|
}
|
|
strcpy ( usefile , optarg ) ;
|
|
fflag++;
|
|
break;
|
|
|
|
case 'c':
|
|
cflag++;
|
|
rflag++;
|
|
break;
|
|
|
|
case 'p':
|
|
pflag++;
|
|
break;
|
|
|
|
case 'u':
|
|
mktemp(tname);
|
|
if ((tfile = fopen(tname, "w")) == NULL) {
|
|
fprintf(stderr, MSGSTR( ETCRTMP,
|
|
"tar: cannot create temporary file (%s)\n"),
|
|
tname);
|
|
done(1);
|
|
}
|
|
|
|
fputs("!!!!!/!/!/!/!/!/!/! 000\n",tfile);
|
|
rflag++;
|
|
break;
|
|
|
|
case 'r':
|
|
rflag++;
|
|
break;
|
|
|
|
case 'v':
|
|
vflag++;
|
|
break;
|
|
|
|
case 'w':
|
|
wflag++;
|
|
break;
|
|
|
|
case 'x':
|
|
xflag++;
|
|
break;
|
|
|
|
case 't':
|
|
tflag++;
|
|
break;
|
|
|
|
case 'm':
|
|
mflag++;
|
|
break;
|
|
|
|
case '-':
|
|
break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
strcpy(usefile, DEF_TAPE);
|
|
usefile[strlen(usefile)-1] = cp;
|
|
break;
|
|
|
|
case 'N':
|
|
Nflag++ ;
|
|
Nblock = maxint / TBLOCK ;
|
|
|
|
case 'b':
|
|
if ( cp == 'b' )
|
|
bflag++ ;
|
|
if (*optarg == 0) {
|
|
fprintf(stderr, MSGSTR ( ETNOBSIZE,
|
|
"tar: blocksize must be specified with 'b' or 'N' option\n"));
|
|
usage();
|
|
}
|
|
nblock = atoi(optarg);
|
|
if (nblock <= 0 || nblock > Nblock) {
|
|
fprintf(stderr, MSGSTR ( EBSIZE,
|
|
"tar: invalid blocksize %s.\n" ), optarg);
|
|
done(1);
|
|
}
|
|
if ( cp == 'N' ) {
|
|
if ( nblock > MAXBLOCK ) {
|
|
nblock = MAXBLOCK ;
|
|
eprintf ( MSGSTR( CREDUCED,
|
|
"tar: Cluster size reduced to %d.\n" ),
|
|
nblock ) ;
|
|
}
|
|
Nblock = nblock ;
|
|
}
|
|
recsize = TBLOCK * nblock;
|
|
nbprset++ ;
|
|
break;
|
|
|
|
case 'l':
|
|
prtlinkerr++;
|
|
break;
|
|
|
|
case 'h':
|
|
hflag++;
|
|
break;
|
|
|
|
case 'i':
|
|
iflag++;
|
|
ignorechksum = 1;
|
|
break;
|
|
|
|
case 'B':
|
|
Bflag++;
|
|
break;
|
|
|
|
case 'F':
|
|
Fflag++;
|
|
break;
|
|
|
|
default:
|
|
usage();
|
|
}
|
|
|
|
if (berkeley) optind = 0;
|
|
if (!rflag && !xflag && !tflag)
|
|
usage();
|
|
if (cflag + rflag + tflag + xflag > (cflag ? 2 : 1))
|
|
usage();
|
|
if (wflag && (!strcmp(usefile,"-"))) {
|
|
if ((response_file = fopen("/dev/tty","r")) == NULL) {
|
|
fprintf(stderr, MSGSTR(NODEVTTYOPEN,
|
|
"tar: can not open /dev/tty for interactive operation.\n"));
|
|
done(1);
|
|
}
|
|
}
|
|
if (rflag) {
|
|
if (cflag && tfile != NULL)
|
|
usage();
|
|
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
|
|
(void) signal(SIGINT, (void (*)(int))onintr);
|
|
if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
|
|
(void) signal(SIGHUP, (void (*)(int))onhup);
|
|
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
|
|
(void) signal(SIGQUIT, (void (*)(int))onquit);
|
|
if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
|
|
(void) signal(SIGTERM, (void (*)(int))onterm);
|
|
mt = openmt(usefile, O_RDWR);
|
|
dorep(argv + optind);
|
|
if (close(mt) < 0) {
|
|
perror ( MSGSTR( ETWRITE, "tar: tape write error" )) ;
|
|
done (1) ;
|
|
}
|
|
done(exit_status);
|
|
}
|
|
mt = openmt(usefile, O_RDONLY);
|
|
{
|
|
struct stat tstat ;
|
|
if ( fstat(mt, &tstat) != 0 ) {
|
|
|
|
eprintf ( MSGSTR( ESTAT,
|
|
"tar: could not stat file = %s\n") , usefile ) ;
|
|
done (1) ;
|
|
}
|
|
/* check for fifo */
|
|
if ( !nbprset && tstat.st_mode == S_IFIFO ) {
|
|
recsize = PIPSIZ ;
|
|
nblock = PIPSIZ / TBLOCK ;
|
|
nbprset++ ;
|
|
}
|
|
|
|
arch_file_type=tstat.st_mode & S_IFMT;
|
|
|
|
if ( xflag )
|
|
doxtract(argv + optind);
|
|
else
|
|
dotable(argv + optind);
|
|
}
|
|
done(exit_status);
|
|
}
|
|
|
|
static
|
|
usage()
|
|
{
|
|
eprintf( MSGSTR( USAGE1,
|
|
"usage: tar -{crtux} [-BFdhilmopsvw] [-num] [-ffile]\n"));
|
|
eprintf( MSGSTR( USAGE2,
|
|
"\t\t [-bblocks] [-S feet] [-S feet@density] [-S blocksb]\n"));
|
|
eprintf( MSGSTR( USAGE3,
|
|
"\t\t [-Linputlist] [-Nblocks] [-C directory] file ...\n"));
|
|
done(1);
|
|
}
|
|
|
|
static int
|
|
openmt(tape, writing)
|
|
char *tape;
|
|
int writing;
|
|
{
|
|
|
|
if (strcmp(tape, "-") == 0) {
|
|
/*
|
|
* Read from standard input or write to standard output.
|
|
*/
|
|
if (writing) {
|
|
if (cflag == 0) {
|
|
fprintf(stderr, MSGSTR( ESTDOUT,
|
|
"tar: can only create standard output archives\n"));
|
|
done(1);
|
|
}
|
|
vfile = stderr;
|
|
setlinebuf(vfile);
|
|
mt = dup(1);
|
|
} else {
|
|
mt = dup(0);
|
|
Bflag++;
|
|
}
|
|
} else {
|
|
/*
|
|
* Use file or tape on local machine.
|
|
*/
|
|
if (writing) {
|
|
if (cflag)
|
|
mt = open(tape, O_RDWR|O_CREAT|O_TRUNC, 0666);
|
|
else
|
|
mt = open(tape, O_RDWR);
|
|
} else
|
|
mt = open(tape, O_RDONLY);
|
|
if (mt < 0) {
|
|
fprintf(stderr, MSGSTR( ETAR, "tar: "));
|
|
perror(tape);
|
|
done(1);
|
|
}
|
|
}
|
|
return(mt);
|
|
}
|
|
|
|
|
|
/* dorep will create a new archive or append to an existing archive.
|
|
*/
|
|
|
|
static
|
|
dorep(argv)
|
|
char *argv[];
|
|
{
|
|
register char *cp2;
|
|
char wdir[TAR_PATH_LEN], tempdir[TAR_PATH_LEN], *parent;
|
|
|
|
{
|
|
struct stat tstat;
|
|
unsigned short f_mode ;
|
|
long offset;
|
|
int errnum=0,val1=0;
|
|
|
|
fstat(mt, &tstat);
|
|
arch_file_type = f_mode = tstat.st_mode & S_IFMT ;
|
|
if (!cflag) {
|
|
/* request is update or replace */
|
|
(void) getdir();
|
|
do {
|
|
passtape();
|
|
if (term)
|
|
done(1);
|
|
(void) getdir();
|
|
} while (!endtape());
|
|
trecs-- ;
|
|
mtfp = fdopen(mt, "r");
|
|
if (nblock == MAXBLOCK) {
|
|
offset = (ftell(mtfp) / TBLOCK) - (trecs * nblock);
|
|
backspc(offset*TBLOCK);
|
|
}
|
|
else
|
|
backspc(TBLOCK*nblock);
|
|
recno--;
|
|
if (tfile != NULL) {
|
|
char buf[200];
|
|
|
|
sprintf(buf,
|
|
"sort -k 1,1 -k 2nr %s | awk '$1 != prev {print; prev=$1}' > %sX; mv %sX %s",
|
|
tname, tname, tname, tname);
|
|
fflush(tfile);
|
|
system(buf);
|
|
freopen(tname, "r", tfile);
|
|
fstat((int)fileno(tfile), &stbuf);
|
|
high = stbuf.st_size;
|
|
}
|
|
}
|
|
else
|
|
/* check for pipe as output */
|
|
if ( !nbprset && f_mode == S_IFIFO ) {
|
|
recsize = PIPSIZ ;
|
|
nblock = PIPSIZ / TBLOCK ;
|
|
nbprset++ ;
|
|
}
|
|
|
|
}
|
|
|
|
(void) getpwd(wdir);
|
|
/* read from input arguments first
|
|
* and then handle input from file
|
|
*/
|
|
while ((Lflag || *argv ) && ! term) {
|
|
char fname [TAR_PATH_LEN] ;
|
|
if ( *argv ) {
|
|
if ( strlen ( *argv ) >= TAR_PATH_LEN ) {
|
|
fprintf ( stderr , MSGSTR( ETLONGFN,
|
|
"tar: %s: file name too long\n") , *argv ) ;
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
*argv++ ;
|
|
continue ;
|
|
}
|
|
strcpy ( fname , *argv ) ;
|
|
}
|
|
else {
|
|
char *ch ;
|
|
/* set donly flag to indicate only the directory entry
|
|
* will be archived and not the contents of the directory
|
|
*/
|
|
if ( !donly )
|
|
donly++ ;
|
|
|
|
/* read from input file */
|
|
fgets ( fname , TAR_PATH_LEN , list_fd ) ;
|
|
if ( feof ( list_fd )) {
|
|
Lflag = 0 ;
|
|
(void) fclose ( list_fd ) ;
|
|
continue ;
|
|
}
|
|
/* replace \n with null */
|
|
if ( ch = index ( fname , '\n' ))
|
|
*ch = '\0' ;
|
|
else {
|
|
/* file name is too long
|
|
read until \n is encountered */
|
|
fprintf ( stderr , MSGSTR( ETAR , "tar: " )) ;
|
|
do {
|
|
fputs(fname,stderr);
|
|
fgets ( fname , TAR_PATH_LEN , list_fd ) ;
|
|
ch = index ( fname , '\n' ) ;
|
|
}
|
|
while ( *ch != '\n' ) ;
|
|
*ch = '\0' ;
|
|
fprintf ( stderr , MSGSTR( ELONGFN,
|
|
"%s: file name too long\n" ), fname ) ;
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
continue ;
|
|
}
|
|
}
|
|
cp2 = fname ;
|
|
|
|
if (!strncmp(cp2, "-C" , (size_t)2)) {
|
|
/* two formats -Cdirectory or
|
|
* -C directory
|
|
*/
|
|
if ( !strcmp (cp2 , "-C")) {
|
|
*argv++ ; /* -C directory format */
|
|
cp2 = *argv ;
|
|
}
|
|
else
|
|
cp2 += 2 ; /* -Cdirectory format */
|
|
|
|
/* check for null directory */
|
|
if ( !cp2 )
|
|
continue ;
|
|
if (chdir(cp2) < 0) {
|
|
fprintf(stderr, MSGSTR( ECHDIR,
|
|
"tar: can't change directories to "));
|
|
perror(cp2);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
} else
|
|
(void) getpwd(wdir);
|
|
*argv++;
|
|
continue;
|
|
}
|
|
parent = wdir;
|
|
cp2 = rindex ( fname , '/' ) ;
|
|
if (cp2) {
|
|
if (cp2 == fname) {
|
|
char save = *++cp2;
|
|
|
|
*cp2 = '\0';
|
|
if (chdir(fname) < 0) {
|
|
fprintf(stderr, MSGSTR( ECHDIR,
|
|
"tar: can't change directories to "));
|
|
perror(fname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
/* When reading from input */
|
|
/* file, argv is not valid. */
|
|
if ( *argv )
|
|
argv++;
|
|
continue;
|
|
}
|
|
*cp2 = save;
|
|
} else {
|
|
*cp2 = '\0';
|
|
if (chdir(fname) < 0) {
|
|
fprintf(stderr, MSGSTR( ECHDIR,
|
|
"tar: can't change directories to "));
|
|
perror(fname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
/* When reading from input */
|
|
/* file, argv is not valid. */
|
|
if ( *argv )
|
|
argv++;
|
|
continue;
|
|
}
|
|
*cp2 = '/';
|
|
cp2++;
|
|
}
|
|
parent = getpwd(tempdir);
|
|
}
|
|
else /* string does not contain "/" */
|
|
cp2 = fname ;
|
|
|
|
/* P39755 : Handle file names like / /dir/ /dir/dir/ ... *
|
|
*/
|
|
if((*cp2 == '\0') && (*fname != '\0'))
|
|
cp2="." ;
|
|
|
|
putfile(fname, cp2, parent, 0);
|
|
if ( *argv )
|
|
*argv++ ;
|
|
if (chdir(wdir) < 0) {
|
|
fprintf(stderr, MSGSTR( ECHBACK,
|
|
"tar: cannot change back: "));
|
|
perror(wdir);
|
|
exit_status = 1;
|
|
}
|
|
}
|
|
putempty();
|
|
putempty();
|
|
if (recno != 0)
|
|
bwrite ((char *) tbuf ) ;
|
|
if (prtlinkerr == 0)
|
|
return;
|
|
for (; ihead != NULL; ihead = ihead->nextp) {
|
|
if (ihead->count == 0)
|
|
continue;
|
|
fprintf(stderr, MSGSTR( ELINKS,
|
|
"tar: missing links to %s\n" ), ihead->pathname);
|
|
exit_status = 1;
|
|
}
|
|
}
|
|
|
|
static
|
|
backspc(nbytes)
|
|
{
|
|
int count;
|
|
struct devinfo devnfo ;
|
|
struct stop st_com;
|
|
|
|
if (ioctl(mt, IOCINFO, &devnfo) == -1)
|
|
devnfo.devtype = '#';
|
|
if ((devnfo.devtype == DD_TAPE) || (devnfo.devtype == DD_SCTAPE)) {
|
|
if (devnfo.un.scmt.blksize == 0)
|
|
count = nbytes/(TBLOCK*nblock);
|
|
else
|
|
count = nbytes/devnfo.un.scmt.blksize;
|
|
st_com.st_op = STRSR;
|
|
st_com.st_count = count;
|
|
if (ioctl(mt, STIOCTOP, &st_com) < 0) {
|
|
perror(MSGSTR(BACKSPCI, "tar: ioctl backspace error"));
|
|
done (1) ;
|
|
}
|
|
} else {
|
|
if (lseek(mt, -(daddr_t)nbytes, SEEK_CUR) == -1) {
|
|
perror(MSGSTR(BACKSPCL, "tar: lseek backspace error"));
|
|
done (1) ;
|
|
}
|
|
}
|
|
}
|
|
|
|
static
|
|
endtape()
|
|
{
|
|
return (dblock.dbuf.name[0] == '\0');
|
|
}
|
|
|
|
|
|
/* The function getdir will read a header from the archive
|
|
* file. A POSIX header will contain all needed information
|
|
* in the correct fields. Non POSIX headers need to be
|
|
* checked and completed (e.g. major and minor device # in
|
|
* the mtime field).
|
|
*/
|
|
static getdir()
|
|
{
|
|
register struct stat *sp = &stbuf;
|
|
int i, ftype;
|
|
int chksum;
|
|
|
|
top:
|
|
readtape((char *)&dblock);
|
|
if (dblock.dbuf.name[0] == '\0' && !ignorechksum)
|
|
return;
|
|
|
|
sscanf(dblock.dbuf.chksum, "%0o", &chksum);
|
|
if ((chksum != (i = checksum())) && (chksum != signed_checksum())) {
|
|
fprintf(stderr, MSGSTR( ECSUMC,
|
|
"tar: directory checksum error (%d != %d)\n" ),
|
|
chksum, i);
|
|
/* ignore checksum errors ? */
|
|
if (iflag)
|
|
goto top;
|
|
done(1);
|
|
}
|
|
|
|
/* since we got a good chksum, we can now turn off ignorechksum
|
|
* which was primaryly for skipping over the initial data blocks
|
|
* so that we can start extraction from any arbitrary volume */
|
|
ignorechksum = 0;
|
|
sscanf(dblock.dbuf.mode, "%0o", &i);
|
|
sp->st_mode = (i & 07777);
|
|
|
|
/* We need to do the following for POSIX which requires 'typeflag'
|
|
be used for setting the S_IFMT part of the mode bits */
|
|
|
|
switch(dblock.dbuf.typeflag) {
|
|
case DIRTYPE:
|
|
sp->st_mode |= S_IFDIR;
|
|
break;
|
|
case FIFOTYPE:
|
|
sp->st_mode |= S_IFIFO;
|
|
break;
|
|
case CHRTYPE:
|
|
sp->st_mode |= S_IFCHR;
|
|
break;
|
|
case BLKTYPE:
|
|
sp->st_mode |= S_IFBLK;
|
|
break;
|
|
case AREGTYPE:
|
|
case REGTYPE:
|
|
i = strlen(dblock.dbuf.name);
|
|
if (i > NAME_SIZE)
|
|
i = NAME_SIZE;
|
|
if (dblock.dbuf.name[i-1] == '/') {
|
|
dblock.dbuf.typeflag = DIRTYPE;
|
|
sp->st_mode |= S_IFDIR;
|
|
} else
|
|
sp->st_mode |= S_IFREG;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* check for posix header */
|
|
bzero ( full_name , sizeof (full_name)) ;
|
|
if ( ! strncmp ( dblock.dbuf.magic , TMAGIC , (size_t)TMAGLEN )) {
|
|
if (i = strlen ( dblock.dbuf.prefix)) {
|
|
/* use prefix + '/' + name */
|
|
strncpy(full_name, dblock.dbuf.prefix, (size_t)PREFIX_SIZE);
|
|
/* don't want '//file' */
|
|
if (strcmp(full_name, "/"))
|
|
full_name[i++] = '/';
|
|
}
|
|
strncpy ( &full_name [i] , dblock.dbuf.name , (size_t)NAME_SIZE ) ;
|
|
|
|
/* set uid and gid based on uname and gname */
|
|
if (!name_to_uid(dblock.dbuf.uname, &sp->st_uid)) {
|
|
sscanf(dblock.dbuf.uid, "%0o", &i);
|
|
sp->st_uid = i;
|
|
}
|
|
if (!name_to_gid(dblock.dbuf.gname, &sp->st_gid)) {
|
|
sscanf(dblock.dbuf.gid, "%0o", &i);
|
|
sp->st_gid = i;
|
|
}
|
|
sscanf(dblock.dbuf.mtime, "%0lo", &sp->st_mtime);
|
|
}
|
|
else {
|
|
/* older archive file */
|
|
strncpy ( full_name , dblock.dbuf.name , (size_t)NAME_SIZE ) ;
|
|
sscanf(dblock.dbuf.uid, "%0o", &i);
|
|
sp->st_uid = i;
|
|
sscanf(dblock.dbuf.gid, "%0o", &i);
|
|
sp->st_gid = i;
|
|
|
|
/* if special file then decode major
|
|
and minor number in mtime */
|
|
sscanf(dblock.dbuf.mtime, "%0lo", &sp->st_mtime);
|
|
ftype = sp->st_mode & S_IFMT ;
|
|
switch ( ftype ) {
|
|
case S_IFIFO :
|
|
case S_IFBLK :
|
|
case S_IFCHR : {
|
|
int val ;
|
|
/* place major and minor num
|
|
* in dblock header
|
|
*/
|
|
val = ((sp->st_mtime >> 8) & 0xff00) |
|
|
(sp->st_mtime & 0xff);
|
|
sprintf(dblock.dbuf.devminor, "%06o " , val);
|
|
val = (sp->st_mtime >> 8) & 0xff;
|
|
sprintf(dblock.dbuf.devmajor, "%06o " , val);
|
|
break ;
|
|
}
|
|
|
|
default :
|
|
/* Required for POSIX */
|
|
sprintf(dblock.dbuf.devminor, "%06o " , 0);
|
|
sprintf(dblock.dbuf.devmajor, "%06o " , 0);
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
sscanf(dblock.dbuf.size, "%0lo", &sp->st_size);
|
|
if (tfile != NULL)
|
|
fprintf(tfile, "%s %.12s\n", full_name, dblock.dbuf.mtime);
|
|
|
|
return (TRUE) ;
|
|
}
|
|
|
|
static passtape()
|
|
{
|
|
long blocks;
|
|
char *bufp;
|
|
|
|
if (dblock.dbuf.typeflag == LNKTYPE)
|
|
return;
|
|
blocks = stbuf.st_size;
|
|
blocks += TBLOCK-1;
|
|
blocks /= TBLOCK;
|
|
|
|
while (blocks-- > 0)
|
|
(void) readtbuf(&bufp, TBLOCK);
|
|
}
|
|
|
|
static putfile(longname, shortname, parent, recursive)
|
|
char *longname;
|
|
char *shortname;
|
|
char *parent;
|
|
int recursive;
|
|
{
|
|
int infile = 0;
|
|
long blocks;
|
|
char buf[TBLOCK];
|
|
char *bigbuf;
|
|
register char *cp;
|
|
struct dirent *dp;
|
|
DIR *dirp;
|
|
register int i;
|
|
long l;
|
|
char newparent[PATH_MAX];
|
|
char linknamebuf[NAME_SIZE + 1];
|
|
int maxread;
|
|
int hint; /* amount to write to get "in sync" */
|
|
|
|
if (!hflag)
|
|
i = lstat(shortname, &stbuf);
|
|
else
|
|
i = stat(shortname, &stbuf);
|
|
if (i < 0) {
|
|
fprintf(stderr, MSGSTR( ETAR, "tar: " ));
|
|
perror(longname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
return;
|
|
}
|
|
if (tfile != NULL && checkupdate(longname) == 0)
|
|
return;
|
|
if (checkw(MSGSTR(DOARCH, "a %s: "), longname) == 0)
|
|
return;
|
|
if (Fflag && checkf(shortname, stbuf.st_mode) == 0)
|
|
return;
|
|
|
|
switch (stbuf.st_mode & S_IFMT) {
|
|
case S_IFDIR:
|
|
for (i = 0, cp = buf; *cp++ = longname[i++];)
|
|
;
|
|
if ( buf [--i-1] != '/' )
|
|
buf [i++] = '/' ;
|
|
cp = &buf [i] ;
|
|
*cp = 0 ;
|
|
/* By default tar used to NOT archive empty directories,
|
|
* the -d option (dflag) was required to do so. This
|
|
* is now done by default and the -d option now only
|
|
* archives special files and fifo's.
|
|
*/
|
|
|
|
stbuf.st_size = 0;
|
|
if ( tomodes(&stbuf, longname , shortname )) {
|
|
sprintf(dblock.dbuf.chksum, "%06o", checksum());
|
|
(void) writetape((char *)&dblock);
|
|
if ( vflag )
|
|
fprintf(vfile, "a %s\n", longname);
|
|
}
|
|
/* donly flag indicates to only archive the given directory,
|
|
* and not its contents, so skip past the code to add the
|
|
* directory's contents to the archive.
|
|
*/
|
|
if (donly)
|
|
break ;
|
|
|
|
/* handle entries that are / and /dir */
|
|
if ( strcmp ( parent , shortname )) {
|
|
if ( parent [strlen (parent)-1] == '/' )
|
|
sprintf(newparent, "%s%s", parent, shortname);
|
|
else
|
|
sprintf(newparent, "%s/%s", parent, shortname);
|
|
if (chdir(shortname) < 0) {
|
|
perror(shortname);
|
|
return;
|
|
}
|
|
} else
|
|
strcpy ( newparent , parent ) ;
|
|
|
|
if ((dirp = opendir(".")) == NULL) {
|
|
fprintf(stderr, MSGSTR( EDIRREAD,
|
|
"tar: %s: directory read error\n" ),
|
|
longname);
|
|
if (chdir(parent) < 0) {
|
|
fprintf(stderr, MSGSTR( ECHBACK,
|
|
"tar: cannot change back: "));
|
|
perror(parent);
|
|
}
|
|
exit_status = 1;
|
|
return;
|
|
}
|
|
while ((dp = readdir(dirp)) != NULL && !term) {
|
|
if (dp->d_ino == 0)
|
|
continue;
|
|
if (!strcmp(".", dp->d_name) ||
|
|
!strcmp("..", dp->d_name))
|
|
continue;
|
|
strcpy(cp, dp->d_name);
|
|
|
|
if ( buf [0] == '/' && strcmp ( &buf[1] , cp ) == 0 )
|
|
putfile(buf, buf, newparent, 1);
|
|
else
|
|
putfile(buf, cp, newparent, 1);
|
|
}
|
|
closedir(dirp);
|
|
if (chdir(parent) < 0) {
|
|
fprintf(stderr, MSGSTR( ECHBACK,
|
|
"tar: cannot change back: "));
|
|
perror(parent);
|
|
exit_status = 1;
|
|
}
|
|
break;
|
|
|
|
case S_IFLNK:
|
|
if ( ! tomodes ( &stbuf , longname , shortname ))
|
|
return;
|
|
if (stbuf.st_size > NAME_SIZE) {
|
|
fprintf(stderr, MSGSTR( ELONGSL,
|
|
"tar: %s: symbolic link too long\n" ),
|
|
longname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
return;
|
|
}
|
|
i = readlink(shortname, dblock.dbuf.linkname, NAME_SIZE);
|
|
if (i < 0) {
|
|
fprintf(stderr, MSGSTR( EREADSL,
|
|
"tar: can't read symbolic link: "));
|
|
perror(longname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
return;
|
|
}
|
|
if (i < NAME_SIZE)
|
|
dblock.dbuf.linkname[i] = '\0';
|
|
dblock.dbuf.typeflag = SYMTYPE;
|
|
if (vflag) {
|
|
strncpy(linknamebuf, dblock.dbuf.linkname, NAME_SIZE);
|
|
linknamebuf[NAME_SIZE] = '\0';
|
|
fprintf(vfile, MSGSTR( SLINKTO,
|
|
"a %s symbolic link to %s\n" ),
|
|
longname, linknamebuf);
|
|
}
|
|
sprintf(dblock.dbuf.size, "%011lo ", 0);
|
|
sprintf(dblock.dbuf.chksum, "%06o", checksum());
|
|
(void) writetape((char *)&dblock);
|
|
break;
|
|
|
|
case S_IFREG:
|
|
if ( ! tomodes ( &stbuf , longname , shortname ))
|
|
return;
|
|
if ((infile = open(shortname, O_RDONLY)) < 0) {
|
|
fprintf(stderr, MSGSTR( ETAR, "tar: "));
|
|
perror(longname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
return;
|
|
}
|
|
if(( i = link_file (longname)) < 0) {
|
|
return;
|
|
}
|
|
else if(i) {
|
|
sprintf(dblock.dbuf.size, "%011lo ", 0); /* POSIX */
|
|
sprintf(dblock.dbuf.chksum, "%06o", checksum());
|
|
(void) writetape( (char *) &dblock);
|
|
close(infile);
|
|
return ;
|
|
}
|
|
blocks = (stbuf.st_size + (TBLOCK-1)) / TBLOCK;
|
|
if (vflag)
|
|
fprintf(vfile, MSGSTR( BLKS,
|
|
"a %s %ld blocks\n" ), longname, blocks);
|
|
sprintf(dblock.dbuf.chksum, "%06o", checksum());
|
|
hint = writetape( (char *) &dblock);
|
|
maxread = max(stbuf.st_size, (nblock * TBLOCK));
|
|
if ((bigbuf = (char *) malloc((size_t)maxread)) == 0) {
|
|
maxread = TBLOCK;
|
|
bigbuf = buf;
|
|
}
|
|
|
|
while ((i = read(infile, bigbuf, (unsigned)(min((hint*TBLOCK), maxread)))) > 0
|
|
&& blocks > 0) {
|
|
register int nblks;
|
|
|
|
nblks = ((i-1)/TBLOCK)+1;
|
|
if (nblks > blocks)
|
|
nblks = blocks;
|
|
hint = writetbuf(bigbuf, nblks);
|
|
blocks -= nblks;
|
|
}
|
|
close(infile);
|
|
if (bigbuf != buf)
|
|
free((void *)bigbuf);
|
|
if (i < 0) {
|
|
fprintf(stderr, MSGSTR( EREAD, "tar: Read error on "));
|
|
perror(longname);
|
|
exit_status = 1;
|
|
} else if (blocks != 0 || i != 0) {
|
|
fprintf(stderr, MSGSTR( EFSIZE,
|
|
"tar: %s: file changed size\n" ),
|
|
longname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
}
|
|
while (--blocks >= 0)
|
|
putempty();
|
|
break;
|
|
|
|
case S_IFBLK:
|
|
case S_IFCHR:
|
|
case S_IFIFO:
|
|
/* special files - write header */
|
|
if ( dflag ) {
|
|
stbuf.st_size = 0;
|
|
if ( ! tomodes ( &stbuf , longname , shortname ))
|
|
return;
|
|
if ( vflag && ( link_file (longname)) == 0)
|
|
fprintf(vfile, "a %s\n", longname);
|
|
sprintf(dblock.dbuf.chksum, "%06o", checksum());
|
|
(void) writetape( (char *) &dblock);
|
|
break ;
|
|
}
|
|
|
|
default:
|
|
fprintf(stderr, MSGSTR( ENOARCH,
|
|
"tar: %s could not be archived\n" ), longname);
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static link_file (longname)
|
|
char *longname;
|
|
{
|
|
struct linkbuf *lp;
|
|
int found = 0;
|
|
|
|
if (stbuf.st_nlink > 1) {
|
|
|
|
for (lp = ihead; lp != NULL; lp = lp->nextp)
|
|
if (lp->inum == stbuf.st_ino &&
|
|
lp->devnum == stbuf.st_dev) {
|
|
found++;
|
|
break;
|
|
}
|
|
if (found) {
|
|
/* If the linked to filename is > NAME_SIZE, lp->pathname will
|
|
be empty. */
|
|
if(lp->pathname[0] == '\0') {
|
|
fprintf(stderr, MSGSTR( ELONGLN,
|
|
"tar: %s linked to a file with a long pathname\n" ),
|
|
longname);
|
|
return(-1);
|
|
}
|
|
|
|
strncpy(dblock.dbuf.linkname, lp->pathname , (size_t)NAME_SIZE );
|
|
dblock.dbuf.typeflag = LNKTYPE;
|
|
if (vflag)
|
|
fprintf(vfile, MSGSTR( LINKTO,
|
|
"a %s link to %s\n" ),
|
|
longname, lp->pathname);
|
|
lp->count--;
|
|
}
|
|
else {
|
|
lp = (struct linkbuf *) getmem(sizeof(*lp));
|
|
if (lp != NULL) {
|
|
lp->nextp = ihead;
|
|
ihead = lp;
|
|
lp->inum = stbuf.st_ino;
|
|
lp->devnum = stbuf.st_dev;
|
|
lp->count = stbuf.st_nlink - 1;
|
|
/* Not allowed to link to this file if its name
|
|
is > NAME_SIZE */
|
|
if((strlen(longname)) <= NAME_SIZE)
|
|
strcpy(lp->pathname, longname);
|
|
else
|
|
lp->pathname[0] = '\0';
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return ( found ) ;
|
|
|
|
} /* end link_file */
|
|
|
|
|
|
static
|
|
doxtract(argv)
|
|
char *argv[];
|
|
{
|
|
long blocks, bytes;
|
|
int ofile, i;
|
|
int ftype ;
|
|
char *rname;
|
|
char linknamebuf[NAME_SIZE + 1];
|
|
|
|
/*
|
|
* if ('-p' flag) or (root user) then ignore umask.
|
|
* NOTE: 'mask' variable is needed for intermediate
|
|
* directory creation, even when umask is ignored.
|
|
*/
|
|
mask = umask(0);
|
|
if (!pflag && getuid())
|
|
umask(mask);
|
|
for (;;) {
|
|
if ((i = wantit(argv)) == 0)
|
|
continue;
|
|
if (i == -1)
|
|
break; /* end of tape */
|
|
if (checkw(MSGSTR(DOEXTR, "x %s: "), full_name) == 0) {
|
|
passtape();
|
|
continue;
|
|
}
|
|
if (Fflag) {
|
|
char *s;
|
|
|
|
if ((s = rindex(full_name, '/')) == 0)
|
|
s = full_name;
|
|
else
|
|
s++;
|
|
if (checkf(s, stbuf.st_mode) == 0) {
|
|
passtape();
|
|
continue;
|
|
}
|
|
}
|
|
checkdir();
|
|
if(dblock.dbuf.typeflag == DIRTYPE) {
|
|
if (access(full_name, 0) < 0) {
|
|
if (mkdir(full_name,stbuf.st_mode) < 0) {
|
|
perror(full_name);
|
|
return (0);
|
|
}
|
|
}
|
|
if (!oflag && is_root)
|
|
chown(full_name, stbuf.st_uid, stbuf.st_gid);
|
|
if (vflag)
|
|
fprintf(vfile, "x %s\n", full_name ) ;
|
|
if (mflag == 0)
|
|
dodirtimes();
|
|
continue;
|
|
}
|
|
|
|
if (dblock.dbuf.typeflag == SYMTYPE) { /* symlink */
|
|
strncpy(linknamebuf, dblock.dbuf.linkname, NAME_SIZE);
|
|
linknamebuf[NAME_SIZE] = '\0';
|
|
/*
|
|
* only unlink non directories or empty
|
|
* directories
|
|
*/
|
|
if (rmdir(full_name) < 0) {
|
|
if (errno == ENOTDIR)
|
|
unlink(full_name);
|
|
}
|
|
if (symlink(linknamebuf, full_name)<0) {
|
|
fprintf(stderr, MSGSTR( ESYMFAIL,
|
|
"tar: %s: symbolic link failed: " ),
|
|
full_name);
|
|
perror("");
|
|
exit_status = 1;
|
|
continue;
|
|
}
|
|
if (vflag)
|
|
fprintf(vfile, MSGSTR( XSYMLINK,
|
|
"x %s symbolic link to %s\n" ),
|
|
full_name, linknamebuf);
|
|
#ifndef _BLD
|
|
/* The '#ifndef' is here so that the build environment
|
|
* version of tar will compile. lchown() is not
|
|
* available to all build environments.
|
|
*/
|
|
if (!oflag && is_root)
|
|
lchown(full_name, stbuf.st_uid, stbuf.st_gid);
|
|
#endif /* _BLD */
|
|
/* ignore alien orders */
|
|
/* Commented out (as is BSD 4.3) since modifications
|
|
are to linked to file, not link itself. */
|
|
/*
|
|
if (mflag == 0)
|
|
setimes(full_name, stbuf.st_mtime);
|
|
*/
|
|
continue;
|
|
}
|
|
if (dblock.dbuf.typeflag == LNKTYPE) { /* regular link */
|
|
char link_type ;
|
|
|
|
strncpy(linknamebuf, dblock.dbuf.linkname, NAME_SIZE);
|
|
linknamebuf[NAME_SIZE] = '\0';
|
|
/*
|
|
* only unlink non directories or empty
|
|
* directories
|
|
*/
|
|
if (rmdir(full_name) < 0) {
|
|
if (errno == ENOTDIR)
|
|
unlink(full_name);
|
|
}
|
|
if (link(linknamebuf, full_name) < 0) {
|
|
if ( !sflag ) {
|
|
fprintf(stderr, MSGSTR( EFLINK,
|
|
"tar: can't link %s to %s: " ),
|
|
full_name, linknamebuf);
|
|
perror("");
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
continue;
|
|
}
|
|
else
|
|
rname = rel_name(linknamebuf, full_name);
|
|
if ( symlink(rname, full_name)<0) {
|
|
fprintf(stderr, MSGSTR (ESYMFAIL,
|
|
|
|
"tar: %s: symbolic link failed: " ),
|
|
full_name);
|
|
perror("");
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
continue;
|
|
}
|
|
else {
|
|
struct stat lk_stat ;
|
|
if ( stat ( linknamebuf , &lk_stat ) < 0 ) {
|
|
if (!oflag && is_root)
|
|
chown(full_name, lk_stat.st_uid, lk_stat.st_gid);
|
|
}
|
|
link_type = SYMTYPE ;
|
|
}
|
|
}
|
|
else
|
|
link_type = LNKTYPE ;
|
|
|
|
if (vflag)
|
|
if ( link_type == LNKTYPE )
|
|
fprintf(vfile, MSGSTR( LINKEDTO,
|
|
"%s linked to %s\n" ),
|
|
full_name, linknamebuf);
|
|
else
|
|
fprintf(vfile, MSGSTR( XSYMLINK,
|
|
"x %s symbolic link to %s\n" ),
|
|
full_name, linknamebuf);
|
|
continue;
|
|
}
|
|
|
|
/* check for special files */
|
|
if (( ftype = stbuf.st_mode & S_IFMT ) == S_IFIFO ||
|
|
ftype == S_IFBLK || ftype == S_IFCHR ) {
|
|
if ( mknod ( full_name , (int) stbuf.st_mode ,
|
|
decode ()) < 0 ) {
|
|
eprintf ( MSGSTR( EMKNOD,
|
|
"cannot mknod %s\n" ), full_name ) ;
|
|
continue ;
|
|
}
|
|
if (!oflag && is_root)
|
|
chown(full_name, stbuf.st_uid, stbuf.st_gid);
|
|
if (vflag)
|
|
fprintf(vfile, "x %s\n", full_name ) ;
|
|
continue ;
|
|
}
|
|
|
|
if ((ofile = creat(full_name,stbuf.st_mode)) < 0) {
|
|
fprintf(stderr, MSGSTR( ETCREATE,
|
|
"tar: can't create %s: " ),
|
|
full_name);
|
|
perror("");
|
|
exit_status = 1;
|
|
passtape();
|
|
continue;
|
|
}
|
|
if (!oflag && is_root)
|
|
chown(full_name, stbuf.st_uid, stbuf.st_gid);
|
|
blocks = ((bytes = stbuf.st_size) + TBLOCK-1)/TBLOCK;
|
|
if (vflag)
|
|
fprintf(vfile, MSGSTR( XSTAT,
|
|
"x %s, %ld bytes, %ld tape blocks\n" ),
|
|
full_name, bytes, blocks);
|
|
for (; blocks > 0;) {
|
|
register int nread;
|
|
char *bufp;
|
|
register int nwant;
|
|
|
|
nwant = NBLOCK*TBLOCK;
|
|
if (nwant > (blocks*TBLOCK))
|
|
nwant = (blocks*TBLOCK);
|
|
nread = readtbuf(&bufp, nwant);
|
|
if (write(ofile, bufp, (unsigned)min(nread, bytes)) < 0) {
|
|
fprintf(stderr, MSGSTR( EXWRITE,
|
|
"tar: %s: Cannot write extracted data: "),
|
|
full_name);
|
|
perror("");
|
|
done(1);
|
|
}
|
|
bytes -= nread;
|
|
blocks -= (((nread-1)/TBLOCK)+1);
|
|
}
|
|
if (close(ofile) < 0) {
|
|
fprintf(stderr, MSGSTR( EXWRITE,
|
|
"tar: %s: Cannot write extracted data: "),
|
|
full_name);
|
|
perror("");
|
|
done(1);
|
|
}
|
|
if (mflag == 0)
|
|
setimes(full_name, stbuf.st_mtime);
|
|
}
|
|
if (mflag == 0) {
|
|
full_name[0] = '\0'; /* process the whole stack */
|
|
dodirtimes();
|
|
}
|
|
}
|
|
|
|
|
|
/* The function decode will return a value which can be
|
|
* used in the function call mknod. The value is extracted
|
|
* from the dblock major and minor character strings.
|
|
*/
|
|
static
|
|
decode ()
|
|
{
|
|
int dev_min , dev_maj ;
|
|
|
|
sscanf(dblock.dbuf.devminor, "%0o", &dev_min);
|
|
sscanf(dblock.dbuf.devmajor, "%0o", &dev_maj);
|
|
return ( makedev ( dev_maj , dev_min )) ;
|
|
} /* end decode */
|
|
|
|
|
|
static
|
|
dotable(argv)
|
|
char *argv[];
|
|
{
|
|
register int i;
|
|
char linknamebuf[NAME_SIZE + 1];
|
|
|
|
for (;;) {
|
|
if ((i = wantit(argv)) == 0)
|
|
continue;
|
|
if (i == -1)
|
|
break; /* end of tape */
|
|
if (vflag)
|
|
longt(&stbuf);
|
|
|
|
fputs(full_name,stdout);
|
|
|
|
if (dblock.dbuf.typeflag == LNKTYPE) {
|
|
strncpy(linknamebuf, dblock.dbuf.linkname, NAME_SIZE);
|
|
linknamebuf[NAME_SIZE] = '\0';
|
|
printf(MSGSTR(LINKED, " linked to %s" ), linknamebuf);
|
|
}
|
|
if (dblock.dbuf.typeflag == SYMTYPE) {
|
|
strncpy(linknamebuf, dblock.dbuf.linkname, NAME_SIZE);
|
|
linknamebuf[NAME_SIZE] = '\0';
|
|
printf(MSGSTR(SLINKED, " symbolic link to %s" ), linknamebuf);
|
|
}
|
|
|
|
fputc('\n',stdout);
|
|
passtape();
|
|
}
|
|
}
|
|
|
|
static putempty()
|
|
{
|
|
char buf[TBLOCK];
|
|
|
|
bzero(buf, sizeof (buf));
|
|
(void) writetape(buf);
|
|
}
|
|
|
|
static longt(st)
|
|
register struct stat *st;
|
|
{
|
|
char *ctime(), timbuf[26];
|
|
|
|
pmode(st);
|
|
printf(" %3d %-2d", st->st_uid, st->st_gid);
|
|
printf(" %7ld ", st->st_size);
|
|
(void)ctime(&st->st_mtime);
|
|
strftime(timbuf,(size_t)26,"%sD %T %Y",localtime(&st->st_mtime));
|
|
printf("%s ",timbuf);
|
|
}
|
|
|
|
/* These constants are defined in tar.h
|
|
*/
|
|
static int m1[] = { 1, TUREAD, 'r', '-' };
|
|
static int m2[] = { 1, TUWRITE, 'w', '-' };
|
|
static int m3[] = { 2, TSUID, 's', TUEXEC, 'x', '-' };
|
|
static int m4[] = { 1, TGREAD, 'r', '-' };
|
|
static int m5[] = { 1, TGWRITE, 'w', '-' };
|
|
static int m6[] = { 2, TSGID, 's', TGEXEC, 'x', '-' };
|
|
static int m7[] = { 1, TOREAD, 'r', '-' };
|
|
static int m8[] = { 1, TOWRITE, 'w', '-' };
|
|
static int m9[] = { 2, TSVTX, 't', TOEXEC, 'x', '-' };
|
|
|
|
static int *m[] = { m1, m2, m3, m4, m5, m6, m7, m8, m9};
|
|
|
|
static pmode(st)
|
|
register struct stat *st;
|
|
{
|
|
char c ;
|
|
register int **mp;
|
|
|
|
switch ( st->st_mode & S_IFMT ) {
|
|
case S_IFREG :
|
|
c = '-' ; break ;
|
|
case S_IFDIR :
|
|
c = 'd' ; break ;
|
|
case S_IFLNK :
|
|
c = 'l' ; break ;
|
|
case S_IFCHR :
|
|
c = 'c' ; break ;
|
|
case S_IFBLK :
|
|
c = 'b' ; break ;
|
|
case S_IFIFO :
|
|
c = 'p' ; break ;
|
|
default :
|
|
c = '?' ; break ;
|
|
}
|
|
putchar (c) ;
|
|
for (mp = &m[0]; mp < &m[9];)
|
|
selectbits(*mp++, st);
|
|
}
|
|
|
|
static selectbits(pairp, st)
|
|
int *pairp;
|
|
struct stat *st;
|
|
{
|
|
register int n, *ap;
|
|
|
|
ap = pairp;
|
|
n = *ap++;
|
|
while (--n>=0 && (st->st_mode&*ap++)==0)
|
|
ap++;
|
|
putchar(*ap);
|
|
}
|
|
|
|
/*
|
|
* Make all directories needed by `name'.
|
|
* The path name is created by concatenating dblock.dbuf.prefix
|
|
* and dblock.dbuf.name together in the function getdir.
|
|
*/
|
|
static
|
|
checkdir()
|
|
{
|
|
#define ROOT "/"
|
|
register char *cp;
|
|
|
|
/* Strip trailing slash. */
|
|
cp = full_name + strlen(full_name) - 1;
|
|
while ((cp > full_name) && (*cp == '/'))
|
|
cp--;
|
|
cp[1] = '\0';
|
|
/*
|
|
* Quick check for existence of directory, needed for older versions.
|
|
*/
|
|
if ( strcmp ( full_name , ROOT ) == 0 )
|
|
return;
|
|
if ((cp = rindex(full_name, '/')) == 0)
|
|
return;
|
|
*cp = '\0';
|
|
if (access(full_name, 0) == 0) { /* already exists */
|
|
*cp = '/';
|
|
return;
|
|
}
|
|
*cp = '/';
|
|
|
|
/*
|
|
* No luck, try to make all directories in path.
|
|
*/
|
|
cp = full_name;
|
|
while (*cp == '/')
|
|
cp++;
|
|
for (; *cp; cp++) {
|
|
if (*cp != '/')
|
|
continue;
|
|
*cp = '\0';
|
|
if (access(full_name, 0) < 0) {
|
|
if (mkdir(full_name, ~mask & (mode_t)0777) < 0) {
|
|
perror(full_name);
|
|
*cp = '/';
|
|
return;
|
|
}
|
|
if (!oflag && is_root)
|
|
chown(full_name, stbuf.st_uid, stbuf.st_gid);
|
|
}
|
|
*cp = '/';
|
|
}
|
|
return;
|
|
}
|
|
|
|
static onintr(void)
|
|
{
|
|
(void) signal(SIGINT, SIG_IGN);
|
|
term++;
|
|
}
|
|
|
|
static onquit(void)
|
|
{
|
|
(void) signal(SIGQUIT, SIG_IGN);
|
|
term++;
|
|
}
|
|
|
|
static onhup(void)
|
|
{
|
|
(void) signal(SIGHUP, SIG_IGN);
|
|
term++;
|
|
}
|
|
|
|
static onterm(void)
|
|
{
|
|
(void) signal(SIGTERM, SIG_IGN);
|
|
term++;
|
|
}
|
|
|
|
|
|
/* The function to modes will fill in the header
|
|
* information for a file.
|
|
*/
|
|
static tomodes(sp, longname , shortname )
|
|
register struct stat *sp;
|
|
char *longname ;
|
|
char *shortname ;
|
|
{
|
|
register char *cp;
|
|
char *slash ;
|
|
int ftype ;
|
|
struct passwd *pw_entry ;
|
|
struct group *gr_entry ;
|
|
int nameLen; /* length of longname */
|
|
register char *nameBkpt; /* Name break point */
|
|
|
|
for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
|
|
*cp = '\0';
|
|
|
|
nameLen = strlen(longname);
|
|
if ( nameLen <= NAME_SIZE ) {
|
|
strncpy ( dblock.dbuf.name , longname , (size_t)NAME_SIZE );
|
|
dblock.dbuf.prefix[0] = '\0';
|
|
} else {
|
|
/*
|
|
* Put as much into prefix as possible and the rest into name, but
|
|
* only break on a slash.
|
|
*/
|
|
nameBkpt = longname + nameLen - 1;
|
|
while (*nameBkpt == '/') /* in case of trailing "/" */
|
|
--nameBkpt;
|
|
if (nameLen <= PREFIX_SIZE) {
|
|
/*
|
|
* Since total name length is less than prefix, put only
|
|
* the basename in the name field.
|
|
*/
|
|
while (longname != nameBkpt && *nameBkpt != '/')
|
|
--nameBkpt;
|
|
} else
|
|
while (*nameBkpt != '/' || (nameBkpt-longname) >= PREFIX_SIZE) {
|
|
if (longname == nameBkpt)
|
|
break;
|
|
--nameBkpt;
|
|
}
|
|
if (longname == nameBkpt) {
|
|
/* one long name > 100 bytes, or prefix > 155 bytes. */
|
|
fprintf(stderr, MSGSTR( ETLONGFN,
|
|
"tar: %s: file name too long\n" ), longname);
|
|
return (FALSE) ;
|
|
} else
|
|
slash = nameBkpt;
|
|
if (strlen(slash+1) > NAME_SIZE) {
|
|
fprintf(stderr, MSGSTR( ETLONGFN,
|
|
"tar: %s: file name too long\n" ), longname);
|
|
return (FALSE) ;
|
|
}
|
|
if ( ('/' == *slash) && ( (slash-longname) < PREFIX_SIZE ) ){
|
|
strncpy( dblock.dbuf.name , slash+1 , (size_t)NAME_SIZE );
|
|
strncpy( dblock.dbuf.prefix , longname, (size_t)PREFIX_SIZE );
|
|
dblock.dbuf.prefix[slash-longname] = '\0';
|
|
|
|
} else {
|
|
fprintf(stderr, MSGSTR( ETLONGFN,
|
|
"tar: %s: file name too long\n" ), longname);
|
|
return (FALSE) ;
|
|
}
|
|
}
|
|
|
|
sprintf(dblock.dbuf.mode, "%06o ", sp->st_mode & (07777 | S_IFMT));
|
|
sprintf(dblock.dbuf.uid, "%06o ", sp->st_uid);
|
|
sprintf(dblock.dbuf.gid, "%06o ", sp->st_gid);
|
|
sprintf(dblock.dbuf.size, "%011lo ", sp->st_size);
|
|
sprintf(dblock.dbuf.mtime, "%011lo ", sp->st_mtime);
|
|
|
|
strncpy (dblock.dbuf.magic, TMAGIC , (size_t)TMAGLEN ) ;
|
|
strncpy (dblock.dbuf.version, TVERSION , (size_t)TVERSLEN ) ;
|
|
|
|
ftype = sp -> st_mode & S_IFMT ;
|
|
|
|
/* set file type */
|
|
switch ( ftype ) {
|
|
case S_IFIFO :
|
|
dblock.dbuf.typeflag = FIFOTYPE ;
|
|
break ;
|
|
|
|
case S_IFCHR :
|
|
dblock.dbuf.typeflag = CHRTYPE ;
|
|
break ;
|
|
|
|
case S_IFBLK :
|
|
dblock.dbuf.typeflag = BLKTYPE ;
|
|
break ;
|
|
|
|
case S_IFDIR :
|
|
{
|
|
int len ;
|
|
if (( len = strlen ( dblock.dbuf.name )) < NAME_SIZE) {
|
|
if ( dblock.dbuf.name [len-1] != '/' )
|
|
dblock.dbuf.name [len] = '/' ;
|
|
} else {
|
|
fprintf(stderr, MSGSTR( ETLONGFN,
|
|
"tar: %s: file name too long\n" ), longname);
|
|
return (FALSE) ;
|
|
}
|
|
dblock.dbuf.typeflag = DIRTYPE ;
|
|
break ;
|
|
}
|
|
|
|
case S_IFREG :
|
|
dblock.dbuf.typeflag = REGTYPE ;
|
|
break ;
|
|
|
|
}
|
|
|
|
/* set uname and gname */
|
|
uid_to_name(sp->st_uid, &dblock.dbuf.uname);
|
|
gid_to_name(sp->st_gid, &dblock.dbuf.gname);
|
|
|
|
/* set major and minor values */
|
|
if ( ftype == S_IFIFO || ftype == S_IFBLK || ftype == S_IFCHR ) {
|
|
sprintf(dblock.dbuf.devminor, "%06o " , minor(sp->st_rdev));
|
|
sprintf(dblock.dbuf.devmajor, "%06o " , major(sp->st_rdev));
|
|
}
|
|
else { /* POSIX requires the 'right stuff' for all types */
|
|
sprintf(dblock.dbuf.devminor, "%06o " , 0);
|
|
sprintf(dblock.dbuf.devmajor, "%06o " , 0);
|
|
}
|
|
|
|
|
|
return (TRUE) ;
|
|
}
|
|
|
|
|
|
/* The checksum function will create a checksum for
|
|
* the header of a file written to the output file.
|
|
* To alleviate problems with previously created AIX
|
|
* checksums, tar will now check for signed checksums if
|
|
* the unsigned checksum failed to compare the same.
|
|
*/
|
|
static
|
|
signed_checksum()
|
|
{
|
|
register i;
|
|
register signed char *cp;
|
|
|
|
for (cp = dblock.dbuf.chksum;
|
|
cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
|
|
*cp = ' ';
|
|
i = 0;
|
|
for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
|
|
i += *cp;
|
|
return (i);
|
|
}
|
|
|
|
static
|
|
checksum()
|
|
{
|
|
register i;
|
|
register unsigned char *cp;
|
|
|
|
for (cp = dblock.dbuf.chksum;
|
|
cp < &dblock.dbuf.chksum[sizeof(dblock.dbuf.chksum)]; cp++)
|
|
*cp = ' ';
|
|
i = 0;
|
|
for (cp = dblock.dummy; cp < &dblock.dummy[TBLOCK]; cp++)
|
|
i += *cp;
|
|
return (i);
|
|
}
|
|
|
|
static
|
|
checkw(query_fmt, name)
|
|
char *query_fmt, *name;
|
|
{
|
|
int retval;
|
|
char answer[LINE_MAX+1];
|
|
|
|
if (!wflag)
|
|
return (1);
|
|
printf(query_fmt, name);
|
|
fgets(answer,LINE_MAX+1,response_file);
|
|
/* return 1 for an affirmative answer, */
|
|
/* and return 0 for any other answer. */
|
|
retval = rpmatch(answer);
|
|
return ((retval == 1) ? 1 : 0);
|
|
}
|
|
|
|
static
|
|
checkf(name, mode)
|
|
char *name;
|
|
int mode;
|
|
{
|
|
int l;
|
|
|
|
if ((mode & S_IFMT) == S_IFDIR){
|
|
if ((strcmp(name, "SCCS")==0) || (strcmp(name, "RCS")==0))
|
|
return(0);
|
|
return(1);
|
|
}
|
|
if ((l = strlen(name)) < 3)
|
|
return (1);
|
|
if (name[l-2] == '.' && name[l-1] == 'o')
|
|
return (0);
|
|
if (strcmp(name, "core") == 0 ||
|
|
strcmp(name, "errs") == 0 ||
|
|
(strcmp(name, "a.out") == 0))
|
|
return (0);
|
|
/* SHOULD CHECK IF IT IS EXECUTABLE */
|
|
return (1);
|
|
}
|
|
|
|
/* Is the current file a new file, or the newest one of the same name? */
|
|
static
|
|
checkupdate(arg)
|
|
char *arg;
|
|
{
|
|
long mtime;
|
|
daddr_t seekp;
|
|
daddr_t lookup();
|
|
|
|
rewind(tfile);
|
|
if ((seekp = lookup(arg)) < 0)
|
|
return (1);
|
|
fseek(tfile, seekp, 0);
|
|
fscanf(tfile, "%*s %lo", &mtime);
|
|
return (stbuf.st_mtime > mtime);
|
|
}
|
|
|
|
static
|
|
done(n)
|
|
{
|
|
unlink(tname);
|
|
exit(n);
|
|
}
|
|
|
|
/*
|
|
* Do we want the next entry on the tape, i.e. is it selected? If
|
|
* not, skip over the entire entry. Return -1 if reached end of tape.
|
|
*/
|
|
static wantit(argv)
|
|
char *argv[];
|
|
{
|
|
register char **cp;
|
|
|
|
getdir() ;
|
|
if (endtape())
|
|
return (-1);
|
|
if (*argv == 0)
|
|
return (1);
|
|
for (cp = argv; *cp; cp++)
|
|
if (prefix(*cp, full_name))
|
|
return (1);
|
|
passtape();
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Does s2 begin with the string s1, on a directory boundary?
|
|
*/
|
|
static prefix(s1, s2)
|
|
register char *s1, *s2;
|
|
{
|
|
while (*s1)
|
|
if (*s1++ != *s2++)
|
|
return (0);
|
|
if (*s2)
|
|
return (*s2 == '/');
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* This value is the length of the name being looked for plus 12 bytes for the
|
|
* "mtime" field and 3 bytes for the space, newline, and NULL characters.
|
|
*/
|
|
#define N (TAR_PATH_LEN+15)
|
|
|
|
static daddr_t
|
|
lookup(s)
|
|
char *s;
|
|
{
|
|
register i;
|
|
|
|
for(i=0; s[i]; i++)
|
|
if (s[i] == ' ')
|
|
break;
|
|
return (bsrch(s, i, 0, high));
|
|
}
|
|
|
|
static daddr_t
|
|
bsrch(s, n, l, h)
|
|
char *s;
|
|
register n;
|
|
daddr_t l, h;
|
|
{
|
|
register i, j, numread;
|
|
char b[N], hold;
|
|
daddr_t m, m1;
|
|
|
|
if (l >= h)
|
|
return ((daddr_t) -1);
|
|
m = l + (h-l)/2 - N; /* Get midpoint */
|
|
if (m < l)
|
|
m = l;
|
|
fseek(tfile, m, 0);
|
|
fgets(b, N, tfile); /* read to next newline */
|
|
m += strlen(b);
|
|
if (m >= h)
|
|
return ((daddr_t) -1);
|
|
fgets(b, N, tfile); /* Get comparison string */
|
|
m1 = m + strlen(b);
|
|
hold = b[n]; /* This is done because there is no */
|
|
b[n] = '\0'; /* strncoll() routine */
|
|
i = strcoll(s, b);
|
|
if ((i == 0) && (hold != ' ')) /* if different length names... */
|
|
i = -1;
|
|
if (i < 0)
|
|
return(bsrch(s, n, l, m));
|
|
else if (i > 0)
|
|
return(bsrch(s, n, m1, h));
|
|
|
|
return (m);
|
|
}
|
|
|
|
|
|
static readtape(buffer)
|
|
char *buffer;
|
|
{
|
|
char *bufp;
|
|
|
|
if (first == 0)
|
|
getbuf();
|
|
(void) readtbuf(&bufp, TBLOCK);
|
|
bcopy(bufp, buffer, TBLOCK);
|
|
return(TBLOCK);
|
|
}
|
|
|
|
static readtbuf(bufpp, size)
|
|
char **bufpp;
|
|
int size;
|
|
{
|
|
register int i;
|
|
|
|
if (recno >= nblock || first == 0) {
|
|
if ( rptape && trecs >= rptape )
|
|
rflag ? nexttape(O_RDWR) : nexttape(O_RDONLY) ;
|
|
i = bread((char *)tbuf, TBLOCK*nblock) ;
|
|
trecs++ ;
|
|
if (first == 0) {
|
|
if ( i == 0 ) {
|
|
fprintf(stderr, MSGSTR( ETREADEOF,
|
|
"tar: tape read error: unexpected EOF\n"));
|
|
done(1);
|
|
}
|
|
else if ((i % TBLOCK) != 0) {
|
|
fprintf(stderr, MSGSTR( EBLKSIZE,
|
|
"tar: tape blocksize error\n"));
|
|
done(1);
|
|
}
|
|
i /= TBLOCK;
|
|
if (i != nblock) {
|
|
if ( arch_file_type != S_IFSOCK )
|
|
fprintf(stderr, MSGSTR( BLKSIZE,
|
|
"tar: blocksize = %d\n" ), i);
|
|
exit_status = 1;
|
|
nblock = i;
|
|
}
|
|
first = 1;
|
|
}
|
|
recno = 0;
|
|
}
|
|
if (size > ((nblock-recno)*TBLOCK))
|
|
size = (nblock-recno)*TBLOCK;
|
|
*bufpp = (char *)&tbuf[recno];
|
|
recno += (size/TBLOCK);
|
|
return (size);
|
|
}
|
|
|
|
static writetbuf(buffer, n)
|
|
register char *buffer;
|
|
register int n;
|
|
{
|
|
int i;
|
|
|
|
if (first == 0) {
|
|
getbuf();
|
|
first = 1;
|
|
}
|
|
if (recno >= nblock)
|
|
bwrite ((char *) tbuf) ;
|
|
|
|
/*
|
|
* Special case: We have an empty tape buffer, and the
|
|
* users data size is >= the tape block size: Avoid
|
|
* the bcopy and dma direct to tape. BIG WIN. Add the
|
|
* residual to the tape buffer.
|
|
*/
|
|
while (recno == 0 && n >= nblock) {
|
|
bwrite (buffer) ;
|
|
n -= nblock;
|
|
buffer += (nblock * TBLOCK);
|
|
}
|
|
|
|
while (n-- > 0) {
|
|
bcopy(buffer, (char *)&tbuf[recno++], TBLOCK);
|
|
buffer += TBLOCK;
|
|
if (recno >= nblock) {
|
|
bwrite ((char *) tbuf) ;
|
|
recno = 0;
|
|
}
|
|
}
|
|
|
|
/* Tell the user how much to write to get in sync */
|
|
return (nblock - recno);
|
|
}
|
|
|
|
/*
|
|
* If tape device is in variable mode, writes are all
|
|
* or nothing, no fragments. But if the tape device
|
|
* is in fixed block mode, then at the end of the tape,
|
|
* we could encounter partial writes. When we get a
|
|
* partial write, we will attempt to write again to see
|
|
* if it is indeed EOT. Exit if not.
|
|
*/
|
|
|
|
static
|
|
bwrite (buffer)
|
|
char *buffer ;
|
|
{
|
|
int wr, frag ;
|
|
if ( rptape && trecs >= rptape )
|
|
nexttape (O_WRONLY) ;
|
|
frag = 0;
|
|
tryagain:
|
|
wr = write(mt, buffer+frag, (unsigned)(TBLOCK*nblock - frag)) ;
|
|
if ( wr == -1 ) {
|
|
if ( errno == ENXIO ) {
|
|
nexttape (O_WRONLY) ;
|
|
goto tryagain ;
|
|
}
|
|
else {
|
|
perror ( MSGSTR( ETWRITE, "tar: tape write error" )) ;
|
|
done (1) ;
|
|
}
|
|
}else
|
|
/* need to differentiate media error from End of Tape */
|
|
if (wr != (TBLOCK*nblock - frag)) {
|
|
frag = wr;
|
|
/* try the write again */
|
|
wr = write(mt, buffer+frag, (unsigned)(TBLOCK*nblock - frag));
|
|
if (wr < 0 && errno == ENXIO) {
|
|
nexttape (O_WRONLY);
|
|
goto tryagain;
|
|
} else {
|
|
fprintf(stderr, MSGSTR( ETWREOF,
|
|
"tar: tape write error: unexpected EOF\n" ));
|
|
done(1);
|
|
}
|
|
}
|
|
++trecs ;
|
|
|
|
}
|
|
|
|
|
|
static
|
|
bread(buf, size)
|
|
char *buf;
|
|
int size;
|
|
{
|
|
int count;
|
|
static int lastread = 0;
|
|
|
|
/*** the for loop is for a communications
|
|
link by specifing the 'B' option,
|
|
all other will exit the for loop
|
|
after the first read unless prompted
|
|
to enter a new device ***/
|
|
|
|
for (count = 0; count < size; count += lastread) {
|
|
lastread = read(mt, buf, (unsigned)(size - count));
|
|
switch ( lastread )
|
|
{
|
|
case -1 :
|
|
fprintf(stderr, MSGSTR( ETREAD, "tar: tape read error: " )) ;
|
|
perror("");
|
|
done (1) ;
|
|
|
|
case 0 :
|
|
if((arch_file_type == S_IFIFO) ||
|
|
(arch_file_type == S_IFSOCK) ||
|
|
(arch_file_type == S_IFREG))
|
|
return(count);/* Input from pipe => End of archive*/
|
|
/* prompt for new device */
|
|
rflag ? nexttape(O_RDWR) : nexttape(O_RDONLY);
|
|
break ;
|
|
|
|
default :
|
|
if ( !Bflag ) {
|
|
if (lastread != size)
|
|
break;
|
|
else return (lastread);
|
|
}
|
|
break ;
|
|
}
|
|
buf += lastread;
|
|
}
|
|
return (count);
|
|
}
|
|
|
|
|
|
static char *
|
|
getpwd(buf)
|
|
char *buf;
|
|
{
|
|
if (getwd(buf) == NULL) {
|
|
fprintf(stderr, MSGSTR( ETARS, "tar: %s\n" ), buf);
|
|
exit(1);
|
|
}
|
|
return (buf);
|
|
}
|
|
|
|
static getbuf()
|
|
{
|
|
struct devinfo dev_info ;
|
|
|
|
/* check for DD_DISK as in /dev/fd0 */
|
|
if ( ioctl ( mt , IOCINFO , &dev_info ) != -1 )
|
|
if ( dev_info.devtype == DD_DISK ) {
|
|
if ( ! bptape )
|
|
bptape = dev_info.un.dk.numblks ;
|
|
nblock = dev_info.un.dk.secptrk * 2 ;
|
|
recsize = nblock * TBLOCK ;
|
|
rptape = bptape / nblock ;
|
|
}
|
|
|
|
if (nblock == 0) {
|
|
fstat(mt, &stbuf);
|
|
if ((stbuf.st_mode & S_IFMT) == S_IFCHR)
|
|
nblock = NBLOCK;
|
|
else {
|
|
nblock = stbuf.st_size / TBLOCK;
|
|
if (nblock == 0)
|
|
nblock = NBLOCK;
|
|
}
|
|
}
|
|
/* check Sflag in order to set the
|
|
number of records per tape */
|
|
if ( Sflag )
|
|
if ( bptape )
|
|
rptape = bptape / nblock ;
|
|
else {
|
|
unsigned long len1rec; /* rcd len (100s of inches) */
|
|
len1rec = ((unsigned long) nblock * (TBLOCK * 10000L))
|
|
/ density + GAP;
|
|
rptape = (tapelen * TAPE_FOOT) / len1rec;
|
|
}
|
|
|
|
/* do not malloc the whole file since this bogs the */
|
|
/* system down or fails when filesize > avail memory */
|
|
if ((nblock > MAXBLOCK) & !Nflag & !Bflag & !bflag)
|
|
nblock = MAXBLOCK;
|
|
tbuf = (union hblock *)malloc((size_t)nblock*TBLOCK);
|
|
if (tbuf == NULL) {
|
|
fprintf(stderr, MSGSTR( EBLKBIG,
|
|
"tar: blocksize %d too big, can't get memory\n" ),
|
|
nblock);
|
|
done(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 directory 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, one for each
|
|
* '/' in the path. A negative mtime means no mtime. The mtime's are
|
|
* offset by one (first index 1, not 0) because calling this with a null
|
|
* directory causes mtime[0] to be set.
|
|
*
|
|
* This stack algorithm is not guaranteed to work for tapes created
|
|
* with the 'r' option, 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.
|
|
*/
|
|
static char dirstack[TAR_PATH_LEN];
|
|
#define NTIM (TAR_PATH_LEN/2+1) /* a/b/c/d/... */
|
|
static time_t mtime[NTIM];
|
|
|
|
static
|
|
dodirtimes()
|
|
{
|
|
register char *p = dirstack;
|
|
register char *q = full_name;
|
|
register int ndir = 0;
|
|
char *savp;
|
|
int savndir;
|
|
|
|
/* Find common prefix */
|
|
while (*p == *q) {
|
|
if (*p++ == '/')
|
|
++ndir;
|
|
q++;
|
|
}
|
|
|
|
savp = p;
|
|
savndir = ndir;
|
|
while (*p) {
|
|
/*
|
|
* Not a child: unwind the stack, setting the times.
|
|
* The order we do this doesn't matter, so we go "forward."
|
|
*/
|
|
if (*p++ == '/')
|
|
if (mtime[++ndir] >= 0) {
|
|
*--p = '\0'; /* zap the slash */
|
|
setimes(dirstack, mtime[ndir]);
|
|
*p++ = '/';
|
|
}
|
|
}
|
|
p = savp;
|
|
ndir = savndir;
|
|
|
|
/* Push this one on the "stack" */
|
|
while (*p = *q++) /* append the rest of the new dir */
|
|
if (*p++ == '/')
|
|
mtime[++ndir] = -1;
|
|
mtime[ndir] = stbuf.st_mtime; /* overwrite the last one */
|
|
}
|
|
|
|
static setimes(path, mt)
|
|
char *path;
|
|
time_t mt;
|
|
{
|
|
struct timeval tv[2];
|
|
|
|
tv[0].tv_sec = time((time_t *) 0);
|
|
tv[1].tv_sec = mt;
|
|
tv[0].tv_usec = tv[1].tv_usec = 0;
|
|
if (utimes(path, tv) < 0) {
|
|
fprintf(stderr, MSGSTR( ESETTIME,
|
|
"tar: can't set time on %s: " ), path);
|
|
perror("");
|
|
if (exit_status != 1)
|
|
exit_status = 2;
|
|
}
|
|
}
|
|
|
|
static char *
|
|
getmem(size)
|
|
{
|
|
char *p = (char *) malloc((size_t) size);
|
|
|
|
if (p == NULL && freemem) {
|
|
fprintf(stderr, MSGSTR( EMEM,
|
|
"tar: out of memory, link and directory modtime info lost\n"));
|
|
freemem = 0;
|
|
}
|
|
return (p);
|
|
}
|
|
|
|
|
|
/* move on to the next tape */
|
|
/* mode is 0 for read, 1 for write */
|
|
#include "termios.h"
|
|
static nexttape(mode)
|
|
{
|
|
char c ;
|
|
|
|
if (close(mt) < 0 && mode) {
|
|
perror ( MSGSTR( ETWRITE, "tar: tape write error" )) ;
|
|
done (1) ;
|
|
}
|
|
eprintf ( MSGSTR( BLKSON, "%ld blocks on %s\n" ),
|
|
trecs * nblock , usefile ) ;
|
|
|
|
if (!isatty(0)) {
|
|
eprintf( MSGSTR( EEOT, "tar: ran off end of tape\n"));
|
|
done(1);
|
|
}
|
|
eprintf( MSGSTR( EOT,
|
|
"\007tar: End of tape. Mount next tape on %s and type return."
|
|
), usefile);
|
|
tcflush(0, TCIFLUSH);
|
|
do {
|
|
if (read(0, &c, 1) != 1)
|
|
done(1);
|
|
if (term)
|
|
done(1);
|
|
} while (c != '\n');
|
|
|
|
/* open it again */
|
|
while((mt= open(usefile,mode)) < 0) {
|
|
eprintf( MSGSTR( ETOPEN, cantopen ), usefile);
|
|
eprintf( MSGSTR( CHKDEV, "Check the backup medium and press return to continue.\n"));
|
|
do {
|
|
c = getchar();
|
|
if (term)
|
|
done(1);
|
|
} while (c != '\n');
|
|
}
|
|
trecs = 0;
|
|
|
|
eprintf( MSGSTR( PROCEED, "proceeding to device %s\n" ), usefile);
|
|
|
|
}
|
|
|
|
/*
|
|
**
|
|
** Given source and destination files
|
|
** to make a links from/to, calculate a
|
|
** relative pathname that will successfully
|
|
** link source to dest, ie dest -> source.
|
|
** we ignore leading common path names as they are
|
|
** likely to cause problems and make the symlink longer
|
|
** than it otherwise need be. e.g. ../a/b/c to ../a/c/b
|
|
** will result in a link to ../b/c
|
|
**
|
|
*/
|
|
|
|
static char *rel_name(source, dest)
|
|
char *source, *dest;
|
|
{
|
|
static char buffer[MAXPATHLEN];
|
|
char *s = source, *d = dest, *p = s;
|
|
|
|
if (source[0]=='/') /* Absolute pathname link */
|
|
return(source);
|
|
|
|
buffer[0]=0;
|
|
while (*s && *s == *d) {
|
|
if (*s == '/')
|
|
p = s + 1; /* remember where last / was */
|
|
++s, ++d;
|
|
}
|
|
|
|
dest += p-source;
|
|
source += p-source;
|
|
|
|
while ((*dest++) == '/');
|
|
|
|
while (*dest)
|
|
if ((*dest++)=='/')
|
|
strcat(buffer,"../");
|
|
|
|
strcat(buffer,source);
|
|
return(buffer);
|
|
}
|
|
|
|
/*
|
|
* what follows is a semi-general LRU cache for name::[ug]id mappings.
|
|
*/
|
|
|
|
#if UNAMELEN > GNAMELEN
|
|
# define NLEN UNAMELEN
|
|
#else
|
|
# define NLEN GNAMELEN
|
|
#endif
|
|
#define NCACHE 64
|
|
#define MAP_FREE 0
|
|
#define MAP_UID 1
|
|
#define MAP_GID 2
|
|
#define N_MAPTYPE 3
|
|
|
|
/*
|
|
* cache <name, UID> and <name, GID> mappings
|
|
*/
|
|
static struct mcache {
|
|
int id; /* UID or GID */
|
|
ushort lru; /* LRU clock */
|
|
char type; /* which? */
|
|
char miss; /* flag: !0 => caching a miss */
|
|
char name[NLEN]; /* uid name or group name */
|
|
} mcache[NCACHE];
|
|
|
|
static struct mcache *map_lookup(char *, int, int);
|
|
|
|
/*
|
|
* name_to_uid - map username to UID
|
|
*
|
|
* Input:
|
|
* name - ^ to username
|
|
* uidp - ^ to place to store UID
|
|
*
|
|
* Returns:
|
|
* !0 - OK: mapping exists, *uidp updated
|
|
* 0 - !OK: mapping !exists, *uidp !updated
|
|
*/
|
|
static
|
|
name_to_uid(name, uidp)
|
|
char *name;
|
|
uid_t *uidp; {
|
|
register struct mcache *m;
|
|
|
|
if (*name == 0)
|
|
return 0;
|
|
|
|
m = map_lookup(name, 0, MAP_UID);
|
|
if (!m->miss) {
|
|
*uidp = m->id;
|
|
return !0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* name_to_gid - map username to GID
|
|
*
|
|
* Input:
|
|
* name - ^ to username
|
|
* gidp - ^ to place to store GID
|
|
*
|
|
* Returns:
|
|
* !0 - OK: mapping exists, *gidp updated
|
|
* 0 - !OK: mapping !exists, *gidp !updated
|
|
*/
|
|
static
|
|
name_to_gid(name, gidp)
|
|
char *name;
|
|
gid_t *gidp; {
|
|
register struct mcache *m;
|
|
|
|
if (*name == 0)
|
|
return 0;
|
|
|
|
m = map_lookup(name, 0, MAP_GID);
|
|
if (!m->miss) {
|
|
*gidp = m->id;
|
|
return !0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* uid_to_name - map UID to username
|
|
*
|
|
* Input:
|
|
* uid - user ID
|
|
* name - ^ to place to store username
|
|
*
|
|
* Returns:
|
|
* !0 - OK: mapping exists, name updated
|
|
* 0 - !OK: mapping !exists, name !updated
|
|
*/
|
|
static
|
|
uid_to_name(uid, name)
|
|
char *name;
|
|
uid_t uid; {
|
|
register struct mcache *m;
|
|
|
|
m = map_lookup(0, (int) uid, MAP_UID);
|
|
if (!m->miss) {
|
|
strncpy(name, m->name, UNAMELEN);
|
|
return !0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* gid_to_name - map GID to username
|
|
*
|
|
* Input:
|
|
* gid - group ID
|
|
* name - ^ to place to store groupname
|
|
*
|
|
* Returns:
|
|
* !0 - OK: mapping exists, name updated
|
|
* 0 - !OK: mapping !exists, name !updated
|
|
*/
|
|
static
|
|
gid_to_name(gid, name)
|
|
char *name;
|
|
gid_t gid; {
|
|
register struct mcache *m;
|
|
|
|
m = map_lookup(0, (int) gid, MAP_GID);
|
|
if (!m->miss) {
|
|
strncpy(name, m->name, GNAMELEN);
|
|
return !0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ushort lru_ticker; /* LRU time keeper */
|
|
|
|
/*
|
|
* map_lookup - lookup mapping by name or by ID
|
|
*
|
|
* Input:
|
|
* name - ^ to name, or NULL
|
|
* id - if name NULL, id
|
|
* which - which name space: UID or GID
|
|
*
|
|
* Returns:
|
|
* !0 - OK: ^ to mapping
|
|
* 0 - !OK: no "which"-type name exists
|
|
*/
|
|
static struct mcache *
|
|
map_lookup(name, id, which)
|
|
char *name; {
|
|
struct passwd *pw;
|
|
struct group *gw;
|
|
register struct mcache *m, *mlru;
|
|
register ushort lru;
|
|
static struct mcache *last[N_MAPTYPE];
|
|
|
|
/*
|
|
* if the ticker wraps, reset all LRU counters
|
|
*/
|
|
if (++lru_ticker == 0)
|
|
for (m = mcache; m < &mcache[NCACHE]; ++m)
|
|
m->lru = 0;
|
|
|
|
/*
|
|
* check the L1 cache first
|
|
*/
|
|
if ((m = last[which]) && m->type == which
|
|
&& ((name && strcmp(name, m->name) == 0)
|
|
|| (!name && id == m->id)))
|
|
goto ok;
|
|
|
|
/*
|
|
* missed, have to check the L2 cache, which is a
|
|
* linear scan thru.
|
|
*/
|
|
for (mlru = m = mcache; m < &mcache[NCACHE]; ++m) {
|
|
if (m->type == which
|
|
&& ((name && strcmp(name, m->name) == 0)
|
|
|| (!name && id == m->id)))
|
|
goto ok;
|
|
|
|
if (m->type == MAP_FREE || mlru->lru > m->lru)
|
|
mlru = m;
|
|
}
|
|
|
|
/*
|
|
* not found in cache. replace LRU entry with mapping
|
|
*/
|
|
m = mlru;
|
|
m->miss = 0; /* assume presence */
|
|
|
|
if (name) switch (which) { /* name lookup */
|
|
case MAP_UID:
|
|
strncpy(m->name, name, UNAMELEN-1);
|
|
m->name[UNAMELEN-1] = 0;
|
|
|
|
pw = getpwnam(name);
|
|
if (pw)
|
|
m->id = pw->pw_uid;
|
|
else {
|
|
m->miss = 1; /* cache misses also */
|
|
m->id = -1;
|
|
}
|
|
break;
|
|
|
|
case MAP_GID:
|
|
strncpy(m->name, name, GNAMELEN-1);
|
|
m->name[GNAMELEN-1] = 0;
|
|
|
|
gw = getgrnam(name);
|
|
if (gw)
|
|
m->id = gw->gr_gid;
|
|
else {
|
|
m->miss = 1; /* cache misses also */
|
|
m->id = -1;
|
|
}
|
|
break;
|
|
} else switch (which) { /* ID lookup */
|
|
case MAP_UID:
|
|
m->id = id;
|
|
pw = getpwuid((uid_t) id);
|
|
if (pw) {
|
|
strncpy(m->name, pw->pw_name, UNAMELEN-1);
|
|
m->name[UNAMELEN-1] = 0;
|
|
} else {
|
|
m->name[0] = 0;
|
|
m->miss = 1; /* cache misses also */
|
|
}
|
|
break;
|
|
|
|
case MAP_GID:
|
|
m->id = id;
|
|
gw = getgrgid((gid_t) id);
|
|
if (gw) {
|
|
strncpy(m->name, gw->gr_name, GNAMELEN-1);
|
|
m->name[GNAMELEN-1] = 0;
|
|
} else {
|
|
m->name[0] = 0;
|
|
m->miss = 1; /* cache misses also */
|
|
}
|
|
break;
|
|
}
|
|
|
|
m->type = which;
|
|
ok:
|
|
m->lru = lru_ticker;
|
|
|
|
return last[which] = m;
|
|
}
|