]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/config.c
cwfs: add optional uid argument to allow command, unify permission override code
[plan9front.git] / sys / src / cmd / cwfs / config.c
1 #include "all.h"
2 #include "io.h"
3
4 static void     dowormcopy(void);
5 static int      dodevcopy(void);
6
7 struct {
8         char*   icharp;
9         char*   charp;
10         int     error;
11         int     newconf;        /* clear before start */
12         int     modconf;        /* write back when done */
13         int     nextiter;
14         int     lastiter;
15         int     diriter;
16         Device* lastcw;
17         Device* devlist;
18 } f;
19
20 static Device* confdev;
21 static int copyworm = 0, copydev = 0;
22 static char *src, *dest;
23
24 static int nononeset;
25 static int noauthset;
26 static int readonlyset;
27 static int resetparams;
28
29 Fspar fspar[] = {
30         { "blocksize",  RBUFSIZE, },
31         { "daddrbits",  sizeof(Off)*8, },
32         { "indirblks",  NIBLOCK, },
33         { "dirblks",    NDBLOCK, },
34         { "namelen",    NAMELEN, },
35         { nil, 0, },
36 };
37
38 int
39 devcmpr(Device *d1, Device *d2)
40 {
41         while (d1 != d2) {
42                 if(d1 == nil || d2 == nil || d1->type != d2->type)
43                         return 1;
44
45                 switch(d1->type) {
46                 default:
47                         fprint(2, "can't compare dev: %Z\n", d1);
48                         panic("devcmp");
49                         return 1;
50
51                 case Devmcat:
52                 case Devmlev:
53                 case Devmirr:
54                         d1 = d1->cat.first;
55                         d2 = d2->cat.first;
56                         while(d1 && d2) {
57                                 if(devcmpr(d1, d2))
58                                         return 1;
59                                 d1 = d1->link;
60                                 d2 = d2->link;
61                         }
62                         break;
63
64                 case Devnone:
65                         return 0;
66
67                 case Devro:
68                         d1 = d1->ro.parent;
69                         d2 = d2->ro.parent;
70                         break;
71
72                 case Devjuke:
73                 case Devcw:
74                         if(devcmpr(d1->cw.c, d2->cw.c))
75                                 return 1;
76                         d1 = d1->cw.w;
77                         d2 = d2->cw.w;
78                         break;
79
80                 case Devfworm:
81                         d1 = d1->fw.fw;
82                         d2 = d2->fw.fw;
83                         break;
84
85                 case Devwren:
86                 case Devworm:
87                 case Devlworm:
88                         if(d1->wren.file || d2->wren.file){
89                                 if(d1->wren.file == nil || d2->wren.file == nil)
90                                         return 1;
91                                 return !!strcmp(d1->wren.file, d2->wren.file);
92                         }
93                         if(d1->wren.ctrl == d2->wren.ctrl)
94                         if(d1->wren.targ == d2->wren.targ)
95                         if(d1->wren.lun == d2->wren.lun)
96                                 return 0;
97                         return 1;
98
99                 case Devpart:
100                         if(d1->part.base == d2->part.base)
101                         if(d1->part.size == d2->part.size) {
102                                 d1 = d1->part.d;
103                                 d2 = d2->part.d;
104                                 break;
105                         }
106                         return 1;
107                 }
108         }
109         return 0;
110 }
111
112 void
113 cdiag(char *s, int c1)
114 {
115
116         f.charp--;
117         if(f.error == 0) {
118                 print("config diag: %s -- <%c>\n", s, c1);
119                 f.error = 1;
120         }
121 }
122
123 int
124 cnumb(void)
125 {
126         int c, n;
127
128         c = *f.charp++;
129         if(c == '<') {
130                 n = f.nextiter;
131                 if(n >= 0) {
132                         f.nextiter = n+f.diriter;
133                         if(n == f.lastiter) {
134                                 f.nextiter = -1;
135                                 f.lastiter = -1;
136                         }
137                         do {
138                                 c = *f.charp++;
139                         } while (c != '>');
140                         return n;
141                 }
142                 n = cnumb();
143                 if(*f.charp++ != '-') {
144                         cdiag("- expected", f.charp[-1]);
145                         return 0;
146                 }
147                 c = cnumb();
148                 if(*f.charp++ != '>') {
149                         cdiag("> expected", f.charp[-1]);
150                         return 0;
151                 }
152                 f.lastiter = c;
153                 f.diriter = 1;
154                 if(n > c)
155                         f.diriter = -1;
156                 f.nextiter = n+f.diriter;
157                 return n;
158         }
159         if(!isascii(c) || !isdigit(c)) {
160                 cdiag("number expected", c);
161                 return 0;
162         }
163         n = 0;
164         while(isascii(c) && isdigit(c)) {
165                 n = n*10 + (c-'0');
166                 c = *f.charp++;
167         }
168         f.charp--;
169         return n;
170 }
171
172 Device*
173 config1(int c)
174 {
175         Device *d, *t;
176         int m;
177
178         d = ialloc(sizeof(Device), 0);
179         do {
180                 t = config();
181                 if(d->cat.first == 0)
182                         d->cat.first = t;
183                 else
184                         d->cat.last->link = t;
185                 d->cat.last = t;
186                 if(f.error)
187                         return devnone;
188                 m = *f.charp;
189                 if(c == '(' && m == ')')
190                         d->type = Devmcat;
191                 else if(c == '[' && m == ']')
192                         d->type = Devmlev;
193                 else if(c == '{' && m == '}')
194                         d->type = Devmirr;
195         } while (d->type == 0);
196         f.charp++;
197         if(d->cat.first == d->cat.last)
198                 d = d->cat.first;
199         return d;
200 }
201
202 static void
203 map(Device *d)
204 {
205         Map *map;
206
207         if (d->type != Devwren || d->wren.mapped)
208                 return;
209         for (map = devmap; map != nil; map = map->next)
210                 if (devcmpr(d, map->fdev) == 0)
211                         break;
212         if (map == nil)
213                 return;
214         if (access(map->to, AEXIST) >= 0){
215                 if(chatty)
216                         print("map: mapped wren %Z to existing file %s\n", d, map->to);
217                 d->wren.file = map->to;         /* wren -> file mapping */
218         } else if (map->tdev != nil){
219                 if(chatty)
220                         print("map: mapped wren %Z to dev %Z\n", d, map->tdev);
221                 *d = *map->tdev;                /* wren -> wren mapping */
222         } else
223                 fprint(2, "bad mapping %Z to %s; no such file or device", d, map->to);
224         d->wren.mapped = 1;
225 }
226
227 Device*
228 config(void)
229 {
230         int c, m;
231         Device *d;
232         char *icp, *s, *e;
233
234         if(f.error)
235                 return devnone;
236         d = ialloc(sizeof(Device), 0);
237         c = *f.charp++;
238         switch(c) {
239         default:
240                 cdiag("unknown type", c);
241                 return devnone;
242
243         case '(':       /* (d+) one or multiple cat */
244         case '[':       /* [d+] one or multiple interleave */
245         case '{':       /* {d+} a mirrored device and optional mirrors */
246                 return config1(c);
247
248         case 'f':       /* fd fake worm */
249                 d->type = Devfworm;
250                 d->fw.fw = config();
251                 break;
252
253         case 'n':
254                 d->type = Devnone;
255                 break;
256
257         case '/':       /* /path/to/file        mapped file */
258         case '"':       /* "/path/to/file"      mapped file */
259         case '\'':      /* '/path/to/file'      mapped file */
260         Mapped:
261                 d->type = Devwren;
262                 if(c == '/'){
263                         s = f.charp-1;
264                         for(e = s+1; *e; e++)
265                                 if(*e == ')' || *e == ']' || *e == '}')
266                                         break;
267                         f.charp = e;
268                 } else {
269                         s = f.charp;
270                         if((e = strchr(s, c)) == nil){
271                                 cdiag("unterminated string", c);
272                                 return devnone;
273                         }
274                         f.charp = e+1;
275                 }
276                 d->wren.ctrl = -1;
277                 d->wren.targ = -1;
278                 d->wren.lun = -1;
279                 d->wren.file = ialloc((e - s) + 1, 0);
280                 memmove(d->wren.file, s, e - s);
281                 d->wren.file[e - s] = 0;
282                 break;
283
284         case 'w':       /* w[#.]#[.#] wren      [ctrl] unit [lun] */
285                 switch(*f.charp){
286                 case '/':
287                 case '"':
288                 case '\'':
289                         c = *f.charp++;
290                         goto Mapped;
291                 }
292         case 'r':       /* r# worm side */
293         case 'l':       /* l# labelled-worm side */
294                 icp = f.charp;
295                 d->type = Devwren;
296                 d->wren.ctrl = 0;
297                 d->wren.targ = cnumb();
298                 d->wren.lun = 0;
299                 m = *f.charp;
300                 if(m == '.') {
301                         f.charp++;
302                         d->wren.lun = cnumb();
303                         m = *f.charp;
304                         if(m == '.') {
305                                 f.charp++;
306                                 d->wren.ctrl = d->wren.targ;
307                                 d->wren.targ = d->wren.lun;
308                                 d->wren.lun = cnumb();
309                         }
310                 }
311                 if(f.nextiter >= 0)
312                         f.charp = icp-1;
313                 if(c == 'r')            /* worms are virtual and not uniqued */
314                         d->type = Devworm;
315                 else if(c == 'l')
316                         d->type = Devlworm;
317                 else
318                         map(d);         /* subject wrens to optional mapping */
319                 break;
320
321         case 'o':       /* o ro part of last cw */
322                 if(f.lastcw == 0) {
323                         cdiag("no cw to match", c);
324                         return devnone;
325                 }
326                 return f.lastcw->cw.ro;
327
328         case 'j':       /* DD jukebox */
329                 d->type = Devjuke;
330                 d->j.j = config();
331                 d->j.m = config();
332                 break;
333
334         case 'c':       /* cache/worm */
335                 d->type = Devcw;
336                 d->cw.c = config();
337                 d->cw.w = config();
338                 d->cw.ro = ialloc(sizeof(Device), 0);
339                 d->cw.ro->type = Devro;
340                 d->cw.ro->ro.parent = d;
341                 f.lastcw = d;
342                 break;
343
344         case 'p':       /* pd#.# partition base% size% */
345                 d->type = Devpart;
346                 d->part.d = config();
347                 d->part.base = cnumb();
348                 c = *f.charp++;
349                 if(c != '.')
350                         cdiag("dot expected", c);
351                 d->part.size = cnumb();
352                 break;
353
354         case 'x':       /* xD swab a device's metadata */
355                 d->type = Devswab;
356                 d->swab.d = config();
357                 break;
358         }
359         d->dlink = f.devlist;
360         f.devlist = d;
361         return d;
362 }
363
364 Device*
365 iconfig(char *s)
366 {
367         Device *d;
368
369         f.nextiter = -1;
370         f.lastiter = -1;
371         f.error = 0;
372         f.icharp = s;
373         f.charp = f.icharp;
374         d = config();
375         if(*f.charp) {
376                 cdiag("junk on end", *f.charp);
377                 f.error = 1;
378         }
379         return d;
380 }
381
382 int
383 testconfig(char *s)
384 {
385         iconfig(s);
386         return f.error;
387 }
388
389 /*
390  * if b is a prefix of a, return 0.
391  */
392 int
393 astrcmp(char *a, char *b)
394 {
395         int n, c;
396
397         n = strlen(b);
398         if(memcmp(a, b, n) != 0)
399                 return 1;
400         c = a[n];
401         if(c == '\0')
402                 return 0;
403         if(a[n+1])
404                 return 1;
405         if(isascii(c) && isdigit(c))
406                 return 0;
407         return 1;
408 }
409
410 static Fspar *
411 getpar(char *name)
412 {
413         Fspar *fsp;
414
415         for (fsp = fspar; fsp->name != nil; fsp++)
416                 if (strcmp(name, fsp->name) == 0)
417                         return fsp;
418         return nil;
419 }
420
421 /*
422  * continue to parse obsolete keywords so that old configurations can
423  * still work.
424  */
425 void
426 mergeconf(Iobuf *p)
427 {
428         char word[Maxword+1];
429         char *cp;
430         Filsys *fs;
431         Fspar *fsp;
432
433         if(!noauthset)
434                 noauth = 0;
435         if(!nononeset)
436                 nonone = 0;
437         if(!noatimeset)
438                 noatime = 0;
439         if(!readonlyset)
440                 readonly = 0;
441         for (cp = p->iobuf; *cp != '\0'; cp++) {
442                 cp = getwrd(word, cp);
443                 if(word[0] == '\0')
444                         break;
445                 else if (word[0] == '#')
446                         while (*cp != '\n' && *cp != '\0')
447                                 cp++;
448                 else if(strcmp(word, "service") == 0) {
449                         cp = getwrd(word, cp);
450                         if(service[0] == 0)
451                                 strncpy(service, word, sizeof service);
452                 } else if(strcmp(word, "noauth") == 0){
453                         if(!noauthset)
454                                 noauth = 1;
455                 } else if(strcmp(word, "nonone") == 0){
456                         if(!nononeset)
457                                 nonone = 1;
458                 } else if(strcmp(word, "noatime") == 0){
459                         if(!noatimeset)
460                                 noatime = 1;
461                 } else if(strcmp(word, "readonly") == 0){
462                         if(!readonlyset)
463                                 readonly = 1;
464                 } else if(strcmp(word, "newcache") == 0){
465                         conf.newcache = 1;
466                 } else if(strcmp(word, "ipauth") == 0)  /* obsolete */
467                         cp = getwrd(word, cp);
468                 else if(astrcmp(word, "ip") == 0)       /* obsolete */
469                         cp = getwrd(word, cp);
470                 else if(astrcmp(word, "ipgw") == 0)     /* obsolete */
471                         cp = getwrd(word, cp);
472                 else if(astrcmp(word, "ipsntp") == 0)   /* obsolete */
473                         cp = getwrd(word, cp);
474                 else if(astrcmp(word, "ipmask") == 0)   /* obsolete */
475                         cp = getwrd(word, cp);
476                 else if(strcmp(word, "filsys") == 0) {
477                         cp = getwrd(word, cp);
478                         for(fs = filsys; fs < filsys + nelem(filsys) - 1 &&
479                             fs->name; fs++)
480                                 if(strcmp(fs->name, word) == 0)
481                                         break;
482                         if (fs >= filsys + nelem(filsys) - 1)
483                                 panic("out of filsys structures");
484                         if (fs->name && strcmp(fs->name, word) == 0 &&
485                             fs->flags & FEDIT)
486                                 cp = getwrd(word, cp);  /* swallow conf */
487                         else {
488                                 fs->name = strdup(word);
489                                 cp = getwrd(word, cp);
490                                 if (word[0] == '\0')
491                                         fs->conf = nil;
492                                 else
493                                         fs->conf = strdup(word);
494                         }
495                 } else if ((fsp = getpar(word)) != nil) {
496                         cp = getwrd(word, cp);
497                         if (!isascii(word[0]) || !isdigit(word[0]))
498                                 fprint(2, "bad %s value: %s", fsp->name, word);
499                         else
500                                 fsp->declared = atol(word);
501                 } else {
502                         putbuf(p);
503                         panic("unknown keyword in config block: %s", word);
504                 }
505
506                 if(*cp != '\n') {
507                         putbuf(p);
508                         panic("syntax error in config block at `%s'", word);
509                 }
510         }
511 }
512
513 void
514 cmd_printconf(int, char *[])
515 {
516         char *p, *s;
517         Iobuf *iob;
518
519         iob = getbuf(confdev, 0, Brd);
520         if(iob == nil)
521                 return;
522         if(checktag(iob, Tconfig, 0)){
523                 putbuf(iob);
524                 return;
525         }
526
527         print("config %s\n", nvrgetconfig());
528         for(s = p = iob->iobuf; *p != 0 && p < iob->iobuf+BUFSIZE; ){
529                 if(*p++ != '\n')
530                         continue;
531                 if (strncmp(s, "ip", 2) != 0)   /* don't print obsolete cmds */
532                         print("%.*s", (int)(p-s), s);
533                 s = p;
534         }
535         if(p != s)
536                 print("%.*s", (int)(p-s), s);
537         print("end\n");
538
539         putbuf(iob);
540 }
541
542 void
543 sysinit(void)
544 {
545         int error;
546         char *cp, *ep;
547         Device *d;
548         Filsys *fs;
549         Fspar *fsp;
550         Iobuf *p;
551
552         cons.chan = fs_chaninit(1, 0);
553
554 start:
555         /*
556          * part 1 -- read the config file
557          */
558         devnone = iconfig("n");
559
560         cp = nvrgetconfig();
561         if(chatty)
562                 print("config %s\n", cp);
563
564         confdev = d = iconfig(cp);
565         devinit(d);
566         if(f.newconf) {
567                 p = getbuf(d, 0, Bmod);
568                 memset(p->iobuf, 0, RBUFSIZE);
569                 settag(p, Tconfig, 0);
570         } else
571                 p = getbuf(d, 0, Brd|Bmod);
572         if(!p || checktag(p, Tconfig, 0))
573                 panic("config io");
574
575         mergeconf(p);
576
577         if (resetparams) {
578                 for (fsp = fspar; fsp->name != nil; fsp++)
579                         fsp->declared = 0;
580                 resetparams = 0;
581         }
582
583         for (fsp = fspar; fsp->name != nil; fsp++) {
584                 /* supply defaults from this cwfs instance */
585                 if (fsp->declared == 0) {
586                         fsp->declared = fsp->actual;
587                         f.modconf = 1;
588                 }
589                 /* warn if declared value is not our compiled-in value */
590                 if (fsp->declared != fsp->actual)
591                         fprint(2, "warning: config %s %ld != compiled-in %ld\n",
592                                 fsp->name, fsp->declared, fsp->actual);
593         }
594
595         if(f.modconf) {
596                 memset(p->iobuf, 0, BUFSIZE);
597                 p->flags |= Bmod|Bimm;
598                 cp = p->iobuf;
599                 ep = p->iobuf + RBUFSIZE - 1;
600                 if(service[0])
601                         cp = seprint(cp, ep, "service %s\n", service);
602                 for(fs=filsys; fs->name; fs++)
603                         if(fs->conf && fs->conf[0] != '\0')
604                                 cp = seprint(cp, ep, "filsys %s %s\n", fs->name,
605                                         fs->conf);
606                 if(noauth)
607                         cp = seprint(cp, ep, "noauth\n");
608                 if(nonone)
609                         cp = seprint(cp, ep, "nonone\n");
610                 if(noatime)
611                         cp = seprint(cp, ep, "noatime\n");
612                 if(readonly)
613                         cp = seprint(cp, ep, "readonly\n");
614                 if(conf.newcache)
615                         cp = seprint(cp, ep, "newcache\n");
616                 for (fsp = fspar; fsp->name != nil; fsp++)
617                         cp = seprint(cp, ep, "%s %ld\n",
618                                 fsp->name, fsp->declared);
619
620                 putbuf(p);
621                 f.modconf = f.newconf = 0;
622                 goto start;
623         }
624         putbuf(p);
625
626         if(chatty)
627                 print("service %s\n", service);
628
629 loop:
630         /*
631          * part 2 -- squeeze out the deleted filesystems
632          */
633         for(fs=filsys; fs->name; fs++)
634                 if(fs->conf == nil || fs->conf[0] == '\0') {
635                         for(; fs->name; fs++)
636                                 *fs = *(fs+1);
637                         goto loop;
638                 }
639         if(filsys[0].name == nil)
640                 panic("no filsys");
641
642         /*
643          * part 3 -- compile the device expression
644          */
645         error = 0;
646         for(fs=filsys; fs->name; fs++) {
647                 if(chatty)
648                         print("filsys %s %s\n", fs->name, fs->conf);
649                 fs->dev = iconfig(fs->conf);
650                 if(f.error) {
651                         error = 1;
652                         continue;
653                 }
654         }
655         if(error)
656                 panic("fs config");
657
658         /*
659          * part 4 -- initialize the devices
660          */
661         for(fs=filsys; fs->name; fs++) {
662                 if(chatty)
663                         print("sysinit: %s\n", fs->name);
664                 if(fs->flags & FREAM)
665                         devream(fs->dev, 1);
666                 if(fs->flags & FRECOVER)
667                         devrecover(fs->dev);
668                 devinit(fs->dev);
669         }
670
671         /*
672          * part 5 -- optionally copy devices or worms
673          */
674         if (copyworm) {
675                 dowormcopy();           /* can return if user quits early */
676                 panic("copyworm bailed out!");
677         }
678         if (copydev)
679                 if (dodevcopy() < 0)
680                         panic("copydev failed!");
681                 else
682                         panic("copydev done.");
683 }
684
685 /* an unfinished idea.  a non-blocking rawchar() would help. */
686 static int
687 userabort(char *msg)
688 {
689         USED(msg);
690         return 0;
691 }
692
693 static int
694 blockok(Device *d, Off a)
695 {
696         Iobuf *p = getbuf(d, a, Brd);
697
698         if (p == 0) {
699                 fprint(2, "i/o error reading %Z block %lld\n", d, (Wideoff)a);
700                 return 0;
701         }
702         putbuf(p);
703         return 1;
704 }
705
706 /*
707  * special case for fake worms only:
708  * we need to size the inner cw's worm device.
709  * in particular, we want to avoid copying the fake-worm bitmap
710  * at the end of the device.
711  *
712  * N.B.: for real worms (e.g. cw jukes), we need to compute devsize(cw(juke)),
713  * *NOT* devsize(juke).
714  */
715 static Device *
716 wormof(Device *dev)
717 {
718         Device *worm = dev, *cw;
719
720         if (dev->type == Devfworm) {
721                 cw = dev->fw.fw;
722                 if (cw != nil && cw->type == Devcw)
723                         worm = cw->cw.w;
724         }
725         return worm;
726 }
727
728 /*
729  * return the number of the highest-numbered block actually written, plus 1.
730  * 0 indicates an error.
731  */
732 static Devsize
733 writtensize(Device *worm)
734 {
735         Devsize lim = devsize(worm);
736         Iobuf *p;
737
738         if(chatty)
739                 print("devsize(%Z) = %lld\n", worm, (Wideoff)lim);
740         if (!blockok(worm, 0) || !blockok(worm, lim-1))
741                 return 0;
742         delay(5*1000);
743         if(userabort("sanity checks"))
744                 return 0;
745
746         /* find worm's last valid block in case "worm" is an (f)worm */
747         while (lim > 0) {
748                 if (userabort("sizing")) {
749                         lim = 0;                /* you lose */
750                         break;
751                 }
752                 --lim;
753                 p = getbuf(worm, lim, Brd);
754                 if (p != 0) {                   /* actually read one okay? */
755                         putbuf(p);
756                         break;
757                 }
758         }
759         if(chatty)
760                 print("limit(%Z) = %lld\n", worm, (Wideoff)lim);
761         if (lim <= 0)
762                 return 0;
763         return lim + 1;
764 }
765
766 /* copy worm fs from "main"'s inner worm to "output" */
767 static void
768 dowormcopy(void)
769 {
770         Filsys *f1, *f2;
771         Device *fdev, *from, *to = nil;
772         Iobuf *p;
773         Off a;
774         Devsize lim;
775
776         /*
777          * convert file system names into Filsyss and Devices.
778          */
779
780         f1 = fsstr("main");
781         if(f1 == nil)
782                 panic("main file system missing");
783         fdev = f1->dev;
784         from = wormof(fdev);                    /* fake worm special */
785         if (from->type != Devfworm && from->type != Devcw) {
786                 print("main file system is not a worm; copyworm may not do what you want!\n");
787                 print("waiting for 20 seconds...\n");
788                 delay(20000);
789         }
790
791         f2 = fsstr("output");
792         if(f2 == nil) {
793                 print("no output file system - check only\n\n");
794                 print("reading worm from %Z (worm %Z)\n", fdev, from);
795         } else {
796                 to = f2->dev;
797                 print("\ncopying worm from %Z (worm %Z) to %Z, starting in 8 seconds\n",
798                         fdev, from, to);
799                 delay(8000);
800         }
801         if (userabort("preparing to copy"))
802                 return;
803
804         /*
805          * initialise devices, size them, more sanity checking.
806          */
807
808         devinit(from);
809         if (0 && fdev != from) {
810                 devinit(fdev);
811                 print("debugging, sizing %Z first\n", fdev);
812                 writtensize(fdev);
813         }
814         lim = writtensize(from);
815         if(lim == 0)
816                 panic("no blocks to copy on %Z", from);
817         if (to) {
818                 print("reaming %Z in 8 seconds\n", to);
819                 delay(8000);
820                 if (userabort("preparing to ream & copy"))
821                         return;
822                 devream(to, 0);
823                 devinit(to);
824                 print("copying worm: %lld blocks from %Z to %Z\n",
825                         (Wideoff)lim, from, to);
826         }
827         /* can't read to's blocks in case to is a real WORM device */
828
829         /*
830          * Copy written fs blocks, a block at a time (or just read
831          * if no "output" fs).
832          */
833
834         for (a = 0; a < lim; a++) {
835                 if (userabort("copy"))
836                         break;
837                 p = getbuf(from, a, Brd);
838                 /*
839                  * if from is a real WORM device, we'll get errors trying to
840                  * read unwritten blocks, but the unwritten blocks need not
841                  * be contiguous.
842                  */
843                 if (p == 0) {
844                         print("%lld not written yet; can't read\n", (Wideoff)a);
845                         continue;
846                 }
847                 if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
848                         print("out block %lld: write error; bailing",
849                                 (Wideoff)a);
850                         break;
851                 }
852                 putbuf(p);
853                 if(a % 20000 == 0)
854                         print("block %lld %T\n", (Wideoff)a, time(nil));
855         }
856
857         /*
858          * wrap up: sync target, loop
859          */
860         print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
861         sync("wormcopy");
862         
863         print("looping; reset the machine at any time.\n");
864         for(;;)
865                 delay(10000);           /* await reset */
866 }
867
868 /* copy device from src to dest */
869 static int
870 dodevcopy(void)
871 {
872         Device *from, *to;
873         Iobuf *p;
874         Off a;
875         Devsize lim, tosize;
876
877         /*
878          * convert config strings into Devices.
879          */
880         from = iconfig(src);
881         if(f.error || from == nil) {
882                 print("bad src device %s\n", src);
883                 return -1;
884         }
885         to = iconfig(dest);
886         if(f.error || to == nil) {
887                 print("bad dest device %s\n", dest);
888                 return -1;
889         }
890
891         /*
892          * initialise devices, size them, more sanity checking.
893          */
894
895         devinit(from);
896         lim = devsize(from);
897         if(lim == 0)
898                 panic("no blocks to copy on %Z", from);
899         devinit(to);
900         tosize = devsize(to);
901         if(tosize == 0)
902                 panic("no blocks to copy on %Z", to);
903
904         /* use smaller of the device sizes */
905         if (tosize < lim)
906                 lim = tosize;
907
908         print("copy %Z to %Z in 8 seconds\n", from, to);
909         delay(8000);
910         if (userabort("preparing to copy"))
911                 return -1;
912         print("copying dev: %lld blocks from %Z to %Z\n", (Wideoff)lim,
913                 from, to);
914
915         /*
916          * Copy all blocks, a block at a time.
917          */
918
919         for (a = 0; a < lim; a++) {
920                 if (userabort("copy"))
921                         break;
922                 p = getbuf(from, a, Brd);
923                 /*
924                  * if from is a real WORM device, we'll get errors trying to
925                  * read unwritten blocks, but the unwritten blocks need not
926                  * be contiguous.
927                  */
928                 if (p == 0) {
929                         fprint(2, "%lld not written yet; can't read\n", (Wideoff)a);
930                         continue;
931                 }
932                 if (to != 0 && devwrite(to, p->addr, p->iobuf) != 0) {
933                         print("out block %lld: write error; bailing",
934                                 (Wideoff)a);
935                         break;
936                 }
937                 putbuf(p);
938                 if(a % 20000 == 0)
939                         print("block %lld %T\n", (Wideoff)a, time(nil));
940         }
941
942         /*
943          * wrap up: sync target
944          */
945         print("copied %lld blocks from %Z to %Z\n", (Wideoff)a, from, to);
946         sync("devcopy");
947         return 0;
948 }
949
950 static int
951 setconfig(char *dev)
952 {
953         if (dev && !testconfig(dev)){
954                 nvrsetconfig(dev);      /* if it fails, it will complain */
955                 return 0;
956         }
957         return -1;
958 }
959
960 void
961 arginit(void)
962 {
963         int verb;
964         char *line;
965         char word[Maxword+1], *cp;
966         Filsys *fs;
967
968         nvrcheck();
969         if(!setconfig(conf.confdev) && !conf.configfirst)
970                 return;
971
972         for (;;) {
973                 print("config: ");
974                 if ((line = Brdline(&bin, '\n')) == nil)
975                         return;
976                 line[Blinelen(&bin)-1] = '\0';
977
978                 cp = getwrd(word, line);
979                 if (word[0] == '\0' || word[0] == '#')
980                         continue;
981                 if(strcmp(word, "end") == 0)
982                         return;
983                 if(strcmp(word, "halt") == 0)
984                         exit();
985                 if(strcmp(word, "queryjuke") == 0) {
986                         getwrd(word, cp);
987                         if(testconfig(word) == 0)
988                                 querychanger(iconfig(word));
989                         continue;
990                 }
991                 if(strcmp(word, "copyworm") == 0) {
992                         copyworm = 1;
993                         continue;
994                 }
995                 if(strcmp(word, "copydev") == 0) {
996                         cp = getwrd(word, cp);
997                         if(testconfig(word))
998                                 continue;
999                         src = strdup(word);
1000                         getwrd(word, cp);
1001                         if(testconfig(word))
1002                                 continue;
1003                         dest = strdup(word);
1004                         copydev = 1;
1005                         continue;
1006                 }
1007                 if(strcmp(word, "noattach") == 0) {
1008                         noattach = !noattach;
1009                         print("attach %s\n", noattach ? "disallowed" : "allowed");
1010                         continue;
1011                 }
1012                 if(strcmp(word, "noauth") == 0) {
1013                         noauth = !noauth;
1014                         print("auth %s\n", noauth ? "disabled" : "enabled");
1015                         noauthset++;
1016                         f.modconf = 1;
1017                         continue;
1018                 }
1019                 if(strcmp(word, "nonone") == 0) {
1020                         nonone = !nonone;
1021                         print("none %s\n", nonone ? "disabled" : "enabled");
1022                         nononeset++;
1023                         f.modconf = 1;
1024                         continue;
1025                 }
1026                 if(strcmp(word, "noatime") == 0) {
1027                         noatime = !noatime;
1028                         print("atime %s\n", noatime ? "disabled" : "enabled");
1029                         noatimeset++;
1030                         f.modconf = 1;
1031                         continue;
1032                 }
1033                 if(strcmp(word, "readonly") == 0) {
1034                         readonly = !readonly;
1035                         print("filesystem %s\n", readonly ? "readonly" : "writable");
1036                         readonlyset++;
1037                         f.modconf = 1;
1038                         continue;
1039                 }
1040
1041                 if(strcmp(word, "ream") == 0) {
1042                         verb = FREAM;
1043                         goto gfsname;
1044                 }
1045                 if(strcmp(word, "recover") == 0) {
1046                         verb = FRECOVER;
1047                         goto gfsname;
1048                 }
1049                 if(strcmp(word, "filsys") == 0) {
1050                         verb = FEDIT;
1051                         goto gfsname;
1052                 }
1053
1054                 if(strcmp(word, "nvram") == 0) {
1055                         getwrd(word, cp);
1056                         if(testconfig(word))
1057                                 continue;
1058                         /* if it fails, it will complain */
1059                         nvrsetconfig(word);
1060                         continue;
1061                 }
1062                 if(strcmp(word, "config") == 0) {
1063                         getwrd(word, cp);
1064                         if(!testconfig(word) && nvrsetconfig(word) == 0)
1065                                 f.newconf = 1;
1066                         continue;
1067                 }
1068                 if(strcmp(word, "service") == 0) {
1069                         getwrd(word, cp);
1070                         strncpy(service, word, sizeof service);
1071                         f.modconf = 1;
1072                         continue;
1073                 }
1074                 if (strcmp(word, "resetparams") == 0) {
1075                         resetparams++;
1076                         continue;
1077                 }
1078
1079                 /*
1080                  * continue to parse obsolete keywords so that old
1081                  * configurations can still work.
1082                  */
1083                 if (strcmp(word, "ipauth") != 0 &&
1084                     astrcmp(word, "ip") != 0 &&
1085                     astrcmp(word, "ipgw") != 0 &&
1086                     astrcmp(word, "ipmask") != 0 &&
1087                     astrcmp(word, "ipsntp") != 0) {
1088                         print("unknown config command\n");
1089                         print("\ttype end to get out\n");
1090                         continue;
1091                 }
1092
1093                 getwrd(word, cp);
1094                 f.modconf = 1;
1095                 continue;
1096
1097         gfsname:
1098                 cp = getwrd(word, cp);
1099                 for(fs=filsys; fs->name; fs++)
1100                         if(strcmp(word, fs->name) == 0)
1101                                 break;
1102                 if (fs->name == nil) {
1103                         memset(fs, 0, sizeof *fs);
1104                         fs->name = strdup(word);
1105                 }
1106                 switch(verb) {
1107                 case FREAM:
1108                 case FRECOVER:
1109                         fs->flags |= verb;
1110                         break;
1111                 case FEDIT:
1112                         f.modconf = 1;
1113                         getwrd(word, cp);
1114                         fs->flags |= verb;
1115                         if(word[0] == 0)
1116                                 fs->conf = nil;
1117                         else if(!testconfig(word))
1118                                 fs->conf = strdup(word);
1119                         break;
1120                 }
1121         }
1122 }