]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/cw.c
cwfs: fix interpretation of startdump argument
[plan9front.git] / sys / src / cmd / cwfs / cw.c
1 /*
2  * cached-worm device
3  */
4 #include "all.h"
5
6 #define CDEV(d)         ((d)->cw.c)
7 #define WDEV(d)         ((d)->cw.w)
8 #define RDEV(d)         ((d)->cw.ro)
9
10 enum {
11         FIRST           = SUPER_ADDR,
12
13         ADDFREE         = 100,
14         CACHE_ADDR      = SUPER_ADDR,
15         MAXAGE          = 10000,
16 };
17
18 /* cache state */
19 enum
20 {
21         /* states -- beware these are recorded on the cache */
22                                 /*    cache    worm     */
23         Cnone = 0,              /*      0       ?       */
24         Cdirty,                 /*      1       0       */
25         Cdump,                  /*      1       0->1    */
26         Cread,                  /*      1       1       */
27         Cwrite,                 /*      2       1       */
28         Cdump1,                 /* inactive form of dump */
29         Cerror,
30
31         /* opcodes -- these are not recorded */
32         Onone,
33         Oread,
34         Owrite,
35         Ogrow,
36         Odump,
37         Orele,
38         Ofree,
39 };
40
41 typedef struct  Cw      Cw;
42 struct  Cw
43 {
44         Device* dev;
45         Device* cdev;
46         Device* wdev;
47         Device* rodev;
48         Cw*     link;
49
50         int     dbucket;        /* last bucket dumped */
51         Off     daddr;          /* last block dumped */
52         Off     ncopy;
53         int     nodump;
54 /*
55  * following are cached variables for dumps
56  */
57         Off     fsize;
58         Off     wsize;
59         Off     ndump;
60         int     depth;
61         int     all;            /* local flag to recur on modified dirs */
62         int     allflag;        /* global flag to recur on modified dirs */
63         Off     falsehits;      /* times recur found modified blocks */
64         struct {
65                 char    name[500];
66                 char    namepad[NAMELEN+10];
67         };
68 };
69
70 static char* cwnames[] =
71 {
72         [Cnone]         "none",
73         [Cdirty]        "dirty",
74         [Cdump]         "dump",
75         [Cread]         "read",
76         [Cwrite]        "write",
77         [Cdump1]        "dump1",
78         [Cerror]        "error",
79
80         [Onone]         "none",
81         [Oread]         "read",
82         [Owrite]        "write",
83         [Ogrow]         "grow",
84         [Odump]         "dump",
85         [Orele]         "rele",
86 };
87
88 Centry* getcentry(Bucket*, Off);
89 int     cwio(Device*, Off, void*, int);
90 void    cmd_cwcmd(int, char*[]);
91
92 /*
93  * console command
94  * initiate a dump
95  */
96 void
97 cmd_dump(int argc, char *argv[])
98 {
99         Filsys *fs;
100
101         fs = cons.curfs;
102         if(argc > 1)
103                 fs = fsstr(argv[1]);
104         if(fs == 0) {
105                 print("%s: unknown file system\n", argv[1]);
106                 return;
107         }
108         cfsdump(fs);
109 }
110
111 /*
112  * console command
113  * worm stats
114  */
115 static void
116 cmd_statw(int, char*[])
117 {
118         Filsys *fs;
119         Iobuf *p;
120         Superb *sb;
121         Cache *h;
122         Bucket *b;
123         Centry *c, *ce;
124         Off m, nw, bw, state[Onone];
125         Off sbfsize, sbcwraddr, sbroraddr, sblast, sbnext;
126         Off hmsize, hmaddr, dsize, dsizepct;
127         Device *dev;
128         Cw *cw;
129         int s;
130
131         fs = cons.curfs;
132         dev = fs->dev;
133         if(dev->type != Devcw) {
134                 print("curfs not type cw\n");
135                 return;
136         }
137
138         cw = dev->private;
139         if(cw == 0) {
140                 print("curfs not inited\n");
141                 return;
142         }
143
144         print("cwstats %s\n", fs->name);
145
146         sbfsize = 0;
147         sbcwraddr = 0;
148         sbroraddr = 0;
149         sblast = 0;
150         sbnext = 0;
151
152         print("\tfilesys %s\n", fs->name);
153 //      print("\tnio   =%7W%7W%7W\n", cw->ncwio+0, cw->ncwio+1, cw->ncwio+2);
154         p = getbuf(dev, cwsaddr(dev), Brd);
155         if(!p || checktag(p, Tsuper, QPSUPER)) {
156                 print("cwstats: checktag super\n");
157                 if(p) {
158                         putbuf(p);
159                         p = 0;
160                 }
161         }
162         if(p) {
163                 sb = (Superb*)p->iobuf;
164                 sbfsize = sb->fsize;
165                 sbcwraddr = sb->cwraddr;
166                 sbroraddr = sb->roraddr;
167                 sblast = sb->last;
168                 sbnext = sb->next;
169                 putbuf(p);
170         }
171
172         p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
173         if(!p || checktag(p, Tcache, QPSUPER)) {
174                 print("cwstats: checktag super\n");
175                 if(p)
176                         putbuf(p);
177                 return;
178         }
179         h = (Cache*)p->iobuf;
180         hmaddr = h->maddr;
181         hmsize = h->msize;
182
183         print("\t\tmaddr  = %8lld\n", (Wideoff)hmaddr);
184         print("\t\tmsize  = %8lld\n", (Wideoff)hmsize);
185         print("\t\tcaddr  = %8lld\n", (Wideoff)h->caddr);
186         print("\t\tcsize  = %8lld\n", (Wideoff)h->csize);
187         print("\t\tsbaddr = %8lld\n", (Wideoff)h->sbaddr);
188         print("\t\tcraddr = %8lld %8lld\n",
189                 (Wideoff)h->cwraddr, (Wideoff)sbcwraddr);
190         print("\t\troaddr = %8lld %8lld\n",
191                 (Wideoff)h->roraddr, (Wideoff)sbroraddr);
192         /* print stats in terms of (first-)disc sides */
193         dsize = wormsizeside(dev, 0);
194         if (dsize < 1) {
195                 dsize = h->wsize;       /* it's probably a fake worm */
196                 if (dsize < 1)
197                         dsize = 1000;   /* don't divide by zero */
198         }
199         dsizepct = dsize/100;
200         print("\t\tfsize  = %8lld %8lld %2lld+%2lld%%\n", (Wideoff)h->fsize,
201                 (Wideoff)sbfsize, (Wideoff)h->fsize/dsize,
202                 (Wideoff)(h->fsize%dsize)/dsizepct);
203         print("\t\tslast  =          %8lld\n", (Wideoff)sblast);
204         print("\t\tsnext  =          %8lld\n", (Wideoff)sbnext);
205         print("\t\twmax   = %8lld          %2lld+%2lld%%\n",
206                 (Wideoff)h->wmax, (Wideoff)h->wmax/dsize,
207                 (Wideoff)(h->wmax%dsize)/dsizepct);
208         print("\t\twsize  = %8lld          %2lld+%2lld%%\n",
209                 (Wideoff)h->wsize, (Wideoff)h->wsize/dsize,
210                 (Wideoff)(h->wsize%dsize)/dsizepct);
211         putbuf(p);
212
213         bw = 0;                 /* max filled bucket */
214         memset(state, 0, sizeof(state));
215         for(m = 0; m < hmsize; m++) {
216                 p = getbuf(cw->cdev, hmaddr + m/BKPERBLK, Brd);
217                 if(!p || checktag(p, Tbuck, hmaddr + m/BKPERBLK)) {
218                         print("cwstats: checktag c bucket\n");
219                         if(p)
220                                 putbuf(p);
221                         return;
222                 }
223                 b = (Bucket*)p->iobuf + m%BKPERBLK;
224                 ce = b->entry + CEPERBK;
225                 nw = 0;
226                 for(c = b->entry; c < ce; c++) {
227                         s = c->state;
228                         state[s]++;
229                         if(s != Cnone && s != Cread)
230                                 nw++;
231                 }
232                 putbuf(p);
233                 if(nw > bw)
234                         bw = nw;
235         }
236         for(s = Cnone; s < Cerror; s++)
237                 print("\t\t%6lld %s\n", (Wideoff)state[s], cwnames[s]);
238         print("\t\tcache %2lld%% full\n", ((Wideoff)bw*100)/CEPERBK);
239 }
240
241 int
242 dumpblock(Device *dev)
243 {
244         Iobuf *p, *cb, *p1, *p2;
245         Cache *h;
246         Centry *c, *ce, *bc;
247         Bucket *b;
248         Off m, a, bn, msize, maddr, wmax, caddr;
249         int s1, s2, count;
250         Cw *cw;
251
252         cw = dev->private;
253         if(cw == 0 || cw->nodump)
254                 return 0;
255
256         cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
257         h = (Cache*)cb->iobuf;
258         msize = h->msize;
259         maddr = h->maddr;
260         wmax = h->wmax;
261         caddr = h->caddr;
262         putbuf(cb);
263
264         for(m=msize; m>=0; m--) {
265                 bn = cw->dbucket + 1;
266                 if(bn < 0 || bn >= msize)
267                         bn = 0;
268                 cw->dbucket = bn;
269                 a = maddr + bn/BKPERBLK;
270                 p = getbuf(cw->cdev, a, Brd);
271                 if(!p || checktag(p, Tbuck, a)) {
272                         fprint(2, "dump: checktag c bucket\n");
273                         if(p)
274                                 putbuf(p);
275                         goto stop;
276                 }
277                 b = (Bucket*)p->iobuf + bn%BKPERBLK;
278                 ce = b->entry + CEPERBK;
279                 bc = 0;
280                 for(c = b->entry; c < ce; c++)
281                         if(c->state == Cdump) {
282                                 if(bc == 0) {
283                                         bc = c;
284                                         continue;
285                                 }
286                                 if(c->waddr < cw->daddr) {
287                                         if(bc->waddr < cw->daddr &&
288                                            bc->waddr > c->waddr)
289                                                 bc = c;
290                                         continue;
291                                 }
292                                 if(bc->waddr < cw->daddr ||
293                                    bc->waddr > c->waddr)
294                                         bc = c;
295                         }
296                 if(bc) {
297                         c = bc;
298                         goto found;
299                 }
300                 putbuf(p);
301         }
302         if(cw->ncopy){
303                 if(chatty)
304                         fprint(2, "%lld blocks copied to worm\n", (Wideoff)cw->ncopy);
305                 cw->ncopy = 0;
306         }
307         cw->nodump = 1;
308         return 0;
309
310 found:
311         if (conf.newcache)
312                 a = bn + (c - b->entry)*msize + caddr;
313         else
314                 a = bn*CEPERBK + (c - b->entry) + caddr;
315         p1 = getbuf(devnone, Cwdump1, 0);
316         count = 0;
317
318 retry:
319         count++;
320         if(count > 10 || devread(cw->cdev, a, p1->iobuf)) {
321                 putbuf(p1);
322                 putbuf(p);
323                 goto stop;
324         }
325         m = c->waddr;
326         cw->daddr = m;
327         s1 = devwrite(cw->wdev, m, p1->iobuf);
328         if(s1) {
329                 p2 = getbuf(devnone, Cwdump2, 0);
330                 s2 = devread(cw->wdev, m, p2->iobuf);
331                 if(s2) {
332                         if(s1 == 0x61 && s2 == 0x60) {
333                                 putbuf(p2);
334                                 goto retry;
335                         }
336                         goto stop1;
337                 }
338                 if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
339                         goto stop1;
340                 putbuf(p2);
341         }
342         /*
343          * reread and compare
344          */
345         if(conf.dumpreread) {
346                 p2 = getbuf(devnone, Cwdump2, 0);
347                 s1 = devread(cw->wdev, m, p2->iobuf);
348                 if(s1)
349                         goto stop1;
350                 if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
351                         fprint(2, "reread C%lld W%lld didnt compare\n",
352                                 (Wideoff)a, (Wideoff)m);
353                         goto stop1;
354                 }
355                 putbuf(p2);
356         }
357
358         putbuf(p1);
359         c->state = Cread;
360         p->flags |= Bmod;
361         putbuf(p);
362
363         if(m > wmax) {
364                 cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
365                 h = (Cache*)cb->iobuf;
366                 if(m > h->wmax)
367                         h->wmax = m;
368                 putbuf(cb);
369         }
370         cw->ncopy++;
371         return 1;
372
373 stop1:
374         putbuf(p2);
375         putbuf(p1);
376         c->state = Cdump1;
377         p->flags |= Bmod;
378         putbuf(p);
379         return 1;
380
381 stop:
382         fprint(2, "stopping dump!!\n");
383         cw->nodump = 1;
384         return 0;
385 }
386
387 void
388 cwinit1(Device *dev)
389 {
390         Cw *cw;
391         static int first;
392
393         cw = dev->private;
394         if(cw)
395                 return;
396
397         if(first == 0) {
398                 cmd_install("dump", "-- make dump backup to worm", cmd_dump);
399                 cmd_install("statw", "-- cache/worm stats", cmd_statw);
400                 cmd_install("cwcmd", "subcommand -- cache/worm errata", cmd_cwcmd);
401                 roflag = flag_install("ro", "-- ro reads and writes");
402                 first = 1;
403         }
404         cw = ialloc(sizeof(Cw), 0);
405         dev->private = cw;
406
407         cw->allflag = 0;
408
409         cw->dev = dev;
410         cw->cdev = CDEV(dev);
411         cw->wdev = WDEV(dev);
412         cw->rodev = RDEV(dev);
413
414         devinit(cw->cdev);
415         devinit(cw->wdev);
416 }
417
418 void
419 cwinit(Device *dev)
420 {
421         Cw *cw;
422         Cache *h;
423         Iobuf *cb, *p;
424         Off l, m;
425
426         cwinit1(dev);
427
428         cw = dev->private;
429         l = devsize(cw->wdev);
430         cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
431         if(!cb || checktag(cb, Tcache, QPSUPER))
432                 panic("cwinit: checktag super");
433         h = (Cache*)cb->iobuf;
434         h->toytime = toytime() + SECOND(30);
435         h->time = time(nil);
436         m = h->wsize;
437         if(l != m) {
438                 if(chatty)
439                         fprint(2, "wdev changed size %lld to %lld\n",
440                                 (Wideoff)m, (Wideoff)l);
441                 h->wsize = l;
442                 cb->flags |= Bmod;
443         }
444         cw->wsize = l;
445
446         for(m=0; m<h->msize; m++) {
447                 p = getbuf(cw->cdev, h->maddr + m/BKPERBLK, Brd);
448                 if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
449                         panic("cwinit: checktag c bucket");
450                 putbuf(p);
451         }
452         putbuf(cb);
453 }
454
455 Off
456 cwsaddr(Device *dev)
457 {
458         Iobuf *cb;
459         Off sa;
460
461         cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
462         sa = ((Cache*)cb->iobuf)->sbaddr;
463         putbuf(cb);
464         return sa;
465 }
466
467 Off
468 cwraddr(Device *dev)
469 {
470         Iobuf *cb;
471         Off ra;
472
473         switch(dev->type) {
474         default:
475                 fprint(2, "unknown dev in cwraddr %Z\n", dev);
476                 return 1;
477
478         case Devcw:
479                 cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
480                 ra = ((Cache*)cb->iobuf)->cwraddr;
481                 break;
482
483         case Devro:
484                 cb = getbuf(CDEV(dev->ro.parent), CACHE_ADDR, Brd|Bres);
485                 ra = ((Cache*)cb->iobuf)->roraddr;
486                 break;
487         }
488         putbuf(cb);
489         return ra;
490 }
491
492 Devsize
493 cwsize(Device *dev)
494 {
495         Iobuf *cb;
496         Devsize fs;
497
498         cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
499         fs = ((Cache*)cb->iobuf)->fsize;
500         putbuf(cb);
501         return fs;
502 }
503
504 int
505 cwread(Device *dev, Off b, void *c)
506 {
507         return cwio(dev, b, c, Oread) == Cerror;
508 }
509
510 int
511 cwwrite(Device *dev, Off b, void *c)
512 {
513         return cwio(dev, b, c, Owrite) == Cerror;
514 }
515
516 int
517 roread(Device *dev, Off b, void *c)
518 {
519         Device *d;
520         int s;
521
522         /*
523          * maybe better is to try buffer pool first
524          */
525         d = dev->ro.parent;
526         if(d == 0 || d->type != Devcw ||
527            d->private == 0 || RDEV(d) != dev) {
528                 fprint(2, "bad rodev %Z\n", dev);
529                 return 1;
530         }
531         s = cwio(d, b, 0, Onone);
532         if(s == Cdump || s == Cdump1 || s == Cread) {
533                 s = cwio(d, b, c, Oread);
534                 if(s == Cdump || s == Cdump1 || s == Cread) {
535                         if(cons.flags & roflag)
536                                 fprint(2, "roread: %Z %lld -> %Z(hit)\n",
537                                         dev, (Wideoff)b, d);
538                         return 0;
539                 }
540         }
541         if(cons.flags & roflag)
542                 fprint(2, "roread: %Z %lld -> %Z(miss)\n",
543                         dev, (Wideoff)b, WDEV(d));
544         return devread(WDEV(d), b, c);
545 }
546
547 int
548 cwio(Device *dev, Off addr, void *buf, int opcode)
549 {
550         Iobuf *p, *p1, *p2, *cb;
551         Cache *h;
552         Bucket *b;
553         Centry *c;
554         Off bn, a1, a2, max, newmax;
555         int state;
556         Cw *cw;
557
558         cw = dev->private;
559
560         cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
561         h = (Cache*)cb->iobuf;
562         if(toytime() >= h->toytime) {
563                 cb->flags |= Bmod;
564                 h->toytime = toytime() + SECOND(30);
565                 h->time = time(nil);
566         }
567
568         if(addr < 0) {
569                 putbuf(cb);
570                 return Cerror;
571         }
572
573         bn = addr % h->msize;
574         a1 = h->maddr + bn/BKPERBLK;
575         if (conf.newcache)
576                 a2 = bn + h->caddr;
577         else
578                 a2 = bn*CEPERBK + h->caddr;
579         max = h->wmax;
580
581         putbuf(cb);
582         newmax = 0;
583
584         p = getbuf(cw->cdev, a1, Brd|Bmod);
585         if(!p || checktag(p, Tbuck, a1))
586                 panic("cwio: checktag c bucket");
587         b = (Bucket*)p->iobuf + bn%BKPERBLK;
588
589         c = getcentry(b, addr);
590         if(c == 0) {
591                 putbuf(p);
592                 fprint(2, "%Z disk cache bucket %lld is full\n",
593                         cw->cdev, (Wideoff)a1);
594                 return Cerror;
595         }
596         if (conf.newcache)
597                 a2 += (c - b->entry) * h->msize;
598         else
599                 a2 += c - b->entry;
600
601         state = c->state;
602         switch(opcode) {
603         default:
604                 goto bad;
605
606         case Onone:
607                 break;
608
609         case Oread:
610                 switch(state) {
611                 default:
612                         goto bad;
613
614                 case Cread:
615                         if(!devread(cw->cdev, a2, buf))
616                                 break;
617                         c->state = Cnone;
618
619                 case Cnone:
620                         if(devread(cw->wdev, addr, buf)) {
621                                 state = Cerror;
622                                 break;
623                         }
624                         if(addr > max)
625                                 newmax = addr;
626                         if(!devwrite(cw->cdev, a2, buf))
627                                 c->state = Cread;
628                         break;
629
630                 case Cdirty:
631                 case Cdump:
632                 case Cdump1:
633                 case Cwrite:
634                         if(devread(cw->cdev, a2, buf))
635                                 state = Cerror;
636                         break;
637                 }
638                 break;
639
640         case Owrite:
641                 switch(state) {
642                 default:
643                         goto bad;
644
645                 case Cdump:
646                 case Cdump1:
647                         /*
648                          * this is hard part -- a dump block must be
649                          * sent to the worm if it is rewritten.
650                          * if this causes an error, there is no
651                          * place to save the dump1 data. the block
652                          * is just reclassified as 'dump1' (botch)
653                          */
654                         p1 = getbuf(devnone, Cwio1, 0);
655                         if(devread(cw->cdev, a2, p1->iobuf)) {
656                                 putbuf(p1);
657                                 fprint(2, "cwio: write induced dump error - r cache\n");
658
659                         casenone:
660                                 if(devwrite(cw->cdev, a2, buf)) {
661                                         state = Cerror;
662                                         break;
663                                 }
664                                 c->state = Cdump1;
665                                 break;
666                         }
667                         if(devwrite(cw->wdev, addr, p1->iobuf)) {
668                                 p2 = getbuf(devnone, Cwio2, 0);
669                                 if(devread(cw->wdev, addr, p2->iobuf)) {
670                                         putbuf(p1);
671                                         putbuf(p2);
672                                         fprint(2, "cwio: write induced dump error - r+w worm\n");
673                                         goto casenone;
674                                 }
675                                 if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
676                                         putbuf(p1);
677                                         putbuf(p2);
678                                         fprint(2, "cwio: write induced dump error - w worm\n");
679                                         goto casenone;
680                                 }
681                                 putbuf(p2);
682                         }
683                         putbuf(p1);
684                         c->state = Cread;
685                         if(addr > max)
686                                 newmax = addr;
687                         cw->ncopy++;
688
689                 case Cnone:
690                 case Cread:
691                         if(devwrite(cw->cdev, a2, buf)) {
692                                 state = Cerror;
693                                 break;
694                         }
695                         c->state = Cwrite;
696                         break;
697
698                 case Cdirty:
699                 case Cwrite:
700                         if(devwrite(cw->cdev, a2, buf))
701                                 state = Cerror;
702                         break;
703                 }
704                 break;
705
706         case Ogrow:
707                 if(state != Cnone) {
708                         fprint(2, "%Z for block %lld cwgrow with state = %s\n",
709                                 cw->cdev, (Wideoff)addr, cwnames[state]);
710                         break;
711                 }
712                 c->state = Cdirty;
713                 break;
714
715         case Odump:
716                 if(state != Cdirty) {   /* BOTCH */
717                         fprint(2, "%Z for block %lld cwdump with state = %s\n",
718                                 cw->cdev, (Wideoff)addr, cwnames[state]);
719                         break;
720                 }
721                 c->state = Cdump;
722                 cw->ndump++;    /* only called from dump command */
723                 break;
724
725         case Orele:
726                 if(state != Cwrite) {
727                         if(state != Cdump1)
728                                 fprint(2, "%Z for block %lld cwrele with state = %s\n",
729                                         cw->cdev, (Wideoff)addr, cwnames[state]);
730                         break;
731                 }
732                 c->state = Cnone;
733                 break;
734
735         case Ofree:
736                 if(state == Cwrite || state == Cread)
737                         c->state = Cnone;
738                 break;
739         }
740         if(chatty > 1)
741                 fprint(2, "cwio: %Z %lld s=%s o=%s ns=%s\n",
742                         dev, (Wideoff)addr, cwnames[state],
743                         cwnames[opcode],
744                         cwnames[c->state]);
745         putbuf(p);
746         if(newmax) {
747                 cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
748                 h = (Cache*)cb->iobuf;
749                 if(newmax > h->wmax)
750                         h->wmax = newmax;
751                 putbuf(cb);
752         }
753         return state;
754
755 bad:
756         fprint(2, "%Z block %lld cw state = %s; cw opcode = %s",
757                 dev, (Wideoff)addr, cwnames[state], cwnames[opcode]);
758         return Cerror;
759 }
760
761
762 int
763 cwgrow(Device *dev, Superb *sb, int)
764 {
765         Iobuf *cb;
766         Cache *h;
767         Off fs, nfs, ws;
768
769         cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bmod|Bres);
770         h = (Cache*)cb->iobuf;
771         ws = h->wsize;
772         fs = h->fsize;
773         if(fs >= ws){
774                 putbuf(cb);
775                 return 0;
776         }
777         nfs = fs + ADDFREE;
778         if(nfs >= ws)
779                 nfs = ws;
780         h->fsize = nfs;
781         putbuf(cb);
782
783         sb->fsize = nfs;
784         for(nfs--; nfs>=fs; nfs--)
785                 switch(cwio(dev, nfs, 0, Ogrow)) {
786                 case Cerror:
787                         return 0;
788                 case Cnone:
789                         addfree(dev, nfs, sb);
790                 }
791         return 1;
792 }
793
794 int
795 cwfree(Device *dev, Off addr)
796 {
797         int state;
798
799         if(dev->type == Devcw) {
800                 state = cwio(dev, addr, 0, Ofree);
801                 if(state != Cdirty)
802                         return 1;       /* do not put in freelist */
803         }
804         return 0;                       /* put in freelist */
805 }
806
807 #ifdef unused
808 int
809 bktcheck(Bucket *b)
810 {
811         Centry *c, *c1, *c2, *ce;
812         int err;
813
814         err = 0;
815         if(b->agegen < CEPERBK || b->agegen > MAXAGE) {
816                 print("agegen %ld\n", b->agegen);
817                 err = 1;
818         }
819
820         ce = b->entry + CEPERBK;
821         c1 = 0;         /* lowest age last pass */
822         for(;;) {
823                 c2 = 0;         /* lowest age this pass */
824                 for(c = b->entry; c < ce; c++) {
825                         if(c1 != 0 && c != c1) {
826                                 if(c->age == c1->age) {
827                                         print("same age %d\n", c->age);
828                                         err = 1;
829                                 }
830                                 if(c1->waddr == c->waddr)
831                                 if(c1->state != Cnone)
832                                 if(c->state != Cnone) {
833                                         print("same waddr %lld\n",
834                                                 (Wideoff)c->waddr);
835                                         err = 1;
836                                 }
837                         }
838                         if(c1 != 0 && c->age <= c1->age)
839                                 continue;
840                         if(c2 == 0 || c->age < c2->age)
841                                 c2 = c;
842                 }
843                 if(c2 == 0)
844                         break;
845                 c1 = c2;
846                 if(c1->age >= b->agegen) {
847                         print("age >= generator %d %ld\n", c1->age, b->agegen);
848                         err = 1;
849                 }
850         }
851         return err;
852 }
853 #endif
854
855 void
856 resequence(Bucket *b)
857 {
858         Centry *c, *ce, *cr;
859         int age, i;
860
861         ce = b->entry + CEPERBK;
862         for(c = b->entry; c < ce; c++) {
863                 c->age += CEPERBK;
864                 if(c->age < CEPERBK)
865                         c->age = MAXAGE;
866         }
867         b->agegen += CEPERBK;
868
869         age = 0;
870         for(i=0;; i++) {
871                 cr = 0;
872                 for(c = b->entry; c < ce; c++) {
873                         if(c->age < i)
874                                 continue;
875                         if(cr == 0 || c->age < age) {
876                                 cr = c;
877                                 age = c->age;
878                         }
879                 }
880                 if(cr == 0)
881                         break;
882                 cr->age = i;
883         }
884         b->agegen = i;
885         cons.nreseq++;
886 }
887
888 Centry*
889 getcentry(Bucket *b, Off addr)
890 {
891         Centry *c, *ce, *cr;
892         int s, age;
893
894         /*
895          * search for cache hit
896          * find oldest block as byproduct
897          */
898         ce = b->entry + CEPERBK;
899         age = 0;
900         cr = 0;
901         for(c = b->entry; c < ce; c++) {
902                 s = c->state;
903                 if(s == Cnone) {
904                         cr = c;
905                         age = 0;
906                         continue;
907                 }
908                 if(c->waddr == addr)
909                         goto found;
910                 if(s == Cread)
911                         if(cr == 0 || c->age < age) {
912                                 cr = c;
913                                 age = c->age;
914                         }
915         }
916
917         /*
918          * remap entry
919          */
920         c = cr;
921         if(c == 0)
922                 return 0;       /* bucket is full */
923
924         c->state = Cnone;
925         c->waddr = addr;
926
927 found:
928         /*
929          * update the age to get filo cache.
930          * small number in age means old
931          */
932         if(!cons.noage || c->state == Cnone) {
933                 age = b->agegen;
934                 c->age = age;
935                 age++;
936                 b->agegen = age;
937                 if(age < 0 || age >= MAXAGE)
938                         resequence(b);
939         }
940         return c;
941 }
942
943 /*
944  * ream the cache
945  * calculate new buckets
946  */
947 Iobuf*
948 cacheinit(Device *dev)
949 {
950         Iobuf *cb, *p;
951         Cache *h;
952         Device *cdev;
953         Off m;
954
955         if(chatty)
956                 print("cache init %Z\n", dev);
957         cdev = CDEV(dev);
958         devinit(cdev);
959
960         cb = getbuf(cdev, CACHE_ADDR, Bmod|Bres);
961         memset(cb->iobuf, 0, RBUFSIZE);
962         settag(cb, Tcache, QPSUPER);
963         h = (Cache*)cb->iobuf;
964
965         /*
966          * calculate csize such that
967          * tsize = msize/BKPERBLK + csize and
968          * msize = csize/CEPERBK
969          */
970         h->maddr = CACHE_ADDR + 1;
971         m = devsize(cdev) - h->maddr;
972         h->csize = ((Devsize)(m-1) * CEPERBK*BKPERBLK) / (CEPERBK*BKPERBLK+1);
973         h->msize = h->csize/CEPERBK - 5;
974         while(!prime(h->msize))
975                 h->msize--;
976         h->csize = h->msize*CEPERBK;
977         h->caddr = h->maddr + (h->msize+BKPERBLK-1)/BKPERBLK;
978         h->wsize = devsize(WDEV(dev));
979
980         if(h->msize <= 0)
981                 panic("cache too small");
982         if(h->caddr + h->csize > m)
983                 panic("cache size error");
984
985         /*
986          * setup cache map
987          */
988         for(m=h->maddr; m<h->caddr; m++) {
989                 p = getbuf(cdev, m, Bmod);
990                 memset(p->iobuf, 0, RBUFSIZE);
991                 settag(p, Tbuck, m);
992                 putbuf(p);
993         }
994         return cb;
995 }
996
997 Off
998 getstartsb(Device *dev)
999 {
1000         Filsys *f;
1001         Startsb *s;
1002
1003         for(f=filsys; f->name; f++){
1004                 if(devcmpr(f->dev, dev) == 0) {
1005                         for(s=startsb; s->name; s++)
1006                                 if(strcmp(f->name, s->name) == 0)
1007                                         return s->startsb;
1008                         fprint(2, "getstartsb: no special starting superblock for %Z %s\n",
1009                                 dev, f->name);
1010                         return FIRST;
1011                 }
1012         }
1013         fprint(2, "getstartsb: no filsys for device %Z\n", dev);
1014         return FIRST;
1015 }
1016
1017 /*
1018  * ream the cache
1019  * calculate new buckets
1020  * get superblock from
1021  * last worm dump block.
1022  */
1023 void
1024 cwrecover(Device *dev)
1025 {
1026         Iobuf *p, *cb;
1027         Cache *h;
1028         Superb *s;
1029         Off m, baddr;
1030         Device *wdev;
1031
1032         if(chatty)
1033                 print("cwrecover %Z\n", dev);
1034         cwinit1(dev);
1035         wdev = WDEV(dev);
1036
1037         p = getbuf(devnone, Cwxx1, 0);
1038         s = (Superb*)p->iobuf;
1039         baddr = 0;
1040         m = getstartsb(dev);
1041         localconfinit();
1042         if(conf.firstsb)
1043                 m = conf.firstsb;
1044         for(;;) {
1045                 memset(p->iobuf, 0, RBUFSIZE);
1046                 if(devread(wdev, m, p->iobuf) ||
1047                    checktag(p, Tsuper, QPSUPER))
1048                         break;
1049                 baddr = m;
1050                 m = s->next;
1051                 if(chatty)
1052                         print("dump %lld is good; %lld next\n", (Wideoff)baddr, (Wideoff)m);
1053                 if(baddr == conf.recovsb)
1054                         break;
1055         }
1056         putbuf(p);
1057         if(!baddr)
1058                 panic("recover: no superblock");
1059
1060         p = getbuf(wdev, baddr, Brd);
1061         s = (Superb*)p->iobuf;
1062
1063         cb = cacheinit(dev);
1064         h = (Cache*)cb->iobuf;
1065         h->sbaddr = baddr;
1066         h->cwraddr = s->cwraddr;
1067         h->roraddr = s->roraddr;
1068         h->fsize = s->fsize + 100;              /* this must be conservative */
1069         if(conf.recovcw)
1070                 h->cwraddr = conf.recovcw;
1071         if(conf.recovro)
1072                 h->roraddr = conf.recovro;
1073
1074         putbuf(cb);
1075         putbuf(p);
1076
1077         p = getbuf(dev, baddr, Brd|Bmod);
1078         s = (Superb*)p->iobuf;
1079
1080         memset(&s->fbuf, 0, sizeof(s->fbuf));
1081         s->fbuf.free[0] = 0;
1082         s->fbuf.nfree = 1;
1083         s->tfree = 0;
1084         if(conf.recovcw)
1085                 s->cwraddr = conf.recovcw;
1086         if(conf.recovro)
1087                 s->roraddr = conf.recovro;
1088
1089         putbuf(p);
1090 }
1091
1092 /*
1093  * ream the cache
1094  * calculate new buckets
1095  * initialize superblock.
1096  */
1097 void
1098 cwream(Device *dev)
1099 {
1100         Iobuf *p, *cb;
1101         Cache *h;
1102         Superb *s;
1103         Off m, baddr;
1104         Device *cdev;
1105
1106         if(chatty)
1107                 print("cwream %Z\n", dev);
1108         cwinit1(dev);
1109         cdev = CDEV(dev);
1110         devinit(cdev);
1111
1112         baddr = FIRST;  /*      baddr   = super addr
1113                                 baddr+1 = cw root
1114                                 baddr+2 = ro root
1115                                 baddr+3 = reserved next superblock */
1116
1117         cb = cacheinit(dev);
1118         h = (Cache*)cb->iobuf;
1119
1120         h->sbaddr = baddr;
1121         h->cwraddr = baddr+1;
1122         h->roraddr = baddr+2;
1123         h->fsize = 0;   /* prevents superream from freeing */
1124
1125         putbuf(cb);
1126
1127         for(m=0; m<3; m++)
1128                 cwio(dev, baddr+m, 0, Ogrow);
1129         superream(dev, baddr);
1130         rootream(dev, baddr+1);                 /* cw root */
1131         rootream(dev, baddr+2);                 /* ro root */
1132
1133         cb = getbuf(cdev, CACHE_ADDR, Brd|Bmod|Bres);
1134         h = (Cache*)cb->iobuf;
1135         h->fsize = baddr+4;
1136         putbuf(cb);
1137
1138         p = getbuf(dev, baddr, Brd|Bmod|Bimm);
1139         s = (Superb*)p->iobuf;
1140         s->last = baddr;
1141         s->cwraddr = baddr+1;
1142         s->roraddr = baddr+2;
1143         s->next = baddr+3;
1144         s->fsize = baddr+4;
1145         putbuf(p);
1146
1147         for(m=0; m<3; m++)
1148                 cwio(dev, baddr+m, 0, Odump);
1149
1150         /* write superblock to worm */
1151         while(dumpblock(dev))
1152                 ;
1153 }
1154
1155 Off
1156 rewalk1(Cw *cw, Off addr, int slot, Wpath *up)
1157 {
1158         Iobuf *p, *p1;
1159         Dentry *d;
1160
1161         if(up == 0)
1162                 return cwraddr(cw->dev);
1163         up->addr = rewalk1(cw, up->addr, up->slot, up->up);
1164         p = getbuf(cw->dev, up->addr, Brd|Bmod);
1165         d = getdir(p, up->slot);
1166         if(!d || !(d->mode & DALLOC)) {
1167                 fprint(2, "rewalk1 1\n");
1168                 if(p)
1169                         putbuf(p);
1170                 return addr;
1171         }
1172         p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
1173         if(!p1) {
1174                 fprint(2, "rewalk1 2\n");
1175                 if(p)
1176                         putbuf(p);
1177                 return addr;
1178         }
1179         if(chatty > 1)
1180                 fprint(2, "rewalk1 %lld to %lld \"%s\"\n",
1181                         (Wideoff)addr, (Wideoff)p1->addr, d->name);
1182         addr = p1->addr;
1183         p1->flags |= Bmod;
1184         putbuf(p1);
1185         putbuf(p);
1186         return addr;
1187 }
1188
1189 Off
1190 rewalk2(Cw *cw, Off addr, int slot, Wpath *up)
1191 {
1192         Iobuf *p, *p1;
1193         Dentry *d;
1194
1195         if(up == 0)
1196                 return cwraddr(cw->rodev);
1197         up->addr = rewalk2(cw, up->addr, up->slot, up->up);
1198         p = getbuf(cw->rodev, up->addr, Brd);
1199         d = getdir(p, up->slot);
1200         if(!d || !(d->mode & DALLOC)) {
1201                 fprint(2, "rewalk2 1\n");
1202                 if(p)
1203                         putbuf(p);
1204                 return addr;
1205         }
1206         p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
1207         if(!p1) {
1208                 fprint(2, "rewalk2 2\n");
1209                 if(p)
1210                         putbuf(p);
1211                 return addr;
1212         }
1213         if(chatty > 1)
1214                 fprint(2, "rewalk2 %lld to %lld \"%s\"\n",
1215                         (Wideoff)addr, (Wideoff)p1->addr, d->name);
1216         addr = p1->addr;
1217         putbuf(p1);
1218         putbuf(p);
1219         return addr;
1220 }
1221
1222 void
1223 rewalk(Cw *cw)
1224 {
1225         int h;
1226         File *f;
1227
1228         for(h=0; h<nelem(flist); h++)
1229                 for(f=flist[h]; f; f=f->next) {
1230                         if(!f->fs)
1231                                 continue;
1232                         if(cw->dev == f->fs->dev)
1233                                 f->addr = rewalk1(cw, f->addr, f->slot, f->wpath);
1234                         else
1235                         if(cw->rodev == f->fs->dev)
1236                                 f->addr = rewalk2(cw, f->addr, f->slot, f->wpath);
1237                 }
1238 }
1239
1240 Off
1241 split(Cw *cw, Iobuf *p, Off addr)
1242 {
1243         Off na;
1244         int state;
1245
1246         na = 0;
1247         if(p && (p->flags & Bmod)) {
1248                 p->flags |= Bimm;
1249                 putbuf(p);
1250                 p = 0;
1251         }
1252
1253         state = cwio(cw->dev, addr, 0, Onone);  /* read the state (twice?) */
1254         switch(state) {
1255         default:
1256                 panic("split: unknown state %s", cwnames[state]);
1257
1258         case Cerror:
1259         case Cnone:
1260         case Cdump:
1261         case Cread:
1262                 break;
1263
1264         case Cdump1:
1265         case Cwrite:
1266                 /* worm full */
1267                 if(cw->fsize >= cw->wsize)
1268                         break;
1269
1270                 /*
1271                  * botch.. could be done by relabeling
1272                  */
1273                 if(!p){
1274                         p = getbuf(cw->dev, addr, Brd);
1275                         if(p == nil) {
1276                                 fprint(2, "split: null getbuf\n");
1277                                 break;
1278                         }
1279                 }
1280                 na = cw->fsize++;
1281                 cwio(cw->dev, na, 0, Ogrow);
1282                 cwio(cw->dev, na, p->iobuf, Owrite);
1283                 cwio(cw->dev, na, 0, Odump);
1284                 cwio(cw->dev, addr, 0, Orele);
1285                 break;
1286
1287         case Cdirty:
1288                 cwio(cw->dev, addr, 0, Odump);
1289                 break;
1290         }
1291         if(p)
1292                 putbuf(p);
1293         return na;
1294 }
1295
1296 int
1297 isdirty(Cw *cw, Iobuf *p, Off addr, int tag)
1298 {
1299         int s;
1300
1301         if(p && (p->flags & Bmod))
1302                 return 1;
1303         s = cwio(cw->dev, addr, 0, Onone);
1304         if(s == Cdirty || s == Cwrite)
1305                 return 1;
1306         if(tag >= Tind1 && tag <= Tmaxind)
1307                 /* botch, get these modified */
1308                 if(s != Cnone)
1309                         return 1;
1310         return 0;
1311 }
1312
1313 Off
1314 cwrecur(Cw *cw, Off addr, int tag, int tag1, Off qp)
1315 {
1316         Iobuf *p;
1317         Dentry *d;
1318         Off qp1;
1319         int i, j, shouldstop;
1320         Off na;
1321         char *np;
1322
1323         shouldstop = 0;
1324         p = getbuf(cw->dev, addr, Bprobe);
1325         if(!isdirty(cw, p, addr, tag)) {
1326                 if(!cw->all) {
1327                         if(chatty > 1)
1328                                 fprint(2, "cwrecur: %lld t=%s not dirty %s\n",
1329                                         (Wideoff)addr, tagnames[tag], cw->name);
1330                         if(p)
1331                                 putbuf(p);
1332                         return 0;
1333                 }
1334                 shouldstop = 1;
1335         }
1336         if(chatty > 1)
1337                 fprint(2, "cwrecur: %lld t=%s %s\n",
1338                         (Wideoff)addr, tagnames[tag], cw->name);
1339         if(cw->depth >= 100) {
1340                 fprint(2, "dump depth too great %s\n", cw->name);
1341                 if(p)
1342                         putbuf(p);
1343                 return 0;
1344         }
1345         cw->depth++;
1346
1347         switch(tag) {
1348         default:
1349                 fprint(2, "cwrecur: unknown tag %d %s\n", tag, cw->name);
1350                 break;
1351
1352         case Tfile:
1353                 if(p && checktag(p, tag, qp))
1354                         fprint(2, "cwrecur: Tfile %s\n", cw->name);
1355                 break;
1356
1357         case Tdir:
1358                 if(cw->depth > 1) {
1359                         cw->namepad[0] = 0;     /* force room */
1360                         np = strchr(cw->name, 0);
1361                         *np++ = '/';
1362                 } else {
1363                         np = 0; /* set */
1364                         cw->name[0] = 0;
1365                 }
1366                 if(!p)
1367                         p = getbuf(cw->dev, addr, Brd);
1368                 if(!p || checktag(p, tag, qp)) {
1369                         fprint(2, "cwrecur: Tdir %s\n", cw->name);
1370                         break;
1371                 }
1372                 for(i=0; i<DIRPERBUF; i++) {
1373                         d = getdir(p, i);
1374                         if((d->mode & (DALLOC|DTMP)) != DALLOC)
1375                                 continue;
1376                         if(np)
1377                                 strncpy(np, d->name, NAMELEN);
1378                         else if(i > 0)
1379                                 fprint(2, "cwrecur: root with >1 directory\n");
1380                         qp1 = d->qid.path;
1381                         tag1 = Tfile;
1382                         if(d->mode & DDIR){
1383                                 qp1 ^= QPDIR;
1384                                 tag1 = Tdir;
1385                         }
1386                         for(j=0; j<NDBLOCK; j++) {
1387                                 na = d->dblock[j];
1388                                 if(na) {
1389                                         na = cwrecur(cw, na, tag1, 0, qp1);
1390                                         if(na) {
1391                                                 d->dblock[j] = na;
1392                                                 p->flags |= Bmod;
1393                                         }
1394                                 }
1395                         }
1396                         for (j = 0; j < NIBLOCK; j++) {
1397                                 na = d->iblocks[j];
1398                                 if(na) {
1399                                         na = cwrecur(cw, na, Tind1+j, tag1, qp1);
1400                                         if(na) {
1401                                                 d->iblocks[j] = na;
1402                                                 p->flags |= Bmod;
1403                                         }
1404                                 }
1405                         }
1406                 }
1407                 break;
1408
1409         case Tind1:
1410                 j = tag1;
1411                 tag1 = 0;
1412                 goto tind;
1413
1414         case Tind2:
1415 #ifndef COMPAT32
1416         case Tind3:
1417         case Tind4:
1418         /* add more Tind tags here ... */
1419 #endif
1420                 j = tag-1;
1421         tind:
1422                 if(!p)
1423                         p = getbuf(cw->dev, addr, Brd);
1424                 if(!p || checktag(p, tag, qp)) {
1425                         fprint(2, "cwrecur: Tind %s\n", cw->name);
1426                         break;
1427                 }
1428                 for(i=0; i<INDPERBUF; i++) {
1429                         na = ((Off *)p->iobuf)[i];
1430                         if(na) {
1431                                 na = cwrecur(cw, na, j, tag1, qp);
1432                                 if(na) {
1433                                         ((Off *)p->iobuf)[i] = na;
1434                                         p->flags |= Bmod;
1435                                 }
1436                         }
1437                 }
1438                 break;
1439         }
1440
1441         na = split(cw, p, addr);
1442
1443         cw->depth--;
1444
1445         if(na){
1446                 if(shouldstop){
1447                         if(cw->allflag && cw->falsehits < 10)
1448                                 fprint(2, "shouldstop %lld %lld t=%s %s\n",
1449                                         (Wideoff)addr, (Wideoff)na,
1450                                         tagnames[tag], cw->name);
1451                         cw->falsehits++;
1452                 }
1453         }
1454
1455         return na;
1456 }
1457
1458 Timet   nextdump(Timet t);
1459
1460 void
1461 cfsdump(Filsys *fs)
1462 {
1463         long m, n, i;
1464         Off orba, rba, oroa, roa, sba, a;
1465         Timet tim;
1466         char tstr[20];
1467         Iobuf *pr, *p1, *p;
1468         Dentry *dr, *d1, *d;
1469         Cache *h;
1470         Superb *s;
1471         Cw *cw;
1472
1473         if(fs->dev->type != Devcw) {
1474                 fprint(2, "cant dump; not cw device: %Z\n", fs->dev);
1475                 return;
1476         }
1477         cw = fs->dev->private;
1478         if(cw == 0) {
1479                 fprint(2, "cant dump: has not been inited: %Z\n", fs->dev);
1480                 return;
1481         }
1482
1483         tim = toytime();
1484         wlock(&mainlock);               /* dump */
1485
1486         /*
1487          * set up static structure
1488          * with frequent variables
1489          */
1490         cw->ndump = 0;
1491         cw->name[0] = 0;
1492         cw->depth = 0;
1493
1494         /*
1495          * cw root
1496          */
1497         sync("before dump");
1498         cw->fsize = cwsize(cw->dev);
1499         orba = cwraddr(cw->dev);
1500         if(chatty)
1501                 fprint(2, "cwroot %lld", (Wideoff)orba);
1502         cons.noage = 1;
1503         cw->all = cw->allflag | noatime | noatimeset;
1504         noatimeset = 0;
1505         rba = cwrecur(cw, orba, Tdir, 0, QPROOT);
1506         if(rba == 0)
1507                 rba = orba;
1508         if(chatty)
1509                 fprint(2, "->%lld\n", (Wideoff)rba);
1510         sync("after cw");
1511
1512         /*
1513          * partial super block
1514          */
1515         p = getbuf(cw->dev, cwsaddr(cw->dev), Brd|Bmod|Bimm);
1516         if(!p || checktag(p, Tsuper, QPSUPER))
1517                 goto bad;
1518         s = (Superb*)p->iobuf;
1519         s->fsize = cw->fsize;
1520         s->cwraddr = rba;
1521         putbuf(p);
1522
1523         /*
1524          * partial cache block
1525          */
1526         p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bimm|Bres);
1527         if(!p || checktag(p, Tcache, QPSUPER))
1528                 goto bad;
1529         h = (Cache*)p->iobuf;
1530         h->fsize = cw->fsize;
1531         h->cwraddr = rba;
1532         putbuf(p);
1533
1534         if(cw->fsize+50 > cw->wsize){
1535                 fprint(2, "dump: worm full after dump\n");
1536                 goto done;
1537         }
1538
1539         /*
1540          * ro root
1541          */
1542         oroa = cwraddr(cw->rodev);
1543         pr = getbuf(cw->dev, oroa, Brd|Bmod);
1544         if(!pr || checktag(pr, Tdir, QPROOT))
1545                 goto bad;
1546         dr = getdir(pr, 0);
1547
1548         datestr(tstr, time(nil));       /* tstr = "yyyymmdd" */
1549         n = 0;
1550         for(a=0;; a++) {
1551                 p1 = dnodebuf(pr, dr, a, Tdir, 0);
1552                 if(!p1 || checktag(p1, Tdir, QPNONE))
1553                         goto bad;
1554                 n++;
1555                 for(i=0; i<DIRPERBUF; i++) {
1556                         d1 = getdir(p1, i);
1557                         if(!d1)
1558                                 goto bad;
1559                         if(!(d1->mode & DALLOC))
1560                                 goto found1;
1561                         if(!memcmp(d1->name, tstr, 4))
1562                                 goto found2;    /* found entry */
1563                 }
1564                 putbuf(p1);
1565         }
1566
1567         /*
1568          * no year directory, create one
1569          */
1570 found1:
1571         p = getbuf(cw->dev, rba, Brd);
1572         if(!p || checktag(p, Tdir, QPROOT))
1573                 goto bad;
1574         d = getdir(p, 0);
1575         d1->qid = d->qid;
1576         d1->qid.version += n;
1577         memmove(d1->name, tstr, 4);
1578         d1->mode = d->mode;
1579         d1->uid = d->uid;
1580         d1->gid = d->gid;
1581         putbuf(p);
1582         accessdir(pr, dr, FWRITE, 0);
1583
1584         /*
1585          * put mmdd[count] in year directory
1586          */
1587 found2:
1588         accessdir(pr, dr, FREAD, 0);
1589         putbuf(pr);
1590         pr = p1;
1591         dr = d1;
1592
1593         n = 0;
1594         m = 0;
1595         for(a=0;; a++) {
1596                 p1 = dnodebuf(pr, dr, a, Tdir, 0);
1597                 if(!p1 || checktag(p1, Tdir, QPNONE))
1598                         goto bad;
1599                 n++;
1600                 for(i=0; i<DIRPERBUF; i++) {
1601                         d1 = getdir(p1, i);
1602                         if(!d1)
1603                                 goto bad;
1604                         if(!(d1->mode & DALLOC))
1605                                 goto found;
1606                         if(!memcmp(d1->name, tstr+4, 4))
1607                                 m++;
1608                 }
1609                 putbuf(p1);
1610         }
1611
1612         /*
1613          * empty slot put in root
1614          */
1615 found:
1616         if(m)   /* how many dumps this date */
1617                 sprint(tstr+8, "%ld", m);
1618
1619         p = getbuf(cw->dev, rba, Brd);
1620         if(!p || checktag(p, Tdir, QPROOT))
1621                 goto bad;
1622         d = getdir(p, 0);
1623         *d1 = *d;                               /* qid is QPROOT */
1624         putbuf(p);
1625         strcpy(d1->name, tstr+4);
1626         d1->qid.version += n;
1627         accessdir(p1, d1, FWRITE, 0);
1628         putbuf(p1);
1629         accessdir(pr, dr, FWRITE, 0);
1630         putbuf(pr);
1631
1632         cw->fsize = cwsize(cw->dev);
1633         oroa = cwraddr(cw->rodev);              /* probably redundant */
1634         if(chatty)
1635                 fprint(2, "roroot %lld", (Wideoff)oroa);
1636
1637         cons.noage = 0;
1638         cw->all = 0;
1639         roa = cwrecur(cw, oroa, Tdir, 0, QPROOT);
1640         if(roa == 0)
1641                 roa = oroa;
1642         if(chatty)
1643                 fprint(2, "->%lld /%.4s/%s\n", (Wideoff)roa, tstr, tstr+4);
1644         sync("after ro");
1645
1646         /*
1647          * final super block
1648          */
1649         a = cwsaddr(cw->dev);
1650         if(chatty)
1651                 fprint(2, "sblock %lld", (Wideoff)a);
1652         p = getbuf(cw->dev, a, Brd|Bmod|Bimm);
1653         if(!p || checktag(p, Tsuper, QPSUPER))
1654                 goto bad;
1655         s = (Superb*)p->iobuf;
1656         s->last = a;
1657         sba = s->next;
1658         s->next = cw->fsize++;
1659         s->fsize = cw->fsize;
1660         s->roraddr = roa;
1661
1662         cwio(cw->dev, sba, 0, Ogrow);
1663         cwio(cw->dev, sba, p->iobuf, Owrite);
1664         cwio(cw->dev, sba, 0, Odump);
1665         if(chatty)
1666                 fprint(2, "->%lld (->%lld)\n", (Wideoff)sba, (Wideoff)s->next);
1667
1668         putbuf(p);
1669
1670         /*
1671          * final cache block
1672          */
1673         p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bimm|Bres);
1674         if(!p || checktag(p, Tcache, QPSUPER))
1675                 goto bad;
1676         h = (Cache*)p->iobuf;
1677         h->fsize = cw->fsize;
1678         h->roraddr = roa;
1679         h->sbaddr = sba;
1680         putbuf(p);
1681
1682 done:
1683         rewalk(cw);
1684         sync("all done");
1685
1686         if(chatty){
1687                 fprint(2, "%lld blocks queued for worm\n", (Wideoff)cw->ndump);
1688                 fprint(2, "%lld falsehits\n", (Wideoff)cw->falsehits);
1689         }
1690         cw->nodump = 0;
1691
1692         /*
1693          * extend all of the locks
1694          */
1695         tim = toytime() - tim;
1696         for(i=0; i<NTLOCK; i++)
1697                 if(tlocks[i].time > 0)
1698                         tlocks[i].time += tim;
1699
1700         wunlock(&mainlock);
1701         nextdump(time(nil));
1702         return;
1703
1704 bad:
1705         panic("dump: bad");
1706 }
1707
1708 void
1709 mvstates(Device *dev, int s1, int s2, int side)
1710 {
1711         Iobuf *p, *cb;
1712         Cache *h;
1713         Bucket *b;
1714         Centry *c, *ce;
1715         Off m, lo, hi, msize, maddr;
1716         Cw *cw;
1717
1718         cw = dev->private;
1719         lo = 0;
1720         hi = lo + devsize(dev->cw.w);   /* size of all sides totalled */
1721         if(side >= 0) {
1722                 /* operate on only a single disc side */
1723                 Sidestarts ss;
1724
1725                 wormsidestarts(dev, side, &ss);
1726                 lo = ss.sstart;
1727                 hi = ss.s1start;
1728         }
1729         cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
1730         if(!cb || checktag(cb, Tcache, QPSUPER))
1731                 panic("mvstates: checktag super");
1732         h = (Cache*)cb->iobuf;
1733         msize = h->msize;
1734         maddr = h->maddr;
1735         putbuf(cb);
1736
1737         for(m=0; m<msize; m++) {
1738                 p = getbuf(cw->cdev, maddr + m/BKPERBLK, Brd|Bmod);
1739                 if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
1740                         panic("mvstates: checktag c bucket");
1741                 b = (Bucket*)p->iobuf + m%BKPERBLK;
1742                 ce = b->entry + CEPERBK;
1743                 for(c=b->entry; c<ce; c++)
1744                         if(c->state == s1 && c->waddr >= lo && c->waddr < hi)
1745                                 c->state = s2;
1746                 putbuf(p);
1747         }
1748 }
1749
1750 void
1751 prchain(Device *dev, Off m, int flg)
1752 {
1753         Iobuf *p;
1754         Superb *s;
1755
1756         if(m == 0) {
1757                 if(flg)
1758                         m = cwsaddr(dev);
1759                 else
1760                         m = getstartsb(dev);
1761         }
1762         p = getbuf(devnone, Cwxx2, 0);
1763         s = (Superb*)p->iobuf;
1764         for(;;) {
1765                 memset(p->iobuf, 0, RBUFSIZE);
1766                 if(devread(WDEV(dev), m, p->iobuf) ||
1767                    checktag(p, Tsuper, QPSUPER))
1768                         break;
1769                 if(flg) {
1770                         print("dump %lld is good; %lld prev\n", (Wideoff)m,
1771                                 (Wideoff)s->last);
1772                         print("\t%lld cwroot; %lld roroot\n",
1773                                 (Wideoff)s->cwraddr, (Wideoff)s->roraddr);
1774                         if(m <= s->last)
1775                                 break;
1776                         m = s->last;
1777                 } else {
1778                         print("dump %lld is good; %lld next\n", (Wideoff)m,
1779                                 (Wideoff)s->next);
1780                         print("\t%lld cwroot; %lld roroot\n",
1781                                 (Wideoff)s->cwraddr, (Wideoff)s->roraddr);
1782                         if(m >= s->next)
1783                                 break;
1784                         m = s->next;
1785                 }
1786         }
1787         putbuf(p);
1788 }
1789
1790 void
1791 touchsb(Device *dev)
1792 {
1793         Iobuf *p;
1794         Off m;
1795
1796         m = cwsaddr(dev);
1797         p = getbuf(devnone, Cwxx2, 0);
1798
1799         memset(p->iobuf, 0, RBUFSIZE);
1800         if(devread(WDEV(dev), m, p->iobuf) ||
1801            checktag(p, Tsuper, QPSUPER))
1802                 fprint(2, "%Z block %lld WORM SUPER BLOCK READ FAILED\n",
1803                         WDEV(dev), (Wideoff)m);
1804         else if(chatty)
1805                 print("%Z touch superblock %lld\n", WDEV(dev), (Wideoff)m);
1806         putbuf(p);
1807 }
1808
1809 void
1810 storesb(Device *dev, Off last, int doit)
1811 {
1812         Iobuf *ph, *ps;
1813         Cache *h;
1814         Superb *s;
1815         Off sbaddr, qidgen;
1816
1817         sbaddr = cwsaddr(dev);
1818
1819         ps = getbuf(devnone, Cwxx2, 0);
1820         if(!ps) {
1821                 fprint(2, "storesb: getbuf\n");
1822                 return;
1823         }
1824
1825         /*
1826          * try to read last sb
1827          */
1828         memset(ps->iobuf, 0, RBUFSIZE);
1829         if(devread(WDEV(dev), last, ps->iobuf) ||
1830            checktag(ps, Tsuper, QPSUPER))
1831                 print("read last failed\n");
1832         else
1833                 print("read last succeeded\n");
1834
1835         s = (Superb*)ps->iobuf;
1836         qidgen = s->qidgen;
1837         if(qidgen == 0)
1838                 qidgen = 0x31415;
1839         qidgen += 1000;
1840         if(s->next != sbaddr)
1841                 print("next(last) is not sbaddr %lld %lld\n",
1842                         (Wideoff)s->next, (Wideoff)sbaddr);
1843         else
1844                 print("next(last) is sbaddr\n");
1845
1846         /*
1847          * read cached superblock
1848          */
1849         ph = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
1850         if(!ph || checktag(ph, Tcache, QPSUPER)) {
1851                 print("storesb: checktag super\n");
1852                 if(ph)
1853                         putbuf(ph);
1854                 putbuf(ps);
1855                 return;
1856         } else
1857                 print("read cached sb succeeded\n");
1858
1859         h = (Cache*)ph->iobuf;
1860
1861         memset(ps->iobuf, 0, RBUFSIZE);
1862         settag(ps, Tsuper, QPSUPER);
1863         ps->flags = 0;
1864         s = (Superb*)ps->iobuf;
1865
1866         s->cwraddr = h->cwraddr;
1867         s->roraddr = h->roraddr;
1868         s->fsize = h->fsize;
1869         s->fstart = 2;
1870         s->last = last;
1871         s->next = h->roraddr+1;
1872
1873         s->qidgen = qidgen;
1874         putbuf(ph);
1875
1876         if(s->fsize-1 != s->next ||
1877            s->fsize-2 != s->roraddr ||
1878            s->fsize-5 != s->cwraddr) {
1879                 print("addrs not in relationship %lld %lld %lld %lld\n",
1880                         (Wideoff)s->cwraddr, (Wideoff)s->roraddr,
1881                         (Wideoff)s->next, (Wideoff)s->fsize);
1882                 putbuf(ps);
1883                 return;
1884         } else
1885                 print("addresses in relation\n");
1886
1887         if(doit)
1888         if(devwrite(WDEV(dev), sbaddr, ps->iobuf))
1889                 print("%Z block %lld WORM SUPER BLOCK WRITE FAILED\n",
1890                         WDEV(dev), (Wideoff)sbaddr);
1891         ps->flags = 0;
1892         putbuf(ps);
1893 }
1894
1895 void
1896 savecache(Device *dev)
1897 {
1898         Iobuf *p, *cb;
1899         Cache *h;
1900         Bucket *b;
1901         Centry *c, *ce;
1902         long n, left;
1903         Off m, maddr, msize, *longp, nbyte;
1904         Device *cdev;
1905
1906         if(walkto("/adm/cache") || con_open(FID2, OWRITE|OTRUNC)) {
1907                 fprint(2, "cant open /adm/cache\n");
1908                 return;
1909         }
1910         cdev = CDEV(dev);
1911         cb = getbuf(cdev, CACHE_ADDR, Brd|Bres);
1912         if(!cb || checktag(cb, Tcache, QPSUPER))
1913                 panic("savecache: checktag super");
1914         h = (Cache*)cb->iobuf;
1915         msize = h->msize;
1916         maddr = h->maddr;
1917         putbuf(cb);
1918
1919         n = BUFSIZE;                    /* calculate write size */
1920         if(n > MAXDAT)
1921                 n = MAXDAT;
1922
1923         cb = getbuf(devnone, Cwxx4, 0);
1924         longp = (Off *)cb->iobuf;
1925         left = n/sizeof(Off);
1926         cons.offset = 0;
1927
1928         for(m=0; m<msize; m++) {
1929                 if(left < BKPERBLK) {
1930                         nbyte = (n/sizeof(Off) - left) * sizeof(Off);
1931                         con_write(FID2, cb->iobuf, cons.offset, nbyte);
1932                         cons.offset += nbyte;
1933                         longp = (Off *)cb->iobuf;
1934                         left = n/sizeof(Off);
1935                 }
1936                 p = getbuf(cdev, maddr + m/BKPERBLK, Brd);
1937                 if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
1938                         panic("savecache: checktag c bucket");
1939                 b = (Bucket*)p->iobuf + m%BKPERBLK;
1940                 ce = b->entry + CEPERBK;
1941                 for(c = b->entry; c < ce; c++)
1942                         if(c->state == Cread) {
1943                                 *longp++ = c->waddr;
1944                                 left--;
1945                         }
1946                 putbuf(p);
1947         }
1948         nbyte = (n/sizeof(Off) - left) * sizeof(Off);
1949         con_write(FID2, cb->iobuf, cons.offset, nbyte);
1950         putbuf(cb);
1951 }
1952
1953 void
1954 loadcache(Device *dev, int dskno)
1955 {
1956         Iobuf *p, *cb;
1957         Off m, nbyte, *longp, count;
1958         Sidestarts ss;
1959
1960         if(walkto("/adm/cache") || con_open(FID2, OREAD)) {
1961                 fprint(2, "cant open /adm/cache\n");
1962                 return;
1963         }
1964
1965         cb = getbuf(devnone, Cwxx4, 0);
1966         cons.offset = 0;
1967         count = 0;
1968
1969         if (dskno >= 0)
1970                 wormsidestarts(dev, dskno, &ss);
1971         for(;;) {
1972                 memset(cb->iobuf, 0, BUFSIZE);
1973                 nbyte = con_read(FID2, cb->iobuf, cons.offset, 100) / sizeof(Off);
1974                 if(nbyte <= 0)
1975                         break;
1976                 cons.offset += nbyte * sizeof(Off);
1977                 longp = (Off *)cb->iobuf;
1978                 while(nbyte > 0) {
1979                         m = *longp++;
1980                         nbyte--;
1981                         if(m == 0)
1982                                 continue;
1983                         /* if given a diskno, restrict to just that disc side */
1984                         if(dskno < 0 || m >= ss.sstart && m < ss.s1start) {
1985                                 p = getbuf(dev, m, Brd);
1986                                 if(p)
1987                                         putbuf(p);
1988                                 count++;
1989                         }
1990                 }
1991         }
1992         putbuf(cb);
1993         print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno);
1994 }
1995
1996 void
1997 morecache(Device *dev, int dskno, Off size)
1998 {
1999         Iobuf *p;
2000         Off m, ml, mh, mm, count;
2001         Cache *h;
2002         Sidestarts ss;
2003
2004         p = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
2005         if(!p || checktag(p, Tcache, QPSUPER))
2006                 panic("morecache: checktag super");
2007         h = (Cache*)p->iobuf;
2008         mm = h->wmax;
2009         putbuf(p);
2010
2011         wormsidestarts(dev, dskno, &ss);
2012         ml = ss.sstart;         /* start at beginning of disc side #dskno */
2013         mh = ml + size;
2014         if(mh > mm) {
2015                 mh = mm;
2016                 print("limited to %lld\n", (Wideoff)mh-ml);
2017         }
2018
2019         count = 0;
2020         for(m=ml; m < mh; m++) {
2021                 p = getbuf(dev, m, Brd);
2022                 if(p)
2023                         putbuf(p);
2024                 count++;
2025         }
2026         print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno);
2027 }
2028
2029 void
2030 blockcmp(Device *dev, Off wa, Off ca)
2031 {
2032         Iobuf *p1, *p2;
2033         int i, c;
2034
2035         p1 = getbuf(WDEV(dev), wa, Brd);
2036         if(!p1) {
2037                 fprint(2, "blockcmp: wdev error\n");
2038                 return;
2039         }
2040
2041         p2 = getbuf(CDEV(dev), ca, Brd);
2042         if(!p2) {
2043                 fprint(2, "blockcmp: cdev error\n");
2044                 putbuf(p1);
2045                 return;
2046         }
2047
2048         c = 0;
2049         for(i=0; i<RBUFSIZE; i++)
2050                 if(p1->iobuf[i] != p2->iobuf[i]) {
2051                         print("%4d: %.2x %.2x\n",
2052                                 i,
2053                                 p1->iobuf[i]&0xff,
2054                                 p2->iobuf[i]&0xff);
2055                         c++;
2056                         if(c >= 10)
2057                                 break;
2058                 }
2059
2060         putbuf(p1);
2061         putbuf(p2);
2062 }
2063
2064 void
2065 wblock(Device *dev, Off addr)
2066 {
2067         Iobuf *p1;
2068         int i;
2069
2070         p1 = getbuf(dev, addr, Brd);
2071         if(p1) {
2072                 i = devwrite(WDEV(dev), addr, p1->iobuf);
2073                 print("i = %d\n", i);
2074                 putbuf(p1);
2075         }
2076 }
2077
2078 void
2079 cwtest(Device*)
2080 {
2081 }
2082
2083 #ifdef  XXX
2084 /* garbage to change sb size
2085  * probably will need it someday
2086  */
2087         fsz = number(0, 0, 10);
2088         count = 0;
2089         if(fsz == number(0, -1, 10))
2090                 count = -1;             /* really do it */
2091         print("fsize = %ld\n", fsz);
2092         cdev = CDEV(dev);
2093         cb = getbuf(cdev, CACHE_ADDR, Brd|Bres);
2094         if(!cb || checktag(cb, Tcache, QPSUPER))
2095                 panic("cwtest: checktag super");
2096         h = (Cache*)cb->iobuf;
2097         for(m=0; m<h->msize; m++) {
2098                 p = getbuf(cdev, h->maddr + m/BKPERBLK, Brd|Bmod);
2099                 if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
2100                         panic("cwtest: checktag c bucket");
2101                 b = (Bucket*)p->iobuf + m%BKPERBLK;
2102                 ce = b->entry + CEPERBK;
2103                 for(c=b->entry; c<ce; c++) {
2104                         if(c->waddr < fsz)
2105                                 continue;
2106                         if(count < 0) {
2107                                 c->state = Cnone;
2108                                 continue;
2109                         }
2110                         if(c->state != Cdirty)
2111                                 count++;
2112                 }
2113                 putbuf(p);
2114         }
2115         if(count < 0) {
2116                 print("old cache hsize = %ld\n", h->fsize);
2117                 h->fsize = fsz;
2118                 cb->flags |= Bmod;
2119                 p = getbuf(dev, h->sbaddr, Brd|Bmod);
2120                 s = (Superb*)p->iobuf;
2121                 print("old super hsize = %ld\n", s->fsize);
2122                 s->fsize = fsz;
2123                 putbuf(p);
2124         }
2125         putbuf(cb);
2126         print("count = %lld\n", (Wideoff)count);
2127 #endif
2128
2129 int
2130 convstate(char *name)
2131 {
2132         int i;
2133
2134         for(i=0; i<nelem(cwnames); i++)
2135                 if(cwnames[i])
2136                         if(strcmp(cwnames[i], name) == 0)
2137                                 return i;
2138         return -1;
2139 }
2140
2141 void
2142 searchtag(Device *d, Off a, int tag, int n)
2143 {
2144         Iobuf *p;
2145         Tag *t;
2146         int i;
2147
2148         if(a == 0)
2149                 a = getstartsb(d);
2150         p = getbuf(devnone, Cwxx2, 0);
2151         t = (Tag*)(p->iobuf+BUFSIZE);
2152         for(i=0; i<n; i++) {
2153                 memset(p->iobuf, 0, RBUFSIZE);
2154                 if(devread(WDEV(d), a+i, p->iobuf)) {
2155                         if(n == 1000)
2156                                 break;
2157                         continue;
2158                 }
2159                 if(t->tag == tag) {
2160                         print("tag %d found at %Z %lld\n", tag, d, (Wideoff)a+i);
2161                         break;
2162                 }
2163         }
2164         putbuf(p);
2165 }
2166
2167 void
2168 cmd_cwcmd(int argc, char *argv[])
2169 {
2170         Device *dev;
2171         char *arg;
2172         char str[28];
2173         Off s1, s2, a, b, n;
2174         Cw *cw;
2175
2176         if(argc <= 1) {
2177                 print("\tcwcmd mvstate state1 state2 [platter]\n");
2178                 print("\tcwcmd prchain [start] [bakflg]\n");
2179                 print("\tcwcmd searchtag [start] [tag] [blocks]\n");
2180                 print("\tcwcmd touchsb\n");
2181                 print("\tcwcmd savecache\n");
2182                 print("\tcwcmd loadcache [dskno]\n");
2183                 print("\tcwcmd morecache dskno [count]\n");
2184                 print("\tcwcmd blockcmp wbno cbno\n");
2185                 print("\tcwcmd startdump [01]\n");
2186                 print("\tcwcmd acct\n");
2187                 print("\tcwcmd clearacct\n");
2188                 return;
2189         }
2190         arg = argv[1];
2191
2192         /*
2193          * items not depend on a cw filesystem
2194          */
2195         if(strcmp(arg, "acct") == 0) {
2196                 for(a=0; a<nelem(growacct); a++) {
2197                         b = growacct[a];
2198                         if(b) {
2199                                 uidtostr(str, a, 1);
2200                                 print("%10lld %s\n",
2201                                         ((Wideoff)b*ADDFREE*RBUFSIZE+500000)/1000000,
2202                                         str);
2203                         }
2204                 }
2205                 return;
2206         }
2207         if(strcmp(arg, "clearacct") == 0) {
2208                 memset(growacct, 0, sizeof(growacct));
2209                 return;
2210         }
2211
2212         /*
2213          * items depend on cw filesystem
2214          */
2215         dev = cons.curfs->dev;
2216         if(dev == 0 || dev->type != Devcw || dev->private == 0) {
2217                 print("cfs not a cw filesystem: %Z\n", dev);
2218                 return;
2219         }
2220         cw = dev->private;
2221         if(strcmp(arg, "searchtag") == 0) {
2222                 a = 0;
2223                 if(argc > 2)
2224                         a = number(argv[2], 0, 10);
2225                 b = Tsuper;
2226                 if(argc > 3)
2227                         b = number(argv[3], 0, 10);
2228                 n = 1000;
2229                 if(argc > 4)
2230                         n = number(argv[4], 0, 10);
2231                 searchtag(dev, a, b, n);
2232         } else if(strcmp(arg, "mvstate") == 0) {
2233                 if(argc < 4)
2234                         goto bad;
2235                 s1 = convstate(argv[2]);
2236                 s2 = convstate(argv[3]);
2237                 if(s1 < 0 || s2 < 0)
2238                         goto bad;
2239                 a = -1;
2240                 if(argc > 4)
2241                         a = number(argv[4], 0, 10);
2242                 mvstates(dev, s1, s2, a);
2243                 return;
2244         bad:
2245                 print("cwcmd mvstate: bad args\n");
2246         } else if(strcmp(arg, "prchain") == 0) {
2247                 a = 0;
2248                 if(argc > 2)
2249                         a = number(argv[2], 0, 10);
2250                 s1 = 0;
2251                 if(argc > 3)
2252                         s1 = number(argv[3], 0, 10);
2253                 prchain(dev, a, s1);
2254         } else if(strcmp(arg, "touchsb") == 0)
2255                 touchsb(dev);
2256         else if(strcmp(arg, "savecache") == 0)
2257                 savecache(dev);
2258         else if(strcmp(arg, "loadcache") == 0) {
2259                 s1 = -1;
2260                 if(argc > 2)
2261                         s1 = number(argv[2], 0, 10);
2262                 loadcache(dev, s1);
2263         } else if(strcmp(arg, "morecache") == 0) {
2264                 if(argc <= 2) {
2265                         print("arg count\n");
2266                         return;
2267                 }
2268                 s1 = number(argv[2], 0, 10);
2269                 if(argc > 3)
2270                         s2 = number(argv[3], 0, 10);
2271                 else
2272                         s2 = wormsizeside(dev, s1); /* default to 1 disc side */
2273                 morecache(dev, s1, s2);
2274         } else if(strcmp(arg, "blockcmp") == 0) {
2275                 if(argc < 4) {
2276                         print("cannot arg count\n");
2277                         return;
2278                 }
2279                 s1 = number(argv[2], 0, 10);
2280                 s2 = number(argv[3], 0, 10);
2281                 blockcmp(dev, s1, s2);
2282         } else if(strcmp(arg, "startdump") == 0) {
2283                 if(argc > 2)
2284                         cw->nodump = !number(argv[2], 0, 10);
2285                 else
2286                         cw->nodump = !cw->nodump;
2287                 if(cw->nodump)
2288                         print("dump stopped\n");
2289                 else
2290                         print("dump allowed\n");
2291         } else if(strcmp(arg, "allflag") == 0) {
2292                 if(argc > 2)
2293                         cw->allflag = number(argv[2], 0, 10);
2294                 else
2295                         cw->allflag = !cw->allflag;
2296                 print("allflag = %d; falsehits = %lld\n",
2297                         cw->allflag, (Wideoff)cw->falsehits);
2298         } else if(strcmp(arg, "storesb") == 0) {
2299                 a = 4168344;
2300                 b = 0;
2301                 if(argc > 2)
2302                         a = number(argv[2], 4168344, 10);
2303                 if(argc > 3)
2304                         b = number(argv[3], 0, 10);
2305                 storesb(dev, a, b);
2306         } else if(strcmp(arg, "test") == 0)
2307                 cwtest(dev);
2308         else
2309                 print("unknown cwcmd %s\n", arg);
2310 }