* Copyright (c) 1991 The Regents of the University of California.
* This module is believed to contain source code proprietary to AT&T.
* Use and redistribution is subject to the Berkeley Software License
* Agreement and your Software Agreement with AT&T (Western Electric).
"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)tar.c 5.19 (Berkeley) 6/21/92";
#define writetape(b) writetbuf(b, 1)
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
void onintr(), onquit(), onhup();
char tname
[] = _PATH_TMP
;
char magtape
[] = _PATH_MAGTAPE
;
for (cp
= *argv
++; *cp
; cp
++)
"tar: tapefile must be specified with 'f' option\n");
if ((tfile
= fopen(tname
, "w")) == NULL
) {
"tar: cannot create temporary file (%s)\n",
fprintf(tfile
, "!!!!!/!/!/!/!/!/!/! 000\n");
"tar: blocksize must be specified with 'b' option\n");
"tar: invalid blocksize \"%s\"\n", *argv
);
fprintf(stderr
, "tar: %c: unknown option\n", *cp
);
if (!rflag
&& !xflag
&& !tflag
)
if (cflag
&& tfile
!= NULL
)
if (signal(SIGINT
, SIG_IGN
) != SIG_IGN
)
(void) signal(SIGINT
, onintr
);
if (signal(SIGHUP
, SIG_IGN
) != SIG_IGN
)
(void) signal(SIGHUP
, onhup
);
if (signal(SIGQUIT
, SIG_IGN
) != SIG_IGN
)
(void) signal(SIGQUIT
, onquit
);
if (signal(SIGTERM
, SIG_IGN
) != SIG_IGN
)
(void) signal(SIGTERM
, onterm
);
"tar: usage: tar -{txru}[cvfblmhopwBi] [tapefile] [blocksize] file1 file2...\n");
if (strcmp(tape
, "-") == 0) {
* Read from standard input or write to standard output.
"tar: can only create standard output archives\n");
* Use file or tape on local machine.
mt
= open(tape
, O_RDWR
|O_CREAT
|O_TRUNC
, 0666);
mt
= open(tape
, O_RDONLY
);
fprintf(stderr
, "tar: %s: %s\n", tape
, strerror(errno
));
"sort +0 -1 +1nr %s -o %s; awk '$1 != prev {print; prev=$1}' %s >%sX; mv %sX %s",
tname
, tname
, tname
, tname
, tname
, tname
);
freopen(tname
, "r", tfile
);
fstat(fileno(tfile
), &stbuf
);
while (*argv
&& ! term
) {
if (!strcmp(cp2
, "-C") && argv
[1]) {
"tar: can't change directories to %s: %s\n",
for (cp
= *argv
; *cp
; cp
++)
"tar: can't change directories to %s: %s\n",
putfile(*argv
++, cp2
, parent
, ARGV
);
fprintf(stderr
, "tar: cannot change back?: %s: %s\n",
for (; ihead
!= NULL
; ihead
= ihead
->nextp
) {
fprintf(stderr
, "tar: missing links to %s\n", ihead
->pathname
);
return (dblock
.dbuf
.name
[0] == '\0');
register struct stat
*sp
;
readtape((char *)&dblock
);
if (dblock
.dbuf
.name
[0] == '\0')
sscanf(dblock
.dbuf
.mode
, "%o", &i
);
sscanf(dblock
.dbuf
.uid
, "%o", &i
);
sscanf(dblock
.dbuf
.gid
, "%o", &i
);
sscanf(dblock
.dbuf
.size
, "%lo", &tempquad
);
sscanf(dblock
.dbuf
.mtime
, "%lo", &sp
->st_mtime
);
sscanf(dblock
.dbuf
.chksum
, "%o", &chksum
);
if (chksum
!= (i
= checksum())) {
fprintf(stderr
, "tar: directory checksum error (%d != %d)\n",
/* strip off leading "/" if present */
if (sflag
&& dblock
.dbuf
.name
[0] == '/') {
register char *cp1
, *cp2
;
for (cp1
= cp2
= dblock
.dbuf
.name
; *cp2
&& *cp2
== '/'; ++cp2
);
fprintf(tfile
, "%s %s\n", dblock
.dbuf
.name
, dblock
.dbuf
.mtime
);
if (dblock
.dbuf
.linkflag
== '1')
(void) readtbuf(&bufp
, TBLOCK
);
putfile(longname
, shortname
, parent
, source
)
char newparent
[NAMSIZ
+64];
int hint
; /* amount to write to get "in sync" */
if (hflag
|| (Hflag
&& source
== ARGV
))
i
= stat(shortname
, &stbuf
);
i
= lstat(shortname
, &stbuf
);
fprintf(stderr
, "tar: %s: %s\n", longname
, strerror(errno
));
if (tfile
!= NULL
&& checkupdate(longname
) == 0)
if (checkw('r', longname
) == 0)
if (Fflag
&& checkf(shortname
, stbuf
.st_mode
, Fflag
) == 0)
switch (stbuf
.st_mode
& S_IFMT
) {
for (i
= 0, cp
= buf
; *cp
++ = longname
[i
++];)
if ((cp
- buf
) >= NAMSIZ
) {
fprintf(stderr
, "tar: %s: file name too long\n",
strcpy(dblock
.dbuf
.name
,buf
);
(void)sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
(void) writetape((char *)&dblock
);
(void)sprintf(newparent
, "%s/%s", parent
, shortname
);
if (chdir(shortname
) < 0) {
fprintf(stderr
, "tar: chdir %s: %s\n",
shortname
, strerror(errno
));
if ((dirp
= opendir(".")) == NULL
) {
fprintf(stderr
, "tar: %s: directory read error\n",
"tar: cannot change back?: %s: %s\n",
parent
, strerror(errno
));
while ((dp
= readdir(dirp
)) != NULL
&& !term
) {
if (!strcmp(".", dp
->d_name
) ||
!strcmp("..", dp
->d_name
))
putfile(buf
, cp
, newparent
, PUTFILE
);
"tar: cannot change back?: %s: %s\n",
parent
, strerror(errno
));
if (strlen(longname
) >= NAMSIZ
) {
fprintf(stderr
, "tar: %s: file name too long\n",
strcpy(dblock
.dbuf
.name
, longname
);
if (stbuf
.st_size
+ 1 >= NAMSIZ
) {
fprintf(stderr
, "tar: %s: symbolic link too long\n",
i
= readlink(shortname
, dblock
.dbuf
.linkname
, NAMSIZ
- 1);
"tar: can't read symbolic link %s: %s\n",
longname
, strerror(errno
));
dblock
.dbuf
.linkname
[i
] = '\0';
dblock
.dbuf
.linkflag
= '2';
fprintf(vfile
, "a %s symbolic link to %s\n",
longname
, dblock
.dbuf
.linkname
);
(void)sprintf(dblock
.dbuf
.size
, "%11lo", 0L);
(void)sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
(void) writetape((char *)&dblock
);
if ((infile
= open(shortname
, 0)) < 0) {
fprintf(stderr
, "tar: %s: %s\n",
longname
, strerror(errno
));
if (strlen(longname
) >= NAMSIZ
) {
fprintf(stderr
, "tar: %s: file name too long\n",
strcpy(dblock
.dbuf
.name
, longname
);
if (stbuf
.st_nlink
> 1) {
for (lp
= ihead
; lp
!= NULL
; lp
= lp
->nextp
)
if (lp
->inum
== stbuf
.st_ino
&&
lp
->devnum
== stbuf
.st_dev
) {
strcpy(dblock
.dbuf
.linkname
, lp
->pathname
);
dblock
.dbuf
.linkflag
= '1';
(void)sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
(void) writetape( (char *) &dblock
);
fprintf(vfile
, "a %s link to %s\n",
lp
= (struct linkbuf
*) getmem(sizeof(*lp
));
lp
->devnum
= stbuf
.st_dev
;
lp
->count
= stbuf
.st_nlink
- 1;
strcpy(lp
->pathname
, longname
);
blocks
= (stbuf
.st_size
+ (TBLOCK
-1)) / TBLOCK
;
fprintf(vfile
, "a %s %ld blocks\n", longname
, blocks
);
(void)sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
hint
= writetape((char *)&dblock
);
maxread
= max(stbuf
.st_blksize
, (nblock
* TBLOCK
));
if ((bigbuf
= malloc((unsigned)maxread
)) == 0) {
while ((i
= read(infile
, bigbuf
, min((hint
*TBLOCK
), maxread
))) > 0
nblks
= ((i
-1)/TBLOCK
)+1;
hint
= writetbuf(bigbuf
, nblks
);
fprintf(stderr
, "tar: Read error on %s: %s\n",
longname
, strerror(errno
));
} else if (blocks
!= 0 || i
!= 0)
fprintf(stderr
, "tar: %s: file changed size\n",
fprintf(stderr
, "tar: %s is not a file. Not dumped\n",
if ((i
= wantit(argv
)) == 0)
if (checkw('x', dblock
.dbuf
.name
) == 0) {
if ((s
= rindex(dblock
.dbuf
.name
, '/')) == 0)
if (checkf(s
, stbuf
.st_mode
, Fflag
) == 0) {
if (checkdir(dblock
.dbuf
.name
)) { /* have a directory */
if (dblock
.dbuf
.linkflag
== '2') { /* symlink */
* only unlink non directories or empty
if (rmdir(dblock
.dbuf
.name
) < 0) {
unlink(dblock
.dbuf
.name
);
if (symlink(dblock
.dbuf
.linkname
, dblock
.dbuf
.name
)<0) {
"tar: %s: symbolic link failed: %s\n",
dblock
.dbuf
.name
, strerror(errno
));
fprintf(vfile
, "x %s symbolic link to %s\n",
dblock
.dbuf
.name
, dblock
.dbuf
.linkname
);
/* ignore alien orders */
chown(dblock
.dbuf
.name
, stbuf
.st_uid
, stbuf
.st_gid
);
setimes(dblock
.dbuf
.name
, stbuf
.st_mtime
);
chmod(dblock
.dbuf
.name
, stbuf
.st_mode
& 07777);
if (dblock
.dbuf
.linkflag
== '1') { /* regular link */
* only unlink non directories or empty
if (rmdir(dblock
.dbuf
.name
) < 0) {
unlink(dblock
.dbuf
.name
);
if (link(dblock
.dbuf
.linkname
, dblock
.dbuf
.name
) < 0) {
"tar: can't link %s to %s: %s\n",
dblock
.dbuf
.name
, dblock
.dbuf
.linkname
,
fprintf(vfile
, "%s linked to %s\n",
dblock
.dbuf
.name
, dblock
.dbuf
.linkname
);
if ((ofile
= creat(dblock
.dbuf
.name
,stbuf
.st_mode
&0xfff)) < 0) {
fprintf(stderr
, "tar: can't create %s: %s\n",
dblock
.dbuf
.name
, strerror(errno
));
chown(dblock
.dbuf
.name
, stbuf
.st_uid
, stbuf
.st_gid
);
blocks
= ((bytes
= stbuf
.st_size
) + TBLOCK
-1)/TBLOCK
;
fprintf(vfile
, "x %s, %ld bytes, %ld tape blocks\n",
dblock
.dbuf
.name
, bytes
, blocks
);
if (nwant
> (blocks
*TBLOCK
))
nread
= readtbuf(&bufp
, nwant
);
if (write(ofile
, bufp
, (int)min(nread
, bytes
)) < 0) {
"tar: %s: HELP - extract write error: %s\n",
dblock
.dbuf
.name
, strerror(errno
));
blocks
-= (((nread
-1)/TBLOCK
)+1);
setimes(dblock
.dbuf
.name
, stbuf
.st_mtime
);
chmod(dblock
.dbuf
.name
, stbuf
.st_mode
& 07777);
dblock
.dbuf
.name
[0] = '\0'; /* process the whole stack */
if ((i
= wantit(argv
)) == 0)
printf("%s", dblock
.dbuf
.name
);
if (dblock
.dbuf
.linkflag
== '1')
printf(" linked to %s", dblock
.dbuf
.linkname
);
if (dblock
.dbuf
.linkflag
== '2')
printf(" symbolic link to %s", dblock
.dbuf
.linkname
);
bzero(buf
, sizeof (buf
));
register struct stat
*st
;
printf("%3u/%1u", st
->st_uid
, st
->st_gid
);
printf("%7qd", st
->st_size
);
cp
= ctime(&st
->st_mtime
);
printf(" %-12.12s %-4.4s ", cp
+4, cp
+20);
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
};
register struct stat
*st
;
for (mp
= &m
[0]; mp
< &m
[9];)
while (--n
>=0 && (st
->st_mode
&*ap
++)==0)
* Make all directories needed by `name'. If `name' is itself
* a directory on the tar tape (indicated by a trailing '/'),
* Quick check for existence of directory.
if ((cp
= rindex(name
, '/')) == 0)
if (access(name
, F_OK
) == 0) { /* already exists */
return (cp
[1] == '\0'); /* return (lastchar == '/') */
* No luck, try to make all directories in path.
for (cp
= name
; *cp
; cp
++) {
if (access(name
, F_OK
) < 0) {
if (mkdir(name
, 0777) < 0) {
fprintf(stderr
, "tar: mkdir: %s: %s\n",
chown(name
, stbuf
.st_uid
, stbuf
.st_gid
);
if (pflag
&& cp
[1] == '\0') /* dir on the tape */
chmod(name
, stbuf
.st_mode
& 07777);
(void) signal(SIGINT
, SIG_IGN
);
(void) signal(SIGQUIT
, SIG_IGN
);
(void) signal(SIGHUP
, SIG_IGN
);
(void) signal(SIGTERM
, SIG_IGN
);
register struct stat
*sp
;
for (cp
= dblock
.dummy
; cp
< &dblock
.dummy
[TBLOCK
]; cp
++)
(void)sprintf(dblock
.dbuf
.mode
, "%6o ", sp
->st_mode
& 07777);
(void)sprintf(dblock
.dbuf
.uid
, "%6o ", sp
->st_uid
);
(void)sprintf(dblock
.dbuf
.gid
, "%6o ", sp
->st_gid
);
(void)sprintf(dblock
.dbuf
.size
, "%11qo ", sp
->st_size
);
(void)sprintf(dblock
.dbuf
.mtime
, "%11lo ", sp
->st_mtime
);
for (cp
= dblock
.dbuf
.chksum
;
cp
< &dblock
.dbuf
.chksum
[sizeof(dblock
.dbuf
.chksum
)]; cp
++)
for (cp
= dblock
.dummy
; cp
< &dblock
.dummy
[TBLOCK
]; cp
++)
return (response() == 'y');
while (getchar() != '\n')
checkf(name
, mode
, howmuch
)
if ((mode
& S_IFMT
) == S_IFDIR
){
if ((strcmp(name
, "SCCS")==0) || (strcmp(name
, "RCS")==0))
if ((l
= strlen(name
)) < 3)
if (howmuch
> 1 && name
[l
-2] == '.' && name
[l
-1] == 'o')
if (strcmp(name
, "core") == 0 ||
strcmp(name
, "errs") == 0 ||
(howmuch
> 1 && strcmp(name
, "a.out") == 0))
/* SHOULD CHECK IF IT IS EXECUTABLE */
/* Is the current file a new file, or the newest one of the same name? */
if ((seekp
= lookup(arg
)) < 0)
fscanf(tfile
, "%s %lo", name
, &mtime
);
return (stbuf
.st_mtime
> mtime
);
* 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.
for (cp
= argv
; *cp
; cp
++)
if (prefix(*cp
, dblock
.dbuf
.name
))
* Does s2 begin with the string s1, on a directory boundary?
a
= bsrch(s
, i
, low
, high
);
return (b
[i
+1] == ' '? 0 : -1);
(void) readtbuf(&bufp
, TBLOCK
);
bcopy(bufp
, buffer
, TBLOCK
);
if (recno
>= nblock
|| first
== 0) {
if ((i
= bread(mt
, (char *)tbuf
, TBLOCK
*nblock
)) < 0)
fprintf(stderr
, "tar: tape blocksize error\n");
fprintf(stderr
, "tar: blocksize = %d\n", i
);
if (size
> ((nblock
-recno
)*TBLOCK
))
size
= (nblock
-recno
)*TBLOCK
;
*bufpp
= (char *)&tbuf
[recno
];
i
= write(mt
, (char *)tbuf
, TBLOCK
*nblock
);
* Special case: We have an empty tape buffer, and the
* users data size is >= the tape block size: Avoid
* the bcopy and dma direct to tape. BIG WIN. Add the
* residual to the tape buffer.
while (recno
== 0 && n
>= nblock
) {
i
= write(mt
, buffer
, TBLOCK
*nblock
);
buffer
+= (nblock
* TBLOCK
);
bcopy(buffer
, (char *)&tbuf
[recno
++], TBLOCK
);
i
= write(mt
, (char *)tbuf
, TBLOCK
*nblock
);
/* Tell the user how much to write to get in sync */
static struct mtop mtop
= {MTBSR
, 1};
mtdev
= ioctl(mt
, MTIOCGET
, (char *)&mtget
);
if (ioctl(mt
, MTIOCTOP
, (char *)&mtop
) < 0) {
fprintf(stderr
, "tar: tape backspace error: %s\n",
(void)lseek(mt
, (off_t
) -TBLOCK
*nblock
, 1);
i
= write(mt
, (char *)tbuf
, TBLOCK
*nblock
);
mterr(operation
, i
, exitcode
)
fprintf(stderr
, "tar: tape %s error: %s\n",
operation
, i
< 0 ? strerror(errno
) : "unexpected EOF");
return (read(fd
, buf
, size
));
for (count
= 0; count
< size
; count
+= lastread
) {
lastread
= read(fd
, buf
, size
- count
);
p
= getcwd((char *)NULL
, 0);
(void)fprintf(stderr
, "tar: %s\n", strerror(errno
));
if ((stbuf
.st_mode
& S_IFMT
) == S_IFCHR
)
nblock
= stbuf
.st_blksize
/ TBLOCK
;
tbuf
= (union hblock
*)malloc((unsigned)nblock
*TBLOCK
);
fprintf(stderr
, "tar: blocksize %d too big, can't get memory\n",
* 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.
#define NTIM (NAMSIZ/2+1) /* a/b/c/d/... */
register char *p
= dirstack
;
register char *q
= hp
->dbuf
.name
;
* Not a child: unwind the stack, setting the times.
* The order we do this doesn't matter, so we go "forward."
if (mtime
[++ndir
] >= 0) {
*--p
= '\0'; /* zap the slash */
setimes(dirstack
, mtime
[ndir
]);
/* Push this one on the "stack" */
while (*p
= *q
++) /* append the rest of the new dir */
mtime
[ndir
] = stbuf
.st_mtime
; /* overwrite the last one */
tv
[0].tv_sec
= time((time_t *) 0);
tv
[0].tv_usec
= tv
[1].tv_usec
= 0;
if (utimes(path
, tv
) < 0)
fprintf(stderr
, "tar: can't set time on %s: %s\n",
char *p
= malloc((unsigned) size
);
if (p
== NULL
&& freemem
) {
"tar: out of memory, link and directory modtime info lost\n");