1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz 5 * Copyright (c) 1980, 1989, 1993 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 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. 27 * 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 38 * SUCH DAMAGE. 39 * 40 * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.4 2000/12/12 19:30:55 tomsoft Exp $ 41 * 42 */ 43 44 #ifndef lint 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"; 49 #endif /* not lint */ 50 51 #ifndef lint 52 static const char rcsid[] = 53 "$FreeBSD$"; 54 #endif /* not lint */ 55 56 /* ********************************************************** INCLUDES ***** */ 57 #include <sys/param.h> 58 #include <sys/disklabel.h> 59 #include <sys/mount.h> 60 #include <sys/stat.h> 61 62 #include <ufs/ufs/extattr.h> 63 #include <ufs/ufs/quota.h> 64 #include <ufs/ufs/ufsmount.h> 65 #include <ufs/ufs/dinode.h> 66 #include <ufs/ffs/fs.h> 67 68 #include <ctype.h> 69 #include <err.h> 70 #include <errno.h> 71 #include <fcntl.h> 72 #include <libufs.h> 73 #include <paths.h> 74 #include <stdint.h> 75 #include <stdio.h> 76 #include <stdlib.h> 77 #include <string.h> 78 #include <unistd.h> 79 80 #include "debug.h" 81 82 /* *********************************************************** GLOBALS ***** */ 83 #ifdef FS_DEBUG 84 int _dbg_lvl_ = (DL_INFO); /* DL_TRC */ 85 #endif /* FS_DEBUG */ 86 87 static struct uufsd disk; 88 89 #define sblock disk.d_fs 90 #define acg disk.d_cg 91 92 static union { 93 struct fs fs; 94 char pad[SBLOCKSIZE]; 95 } fsun; 96 97 #define osblock fsun.fs 98 99 static char i1blk[MAXBSIZE]; 100 static char i2blk[MAXBSIZE]; 101 static char i3blk[MAXBSIZE]; 102 103 static struct csum *fscs; 104 105 /* ******************************************************** PROTOTYPES ***** */ 106 static void usage(void); 107 static void dump_whole_ufs1_inode(ino_t, int); 108 static void dump_whole_ufs2_inode(ino_t, int); 109 110 #define DUMP_WHOLE_INODE(A,B) \ 111 ( disk.d_ufs == 1 \ 112 ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) ) 113 114 /* ************************************************************** main ***** */ 115 /* 116 * ffsinfo(8) is a tool to dump all metadata of a file system. It helps to find 117 * errors is the file system much easier. You can run ffsinfo before and after 118 * an fsck(8), and compare the two ascii dumps easy with diff, and you see 119 * directly where the problem is. You can control how much detail you want to 120 * see with some command line arguments. You can also easy check the status 121 * of a file system, like is there is enough space for growing a file system, 122 * or how many active snapshots do we have. It provides much more detailed 123 * information then dumpfs. Snapshots, as they are very new, are not really 124 * supported. They are just mentioned currently, but it is planned to run 125 * also over active snapshots, to even get that output. 126 */ 127 int 128 main(int argc, char **argv) 129 { 130 DBG_FUNC("main") 131 char *device, *special; 132 int ch; 133 size_t len; 134 struct stat st; 135 struct csum *dbg_csp; 136 int dbg_csc; 137 char dbg_line[80]; 138 int cylno,i; 139 int cfg_cg, cfg_in, cfg_lv; 140 int cg_start, cg_stop; 141 ino_t in; 142 char *out_file; 143 144 DBG_ENTER; 145 146 cfg_lv = 0xff; 147 cfg_in = -2; 148 cfg_cg = -2; 149 out_file = strdup("-"); 150 151 while ((ch = getopt(argc, argv, "g:i:l:o:")) != -1) { 152 switch (ch) { 153 case 'g': 154 cfg_cg = strtol(optarg, NULL, 0); 155 if (errno == EINVAL || errno == ERANGE) 156 err(1, "%s", optarg); 157 if (cfg_cg < -1) 158 usage(); 159 break; 160 case 'i': 161 cfg_in = strtol(optarg, NULL, 0); 162 if (errno == EINVAL || errno == ERANGE) 163 err(1, "%s", optarg); 164 if (cfg_in < 0) 165 usage(); 166 break; 167 case 'l': 168 cfg_lv = strtol(optarg, NULL, 0); 169 if (errno == EINVAL||errno == ERANGE) 170 err(1, "%s", optarg); 171 if (cfg_lv < 0x1 || cfg_lv > 0x3ff) 172 usage(); 173 break; 174 case 'o': 175 free(out_file); 176 out_file = strdup(optarg); 177 if (out_file == NULL) 178 errx(1, "strdup failed"); 179 break; 180 case '?': 181 /* FALLTHROUGH */ 182 default: 183 usage(); 184 } 185 } 186 argc -= optind; 187 argv += optind; 188 189 if (argc != 1) 190 usage(); 191 device = *argv; 192 193 /* 194 * Now we try to guess the (raw)device name. 195 */ 196 if (0 == strrchr(device, '/') && stat(device, &st) == -1) { 197 /*- 198 * No path prefix was given, so try in this order: 199 * /dev/r%s 200 * /dev/%s 201 * /dev/vinum/r%s 202 * /dev/vinum/%s. 203 * 204 * FreeBSD now doesn't distinguish between raw and block 205 * devices any longer, but it should still work this way. 206 */ 207 len = strlen(device) + strlen(_PATH_DEV) + 2 + strlen("vinum/"); 208 special = (char *)malloc(len); 209 if (special == NULL) 210 errx(1, "malloc failed"); 211 snprintf(special, len, "%sr%s", _PATH_DEV, device); 212 if (stat(special, &st) == -1) { 213 snprintf(special, len, "%s%s", _PATH_DEV, device); 214 if (stat(special, &st) == -1) { 215 snprintf(special, len, "%svinum/r%s", 216 _PATH_DEV, device); 217 if (stat(special, &st) == -1) 218 /* For now this is the 'last resort' */ 219 snprintf(special, len, "%svinum/%s", 220 _PATH_DEV, device); 221 } 222 } 223 device = special; 224 } 225 226 if (ufs_disk_fillout(&disk, device) == -1) 227 err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error); 228 229 DBG_OPEN(out_file); /* already here we need a superblock */ 230 231 if (cfg_lv & 0x001) 232 DBG_DUMP_FS(&sblock, "primary sblock"); 233 234 /* Determine here what cylinder groups to dump */ 235 if (cfg_cg==-2) { 236 cg_start = 0; 237 cg_stop = sblock.fs_ncg; 238 } else if (cfg_cg == -1) { 239 cg_start = sblock.fs_ncg - 1; 240 cg_stop = sblock.fs_ncg; 241 } else if (cfg_cg < sblock.fs_ncg) { 242 cg_start = cfg_cg; 243 cg_stop = cfg_cg + 1; 244 } else { 245 cg_start = sblock.fs_ncg; 246 cg_stop = sblock.fs_ncg; 247 } 248 249 if (cfg_lv & 0x004) { 250 fscs = (struct csum *)calloc((size_t)1, 251 (size_t)sblock.fs_cssize); 252 if (fscs == NULL) 253 errx(1, "calloc failed"); 254 255 /* get the cylinder summary into the memory ... */ 256 for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { 257 if (bread(&disk, fsbtodb(&sblock, 258 sblock.fs_csaddr + numfrags(&sblock, i)), 259 (void *)(((char *)fscs)+i), 260 (size_t)(sblock.fs_cssize-i < sblock.fs_bsize ? 261 sblock.fs_cssize - i : sblock.fs_bsize)) == -1) 262 err(1, "bread: %s", disk.d_error); 263 } 264 265 dbg_csp = fscs; 266 /* ... and dump it */ 267 for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { 268 snprintf(dbg_line, sizeof(dbg_line), 269 "%d. csum in fscs", dbg_csc); 270 DBG_DUMP_CSUM(&sblock, 271 dbg_line, 272 dbg_csp++); 273 } 274 } 275 276 if (cfg_lv & 0xf8) { 277 /* for each requested cylinder group ... */ 278 for (cylno = cg_start; cylno < cg_stop; cylno++) { 279 snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno); 280 if (cfg_lv & 0x002) { 281 /* dump the superblock copies */ 282 if (bread(&disk, fsbtodb(&sblock, 283 cgsblock(&sblock, cylno)), 284 (void *)&osblock, SBLOCKSIZE) == -1) 285 err(1, "bread: %s", disk.d_error); 286 DBG_DUMP_FS(&osblock, dbg_line); 287 } 288 289 /* 290 * Read the cylinder group and dump whatever was 291 * requested. 292 */ 293 if (bread(&disk, fsbtodb(&sblock, 294 cgtod(&sblock, cylno)), (void *)&acg, 295 (size_t)sblock.fs_cgsize) == -1) 296 err(1, "bread: %s", disk.d_error); 297 298 if (cfg_lv & 0x008) 299 DBG_DUMP_CG(&sblock, dbg_line, &acg); 300 if (cfg_lv & 0x010) 301 DBG_DUMP_INMAP(&sblock, dbg_line, &acg); 302 if (cfg_lv & 0x020) 303 DBG_DUMP_FRMAP(&sblock, dbg_line, &acg); 304 if (cfg_lv & 0x040) { 305 DBG_DUMP_CLMAP(&sblock, dbg_line, &acg); 306 DBG_DUMP_CLSUM(&sblock, dbg_line, &acg); 307 } 308 #ifdef NOT_CURRENTLY 309 /* 310 * See the comment in sbin/growfs/debug.c for why this 311 * is currently disabled, and what needs to be done to 312 * re-enable it. 313 */ 314 if (disk.d_ufs == 1 && cfg_lv & 0x080) 315 DBG_DUMP_SPTBL(&sblock, dbg_line, &acg); 316 #endif 317 } 318 } 319 320 if (cfg_lv & 0x300) { 321 /* Dump the requested inode(s) */ 322 if (cfg_in != -2) 323 DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv); 324 else { 325 for (in = cg_start * sblock.fs_ipg; 326 in < (ino_t)cg_stop * sblock.fs_ipg; 327 in++) 328 DUMP_WHOLE_INODE(in, cfg_lv); 329 } 330 } 331 332 DBG_CLOSE; 333 DBG_LEAVE; 334 335 return 0; 336 } 337 338 /* ********************************************** dump_whole_ufs1_inode ***** */ 339 /* 340 * Here we dump a list of all blocks allocated by this inode. We follow 341 * all indirect blocks. 342 */ 343 void 344 dump_whole_ufs1_inode(ino_t inode, int level) 345 { 346 DBG_FUNC("dump_whole_ufs1_inode") 347 union dinodep dp; 348 int rb; 349 unsigned int ind2ctr, ind3ctr; 350 ufs1_daddr_t *ind2ptr, *ind3ptr; 351 char comment[80]; 352 353 DBG_ENTER; 354 355 /* 356 * Read the inode from disk/cache. 357 */ 358 if (getinode(&disk, &dp, inode) == -1) 359 err(1, "getinode: %s", disk.d_error); 360 361 if (dp.dp1->di_nlink == 0) { 362 DBG_LEAVE; 363 return; /* inode not in use */ 364 } 365 366 /* 367 * Dump the main inode structure. 368 */ 369 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); 370 if (level & 0x100) { 371 DBG_DUMP_INO(&sblock, 372 comment, 373 dp.dp1); 374 } 375 376 if (!(level & 0x200)) { 377 DBG_LEAVE; 378 return; 379 } 380 381 /* 382 * Ok, now prepare for dumping all direct and indirect pointers. 383 */ 384 rb = howmany(dp.dp1->di_size, sblock.fs_bsize) - UFS_NDADDR; 385 if (rb > 0) { 386 /* 387 * Dump single indirect block. 388 */ 389 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[0]), 390 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 391 err(1, "bread: %s", disk.d_error); 392 } 393 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", 394 (uintmax_t)inode); 395 DBG_DUMP_IBLK(&sblock, 396 comment, 397 i1blk, 398 (size_t)rb); 399 rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); 400 } 401 if (rb > 0) { 402 /* 403 * Dump double indirect blocks. 404 */ 405 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[1]), 406 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 407 err(1, "bread: %s", disk.d_error); 408 } 409 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", 410 (uintmax_t)inode); 411 DBG_DUMP_IBLK(&sblock, 412 comment, 413 i2blk, 414 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); 415 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 416 sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { 417 ind2ptr = &((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr]; 418 419 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 420 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 421 err(1, "bread: %s", disk.d_error); 422 } 423 snprintf(comment, sizeof(comment), 424 "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode, 425 ind2ctr); 426 DBG_DUMP_IBLK(&sblock, 427 comment, 428 i1blk, 429 (size_t)rb); 430 rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); 431 } 432 } 433 if (rb > 0) { 434 /* 435 * Dump triple indirect blocks. 436 */ 437 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[2]), 438 (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { 439 err(1, "bread: %s", disk.d_error); 440 } 441 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", 442 (uintmax_t)inode); 443 #define SQUARE(a) ((a)*(a)) 444 DBG_DUMP_IBLK(&sblock, 445 comment, 446 i3blk, 447 howmany(rb, 448 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))))); 449 #undef SQUARE 450 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, 451 sizeof(ufs1_daddr_t))) && (rb > 0)); ind3ctr++) { 452 ind3ptr = &((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr]; 453 454 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), 455 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 456 err(1, "bread: %s", disk.d_error); 457 } 458 snprintf(comment, sizeof(comment), 459 "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode, 460 ind3ctr); 461 DBG_DUMP_IBLK(&sblock, 462 comment, 463 i2blk, 464 howmany(rb, 465 howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); 466 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 467 sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { 468 ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk) 469 [ind2ctr]; 470 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 471 (void *)&i1blk, (size_t)sblock.fs_bsize) 472 == -1) { 473 err(1, "bread: %s", disk.d_error); 474 } 475 snprintf(comment, sizeof(comment), 476 "Inode 0x%08jx: indirect 2->%d->%d", 477 (uintmax_t)inode, ind3ctr, ind3ctr); 478 DBG_DUMP_IBLK(&sblock, 479 comment, 480 i1blk, 481 (size_t)rb); 482 rb -= howmany(sblock.fs_bsize, 483 sizeof(ufs1_daddr_t)); 484 } 485 } 486 } 487 488 DBG_LEAVE; 489 return; 490 } 491 492 /* ********************************************** dump_whole_ufs2_inode ***** */ 493 /* 494 * Here we dump a list of all blocks allocated by this inode. We follow 495 * all indirect blocks. 496 */ 497 void 498 dump_whole_ufs2_inode(ino_t inode, int level) 499 { 500 DBG_FUNC("dump_whole_ufs2_inode") 501 union dinodep dp; 502 int rb; 503 unsigned int ind2ctr, ind3ctr; 504 ufs2_daddr_t *ind2ptr, *ind3ptr; 505 char comment[80]; 506 507 DBG_ENTER; 508 509 /* 510 * Read the inode from disk/cache. 511 */ 512 if (getinode(&disk, &dp, inode) == -1) 513 err(1, "getinode: %s", disk.d_error); 514 515 if (dp.dp2->di_nlink == 0) { 516 DBG_LEAVE; 517 return; /* inode not in use */ 518 } 519 520 /* 521 * Dump the main inode structure. 522 */ 523 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); 524 if (level & 0x100) { 525 DBG_DUMP_INO(&sblock, comment, dp.dp2); 526 } 527 528 if (!(level & 0x200)) { 529 DBG_LEAVE; 530 return; 531 } 532 533 /* 534 * Ok, now prepare for dumping all direct and indirect pointers. 535 */ 536 rb = howmany(dp.dp2->di_size, sblock.fs_bsize) - UFS_NDADDR; 537 if (rb > 0) { 538 /* 539 * Dump single indirect block. 540 */ 541 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[0]), 542 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 543 err(1, "bread: %s", disk.d_error); 544 } 545 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", 546 (uintmax_t)inode); 547 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); 548 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); 549 } 550 if (rb > 0) { 551 /* 552 * Dump double indirect blocks. 553 */ 554 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[1]), 555 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 556 err(1, "bread: %s", disk.d_error); 557 } 558 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", 559 (uintmax_t)inode); 560 DBG_DUMP_IBLK(&sblock, 561 comment, 562 i2blk, 563 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); 564 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 565 sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) { 566 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr]; 567 568 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 569 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 570 err(1, "bread: %s", disk.d_error); 571 } 572 snprintf(comment, sizeof(comment), 573 "Inode 0x%08jx: indirect 1->%d", 574 (uintmax_t)inode, ind2ctr); 575 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); 576 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); 577 } 578 } 579 if (rb > 0) { 580 /* 581 * Dump triple indirect blocks. 582 */ 583 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[2]), 584 (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { 585 err(1, "bread: %s", disk.d_error); 586 } 587 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", 588 (uintmax_t)inode); 589 #define SQUARE(a) ((a)*(a)) 590 DBG_DUMP_IBLK(&sblock, 591 comment, 592 i3blk, 593 howmany(rb, 594 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))))); 595 #undef SQUARE 596 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, 597 sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) { 598 ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr]; 599 600 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), 601 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 602 err(1, "bread: %s", disk.d_error); 603 } 604 snprintf(comment, sizeof(comment), 605 "Inode 0x%08jx: indirect 2->%d", 606 (uintmax_t)inode, ind3ctr); 607 DBG_DUMP_IBLK(&sblock, 608 comment, 609 i2blk, 610 howmany(rb, 611 howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); 612 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 613 sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) { 614 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr]; 615 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 616 (void *)&i1blk, (size_t)sblock.fs_bsize) 617 == -1) { 618 err(1, "bread: %s", disk.d_error); 619 } 620 snprintf(comment, sizeof(comment), 621 "Inode 0x%08jx: indirect 2->%d->%d", 622 (uintmax_t)inode, ind3ctr, ind3ctr); 623 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); 624 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); 625 } 626 } 627 } 628 629 DBG_LEAVE; 630 return; 631 } 632 633 /* ************************************************************* usage ***** */ 634 /* 635 * Dump a line of usage. 636 */ 637 void 638 usage(void) 639 { 640 DBG_FUNC("usage") 641 642 DBG_ENTER; 643 644 fprintf(stderr, 645 "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] " 646 "[-o outfile]\n" 647 " special | file\n"); 648 649 DBG_LEAVE; 650 exit(1); 651 } 652