|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 14346 (0x380a)
Types: TextFile
Notes: UNIX file
Names: »dump.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/dump/dump.c«
/*
* Dump a filesystem onto a
* tape. Dump only those files that
* have checked since the last
* dump.
*/
#include <stdio.h>
#include <types.h>
#include <discbuf.h>
#include <dumptape.h>
#include <canon.h>
#include <filsys.h>
char *dtn = DTAPE; /* Tape name */
FILE *dtp; /* Tape file pointer */
char *fsn = DFSYS; /* File system name */
int block = 20; /* Blocking factor */
int density = 1600; /* Tape density, bpi */
long length = 2300L*12L*100L; /* Length of tape, inches/100 */
long used; /* Tape used, inches/100 */
struct dumpheader dh; /* Dump header */
int reel = 1; /* Reel # */
int uflag = 1; /* Update dates file flag */
int vflag; /* Verbose flag */
int Sflag; /* Set when sizes are in blocks */
int level = 9; /* Dump level */
time_t bdate; /* Date at beginning of dump */
time_t ddate; /* Dump since date */
char *map; /* Map pointer */
char *ddbuf; /* Dump data buffer */
char *ddend; /* Dump data buffer end pointer */
int ddnbuf; /* Buffer size */
union dumpdata *ddptr; /* Pointer to current item */
ino_t isize; /* # of inodes */
char dev[DIRSIZ+1]; /* Name of device, squashed */
char fname[6]; /* File system name */
char fpack[6]; /* File system pack name */
long ninodes; /* # of inodes dumped */
long nblocks; /* # of blocks of data */
long nsparse; /* # of sparse blocks deleted */
char helpmessage[] = "\
Usage: dump [bdfsuvS0123456789 [args]] filesystem ...\n\
";
/*
* Tables used by imap.
* This effectively implements
* the access polynomial for the indirect
* blocks.
*/
daddr_t ranges[] = {
ND,
ND+(daddr_t)1*NBN,
ND+(daddr_t)1*NBN + (daddr_t)1*NBN*NBN,
ND+(daddr_t)1*NBN + (daddr_t)1*NBN*NBN + (daddr_t)1*NBN*NBN*NBN
};
char offsets[] = {
0,
ND,
ND+1,
ND+1+1
};
daddr_t coeff[] = {
1,
(daddr_t) NBN,
(daddr_t) NBN*NBN,
(daddr_t) NBN*NBN*NBN
};
/*
* One pass compilers are
* such fun.
*/
long time();
long ddlen();
union dumpdata *ddnextbuf();
long getddate();
char *ctime();
daddr_t imap();
char *calloc();
main(argc, argv)
char *argv[];
{
register DISCBUF *dbp;
register struct filsys *fsp;
unsigned fi;
init(argc, argv);
if (*fsn == 0)
fatal("no filesystem specified");
if ((dbfp=fopen(fsn, "r")) == NULL)
fatal("%s: cannot open filesystem", fsn);
if ((dtp=fopen(dtn, "w")) == NULL)
fatal("%s: cannot open dump file", dtn);
squash(dev, fsn);
bdate = time((long *) 0);
ddate = getddate();
if (vflag) {
if (ddate == 0)
fprintf(stderr, "dump: since epoch\n");
else
fprintf(stderr, "dump: since %s", ctime(&ddate));
}
dbclaim(5);
sync();
dbp = dbread((long) SUPERI);
fsp = (struct filsys *) (dbp->db_data);
fi = fsp->s_isize;
canshort(fi);
isize = (fi-INODEI)*INOPB;
strncpy(fname, fsp->s_fname, sizeof(fname));
strncpy(fpack, fsp->s_fpack, sizeof(fpack));
dbfree(dbp, 0);
if ((map=calloc(isize, sizeof(char))) == NULL)
fatal("out of memory (map)");
walk((ino_t) ROOTIN);
nextvol();
dumpinomap();
dump(DD_DIR); /* Directories */
dump(0); /* Files */
dumpfinish();
if (uflag)
putddate();
if (vflag) {
if (Sflag)
used /= 512; else
used /= 100*12;
fprintf(stderr, "dump: length is %D ", used);
if (Sflag)
fprintf(stderr, "blocks\n");
else if (used == 1)
fprintf(stderr, "foot\n");
else
fprintf(stderr, "feet\n");
fprintf(stderr, "dump: %D inodes, ", ninodes);
fprintf(stderr, "%D real blocks, ", nblocks);
fprintf(stderr, "%D sparse blocks\n", nsparse);
}
exit(0);
}
/*
* Scan the command line and
* set options. Perhaps reset the name
* of the filesystem, the dump tape
* and the dump origin.
*/
init(argc, argv)
char *argv[];
{
register char *p;
register c, i;
if (argc > 1) {
uflag = 0;
i = 1;
p = argv[1];
while ((c = *p++) != '\0') {
switch (c) {
case 'f':
if (++i >= argc)
usage();
dtn = argv[i];
break;
case 'u':
uflag = 1;
break;
case 'v':
vflag = 1;
break;
case 's':
if (++i >= argc)
usage();
length = (long)12*100*num(argv[i], "length",
600, 4800);
break;
case 'S':
if (++i >= argc)
usage();
Sflag++;
length = (long)512 * num(argv[i], "block size",
0, (unsigned)65535L);
break;
case 'b':
if (++i >= argc)
usage();
block = num(argv[i], "blocking", 1, 20);
break;
case 'd':
if (++i >= argc)
usage();
density = num(argv[i], "density", 800, 6250);
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
level = c-'0';
break;
default:
usage();
}
}
if (++i < argc)
fsn = argv[i];
}
}
/*
* Collect and range check
* numeric arguments.
*/
num(s, msg, ll, hl)
register char *s;
char *msg;
unsigned ll, hl;
{
register c, n;
n = 0;
while ((c=*s++)>='0' && c<='9')
n = 10*n + c - '0';
if (c!='\0' || n<ll || n>hl)
fatal("bad %s specification", msg);
return (n);
}
/*
* Walk down the file system,
* looking for files that should be
* dumped. Mark them in the map.
* All directories that lead to changed
* inodes are forced to be dumped.
*/
walk(ino)
ino_t ino;
{
register DISCBUF *dbp;
register struct direct *dirp;
int flag, nent, ndir;
long pb, dirb;
struct dinode dinode;
ino_t ent[BUFSIZ/sizeof(struct direct)];
if ((map[ino-1]&DD_DEJA) != 0)
return (0);
ireadin(&dinode, ino);
canshort(dinode.di_mode);
cantime(dinode.di_ctime);
/*
* Normally pipes stay in core,
* and are not in directories. Just to be
* safe we pretend any pipe is a clear
* inode.
*/
if ((dinode.di_mode&IFMT) == IFPIPE)
return (0);
map[ino-1] |= DD_BUSY;
if ((dinode.di_mode&IFMT) != IFDIR) {
if (level==0 || dinode.di_ctime>ddate) {
map[ino-1] |= DD_DUMP;
return (1);
}
return (0);
}
map[ino-1] |= DD_DEJA|DD_DIR;
flag = dirb = 0;
cansize(dinode.di_size);
while (dinode.di_size != 0) {
if ((pb = imap(&dinode, dirb++)) == 0) {
dinode.di_size -= BUFSIZ;
continue;
}
/*
* Writing the code is this
* strange fashon makes it require 1 cache
* buffer, not 1 per level.
*/
dbp = dbread(pb);
nent = 0;
ndir = BUFSIZ / sizeof(struct direct);
if (dinode.di_size < BUFSIZ)
ndir = dinode.di_size / sizeof(struct direct);
dirp = (struct direct *) (dbp->db_data);
while (ndir--) {
if (dirp->d_ino != 0) {
ent[nent] = dirp->d_ino;
canino(ent[nent]);
++nent;
}
++dirp;
dinode.di_size -= sizeof(struct direct);
}
dbfree(dbp, 0);
while (nent--) {
if (walk(ent[nent]))
flag = 1;
}
}
if (flag!=0 || level==0 || dinode.di_ctime>ddate)
map[ino-1] |= DD_DUMP;
map[ino-1] &= ~DD_DEJA;
return (flag);
}
/*
* Read in an inode.
* Leave it in the buffer in disc
* (that is, cannonical) format.
*/
ireadin(diop, ino)
register struct dinode *diop;
ino_t ino;
{
register struct dinode *diip;
DISCBUF *dbp;
dbp = dbread((long)((ino-1)/INOPB + INODEI));
diip = (struct dinode *)(dbp->db_data) + (ino-1)%INOPB;
*diop = *diip; /* Structure assignment */
dbfree(dbp, 0);
}
/*
* Map logical block `lb' in the
* file whose in core inode is pointed to
* by `ip' to the associated physical
* block. Return 0 if the block will not
* map.
*/
daddr_t
imap(ip, lb)
register struct dinode *ip;
daddr_t lb;
{
register il;
daddr_t bpos, pb;
DISCBUF *dbp;
register daddr_t *bp;
register daddr_t addrs[NADDR];
l3tol(addrs, ip->di_addr, NADDR);
for (il=0; il<4; il++)
if (lb < ranges[il]) {
if (il != 0)
lb -= ranges[il-1];
bpos = lb/coeff[il];
lb %= coeff[il];
bp = &addrs[(int)bpos + offsets[il]];
if ((pb = *bp) != 0) {
while (il-- > 0) {
dbp = dbread((long) pb);
bpos = lb/coeff[il];
lb %= coeff[il];
bp = (daddr_t *)(dbp->db_data) + bpos;
dbfree(dbp, 0);
if ((pb = *bp) == 0)
break;
candaddr(pb);
}
}
return (pb);
}
return (0);
}
/*
* Dump out the map.
* The map is packed into ordinary
* tape blocks.
*/
dumpinomap()
{
register ino_t ibase;
register char *imapp;
register nmap;
union dumpdata *ddp;
ibase = 0;
while ((nmap = isize-ibase) > 0) {
ddp = ddnextbuf();
ddp->dd_type = DD_MAP;
ddp->dd_ino = ibase+1;
if (nmap > BUFSIZ)
nmap = BUFSIZ;
ddp->dd_nmap = nmap;
canint(ddp->dd_type);
canino(ddp->dd_ino);
canint(ddp->dd_nmap);
imapp = &ddp->dd_map[0];
do {
*imapp++ = map[ibase++];
} while (--nmap);
}
}
/*
* Fabricate a dump header and
* write it to the dump file. All the
* stuff in the header is in the
* usual canonical format. This makes it
* a little easier to move tapes from
* one machine to another.
*/
dumpheader()
{
register char *p;
register checksum;
dh.dh_magic = DH_MAG;
dh.dh_nino = isize;
dh.dh_bdate = bdate;
dh.dh_ddate = ddate;
dh.dh_level = level;
dh.dh_reel = reel++;
dh.dh_blocking = block;
dh.dh_nbyte = Sflag ? length : 0;
strncpy(dh.dh_dev, dev, DIRSIZ);
strncpy(dh.dh_fname, fname, 6);
strncpy(dh.dh_fpack, fpack, 6);
p = (char *) &dh;
checksum = 0;
while (p < (char *) &dh.dh_checksum)
checksum += (*p++) & 0377;
dh.dh_checksum = checksum;
canint(dh.dh_magic);
canino(dh.dh_nino);
cantime(dh.dh_bdate);
cantime(dh.dh_ddate);
canint(dh.dh_level);
canint(dh.dh_reel);
canint(dh.dh_blocking);
cansize(dh.dh_nbyte);
canint(dh.dh_checksum);
if (write(fileno(dtp), &dh, sizeof dh) != sizeof dh)
fatal("header write error");
}
/*
* Put out the end of tape item
* and the very end of the very last
* dump tape.
*/
dumpfinish()
{
register union dumpdata *ddp;
ddp = ddnextbuf();
ddp->dd_type = DD_EOT;
canint(ddp->dd_type);
ddflush();
}
/*
* Dump out all inodes with
* the appropriate flags set in the
* map.
*/
dump(flag)
register flag;
{
register ino_t i;
register mapentry;
for (i=0; i<isize; ++i) {
mapentry = map[i];
if ((mapentry&DD_DUMP)!=0 && (mapentry&DD_DIR)==flag) {
dumpi(i+1);
map[i] = 0;
}
}
}
/*
* Dump an inode.
*/
dumpi(ino)
ino_t ino;
{
struct dinode dinode;
register union dumpdata *ddp;
register DISCBUF *dbp;
daddr_t lb, pb;
int size;
++ninodes;
ireadin(&dinode, ino);
ddp = ddnextbuf();
ddp->dd_type = DD_INO;
ddp->dd_ino = ino;
canint(ddp->dd_type);
canino(ddp->dd_ino);
copyb(&ddp->dd_dinode, &dinode, sizeof(struct dinode));
lb = 0;
cansize(dinode.di_size);
while (dinode.di_size != 0) {
pb = imap(&dinode, lb);
size = dinode.di_size<BUFSIZ ? dinode.di_size : BUFSIZ;
if (pb != 0) {
dbp = dbread(pb);
if (allzeros(dbp->db_data, size) == 0) {
++nblocks;
ddp = ddnextbuf();
ddp->dd_type = DD_DATA;
ddp->dd_ino = ino;
ddp->dd_block = lb;
ddp->dd_size = size;
canint(ddp->dd_type);
canino(ddp->dd_ino);
candaddr(ddp->dd_block);
canint(ddp->dd_size);
copyb(ddp->dd_data, dbp->db_data, size);
} else
++nsparse;
dbfree(dbp, 0);
}
dinode.di_size -= size;
++lb;
}
}
/*
* Check if a disc block is all
* zero bytes. True return if it is.
*/
allzeros(cp, nb)
register char *cp;
register int nb;
{
do {
if (*cp++ != 0)
return (0);
} while (--nb);
return (1);
}
/*
* Copy a block of bytes.
*/
copyb(atp, afp, anb)
char *atp, *afp;
{
register char *tp, *fp;
register nb;
if ((nb = anb) != 0) {
tp = atp;
fp = afp;
do {
*tp++ = *fp++;
} while (--nb);
}
}
/*
* Return a pointer to the
* next data buffer.
*/
union dumpdata *
ddnextbuf()
{
register char *p;
register n;
if (ddbuf == NULL) {
ddnbuf = block * sizeof(union dumpdata);
if ((ddbuf = malloc(ddnbuf)) == NULL)
fatal("out of memory (buffer)");
ddend = &ddbuf[ddnbuf];
ddptr = (union dumpdata *) ddbuf;
}
if ((char *) ddptr == ddend) {
ddflush();
ddptr = (union dumpdata *) ddbuf;
}
if ((char *) ddptr == ddbuf) {
p = ddbuf;
n = ddnbuf;
do {
*p++ = 0;
} while (--n);
}
return (ddptr++);
}
/*
* Write out the big data buffer.
*/
ddflush()
{
used += ddlen(ddnbuf);
if (used >= length) {
nextvol();
used += ddlen(ddnbuf);
}
if (write(fileno(dtp), ddbuf, ddnbuf) != ddnbuf)
fatal("dump write error");
}
/*
* Request next dump volume.
*/
nextvol()
{
char buf[40];
if (Sflag != 0 || reel != 1) {
fclose(dtp);
fprintf(stderr, "dump: mount %s %d, type return key ...",
Sflag ? "volume" : "reel", reel);
if (gets(buf) == NULL)
exit(1);
if ((dtp = fopen(dtn, "w")) == NULL)
fatal("%s: cannot open dump file", dtn);
}
dumpheader();
used = ddlen(sizeof(dh));
}
/*
* Given a record size in bytes,
* return the number of inches/100 of tape
* that will get used.
* For blocked devices, the granularity is
* actually in bytes.
*/
long
ddlen(nbytes)
{
if (Sflag)
return (nbytes);
return (100L*((nbytes+density-1)/density) + 75);
}
/*
* Update the dump date in the
* dump date file. The date used is the
* date at the beginning of the dump.
*/
putddate()
{
register FILE *ddfp;
struct idates id;
if ((ddfp = fopen(DDATE, "r+w")) == NULL) {
fprintf(stderr, "dump: cannot update dump date\n");
return;
}
while (fread(&id, sizeof(id), 1, ddfp) == 1) {
if (strncmp(id.id_name, dev, DIRSIZ) == 0
&& id.id_incno==level) {
fseek(ddfp, (long)-sizeof(id), 1);
break;
}
}
strncpy(id.id_name, dev, DIRSIZ);
id.id_incno = level;
id.id_ddate = bdate;
if (fwrite(&id, sizeof(id), 1, ddfp) != 1)
fprintf(stderr, "dump: error updating date file\n");
fclose(ddfp);
}
/*
* Get dump date.
*/
long
getddate()
{
register FILE *ddfp;
register long ddate;
struct idates id;
ddate = 0;
if ((ddfp = fopen(DDATE, "r")) != NULL) {
while (fread(&id, sizeof(id), 1, ddfp) == 1) {
if (strncmp(id.id_name, dev, DIRSIZ)==0
&& id.id_incno<level
&& id.id_ddate>ddate)
ddate = id.id_ddate;
}
fclose(ddfp);
}
return (ddate);
}
/*
* Print out a fatal diagnostic
* and die.
*/
fatal(a)
{
fprintf(stderr, "dump: %r", &a);
fprintf(stderr, "\n");
exit(1);
}
/*
* Print out a usage message and
* exit. If you can figure out how to use
* dump from the usage message you are
* a better man than I.
*/
usage()
{
fprintf(stderr, helpmessage);
exit(1);
}
/*
* Squash off any structure
* before the last part of a file
* name and put it in the supplied
* buffer.
*/
squash(tp, fp)
register char *tp, *fp;
{
register char *np;
np = fp;
while (*fp != '\0') {
if (*fp == '/')
np = fp+1;
++fp;
}
while (*tp++ = *np++)
;
}