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>
78 /* *********************************************************** GLOBALS ***** */
80 int _dbg_lvl_ = (DL_INFO); /* DL_TRC */
83 static struct uufsd disk;
85 #define sblock disk.d_fs
93 #define osblock fsun.fs
95 static char i1blk[MAXBSIZE];
96 static char i2blk[MAXBSIZE];
97 static char i3blk[MAXBSIZE];
99 static struct csum *fscs;
101 /* ******************************************************** PROTOTYPES ***** */
102 static void usage(void);
103 static void dump_whole_ufs1_inode(ino_t, int);
104 static void dump_whole_ufs2_inode(ino_t, int);
106 #define DUMP_WHOLE_INODE(A,B) \
108 ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) )
110 /* ************************************************************** main ***** */
112 * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find
113 * errors is the file system much easier. You can run ffsinfo before and after
114 * an fsck(8), and compare the two ascii dumps easy with diff, and you see
115 * directly where the problem is. You can control how much detail you want to
116 * see with some command line arguments. You can also easy check the status
117 * of a file system, like is there is enough space for growing a file system,
118 * or how many active snapshots do we have. It provides much more detailed
119 * information then dumpfs. Snapshots, as they are very new, are not really
120 * supported. They are just mentioned currently, but it is planned to run
121 * also over active snapshots, to even get that output.
124 main(int argc, char **argv)
127 char *device, *special;
131 struct csum *dbg_csp;
135 int cfg_cg, cfg_in, cfg_lv;
136 int cg_start, cg_stop;
145 out_file = strdup("-");
147 while ((ch = getopt(argc, argv, "g:i:l:o:")) != -1) {
150 cfg_cg = strtol(optarg, NULL, 0);
151 if (errno == EINVAL || errno == ERANGE)
152 err(1, "%s", optarg);
157 cfg_in = strtol(optarg, NULL, 0);
158 if (errno == EINVAL || errno == ERANGE)
159 err(1, "%s", optarg);
164 cfg_lv = strtol(optarg, NULL, 0);
165 if (errno == EINVAL||errno == ERANGE)
166 err(1, "%s", optarg);
167 if (cfg_lv < 0x1 || cfg_lv > 0x3ff)
172 out_file = strdup(optarg);
173 if (out_file == NULL)
174 errx(1, "strdup failed");
190 * Now we try to guess the (raw)device name.
192 if (0 == strrchr(device, '/') && stat(device, &st) == -1) {
194 * No path prefix was given, so try in this order:
200 * FreeBSD now doesn't distinguish between raw and block
201 * devices any longer, but it should still work this way.
203 len = strlen(device) + strlen(_PATH_DEV) + 2 + strlen("vinum/");
204 special = (char *)malloc(len);
206 errx(1, "malloc failed");
207 snprintf(special, len, "%sr%s", _PATH_DEV, device);
208 if (stat(special, &st) == -1) {
209 snprintf(special, len, "%s%s", _PATH_DEV, device);
210 if (stat(special, &st) == -1) {
211 snprintf(special, len, "%svinum/r%s",
213 if (stat(special, &st) == -1)
214 /* For now this is the 'last resort' */
215 snprintf(special, len, "%svinum/%s",
222 if (ufs_disk_fillout(&disk, device) == -1)
223 err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error);
225 DBG_OPEN(out_file); /* already here we need a superblock */
228 DBG_DUMP_FS(&sblock, "primary sblock");
230 /* Determine here what cylinder groups to dump */
233 cg_stop = sblock.fs_ncg;
234 } else if (cfg_cg == -1) {
235 cg_start = sblock.fs_ncg - 1;
236 cg_stop = sblock.fs_ncg;
237 } else if (cfg_cg < sblock.fs_ncg) {
239 cg_stop = cfg_cg + 1;
241 cg_start = sblock.fs_ncg;
242 cg_stop = sblock.fs_ncg;
245 if (cfg_lv & 0x004) {
246 fscs = (struct csum *)calloc((size_t)1,
247 (size_t)sblock.fs_cssize);
249 errx(1, "calloc failed");
251 /* get the cylinder summary into the memory ... */
252 for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
253 if (bread(&disk, fsbtodb(&sblock,
254 sblock.fs_csaddr + numfrags(&sblock, i)),
255 (void *)(((char *)fscs)+i),
256 (size_t)(sblock.fs_cssize-i < sblock.fs_bsize ?
257 sblock.fs_cssize - i : sblock.fs_bsize)) == -1)
258 err(1, "bread: %s", disk.d_error);
262 /* ... and dump it */
263 for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) {
264 snprintf(dbg_line, sizeof(dbg_line),
265 "%d. csum in fscs", dbg_csc);
266 DBG_DUMP_CSUM(&sblock,
273 /* for each requested cylinder group ... */
274 for (cylno = cg_start; cylno < cg_stop; cylno++) {
275 snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno);
276 if (cfg_lv & 0x002) {
277 /* dump the superblock copies */
278 if (bread(&disk, fsbtodb(&sblock,
279 cgsblock(&sblock, cylno)),
280 (void *)&osblock, SBLOCKSIZE) == -1)
281 err(1, "bread: %s", disk.d_error);
282 DBG_DUMP_FS(&osblock, dbg_line);
286 * Read the cylinder group and dump whatever was
289 if (bread(&disk, fsbtodb(&sblock,
290 cgtod(&sblock, cylno)), (void *)&acg,
291 (size_t)sblock.fs_cgsize) == -1)
292 err(1, "bread: %s", disk.d_error);
295 DBG_DUMP_CG(&sblock, dbg_line, &acg);
297 DBG_DUMP_INMAP(&sblock, dbg_line, &acg);
299 DBG_DUMP_FRMAP(&sblock, dbg_line, &acg);
300 if (cfg_lv & 0x040) {
301 DBG_DUMP_CLMAP(&sblock, dbg_line, &acg);
302 DBG_DUMP_CLSUM(&sblock, dbg_line, &acg);
306 * See the comment in sbin/growfs/debug.c for why this
307 * is currently disabled, and what needs to be done to
310 if (disk.d_ufs == 1 && cfg_lv & 0x080)
311 DBG_DUMP_SPTBL(&sblock, dbg_line, &acg);
316 if (cfg_lv & 0x300) {
317 /* Dump the requested inode(s) */
319 DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv);
321 for (in = cg_start * sblock.fs_ipg;
322 in < (ino_t)cg_stop * sblock.fs_ipg;
324 DUMP_WHOLE_INODE(in, cfg_lv);
334 /* ********************************************** dump_whole_ufs1_inode ***** */
336 * Here we dump a list of all blocks allocated by this inode. We follow
337 * all indirect blocks.
340 dump_whole_ufs1_inode(ino_t inode, int level)
342 DBG_FUNC("dump_whole_ufs1_inode")
343 struct ufs1_dinode *ino;
345 unsigned int ind2ctr, ind3ctr;
346 ufs1_daddr_t *ind2ptr, *ind3ptr;
352 * Read the inode from disk/cache.
354 if (getino(&disk, (void **)&ino, inode, &mode) == -1)
355 err(1, "getino: %s", disk.d_error);
357 if(ino->di_nlink==0) {
359 return; /* inode not in use */
363 * Dump the main inode structure.
365 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode);
367 DBG_DUMP_INO(&sblock,
372 if (!(level & 0x200)) {
378 * Ok, now prepare for dumping all direct and indirect pointers.
380 rb=howmany(ino->di_size, sblock.fs_bsize)-NDADDR;
383 * Dump single indirect block.
385 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[0]), (void *)&i1blk,
386 (size_t)sblock.fs_bsize) == -1) {
387 err(1, "bread: %s", disk.d_error);
389 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0",
391 DBG_DUMP_IBLK(&sblock,
395 rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
399 * Dump double indirect blocks.
401 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[1]), (void *)&i2blk,
402 (size_t)sblock.fs_bsize) == -1) {
403 err(1, "bread: %s", disk.d_error);
405 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1",
407 DBG_DUMP_IBLK(&sblock,
410 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
411 for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
412 sizeof(ufs1_daddr_t))) && (rb>0)); ind2ctr++) {
413 ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr];
415 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
416 (size_t)sblock.fs_bsize) == -1) {
417 err(1, "bread: %s", disk.d_error);
419 snprintf(comment, sizeof(comment),
420 "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode,
422 DBG_DUMP_IBLK(&sblock,
426 rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
431 * Dump triple indirect blocks.
433 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[2]), (void *)&i3blk,
434 (size_t)sblock.fs_bsize) == -1) {
435 err(1, "bread: %s", disk.d_error);
437 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2",
439 #define SQUARE(a) ((a)*(a))
440 DBG_DUMP_IBLK(&sblock,
444 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))));
446 for(ind3ctr=0; ((ind3ctr<howmany(sblock.fs_bsize,
447 sizeof(ufs1_daddr_t)))&&(rb>0)); ind3ctr++) {
448 ind3ptr=&((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr];
450 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk,
451 (size_t)sblock.fs_bsize) == -1) {
452 err(1, "bread: %s", disk.d_error);
454 snprintf(comment, sizeof(comment),
455 "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode,
457 DBG_DUMP_IBLK(&sblock,
461 howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
462 for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
463 sizeof(ufs1_daddr_t)))&&(rb>0)); ind2ctr++) {
464 ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk)
466 if (bread(&disk, fsbtodb(&sblock, *ind2ptr),
467 (void *)&i1blk, (size_t)sblock.fs_bsize)
469 err(1, "bread: %s", disk.d_error);
471 snprintf(comment, sizeof(comment),
472 "Inode 0x%08jx: indirect 2->%d->%d",
473 (uintmax_t)inode, ind3ctr, ind3ctr);
474 DBG_DUMP_IBLK(&sblock,
478 rb-=howmany(sblock.fs_bsize,
479 sizeof(ufs1_daddr_t));
488 /* ********************************************** dump_whole_ufs2_inode ***** */
490 * Here we dump a list of all blocks allocated by this inode. We follow
491 * all indirect blocks.
494 dump_whole_ufs2_inode(ino_t inode, int level)
496 DBG_FUNC("dump_whole_ufs2_inode")
497 struct ufs2_dinode *ino;
499 unsigned int ind2ctr, ind3ctr;
500 ufs2_daddr_t *ind2ptr, *ind3ptr;
506 * Read the inode from disk/cache.
508 if (getino(&disk, (void **)&ino, inode, &mode) == -1)
509 err(1, "getino: %s", disk.d_error);
511 if (ino->di_nlink == 0) {
513 return; /* inode not in use */
517 * Dump the main inode structure.
519 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode);
521 DBG_DUMP_INO(&sblock, comment, ino);
524 if (!(level & 0x200)) {
530 * Ok, now prepare for dumping all direct and indirect pointers.
532 rb = howmany(ino->di_size, sblock.fs_bsize) - NDADDR;
535 * Dump single indirect block.
537 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[0]), (void *)&i1blk,
538 (size_t)sblock.fs_bsize) == -1) {
539 err(1, "bread: %s", disk.d_error);
541 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0",
543 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
544 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
548 * Dump double indirect blocks.
550 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[1]), (void *)&i2blk,
551 (size_t)sblock.fs_bsize) == -1) {
552 err(1, "bread: %s", disk.d_error);
554 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1",
556 DBG_DUMP_IBLK(&sblock,
559 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
560 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
561 sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) {
562 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr];
564 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
565 (size_t)sblock.fs_bsize) == -1) {
566 err(1, "bread: %s", disk.d_error);
568 snprintf(comment, sizeof(comment),
569 "Inode 0x%08jx: indirect 1->%d",
570 (uintmax_t)inode, ind2ctr);
571 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
572 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
577 * Dump triple indirect blocks.
579 if (bread(&disk, fsbtodb(&sblock, ino->di_ib[2]), (void *)&i3blk,
580 (size_t)sblock.fs_bsize) == -1) {
581 err(1, "bread: %s", disk.d_error);
583 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2",
585 #define SQUARE(a) ((a)*(a))
586 DBG_DUMP_IBLK(&sblock,
590 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))));
592 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize,
593 sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) {
594 ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr];
596 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk,
597 (size_t)sblock.fs_bsize) == -1) {
598 err(1, "bread: %s", disk.d_error);
600 snprintf(comment, sizeof(comment),
601 "Inode 0x%08jx: indirect 2->%d",
602 (uintmax_t)inode, ind3ctr);
603 DBG_DUMP_IBLK(&sblock,
607 howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
608 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
609 sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) {
610 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr];
611 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk,
612 (size_t)sblock.fs_bsize) == -1) {
613 err(1, "bread: %s", disk.d_error);
615 snprintf(comment, sizeof(comment),
616 "Inode 0x%08jx: indirect 2->%d->%d",
617 (uintmax_t)inode, ind3ctr, ind3ctr);
618 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
619 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
628 /* ************************************************************* usage ***** */
630 * Dump a line of usage.
640 "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] "
642 " special | file\n");