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_blank(&disk, device) == -1 || 227 sbfind(&disk, 0) == -1) 228 err(1, "superblock fetch(%s) failed: %s", device, disk.d_error); 229 230 DBG_OPEN(out_file); /* already here we need a superblock */ 231 232 if (cfg_lv & 0x001) 233 DBG_DUMP_FS(&sblock, "primary sblock"); 234 235 /* Determine here what cylinder groups to dump */ 236 if (cfg_cg==-2) { 237 cg_start = 0; 238 cg_stop = sblock.fs_ncg; 239 } else if (cfg_cg == -1) { 240 cg_start = sblock.fs_ncg - 1; 241 cg_stop = sblock.fs_ncg; 242 } else if (cfg_cg < sblock.fs_ncg) { 243 cg_start = cfg_cg; 244 cg_stop = cfg_cg + 1; 245 } else { 246 cg_start = sblock.fs_ncg; 247 cg_stop = sblock.fs_ncg; 248 } 249 250 if (cfg_lv & 0x004) { 251 fscs = (struct csum *)calloc((size_t)1, 252 (size_t)sblock.fs_cssize); 253 if (fscs == NULL) 254 errx(1, "calloc failed"); 255 256 /* get the cylinder summary into the memory ... */ 257 for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { 258 if (bread(&disk, fsbtodb(&sblock, 259 sblock.fs_csaddr + numfrags(&sblock, i)), 260 (void *)(((char *)fscs)+i), 261 (size_t)(sblock.fs_cssize-i < sblock.fs_bsize ? 262 sblock.fs_cssize - i : sblock.fs_bsize)) == -1) 263 err(1, "bread: %s", disk.d_error); 264 } 265 266 dbg_csp = fscs; 267 /* ... and dump it */ 268 for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { 269 snprintf(dbg_line, sizeof(dbg_line), 270 "%d. csum in fscs", dbg_csc); 271 DBG_DUMP_CSUM(&sblock, 272 dbg_line, 273 dbg_csp++); 274 } 275 } 276 277 if (cfg_lv & 0xf8) { 278 /* for each requested cylinder group ... */ 279 for (cylno = cg_start; cylno < cg_stop; cylno++) { 280 snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno); 281 if (cfg_lv & 0x002) { 282 /* dump the superblock copies */ 283 if (bread(&disk, fsbtodb(&sblock, 284 cgsblock(&sblock, cylno)), 285 (void *)&osblock, SBLOCKSIZE) == -1) 286 err(1, "bread: %s", disk.d_error); 287 DBG_DUMP_FS(&osblock, dbg_line); 288 } 289 290 /* 291 * Read the cylinder group and dump whatever was 292 * requested. 293 */ 294 if (bread(&disk, fsbtodb(&sblock, 295 cgtod(&sblock, cylno)), (void *)&acg, 296 (size_t)sblock.fs_cgsize) == -1) 297 err(1, "bread: %s", disk.d_error); 298 299 if (cfg_lv & 0x008) 300 DBG_DUMP_CG(&sblock, dbg_line, &acg); 301 if (cfg_lv & 0x010) 302 DBG_DUMP_INMAP(&sblock, dbg_line, &acg); 303 if (cfg_lv & 0x020) 304 DBG_DUMP_FRMAP(&sblock, dbg_line, &acg); 305 if (cfg_lv & 0x040) { 306 DBG_DUMP_CLMAP(&sblock, dbg_line, &acg); 307 DBG_DUMP_CLSUM(&sblock, dbg_line, &acg); 308 } 309 #ifdef NOT_CURRENTLY 310 /* 311 * See the comment in sbin/growfs/debug.c for why this 312 * is currently disabled, and what needs to be done to 313 * re-enable it. 314 */ 315 if (disk.d_ufs == 1 && cfg_lv & 0x080) 316 DBG_DUMP_SPTBL(&sblock, dbg_line, &acg); 317 #endif 318 } 319 } 320 321 if (cfg_lv & 0x300) { 322 /* Dump the requested inode(s) */ 323 if (cfg_in != -2) 324 DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv); 325 else { 326 for (in = cg_start * sblock.fs_ipg; 327 in < (ino_t)cg_stop * sblock.fs_ipg; 328 in++) 329 DUMP_WHOLE_INODE(in, cfg_lv); 330 } 331 } 332 333 DBG_CLOSE; 334 DBG_LEAVE; 335 336 return 0; 337 } 338 339 /* ********************************************** dump_whole_ufs1_inode ***** */ 340 /* 341 * Here we dump a list of all blocks allocated by this inode. We follow 342 * all indirect blocks. 343 */ 344 void 345 dump_whole_ufs1_inode(ino_t inode, int level) 346 { 347 DBG_FUNC("dump_whole_ufs1_inode") 348 union dinodep dp; 349 int rb; 350 unsigned int ind2ctr, ind3ctr; 351 ufs1_daddr_t *ind2ptr, *ind3ptr; 352 char comment[80]; 353 354 DBG_ENTER; 355 356 /* 357 * Read the inode from disk/cache. 358 */ 359 if (getinode(&disk, &dp, inode) == -1) 360 err(1, "getinode: %s", disk.d_error); 361 362 if (dp.dp1->di_nlink == 0) { 363 DBG_LEAVE; 364 return; /* inode not in use */ 365 } 366 367 /* 368 * Dump the main inode structure. 369 */ 370 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); 371 if (level & 0x100) { 372 DBG_DUMP_INO(&sblock, 373 comment, 374 dp.dp1); 375 } 376 377 if (!(level & 0x200)) { 378 DBG_LEAVE; 379 return; 380 } 381 382 /* 383 * Ok, now prepare for dumping all direct and indirect pointers. 384 */ 385 rb = howmany(dp.dp1->di_size, sblock.fs_bsize) - UFS_NDADDR; 386 if (rb > 0) { 387 /* 388 * Dump single indirect block. 389 */ 390 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[0]), 391 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 392 err(1, "bread: %s", disk.d_error); 393 } 394 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", 395 (uintmax_t)inode); 396 DBG_DUMP_IBLK(&sblock, 397 comment, 398 i1blk, 399 (size_t)rb); 400 rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); 401 } 402 if (rb > 0) { 403 /* 404 * Dump double indirect blocks. 405 */ 406 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[1]), 407 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 408 err(1, "bread: %s", disk.d_error); 409 } 410 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", 411 (uintmax_t)inode); 412 DBG_DUMP_IBLK(&sblock, 413 comment, 414 i2blk, 415 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); 416 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 417 sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { 418 ind2ptr = &((ufs1_daddr_t *)(void *)&i2blk)[ind2ctr]; 419 420 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 421 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 422 err(1, "bread: %s", disk.d_error); 423 } 424 snprintf(comment, sizeof(comment), 425 "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode, 426 ind2ctr); 427 DBG_DUMP_IBLK(&sblock, 428 comment, 429 i1blk, 430 (size_t)rb); 431 rb -= howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); 432 } 433 } 434 if (rb > 0) { 435 /* 436 * Dump triple indirect blocks. 437 */ 438 if (bread(&disk, fsbtodb(&sblock, dp.dp1->di_ib[2]), 439 (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { 440 err(1, "bread: %s", disk.d_error); 441 } 442 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", 443 (uintmax_t)inode); 444 #define SQUARE(a) ((a)*(a)) 445 DBG_DUMP_IBLK(&sblock, 446 comment, 447 i3blk, 448 howmany(rb, 449 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))))); 450 #undef SQUARE 451 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, 452 sizeof(ufs1_daddr_t))) && (rb > 0)); ind3ctr++) { 453 ind3ptr = &((ufs1_daddr_t *)(void *)&i3blk)[ind3ctr]; 454 455 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), 456 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 457 err(1, "bread: %s", disk.d_error); 458 } 459 snprintf(comment, sizeof(comment), 460 "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode, 461 ind3ctr); 462 DBG_DUMP_IBLK(&sblock, 463 comment, 464 i2blk, 465 howmany(rb, 466 howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)))); 467 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 468 sizeof(ufs1_daddr_t))) && (rb > 0)); ind2ctr++) { 469 ind2ptr=&((ufs1_daddr_t *)(void *)&i2blk) 470 [ind2ctr]; 471 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 472 (void *)&i1blk, (size_t)sblock.fs_bsize) 473 == -1) { 474 err(1, "bread: %s", disk.d_error); 475 } 476 snprintf(comment, sizeof(comment), 477 "Inode 0x%08jx: indirect 2->%d->%d", 478 (uintmax_t)inode, ind3ctr, ind3ctr); 479 DBG_DUMP_IBLK(&sblock, 480 comment, 481 i1blk, 482 (size_t)rb); 483 rb -= howmany(sblock.fs_bsize, 484 sizeof(ufs1_daddr_t)); 485 } 486 } 487 } 488 489 DBG_LEAVE; 490 return; 491 } 492 493 /* ********************************************** dump_whole_ufs2_inode ***** */ 494 /* 495 * Here we dump a list of all blocks allocated by this inode. We follow 496 * all indirect blocks. 497 */ 498 void 499 dump_whole_ufs2_inode(ino_t inode, int level) 500 { 501 DBG_FUNC("dump_whole_ufs2_inode") 502 union dinodep dp; 503 int rb; 504 unsigned int ind2ctr, ind3ctr; 505 ufs2_daddr_t *ind2ptr, *ind3ptr; 506 char comment[80]; 507 508 DBG_ENTER; 509 510 /* 511 * Read the inode from disk/cache. 512 */ 513 if (getinode(&disk, &dp, inode) == -1) 514 err(1, "getinode: %s", disk.d_error); 515 516 if (dp.dp2->di_nlink == 0) { 517 DBG_LEAVE; 518 return; /* inode not in use */ 519 } 520 521 /* 522 * Dump the main inode structure. 523 */ 524 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); 525 if (level & 0x100) { 526 DBG_DUMP_INO(&sblock, comment, dp.dp2); 527 } 528 529 if (!(level & 0x200)) { 530 DBG_LEAVE; 531 return; 532 } 533 534 /* 535 * Ok, now prepare for dumping all direct and indirect pointers. 536 */ 537 rb = howmany(dp.dp2->di_size, sblock.fs_bsize) - UFS_NDADDR; 538 if (rb > 0) { 539 /* 540 * Dump single indirect block. 541 */ 542 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[0]), 543 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 544 err(1, "bread: %s", disk.d_error); 545 } 546 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", 547 (uintmax_t)inode); 548 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); 549 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); 550 } 551 if (rb > 0) { 552 /* 553 * Dump double indirect blocks. 554 */ 555 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[1]), 556 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 557 err(1, "bread: %s", disk.d_error); 558 } 559 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", 560 (uintmax_t)inode); 561 DBG_DUMP_IBLK(&sblock, 562 comment, 563 i2blk, 564 howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); 565 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 566 sizeof(ufs2_daddr_t))) && (rb>0)); ind2ctr++) { 567 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk)[ind2ctr]; 568 569 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 570 (void *)&i1blk, (size_t)sblock.fs_bsize) == -1) { 571 err(1, "bread: %s", disk.d_error); 572 } 573 snprintf(comment, sizeof(comment), 574 "Inode 0x%08jx: indirect 1->%d", 575 (uintmax_t)inode, ind2ctr); 576 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); 577 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); 578 } 579 } 580 if (rb > 0) { 581 /* 582 * Dump triple indirect blocks. 583 */ 584 if (bread(&disk, fsbtodb(&sblock, dp.dp2->di_ib[2]), 585 (void *)&i3blk, (size_t)sblock.fs_bsize) == -1) { 586 err(1, "bread: %s", disk.d_error); 587 } 588 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", 589 (uintmax_t)inode); 590 #define SQUARE(a) ((a)*(a)) 591 DBG_DUMP_IBLK(&sblock, 592 comment, 593 i3blk, 594 howmany(rb, 595 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))))); 596 #undef SQUARE 597 for (ind3ctr = 0; ((ind3ctr < howmany(sblock.fs_bsize, 598 sizeof(ufs2_daddr_t))) && (rb > 0)); ind3ctr++) { 599 ind3ptr = &((ufs2_daddr_t *)(void *)&i3blk)[ind3ctr]; 600 601 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), 602 (void *)&i2blk, (size_t)sblock.fs_bsize) == -1) { 603 err(1, "bread: %s", disk.d_error); 604 } 605 snprintf(comment, sizeof(comment), 606 "Inode 0x%08jx: indirect 2->%d", 607 (uintmax_t)inode, ind3ctr); 608 DBG_DUMP_IBLK(&sblock, 609 comment, 610 i2blk, 611 howmany(rb, 612 howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)))); 613 for (ind2ctr = 0; ((ind2ctr < howmany(sblock.fs_bsize, 614 sizeof(ufs2_daddr_t))) && (rb > 0)); ind2ctr++) { 615 ind2ptr = &((ufs2_daddr_t *)(void *)&i2blk) [ind2ctr]; 616 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 617 (void *)&i1blk, (size_t)sblock.fs_bsize) 618 == -1) { 619 err(1, "bread: %s", disk.d_error); 620 } 621 snprintf(comment, sizeof(comment), 622 "Inode 0x%08jx: indirect 2->%d->%d", 623 (uintmax_t)inode, ind3ctr, ind3ctr); 624 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); 625 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); 626 } 627 } 628 } 629 630 DBG_LEAVE; 631 return; 632 } 633 634 /* ************************************************************* usage ***** */ 635 /* 636 * Dump a line of usage. 637 */ 638 void 639 usage(void) 640 { 641 DBG_FUNC("usage") 642 643 DBG_ENTER; 644 645 fprintf(stderr, 646 "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] " 647 "[-o outfile]\n" 648 " special | file\n"); 649 650 DBG_LEAVE; 651 exit(1); 652 } 653