2 * SPDX-License-Identifier: BSD-4-Clause
4 * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
5 * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
8 * This code is derived from software contributed to Berkeley by
9 * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgment:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors, as well as Christoph
23 * Herrmann and Thomas-Henning von Kamptz.
24 * 4. Neither the name of the University nor the names of its contributors
25 * may be used to endorse or promote products derived from this software
26 * without specific prior written permission.
28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
40 * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.4 2000/12/12 19:30:55 tomsoft Exp $
45 static const char copyright[] =
46 "@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\
47 Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\
48 All rights reserved.\n";
52 static const char rcsid[] =
56 /* ********************************************************** INCLUDES ***** */
57 #include <sys/param.h>
58 #include <sys/disklabel.h>
59 #include <sys/mount.h>
62 #include <ufs/ufs/ufsmount.h>
63 #include <ufs/ufs/dinode.h>
64 #include <ufs/ffs/fs.h>
80 /* *********************************************************** GLOBALS ***** */
82 int _dbg_lvl_ = (DL_INFO); /* DL_TRC */
85 static struct uufsd disk;
87 #define sblock disk.d_fs
95 #define osblock fsun.fs
97 static char i1blk[MAXBSIZE];
98 static char i2blk[MAXBSIZE];
99 static char i3blk[MAXBSIZE];
101 static struct csum *fscs;
103 /* ******************************************************** PROTOTYPES ***** */
104 static void usage(void);
105 static void dump_whole_ufs1_inode(ino_t, int);
106 static void dump_whole_ufs2_inode(ino_t, int);
108 #define DUMP_WHOLE_INODE(A,B) \
110 ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) )
112 /* ************************************************************** main ***** */
114 * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find
115 * errors is the file system much easier. You can run ffsinfo before and after
116 * an fsck(8), and compare the two ascii dumps easy with diff, and you see
117 * directly where the problem is. You can control how much detail you want to
118 * see with some command line arguments. You can also easy check the status
119 * of a file system, like is there is enough space for growing a file system,
120 * or how many active snapshots do we have. It provides much more detailed
121 * information then dumpfs. Snapshots, as they are very new, are not really
122 * supported. They are just mentioned currently, but it is planned to run
123 * also over active snapshots, to even get that output.
126 main(int argc, char **argv)
129 char *device, *special;
133 struct csum *dbg_csp;
137 int cfg_cg, cfg_in, cfg_lv;
138 int cg_start, cg_stop;
147 out_file = strdup("-");
149 while ((ch = getopt(argc, argv, "g:i:l:o:")) != -1) {
152 cfg_cg = strtol(optarg, NULL, 0);
153 if (errno == EINVAL || errno == ERANGE)
154 err(1, "%s", optarg);
159 cfg_in = strtol(optarg, NULL, 0);
160 if (errno == EINVAL || errno == ERANGE)
161 err(1, "%s", optarg);
166 cfg_lv = strtol(optarg, NULL, 0);
167 if (errno == EINVAL||errno == ERANGE)
168 err(1, "%s", optarg);
169 if (cfg_lv < 0x1 || cfg_lv > 0x3ff)
174 out_file = strdup(optarg);
175 if (out_file == NULL)
176 errx(1, "strdup failed");
192 * Now we try to guess the (raw)device name.
194 if (0 == strrchr(device, '/') && stat(device, &st) == -1) {
196 * No path prefix was given, so try in this order:
202 * FreeBSD now doesn't distinguish between raw and block
203 * devices any longer, but it should still work this way.
205 len = strlen(device) + strlen(_PATH_DEV) + 2 + strlen("vinum/");
206 special = (char *)malloc(len);
208 errx(1, "malloc failed");
209 snprintf(special, len, "%sr%s", _PATH_DEV, device);
210 if (stat(special, &st) == -1) {
211 snprintf(special, len, "%s%s", _PATH_DEV, device);
212 if (stat(special, &st) == -1) {
213 snprintf(special, len, "%svinum/r%s",
215 if (stat(special, &st) == -1)
216 /* For now this is the 'last resort' */
217 snprintf(special, len, "%svinum/%s",
224 if (ufs_disk_fillout(&disk, device) == -1)
225 err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error);
227 DBG_OPEN(out_file); /* already here we need a superblock */
230 DBG_DUMP_FS(&sblock, "primary sblock");
232 /* Determine here what cylinder groups to dump */
235 cg_stop = sblock.fs_ncg;
236 } else if (cfg_cg == -1) {
237 cg_start = sblock.fs_ncg - 1;
238 cg_stop = sblock.fs_ncg;
239 } else if (cfg_cg < sblock.fs_ncg) {
241 cg_stop = cfg_cg + 1;
243 cg_start = sblock.fs_ncg;
244 cg_stop = sblock.fs_ncg;
247 if (cfg_lv & 0x004) {
248 fscs = (struct csum *)calloc((size_t)1,
249 (size_t)sblock.fs_cssize);
251 errx(1, "calloc failed");
253 /* get the cylinder summary into the memory ... */
254 for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
255 if (bread(&disk, fsbtodb(&sblock,
256 sblock.fs_csaddr + numfrags(&sblock, i)),
257 (void *)(((char *)fscs)+i),
258 (size_t)(sblock.fs_cssize-i < sblock.fs_bsize ?
259 sblock.fs_cssize - i : sblock.fs_bsize)) == -1)
260 err(1, "bread: %s", disk.d_error);
264 /* ... and dump it */
265 for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) {
266 snprintf(dbg_line, sizeof(dbg_line),
267 "%d. csum in fscs", dbg_csc);
268 DBG_DUMP_CSUM(&sblock,
275 /* for each requested cylinder group ... */
276 for (cylno = cg_start; cylno < cg_stop; cylno++) {
277 snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno);
278 if (cfg_lv & 0x002) {
279 /* dump the superblock copies */
280 if (bread(&disk, fsbtodb(&sblock,
281 cgsblock(&sblock, cylno)),
282 (void *)&osblock, SBLOCKSIZE) == -1)
283 err(1, "bread: %s", disk.d_error);
284 DBG_DUMP_FS(&osblock, dbg_line);
288 * Read the cylinder group and dump whatever was
291 if (bread(&disk, fsbtodb(&sblock,
292 cgtod(&sblock, cylno)), (void *)&acg,
293 (size_t)sblock.fs_cgsize) == -1)
294 err(1, "bread: %s", disk.d_error);
297 DBG_DUMP_CG(&sblock, dbg_line, &acg);
299 DBG_DUMP_INMAP(&sblock, dbg_line, &acg);
301 DBG_DUMP_FRMAP(&sblock, dbg_line, &acg);
302 if (cfg_lv & 0x040) {
303 DBG_DUMP_CLMAP(&sblock, dbg_line, &acg);
304 DBG_DUMP_CLSUM(&sblock, dbg_line, &acg);
308 * See the comment in sbin/growfs/debug.c for why this
309 * is currently disabled, and what needs to be done to
312 if (disk.d_ufs == 1 && cfg_lv & 0x080)
313 DBG_DUMP_SPTBL(&sblock, dbg_line, &acg);
318 if (cfg_lv & 0x300) {
319 /* Dump the requested inode(s) */
321 DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv);
323 for (in = cg_start * sblock.fs_ipg;
324 in < (ino_t)cg_stop * sblock.fs_ipg;
326 DUMP_WHOLE_INODE(in, cfg_lv);
336 /* ********************************************** dump_whole_ufs1_inode ***** */
338 * Here we dump a list of all blocks allocated by this inode. We follow
339 * all indirect blocks.
342 dump_whole_ufs1_inode(ino_t inode, int level)
344 DBG_FUNC("dump_whole_ufs1_inode")
347 unsigned int ind2ctr, ind3ctr;
348 ufs1_daddr_t *ind2ptr, *ind3ptr;
354 * Read the inode from disk/cache.
356 if (getinode(&disk, &dp, inode) == -1)
357 err(1, "getinode: %s", disk.d_error);
359 if (dp.dp1->di_nlink == 0) {
361 return; /* inode not in use */
365 * Dump the main inode structure.
367 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode);
369 DBG_DUMP_INO(&sblock,
374 if (!(level & 0x200)) {
380 * Ok, now prepare for dumping all direct and indirect pointers.
382 rb = howmany(dp.dp1->di_size, sblock.fs_bsize) - UFS_NDADDR;
385 * Dump single indirect block.
387 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[0]),
388 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) {
389 err(1, "bread: %s", disk.d_error);
391 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0",
393 DBG_DUMP_IBLK(&sblock,
397 rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
401 * Dump double indirect blocks.
403 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[1]),
404 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) {
405 err(1, "bread: %s", disk.d_error);
407 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1",
409 DBG_DUMP_IBLK(&sblock,
412 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
413 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
414 sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) {
415 ind2ptr = &((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr];
417 if (bread(&disk, fsbtodb(&sblock, *ind2ptr),
418 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) {
419 err(1, "bread: %s", disk.d_error);
421 snprintf(comment, sizeof(comment),
422 "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode,
424 DBG_DUMP_IBLK(&sblock,
428 rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t));
433 * Dump triple indirect blocks.
435 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[2]),
436 (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) {
437 err(1, "bread: %s", disk.d_error);
439 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2",
441 #define SQUARE(a) ((a)*(a))
442 DBG_DUMP_IBLK(&sblock,
446 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))));
448 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize,
449 sizeof(ufs1_daddr_t))) && (rb > 0)); ind3ctr++) {
450 ind3ptr = &((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr];
452 if (bread(&disk, fsbtodb(&sblock, *ind3ptr),
453 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) {
454 err(1, "bread: %s", disk.d_error);
456 snprintf(comment, sizeof(comment),
457 "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode,
459 DBG_DUMP_IBLK(&sblock,
463 howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))));
464 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
465 sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) {
466 ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk)
468 if (bread(&disk, fsbtodb(&sblock, *ind2ptr),
469 (void *)&i1blk, (size_t)sblock.fs_bsize)
471 err(1, "bread: %s", disk.d_error);
473 snprintf(comment, sizeof(comment),
474 "Inode 0x%08jx: indirect 2->%d->%d",
475 (uintmax_t)inode, ind3ctr, ind3ctr);
476 DBG_DUMP_IBLK(&sblock,
480 rb -= howmany(sblock.fs_bsize,
481 sizeof(ufs1_daddr_t));
490 /* ********************************************** dump_whole_ufs2_inode ***** */
492 * Here we dump a list of all blocks allocated by this inode. We follow
493 * all indirect blocks.
496 dump_whole_ufs2_inode(ino_t inode, int level)
498 DBG_FUNC("dump_whole_ufs2_inode")
501 unsigned int ind2ctr, ind3ctr;
502 ufs2_daddr_t *ind2ptr, *ind3ptr;
508 * Read the inode from disk/cache.
510 if (getinode(&disk, &dp, inode) == -1)
511 err(1, "getinode: %s", disk.d_error);
513 if (dp.dp2->di_nlink == 0) {
515 return; /* inode not in use */
519 * Dump the main inode structure.
521 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode);
523 DBG_DUMP_INO(&sblock, comment, dp.dp2);
526 if (!(level & 0x200)) {
532 * Ok, now prepare for dumping all direct and indirect pointers.
534 rb = howmany(dp.dp2->di_size, sblock.fs_bsize) - UFS_NDADDR;
537 * Dump single indirect block.
539 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[0]),
540 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) {
541 err(1, "bread: %s", disk.d_error);
543 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0",
545 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
546 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
550 * Dump double indirect blocks.
552 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[1]),
553 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) {
554 err(1, "bread: %s", disk.d_error);
556 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1",
558 DBG_DUMP_IBLK(&sblock,
561 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
562 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
563 sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) {
564 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr];
566 if (bread(&disk, fsbtodb(&sblock, *ind2ptr),
567 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) {
568 err(1, "bread: %s", disk.d_error);
570 snprintf(comment, sizeof(comment),
571 "Inode 0x%08jx: indirect 1->%d",
572 (uintmax_t)inode, ind2ctr);
573 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
574 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
579 * Dump triple indirect blocks.
581 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[2]),
582 (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) {
583 err(1, "bread: %s", disk.d_error);
585 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2",
587 #define SQUARE(a) ((a)*(a))
588 DBG_DUMP_IBLK(&sblock,
592 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))));
594 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize,
595 sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) {
596 ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr];
598 if (bread(&disk, fsbtodb(&sblock, *ind3ptr),
599 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) {
600 err(1, "bread: %s", disk.d_error);
602 snprintf(comment, sizeof(comment),
603 "Inode 0x%08jx: indirect 2->%d",
604 (uintmax_t)inode, ind3ctr);
605 DBG_DUMP_IBLK(&sblock,
609 howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))));
610 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize,
611 sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) {
612 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr];
613 if (bread(&disk, fsbtodb(&sblock, *ind2ptr),
614 (void *)&i1blk, (size_t)sblock.fs_bsize)
616 err(1, "bread: %s", disk.d_error);
618 snprintf(comment, sizeof(comment),
619 "Inode 0x%08jx: indirect 2->%d->%d",
620 (uintmax_t)inode, ind3ctr, ind3ctr);
621 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb);
622 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t));
631 /* ************************************************************* usage ***** */
633 * Dump a line of usage.
643 "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] "
645 " special | file\n");