]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/cwfs/chk.c
disk/format: implement long name support
[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;
434         if(d->mode & DDIR)
435                 edent.qpath ^= QPDIR;
436         qmark(edent.qpath);
437         if(edent.qpath > maxq)
438                 maxq = edent.qpath;
439
440         /* clear temporary files (after recover) */
441         if((d->mode & DTMP) && (flags & Crtmp)){
442                 for(i=0; i<NDBLOCK; i++)
443                         d->dblock[i] = 0;
444                 for(i=0; i<NIBLOCK; i++)
445                         d->iblocks[i] = 0;
446                 d->size = 0;
447
448                 /* if not directory, delete file */
449                 if((d->mode & DDIR) == 0)
450                         memset(d, 0, sizeof(Dentry));
451
452                 return 1;
453         }
454
455         /* check direct blocks (the common case) */
456         dmod = 0;
457         {
458                 Off a;
459
460                 for(i=0; i<NDBLOCK; i++) {
461                         dmod += daddrck(&d->dblock[i], &a);
462                         if (a)
463                                 dmod += dirck(&edent, a);
464                 }
465         }
466         /* check indirect^n blocks, if any */
467         for (i = 0; i < NIBLOCK; i++)
468                 dmod += indiraddrck(&edent, &d->iblocks[i], Tind1+i);
469         return dmod;
470 }
471
472 enum { XFEN = FEPERBUF + 6 };
473
474 typedef struct {
475         int     flag;
476         int     count;
477         int     next;
478         Off     addr[XFEN];
479 } Xfree;
480
481 static void
482 xaddfree(Device *dev, Off a, Superb *sb, Iobuf *p)
483 {
484         Xfree *x;
485
486         x = (Xfree*)p->iobuf;
487         if(x->count < XFEN) {
488                 x->addr[x->count] = a;
489                 x->count++;
490                 return;
491         }
492         if(!x->flag) {
493                 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
494                 sb->fbuf.free[0] = 0L;
495                 sb->fbuf.nfree = 1;
496                 sb->tfree = 0;
497                 x->flag = 1;
498         }
499         addfree(dev, a, sb);
500 }
501
502 static void
503 xflush(Device *dev, Superb *sb, Iobuf *p)
504 {
505         int i;
506         Xfree *x;
507
508         x = (Xfree*)p->iobuf;
509         if(!x->flag) {
510                 memset(&sb->fbuf, 0, sizeof(sb->fbuf));
511                 sb->fbuf.free[0] = 0L;
512                 sb->fbuf.nfree = 1;
513                 sb->tfree = 0;
514         }
515         for(i=0; i<x->count; i++)
516                 addfree(dev, x->addr[i], sb);
517 }
518
519 /*
520  * make freelist
521  * from existing freelist
522  * (cw devices)
523  */
524 static void
525 trfreelist(Superb *sb)
526 {
527         Off a, n;
528         int i;
529         Iobuf *p, *xp;
530         Fbuf *fb;
531
532
533         xp = getbuf(devnone, Cckbuf, 0);
534         memset(xp->iobuf, 0, BUFSIZE);
535         fb = &sb->fbuf;
536         p = 0;
537         for(;;) {
538                 n = fb->nfree;
539                 if(n < 0 || n > FEPERBUF)
540                         break;
541                 for(i=1; i<n; i++) {
542                         a = fb->free[i];
543                         if(a && !ftest(a))
544                                 xaddfree(dev, a, sb, xp);
545                 }
546                 a = fb->free[0];
547                 if(!a)
548                         break;
549                 if(ftest(a))
550                         break;
551                 xaddfree(dev, a, sb, xp);
552                 if(p)
553                         putbuf(p);
554                 p = xtag(a, Tfree, QPNONE);
555                 if(!p)
556                         break;
557                 fb = (Fbuf*)p->iobuf;
558         }
559         if(p)
560                 putbuf(p);
561         xflush(dev, sb, xp);
562         putbuf(xp);
563         mod++;
564         print("%lld blocks free\n", (Wideoff)sb->tfree);
565 }
566
567 static void
568 ckfreelist(Superb *sb)
569 {
570         Off a, lo, hi;
571         int n, i;
572         Iobuf *p;
573         Fbuf *fb;
574
575         strcpy(name, "free list");
576         print("check %s\n", name);
577         fb = &sb->fbuf;
578         a = sbaddr;
579         p = 0;
580         lo = 0;
581         hi = 0;
582         for(;;) {
583                 n = fb->nfree;
584                 if(n < 0 || n > FEPERBUF) {
585                         print("check: nfree bad %lld\n", (Wideoff)a);
586                         break;
587                 }
588                 for(i=1; i<n; i++) {
589                         a = fb->free[i];
590                         if(a && !fmark(a)) {
591                                 if(!lo || lo > a)
592                                         lo = a;
593                                 if(!hi || hi < a)
594                                         hi = a;
595                         }
596                 }
597                 a = fb->free[0];
598                 if(!a)
599                         break;
600                 if(fmark(a))
601                         break;
602                 if(!lo || lo > a)
603                         lo = a;
604                 if(!hi || hi < a)
605                         hi = a;
606                 if(p)
607                         putbuf(p);
608                 p = xtag(a, Tfree, QPNONE);
609                 if(!p)
610                         break;
611                 fb = (Fbuf*)p->iobuf;
612         }
613         if(p)
614                 putbuf(p);
615         if (flags & Ctrim) {
616                 fsize = hi--;           /* fsize = hi + 1 */
617                 sb->fsize = fsize;
618                 mod++;
619                 print("set fsize to %lld\n", (Wideoff)fsize);
620         }
621         print("lo = %lld; hi = %lld\n", (Wideoff)lo, (Wideoff)hi);
622 }
623
624 /*
625  * make freelist from scratch
626  */
627 static void
628 mkfreelist(Superb *sb)
629 {
630         Off a;
631         int i, b;
632
633         if(ronly) {
634                 print("cant make freelist on ronly device\n");
635                 return;
636         }
637         strcpy(name, "free list");
638         memset(&sb->fbuf, 0, sizeof(sb->fbuf));
639         sb->fbuf.free[0] = 0L;
640         sb->fbuf.nfree = 1;
641         sb->tfree = 0;
642         for(a=fsize-fstart-1; a >= 0; a--) {
643                 i = a/8;
644                 if(i < 0 || i >= sizabits)
645                         continue;
646                 b = 1 << (a&7);
647                 if(abits[i] & b)
648                         continue;
649                 addfree(dev, fstart+a, sb);
650         }
651         print("%lld blocks free\n", (Wideoff)sb->tfree);
652         mod++;
653 }
654
655 static Dentry*
656 maked(Off a, int s, Off qpath)
657 {
658         Iobuf *p;
659         Dentry *d, *d1;
660
661         p = xtag(a, Tdir, qpath);
662         if(!p)
663                 return 0;
664         d = getdir(p, s);
665         d1 = chkalloc(sizeof(Dentry));
666         memmove(d1, d, sizeof(Dentry));
667         putbuf(p);
668         return d1;
669 }
670
671 static void
672 modd(Off a, int s, Dentry *d1)
673 {
674         Iobuf *p;
675         Dentry *d;
676
677         if(!(flags & (Cbad|Crtmp)))
678                 return;
679         p = getbuf(dev, a, Brd);
680         d = getdir(p, s);
681         if(!d) {
682                 if(p)
683                         putbuf(p);
684                 return;
685         }
686         memmove(d, d1, sizeof(Dentry));
687         p->flags |= Bmod;
688         putbuf(p);
689 }
690
691 static void
692 xread(Off a, Off qpath)
693 {
694         Iobuf *p;
695
696         p = xtag(a, Tfile, qpath);
697         if(p)
698                 putbuf(p);
699 }
700
701 static Iobuf*
702 xtag(Off a, int tag, Off qpath)
703 {
704         Iobuf *p;
705
706         if(a == 0)
707                 return 0;
708         p = getbuf(dev, a, Brd);
709         if(!p) {
710                 print("check: \"%s\": xtag: p null\n", name);
711                 if(flags & (Cream|Ctag)) {
712                         p = getbuf(dev, a, Bmod);
713                         if(p) {
714                                 memset(p->iobuf, 0, RBUFSIZE);
715                                 settag(p, tag, qpath);
716                                 mod++;
717                                 return p;
718                         }
719                 }
720                 return 0;
721         }
722         if(checktag(p, tag, qpath)) {
723                 print("check: \"%s\": xtag: checktag\n", name);
724                 if(flags & (Cream|Ctag)) {
725                         if(flags & Cream)
726                                 memset(p->iobuf, 0, RBUFSIZE);
727                         settag(p, tag, qpath);
728                         mod++;
729                         return p;
730                 }
731                 return p;
732         }
733         return p;
734 }
735
736 static int
737 amark(Off a)
738 {
739         Off i;
740         int b;
741
742         if(a < fstart || a >= fsize) {
743                 if(a == 0)
744                         return 0;
745                 print("check: \"%s\": range %lld\n",
746                         name, (Wideoff)a);
747                 nbad++;
748                 return 1;
749         }
750         a -= fstart;
751         i = a/8;
752         b = 1 << (a&7);
753         if(abits[i] & b) {
754                 if(!ronly)
755                         if(ndup < 10)
756                                 print("check: \"%s\": address dup %lld\n",
757                                         name, (Wideoff)fstart+a);
758                         else if(ndup == 10)
759                                 print("...");
760                 ndup++;
761                 return 1;
762         }
763         abits[i] |= b;
764         nused++;
765         return 0;
766 }
767
768 static int
769 fmark(Off a)
770 {
771         Off i;
772         int b;
773
774         if(a < fstart || a >= fsize) {
775                 print("check: \"%s\": range %lld\n",
776                         name, (Wideoff)a);
777                 nbad++;
778                 return 1;
779         }
780         a -= fstart;
781         i = a/8;
782         b = 1 << (a&7);
783         if(abits[i] & b) {
784                 print("check: \"%s\": address dup %lld\n",
785                         name, (Wideoff)fstart+a);
786                 nfdup++;
787                 return 1;
788         }
789         abits[i] |= b;
790         nfree++;
791         return 0;
792 }
793
794 static int
795 ftest(Off a)
796 {
797         Off i;
798         int b;
799
800         if(a < fstart || a >= fsize)
801                 return 1;
802         a -= fstart;
803         i = a/8;
804         b = 1 << (a&7);
805         if(abits[i] & b)
806                 return 1;
807         abits[i] |= b;
808         return 0;
809 }
810
811 static void
812 missing(void)
813 {
814         Off a, i;
815         int b, n;
816
817         n = 0;
818         for(a=fsize-fstart-1; a>=0; a--) {
819                 i = a/8;
820                 b = 1 << (a&7);
821                 if(!(abits[i] & b)) {
822                         print("missing: %lld\n", (Wideoff)fstart+a);
823                         n++;
824                 }
825                 if(n > 10) {
826                         print(" ...\n");
827                         break;
828                 }
829         }
830 }
831
832 static void
833 qmark(Off qpath)
834 {
835         int b;
836         Off i;
837
838         i = qpath/8;
839         b = 1 << (qpath&7);
840         if(i < 0 || i >= sizqbits) {
841                 nqbad++;
842                 if(nqbad < 20)
843                         print("check: \"%s\": qid out of range %llux\n",
844                                 name, (Wideoff)qpath);
845                 return;
846         }
847         if((qbits[i] & b) && !ronly) {
848                 nqbad++;
849                 if(nqbad < 20)
850                         print("check: \"%s\": qid dup %llux\n", name,
851                                 (Wideoff)qpath);
852         }
853         qbits[i] |= b;
854 }