]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/disk/kfs/chk.c
Add Erik Quanstrom's smart tool for ATA SMART.
[plan9front.git] / sys / src / cmd / disk / kfs / chk.c
1 #include        "all.h"
2
3 #define DSIZE           546000
4 #define MAXDEPTH        100
5
6 static  char*   abits;
7 static  long    sizabits;
8 static  char*   qbits;
9 static  long    sizqbits;
10 static  char*   name;
11 static  long    sizname;
12 static  long    fstart;
13 static  long    fsize;
14 static  long    nfiles;
15 static  long    maxq;
16 static  char*   fence;
17 static  char*   fencebase;
18 static  Device  dev;
19 static  long    ndup;
20 static  long    nused;
21 static  long    nfdup;
22 static  long    nqbad;
23 static  long    nfree;
24 static  long    nbad;
25 static  int     mod;
26 static  int     flags;
27 static  int     ronly;
28 static  int     cwflag;
29 static  long    sbaddr;
30 static  long    oldblock;
31 static  int     depth;
32 static  int     maxdepth;
33
34 /* local prototypes */
35 static  int     fsck(Dentry*);
36 static  void    ckfreelist(Superb*);
37 static  void    mkfreelist(Superb*);
38 static  Dentry* maked(long, int, long);
39 static  void    modd(long, int, Dentry*);
40 static  void    xread(long, long);
41 static  int     amark(long);
42 static  int     fmark(long);
43 static  void    missing(void);
44 static  void    qmark(long);
45 static  void*   zalloc(ulong);
46 static  void*   dalloc(ulong);
47 static  Iobuf*  xtag(long, int, long);
48
49 static
50 void*
51 zalloc(ulong n)
52 {
53         char *p;
54
55         p = malloc(n);
56         if(p == 0)
57                 panic("zalloc: out of memory\n");
58         memset(p, '\0', n);
59         return p;
60 }
61
62 static
63 void*
64 dalloc(ulong n)
65 {
66         char *p;
67
68         if(fencebase == 0)
69                 fence = fencebase = zalloc(MAXDEPTH*sizeof(Dentry));
70         p = fence;
71         fence += n;
72         if(fence > fencebase+MAXDEPTH*sizeof(Dentry))
73                 panic("dalloc too much memory\n");
74         return p;
75 }
76
77 void
78 check(Filsys *fs, long flag)
79 {
80         Iobuf *p;
81         Superb *sb;
82         Dentry *d;
83         long raddr;
84         long nqid;
85
86         wlock(&mainlock);
87         dev = fs->dev;
88         flags = flag;
89         fence = fencebase;
90
91         sizname = 4000;
92         name = zalloc(sizname);
93         sizname -= NAMELEN+10;  /* for safety */
94
95         sbaddr = superaddr(dev);
96         raddr = getraddr(dev);
97         p = xtag(sbaddr, Tsuper, QPSUPER);
98         if(!p){
99                 cprint("bad superblock\n");
100                 goto out;
101         }
102         sb = (Superb*)p->iobuf;
103         fstart = 1;
104
105         fsize = sb->fsize;
106         sizabits = (fsize-fstart + 7)/8;
107         abits = zalloc(sizabits);
108
109         nqid = sb->qidgen+100;          /* not as much of a botch */
110         if(nqid > 1024*1024*8)
111                 nqid = 1024*1024*8;
112         if(nqid < 64*1024)
113                 nqid = 64*1024;
114
115         sizqbits = (nqid+7)/8;
116         qbits = zalloc(sizqbits);
117
118         mod = 0;
119         nfree = 0;
120         nfdup = 0;
121         nused = 0;
122         nbad = 0;
123         ndup = 0;
124         nqbad = 0;
125         depth = 0;
126         maxdepth = 0;
127
128         if(flags & Ctouch) {
129                 oldblock = fsize/DSIZE;
130                 oldblock *= DSIZE;
131                 if(oldblock < 0)
132                         oldblock = 0;
133                 cprint("oldblock = %ld\n", oldblock);
134         }
135         if(amark(sbaddr))
136                 {}
137         if(cwflag) {
138                 if(amark(sb->roraddr))
139                         {}
140                 if(amark(sb->next))
141                         {}
142         }
143
144         if(!(flags & Cquiet))
145                 cprint("checking file system: %s\n", fs->name);
146         nfiles = 0;
147         maxq = 0;
148
149         d = maked(raddr, 0, QPROOT);
150         if(d) {
151                 if(amark(raddr))
152                         {}
153                 if(fsck(d))
154                         modd(raddr, 0, d);
155                 depth--;
156                 fence -= sizeof(Dentry);
157                 if(depth)
158                         cprint("depth not zero on return\n");
159         }
160
161         if(flags & Cfree) {
162                 mkfreelist(sb);
163                 sb->qidgen = maxq;
164                 settag(p, Tsuper, QPNONE);
165         }
166
167         if(sb->qidgen < maxq)
168                 cprint("qid generator low path=%ld maxq=%ld\n",
169                         sb->qidgen, maxq);
170         if(!(flags & Cfree))
171                 ckfreelist(sb);
172         if(mod) {
173                 cprint("file system was modified\n");
174                 settag(p, Tsuper, QPNONE);
175         }
176
177         if(!(flags & Cquiet)){
178                 cprint("%8ld files\n", nfiles);
179                 cprint("%8ld blocks in the file system\n", fsize-fstart);
180                 cprint("%8ld used blocks\n", nused);
181                 cprint("%8ld free blocks\n", sb->tfree);
182         }
183         if(!(flags & Cfree)){
184                 if(nfree != sb->tfree)
185                         cprint("%8ld free blocks found\n", nfree);
186                 if(nfdup)
187                         cprint("%8ld blocks duplicated in the free list\n", nfdup);
188                 if(fsize-fstart-nused-nfree)
189                         cprint("%8ld missing blocks\n", fsize-fstart-nused-nfree);
190         }
191         if(ndup)
192                 cprint("%8ld address duplications\n", ndup);
193         if(nbad)
194                 cprint("%8ld bad block addresses\n", nbad);
195         if(nqbad)
196                 cprint("%8ld bad qids\n", nqbad);
197         if(!(flags & Cquiet))
198                 cprint("%8ld maximum qid path\n", maxq);
199         missing();
200
201 out:
202         if(p)
203                 putbuf(p);
204         free(abits);
205         free(name);
206         free(qbits);
207         wunlock(&mainlock);
208 }
209
210 static
211 int
212 touch(long a)
213 {
214         Iobuf *p;
215
216         if((flags&Ctouch) && a && a < oldblock){
217                 p = getbuf(dev, a, Bread|Bmod);
218                 if(p)
219                         putbuf(p);
220                 return 1;
221         }
222         return 0;
223 }
224
225 static
226 int
227 checkdir(long a, long qpath)
228 {
229         Dentry *nd;
230         int i, ns, dmod;
231
232         ns = strlen(name);
233         dmod = touch(a);
234         for(i=0; i<DIRPERBUF; i++) {
235                 nd = maked(a, i, qpath);
236                 if(!nd)
237                         break;
238                 if(fsck(nd)) {
239                         modd(a, i, nd);
240                         dmod++;
241                 }
242                 depth--;
243                 fence -= sizeof(Dentry);
244                 name[ns] = 0;
245         }
246         name[ns] = 0;
247         return dmod;
248 }
249
250 static
251 int
252 checkindir(long a, Dentry *d, long qpath)
253 {
254         Iobuf *p;
255         int i, dmod;
256
257         dmod = touch(a);
258         p = xtag(a, Tind1, qpath);
259         if(!p)
260                 return dmod;
261         for(i=0; i<INDPERBUF; i++) {
262                 a = ((long*)p->iobuf)[i];
263                 if(!a)
264                         continue;
265                 if(amark(a)) {
266                         if(flags & Cbad) {
267                                 ((long*)p->iobuf)[i] = 0;
268                                 p->flags |= Bmod;
269                         }
270                         continue;
271                 }
272                 if(d->mode & DDIR)
273                         dmod += checkdir(a, qpath);
274                 else if(flags & Crdall)
275                         xread(a, qpath);
276         }
277         putbuf(p);
278         return dmod;
279 }
280
281 static
282 int
283 fsck(Dentry *d)
284 {
285         char *s;
286         Rune r;
287         Iobuf *p;
288         int l, i, ns, dmod;
289         long a, qpath;
290
291         depth++;
292         if(depth >= maxdepth){
293                 maxdepth = depth;
294                 if(maxdepth >= MAXDEPTH){
295                         cprint("max depth exceeded: %s\n", name);
296                         return 0;
297                 }
298         }
299         dmod = 0;
300         if(!(d->mode & DALLOC))
301                 return 0;
302         nfiles++;
303
304         ns = strlen(name);
305         i = strlen(d->name);
306         if(i >= NAMELEN){
307                 d->name[NAMELEN-1] = 0;
308                 cprint("%s->name (%s) not terminated\n", name, d->name);
309                 return 0;
310         }
311         ns += i;
312         if(ns >= sizname){
313                 cprint("%s->name (%s) name too large\n", name, d->name);
314                 return 0;
315         }
316         for (s = d->name; *s; s += l){
317                 l = chartorune(&r, s);
318                 if (r == Runeerror)
319                         for (i = 0; i < l; i++){
320                                 s[i] = '_';
321                                 cprint("%s->name (%s) bad UTF\n", name, d->name);
322                                 dmod++;
323                         }
324         }
325         strcat(name, d->name);
326
327         if(d->mode & DDIR){
328                 if(ns > 1)
329                         strcat(name, "/");
330                 if(flags & Cpdir)
331                         cprint("%s\n", name);
332         } else
333         if(flags & Cpfile)
334                 cprint("%s\n", name);
335
336         qpath = d->qid.path & ~QPDIR;
337         qmark(qpath);
338         if(qpath > maxq)
339                 maxq = qpath;
340         for(i=0; i<NDBLOCK; i++) {
341                 a = d->dblock[i];
342                 if(!a)
343                         continue;
344                 if(amark(a)) {
345                         d->dblock[i] = 0;
346                         dmod++;
347                         continue;
348                 }
349                 if(d->mode & DDIR)
350                         dmod += checkdir(a, qpath);
351                 else if(flags & Crdall)
352                         xread(a, qpath);
353         }
354         a = d->iblock;
355         if(a && amark(a)) {
356                 d->iblock = 0;
357                 dmod++;
358         }
359         else if(a)
360                 dmod += checkindir(a, d, qpath);
361
362         a = d->diblock;
363         if(a && amark(a)) {
364                 d->diblock = 0;
365                 return dmod + 1;
366         }
367         dmod += touch(a);
368         if(p = xtag(a, Tind2, qpath)){
369                 for(i=0; i<INDPERBUF; i++){
370                         a = ((long*)p->iobuf)[i];
371                         if(!a)
372                                 continue;
373                         if(amark(a)) {
374                                 if(flags & Cbad) {
375                                         ((long*)p->iobuf)[i] = 0;
376                                         p->flags |= Bmod;
377                                 }
378                                 continue;
379                         }
380                         dmod += checkindir(a, d, qpath);
381                 }
382                 putbuf(p);
383         }
384         return dmod;
385 }
386
387 static
388 void
389 ckfreelist(Superb *sb)
390 {
391         long a, lo, hi;
392         int n, i;
393         Iobuf *p;
394         Fbuf *fb;
395
396
397         strcpy(name, "free list");
398         cprint("check %s\n", name);
399         fb = &sb->fbuf;
400         a = sbaddr;
401         p = 0;
402         lo = 0;
403         hi = 0;
404         for(;;) {
405                 n = fb->nfree;
406                 if(n < 0 || n > FEPERBUF) {
407                         cprint("check: nfree bad %ld\n", a);
408                         break;
409                 }
410                 for(i=1; i<n; i++) {
411                         a = fb->free[i];
412                         if(a && !fmark(a)) {
413                                 if(!lo || lo > a)
414                                         lo = a;
415                                 if(!hi || hi < a)
416                                         hi = a;
417                         }
418                 }
419                 a = fb->free[0];
420                 if(!a)
421                         break;
422                 if(fmark(a))
423                         break;
424                 if(!lo || lo > a)
425                         lo = a;
426                 if(!hi || hi < a)
427                         hi = a;
428                 if(p)
429                         putbuf(p);
430                 p = xtag(a, Tfree, QPNONE);
431                 if(!p)
432                         break;
433                 fb = (Fbuf*)p->iobuf;
434         }
435         if(p)
436                 putbuf(p);
437         cprint("lo = %ld; hi = %ld\n", lo, hi);
438 }
439
440 /*
441  * make freelist from scratch
442  */
443 static
444 void
445 mkfreelist(Superb *sb)
446 {
447         long a;
448         int i, b;
449
450         strcpy(name, "free list");
451         memset(&sb->fbuf, 0, sizeof(sb->fbuf));
452         sb->fbuf.nfree = 1;
453         sb->tfree = 0;
454         for(a=fsize-fstart-1; a >= 0; a--) {
455                 i = a/8;
456                 if(i < 0 || i >= sizabits)
457                         continue;
458                 b = 1 << (a&7);
459                 if(abits[i] & b)
460                         continue;
461                 addfree(dev, fstart+a, sb);
462                 abits[i] |= b;
463         }
464 }
465
466 static
467 Dentry*
468 maked(long a, int s, long qpath)
469 {
470         Iobuf *p;
471         Dentry *d, *d1;
472
473         p = xtag(a, Tdir, qpath);
474         if(!p)
475                 return 0;
476         d = getdir(p, s);
477         d1 = dalloc(sizeof(Dentry));
478         memmove(d1, d, sizeof(Dentry));
479         putbuf(p);
480         return d1;
481 }
482
483 static
484 void
485 modd(long a, int s, Dentry *d1)
486 {
487         Iobuf *p;
488         Dentry *d;
489
490         if(!(flags & Cbad))
491                 return;
492         p = getbuf(dev, a, Bread);
493         d = getdir(p, s);
494         if(!d) {
495                 if(p)
496                         putbuf(p);
497                 return;
498         }
499         memmove(d, d1, sizeof(Dentry));
500         p->flags |= Bmod;
501         putbuf(p);
502 }
503
504 static
505 void
506 xread(long a, long qpath)
507 {
508         Iobuf *p;
509
510         p = xtag(a, Tfile, qpath);
511         if(p)
512                 putbuf(p);
513 }
514
515 static
516 Iobuf*
517 xtag(long a, int tag, long qpath)
518 {
519         Iobuf *p;
520
521         if(a == 0)
522                 return 0;
523         p = getbuf(dev, a, Bread);
524         if(!p) {
525                 cprint("check: \"%s\": xtag: p null\n", name);
526                 if(flags & (Cream|Ctag)) {
527                         p = getbuf(dev, a, Bmod);
528                         if(p) {
529                                 memset(p->iobuf, 0, RBUFSIZE);
530                                 settag(p, tag, qpath);
531                                 mod++;
532                                 return p;
533                         }
534                 }
535                 return 0;
536         }
537         if(checktag(p, tag, qpath)) {
538                 cprint("check: \"%s\": xtag: checktag\n", name);
539                 if(flags & Cream)
540                         memset(p->iobuf, 0, RBUFSIZE);
541                 if(flags & (Cream|Ctag)) {
542                         settag(p, tag, qpath);
543                         mod++;
544                 }
545                 return p;
546         }
547         return p;
548 }
549
550 static
551 int
552 amark(long a)
553 {
554         long i;
555         int b;
556
557         if(a < fstart || a >= fsize) {
558                 cprint("check: \"%s\": range %ld\n",
559                         name, a);
560                 nbad++;
561                 return 1;
562         }
563         a -= fstart;
564         i = a/8;
565         b = 1 << (a&7);
566         if(abits[i] & b) {
567                 if(!ronly) {
568                         if(ndup < 10)
569                                 cprint("check: \"%s\": address dup %ld\n",
570                                         name, fstart+a);
571                         else
572                         if(ndup == 10)
573                                 cprint("...");
574                 }
575                 ndup++;
576                 return 0;       /* really?? */
577         }
578         abits[i] |= b;
579         nused++;
580         return 0;
581 }
582
583 static
584 int
585 fmark(long a)
586 {
587         long i;
588         int b;
589
590         if(a < fstart || a >= fsize) {
591                 cprint("check: \"%s\": range %ld\n",
592                         name, a);
593                 nbad++;
594                 return 1;
595         }
596         a -= fstart;
597         i = a/8;
598         b = 1 << (a&7);
599         if(abits[i] & b) {
600                 cprint("check: \"%s\": address dup %ld\n",
601                         name, fstart+a);
602                 nfdup++;
603                 return 1;
604         }
605         abits[i] |= b;
606         nfree++;
607         return 0;
608 }
609
610 static
611 void
612 missing(void)
613 {
614         long a, i;
615         int b, n;
616
617         n = 0;
618         for(a=fsize-fstart-1; a>=0; a--) {
619                 i = a/8;
620                 b = 1 << (a&7);
621                 if(!(abits[i] & b)) {
622                         cprint("missing: %ld\n", fstart+a);
623                         n++;
624                 }
625                 if(n > 10) {
626                         cprint(" ...\n");
627                         break;
628                 }
629         }
630 }
631
632 static
633 void
634 qmark(long qpath)
635 {
636         int i, b;
637
638         i = qpath/8;
639         b = 1 << (qpath&7);
640         if(i < 0 || i >= sizqbits) {
641                 nqbad++;
642                 if(nqbad < 20)
643                         cprint("check: \"%s\": qid out of range %lux\n",
644                                 name, qpath);
645                 return;
646         }
647         if((qbits[i] & b) && !ronly) {
648                 nqbad++;
649                 if(nqbad < 20)
650                         cprint("check: \"%s\": qid dup %lux\n",
651                                 name, qpath);
652         }
653         qbits[i] |= b;
654 }