Ultrix-3.1/src/cmd/dump.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
static char Sccsid[] = "@(#)dump.c 3.1 7/10/87";
/*
* ULTRIX-11 file system dump program (dump)
*
* Modified for use with the Micro/pdp-11 and to prevent
* accidentally overwriting the file system being dumped, due to
* typing errors when typing the dump command.
*
* Also modified to allow for restarting multi-volume dumps
* in the event of dump device errors.
*
* The m option is used to dump to RX50 floppy diskettes
* on the Micro/pdp-11. The e option is used for RX33 diskettes.
*
* The y option causes the `continue' question not to be asked.
* This is required when dump is used in a makefile, for
* making distributions.
*
* Usage: dump 0mf /dev/rrx0 /dev/rrd2
*
* Fred Canter 4/7/83
*/
#define NI 8 /* changed to 8 from 16 with the increase in BSIZE */
/* (to prevent size overflow for overlay version) */
#define DIRPB (BSIZE/sizeof(struct direct))
/*
* Not using stdio here, kernel printf() has been included locally.
* WARNING: the printf version here only recognizes a subset of
* format specifications, see printf() routine at the end of this
* file for details.
*/
/*
#include <stdio.h>
*/
#include <sys/param.h>
#include <sys/inode.h>
#include <sys/ino.h>
#include <sys/fblk.h>
#include <sys/filsys.h>
#include <sys/dir.h>
#include <dumprestor.h>
#include <signal.h>
#include <errno.h>
#define MWORD(m,i) (m[(unsigned)(i-1)/MLEN])
#define MBIT(i) (1<<((unsigned)(i-1)%MLEN))
#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))
struct filsys sblock;
struct dinode itab[INOPB*NI];
short clrmap[MSIZ];
short dirmap[MSIZ];
short nodmap[MSIZ];
char *disk;
char *tape;
char *increm;
char incno;
int uflag;
int mflag;
int kflag;
int yflag;
int tflag; /* flag used to prevent rewrite on error question */
/* see flusht(). Causes dump to exit with status 1, */
/* allows for more control in scripts using dump */
int fi;
int to;
ino_t ino;
int nsubdir;
int ntape;
int nadded;
int dadded;
int density = 160;
int mpid;
int ppid;
char *ctime();
char *prdate();
long atol();
int fi;
long tsize;
long esize;
long asize;
int mark();
int add();
int dump();
int tapsrec();
int dmpspc();
int dsrch();
int nullf();
#define HOUR (60L*60L)
#define DAY (24L*HOUR)
#define YEAR (365L*DAY)
int intr();
main(argc, argv)
char *argv[];
{
char *arg;
register i;
time(&spcl.c_date);
tsize = 2300L*12L*10L;
tape = "/dev/rht0";
disk = "/dev/null";
increm = "/etc/ddate";
incno = '9';
uflag = 0;
mflag = 0;
kflag = 0;
yflag = 0;
arg = "u";
if(argc > 1) {
argv++;
argc--;
arg = *argv;
}
while(*arg)
switch (*arg++) {
case 'f':
if(argc > 1) {
argv++;
argc--;
tape = *argv;
}
break;
case 'd':
if (argc > 1) {
argv++;
argc--;
if(strlen(*argv) > 4) {
denerr:
printf("bad density\n");
exit(1);
}
density = atoi(*argv)/10;
if(density == 0)
goto denerr;
}
break;
case 's':
if(argc > 1) {
argv++;
argc--;
tsize = atol(*argv);
tsize *= 12L*10L;
}
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
incno = arg[-1];
break;
case 'u':
uflag++;
break;
case 'm': /* dump to RX50 diskettes */
mflag++;
#ifdef UCB_NKB
tsize = 400L;
#else
tsize = 800L;
#endif
break;
case 'e': /* dump to RX33 diskettes */
mflag++;
#ifdef UCB_NKB
tsize = 1200L;
#else
tsize = 2400L;
#endif
break;
case 'k':
kflag++;
tsize = 8000L;
tape = "/dev/rtk0";
break;
case 'y':
yflag++;
break;
case 't':
tflag++;
break;
default:
printf("bad key '%c%'\n", arg[-1]);
exit(1);
}
if(argc > 1) {
argv++;
argc--;
disk = *argv;
}
mpid = getpid();
signal(SIGTERM, SIG_IGN);
getitime();
printf(" date = %s\n", prdate(spcl.c_date));
printf("dump date = %s\n", prdate(spcl.c_ddate));
printf("dumping %s to %s\n", disk, tape);
if(yflag == 0) {
printf("\n\7\7\7Last chance before writing on %s\n", tape);
printf("\nContinue <y or n> ? ");
if(getchar() != 'y') {
printf("\nDump aborted\n");
exit(1);
}
while(getchar() != '\n') ; /* flush input */
}
fi = open(disk, 0);
if(fi < 0) {
printf("dump: cannot open %s\n", disk);
exit(1);
}
otape();
printf("I\n");
esize = 0;
CLR(clrmap);
CLR(dirmap);
CLR(nodmap);
pass(mark, (short *)NULL);
do {
printf("II\n");
nadded = 0;
pass(add, dirmap);
} while(nadded);
bmapest(clrmap);
bmapest(nodmap);
if(mflag) {
printf("estimated %D blocks on %D diskette(s)\n",
esize, (esize/tsize)+1);
} else {
printf("estimated %D tape blocks on %d tape(s)\n",
esize, 0);
}
printf("III\n");
bitmap(clrmap, TS_CLRI);
pass(dump, dirmap);
printf("IV\n");
pass(dump, nodmap);
putitime();
printf("DONE\n");
spcl.c_type = TS_END;
for(i=0; i<NTREC; i++)
spclrec();
if(mflag) {
printf("%D blocks on %d diskette(s)\n",
spcl.c_tapea, spcl.c_volume);
} else {
printf("%D tape blocks on %d tape(s)\n",
spcl.c_tapea, spcl.c_volume);
}
close(to);
kill(ppid, SIGTERM);
kill(mpid, SIGINT); /* kill master copy of dump */
}
pass(fn, map)
int (*fn)();
short *map;
{
register i, j;
int bits;
ino_t mino;
daddr_t d;
sync();
bread((daddr_t)1, (char *)&sblock, sizeof(sblock));
mino = (sblock.s_isize-2) * INOPB;
ino = 0;
for(i=2;; i+=NI) {
if(ino >= mino)
break;
d = (unsigned)i;
for(j=0; j<INOPB*NI; j++) {
if(ino >= mino)
break;
if((ino % MLEN) == 0) {
bits = ~0;
if(map != NULL)
bits = *map++;
}
ino++;
if(bits & 1) {
if(d != 0) {
bread(d, (char *)itab, sizeof(itab));
d = 0;
}
(*fn)(&itab[j]);
}
bits >>= 1;
}
}
}
icat(ip, fn1, fn2)
struct dinode *ip;
int (*fn1)(), (*fn2)();
{
register i;
daddr_t d[NADDR];
l3tol(&d[0], &ip->di_addr[0], NADDR);
(*fn2)(d, NADDR-3);
for(i=0; i<NADDR; i++) {
if(d[i] != 0) {
if(i < NADDR-3)
(*fn1)(d[i]); else
indir(d[i], fn1, fn2, i-(NADDR-3));
}
}
}
indir(d, fn1, fn2, n)
daddr_t d;
int (*fn1)(), (*fn2)();
{
register i;
daddr_t idblk[NINDIR];
bread(d, (char *)idblk, sizeof(idblk));
if(n <= 0) {
spcl.c_type = TS_ADDR;
(*fn2)(idblk, NINDIR);
for(i=0; i<NINDIR; i++) {
d = idblk[i];
if(d != 0)
(*fn1)(d);
}
} else {
n--;
for(i=0; i<NINDIR; i++) {
d = idblk[i];
if(d != 0)
indir(d, fn1, fn2, n);
}
}
}
mark(ip)
struct dinode *ip;
{
register f;
f = ip->di_mode & IFMT;
if(f == 0)
return;
BIS(ino, clrmap);
if(f == IFDIR)
BIS(ino, dirmap);
if(ip->di_mtime >= spcl.c_ddate ||
ip->di_ctime >= spcl.c_ddate) {
BIS(ino, nodmap);
#ifdef UCB_SYMLINKS
if (f != IFREG && f != IFLNK)
#else
if (f != IFREG)
#endif
return;
est(ip);
}
}
add(ip)
struct dinode *ip;
{
if(BIT(ino, nodmap))
return;
nsubdir = 0;
dadded = 0;
icat(ip, dsrch, nullf);
if(dadded) {
BIS(ino, nodmap);
est(ip);
nadded++;
}
if(nsubdir == 0)
if(!BIT(ino, nodmap))
BIC(ino, dirmap);
}
dump(ip)
struct dinode *ip;
{
register i;
if(ntape) {
ntape = 0;
bitmap(nodmap, TS_BITS);
}
BIC(ino, nodmap);
spcl.c_dinode = *ip;
spcl.c_type = TS_INODE;
spcl.c_count = 0;
i = ip->di_mode & IFMT;
#ifdef UCB_SYMLINKS
if(i != IFDIR && i != IFREG && i != IFLNK)
#else
if(i != IFDIR && i != IFREG)
#endif
{
spclrec();
return;
}
icat(ip, tapsrec, dmpspc);
}
dmpspc(dp, n)
daddr_t *dp;
{
register i, t;
spcl.c_count = n;
for(i=0; i<n; i++) {
t = 0;
if(dp[i] != 0)
t++;
spcl.c_addr[i] = t;
}
spclrec();
}
bitmap(map, typ)
short *map;
{
register i, n;
char *cp;
n = -1;
for(i=0; i<MSIZ; i++)
if(map[i])
n = i;
if(n < 0)
return;
spcl.c_type = typ;
spcl.c_count = (n*sizeof(map[0]) + BSIZE)/BSIZE;
spclrec();
cp = (char *)map;
for(i=0; i<spcl.c_count; i++) {
taprec(cp);
cp += BSIZE;
}
}
spclrec()
{
register i, *ip, s;
spcl.c_inumber = ino;
spcl.c_magic = MAGIC;
spcl.c_checksum = 0;
ip = (int *)&spcl;
s = 0;
for(i=0; i<BSIZE/sizeof(*ip); i++)
s += *ip++;
spcl.c_checksum = CHECKSUM - s;
taprec((char *)&spcl);
}
dsrch(d)
daddr_t d;
{
register char *cp;
register i;
register ino_t in;
struct direct dblk[DIRPB];
if(dadded)
return;
bread(d, (char *)dblk, sizeof(dblk));
for(i=0; i<DIRPB; i++) {
in = dblk[i].d_ino;
if(in == 0)
continue;
cp = dblk[i].d_name;
if(cp[0] == '.') {
if(cp[1] == '\0')
continue;
if(cp[1] == '.' && cp[2] == '\0')
continue;
}
if(BIT(in, nodmap)) {
dadded++;
return;
}
if(BIT(in, dirmap))
nsubdir++;
}
}
nullf()
{
}
bread(da, ba, c)
daddr_t da;
char *ba;
{
register n;
#ifndef UCB_NKB
lseek(fi, da*512, 0);
#else
lseek(fi, da*BSIZE, 0);
#endif UCB_NKB
n = read(fi, ba, c);
if(n != c) {
printf("\n\7\7\7Read error on %s, bn = %D\n", disk, da);
printf("asked for %d; got %d bytes\n", c, n);
}
}
CLR(map)
register short *map;
{
register n;
n = MSIZ;
do
*map++ = 0;
while(--n);
}
char tblock[NTREC][BSIZE];
daddr_t tdaddr[NTREC];
int trecno;
taprec(dp)
char *dp;
{
register i;
for(i=0; i<BSIZE; i++)
tblock[trecno][i] = *dp++;
tdaddr[trecno] = 0;
trecno++;
spcl.c_tapea++;
if(trecno >= NTREC)
flusht();
}
tapsrec(d)
daddr_t d;
{
if(d == 0)
return;
tdaddr[trecno] = d;
trecno++;
spcl.c_tapea++;
if(trecno >= NTREC)
flusht();
}
flusht()
{
char place[100];
register i, si;
daddr_t d;
while(trecno < NTREC)
tdaddr[trecno++] = 1;
loop:
d = 0;
for(i=0; i<NTREC; i++)
if(tdaddr[i] != 0)
if(d == 0 || tdaddr[i] < d) {
si = i;
d = tdaddr[i];
}
if(d != 0) {
bread(d, tblock[si], BSIZE);
tdaddr[si] = 0;
goto loop;
}
trecno = 0;
if(write(to, tblock[0], sizeof(tblock)) != sizeof(tblock)) {
close(to);
printf("dump: write error on %s \ndump: ", tape);
switch(errno) {
case ENOSPC: printf("No space left on device");
break;
case ETPL: printf("Fatal error - tape position lost");
break;
default:
break;
}
printf(" (errno = %d)\n",errno);
/* below is used to cause parent to exit with status 1
and let a controlling script/process decide what to
do (helps out dumps with TK25 where tape repositioning
must be done) */
if(!tflag) {
printf("\nRewrite this volume <y or n> ? ");
read(0, place, sizeof(place));
if((place[0] == 'y') || (place[0] == '\n'))
exit(0); /* tells parent to restart */
else {
kill(mpid, SIGINT); /* causes parent to exit */
exit(1); /* with 0 status */
}
} else
exit(1); /* tells parent to exit with status 1 */
}
if(mflag)
asize += sizeof(tblock)/BSIZE;
else if(kflag)
asize++;
else {
asize += sizeof(tblock)/density;
asize += 7;
}
if(asize >= tsize) {
close(to);
printf("\nMount next ");
if(mflag)
printf("diskette");
else
printf("tape");
printf(" <type return when ready>");
read(0, place, sizeof(place));
kill(ppid, SIGTERM);
otape();
}
}
otape()
{
int cpid;
int status;
char c;
redump:
ppid = getpid();
if(ppid != mpid)
signal(SIGTERM, SIG_DFL);
cpid = fork();
if(cpid < 0) {
printf("Can't fork (pid = %d)\n", ppid);
exit(1);
}
if(cpid != 0) { /* parent */
if(getpid() == mpid)
signal(SIGINT, intr);
wait(&status);
if(status == 0)
goto redump;
/* tflag added to allow dump to exit with status 1 */
if((getpid() == mpid) && (!tflag))
for( ;;) ;
exit(1);
} else { /* child */
reopen:
to = creat(tape, 0666);
if(to < 0) {
printf("dump: cannot create %s \ndump: ", tape);
switch (errno) {
case ENODEV:
case ENXIO: printf("No such device or address");
break;
case EACCES: printf("Permission denied");
break;
case EROFS: printf("Read-only device");
break;
case ETOL: printf("Tape unit offline");
break;
case ETWL: printf("Tape unit write locked");
break;
case ETO: printf("Tape unit already open");
break;
default:
break;
}
printf(" (errno = %d)\n",errno);
ask_rt:
printf("\nretry <y or n> ? ");
c = getchar();
if(c == '\n')
goto ask_rt;
while(getchar() != '\n') ;
if(c == 'y')
goto reopen;
if(c != 'n')
goto ask_rt;
kill(mpid, SIGINT);
exit(1);
}
asize = 0;
ntape++;
spcl.c_volume++;
spcl.c_type = TS_TAPE;
spclrec();
}
}
char *
prdate(d)
time_t d;
{
char *p;
if(d == 0)
return("the epoch");
p = ctime(&d);
p[24] = 0;
return(p);
}
getitime()
{
register i, df;
struct idates idbuf;
char *fname;
fname = disk;
l1:
for(i=0; fname[i]; i++)
if(fname[i] == '/') {
fname += i+1;
goto l1;
}
spcl.c_ddate = 0;
df = open(increm, 0);
if(df < 0) {
printf("cannot open %s\n", increm);
exit(1);
}
l2:
i = read(df, (char *)&idbuf, sizeof(idbuf));
if(i != sizeof(idbuf)) {
close(df);
return;
}
for(i=0;; i++) {
if(fname[i] != idbuf.id_name[i])
goto l2;
if(fname[i] == '\0')
break;
}
if(idbuf.id_incno >= incno)
goto l2;
if(idbuf.id_ddate <= spcl.c_ddate)
goto l2;
spcl.c_ddate = idbuf.id_ddate;
goto l2;
}
putitime()
{
register i, n, df;
struct idates idbuf;
char *fname;
if(uflag == 0)
return;
fname = disk;
l1:
for(i=0; fname[i]; i++)
if(fname[i] == '/') {
fname += i+1;
goto l1;
}
spcl.c_ddate = 0;
df = open(increm, 2);
if(df < 0) {
printf("cannot open %s\n", increm);
exit(1);
}
n = 0;
l2:
i = read(df, (char *)&idbuf, sizeof(idbuf));
if(i != sizeof(idbuf)) {
lseek(df, (long)n*sizeof(idbuf), 0);
goto l3;
}
n++;
for(i=0;; i++) {
if(fname[i] != idbuf.id_name[i])
goto l2;
if(fname[i] == '\0')
break;
}
if(idbuf.id_incno != incno)
goto l2;
lseek(df, (long)(n-1)*sizeof(idbuf), 0);
l3:
for(i=0;; i++) {
idbuf.id_name[i] = fname[i];
if(fname[i] == '\0') { /* fill the rest of name with blanks:
* George Mathew 6/20/85) */
for (i++; i < (sizeof(idbuf.id_name) -1); i++)
idbuf.id_name[i] = ' ';
idbuf.id_name[i] = '\0';
break;
}
}
idbuf.id_incno = incno;
idbuf.id_ddate = spcl.c_date;
if (write(df, (char *)&idbuf, sizeof(idbuf)) != sizeof(idbuf)) {
close(df);
printf("dump: /etc/ddate write error\n");
exit(-1);
}
close(df);
printf("level %c dump on %s\n", incno, prdate(spcl.c_date));
}
est(ip)
struct dinode *ip;
{
long s;
esize++;
s = (ip->di_size + BSIZE-1) / BSIZE;
esize += s;
if(s > NADDR-3) {
s -= NADDR-3;
s = (s + (BSIZE/sizeof(daddr_t))-1) / (BSIZE/sizeof(daddr_t));
esize += s;
}
}
bmapest(map)
short *map;
{
register i, n;
n = -1;
for(i=0; i<MSIZ; i++)
if(map[i])
n = i;
if(n < 0)
return;
esize++;
esize += (n + (BSIZE/sizeof(short))-1) / (BSIZE/sizeof(short));
}
intr()
{
exit(0); /* to keep make happy */
}
getchar()
{
char c;
if (read(0, &c, 1) < 0)
return(-1);
else
return(c);
}
putchar(c)
char c;
{
write(1, &c, 1);
}
/*
* Scaled down version of C Library printf.
* Only %s %u %d (==%u) %o %x %D are recognized.
*/
/* VARARGS 1 */
printf(fmt, x1)
register char *fmt;
unsigned x1;
{
register c;
register unsigned int *adx;
char *s;
adx = &x1;
loop:
while((c = *fmt++) != '%') {
if(c == '\0')
return;
putchar(c);
}
c = *fmt++;
if(c == 'd' || c == 'u' || c == 'o' || c == 'x')
printn((long)*adx, c=='o'? 8: (c=='x'? 16:10));
else if(c == 's') {
s = (char *)*adx;
while(c = *s++)
putchar(c);
} else if (c == 'c') {
putchar(*(char *)adx);
} else if (c == 'D') {
printn(*(long *)adx, 10);
adx += (sizeof(long) / sizeof(int)) - 1;
}
adx++;
goto loop;
}
/*
* Print an unsigned integer in base b.
*/
printn(n, b)
long n;
{
register long a;
if (n<0) { /* shouldn't happen */
putchar('-');
n = -n;
}
if(a = n/b)
printn(a, b);
putchar("0123456789ABCDEF"[(int)(n%b)]);
}