* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
static char sccsid
[] = "@(#)tape.c 5.9 (Berkeley) %G%";
#include <protocols/dumprestore.h>
static long fssize
= MAXBSIZE
;
static char magtape
[BUFSIZ
];
static union u_spcl endoftapemark
;
static int gettingfile
= 0; /* restart has a valid frame */
static char lnkbuf
[MAXPATHLEN
+ 1];
int Bcvt
; /* Swap Bytes (for CCI or sun) */
static int Qcvt
; /* Swap quads (for sun) */
newtapebuf(NTREC
> HIGHDENSITYTREC
? NTREC
: HIGHDENSITYTREC
);
msg("need keyletter ``f'' and device ``host:tape''\n");
(void) strcpy(magtape
, tape
);
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\")");
(void) strcpy(magtape
, source
);
tbf
= (char *)malloc(size
* TP_BSIZE
);
fprintf(stderr
, "Cannot allocate space for tape buffer\n");
* 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')
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\n", newvol
);
fprintf(stderr
, "then enter tape name (default: %s) ", magtape
);
(void) fgets(tbf
, BUFSIZ
, terminal
);
(void) strcpy(magtape
, tbf
);
magtape
[strlen(magtape
) - 1] = '\0';
if ((mt
= rmtopen(magtape
, 0)) == -1)
if ((mt
= open(magtape
, 0)) == -1)
fprintf(stderr
, "Cannot open %s\n", magtape
);
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");
* Skip up to the beginning of the next record
if (tmpbuf
.c_type
== TS_TAPE
)
for (i
= tmpbuf
.c_count
; i
> 0; i
--)
* 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)
fprintf(stdout
, "Dump date: %s", ctime(&spcl
.c_date
));
fprintf(stdout
, "Dumped from: %s", ctime(&spcl
.c_ddate
));
if (spcl
.c_host
[0] == '\0')
fprintf(stderr
, "Level %d dump of %s on %s:%s\n",
spcl
.c_level
, spcl
.c_filesys
, spcl
.c_host
, spcl
.c_dev
);
fprintf(stderr
, "Label: %s\n", spcl
.c_label
);
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
);
vprintf(stdout
, "skipped socket %s\n", name
);
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) {
for (i
++; i
< spcl
.c_count
; i
++)
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
bcopy(&tbf
[(bct
++*TP_BSIZE
)], b
, (long)TP_BSIZE
);
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",
bcopy((char *)&endoftapemark
, &tbf
[i
],
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");
panic("partial block read: %d should be %d\n",
bcopy((char *)&endoftapemark
, &tbf
[rd
], (long)TP_BSIZE
);
bcopy(&tbf
[(bct
++*TP_BSIZE
)], b
, (long)TP_BSIZE
);
for (i
= 0; i
< ntrec
; i
++)
((struct s_spcl
*)&tbf
[i
* TP_BSIZE
])->c_magic
= 0;
i
= rmtread(tbf
, ntrec
* TP_BSIZE
);
i
= read(mt
, tbf
, ntrec
* TP_BSIZE
);
perror("Tape read error");
fprintf(stderr
, "Tape block size (%d) %s (%d)\n",
i
, "is not a multiple of dump block size", TP_BSIZE
);
vprintf(stdout
, "Tape block size is %d\n", ntrec
);
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
) {
if (swabl(buf
->c_magic
) != NFS_MAGIC
)
vprintf(stdout
, "Note: Doing Byte swapping\n");
if (checksum((int *)buf
) == FAIL
)
swabst("8l4s31l", (char *)buf
);
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
;
j
= buf
->c_dinode
.di_ic
.ic_size
.val
;
if (buf
->c_dinode
.di_size
== 0 &&
(buf
->c_dinode
.di_mode
& IFMT
) == IFDIR
&& Qcvt
==0) {
printf("Note: Doing Quad swapping\n");
* 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
|| header
->c_type
== TS_OTAPE
) {
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.
curfile
.name
= "<name unknown>";
curfile
.action
= UNKNOWN
;
curfile
.dip
= (struct dinode
*)NIL
;
if (ishead(header
) == FAIL
) {
while (gethead(header
) == FAIL
|| header
->c_date
!= dumpdate
)
if (checktype(header
, TS_ADDR
) == GOOD
) {
* Skip up to the beginning of the next record
for (i
= 0; i
< header
->c_count
; i
++)
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
)
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);
/* What happens if we want to read restore tapes
for a 16bit int machine??? */
fprintf(stderr
, "Checksum error %o, inode %d file %s\n", i
,
curfile
.ino
, curfile
.name
);
fprintf(stderr
, cp
, a1
, a2
, a3
);
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n
= (n
* 10) + (*cp
++ - '0');
case 's': case 'w': case 'h':
c
= sp
[0]; sp
[0] = sp
[1]; sp
[1] = c
;
c
= sp
[0]; sp
[0] = sp
[3]; sp
[3] = c
;
c
= sp
[2]; sp
[2] = sp
[1]; sp
[1] = c
;
sp
++; /* Any other character, like 'b' counts as byte. */
swabl(x
) { unsigned long l
= x
; swabst("l", (char *)&l
); return l
; }