2 * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
3 * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
6 * This code is derived from software contributed to Berkeley by
7 * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
38 * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.4 2000/12/12 19:30:55 tomsoft Exp $
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";
50 static const char rcsid[] =
54 /* ********************************************************** INCLUDES ***** */
55 #include <sys/param.h>
56 #include <sys/disklabel.h>
57 #include <sys/mount.h>
60 #include <ufs/ufs/ufsmount.h>
61 #include <ufs/ufs/dinode.h>
62 #include <ufs/ffs/fs.h>
77 /* *********************************************************** GLOBALS ***** */
79 int _dbg_lvl_ = (DL_INFO); /* DL_TRC */
84 #define sblock disk.d_fs
92 #define osblock fsun.fs
94 static char i1blk[MAXBSIZE];
95 static char i2blk[MAXBSIZE];
96 static char i3blk[MAXBSIZE];
98 static struct csum *fscs;
100 /* ******************************************************** PROTOTYPES ***** */
101 static void usage(void);
102 static void dump_whole_ufs1_inode(ino_t, int);
103 static void dump_whole_ufs2_inode(ino_t, int);
105 #define DUMP_WHOLE_INODE(A,B) \
107 ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) )
109 /* ************************************************************** main ***** */
111 * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find
112 * errors is the file system much easier. You can run ffsinfo before and after
113 * an fsck(8), and compare the two ascii dumps easy with diff, and you see
114 * directly where the problem is. You can control how much detail you want to
115 * see with some command line arguments. You can also easy check the status
116 * of a file system, like is there is enough space for growing a file system,
117 * or how many active snapshots do we have. It provides much more detailed
118 * information then dumpfs. Snapshots, as they are very new, are not really
119 * supported. They are just mentioned currently, but it is planned to run
120 * also over active snapshots, to even get that output.
123 main(int argc, char **argv)
126 char *device, *special;
130 struct csum *dbg_csp;
134 int cfg_cg, cfg_in, cfg_lv;
135 int cg_start, cg_stop;
146 while ((ch=getopt(argc, argv, "g:i:l:o:")) != -1) {
149 cfg_cg=strtol(optarg, NULL, 0);
150 if(errno == EINVAL||errno == ERANGE)
151 err(1, "%s", optarg);
157 cfg_in=strtol(optarg, NULL, 0);
158 if(errno == EINVAL||errno == ERANGE)
159 err(1, "%s", optarg);
165 cfg_lv=strtol(optarg, NULL, 0);
166 if(errno == EINVAL||errno == ERANGE)
167 err(1, "%s", optarg);
168 if(cfg_lv < 0x1||cfg_lv > 0x3ff) {
174 out_file=strdup(optarg);
175 if(out_file == NULL) {
176 errx(1, "strdup failed");
192 if (out_file == NULL)
193 errx(1, "out_file not specified");
196 * Now we try to guess the (raw)device name.
198 if (0 == strrchr(device, '/') && (stat(device, &st) == -1)) {
200 * No path prefix was given, so try in that order:
206 * FreeBSD now doesn't distinguish between raw and block
207 * devices any longer, but it should still work this way.
209 len=strlen(device)+strlen(_PATH_DEV)+2+strlen("vinum/");
210 special=(char *)malloc(len);
211 if(special == NULL) {
212 errx(1, "malloc failed");
214 snprintf(special, len, "%sr%s", _PATH_DEV, device);
215 if (stat(special, &st) == -1) {
216 snprintf(special, len, "%s%s", _PATH_DEV, device);
217 if (stat(special, &st) == -1) {
218 snprintf(special, len, "%svinum/r%s",
220 if (stat(special, &st) == -1) {
222 * For now this is the 'last resort'.
224 snprintf(special, len, "%svinum/%s",
232 if (ufs_disk_fillout(&disk, device) == -1)
233 err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error);
235 DBG_OPEN(out_file); /* already here we need a superblock */
243 * Determine here what cylinder groups to dump.
247 cg_stop=sblock.fs_ncg;
248 } else if (cfg_cg==-1) {
249 cg_start=sblock.fs_ncg-1;
250 cg_stop=sblock.fs_ncg;
251 } else if (cfg_cg<sblock.fs_ncg) {
255 cg_start=sblock.fs_ncg;
256 cg_stop=sblock.fs_ncg;
259 if (cfg_lv & 0x004) {
260 fscs = (struct csum *)calloc((size_t)1,
261 (size_t)sblock.fs_cssize);
263 errx(1, "calloc failed");
267 * Get the cylinder summary into the memory ...
269 for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
271 fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)),
272 (void *)(((char *)fscs)+i),
273 (size_t)(sblock.fs_cssize-i < sblock.fs_bsize
274 ? sblock.fs_cssize - i
275 : sblock.fs_bsize)) == -1) {
276 err(1, "bread: %s", disk.d_error);
284 for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) {
285 snprintf(dbg_line, sizeof(dbg_line),
286 "%d. csum in fscs", dbg_csc);
287 DBG_DUMP_CSUM(&sblock,
294 * For each requested cylinder group ...
296 for(cylno=cg_start; cylno<cg_stop; cylno++) {
297 snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno);
300 * ... dump the superblock copies ...
302 if (bread(&disk, fsbtodb(&sblock, cgsblock(&sblock, cylno)),
303 (void *)&osblock, SBLOCKSIZE) == -1) {
304 err(1, "bread: %s", disk.d_error);
306 DBG_DUMP_FS(&osblock,
310 * ... read the cylinder group and dump whatever was requested.
312 if (bread(&disk, fsbtodb(&sblock, cgtod(&sblock, cylno)),
313 (void *)&acg, (size_t)sblock.fs_cgsize) == -1) {
314 err(1, "bread: %s", disk.d_error);
322 DBG_DUMP_INMAP(&sblock,
327 DBG_DUMP_FRMAP(&sblock,
332 DBG_DUMP_CLMAP(&sblock,
335 DBG_DUMP_CLSUM(&sblock,
341 * See the comment in sbin/growfs/debug.c for why this
342 * is currently disabled, and what needs to be done to
345 if(disk.d_ufs == 1 && cfg_lv & 0x080) {
346 DBG_DUMP_SPTBL(&sblock,
353 * Dump the requested inode(s).
356 DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv);
358 for(in=cg_start*sblock.fs_ipg; in<(ino_t)cg_stop*sblock.fs_ipg;
360 DUMP_WHOLE_INODE(in, cfg_lv);
370 /* ********************************************** dump_whole_ufs1_inode ***** */
372 * Here we dump a list of all blocks allocated by this inode. We follow
373 * all indirect blocks.
376 dump_whole_ufs1_inode(ino_t inode, int level)
378 DBG_FUNC("dump_whole_ufs1_inode")
379 struct ufs1_dinode *ino;
381 unsigned int ind2ctr, ind3ctr;
382 ufs1_daddr_t *ind2ptr, *ind3ptr;
388 * Read the inode from disk/cache.
390 if (getino(&disk, (void **)&ino, inode, &mode) == -1)
391 err(1, "getino: %s", disk.d_error);
393 if(ino->di_nlink==0) {
395 return; /* inode not in use */
399 * Dump the main inode structure.
401 snprintf(comment, sizeof(comment), "Inode 0x%08x", inode);
403 DBG_DUMP_INO(&sblock,
408 if (!(level & 0x200)) {
414 * Ok, now prepare for dumping all direct and indirect pointers.
416 rb=howmany(ino->di_size, sblock.fs_bsize)-NDADDR;
419 * Dump single indirect block.
421 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[0]), (void *)&i1blk,
422 (size_t)sblock.fs_bsize) == -1) {
423 err(1, "bread: %s", disk.d_error);
425 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 0",
427 DBG_DUMP_IBLK(&sblock,
431 rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
435 * Dump double indirect blocks.
437 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[1]), (void *)&i2blk,
438 (size_t)sblock.fs_bsize) == -1) {
439 err(1, "bread: %s", disk.d_error);
441 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 1",
443 DBG_DUMP_IBLK(&sblock,
446 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
447 for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
448 sizeof(ufs1_daddr_t))) && (rb>0)); ind2ctr++) {
449 ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr];
451 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
452 (size_t)sblock.fs_bsize) == -1) {
453 err(1, "bread: %s", disk.d_error);
455 snprintf(comment, sizeof(comment),
456 "Inode 0x%08x: indirect 1->%d", inode, ind2ctr);
457 DBG_DUMP_IBLK(&sblock,
461 rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
466 * Dump triple indirect blocks.
468 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[2]), (void *)&i3blk,
469 (size_t)sblock.fs_bsize) == -1) {
470 err(1, "bread: %s", disk.d_error);
472 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 2",
474 #define SQUARE(a) ((a)*(a))
475 DBG_DUMP_IBLK(&sblock,
479 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))));
481 for(ind3ctr=0; ((ind3ctr<howmany(sblock.fs_bsize,
482 sizeof(ufs1_daddr_t)))&&(rb>0)); ind3ctr++) {
483 ind3ptr=&((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr];
485 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk,
486 (size_t)sblock.fs_bsize) == -1) {
487 err(1, "bread: %s", disk.d_error);
489 snprintf(comment, sizeof(comment),
490 "Inode 0x%08x: indirect 2->%d", inode, ind3ctr);
491 DBG_DUMP_IBLK(&sblock,
495 howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
496 for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
497 sizeof(ufs1_daddr_t)))&&(rb>0)); ind2ctr++) {
498 ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk)
500 if (bread(&disk, fsbtodb(&sblock, *ind2ptr),
501 (void *)&i1blk, (size_t)sblock.fs_bsize)
503 err(1, "bread: %s", disk.d_error);
505 snprintf(comment, sizeof(comment),
506 "Inode 0x%08x: indirect 2->%d->%d", inode,
508 DBG_DUMP_IBLK(&sblock,
512 rb-=howmany(sblock.fs_bsize,
513 sizeof(ufs1_daddr_t));
522 /* ********************************************** dump_whole_ufs2_inode ***** */
524 * Here we dump a list of all blocks allocated by this inode. We follow
525 * all indirect blocks.
528 dump_whole_ufs2_inode(ino_t inode, int level)
530 DBG_FUNC("dump_whole_ufs2_inode")
531 struct ufs2_dinode *ino;
533 unsigned int ind2ctr, ind3ctr;
534 ufs2_daddr_t *ind2ptr, *ind3ptr;
540 * Read the inode from disk/cache.
542 if (getino(&disk, (void **)&ino, inode, &mode) == -1)
543 err(1, "getino: %s", disk.d_error);
545 if (ino->di_nlink == 0) {
547 return; /* inode not in use */
551 * Dump the main inode structure.
553 snprintf(comment, sizeof(comment), "Inode 0x%08x", inode);
555 DBG_DUMP_INO(&sblock, comment, ino);
558 if (!(level & 0x200)) {
564 * Ok, now prepare for dumping all direct and indirect pointers.
566 rb = howmany(ino->di_size, sblock.fs_bsize) - NDADDR;
569 * Dump single indirect block.
571 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[0]), (void *)&i1blk,
572 (size_t)sblock.fs_bsize) == -1) {
573 err(1, "bread: %s", disk.d_error);
575 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 0", inode);
576 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
577 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
581 * Dump double indirect blocks.
583 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[1]), (void *)&i2blk,
584 (size_t)sblock.fs_bsize) == -1) {
585 err(1, "bread: %s", disk.d_error);
587 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 1", inode);
588 DBG_DUMP_IBLK(&sblock,
591 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
592 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
593 sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) {
594 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr];
596 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
597 (size_t)sblock.fs_bsize) == -1) {
598 err(1, "bread: %s", disk.d_error);
600 snprintf(comment, sizeof(comment),
601 "Inode 0x%08x: indirect 1->%d", inode, ind2ctr);
602 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
603 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
608 * Dump triple indirect blocks.
610 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[2]), (void *)&i3blk,
611 (size_t)sblock.fs_bsize) == -1) {
612 err(1, "bread: %s", disk.d_error);
614 snprintf(comment, sizeof(comment), "Inode 0x%08x: indirect 2", inode);
615 #define SQUARE(a) ((a)*(a))
616 DBG_DUMP_IBLK(&sblock,
620 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))));
622 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize,
623 sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) {
624 ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr];
626 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk,
627 (size_t)sblock.fs_bsize) == -1) {
628 err(1, "bread: %s", disk.d_error);
630 snprintf(comment, sizeof(comment),
631 "Inode 0x%08x: indirect 2->%d", inode, ind3ctr);
632 DBG_DUMP_IBLK(&sblock,
636 howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
637 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
638 sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) {
639 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr];
640 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
641 (size_t)sblock.fs_bsize) == -1) {
642 err(1, "bread: %s", disk.d_error);
644 snprintf(comment, sizeof(comment),
645 "Inode 0x%08x: indirect 2->%d->%d", inode,
647 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
648 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
657 /* ************************************************************* usage ***** */
659 * Dump a line of usage.
669 "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] "
671 " special | file\n");