]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ffsinfo/ffsinfo.c
Do not build the majority of the games. Remaining are the
[FreeBSD/FreeBSD.git] / sbin / ffsinfo / ffsinfo.c
1 /*
2  * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
3  * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
4  * All rights reserved.
5  * 
6  * This code is derived from software contributed to Berkeley by
7  * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
8  * 
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgment:
19  *      This product includes software developed by the University of
20  *      California, Berkeley and its contributors, as well as Christoph
21  *      Herrmann and Thomas-Henning von Kamptz.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  * 
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.4 2000/12/12 19:30:55 tomsoft Exp $
39  *
40  */
41
42 #ifndef lint
43 static const char copyright[] =
44 "@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\
45 Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\
46 All rights reserved.\n";
47 #endif /* not lint */
48
49 #ifndef lint
50 static const char rcsid[] =
51   "$FreeBSD$";
52 #endif /* not lint */
53
54 /* ********************************************************** INCLUDES ***** */
55 #include <sys/param.h>
56 #include <sys/disklabel.h>
57 #include <sys/stat.h>
58
59 #include <stdio.h>
60 #include <paths.h>
61 #include <ctype.h>
62 #include <err.h>
63 #include <fcntl.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67
68 #include "debug.h"
69
70 /* *********************************************************** GLOBALS ***** */
71 #ifdef FS_DEBUG
72 int     _dbg_lvl_ = (DL_INFO); /* DL_TRC */
73 #endif /* FS_DEBUG */
74
75 static union {
76         struct fs       fs;
77         char    pad[SBSIZE];
78 } fsun1, fsun2;
79 #define sblock  fsun1.fs
80 #define osblock fsun2.fs
81
82 static union {
83         struct cg       cg;
84         char    pad[MAXBSIZE];
85 } cgun1;
86 #define acg     cgun1.cg
87
88 static char     ablk[MAXBSIZE];
89 static char     i1blk[MAXBSIZE];
90 static char     i2blk[MAXBSIZE];
91 static char     i3blk[MAXBSIZE];
92
93 static struct csum      *fscs;
94
95 /* ******************************************************** PROTOTYPES ***** */
96 static void     rdfs(daddr_t, size_t, void *, int);
97 static void     usage(void);
98 static struct disklabel *get_disklabel(int);
99 static struct dinode    *ginode(ino_t, int);
100 static void     dump_whole_inode(ino_t, int, int);
101
102 /* ************************************************************** rdfs ***** */
103 /*
104  * Here we read some block(s) from disk.
105  */
106 void
107 rdfs(daddr_t bno, size_t size, void *bf, int fsi)
108 {
109         DBG_FUNC("rdfs")
110         ssize_t n;
111
112         DBG_ENTER;
113
114         if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) {
115                 err(33, "rdfs: seek error: %ld", (long)bno);
116         }
117         n = read(fsi, bf, size);
118         if (n != (ssize_t)size) {
119                 err(34, "rdfs: read error: %ld", (long)bno);
120         }
121
122         DBG_LEAVE;
123         return;
124 }
125
126 /* ************************************************************** main ***** */
127 /*
128  * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find
129  * errors is the file system much easier. You can run ffsinfo before and  after
130  * an  fsck(8),  and compare the two ascii dumps easy with diff, and  you  see
131  * directly where the problem is. You can control how much detail you want  to
132  * see  with some command line arguments. You can also easy check  the  status
133  * of  a file system, like is there is enough space for growing  a  file system,
134  * or  how  many active snapshots do we have. It provides much  more  detailed
135  * information  then dumpfs. Snapshots, as they are very new, are  not  really
136  * supported.  They  are just mentioned currently, but it is  planned  to  run
137  * also over active snapshots, to even get that output.
138  */
139 int
140 main(int argc, char **argv)
141 {
142         DBG_FUNC("main")
143         char    *device, *special, *cp;
144         char    ch;
145         size_t  len;
146         struct stat     st;
147         struct disklabel        *lp;
148         struct partition        *pp;
149         int     fsi;
150         struct csum     *dbg_csp;
151         int     dbg_csc;
152         char    dbg_line[80];
153         int     cylno,i;
154         int     cfg_cg, cfg_in, cfg_lv;
155         int     cg_start, cg_stop;
156         ino_t   in;
157         char    *out_file;
158         int     Lflag=0;
159
160         DBG_ENTER;
161
162         cfg_lv=0xff;
163         cfg_in=-2;
164         cfg_cg=-2;
165         out_file=strdup("/var/tmp/ffsinfo");
166         if(out_file == NULL) {
167                 errx(1, "strdup failed");
168         }
169
170         while ((ch=getopt(argc, argv, "Lg:i:l:o:")) != -1) {
171                 switch(ch) {
172                 case 'L':
173                         Lflag=1;
174                         break;
175                 case 'g':
176                         cfg_cg=atol(optarg);
177                         if(cfg_cg < -1) {
178                                 usage();
179                         }
180                         break;
181                 case 'i':
182                         cfg_in=atol(optarg);
183                         if(cfg_in < 0) {
184                                 usage();
185                         }
186                         break; 
187                 case 'l':
188                         cfg_lv=atol(optarg);
189                         if(cfg_lv < 0x1||cfg_lv > 0x3ff) {
190                                 usage();
191                         }
192                         break;
193                 case 'o':
194                         free(out_file);
195                         out_file=strdup(optarg);
196                         if(out_file == NULL) {
197                                 errx(1, "strdup failed");
198                         }
199                         break;
200                 case '?':
201                         /* FALLTHROUGH */
202                 default:
203                         usage();
204                 }
205         }
206         argc -= optind;
207         argv += optind;
208
209         if(argc != 1) {
210                 usage();
211         }
212         device=*argv;
213         
214         /*
215          * Now we try to guess the (raw)device name.
216          */
217         if (0 == strrchr(device, '/') && (stat(device, &st) == -1)) {
218                 /*
219                  * No path prefix was given, so try in that order:
220                  *     /dev/r%s
221                  *     /dev/%s
222                  *     /dev/vinum/r%s
223                  *     /dev/vinum/%s.
224                  * 
225                  * FreeBSD now doesn't distinguish between raw and  block
226                  * devices any longer, but it should still work this way.
227                  */
228                 len=strlen(device)+strlen(_PATH_DEV)+2+strlen("vinum/");
229                 special=(char *)malloc(len);
230                 if(special == NULL) {
231                         errx(1, "malloc failed");
232                 }
233                 snprintf(special, len, "%sr%s", _PATH_DEV, device);
234                 if (stat(special, &st) == -1) {
235                         snprintf(special, len, "%s%s", _PATH_DEV, device);
236                         if (stat(special, &st) == -1) {
237                                 snprintf(special, len, "%svinum/r%s",
238                                     _PATH_DEV, device);
239                                 if (stat(special, &st) == -1) {
240                                         /*
241                                          * For now this is the 'last resort'.
242                                          */
243                                         snprintf(special, len, "%svinum/%s",
244                                             _PATH_DEV, device);
245                                 }
246                         }
247                 }
248                 device = special;
249         }
250
251         /*
252          * Open our device for reading.
253          */
254         fsi = open(device, O_RDONLY);
255         if (fsi < 0) {
256                 err(1, "%s", device);
257         }
258
259         stat(device, &st);
260         
261         if(S_ISREG(st.st_mode)) { /* label check not supported for files */
262                 Lflag=1;
263         }
264
265         if(!Lflag) {
266                 /*
267                  * Try  to read a label and gess the slice if not  specified.
268                  * This code should guess the right thing and avaid to bother
269                  * the user user with the task of specifying the option -v on
270                  * vinum volumes.
271                  */
272                 cp=device+strlen(device)-1;
273                 lp = get_disklabel(fsi);
274                 if(lp->d_type == DTYPE_VINUM) {
275                         pp = &lp->d_partitions[0];
276                 } else if (isdigit(*cp)) {
277                         pp = &lp->d_partitions[2];
278                 } else if (*cp>='a' && *cp<='h') {
279                         pp = &lp->d_partitions[*cp - 'a'];
280                 } else {
281                         errx(1, "unknown device");
282                 }
283         
284                 /*
285                  * Check if that partition looks suited for dumping.
286                  */
287                 if (pp->p_size < 1) {
288                         errx(1, "partition is unavailable");
289                 }
290                 if (pp->p_fstype != FS_BSDFFS) {
291                         errx(1, "partition not 4.2BSD");
292                 }
293         }
294
295         /*
296          * Read the current superblock.
297          */
298         rdfs((daddr_t)(SBOFF/DEV_BSIZE), (size_t)SBSIZE, (void *)&sblock, fsi);
299         if (sblock.fs_magic != FS_MAGIC) {
300                 errx(1, "superblock not recognized");
301         }
302
303         DBG_OPEN(out_file); /* already here we need a superblock */
304
305         if(cfg_lv & 0x001) {
306                 DBG_DUMP_FS(&sblock,
307                     "primary sblock");
308         }
309
310         /*
311          * Determine here what cylinder groups to dump.
312          */
313         if(cfg_cg==-2) {
314                 cg_start=0;
315                 cg_stop=sblock.fs_ncg;
316         } else if (cfg_cg==-1) {
317                 cg_start=sblock.fs_ncg-1;
318                 cg_stop=sblock.fs_ncg;
319         } else if (cfg_cg<sblock.fs_ncg) {
320                 cg_start=cfg_cg;
321                 cg_stop=cfg_cg+1;
322         } else {
323                 cg_start=sblock.fs_ncg;
324                 cg_stop=sblock.fs_ncg;
325         }
326
327         if (cfg_lv & 0x004) {
328                 fscs = (struct csum *)calloc((size_t)1,
329                     (size_t)sblock.fs_cssize);
330                 if(fscs == NULL) {
331                         errx(1, "calloc failed");
332                 }
333
334                 /*
335                  * Get the cylinder summary into the memory ...
336                  */
337                 for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
338                         rdfs(fsbtodb(&sblock, sblock.fs_csaddr +
339                             numfrags(&sblock, i)), (size_t)(sblock.fs_cssize-i<
340                             sblock.fs_bsize ? sblock.fs_cssize - i :
341                             sblock.fs_bsize), (void *)(((char *)fscs)+i), fsi);
342                 }
343
344                 dbg_csp=fscs;
345                 /*
346                  * ... and dump it.
347                  */
348                 for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) {
349                         snprintf(dbg_line, sizeof(dbg_line),
350                             "%d. csum in fscs", dbg_csc);
351                         DBG_DUMP_CSUM(&sblock,
352                             dbg_line,
353                             dbg_csp++);
354                 }
355         }
356
357         /*
358          * For each requested cylinder group ...
359          */
360         for(cylno=cg_start; cylno<cg_stop; cylno++) {
361                 snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno);
362                 if(cfg_lv & 0x002) {
363                         /*
364                          * ... dump the superblock copies ...
365                          */
366                         rdfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
367                             (size_t)SBSIZE, (void *)&osblock, fsi);
368                         DBG_DUMP_FS(&osblock,
369                             dbg_line);
370                 }
371                 /*
372                  * ... read the cylinder group and dump whatever was requested.
373                  */
374                 rdfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
375                     (size_t)sblock.fs_cgsize, (void *)&acg, fsi);
376                 if(cfg_lv & 0x008) {
377                         DBG_DUMP_CG(&sblock,
378                             dbg_line,
379                             &acg);
380                 }
381                 if(cfg_lv & 0x010) {
382                         DBG_DUMP_INMAP(&sblock,
383                             dbg_line,
384                             &acg);
385                 }
386                 if(cfg_lv & 0x020) {
387                         DBG_DUMP_FRMAP(&sblock,
388                             dbg_line,
389                             &acg);
390                 }
391                 if(cfg_lv & 0x040) {
392                         DBG_DUMP_CLMAP(&sblock,
393                             dbg_line,
394                             &acg);
395                         DBG_DUMP_CLSUM(&sblock,
396                             dbg_line,
397                             &acg);
398                 }
399                 if(cfg_lv & 0x080) {
400                         DBG_DUMP_SPTBL(&sblock,
401                             dbg_line,
402                             &acg);
403                 }
404         }
405         /*
406          * Dump the requested inode(s).
407          */
408         if(cfg_in != -2) {
409                 dump_whole_inode((ino_t)cfg_in, fsi, cfg_lv);
410         } else {
411                 for(in=cg_start*sblock.fs_ipg; in<(ino_t)cg_stop*sblock.fs_ipg;
412                     in++) {
413                         dump_whole_inode(in, fsi, cfg_lv);
414                 }
415         }
416
417         DBG_CLOSE;
418
419         close(fsi);
420
421         DBG_LEAVE;
422         return 0;
423 }
424
425 /* ************************************************** dump_whole_inode ***** */
426 /*
427  * Here we dump a list of all blocks allocated by this inode. We follow
428  * all indirect blocks.
429  */
430 void
431 dump_whole_inode(ino_t inode, int fsi, int level)
432 {
433         DBG_FUNC("dump_whole_inode")
434         struct dinode   *ino;
435         int     rb;
436         unsigned int    ind2ctr, ind3ctr;
437         ufs_daddr_t     *ind2ptr, *ind3ptr;
438         char    comment[80];
439         
440         DBG_ENTER;
441
442         /*
443          * Read the inode from disk/cache.
444          */
445         ino=ginode(inode, fsi);
446
447         if(ino->di_nlink==0) {
448                 DBG_LEAVE;
449                 return; /* inode not in use */
450         }
451
452         /*
453          * Dump the main inode structure.
454          */
455         snprintf(comment, sizeof(comment), "Inode 0x%08x", inode);
456         if (level & 0x100) {
457                 DBG_DUMP_INO(&sblock,
458                     comment,
459                     ino);
460         }
461
462         if (!(level & 0x200)) {
463                 DBG_LEAVE;
464                 return;
465         }
466
467         /*
468          * Ok, now prepare for dumping all direct and indirect pointers.
469          */
470         rb=howmany(ino->di_size, sblock.fs_bsize)-NDADDR;
471         if(rb>0) {
472                 /*
473                  * Dump single indirect block.
474                  */
475                 rdfs(fsbtodb(&sblock, ino->di_ib[0]), (size_t)sblock.fs_bsize,
476                     (void *)&i1blk, fsi);
477                 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 0",
478                     inode);
479                 DBG_DUMP_IBLK(&sblock,
480                     comment,
481                     i1blk,
482                     (size_t)rb);
483                 rb-=howmany(sblock.fs_bsize, sizeof(ufs_daddr_t));
484         }
485         if(rb>0) {
486                 /*
487                  * Dump double indirect blocks.
488                  */
489                 rdfs(fsbtodb(&sblock, ino->di_ib[1]), (size_t)sblock.fs_bsize,
490                     (void *)&i2blk, fsi);
491                 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 1",
492                     inode);
493                 DBG_DUMP_IBLK(&sblock,
494                     comment,
495                     i2blk,
496                     howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs_daddr_t))));
497                 for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
498                     sizeof(ufs_daddr_t)))&&(rb>0)); ind2ctr++) {
499                         ind2ptr=&((ufs_daddr_t *)(void *)&i2blk)[ind2ctr];
500
501                         rdfs(fsbtodb(&sblock, *ind2ptr),
502                             (size_t)sblock.fs_bsize, (void *)&i1blk, fsi);
503                         snprintf(comment, sizeof(comment),
504                             "Inode 0x%08x: indirect 1->%d", inode, ind2ctr);
505                         DBG_DUMP_IBLK(&sblock,
506                             comment,
507                             i1blk,
508                             (size_t)rb);
509                         rb-=howmany(sblock.fs_bsize, sizeof(ufs_daddr_t));
510                 }
511         }
512         if(rb>0) {
513                 /*
514                  * Dump triple indirect blocks.
515                  */
516                 rdfs(fsbtodb(&sblock, ino->di_ib[2]), (size_t)sblock.fs_bsize,
517                     (void *)&i3blk, fsi);
518                 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 2",
519                     inode);
520 #define SQUARE(a) ((a)*(a))
521                 DBG_DUMP_IBLK(&sblock,
522                     comment,
523                     i3blk,
524                     howmany(rb,
525                       SQUARE(howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)))));
526 #undef SQUARE
527                 for(ind3ctr=0; ((ind3ctr < howmany(sblock.fs_bsize,
528                     sizeof(ufs_daddr_t)))&&(rb>0)); ind3ctr ++) {
529                         ind3ptr=&((ufs_daddr_t *)(void *)&i3blk)[ind3ctr];
530
531                         rdfs(fsbtodb(&sblock, *ind3ptr),
532                             (size_t)sblock.fs_bsize, (void *)&i2blk, fsi);
533                         snprintf(comment, sizeof(comment),
534                             "Inode 0x%08x: indirect 2->%d", inode, ind3ctr);
535                         DBG_DUMP_IBLK(&sblock,
536                             comment,
537                             i2blk,
538                             howmany(rb,
539                               howmany(sblock.fs_bsize, sizeof(ufs_daddr_t))));
540                         for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
541                             sizeof(ufs_daddr_t)))&&(rb>0)); ind2ctr ++) {
542                                 ind2ptr=&((ufs_daddr_t *)(void *)&i2blk)
543                                     [ind2ctr];
544                                 rdfs(fsbtodb(&sblock, *ind2ptr),
545                                     (size_t)sblock.fs_bsize, (void *)&i1blk,
546                                     fsi);
547                                 snprintf(comment, sizeof(comment),
548                                     "Inode 0x%08x: indirect 2->%d->%d", inode,
549                                     ind3ctr, ind3ctr);
550                                 DBG_DUMP_IBLK(&sblock,
551                                     comment,
552                                     i1blk,
553                                     (size_t)rb);
554                                 rb-=howmany(sblock.fs_bsize,
555                                     sizeof(ufs_daddr_t));
556                         }
557                 }
558         }
559
560         DBG_LEAVE;
561         return;
562 }
563
564 /* ***************************************************** get_disklabel ***** */
565 /*
566  * Read the disklabel from disk.
567  */
568 struct disklabel *
569 get_disklabel(int fd)
570 {
571         DBG_FUNC("get_disklabel")
572         static struct disklabel *lab;
573
574         DBG_ENTER;
575
576         lab=(struct disklabel *)malloc(sizeof(struct disklabel));
577         if (!lab) {
578                 errx(1, "malloc failed");
579         }
580         if (ioctl(fd, DIOCGDINFO, (char *)lab) < 0) {
581                 errx(1, "DIOCGDINFO failed");
582                 exit(-1);
583         }
584
585         DBG_LEAVE;
586         return (lab);
587 }
588
589
590 /* ************************************************************* usage ***** */
591 /*
592  * Dump a line of usage.
593  */
594 void
595 usage(void)
596 {
597         DBG_FUNC("usage")       
598
599         DBG_ENTER;
600
601         fprintf(stderr,
602             "usage: ffsinfo [-L] [-g cylgrp] [-i inode] [-l level] "
603             "[-o outfile]\n"
604             "               special | file\n");
605
606         DBG_LEAVE;
607         exit(1);
608 }
609
610 /* ************************************************************ ginode ***** */
611 /*
612  * This function provides access to an individual inode. We find out in which
613  * block  the  requested inode is located, read it from disk if  needed,  and
614  * return  the pointer into that block. We maintain a cache of one  block  to
615  * not  read the same block again and again if we iterate linearly  over  all
616  * inodes.
617  */
618 struct dinode *
619 ginode(ino_t inumber, int fsi)
620 {
621         DBG_FUNC("ginode")
622         ufs_daddr_t     iblk;
623         static ino_t    startinum=0;    /* first inode in cached block */
624         struct dinode   *pi;
625
626         DBG_ENTER;
627
628         pi=(struct dinode *)(void *)ablk;
629         if (startinum == 0 || inumber < startinum ||
630             inumber >= startinum + INOPB(&sblock)) {
631                 /*
632                  * The block needed is not cached, so we have to read it from
633                  * disk now.
634                  */
635                 iblk = ino_to_fsba(&sblock, inumber);
636                 rdfs(fsbtodb(&sblock, iblk), (size_t)sblock.fs_bsize,
637                     (void *)&ablk, fsi);
638                 startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
639         }
640
641         DBG_LEAVE;
642         return (&(pi[inumber % INOPB(&sblock)]));
643 }
644