* 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.4 (Berkeley) %G%";
#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
)
if (signal(SIGHUP
, SIG_IGN
) != SIG_IGN
)
if (signal(SIGQUIT
, SIG_IGN
) != SIG_IGN
)
if (signal(SIGTERM
, SIG_IGN
) != SIG_IGN
)
if (strcmp(usefile
, "-") == 0) {
"tar: can only create standard output archives\n");
} else if ((mt
= open(usefile
, 2)) < 0) {
if (cflag
== 0 || (mt
= creat(usefile
, 0666)) < 0) {
"tar: cannot open %s\n", usefile
);
if (strcmp(usefile
, "-") == 0) {
} else if ((mt
= open(usefile
, 0)) < 0) {
fprintf(stderr
, "tar: cannot open %s\n", usefile
);
"tar: usage: tar -{txru}[cvfblmhopwBi] [tapefile] [blocksize] file1 file2...\n");
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]) {
for (cp
= *argv
; *cp
; cp
++)
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",
fprintf(tfile
, "%s %s\n", dblock
.dbuf
.name
, dblock
.dbuf
.mtime
);
if (dblock
.dbuf
.linkflag
== '1')
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: %s: cannot open file\n", longname
);
fprintf(stderr
, "tar: %s: no such file or directory\n",
fprintf(stderr
, "tar: %s: cannot stat file\n", longname
);
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
);
sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
writetape((char *)&dblock
);
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);
dblock
.dbuf
.linkname
[i
] = '\0';
dblock
.dbuf
.linkflag
= '2';
fprintf(vfile
, "a %s symbolic link to %s\n",
longname
, dblock
.dbuf
.linkname
);
sprintf(dblock
.dbuf
.size
, "%11lo", 0);
sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
writetape((char *)&dblock
);
if ((infile
= open(shortname
, 0)) < 0) {
fprintf(stderr
, "tar: %s: cannot open file\n", longname
);
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';
sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
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
);
sprintf(dblock
.dbuf
.chksum
, "%6o", checksum());
hint
= writetape((char *)&dblock
);
maxread
= max(stbuf
.st_blksize
, (nblock
* TBLOCK
));
if ((bigbuf
= malloc(maxread
)) == 0) {
while ((i
= read(infile
, bigbuf
, min((hint
*TBLOCK
), maxread
))) > 0
nblks
= ((i
-1)/TBLOCK
)+1;
hint
= writetbuf(bigbuf
, nblks
);
if (blocks
!= 0 || i
!= 0)
fprintf(stderr
, "tar: %s: file changed size\n",
fprintf(stderr
, "tar: %s is not a file. Not dumped\n",
for (cp
= argv
; *cp
; cp
++)
if (prefix(*cp
, dblock
.dbuf
.name
))
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\n",
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: %s: cannot link\n",
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: %s - cannot create\n",
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\n",
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 */
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("%7D", 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 a
* directory itself 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')
chmod(name
, stbuf
.st_mode
& 0777);
signal(SIGQUIT
, SIG_IGN
);
signal(SIGTERM
, SIG_IGN
);
register struct stat
*sp
;
for (cp
= dblock
.dummy
; cp
< &dblock
.dummy
[TBLOCK
]; cp
++)
sprintf(dblock
.dbuf
.mode
, "%6o ", sp
->st_mode
& 07777);
sprintf(dblock
.dbuf
.uid
, "%6o ", sp
->st_uid
);
sprintf(dblock
.dbuf
.gid
, "%6o ", sp
->st_gid
);
sprintf(dblock
.dbuf
.size
, "%11lo ", sp
->st_size
);
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
);
* Does s2 begin with the string s1, on a directory boundary?
a
= bsrch(s
, i
, low
, high
);
return (b
[i
+1] == ' '? 0 : -1);
bcopy(bufp
, buffer
, TBLOCK
);
if (recno
>= nblock
|| first
== 0) {
if ((i
= bread(mt
, tbuf
, TBLOCK
*nblock
)) < 0) {
fprintf(stderr
, "tar: tape read error\n");
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
];
if (write(mt
, tbuf
, TBLOCK
*nblock
) < 0) {
fprintf(stderr
,"tar: tape write error\n");
* 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
) {
if (write(mt
, buffer
, TBLOCK
*nblock
) < 0) {
fprintf(stderr
,"tar: tape write error\n");
buffer
+= (nblock
* TBLOCK
);
bcopy(buffer
, (char *)&tbuf
[recno
++], TBLOCK
);
if (write(mt
, tbuf
, TBLOCK
*nblock
) < 0) {
fprintf(stderr
,"tar: tape write error\n");
/* Tell the user how much to write to get in sync */
static struct mtop mtop
= {MTBSR
, 1};
if (ioctl(mt
, MTIOCTOP
, &mtop
) < 0) {
fprintf(stderr
, "tar: tape backspace error\n");
lseek(mt
, (daddr_t
) -TBLOCK
*nblock
, 1);
write(mt
, tbuf
, TBLOCK
*nblock
);
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_blocks
/ TBLOCK
;
tbuf
= (union hblock
*)malloc(nblock
*TBLOCK
);
fprintf(stderr
, "tar: blocksize %d too big, can't get memory\n",
* We are really keeping a directory stack, but since all the
* elements of it 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 the null string 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\n", path
);
char *p
= malloc((unsigned) size
);
if (p
== NULL
&& freemem
) {
"tar: out of memory, link and directory modtime info lost\n");