* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1980 Regents of the University of California.\n\
static char sccsid
[] = "@(#)tar.c 5.12 (Berkeley) 5/23/88";
#define writetape(b) writetbuf(b, 1)
#define min(a,b) ((a) < (b) ? (a) : (b))
#define max(a,b) ((a) > (b) ? (a) : (b))
char tname
[] = "/tmp/tarXXXXXX";
char magtape
[] = "/dev/rmt8";
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: ");
char wdir
[MAXPATHLEN
], tempdir
[MAXPATHLEN
], *parent
;
"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]) {
fprintf(stderr
, "tar: can't change directories to ");
for (cp
= *argv
; *cp
; cp
++)
fprintf(stderr
, "tar: can't change directories to ");
parent
= getcwd(tempdir
);
putfile(*argv
++, cp2
, parent
);
fprintf(stderr
, "tar: cannot change back?: ");
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", &sp
->st_size
);
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
)
char newparent
[NAMSIZ
+64];
int hint
; /* amount to write to get "in sync" */
i
= lstat(shortname
, &stbuf
);
i
= stat(shortname
, &stbuf
);
fprintf(stderr
, "tar: ");
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) {
if ((dirp
= opendir(".")) == NULL
) {
fprintf(stderr
, "tar: %s: directory read error\n",
fprintf(stderr
, "tar: cannot change back?: ");
while ((dp
= readdir(dirp
)) != NULL
&& !term
) {
if (!strcmp(".", dp
->d_name
) ||
!strcmp("..", dp
->d_name
))
putfile(buf
, cp
, newparent
);
fprintf(stderr
, "tar: cannot change back?: ");
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);
fprintf(stderr
, "tar: can't read symbolic link ");
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: ");
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 ");
} 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) {
fprintf(stderr
, "tar: %s: symbolic link failed: ",
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) {
fprintf(stderr
, "tar: can't link %s to %s: ",
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: ",
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: ",
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("%3d/%1d", st
->st_uid
, st
->st_gid
);
printf("%7ld", 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
, 0) == 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
, 0) < 0) {
if (mkdir(name
, 0777) < 0) {
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
, "%11lo ", 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: ");
lseek(mt
, (daddr_t
) -TBLOCK
*nblock
, 1);
i
= write(mt
, (char *)tbuf
, TBLOCK
*nblock
);
mterr(operation
, i
, exitcode
)
fprintf(stderr
, "tar: tape %s error: ", operation
);
fprintf(stderr
, "unexpected EOF\n");
return (read(fd
, buf
, size
));
for (count
= 0; count
< size
; count
+= lastread
) {
lastread
= read(fd
, buf
, size
- count
);
if (getwd(buf
) == NULL
) {
fprintf(stderr
, "tar: %s\n", buf
);
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: ", path
);
char *p
= malloc((unsigned) size
);
if (p
== NULL
&& freemem
) {
"tar: out of memory, link and directory modtime info lost\n");