]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/chk.c
kernel: keep segment locked for data2txt
[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
63         if (p == nil)
64                 panic("chkalloc: out of memory");
65         return p;
66 }
67
68 void
69 chkfree(void *p)
70 {
71         free(p);
72 }
73
74 /*
75  * check flags
76  */
77 enum
78 {
79         Crdall  = (1<<0),       /* read all files */
80         Ctag    = (1<<1),       /* rebuild tags */
81         Cpfile  = (1<<2),       /* print files */
82         Cpdir   = (1<<3),       /* print directories */
83         Cfree   = (1<<4),       /* rebuild free list */
84 //      Csetqid = (1<<5),       /* resequence qids */
85         Cream   = (1<<6),       /* clear all bad tags */
86         Cbad    = (1<<7),       /* clear all bad blocks */
87         Ctouch  = (1<<8),       /* touch old dir and indir */
88         Ctrim   = (1<<9),   /* trim fsize back to fit when checking free list */
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         0,
106 };
107
108 void
109 cmd_check(int argc, char *argv[])
110 {
111         long f, i, flag;
112         Off raddr;
113         Filsys *fs;
114         Iobuf *p;
115         Superb *sb;
116         Dentry *d;
117
118         flag = 0;
119         for(i=1; i<argc; i++) {
120                 for(f=0; ckoption[f].option; f++)
121                         if(strcmp(argv[i], ckoption[f].option) == 0)
122                                 goto found;
123                 print("unknown check option %s\n", argv[i]);
124                 for(f=0; ckoption[f].option; f++)
125                         print("\t%s\n", ckoption[f].option);
126                 return;
127         found:
128                 flag |= ckoption[f].flag;
129         }
130         fs = cons.curfs;
131
132         dev = fs->dev;
133         ronly = (dev->type == Devro);
134         cwflag = (dev->type == Devcw) | (dev->type == Devro);
135         if(!ronly)
136                 wlock(&mainlock);               /* check */
137         flags = flag;
138
139         name = abits = qbits = nil;             /* in case of goto */
140         sbaddr = superaddr(dev);
141         raddr = getraddr(dev);
142         p = xtag(sbaddr, Tsuper, QPSUPER);
143         if(!p)
144                 goto out;
145         sb = (Superb*)p->iobuf;
146         fstart = 2;
147         cons.noage = 1;
148
149         /* 200 is slop since qidgen is likely to be a little bit low */
150         sizqbits = (sb->qidgen+200 + 7) / 8;
151         qbits = chkalloc(sizqbits);
152
153         fsize = sb->fsize;
154         sizabits = (fsize-fstart + 7)/8;
155         abits = chkalloc(sizabits);
156
157         sizname = 4000;
158         name = chkalloc(sizname);
159         sizname -= NAMELEN+10;  /* for safety */
160
161         mod = 0;
162         nfree = 0;
163         nfdup = 0;
164         nused = 0;
165         nbad = 0;
166         ndup = 0;
167         nqbad = 0;
168         depth = 0;
169         maxdepth = 0;
170
171         if(flags & Ctouch) {
172                 /* round fsize down to start of current side */
173                 int s;
174                 Devsize dsize;
175
176                 oldblock = 0;
177                 for (s = 0; dsize = wormsizeside(dev, s),
178                      dsize > 0 && oldblock + dsize < fsize; s++)
179                         oldblock += dsize;
180                 print("oldblock = %lld\n", (Wideoff)oldblock);
181         }
182         amark(sbaddr);
183         if(cwflag) {
184                 amark(sb->roraddr);
185                 amark(sb->next);
186         }
187
188         print("checking filsys: %s\n", fs->name);
189         nfiles = 0;
190         maxq = 0;
191
192         d = maked(raddr, 0, QPROOT);
193         if(d) {
194                 amark(raddr);
195                 if(fsck(d))
196                         modd(raddr, 0, d);
197                 chkfree(d);
198                 depth--;
199                 if(depth)
200                         print("depth not zero on return\n");
201         }
202
203         if(flags & Cfree)
204                 if(cwflag)
205                         trfreelist(sb);
206                 else
207                         mkfreelist(sb);
208
209         if(sb->qidgen < maxq)
210                 print("qid generator low path=%lld maxq=%lld\n",
211                         (Wideoff)sb->qidgen, (Wideoff)maxq);
212         if(!(flags & Cfree))
213                 ckfreelist(sb);
214         if(mod) {
215                 sb->qidgen = maxq;
216                 print("file system was modified\n");
217                 settag(p, Tsuper, QPNONE);
218         }
219
220         print("nfiles = %lld\n", (Wideoff)nfiles);
221         print("fsize  = %lld\n", (Wideoff)fsize);
222         print("nused  = %lld\n", (Wideoff)nused);
223         print("ndup   = %lld\n", (Wideoff)ndup);
224         print("nfree  = %lld\n", (Wideoff)nfree);
225         print("tfree  = %lld\n", (Wideoff)sb->tfree);
226         print("nfdup  = %lld\n", (Wideoff)nfdup);
227         print("nmiss  = %lld\n", (Wideoff)fsize-fstart-nused-nfree);
228         print("nbad   = %lld\n", (Wideoff)nbad);
229         print("nqbad  = %lld\n", (Wideoff)nqbad);
230         print("maxq   = %lld\n", (Wideoff)maxq);
231         print("base stack=%llud\n", (vlong)startstack);
232         /* high-water mark of stack usage */
233         print("high stack=%llud\n", (vlong)lowstack);
234         print("deepest recursion=%d\n", maxdepth-1);    /* one-origin */
235         if(!cwflag)
236                 missing();
237
238 out:
239         cons.noage = 0;
240         putbuf(p);
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                 putbuf(p1);
365         }
366         return dmod;
367 }
368
369 static int
370 indiraddrck(Extdentry *ed, Off *indirp, int tag)
371 {
372         int dmod;
373         Off a;
374
375         dmod = daddrck(indirp, &a);
376         return dmod + indirck(ed, a, tag);
377 }
378
379 /* if result is true, *d was modified */
380 static int
381 fsck(Dentry *d)
382 {
383         int i, dmod;
384         Extdentry edent;
385
386         depth++;
387         if(depth >= maxdepth)
388                 maxdepth = depth;
389         if (lowstack == nil)
390                 startstack = lowstack = (uchar *)&edent;
391         /* more precise check, assumes downward-growing stack */
392         if ((uchar *)&edent < lowstack)
393                 lowstack = (uchar *)&edent;
394
395         /* check that entry is allocated */
396         if(!(d->mode & DALLOC))
397                 return 0;
398         nfiles++;
399
400         /* we stash qpath & ns in an Extdentry for eventual use by dirck() */
401         memset(&edent, 0, sizeof edent);
402         edent.d = d;
403
404         /* check name */
405         edent.ns = strlen(name);
406         i = strlen(d->name);
407         if(i >= NAMELEN) {
408                 d->name[NAMELEN-1] = 0;
409                 print("%s->name (%s) not terminated\n", name, d->name);
410                 return 0;
411         }
412         edent.ns += i;
413         if(edent.ns >= sizname) {
414                 print("%s->name (%s) name too large\n", name, d->name);
415                 return 0;
416         }
417         strcat(name, d->name);
418
419         if(d->mode & DDIR) {
420                 if(edent.ns > 1) {
421                         strcat(name, "/");
422                         edent.ns++;
423                 }
424                 if(flags & Cpdir) {
425                         print("%s\n", name);
426                         prflush();
427                 }
428         } else if(flags & Cpfile) {
429                 print("%s\n", name);
430                 prflush();
431         }
432
433         /* check qid */
434         edent.qpath = d->qid.path & ~QPDIR;
435         qmark(edent.qpath);
436         if(edent.qpath > maxq)
437                 maxq = edent.qpath;
438
439         /* check direct blocks (the common case) */
440         dmod = 0;
441         {
442                 Off a;
443
444                 for(i=0; i<NDBLOCK; i++) {
445                         dmod += daddrck(&d->dblock[i], &a);
446                         if (a)
447                                 dmod += dirck(&edent, a);
448                 }
449         }
450         /* check indirect^n blocks, if any */
451         for (i = 0; i < NIBLOCK; i++)
452                 dmod += indiraddrck(&edent, &d->iblocks[i], Tind1+i);
453         return dmod;
454 }
455
456 enum { XFEN = FEPERBUF + 6 };
457
458 typedef struct {
459         int     flag;
460         int     count;
461         int     next;
462         Off     addr[XFEN];
463 } Xfree;
464
465 static void
466 xaddfree(Device *dev, Off a, Superb *sb, Iobuf *p)
467 {
468         Xfree *x;
469
470         x = (Xfree*)p->iobuf;
471         if(x->count < XFEN) {
472                 x->addr[x->count] = a;
473                 x->count++;
474                 return;
475         }
476         if(!x->flag) {
477                 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
478                 sb->fbuf.free[0] = 0L;
479                 sb->fbuf.nfree = 1;
480                 sb->tfree = 0;
481                 x->flag = 1;
482         }
483         addfree(dev, a, sb);
484 }
485
486 static void
487 xflush(Device *dev, Superb *sb, Iobuf *p)
488 {
489         int i;
490         Xfree *x;
491
492         x = (Xfree*)p->iobuf;
493         if(!x->flag) {
494                 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
495                 sb->fbuf.free[0] = 0L;
496                 sb->fbuf.nfree = 1;
497                 sb->tfree = 0;
498         }
499         for(i=0; i<x->count; i++)
500                 addfree(dev, x->addr[i], sb);
501 }
502
503 /*
504  * make freelist
505  * from existing freelist
506  * (cw devices)
507  */
508 static void
509 trfreelist(Superb *sb)
510 {
511         Off a, n;
512         int i;
513         Iobuf *p, *xp;
514         Fbuf *fb;
515
516
517         xp = getbuf(devnone, Cckbuf, 0);
518         memset(xp->iobuf, 0, BUFSIZE);
519         fb = &sb->fbuf;
520         p = 0;
521         for(;;) {
522                 n = fb->nfree;
523                 if(n < 0 || n > FEPERBUF)
524                         break;
525                 for(i=1; i<n; i++) {
526                         a = fb->free[i];
527                         if(a && !ftest(a))
528                                 xaddfree(dev, a, sb, xp);
529                 }
530                 a = fb->free[0];
531                 if(!a)
532                         break;
533                 if(ftest(a))
534                         break;
535                 xaddfree(dev, a, sb, xp);
536                 if(p)
537                         putbuf(p);
538                 p = xtag(a, Tfree, QPNONE);
539                 if(!p)
540                         break;
541                 fb = (Fbuf*)p->iobuf;
542         }
543         if(p)
544                 putbuf(p);
545         xflush(dev, sb, xp);
546         putbuf(xp);
547         mod++;
548         print("%lld blocks free\n", (Wideoff)sb->tfree);
549 }
550
551 static void
552 ckfreelist(Superb *sb)
553 {
554         Off a, lo, hi;
555         int n, i;
556         Iobuf *p;
557         Fbuf *fb;
558
559         strcpy(name, "free list");
560         print("check %s\n", name);
561         fb = &sb->fbuf;
562         a = sbaddr;
563         p = 0;
564         lo = 0;
565         hi = 0;
566         for(;;) {
567                 n = fb->nfree;
568                 if(n < 0 || n > FEPERBUF) {
569                         print("check: nfree bad %lld\n", (Wideoff)a);
570                         break;
571                 }
572                 for(i=1; i<n; i++) {
573                         a = fb->free[i];
574                         if(a && !fmark(a)) {
575                                 if(!lo || lo > a)
576                                         lo = a;
577                                 if(!hi || hi < a)
578                                         hi = a;
579                         }
580                 }
581                 a = fb->free[0];
582                 if(!a)
583                         break;
584                 if(fmark(a))
585                         break;
586                 if(!lo || lo > a)
587                         lo = a;
588                 if(!hi || hi < a)
589                         hi = a;
590                 if(p)
591                         putbuf(p);
592                 p = xtag(a, Tfree, QPNONE);
593                 if(!p)
594                         break;
595                 fb = (Fbuf*)p->iobuf;
596         }
597         if(p)
598                 putbuf(p);
599         if (flags & Ctrim) {
600                 fsize = hi--;           /* fsize = hi + 1 */
601                 sb->fsize = fsize;
602                 mod++;
603                 print("set fsize to %lld\n", (Wideoff)fsize);
604         }
605         print("lo = %lld; hi = %lld\n", (Wideoff)lo, (Wideoff)hi);
606 }
607
608 /*
609  * make freelist from scratch
610  */
611 static void
612 mkfreelist(Superb *sb)
613 {
614         Off a;
615         int i, b;
616
617         if(ronly) {
618                 print("cant make freelist on ronly device\n");
619                 return;
620         }
621         strcpy(name, "free list");
622         memset(&sb->fbuf, 0, sizeof(sb->fbuf));
623         sb->fbuf.free[0] = 0L;
624         sb->fbuf.nfree = 1;
625         sb->tfree = 0;
626         for(a=fsize-fstart-1; a >= 0; a--) {
627                 i = a/8;
628                 if(i < 0 || i >= sizabits)
629                         continue;
630                 b = 1 << (a&7);
631                 if(abits[i] & b)
632                         continue;
633                 addfree(dev, fstart+a, sb);
634         }
635         print("%lld blocks free\n", (Wideoff)sb->tfree);
636         mod++;
637 }
638
639 static Dentry*
640 maked(Off a, int s, Off qpath)
641 {
642         Iobuf *p;
643         Dentry *d, *d1;
644
645         p = xtag(a, Tdir, qpath);
646         if(!p)
647                 return 0;
648         d = getdir(p, s);
649         d1 = chkalloc(sizeof(Dentry));
650         memmove(d1, d, sizeof(Dentry));
651         putbuf(p);
652         return d1;
653 }
654
655 static void
656 modd(Off a, int s, Dentry *d1)
657 {
658         Iobuf *p;
659         Dentry *d;
660
661         if(!(flags & Cbad))
662                 return;
663         p = getbuf(dev, a, Brd);
664         d = getdir(p, s);
665         if(!d) {
666                 if(p)
667                         putbuf(p);
668                 return;
669         }
670         memmove(d, d1, sizeof(Dentry));
671         p->flags |= Bmod;
672         putbuf(p);
673 }
674
675 static void
676 xread(Off a, Off qpath)
677 {
678         Iobuf *p;
679
680         p = xtag(a, Tfile, qpath);
681         if(p)
682                 putbuf(p);
683 }
684
685 static Iobuf*
686 xtag(Off a, int tag, Off qpath)
687 {
688         Iobuf *p;
689
690         if(a == 0)
691                 return 0;
692         p = getbuf(dev, a, Brd);
693         if(!p) {
694                 print("check: \"%s\": xtag: p null\n", name);
695                 if(flags & (Cream|Ctag)) {
696                         p = getbuf(dev, a, Bmod);
697                         if(p) {
698                                 memset(p->iobuf, 0, RBUFSIZE);
699                                 settag(p, tag, qpath);
700                                 mod++;
701                                 return p;
702                         }
703                 }
704                 return 0;
705         }
706         if(checktag(p, tag, qpath)) {
707                 print("check: \"%s\": xtag: checktag\n", name);
708                 if(flags & (Cream|Ctag)) {
709                         if(flags & Cream)
710                                 memset(p->iobuf, 0, RBUFSIZE);
711                         settag(p, tag, qpath);
712                         mod++;
713                         return p;
714                 }
715                 return p;
716         }
717         return p;
718 }
719
720 static int
721 amark(Off a)
722 {
723         Off i;
724         int b;
725
726         if(a < fstart || a >= fsize) {
727                 if(a == 0)
728                         return 0;
729                 print("check: \"%s\": range %lld\n",
730                         name, (Wideoff)a);
731                 nbad++;
732                 return 1;
733         }
734         a -= fstart;
735         i = a/8;
736         b = 1 << (a&7);
737         if(abits[i] & b) {
738                 if(!ronly)
739                         if(ndup < 10)
740                                 print("check: \"%s\": address dup %lld\n",
741                                         name, (Wideoff)fstart+a);
742                         else if(ndup == 10)
743                                 print("...");
744                 ndup++;
745                 return 1;
746         }
747         abits[i] |= b;
748         nused++;
749         return 0;
750 }
751
752 static int
753 fmark(Off a)
754 {
755         Off i;
756         int b;
757
758         if(a < fstart || a >= fsize) {
759                 print("check: \"%s\": range %lld\n",
760                         name, (Wideoff)a);
761                 nbad++;
762                 return 1;
763         }
764         a -= fstart;
765         i = a/8;
766         b = 1 << (a&7);
767         if(abits[i] & b) {
768                 print("check: \"%s\": address dup %lld\n",
769                         name, (Wideoff)fstart+a);
770                 nfdup++;
771                 return 1;
772         }
773         abits[i] |= b;
774         nfree++;
775         return 0;
776 }
777
778 static int
779 ftest(Off a)
780 {
781         Off i;
782         int b;
783
784         if(a < fstart || a >= fsize)
785                 return 1;
786         a -= fstart;
787         i = a/8;
788         b = 1 << (a&7);
789         if(abits[i] & b)
790                 return 1;
791         abits[i] |= b;
792         return 0;
793 }
794
795 static void
796 missing(void)
797 {
798         Off a, i;
799         int b, n;
800
801         n = 0;
802         for(a=fsize-fstart-1; a>=0; a--) {
803                 i = a/8;
804                 b = 1 << (a&7);
805                 if(!(abits[i] & b)) {
806                         print("missing: %lld\n", (Wideoff)fstart+a);
807                         n++;
808                 }
809                 if(n > 10) {
810                         print(" ...\n");
811                         break;
812                 }
813         }
814 }
815
816 static void
817 qmark(Off qpath)
818 {
819         int b;
820         Off i;
821
822         i = qpath/8;
823         b = 1 << (qpath&7);
824         if(i < 0 || i >= sizqbits) {
825                 nqbad++;
826                 if(nqbad < 20)
827                         print("check: \"%s\": qid out of range %llux\n",
828                                 name, (Wideoff)qpath);
829                 return;
830         }
831         if((qbits[i] & b) && !ronly) {
832                 nqbad++;
833                 if(nqbad < 20)
834                         print("check: \"%s\": qid dup %llux\n", name,
835                                 (Wideoff)qpath);
836         }
837         qbits[i] |= b;
838 }