#include "all.h"
#include "io.h"
static void dowormcopy(void);
static int dodevcopy(void);
struct {
char* icharp;
char* charp;
int error;
int newconf; /* clear before start */
int modconf; /* write back when done */
int nextiter;
int lastiter;
int diriter;
Device* lastcw;
Device* devlist;
} f;
static Device* confdev;
static int copyworm = 0, copydev = 0;
static char *src, *dest;
static int resetparams;
Fspar fspar[] = {
{ "blocksize", RBUFSIZE, },
{ "daddrbits", sizeof(Off)*8, },
{ "indirblks", NIBLOCK, },
{ "dirblks", NDBLOCK, },
{ "namelen", NAMELEN, },
{ nil, 0, },
};
int
devcmpr(Device *d1, Device *d2)
{
while (d1 != d2) {
if(d1 == nil || d2 == nil || d1->type != d2->type)
return 1;
switch(d1->type) {
default:
print("can't compare dev: %Z\n", d1);
panic("devcmp");
return 1;
case Devmcat:
case Devmlev:
case Devmirr:
d1 = d1->cat.first;
d2 = d2->cat.first;
while(d1 && d2) {
if(devcmpr(d1, d2))
return 1;
d1 = d1->link;
d2 = d2->link;
}
break;
case Devnone:
return 0;
case Devro:
d1 = d1->ro.parent;
d2 = d2->ro.parent;
break;
case Devjuke:
case Devcw:
if(devcmpr(d1->cw.c, d2->cw.c))
return 1;
d1 = d1->cw.w;
d2 = d2->cw.w;
break;
case Devfworm:
d1 = d1->fw.fw;
d2 = d2->fw.fw;
break;
case Devwren:
case Devworm:
case Devlworm:
if(d1->wren.ctrl == d2->wren.ctrl)
if(d1->wren.targ == d2->wren.targ)
if(d1->wren.lun == d2->wren.lun)
return 0;
return 1;
case Devpart:
if(d1->part.base == d2->part.base)
if(d1->part.size == d2->part.size) {
d1 = d1->part.d;
d2 = d2->part.d;
break;
}
return 1;
}
}
return 0;
}
void
cdiag(char *s, int c1)
{
f.charp--;
if(f.error == 0) {
print("config diag: %s -- <%c>\n", s, c1);
f.error = 1;
}
}
int
cnumb(void)
{
int c, n;
c = *f.charp++;
if(c == '<') {
n = f.nextiter;
if(n >= 0) {
f.nextiter = n+f.diriter;
if(n == f.lastiter) {
f.nextiter = -1;
f.lastiter = -1;
}
do {
c = *f.charp++;
} while (c != '>');
return n;
}
n = cnumb();
if(*f.charp++ != '-') {
cdiag("- expected", f.charp[-1]);
return 0;
}
c = cnumb();
if(*f.charp++ != '>') {
cdiag("> expected", f.charp[-1]);
return 0;
}
f.lastiter = c;
f.diriter = 1;
if(n > c)
f.diriter = -1;
f.nextiter = n+f.diriter;
return n;
}
if(!isascii(c) || !isdigit(c)) {
cdiag("number expected", c);
return 0;
}
n = 0;
while(isascii(c) && isdigit(c)) {
n = n*10 + (c-'0');
c = *f.charp++;
}
f.charp--;
return n;
}
Device*
config1(int c)
{
Device *d, *t;
int m;
d = malloc(sizeof(Device));
do {
t = config();
if(d->cat.first == 0)
d->cat.first = t;
else
d->cat.last->link = t;
d->cat.last = t;
if(f.error)
return devnone;
m = *f.charp;
if(c == '(' && m == ')')
d->type = Devmcat;
else if(c == '[' && m == ']')
d->type = Devmlev;
else if(c == '{' && m == '}')
d->type = Devmirr;
} while (d->type == 0);
f.charp++;
if(d->cat.first == d->cat.last)
d = d->cat.first;
return d;
}
static void
map(Device *d)
{
Map *map;
if (d->type != Devwren || d->wren.mapped)
return;
for (map = devmap; map != nil; map = map->next)
if (devcmpr(d, map->fdev) == 0)
break;
if (map == nil)
return;
if (access(map->to, AEXIST) >= 0)
{ print("map: mapped wren %Z to existing file %s\n", d, map->to); // DEBUG
d->wren.file = map->to; /* wren -> file mapping */
}
else if (map->tdev != nil)
{ print("map: mapped wren %Z to dev %Z\n", d, map->tdev); // DEBUG
*d = *map->tdev; /* wren -> wren mapping */
}
else
print("bad mapping %Z to %s; no such file or device",
d, map->to);
d->wren.mapped = 1;
}
Device*
config(void)
{
int c, m;
Device *d;
char *icp;
if(f.error)
return devnone;
d = malloc(sizeof(Device));
c = *f.charp++;
switch(c) {
default:
cdiag("unknown type", c);
return devnone;
case '(': /* (d+) one or multiple cat */
case '[': /* [d+] one or multiple interleave */
case '{': /* {d+} a mirrored device and optional mirrors */
return config1(c);
case 'f': /* fd fake worm */
d->type = Devfworm;
d->fw.fw = config();
break;
case 'n':
d->type = Devnone;
break;
case 'w': /* w[#.]#[.#] wren [ctrl] unit [lun] */
case 'r': /* r# worm side */
case 'l': /* l# labelled-worm side */
icp = f.charp;
d->type = Devwren;
d->wren.ctrl = 0;
d->wren.targ = cnumb();
d->wren.lun = 0;
m = *f.charp;
if(m == '.') {
f.charp++;
d->wren.lun = cnumb();
m = *f.charp;
if(m == '.') {
f.charp++;
d->wren.ctrl = d->wren.targ;
d->wren.targ = d->wren.lun;
d->wren.lun = cnumb();
}
}
if(f.nextiter >= 0)
f.charp = icp-1;
if(c == 'r') /* worms are virtual and not uniqued */
d->type = Devworm;
else if(c == 'l')
d->type = Devlworm;
else
map(d); /* subject wrens to optional mapping */
break;
case 'o': /* o ro part of last cw */
if(f.lastcw == 0) {
cdiag("no cw to match", c);
return devnone;
}
return f.lastcw->cw.ro;
case 'j': /* DD jukebox */
d->type = Devjuke;
d->j.j = config();
d->j.m = config();
break;
case 'c': /* cache/worm */
d->type = Devcw;
d->cw.c = config();
d->cw.w = config();
d->cw.ro = malloc(sizeof(Device));
d->cw.ro->type = Devro;
d->cw.ro->ro.parent = d;
f.lastcw = d;
break;
case 'p': /* pd#.# partition base% size% */
d->type = Devpart;
d->part.d = config();
d->part.base = cnumb();
c = *f.charp++;
if(c != '.')
cdiag("dot expected", c);
d->part.size = cnumb();
break;
case 'x': /* xD swab a device's metadata */
d->type = Devswab;
d->swab.d = config();
break;
}
d->dlink = f.devlist;
f.devlist = d;
return d;
}
Device*
iconfig(char *s)
{
Device *d;
f.nextiter = -1;
f.lastiter = -1;
f.error = 0;
f.icharp = s;
f.charp = f.icharp;
d = config();
if(*f.charp) {
cdiag("junk on end", *f.charp);
f.error = 1;
}
return d;
}
int
testconfig(char *s)
{
iconfig(s);
return f.error;
}
/*
* if b is a prefix of a, return 0.
*/
int
astrcmp(char *a, char *b)
{
int n, c;
n = strlen(b);
if(memcmp(a, b, n) != 0)
return 1;
c = a[n];
if(c == '\0')
return 0;
if(a[n+1])
return 1;
if(isascii(c) && isdigit(c))
return 0;
return 1;
}
static Fspar *
getpar(char *name)
{
Fspar *fsp;
for (fsp = fspar; fsp->name != nil; fsp++)
if (strcmp(name, fsp->name) == 0)
return fsp;
return nil;
}
/*
* continue to parse obsolete keywords so that old configurations can
* still work.
*/
void
mergeconf(Iobuf *p)
{
char word[Maxword+1];
char *cp;
Filsys *fs;
Fspar *fsp;
for (cp = p->iobuf; *cp != '\0'; cp++) {
cp = getwrd(word, cp);
if(word[0] == '\0')
break;
else if (word[0] == '#')
while (*cp != '\n' && *cp != '\0')
cp++;
else if(strcmp(word, "service") == 0) {
cp = getwrd(word, cp);
if(service[0] == 0)
strncpy(service, word, sizeof service);
} else if(strcmp(word, "ipauth") == 0) /* obsolete */
cp = getwrd(word, cp);
else if(astrcmp(word, "ip") == 0) /* obsolete */
cp = getwrd(word, cp);
else if(astrcmp(word, "ipgw") == 0) /* obsolete */
cp = getwrd(word, cp);
else if(astrcmp(word, "ipsntp") == 0) /* obsolete */
cp = getwrd(word, cp);
else if(astrcmp(word, "ipmask") == 0) /* obsolete */
cp = getwrd(word, cp);
else if(strcmp(word, "filsys") == 0) {
cp = getwrd(word, cp);
for(fs = filsys; fs < filsys + nelem(filsys) - 1 &&
fs->name; fs++)
if(strcmp(fs->name, word) == 0)
break;
if (fs >= filsys + nelem(filsys) - 1)
panic("out of filsys structures");
if (fs->name && strcmp(fs->name, word) == 0 &&
fs->flags & FEDIT)
cp = getwrd(word, cp); /* swallow conf */
else {
fs->name = strdup(word);
cp = getwrd(word, cp);
if (word[0] == '\0')
fs->conf = nil;
else
fs->conf = strdup(word);
}
} else if ((fsp = getpar(word)) != nil) {
cp = getwrd(word, cp);
if (!isascii(word[0]) || !isdigit(word[0]))
print("bad %s value: %s", fsp->name, word);
else
fsp->declared = atol(word);
} else {
putbuf(p);
panic("unknown keyword in config block: %s", word);
}
if(*cp != '\n') {
putbuf(p);
panic("syntax error in config block at `%s'", word);
}
}
}
void
cmd_printconf(int, char *[])
{
char *p, *s;
Iobuf *iob;
iob = getbuf(confdev, 0, Brd);
if(iob == nil)
return;
if(checktag(iob, Tconfig, 0)){
putbuf(iob);
return;
}
print("config %s\n", nvrgetconfig());
for(s = p = iob->iobuf; *p != 0 && p < iob->iobuf+BUFSIZE; ){
if(*p++ != '\n')
continue;
if (strncmp(s, "ip", 2) != 0) /* don't print obsolete cmds */
print("%.*s", (int)(p-s), s);
s = p;
}
if(p != s)
print("%.*s", (int)(p-s), s);
print("end\n");
putbuf(iob);
}
void
sysinit(void)
{
int error;
char *cp, *ep;
Device *d;
Filsys *fs;
Fspar *fsp;
Iobuf *p;
cons.chan = fs_chaninit(Devcon, 1, 0);
start:
/*
* part 1 -- read the config file
*/
devnone = iconfig("n");
cp = nvrgetconfig();
print("config %s\n", cp);
confdev = d = iconfig(cp);
devinit(d);
if(f.newconf) {
p = getbuf(d, 0, Bmod);
memset(p->iobuf, 0, RBUFSIZE);
settag(p, Tconfig, 0);
} else
p = getbuf(d, 0, Brd|Bmod);
if(!p || checktag(p, Tconfig, 0))
panic("config io");
mergeconf(p);
if (resetparams) {
for (fsp = fspar; fsp->name != nil; fsp++)
fsp->declared = 0;
resetparams = 0;
}
for (fsp = fspar; fsp->name != nil; fsp++) {
/* supply defaults from this cwfs instance */
if (fsp->declared == 0) {
fsp->declared = fsp->actual;
f.modconf = 1;
}
/* warn if declared value is not our compiled-in value */
if (fsp->declared != fsp->actual)
print("warning: config %s %ld != compiled-in %ld\n",
fsp->name, fsp->declared, fsp->actual);
}
if(f.modconf) {
memset(p->iobuf, 0, BUFSIZE);
p->flags |= Bmod|Bimm;
cp = p->iobuf;
ep = p->iobuf + RBUFSIZE - 1;
if(service[0])
cp = seprint(cp, ep, "service %s\n", service);
for(fs=filsys; fs->name; fs++)
if(fs->conf && fs->conf[0] != '\0')
cp = seprint(cp, ep, "filsys %s %s\n", fs->name,
fs->conf);
for (fsp = fspar; fsp->name != nil; fsp++)
cp = seprint(cp, ep, "%s %ld\n",
fsp->name, fsp->declared);
putbuf(p);
f.modconf = f.newconf = 0;
print("config block written\n");
goto start;
}
putbuf(p);
print("service %s\n", service);
loop:
/*
* part 2 -- squeeze out the deleted filesystems
*/
for(fs=filsys; fs->name; fs++)
if(fs->conf == nil || fs->conf[0] == '\0') {
for(; fs->name; fs++)
*fs = *(fs+1);
goto loop;
}
if(filsys[0].name == nil)
panic("no filsys");
/*
* part 3 -- compile the device expression
*/
error = 0;
for(fs=filsys; fs->name; fs++) {
print("filsys %s %s\n", fs->name, fs->conf);
fs->dev = iconfig(fs->conf);
if(f.error) {
error = 1;
continue;
}
}
if(error)
panic("fs config");
/*
* part 4 -- initialize the devices
*/
for(fs=filsys; fs->name; fs++) {
delay(3000);
print("sysinit: %s\n", fs->name);
if(fs->flags & FREAM)
devream(fs->dev, 1);
if(fs->flags & FRECOVER)
devrecover(fs->dev);
devinit(fs->dev);
}
/*
* part 5 -- optionally copy devices or worms
*/
if (copyworm) {
dowormcopy(); /* can return if user quits early */
panic("copyworm bailed out!");
}
if (copydev)
if (dodevcopy() < 0)
panic("copydev failed!");
else
panic("copydev done.");
}
/* an unfinished idea. a non-blocking rawchar() would help. */
static int
userabort(char *msg)
{
USED(msg);
return 0;
}
static int
blockok(Device *d, Off a)
{
Iobuf *p = getbuf(d, a, Brd);
if (p == 0) {
print("i/o error reading %Z block %lld\n", d, (Wideoff)a);
return 0;
}
putbuf(p);
return 1;
}
/*
* special case for fake worms only:
* we need to size the inner cw's worm device.
* in particular, we want to avoid copying the fake-worm bitmap
* at the end of the device.
*
* N.B.: for real worms (e.g. cw jukes), we need to compute devsize(cw(juke)),
* *NOT* devsize(juke).
*/
static Device *
wormof(Device *dev)
{
Device *worm = dev, *cw;
if (dev->type == Devfworm) {
cw = dev->fw.fw;
if (cw != nil && cw->type == Devcw)
worm = cw->cw.w;
}
// print("wormof(%Z)=%Z\n", dev, worm);
return worm;
}
/*
* return the number of the highest-numbered block actually written, plus 1.
* 0 indicates an error.
*/
static Devsize
writtensize(Device *worm)
{
Devsize lim = devsize(worm);
Iobuf *p;
print("devsize(%Z) = %lld\n", worm, (Wideoff)lim);
if (!blockok(worm, 0) || !blockok(worm, lim-1))
return 0;
delay(5*1000);
if (userabort("sanity checks"))
return 0;
/* find worm's last valid block in case "worm" is an (f)worm */
while (lim > 0) {
if (userabort("sizing")) {
lim = 0; /* you lose */
break;
}
--lim;
p = getbuf(worm, lim, Brd);
if (p != 0) { /* actually read one okay? */
putbuf(p);
break;
}
}
print("limit(%Z) = %lld\n", worm, (Wideoff)lim);
if (lim <= 0)
return 0;
return lim + 1;
}
/* copy worm fs from "main"'s inner worm to "output" */
static void
dowormcopy(void)
{
Filsys *f1, *f2;
Device *fdev, *from, *to = nil;
Iobuf *p;
Off a;
Devsize lim;
/*
* convert file system names into Filsyss and Devices.
*/
f1 = fsstr("main");
if(f1 == nil)
panic("main file system missing");
fdev = f1->dev;
from = wormof(fdev); /* fake worm special */
if (from->type != Devfworm && from->type != Devcw) {
print("main file system is not a worm; copyworm may not do what you want!\n");
print("waiting for 20 seconds...\n");
delay(20000);
}
f2 = fsstr("output");
if(f2 == nil) {
print("no output file system - check only\n\n");
print("reading worm from %Z (worm %Z)\n", fdev, from);
} else {
to = f2->dev;
print("\ncopying worm from %Z (worm %Z) to %Z, starting in 8 seconds\n",
fdev, from, to);
delay(8000);
}
if (userabort("preparing to copy"))
return;
/*
* initialise devices, size them, more sanity checking.
*/
devinit(from);
if (0 && fdev != from) {
devinit(fdev);
print("debugging, sizing %Z first\n", fdev);
writtensize(fdev);
}
lim = writtensize(from);
if(lim == 0)
panic("no blocks to copy on %Z", from);
if (to) {
print("reaming %Z in 8 seconds\n", to);
delay(8000);
if (userabort("preparing to ream & copy"))
return;
devream(to, 0);
devinit(to);
print("copying worm: %lld blocks from %Z to %Z\n",
(Wideoff)lim, from, to);
}
/* can't read to's blocks in case to is a real WORM device */
/*
* Copy written fs blocks, a block at a time (or just read
* if no "output" fs).
*/
for (a = 0; a < lim; a++) {
if (userabort("copy"))
break;
p = getbuf(from, a, Brd);
/*
* if from is a real WORM device, we'll get errors trying to
* read unwritten blocks, but the unwritten blocks need not
* be contiguous.
*/
if (p == 0) {
print("%lld not written yet; can't read\n", (Wideoff)a);
continue;
}
if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
print("out block %lld: write error; bailing",
(Wideoff)a);
break;
}
putbuf(p);
if(a % 20000 == 0)
print("block %lld %T\n", (Wideoff)a, time(nil));
}
/*
* wrap up: sync target, loop
*/
print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
sync("wormcopy");
delay(2000);
print("looping; reset the machine at any time.\n");
for (; ; )
continue; /* await reset */
}
/* copy device from src to dest */
static int
dodevcopy(void)
{
Device *from, *to;
Iobuf *p;
Off a;
Devsize lim, tosize;
/*
* convert config strings into Devices.
*/
from = iconfig(src);
if(f.error || from == nil) {
print("bad src device %s\n", src);
return -1;
}
to = iconfig(dest);
if(f.error || to == nil) {
print("bad dest device %s\n", dest);
return -1;
}
/*
* initialise devices, size them, more sanity checking.
*/
devinit(from);
lim = devsize(from);
if(lim == 0)
panic("no blocks to copy on %Z", from);
devinit(to);
tosize = devsize(to);
if(tosize == 0)
panic("no blocks to copy on %Z", to);
/* use smaller of the device sizes */
if (tosize < lim)
lim = tosize;
print("copy %Z to %Z in 8 seconds\n", from, to);
delay(8000);
if (userabort("preparing to copy"))
return -1;
print("copying dev: %lld blocks from %Z to %Z\n", (Wideoff)lim,
from, to);
/*
* Copy all blocks, a block at a time.
*/
for (a = 0; a < lim; a++) {
if (userabort("copy"))
break;
p = getbuf(from, a, Brd);
/*
* if from is a real WORM device, we'll get errors trying to
* read unwritten blocks, but the unwritten blocks need not
* be contiguous.
*/
if (p == 0) {
print("%lld not written yet; can't read\n", (Wideoff)a);
continue;
}
if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
print("out block %lld: write error; bailing",
(Wideoff)a);
break;
}
putbuf(p);
if(a % 20000 == 0)
print("block %lld %T\n", (Wideoff)a, time(nil));
}
/*
* wrap up: sync target
*/
print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
sync("devcopy");
return 0;
}
static void
setconfig(char *dev)
{
if (dev != nil && !testconfig(dev))
nvrsetconfig(dev); /* if it fails, it will complain */
}
void
arginit(void)
{
int verb;
char *line;
char word[Maxword+1], *cp;
Filsys *fs;
if(nvrcheck() == 0) {
setconfig(conf.confdev);
if (!conf.configfirst)
return;
}
/* nvr was bad or invoker requested configuration step */
setconfig(conf.confdev);
for (;;) {
print("config: ");
if ((line = Brdline(&bin, '\n')) == nil)
return;
line[Blinelen(&bin)-1] = '\0';
cp = getwrd(word, line);
if (word[0] == '\0' || word[0] == '#')
continue;
if(strcmp(word, "end") == 0)
return;
if(strcmp(word, "halt") == 0)
exit();
if(strcmp(word, "queryjuke") == 0) {
getwrd(word, cp);
if(testconfig(word) == 0)
querychanger(iconfig(word));
continue;
}
if(strcmp(word, "allow") == 0) {
wstatallow = 1;
writeallow = 1;
continue;
}
if(strcmp(word, "copyworm") == 0) {
copyworm = 1;
continue;
}
if(strcmp(word, "copydev") == 0) {
cp = getwrd(word, cp);
if(testconfig(word))
continue;
src = strdup(word);
getwrd(word, cp);
if(testconfig(word))
continue;
dest = strdup(word);
copydev = 1;
continue;
}
if(strcmp(word, "noauth") == 0) {
noauth = !noauth;
continue;
}
if(strcmp(word, "noattach") == 0) {
noattach = !noattach;
continue;
}
if(strcmp(word, "readonly") == 0) {
readonly = 1;
continue;
}
if(strcmp(word, "ream") == 0) {
verb = FREAM;
goto gfsname;
}
if(strcmp(word, "recover") == 0) {
verb = FRECOVER;
goto gfsname;
}
if(strcmp(word, "filsys") == 0) {
verb = FEDIT;
goto gfsname;
}
if(strcmp(word, "nvram") == 0) {
getwrd(word, cp);
if(testconfig(word))
continue;
/* if it fails, it will complain */
nvrsetconfig(word);
continue;
}
if(strcmp(word, "config") == 0) {
getwrd(word, cp);
if(!testconfig(word) && nvrsetconfig(word) == 0)
f.newconf = 1;
continue;
}
if(strcmp(word, "service") == 0) {
getwrd(word, cp);
strncpy(service, word, sizeof service);
f.modconf = 1;
continue;
}
if (strcmp(word, "resetparams") == 0) {
resetparams++;
continue;
}
/*
* continue to parse obsolete keywords so that old
* configurations can still work.
*/
if (strcmp(word, "ipauth") != 0 &&
astrcmp(word, "ip") != 0 &&
astrcmp(word, "ipgw") != 0 &&
astrcmp(word, "ipmask") != 0 &&
astrcmp(word, "ipsntp") != 0) {
print("unknown config command\n");
print("\ttype end to get out\n");
continue;
}
getwrd(word, cp);
f.modconf = 1;
continue;
gfsname:
cp = getwrd(word, cp);
for(fs=filsys; fs->name; fs++)
if(strcmp(word, fs->name) == 0)
break;
if (fs->name == nil) {
memset(fs, 0, sizeof *fs);
fs->name = strdup(word);
}
switch(verb) {
case FREAM:
if(strcmp(fs->name, "main") == 0)
wstatallow = 1; /* only set, never reset */
/* fallthrough */
case FRECOVER:
fs->flags |= verb;
break;
case FEDIT:
f.modconf = 1;
getwrd(word, cp);
fs->flags |= verb;
if(word[0] == 0)
fs->conf = nil;
else if(!testconfig(word))
fs->conf = strdup(word);
break;
}
}
}
|