]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/chk.c
webfs(4): document -d and -D flags
[plan9front.git] / sys / src / cmd / cwfs / chk.c
1 #include        "all.h"
2
3 /* augmented Dentry */
4 typedef struct {
5         Dentry  *d;
6         Off     qpath;
7         int     ns;
8 } Extdentry;
9
10 static  char*   abits;
11 static  long    sizabits;
12 static  char*   qbits;
13 static  long    sizqbits;
14
15 static  char*   name;
16 static  long    sizname;
17
18 static  Off     fstart;
19 static  Off     fsize;
20 static  Off     nfiles;
21 static  Off     maxq;
22 static  Device* dev;
23 static  Off     ndup;
24 static  Off     nused;
25 static  Off     nfdup;
26 static  Off     nqbad;
27 static  Off     nfree;
28 static  Off     nbad;
29 static  int     mod;
30 static  int     flags;
31 static  int     ronly;
32 static  int     cwflag;
33 static  Devsize sbaddr;
34 static  Devsize oldblock;
35
36 static  int     depth;
37 static  int     maxdepth;
38 static  uchar   *lowstack, *startstack;
39
40 /* local prototypes */
41 static  int     amark(Off);
42 static  void*   chkalloc(ulong);
43 static  void    ckfreelist(Superb*);
44 static  int     fmark(Off);
45 static  int     fsck(Dentry*);
46 static  int     ftest(Off);
47 static  Dentry* maked(Off, int, Off);
48 static  void    missing(void);
49 static  void    mkfreelist(Superb*);
50 static  void    modd(Off, int, Dentry*);
51 static  void    qmark(Off);
52 static  void    trfreelist(Superb*);
53 static  void    xaddfree(Device*, Off, Superb*, Iobuf*);
54 static  void    xflush(Device*, Superb*, Iobuf*);
55 static  void    xread(Off, Off);
56 static  Iobuf*  xtag(Off, int, Off);
57
58 static void *
59 chkalloc(ulong n)
60 {
61         char *p = mallocz(n, 1);
62         if (p == nil)
63                 panic("chkalloc: out of memory");
64         return p;
65 }
66
67 void
68 chkfree(void *p)
69 {
70         free(p);
71 }
72
73 /*
74  * check flags
75  */
76 enum
77 {
78         Crdall  = (1<<0),       /* read all files */
79         Ctag    = (1<<1),       /* rebuild tags */
80         Cpfile  = (1<<2),       /* print files */
81         Cpdir   = (1<<3),       /* print directories */
82         Cfree   = (1<<4),       /* rebuild free list */
83         Csetqid = (1<<5),       /* resequence qids */
84         Cream   = (1<<6),       /* clear all bad tags */
85         Cbad    = (1<<7),       /* clear all bad blocks */
86         Ctouch  = (1<<8),       /* touch old dir and indir */
87         Ctrim   = (1<<9),       /* trim fsize back to fit when checking free list */
88         Crtmp   = (1<<10),      /* clear temporary files (after recover) */
89 };
90
91 static struct {
92         char*   option;
93         long    flag;
94 } ckoption[] = {
95         "rdall",        Crdall,
96         "tag",          Ctag,
97         "pfile",        Cpfile,
98         "pdir",         Cpdir,
99         "free",         Cfree,
100 //      "setqid",       Csetqid,
101         "ream",         Cream,
102         "bad",          Cbad,
103         "touch",        Ctouch,
104         "trim",         Ctrim,
105         "rtmp",         Crtmp,
106         0,
107 };
108
109 void
110 cmd_check(int argc, char *argv[])
111 {
112         long f, i, flag;
113         Off raddr;
114         Filsys *fs;
115         Iobuf *p;
116         Superb *sb;
117         Dentry *d;
118
119         flag = 0;
120         for(i=1; i<argc; i++) {
121                 for(f=0; ckoption[f].option; f++)
122                         if(strcmp(argv[i], ckoption[f].option) == 0)
123                                 goto found;
124                 print("unknown check option %s\n", argv[i]);
125                 for(f=0; ckoption[f].option; f++)
126                         print("\t%s\n", ckoption[f].option);
127                 return;
128         found:
129                 flag |= ckoption[f].flag;
130         }
131         fs = cons.curfs;
132
133         dev = fs->dev;
134         ronly = (dev->type == Devro);
135         cwflag = (dev->type == Devcw) | (dev->type == Devro);
136         if(!ronly)
137                 wlock(&mainlock);               /* check */
138         flags = flag;
139
140         name = abits = qbits = nil;             /* in case of goto */
141         sbaddr = superaddr(dev);
142         raddr = getraddr(dev);
143         p = xtag(sbaddr, Tsuper, QPSUPER);
144         if(!p)
145                 goto out;
146         sb = (Superb*)p->iobuf;
147         fstart = 2;
148         cons.noage = 1;
149
150         /* 200 is slop since qidgen is likely to be a little bit low */
151         sizqbits = (sb->qidgen+200 + 7) / 8;
152         qbits = chkalloc(sizqbits);
153
154         fsize = sb->fsize;
155         sizabits = (fsize-fstart + 7)/8;
156         abits = chkalloc(sizabits);
157
158         sizname = 4000;
159         name = chkalloc(sizname);
160         sizname -= NAMELEN+10;  /* for safety */
161
162         mod = 0;
163         nfree = 0;
164         nfdup = 0;
165         nused = 0;
166         nbad = 0;
167         ndup = 0;
168         nqbad = 0;
169         depth = 0;
170         maxdepth = 0;
171
172         if(flags & Ctouch) {
173                 /* round fsize down to start of current side */
174                 int s;
175                 Devsize dsize;
176
177                 oldblock = 0;
178                 for (s = 0; dsize = wormsizeside(dev, s),
179                      dsize > 0 && oldblock + dsize < fsize; s++)
180                         oldblock += dsize;
181                 print("oldblock = %lld\n", (Wideoff)oldblock);
182         }
183         amark(sbaddr);
184         if(cwflag) {
185                 amark(sb->roraddr);
186                 amark(sb->next);
187         }
188
189         print("checking filsys: %s\n", fs->name);
190         nfiles = 0;
191         maxq = 0;
192
193         d = maked(raddr, 0, QPROOT);
194         if(d) {
195                 amark(raddr);
196                 if(fsck(d))
197                         modd(raddr, 0, d);
198                 chkfree(d);
199                 depth--;
200                 if(depth)
201                         print("depth not zero on return\n");
202         }
203
204         if(flags & Cfree)
205                 if(cwflag)
206                         trfreelist(sb);
207                 else
208                         mkfreelist(sb);
209
210         if(sb->qidgen < maxq)
211                 print("qid generator low path=%lld maxq=%lld\n",
212                         (Wideoff)sb->qidgen, (Wideoff)maxq);
213         if(!(flags & Cfree))
214                 ckfreelist(sb);
215         if(mod) {
216                 sb->qidgen = maxq;
217                 print("file system was modified\n");
218                 settag(p, Tsuper, QPNONE);
219         }
220
221         print("nfiles = %lld\n", (Wideoff)nfiles);
222         print("fsize  = %lld\n", (Wideoff)fsize);
223         print("nused  = %lld\n", (Wideoff)nused);
224         print("ndup   = %lld\n", (Wideoff)ndup);
225         print("nfree  = %lld\n", (Wideoff)nfree);
226         print("tfree  = %lld\n", (Wideoff)sb->tfree);
227         print("nfdup  = %lld\n", (Wideoff)nfdup);
228         print("nmiss  = %lld\n", (Wideoff)fsize-fstart-nused-nfree);
229         print("nbad   = %lld\n", (Wideoff)nbad);
230         print("nqbad  = %lld\n", (Wideoff)nqbad);
231         print("maxq   = %lld\n", (Wideoff)maxq);
232         print("base stack=%llud\n", (vlong)startstack);
233         /* high-water mark of stack usage */
234         print("high stack=%llud\n", (vlong)lowstack);
235         print("deepest recursion=%d\n", maxdepth-1);    /* one-origin */
236         if(!cwflag)
237                 missing();
238         putbuf(p);
239 out:
240         cons.noage = 0;
241         chkfree(name);
242         chkfree(abits);
243         chkfree(qbits);
244         name = abits = qbits = nil;
245         if(!ronly)
246                 wunlock(&mainlock);
247 }
248
249 /*
250  * if *blkp is already allocated and Cbad is set, zero it.
251  * returns *blkp if it's free, else 0.
252  */
253 static Off
254 blkck(Off *blkp, int *flgp)
255 {
256         Off a = *blkp;
257
258         if(amark(a)) {
259                 if(flags & Cbad) {
260                         *blkp = 0;
261                         *flgp |= Bmod;
262                 }
263                 a = 0;
264         }
265         return a;
266 }
267
268 /*
269  * if a block address within a Dentry, *blkp, is already allocated
270  * and Cbad is set, zero it.
271  * stores 0 into *resp if already allocated, else stores *blkp.
272  * returns dmod count.
273  */
274 static int
275 daddrck(Off *blkp, Off *resp)
276 {
277         int dmod = 0;
278
279         if(amark(*blkp)) {
280                 if(flags & Cbad) {
281                         *blkp = 0;
282                         dmod++;
283                 }
284                 *resp = 0;
285         } else
286                 *resp = *blkp;
287         return dmod;
288 }
289
290 /*
291  * under Ctouch, read block `a' if it's in range.
292  * returns dmod count.
293  */
294 static int
295 touch(Off a)
296 {
297         if((flags&Ctouch) && a < oldblock) {
298                 Iobuf *pd = getbuf(dev, a, Brd|Bmod);
299
300                 if(pd)
301                         putbuf(pd);
302                 return 1;
303         }
304         return 0;
305 }
306
307 /*
308  * if d is a directory, touch it and check all its entries in block a.
309  * if not, under Crdall, read a.
310  * returns dmod count.
311  */
312 static int
313 dirck(Extdentry *ed, Off a)
314 {
315         int k, dmod = 0;
316
317         if(ed->d->mode & DDIR) {
318                 dmod += touch(a);
319                 for(k=0; k<DIRPERBUF; k++) {
320                         Dentry *nd = maked(a, k, ed->qpath);
321
322                         if(nd == nil)
323                                 break;
324                         if(fsck(nd)) {
325                                 modd(a, k, nd);
326                                 dmod++;
327                         }
328                         chkfree(nd);
329                         depth--;
330                         name[ed->ns] = 0;
331                 }
332         } else if(flags & Crdall)
333                 xread(a, ed->qpath);
334         return dmod;
335 }
336
337 /*
338  * touch a, check a's tag for Tind1, Tind2, etc.
339  * if the tag is right, validate each block number in the indirect block,
340  * and check each block (mostly in case we are reading a huge directory).
341  */
342 static int
343 indirck(Extdentry *ed, Off a, int tag)
344 {
345         int i, dmod = 0;
346         Iobuf *p1;
347
348         if (a == 0)
349                 return dmod;
350         dmod = touch(a);
351         if (p1 = xtag(a, tag, ed->qpath)) {
352                 for(i=0; i<INDPERBUF; i++) {
353                         a = blkck(&((Off *)p1->iobuf)[i], &p1->flags);
354                         if (a) {
355                                 /*
356                                  * check each block named in this
357                                  * indirect(^n) block (a).
358                                  */
359                                 if (tag == Tind1)
360                                         dmod += dirck(ed, a);
361                                 else
362                                         dmod += indirck(ed, a, tag-1);
363                         }
364                 }
365                 putbuf(p1);
366         }
367         return dmod;
368 }
369
370 static int
371 indiraddrck(Extdentry *ed, Off *indirp, int tag)
372 {
373         int dmod;
374         Off a;
375
376         dmod = daddrck(indirp, &a);
377         return dmod + indirck(ed, a, tag);
378 }
379
380 /* if result is true, *d was modified */
381 static int
382 fsck(Dentry *d)
383 {
384         int i, dmod;
385         Extdentry edent;
386
387         depth++;
388         if(depth >= maxdepth)
389                 maxdepth = depth;
390         if (lowstack == nil)
391                 startstack = lowstack = (uchar *)&edent;
392         /* more precise check, assumes downward-growing stack */
393         if ((uchar *)&edent < lowstack)
394                 lowstack = (uchar *)&edent;
395
396         /* check that entry is allocated */
397         if(!(d->mode & DALLOC) || (ronly && (d->mode & DTMP)))
398                 return 0;
399         nfiles++;
400
401         /* we stash qpath & ns in an Extdentry for eventual use by dirck() */
402         memset(&edent, 0, sizeof edent);
403         edent.d = d;
404
405         /* check name */
406         edent.ns = strlen(name);
407         i = strlen(d->name);
408         if(i >= NAMELEN) {
409                 d->name[NAMELEN-1] = 0;
410                 print("%s->name (%s) not terminated\n", name, d->name);
411                 return 0;
412         }
413         edent.ns += i;
414         if(edent.ns >= sizname) {
415                 print("%s->name (%s) name too large\n", name, d->name);
416                 return 0;
417         }
418         strcat(name, d->name);
419
420         if(d->mode & DDIR) {
421                 if(edent.ns > 1) {
422                         strcat(name, "/");
423                         edent.ns++;
424                 }
425                 if(flags & Cpdir) {
426                         print("%s\n", name);
427                 }
428         } else if(flags & Cpfile) {
429                 print("%s\n", name);
430         }
431
432         /* check qid */
433         edent.qpath = d->qid.path & ~QPDIR;
434         qmark(edent.qpath);
435         if(edent.qpath > maxq)
436                 maxq = edent.qpath;
437
438         /* clear temporary files (after recover) */
439         if((d->mode & DTMP) && (flags & Crtmp)){
440                 for(i=0; i<NDBLOCK; i++)
441                         d->dblock[i] = 0;
442                 for(i=0; i<NIBLOCK; i++)
443                         d->iblocks[i] = 0;
444                 d->size = 0;
445
446                 /* if not directory, delete file */
447                 if((d->mode & DDIR) == 0)
448                         memset(d, 0, sizeof(Dentry));
449
450                 return 1;
451         }
452
453         /* check direct blocks (the common case) */
454         dmod = 0;
455         {
456                 Off a;
457
458                 for(i=0; i<NDBLOCK; i++) {
459                         dmod += daddrck(&d->dblock[i], &a);
460                         if (a)
461                                 dmod += dirck(&edent, a);
462                 }
463         }
464         /* check indirect^n blocks, if any */
465         for (i = 0; i < NIBLOCK; i++)
466                 dmod += indiraddrck(&edent, &d->iblocks[i], Tind1+i);
467         return dmod;
468 }
469
470 enum { XFEN = FEPERBUF + 6 };
471
472 typedef struct {
473         int     flag;
474         int     count;
475         int     next;
476         Off     addr[XFEN];
477 } Xfree;
478
479 static void
480 xaddfree(Device *dev, Off a, Superb *sb, Iobuf *p)
481 {
482         Xfree *x;
483
484         x = (Xfree*)p->iobuf;
485         if(x->count < XFEN) {
486                 x->addr[x->count] = a;
487                 x->count++;
488                 return;
489         }
490         if(!x->flag) {
491                 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
492                 sb->fbuf.free[0] = 0L;
493                 sb->fbuf.nfree = 1;
494                 sb->tfree = 0;
495                 x->flag = 1;
496         }
497         addfree(dev, a, sb);
498 }
499
500 static void
501 xflush(Device *dev, Superb *sb, Iobuf *p)
502 {
503         int i;
504         Xfree *x;
505
506         x = (Xfree*)p->iobuf;
507         if(!x->flag) {
508                 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
509                 sb->fbuf.free[0] = 0L;
510                 sb->fbuf.nfree = 1;
511                 sb->tfree = 0;
512         }
513         for(i=0; i<x->count; i++)
514                 addfree(dev, x->addr[i], sb);
515 }
516
517 /*
518  * make freelist
519  * from existing freelist
520  * (cw devices)
521  */
522 static void
523 trfreelist(Superb *sb)
524 {
525         Off a, n;
526         int i;
527         Iobuf *p, *xp;
528         Fbuf *fb;
529
530
531         xp = getbuf(devnone, Cckbuf, 0);
532         memset(xp->iobuf, 0, BUFSIZE);
533         fb = &sb->fbuf;
534         p = 0;
535         for(;;) {
536                 n = fb->nfree;
537                 if(n < 0 || n > FEPERBUF)
538                         break;
539                 for(i=1; i<n; i++) {
540                         a = fb->free[i];
541                         if(a && !ftest(a))
542                                 xaddfree(dev, a, sb, xp);
543                 }
544                 a = fb->free[0];
545                 if(!a)
546                         break;
547                 if(ftest(a))
548                         break;
549                 xaddfree(dev, a, sb, xp);
550                 if(p)
551                         putbuf(p);
552                 p = xtag(a, Tfree, QPNONE);
553                 if(!p)
554                         break;
555                 fb = (Fbuf*)p->iobuf;
556         }
557         if(p)
558                 putbuf(p);
559         xflush(dev, sb, xp);
560         putbuf(xp);
561         mod++;
562         print("%lld blocks free\n", (Wideoff)sb->tfree);
563 }
564
565 static void
566 ckfreelist(Superb *sb)
567 {
568         Off a, lo, hi;
569         int n, i;
570         Iobuf *p;
571         Fbuf *fb;
572
573         strcpy(name, "free list");
574         print("check %s\n", name);
575         fb = &sb->fbuf;
576         a = sbaddr;
577         p = 0;
578         lo = 0;
579         hi = 0;
580         for(;;) {
581                 n = fb->nfree;
582                 if(n < 0 || n > FEPERBUF) {
583                         print("check: nfree bad %lld\n", (Wideoff)a);
584                         break;
585                 }
586                 for(i=1; i<n; i++) {
587                         a = fb->free[i];
588                         if(a && !fmark(a)) {
589                                 if(!lo || lo > a)
590                                         lo = a;
591                                 if(!hi || hi < a)
592                                         hi = a;
593                         }
594                 }
595                 a = fb->free[0];
596                 if(!a)
597                         break;
598                 if(fmark(a))
599                         break;
600                 if(!lo || lo > a)
601                         lo = a;
602                 if(!hi || hi < a)
603                         hi = a;
604                 if(p)
605                         putbuf(p);
606                 p = xtag(a, Tfree, QPNONE);
607                 if(!p)
608                         break;
609                 fb = (Fbuf*)p->iobuf;
610         }
611         if(p)
612                 putbuf(p);
613         if (flags & Ctrim) {
614                 fsize = hi--;           /* fsize = hi + 1 */
615                 sb->fsize = fsize;
616                 mod++;
617                 print("set fsize to %lld\n", (Wideoff)fsize);
618         }
619         print("lo = %lld; hi = %lld\n", (Wideoff)lo, (Wideoff)hi);
620 }
621
622 /*
623  * make freelist from scratch
624  */
625 static void
626 mkfreelist(Superb *sb)
627 {
628         Off a;
629         int i, b;
630
631         if(ronly) {
632                 print("cant make freelist on ronly device\n");
633                 return;
634         }
635         strcpy(name, "free list");
636         memset(&sb->fbuf, 0, sizeof(sb->fbuf));
637         sb->fbuf.free[0] = 0L;
638         sb->fbuf.nfree = 1;
639         sb->tfree = 0;
640         for(a=fsize-fstart-1; a >= 0; a--) {
641                 i = a/8;
642                 if(i < 0 || i >= sizabits)
643                         continue;
644                 b = 1 << (a&7);
645                 if(abits[i] & b)
646                         continue;
647                 addfree(dev, fstart+a, sb);
648         }
649         print("%lld blocks free\n", (Wideoff)sb->tfree);
650         mod++;
651 }
652
653 static Dentry*
654 maked(Off a, int s, Off qpath)
655 {
656         Iobuf *p;
657         Dentry *d, *d1;
658
659         p = xtag(a, Tdir, qpath);
660         if(!p)
661                 return 0;
662         d = getdir(p, s);
663         d1 = chkalloc(sizeof(Dentry));
664         memmove(d1, d, sizeof(Dentry));
665         putbuf(p);
666         return d1;
667 }
668
669 static void
670 modd(Off a, int s, Dentry *d1)
671 {
672         Iobuf *p;
673         Dentry *d;
674
675         if(!(flags & (Cbad|Crtmp)))
676                 return;
677         p = getbuf(dev, a, Brd);
678         d = getdir(p, s);
679         if(!d) {
680                 if(p)
681                         putbuf(p);
682                 return;
683         }
684         memmove(d, d1, sizeof(Dentry));
685         p->flags |= Bmod;
686         putbuf(p);
687 }
688
689 static void
690 xread(Off a, Off qpath)
691 {
692         Iobuf *p;
693
694         p = xtag(a, Tfile, qpath);
695         if(p)
696                 putbuf(p);
697 }
698
699 static Iobuf*
700 xtag(Off a, int tag, Off qpath)
701 {
702         Iobuf *p;
703
704         if(a == 0)
705                 return 0;
706         p = getbuf(dev, a, Brd);
707         if(!p) {
708                 print("check: \"%s\": xtag: p null\n", name);
709                 if(flags & (Cream|Ctag)) {
710                         p = getbuf(dev, a, Bmod);
711                         if(p) {
712                                 memset(p->iobuf, 0, RBUFSIZE);
713                                 settag(p, tag, qpath);
714                                 mod++;
715                                 return p;
716                         }
717                 }
718                 return 0;
719         }
720         if(checktag(p, tag, qpath)) {
721                 print("check: \"%s\": xtag: checktag\n", name);
722                 if(flags & (Cream|Ctag)) {
723                         if(flags & Cream)
724                                 memset(p->iobuf, 0, RBUFSIZE);
725                         settag(p, tag, qpath);
726                         mod++;
727                         return p;
728                 }
729                 return p;
730         }
731         return p;
732 }
733
734 static int
735 amark(Off a)
736 {
737         Off i;
738         int b;
739
740         if(a < fstart || a >= fsize) {
741                 if(a == 0)
742                         return 0;
743                 print("check: \"%s\": range %lld\n",
744                         name, (Wideoff)a);
745                 nbad++;
746                 return 1;
747         }
748         a -= fstart;
749         i = a/8;
750         b = 1 << (a&7);
751         if(abits[i] & b) {
752                 if(!ronly)
753                         if(ndup < 10)
754                                 print("check: \"%s\": address dup %lld\n",
755                                         name, (Wideoff)fstart+a);
756                         else if(ndup == 10)
757                                 print("...");
758                 ndup++;
759                 return 1;
760         }
761         abits[i] |= b;
762         nused++;
763         return 0;
764 }
765
766 static int
767 fmark(Off a)
768 {
769         Off i;
770         int b;
771
772         if(a < fstart || a >= fsize) {
773                 print("check: \"%s\": range %lld\n",
774                         name, (Wideoff)a);
775                 nbad++;
776                 return 1;
777         }
778         a -= fstart;
779         i = a/8;
780         b = 1 << (a&7);
781         if(abits[i] & b) {
782                 print("check: \"%s\": address dup %lld\n",
783                         name, (Wideoff)fstart+a);
784                 nfdup++;
785                 return 1;
786         }
787         abits[i] |= b;
788         nfree++;
789         return 0;
790 }
791
792 static int
793 ftest(Off a)
794 {
795         Off i;
796         int b;
797
798         if(a < fstart || a >= fsize)
799                 return 1;
800         a -= fstart;
801         i = a/8;
802         b = 1 << (a&7);
803         if(abits[i] & b)
804                 return 1;
805         abits[i] |= b;
806         return 0;
807 }
808
809 static void
810 missing(void)
811 {
812         Off a, i;
813         int b, n;
814
815         n = 0;
816         for(a=fsize-fstart-1; a>=0; a--) {
817                 i = a/8;
818                 b = 1 << (a&7);
819                 if(!(abits[i] & b)) {
820                         print("missing: %lld\n", (Wideoff)fstart+a);
821                         n++;
822                 }
823                 if(n > 10) {
824                         print(" ...\n");
825                         break;
826                 }
827         }
828 }
829
830 static void
831 qmark(Off qpath)
832 {
833         int b;
834         Off i;
835
836         i = qpath/8;
837         b = 1 << (qpath&7);
838         if(i < 0 || i >= sizqbits) {
839                 nqbad++;
840                 if(nqbad < 20)
841                         print("check: \"%s\": qid out of range %llux\n",
842                                 name, (Wideoff)qpath);
843                 return;
844         }
845         if((qbits[i] & b) && !ronly) {
846                 nqbad++;
847                 if(nqbad < 20)
848                         print("check: \"%s\": qid dup %llux\n", name,
849                                 (Wideoff)qpath);
850         }
851         qbits[i] |= b;
852 }