/* Copyright (c) 1982 Regents of the University of California */
char version
[] = "@(#)main.c 2.7 %G%";
/* Modified to include h option (recursively extract all files within
* a subtree) and m option (recreate the heirarchical structure of
* that subtree and move extracted files to their proper homes).
* 8/29/80 by Mike Litzkow
* Modified to work on the new file system
* 1/19/82 by Kirk McKusick
* Includes the s (skip files) option for use with multiple dumps on
#include "../h/dumprestor.h"
#define MWORD(m,i) (m[(unsigned)(i-1)/NBBY])
#define MBIT(i) (1<<((unsigned)(i-1)%NBBY))
#define BIS(i,w) (MWORD(w,i) |= MBIT(i))
#define BIC(i,w) (MWORD(w,i) &= ~MBIT(i))
#define BIT(i,w) (MWORD(w,i) & MBIT(i))
int eflag
= 0, hflag
= 0, mflag
= 0, cvtdir
= 0;
char tapename
[] = "/dev/rmt8";
char *magtape
= tapename
;
char tbf
[NTREC
*TP_BSIZE
];
char dirfile
[] = "/tmp/rstXXXXXX";
char lnkbuf
[MAXPATHLEN
+ 1];
#define INOHASH(val) (val % MAXINO)
struct xtrlist
*x_linkedto
;
char clearedbuf
[MAXBSIZE
];
if (signal(SIGINT
, done
) == SIG_IGN
)
if (signal(SIGTERM
, done
) == SIG_IGN
)
signal(SIGTERM
, SIG_IGN
);
fprintf(stderr
, "Usage: restor x[s|m|h|v] file file..., restor r|R filesys, or restor t\n");
for (cp
= *argv
++; *cp
; cp
++) {
magtape
= index(host
, ':');
msg("need keyletter ``f'' and device ``host:tape''");
/* s dumpnum (skip to) for multifile dump tapes */
fprintf(stderr
, "Dump number must be a positive integer\n");
fprintf(stderr
, "Bad key character %c\n", *cp
);
doit(command
, argc
, argv
);
doit(command
, argc
, argv
)
if ((mt
= rmtopen(magtape
, 0)) < 0) {
if ((mt
= open(magtape
, 0)) < 0) {
fprintf(stderr
, "%s: cannot open tape\n", magtape
);
tcom
.mt_count
= dumpnum
-1;
rmtioctl(MTFSF
,dumpnum
- 1);
if (ioctl(mt
,MTIOCTOP
,&tcom
) < 0)
blkclr(clearedbuf
, (long)MAXBSIZE
);
if (readhdr(&spcl
) == 0) {
fprintf(stderr
, "Tape is not a dump tape\n");
fprintf(stdout
, "Dump date: %s", ctime(&spcl
.c_date
));
fprintf(stdout
, "Dumped from: %s", ctime(&spcl
.c_ddate
));
/* and then extract it all */
df
= fopen(dirfile
, "w");
fprintf(stderr
, "restor: %s - cannot create directory temporary\n", dirfile
);
extractfiles(argc
, argv
);
register struct xtrlist
*xp
;
int xtrfile(), xtrskip(), xtrcvtdir(), xtrcvtskip(),
xtrlnkfile(), xtrlnkskip(), null();
if (stat(".", &stbuf
) < 0) {
fprintf(stderr
, "cannot stat .\n");
fssize = stbuf.st_blksize;
if (readhdr(&spcl
) == 0) {
fprintf(stderr
, "Tape is not a dump tape\n");
if (checkvol(&spcl
, 1) == 0) {
fprintf(stderr
, "Tape is not volume 1 of the dump\n");
pass1(1); /* This sets the various maps on the way by */
if ((d
= psearch(*argv
)) == 0 || BIT(d
,dumpmap
) == 0) {
fprintf(stdout
, "%s: not on tape\n", *argv
++);
allocxtr(d
, *argv
++, XINUSE
);
* if this is a multi-dump tape we always start with
* volume 1, so as to avoid accidentally restoring
fprintf(stderr
, "Mount desired tape volume; Specify volume #: ");
fprintf(stderr
, "Volume numbers are positive numerics\n");
if ((mt
= rmtopen(magtape
, 0)) == -1) {
if ((mt
= open(magtape
, 0)) == -1) {
fprintf(stderr
, "Cannot open tape!\n");
if (readhdr(&spcl
) == 0) {
fprintf(stderr
, "tape is not dump tape\n");
if (checkvol(&spcl
, volno
) == 0) {
fprintf(stderr
, "Wrong volume (%d)\n", spcl
.c_volume
);
while (gethead(&spcl
) == 0)
if (checktype(&spcl
, TS_INODE
) == 1) {
fprintf(stderr
, "Can't find inode mask!\n");
if (checktype(&spcl
, TS_BITS
) == 0)
if (ishead(&spcl
) == 0) {
while (gethead(&spcl
) == 0)
fprintf(stderr
, "resync restor, skipped %i blocks\n",
if (checktype(&spcl
, TS_END
) == 1) {
fprintf(stderr
, "end of tape\n");
if (checktype(&spcl
, TS_INODE
) == 0) {
for (xp
= xtrlist
[INOHASH(d
)]; xp
; xp
= xp
->x_next
) {
if (xp
->x_flags
& XLINKED
)
xp
->x_timep
[0] = spcl
.c_dinode
.di_atime
;
xp
->x_timep
[1] = spcl
.c_dinode
.di_mtime
;
mode
= spcl
.c_dinode
.di_mode
;
strcpy(name
, xp
->x_name
);
sprintf(name
, "%u", xp
->x_ino
);
fprintf(stderr
, "%s: unknown file mode 0%o\n",
fprintf(stdout
, "extract special file %s\n", name
);
if (mknod(name
, mode
, spcl
.c_dinode
.di_rdev
)) {
fprintf(stderr
, "%s: cannot create special file\n", name
);
getfile(null
, null
, spcl
.c_dinode
.di_size
);
fprintf(stdout
, "extract directory %s\n", name
);
strncat(name
, "/.", BUFSIZ
);
chown(xp
->x_name
, spcl
.c_dinode
.di_uid
, spcl
.c_dinode
.di_gid
);
getfile(null
, null
, spcl
.c_dinode
.di_size
);
fprintf(stdout
, "extract file %s\n", name
);
if ((ofile
= creat(name
, 0666)) < 0) {
fprintf(stderr
, "%s: cannot create file\n", name
);
chown(name
, spcl
.c_dinode
.di_uid
, spcl
.c_dinode
.di_gid
);
getfile(xtrcvtdir
, xtrcvtskip
,
getfile(xtrfile
, xtrskip
,
fprintf(stdout
, "extract symbolic link %s\n", name
);
uid
= spcl
.c_dinode
.di_uid
;
gid
= spcl
.c_dinode
.di_gid
;
getfile(xtrlnkfile
, xtrlnkskip
, spcl
.c_dinode
.di_size
);
if (symlink(lnkbuf
, name
) < 0) {
fprintf(stderr
, "%s: cannot create symbolic link\n", name
);
fprintf(stdout
, "extract file %s\n", name
);
if ((ofile
= creat(name
, 0666)) < 0) {
fprintf(stderr
, "%s: cannot create file\n", name
);
chown(name
, spcl
.c_dinode
.di_uid
, spcl
.c_dinode
.di_gid
);
getfile(xtrfile
, xtrskip
, spcl
.c_dinode
.di_size
);
utime(name
, xp
->x_timep
);
getfile(null
, null
, spcl
.c_dinode
.di_size
);
if (xtrcnt
== 0 && !mflag
)
for (xpp
= xtrlist
; xpp
< &xtrlist
[MAXINO
]; xpp
++) {
for (xp
= *xpp
; xp
; xp
= xp
->x_next
) {
if (mflag
&& (xp
->x_flags
& XISDIR
))
utime(xp
->x_name
, xp
->x_timep
);
if (xp
->x_flags
& XTRACTD
)
if ((xp
->x_flags
& XLINKED
) == 0) {
fprintf(stderr
, "cannot find file %s\n",
fprintf(stdout
, "link %s to %s\n",
xp
->x_linkedto
->x_name
, xp
->x_name
);
if (link(xp
->x_linkedto
->x_name
, xp
->x_name
) < 0)
fprintf(stderr
, "link %s to %s failed\n",
xp
->x_linkedto
->x_name
, xp
->x_name
);
* Read the tape, bulding up a directory structure for extraction
register struct dinode
*ip
;
int putdir(), null(), dirwrite();
strncpy(nulldir
.d_name
, "/", nulldir
.d_namlen
);
nulldir
.d_reclen
= DIRSIZ(&nulldir
);
while (gethead(&spcl
) == 0) {
fprintf(stderr
, "Can't find directory header!\n");
if (checktype(&spcl
, TS_BITS
) == 1) {
if (checktype(&spcl
, TS_CLRI
) == 1) {
if (checktype(&spcl
, TS_INODE
) == 0) {
if (spcl
.c_inumber
== ROOTINO
) {
bct
--; /* push back this block */
if (((struct direct
*)buf
)->d_ino
!= ROOTINO
) {
if (((struct odirect
*)buf
)->d_ino
!= ROOTINO
) {
fprintf(stderr
, "bad root directory\n");
fprintf(stderr
, "converting to new directory format\n");
if (!savedir
&& !cvtdir
) {
/* if no conversion, just return */
allocinotab(spcl
.c_inumber
, seekpt
);
getfile(putdir
, null
, spcl
.c_dinode
.di_size
);
putent(&nulldir
, dirwrite
);
getfile(null
, null
, spcl
.c_dinode
.di_size
);
* Put the directory entries in the directory file
register struct odirect
*odp
;
register struct direct
*dp
;
eodp
= (struct odirect
*)&buf
[size
];
for (odp
= (struct odirect
*)buf
; odp
< eodp
; odp
++)
putent(&cvtbuf
, dirwrite
);
for (loc
= 0; loc
< size
; ) {
dp
= (struct direct
*)(buf
+ loc
);
i
= DIRBLKSIZ
- (loc
& (DIRBLKSIZ
- 1));
if (dp
->d_reclen
<= 0 || dp
->d_reclen
> i
) {
* Recursively find names and inumbers of all files in subtree
* pname and put them in xtrlist[]
register struct inotab
*itp
;
register struct direct
*dp
;
char locname
[BUFSIZ
+ 1];
if (BIT(ino
, dumpmap
) == 0) {
fprintf(stdout
, "%s: not on the tape\n", pname
);
for (itp
= inotab
[INOHASH(ino
)]; itp
; itp
= itp
->t_next
) {
* pname is a directory name
allocxtr(ino
, pname
, XISDIR
);
* begin search through the directory
* skipping over "." and ".."
strncpy(locname
, pname
, BUFSIZ
);
strncat(locname
, "/", BUFSIZ
);
namelen
= strlen(locname
);
seekdir(dirp
, itp
->t_seekpt
, itp
->t_seekpt
);
* "/" signals end of directory
while (dp
->d_namlen
!= 1 || dp
->d_name
[0] != '/') {
if (namelen
+ dp
->d_namlen
>= BUFSIZ
) {
fprintf(stderr
, "%s%s: name exceedes %d char\n",
locname
, dp
->d_name
, BUFSIZ
);
strncat(locname
, dp
->d_name
, dp
->d_namlen
);
getleaves(dp
->d_ino
, locname
);
seekdir(dirp
, bpt
, itp
->t_seekpt
);
* locname is name of a simple file
allocxtr(ino
, pname
, XINUSE
);
* Search the directory tree rooted at inode ROOTINO
* for the path pointed at by n
while (*cp1
!= '/' && *cp1
)
* search the directory inode ino
register struct direct
*dp
;
register struct inotab
*itp
;
for (itp
= inotab
[INOHASH(inum
)]; itp
; itp
= itp
->t_next
)
seekdir(dirp
, itp
->t_seekpt
, itp
->t_seekpt
);
if (dp
->d_namlen
== 1 && dp
->d_name
[0] == '/')
} while (dp
->d_namlen
!= len
|| strncmp(dp
->d_name
, cp
, len
));
* Do the file extraction, calling the supplied functions
char buf
[MAXBSIZE
/ TP_BSIZE
][TP_BSIZE
];
# define addrblock addrblk.s_spcl
for (i
= 0; i
< addrblock
.c_count
; i
++) {
if (addrblock
.c_addr
[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) {
while (gethead(&spcl
) == 0)
if (checktype(&spcl
, TS_ADDR
) == 1)
if (gethead(&addrblock
) == 0) {
fprintf(stderr
, "Missing address (header) block, ino%u\n", ino
);
if (checktype(&addrblock
, TS_ADDR
) == 0) {
(*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) {
if (lseek(ofile
, size
, 1) == -1) {
struct odirect
*odp
, *edp
;
edp
= &buf
[size
/ sizeof(struct odirect
)];
for (odp
= buf
; odp
< edp
; odp
++) {
putent(&cvtbuf
, xtrfile
);
fprintf(stderr
, "unallocated block in directory\n");
if (pathlen
> MAXPATHLEN
) {
fprintf(stderr
, "symbolic link name: %s; too long %d\n",
fprintf(stderr
, "unallocated block in symbolic link\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;
if ((i
= rmtread(tbf
, NTREC
*TP_BSIZE
)) < 0) {
if ((i
= read(mt
, tbf
, NTREC
*TP_BSIZE
)) < 0) {
fprintf(stderr
, "Tape read error, continue?");
fprintf(stderr
, "[yn]\n");
while (getchar() != '\n')
} while (c
!= 'y' && c
!= 'n');
if (lseek(mt
, i
, 1) < 0) {
fprintf(stderr
, "continuation failed\n");
fprintf(stderr
, "Mount volume %d\n", volno
);
while (getchar() != '\n')
if ((mt
= rmtopen(magtape
, 0)) == -1) {
if ((mt
= open(magtape
, 0)) == -1) {
fprintf(stderr
, "Cannot open tape!\n");
if (readhdr(&tmpbuf
) == 0) {
fprintf(stderr
, "Not a dump tape.Try again\n");
if (checkvol(&tmpbuf
, volno
) == 0) {
fprintf(stderr
, "Wrong tape. Try again\n");
blkcpy(&tbf
[(bct
++*TP_BSIZE
)], b
, (long)TP_BSIZE
);
from
= from
, to
= to
, size
= size
;
asm(" movc3 12(ap),*4(ap),*8(ap)");
asm("movc5 $0,(r0),$0,8(ap),*4(ap)");
if (rmtioctl(tcom
.mt_op
, 1) == -1) {
/* kludge for disk dumps */
if (ioctl(mt
,MTIOCTOP
,&tcom
) == -1) {
/* kludge for disk dumps */
ioctl(mt
,MTIOCTOP
,&tcom
);
if (checktype(b
, TS_TAPE
) == 0)
* read the tape into buf, then return whether or
* or not it is a header block.
if (buf
->c_magic
!= MAGIC
|| checksum((int *)buf
) == 0)
* return whether or not the buffer contains a header block
if (buf
->c_magic
!= MAGIC
|| checksum((int *)buf
) == 0)
* read a bit mask from the tape into m.
*mapp
= (char *)calloc(i
, (TP_BSIZE
/(NBBY
/BITS
)));
m
+= (TP_BSIZE
/(NBBY
/BITS
));
while (gethead(&spcl
) == 0)
j
= sizeof(union u_spcl
) / sizeof(int);
fprintf(stderr
, "Checksum error %o, ino %u\n", i
, ino
);
* Check for access into each directory in the pathname of an extracted
* file and create such a directory if needed in preparation for moving
* the file to its proper home.
for (cp
= name
; *cp
; cp
++) {
if (access(name
, 01) < 0) {
if ((pid
= fork()) == 0) {
execl("/bin/mkdir", "mkdir", name
, 0);
execl("/usr/bin/mkdir", "mkdir", name
, 0);
fprintf(stderr
, "restor: cannot find mkdir!\n");
while ((rp
= wait(&i
)) >= 0 && rp
!= pid
)
fprintf(stderr
, "Can't open checklist file: %s\n", FSTAB
);
while ((fsp
= getfsent()) != 0) {
if (strcmp(fsp
->fs_spec
, dev
) == 0) {
printf("%s mounted on %s\n", dev
, fsp
->fs_file
);
if (chdir(fsp
->fs_file
) >= 0)
fprintf(stderr
, "%s cannot chdir to %s\n",
fprintf(stderr
, "%s not mounted\n", dev
);
* These variables are "local" to the following two functions.
* add a new directory entry to a file.
if (dirloc
+ dp
->d_reclen
> DIRBLKSIZ
) {
((struct direct
*)(dirbuf
+ prev
))->d_reclen
=
(*wrtfunc
)(dirbuf
, DIRBLKSIZ
);
blkcpy((char *)dp
, dirbuf
+ dirloc
, (long)dp
->d_reclen
);
* flush out a directory that is finished.
((struct direct
*)(dirbuf
+ prev
))->d_reclen
= DIRBLKSIZ
- prev
;
(*wrtfunc
)(dirbuf
, dirloc
);
fwrite(buf
, 1, size
, df
);
register struct odirect
*odp
;
register struct direct
*ndp
;
blkclr((char *)ndp
, (long)(sizeof *ndp
));
strncpy(ndp
->d_name
, odp
->d_name
, ODIRSIZ
);
ndp
->d_namlen
= strlen(ndp
->d_name
);
ndp
->d_reclen
= DIRSIZ(ndp
);
* this quickly calculates if this inode is a directory.
* Currently not maintained.
for (itp = inotab[INOHASH(odp->d_ino)]; itp; itp = itp->t_next) {
if (itp->t_ino != odp->d_ino)
* Modified to allow any random file to be a legal directory.
dirp
= (DIR *)malloc(sizeof(DIR));
dirp
->dd_fd
= open(name
, 0);
* Seek to an entry in a directory.
* Only values returned by ``telldir'' should be passed to seekdir.
* Modified to have many directories based in one file.
if (loc
== telldir(dirp
))
fprintf(stderr
, "bad seek pointer to seekdir %d\n", loc
);
(void)lseek(dirp
->dd_fd
, base
+ (loc
& ~(DIRBLKSIZ
- 1)), 0);
dirp
->dd_loc
= loc
& (DIRBLKSIZ
- 1);
dirp
->dd_size
= read(dirp
->dd_fd
, dirp
->dd_buf
, DIRBLKSIZ
);
* get next entry in a directory.
register struct direct
*dp
;
dirp
->dd_size
= read(dirp
->dd_fd
, dirp
->dd_buf
,
if (dirp
->dd_loc
>= dirp
->dd_size
) {
dp
= (struct direct
*)(dirp
->dd_buf
+ dirp
->dd_loc
);
dp
->d_reclen
> DIRBLKSIZ
+ 1 - dirp
->dd_loc
)
dirp
->dd_loc
+= dp
->d_reclen
;
register struct inotab
*itp
;
itp
= (struct inotab
*)calloc(1, sizeof(struct inotab
));
itp
->t_next
= inotab
[INOHASH(ino
)];
inotab
[INOHASH(ino
)] = itp
;
allocxtr(ino
, name
, flags
)
register struct xtrlist
*xp
, *pxp
;
xp
= (struct xtrlist
*)calloc(1, sizeof(struct xtrlist
) + strlen(name
));
xp
->x_next
= xtrlist
[INOHASH(ino
)];
xtrlist
[INOHASH(ino
)] = xp
;
strcpy(xp
->x_name
, name
);
for (pxp
= xp
->x_next
; pxp
; pxp
= pxp
->x_next
)
if (pxp
->x_ino
== ino
&& (pxp
->x_flags
& XLINKED
) == 0) {
if (xp
->x_flags
& XLINKED
)
fprintf(stdout
, "%s: linked to %s\n", xp
->x_name
, pxp
->x_name
);
else if (xp
->x_flags
& XISDIR
)
fprintf(stdout
, "%s: directory inode %u\n", xp
->x_name
, ino
);
fprintf(stdout
, "%s: inode %u\n", xp
->x_name
, ino
);