static char sccsid
[] = "@(#)tape.c 3.25 (Berkeley) 85/01/18";
/* Copyright (c) 1983 Regents of the University of California */
static long fssize
= MAXBSIZE
;
static union u_spcl endoftapemark
;
static int gettingfile
= 0; /* restart has a valid frame */
static char lnkbuf
[MAXPATHLEN
+ 1];
tbf
= (char *)malloc(ntrec
* TP_BSIZE
);
fprintf(stderr
, "Cannot allocate space for tape buffer\n");
magtape
= index(host
, ':');
msg("need keyletter ``f'' and device ``host:tape''\n");
setuid(getuid()); /* no longer need or want root privileges */
if (strcmp(source
, "-") == 0) {
* Since input is coming from a pipe we must establish
* our own connection to the terminal.
terminal
= fopen("/dev/tty", "r");
perror("Cannot open(\"/dev/tty\")");
terminal
= fopen("/dev/null", "r");
perror("Cannot open(\"/dev/null\")");
* Verify that the tape drive can be accessed and
* that it actually is a dump tape.
extern int xtrmap(), xtrmapskip();
vprintf(stdout
, "Verify tape and initialize maps\n");
if ((mt
= rmtopen(magtape
, 0)) < 0)
else if ((mt
= open(magtape
, 0)) < 0)
if (gethead(&spcl
) == FAIL
) {
bct
--; /* push back this block */
if (gethead(&spcl
) == FAIL
) {
fprintf(stderr
, "Tape is not a dump tape\n");
fprintf(stderr
, "Converting to new file system format.\n");
endoftapemark
.s_spcl
.c_magic
= cvtflag
? OFS_MAGIC
: NFS_MAGIC
;
endoftapemark
.s_spcl
.c_type
= TS_END
;
ip
= (int *)&endoftapemark
;
j
= sizeof(union u_spcl
) / sizeof(int);
endoftapemark
.s_spcl
.c_checksum
= CHECKSUM
- i
;
if (vflag
|| command
== 't') {
fprintf(stdout
, "Dump date: %s", ctime(&spcl
.c_date
));
fprintf(stdout
, "Dumped from: %s", ctime(&spcl
.c_ddate
));
if (stat(".", &stbuf
) < 0) {
if (stbuf
.st_blksize
> 0 && stbuf
.st_blksize
<= MAXBSIZE
)
fssize
= stbuf
.st_blksize
;
if (((fssize
- 1) & fssize
) != 0) {
fprintf(stderr
, "bad block size %d\n", fssize
);
if (checkvol(&spcl
, (long)1) == FAIL
) {
fprintf(stderr
, "Tape is not volume 1 of the dump\n");
if (readhdr(&spcl
) == FAIL
)
panic("no header after volume mark!\n");
if (checktype(&spcl
, TS_CLRI
) == FAIL
) {
fprintf(stderr
, "Cannot find file removal list\n");
maxino
= (spcl
.c_count
* TP_BSIZE
* NBBY
) + 1;
dprintf(stdout
, "maxino = %d\n", maxino
);
map
= calloc((unsigned)1, (unsigned)howmany(maxino
, NBBY
));
panic("no memory for file removal list\n");
getfile(xtrmap
, xtrmapskip
);
if (checktype(&spcl
, TS_BITS
) == FAIL
) {
fprintf(stderr
, "Cannot find file dump list\n");
map
= calloc((unsigned)1, (unsigned)howmany(maxino
, NBBY
));
panic("no memory for file dump list\n");
getfile(xtrmap
, xtrmapskip
);
* Prompt user to load a new dump volume.
* "Nextvol" is the next suggested volume to use.
* This suggested volume is enforced when doing full
* or incremental restores, but can be overrridden by
* the user when only extracting a subset of the files.
# define tmpbuf tmpspcl.s_spcl
panic("Changing volumes on pipe input?\n");
done(1); /* pipes do not get a second chance */
if (command
== 'R' || command
== 'r' || curfile
.action
!= SKIP
)
fprintf(stderr
, "%s%s%s%s%s",
"You have not read any tapes yet.\n",
"Unless you know which volume your",
" file(s) are on you should start\n",
"with the last volume and work",
" towards towards the first.\n");
fprintf(stderr
, "You have read volumes");
if (tapesread
& (1 << i
)) {
fprintf(stderr
, "%s%d", tbf
, i
);
fprintf(stderr
, "Specify next volume #: ");
(void) fgets(tbf
, BUFSIZ
, terminal
);
} while (!feof(terminal
) && tbf
[0] == '\n');
"Volume numbers are positive numerics\n");
fprintf(stderr
, "Mount tape volume %d then type return ", newvol
);
while (getc(terminal
) != '\n')
if ((mt
= rmtopen(magtape
, 0)) == -1)
if ((mt
= open(magtape
, 0)) == -1)
fprintf(stderr
, "Cannot open tape!\n");
if (readhdr(&tmpbuf
) == FAIL
) {
fprintf(stderr
, "tape is not dump tape\n");
if (checkvol(&tmpbuf
, volno
) == FAIL
) {
fprintf(stderr
, "Wrong volume (%d)\n", tmpbuf
.c_volume
);
if (tmpbuf
.c_date
!= dumpdate
|| tmpbuf
.c_ddate
!= dumptime
) {
fprintf(stderr
, "Wrong dump date\n\tgot: %s",
fprintf(stderr
, "\twanted: %s", ctime(&dumpdate
));
if (curfile
.action
== USING
) {
panic("active file into volume 1\n");
findinode(&spcl
, curfile
.action
== UNKNOWN
? 1 : 0);
* handle multiple dumps per tape by skipping forward to the
if (dumpnum
== 1 || volno
!= 1)
fprintf(stderr
, "Cannot have multiple dumps on pipe input\n");
tcom
.mt_count
= dumpnum
- 1;
rmtioctl(MTFSF
, dumpnum
- 1);
if (ioctl(mt
, (int)MTIOCTOP
, (char *)&tcom
) < 0)
extern int xtrlnkfile(), xtrlnkskip();
extern int xtrfile(), xtrskip();
timep
[0] = curfile
.dip
->di_atime
;
timep
[1] = curfile
.dip
->di_mtime
;
mode
= curfile
.dip
->di_mode
;
fprintf(stderr
, "%s: unknown file mode 0%o\n", name
, mode
);
if (ep
== NIL
|| ep
->e_flags
& EXTRACT
)
panic("unextracted directory %s\n", name
);
vprintf(stdout
, "extract file %s\n", name
);
return (genliteraldir(name
, curfile
.ino
));
getfile(xtrlnkfile
, xtrlnkskip
);
"%s: zero length symbolic link (ignored)\n", name
);
return (linkit(lnkbuf
, name
, SYMLINK
));
vprintf(stdout
, "extract special file %s\n", name
);
if (mknod(name
, mode
, (int)curfile
.dip
->di_rdev
) < 0) {
fprintf(stderr
, "%s: ", name
);
perror("cannot create special file");
(void) chown(name
, curfile
.dip
->di_uid
, curfile
.dip
->di_gid
);
(void) chmod(name
, mode
);
vprintf(stdout
, "extract file %s\n", name
);
if ((ofile
= creat(name
, 0666)) < 0) {
fprintf(stderr
, "%s: ", name
);
perror("cannot create file");
(void) fchown(ofile
, curfile
.dip
->di_uid
, curfile
.dip
->di_gid
);
(void) fchmod(ofile
, mode
);
getfile(xtrfile
, xtrskip
);
* skip over bit maps on the tape
while (checktype(&spcl
, TS_CLRI
) == GOOD
||
checktype(&spcl
, TS_BITS
) == GOOD
)
* skip over a file on the tape
* Do the file extraction, calling the supplied functions
off_t size
= spcl
.c_dinode
.di_size
;
static char clearedbuf
[MAXBSIZE
];
char buf
[MAXBSIZE
/ TP_BSIZE
][TP_BSIZE
];
if (checktype(&spcl
, TS_END
) == GOOD
)
panic("ran off end of tape\n");
if (ishead(&spcl
) == FAIL
)
panic("not at beginning of a file\n");
if (!gettingfile
&& setjmp(restart
) != 0)
for (i
= 0; i
< spcl
.c_count
; i
++) {
readtape(&buf
[curblk
++][0]);
if (curblk
== fssize
/ TP_BSIZE
) {
(*f1
)(buf
, size
> TP_BSIZE
?
(curblk
- 1) * TP_BSIZE
+ size
);
(*f1
)(buf
, size
> TP_BSIZE
?
(long) (curblk
* TP_BSIZE
) :
(curblk
- 1) * TP_BSIZE
+ size
);
(*f2
)(clearedbuf
, size
> TP_BSIZE
?
if ((size
-= TP_BSIZE
) <= 0)
if (readhdr(&spcl
) == GOOD
&& size
> 0) {
if (checktype(&spcl
, TS_ADDR
) == GOOD
)
dprintf(stdout
, "Missing address (header) block for %s\n",
(*f1
)(buf
, (curblk
* TP_BSIZE
) + size
);
* The next routines are called during file extraction to
* put the data into the right form and place.
if (write(ofile
, buf
, (int) size
) == -1) {
fprintf(stderr
, "write error extracting inode %d, name %s\n",
curfile
.ino
, curfile
.name
);
if (lseek(ofile
, size
, 1) == (long)-1) {
fprintf(stderr
, "seek error extracting inode %d, name %s\n",
curfile
.ino
, curfile
.name
);
if (pathlen
> MAXPATHLEN
) {
fprintf(stderr
, "symbolic link name: %s->%s%s; too long %d\n",
curfile
.name
, lnkbuf
, buf
, pathlen
);
(void) strcat(lnkbuf
, buf
);
fprintf(stderr
, "unallocated block in symbolic link %s\n",
* Do the tape i/o, dealing with volume changes
for (i
= 0; i
< ntrec
; i
++)
((struct s_spcl
*)&tbf
[i
*TP_BSIZE
])->c_magic
= 0;
i
= rmtread(&tbf
[rd
], cnt
);
i
= read(mt
, &tbf
[rd
], cnt
);
if (i
> 0 && i
!= ntrec
*TP_BSIZE
) {
panic("partial block read: %d should be %d\n",
fprintf(stderr
, "Tape read error while ");
switch (curfile
.action
) {
fprintf(stderr
, "trying to set up tape\n");
fprintf(stderr
, "trying to resyncronize\n");
fprintf(stderr
, "restoring %s\n", curfile
.name
);
fprintf(stderr
, "skipping over inode %d\n",
if (!yflag
&& !reply("continue"))
if (lseek(mt
, i
, 1) == (long)-1)
perror("continuation failed");
bcopy((char *)&endoftapemark
, b
,
bcopy(&tbf
[(bct
++*TP_BSIZE
)], b
, (long)TP_BSIZE
);
if (gethead(b
) == FAIL
) {
dprintf(stdout
, "readhdr fails at %d blocks\n", blksread
);
* read the tape into buf, then return whether or
* or not it is a header block.
if (buf
->c_magic
!= NFS_MAGIC
|| checksum((int *)buf
) == FAIL
)
readtape((char *)(&u_ospcl
.s_ospcl
));
bzero((char *)buf
, (long)TP_BSIZE
);
buf
->c_type
= u_ospcl
.s_ospcl
.c_type
;
buf
->c_date
= u_ospcl
.s_ospcl
.c_date
;
buf
->c_ddate
= u_ospcl
.s_ospcl
.c_ddate
;
buf
->c_volume
= u_ospcl
.s_ospcl
.c_volume
;
buf
->c_tapea
= u_ospcl
.s_ospcl
.c_tapea
;
buf
->c_inumber
= u_ospcl
.s_ospcl
.c_inumber
;
buf
->c_checksum
= u_ospcl
.s_ospcl
.c_checksum
;
buf
->c_magic
= u_ospcl
.s_ospcl
.c_magic
;
buf
->c_dinode
.di_mode
= u_ospcl
.s_ospcl
.c_dinode
.odi_mode
;
buf
->c_dinode
.di_nlink
= u_ospcl
.s_ospcl
.c_dinode
.odi_nlink
;
buf
->c_dinode
.di_uid
= u_ospcl
.s_ospcl
.c_dinode
.odi_uid
;
buf
->c_dinode
.di_gid
= u_ospcl
.s_ospcl
.c_dinode
.odi_gid
;
buf
->c_dinode
.di_size
= u_ospcl
.s_ospcl
.c_dinode
.odi_size
;
buf
->c_dinode
.di_rdev
= u_ospcl
.s_ospcl
.c_dinode
.odi_rdev
;
buf
->c_dinode
.di_atime
= u_ospcl
.s_ospcl
.c_dinode
.odi_atime
;
buf
->c_dinode
.di_mtime
= u_ospcl
.s_ospcl
.c_dinode
.odi_mtime
;
buf
->c_dinode
.di_ctime
= u_ospcl
.s_ospcl
.c_dinode
.odi_ctime
;
buf
->c_count
= u_ospcl
.s_ospcl
.c_count
;
bcopy(u_ospcl
.s_ospcl
.c_addr
, buf
->c_addr
, (long)256);
if (u_ospcl
.s_ospcl
.c_magic
!= OFS_MAGIC
||
checksum((int *)(&u_ospcl
.s_ospcl
)) == FAIL
)
buf
->c_magic
= NFS_MAGIC
;
* Have to patch up missing information in bit map headers
buf
->c_dinode
.di_size
= buf
->c_count
* TP_BSIZE
;
for (i
= 0; i
< buf
->c_count
; i
++)
panic("gethead: unknown inode type %d\n", buf
->c_type
);
* Check that a header is where it belongs and predict the next header
static ino_t previno
= 0x7fffffff;
if (header
->c_type
== TS_TAPE
) {
fprintf(stderr
, "Volume header\n");
if (previno
== 0x7fffffff)
fprintf(stderr
, "Dump mask header");
fprintf(stderr
, "Remove mask header");
fprintf(stderr
, "File header, ino %d", previno
);
fprintf(stderr
, "File continuation header, ino %d", previno
);
fprintf(stderr
, "End of tape header");
if (predict
!= blksread
- 1)
fprintf(stderr
, "; predicted %d blocks, got %d blocks",
if (header
->c_type
!= TS_END
)
for (i
= 0; i
< header
->c_count
; i
++)
if (header
->c_addr
[i
] != 0)
prevtype
= header
->c_type
;
previno
= header
->c_inumber
;
* Complain if had to skip, and complain is set.
findinode(header
, complain
)
curfile
.name
= "<name unknown>";
curfile
.action
= UNKNOWN
;
curfile
.dip
= (struct dinode
*)NIL
;
if (ishead(header
) == FAIL
) {
while (gethead(header
) == FAIL
)
if (checktype(header
, TS_INODE
) == GOOD
) {
curfile
.dip
= &header
->c_dinode
;
curfile
.ino
= header
->c_inumber
;
if (checktype(header
, TS_END
) == GOOD
) {
if (checktype(header
, TS_CLRI
) == GOOD
) {
curfile
.name
= "<file removal list>";
if (checktype(header
, TS_BITS
) == GOOD
) {
curfile
.name
= "<file dump list>";
while (gethead(header
) == FAIL
)
if (skipcnt
> 0 && complain
)
fprintf(stderr
, "resync restore, skipped %d blocks\n", skipcnt
);
* return whether or not the buffer contains a header block
if (buf
->c_magic
!= NFS_MAGIC
)
j
= sizeof(union u_spcl
) / sizeof(int);
fprintf(stderr
, "Checksum error %o, inode %d file %s\n", i
,
curfile
.ino
, curfile
.name
);
fprintf(stderr
, cp
, a1
, a2
, a3
);