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