1 /* 2 * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz 3 * Copyright (c) 1980, 1989, 1993 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 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. 25 * 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 36 * SUCH DAMAGE. 37 * 38 * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.4 2000/12/12 19:30:55 tomsoft Exp $ 39 * 40 */ 41 42 #ifndef lint 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"; 47 #endif /* not lint */ 48 49 #ifndef lint 50 static const char rcsid[] = 51 "$FreeBSD$"; 52 #endif /* not lint */ 53 54 /* ********************************************************** INCLUDES ***** */ 55 #include <sys/param.h> 56 #include <sys/disklabel.h> 57 #include <sys/mount.h> 58 #include <sys/stat.h> 59 60 #include <ufs/ufs/ufsmount.h> 61 #include <ufs/ufs/dinode.h> 62 #include <ufs/ffs/fs.h> 63 64 #include <ctype.h> 65 #include <err.h> 66 #include <errno.h> 67 #include <fcntl.h> 68 #include <libufs.h> 69 #include <paths.h> 70 #include <stdint.h> 71 #include <stdio.h> 72 #include <stdlib.h> 73 #include <string.h> 74 #include <unistd.h> 75 76 #include "debug.h" 77 78 /* *********************************************************** GLOBALS ***** */ 79 #ifdef FS_DEBUG 80 int _dbg_lvl_ = (DL_INFO); /* DL_TRC */ 81 #endif /* FS_DEBUG */ 82 83 static struct uufsd disk; 84 85 #define sblock disk.d_fs 86 #define acg disk.d_cg 87 88 static union { 89 struct fs fs; 90 char pad[SBLOCKSIZE]; 91 } fsun; 92 93 #define osblock fsun.fs 94 95 static char i1blk[MAXBSIZE]; 96 static char i2blk[MAXBSIZE]; 97 static char i3blk[MAXBSIZE]; 98 99 static struct csum *fscs; 100 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); 105 106 #define DUMP_WHOLE_INODE(A,B) \ 107 ( disk.d_ufs == 1 \ 108 ? dump_whole_ufs1_inode((A),(B)) : dump_whole_ufs2_inode((A),(B)) ) 109 110 /* ************************************************************** main ***** */ 111 /* 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. 122 */ 123 int 124 main(int argc, char **argv) 125 { 126 DBG_FUNC("main") 127 char *device, *special; 128 int ch; 129 size_t len; 130 struct stat st; 131 struct csum *dbg_csp; 132 int dbg_csc; 133 char dbg_line[80]; 134 int cylno,i; 135 int cfg_cg, cfg_in, cfg_lv; 136 int cg_start, cg_stop; 137 ino_t in; 138 char *out_file; 139 140 DBG_ENTER; 141 142 cfg_lv = 0xff; 143 cfg_in = -2; 144 cfg_cg = -2; 145 out_file = strdup("-"); 146 147 while ((ch = getopt(argc, argv, "g:i:l:o:")) != -1) { 148 switch (ch) { 149 case 'g': 150 cfg_cg = strtol(optarg, NULL, 0); 151 if (errno == EINVAL || errno == ERANGE) 152 err(1, "%s", optarg); 153 if (cfg_cg < -1) 154 usage(); 155 break; 156 case 'i': 157 cfg_in = strtol(optarg, NULL, 0); 158 if (errno == EINVAL || errno == ERANGE) 159 err(1, "%s", optarg); 160 if (cfg_in < 0) 161 usage(); 162 break; 163 case 'l': 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) 168 usage(); 169 break; 170 case 'o': 171 free(out_file); 172 out_file = strdup(optarg); 173 if (out_file == NULL) 174 errx(1, "strdup failed"); 175 break; 176 case '?': 177 /* FALLTHROUGH */ 178 default: 179 usage(); 180 } 181 } 182 argc -= optind; 183 argv += optind; 184 185 if (argc != 1) 186 usage(); 187 device = *argv; 188 189 /* 190 * Now we try to guess the (raw)device name. 191 */ 192 if (0 == strrchr(device, '/') && stat(device, &st) == -1) { 193 /*- 194 * No path prefix was given, so try in this order: 195 * /dev/r%s 196 * /dev/%s 197 * /dev/vinum/r%s 198 * /dev/vinum/%s. 199 * 200 * FreeBSD now doesn't distinguish between raw and block 201 * devices any longer, but it should still work this way. 202 */ 203 len = strlen(device) + strlen(_PATH_DEV) + 2 + strlen("vinum/"); 204 special = (char *)malloc(len); 205 if (special == NULL) 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", 212 _PATH_DEV, device); 213 if (stat(special, &st) == -1) 214 /* For now this is the 'last resort' */ 215 snprintf(special, len, "%svinum/%s", 216 _PATH_DEV, device); 217 } 218 } 219 device = special; 220 } 221 222 if (ufs_disk_fillout(&disk, device) == -1) 223 err(1, "ufs_disk_fillout(%s) failed: %s", device, disk.d_error); 224 225 DBG_OPEN(out_file); /* already here we need a superblock */ 226 227 if (cfg_lv & 0x001) 228 DBG_DUMP_FS(&sblock, "primary sblock"); 229 230 /* Determine here what cylinder groups to dump */ 231 if (cfg_cg==-2) { 232 cg_start = 0; 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) { 238 cg_start = cfg_cg; 239 cg_stop = cfg_cg + 1; 240 } else { 241 cg_start = sblock.fs_ncg; 242 cg_stop = sblock.fs_ncg; 243 } 244 245 if (cfg_lv & 0x004) { 246 fscs = (struct csum *)calloc((size_t)1, 247 (size_t)sblock.fs_cssize); 248 if (fscs == NULL) 249 errx(1, "calloc failed"); 250 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); 259 } 260 261 dbg_csp = fscs; 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, 267 dbg_line, 268 dbg_csp++); 269 } 270 } 271 272 if (cfg_lv & 0xf8) { 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); 283 } 284 285 /* 286 * Read the cylinder group and dump whatever was 287 * requested. 288 */ 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); 293 294 if (cfg_lv & 0x008) 295 DBG_DUMP_CG(&sblock, dbg_line, &acg); 296 if (cfg_lv & 0x010) 297 DBG_DUMP_INMAP(&sblock, dbg_line, &acg); 298 if (cfg_lv & 0x020) 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); 303 } 304 #ifdef NOT_CURRENTLY 305 /* 306 * See the comment in sbin/growfs/debug.c for why this 307 * is currently disabled, and what needs to be done to 308 * re-enable it. 309 */ 310 if (disk.d_ufs == 1 && cfg_lv & 0x080) 311 DBG_DUMP_SPTBL(&sblock, dbg_line, &acg); 312 #endif 313 } 314 } 315 316 if (cfg_lv & 0x300) { 317 /* Dump the requested inode(s) */ 318 if (cfg_in != -2) 319 DUMP_WHOLE_INODE((ino_t)cfg_in, cfg_lv); 320 else { 321 for (in = cg_start * sblock.fs_ipg; 322 in < (ino_t)cg_stop * sblock.fs_ipg; 323 in++) 324 DUMP_WHOLE_INODE(in, cfg_lv); 325 } 326 } 327 328 DBG_CLOSE; 329 DBG_LEAVE; 330 331 return 0; 332 } 333 334 /* ********************************************** dump_whole_ufs1_inode ***** */ 335 /* 336 * Here we dump a list of all blocks allocated by this inode. We follow 337 * all indirect blocks. 338 */ 339 void 340 dump_whole_ufs1_inode(ino_t inode, int level) 341 { 342 DBG_FUNC("dump_whole_ufs1_inode") 343 struct ufs1_dinode *ino; 344 int rb, mode; 345 unsigned int ind2ctr, ind3ctr; 346 ufs1_daddr_t *ind2ptr, *ind3ptr; 347 char comment[80]; 348 349 DBG_ENTER; 350 351 /* 352 * Read the inode from disk/cache. 353 */ 354 if (getino(&disk, (void **)&ino, inode, &mode) == -1) 355 err(1, "getino: %s", disk.d_error); 356 357 if(ino->di_nlink==0) { 358 DBG_LEAVE; 359 return; /* inode not in use */ 360 } 361 362 /* 363 * Dump the main inode structure. 364 */ 365 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); 366 if (level & 0x100) { 367 DBG_DUMP_INO(&sblock, 368 comment, 369 ino); 370 } 371 372 if (!(level & 0x200)) { 373 DBG_LEAVE; 374 return; 375 } 376 377 /* 378 * Ok, now prepare for dumping all direct and indirect pointers. 379 */ 380 rb = howmany(ino->di_size, sblock.fs_bsize) - UFS_NDADDR; 381 if(rb>0) { 382 /* 383 * Dump single indirect block. 384 */ 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); 388 } 389 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", 390 (uintmax_t)inode); 391 DBG_DUMP_IBLK(&sblock, 392 comment, 393 i1blk, 394 (size_t)rb); 395 rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); 396 } 397 if(rb>0) { 398 /* 399 * Dump double indirect blocks. 400 */ 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); 404 } 405 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", 406 (uintmax_t)inode); 407 DBG_DUMP_IBLK(&sblock, 408 comment, 409 i2blk, 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]; 414 415 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, 416 (size_t)sblock.fs_bsize) == -1) { 417 err(1, "bread: %s", disk.d_error); 418 } 419 snprintf(comment, sizeof(comment), 420 "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode, 421 ind2ctr); 422 DBG_DUMP_IBLK(&sblock, 423 comment, 424 i1blk, 425 (size_t)rb); 426 rb-=howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t)); 427 } 428 } 429 if(rb>0) { 430 /* 431 * Dump triple indirect blocks. 432 */ 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); 436 } 437 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", 438 (uintmax_t)inode); 439 #define SQUARE(a) ((a)*(a)) 440 DBG_DUMP_IBLK(&sblock, 441 comment, 442 i3blk, 443 howmany(rb, 444 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs1_daddr_t))))); 445 #undef SQUARE 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]; 449 450 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk, 451 (size_t)sblock.fs_bsize) == -1) { 452 err(1, "bread: %s", disk.d_error); 453 } 454 snprintf(comment, sizeof(comment), 455 "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode, 456 ind3ctr); 457 DBG_DUMP_IBLK(&sblock, 458 comment, 459 i2blk, 460 howmany(rb, 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) 465 [ind2ctr]; 466 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), 467 (void *)&i1blk, (size_t)sblock.fs_bsize) 468 == -1) { 469 err(1, "bread: %s", disk.d_error); 470 } 471 snprintf(comment, sizeof(comment), 472 "Inode 0x%08jx: indirect 2->%d->%d", 473 (uintmax_t)inode, ind3ctr, ind3ctr); 474 DBG_DUMP_IBLK(&sblock, 475 comment, 476 i1blk, 477 (size_t)rb); 478 rb-=howmany(sblock.fs_bsize, 479 sizeof(ufs1_daddr_t)); 480 } 481 } 482 } 483 484 DBG_LEAVE; 485 return; 486 } 487 488 /* ********************************************** dump_whole_ufs2_inode ***** */ 489 /* 490 * Here we dump a list of all blocks allocated by this inode. We follow 491 * all indirect blocks. 492 */ 493 void 494 dump_whole_ufs2_inode(ino_t inode, int level) 495 { 496 DBG_FUNC("dump_whole_ufs2_inode") 497 struct ufs2_dinode *ino; 498 int rb, mode; 499 unsigned int ind2ctr, ind3ctr; 500 ufs2_daddr_t *ind2ptr, *ind3ptr; 501 char comment[80]; 502 503 DBG_ENTER; 504 505 /* 506 * Read the inode from disk/cache. 507 */ 508 if (getino(&disk, (void **)&ino, inode, &mode) == -1) 509 err(1, "getino: %s", disk.d_error); 510 511 if (ino->di_nlink == 0) { 512 DBG_LEAVE; 513 return; /* inode not in use */ 514 } 515 516 /* 517 * Dump the main inode structure. 518 */ 519 snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode); 520 if (level & 0x100) { 521 DBG_DUMP_INO(&sblock, comment, ino); 522 } 523 524 if (!(level & 0x200)) { 525 DBG_LEAVE; 526 return; 527 } 528 529 /* 530 * Ok, now prepare for dumping all direct and indirect pointers. 531 */ 532 rb = howmany(ino->di_size, sblock.fs_bsize) - UFS_NDADDR; 533 if (rb > 0) { 534 /* 535 * Dump single indirect block. 536 */ 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); 540 } 541 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0", 542 (uintmax_t)inode); 543 DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); 544 rb -= howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t)); 545 } 546 if (rb > 0) { 547 /* 548 * Dump double indirect blocks. 549 */ 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); 553 } 554 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1", 555 (uintmax_t)inode); 556 DBG_DUMP_IBLK(&sblock, 557 comment, 558 i2blk, 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]; 563 564 if (bread(&disk, fsbtodb(&sblock, *ind2ptr), (void *)&i1blk, 565 (size_t)sblock.fs_bsize) == -1) { 566 err(1, "bread: %s", disk.d_error); 567 } 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)); 573 } 574 } 575 if (rb > 0) { 576 /* 577 * Dump triple indirect blocks. 578 */ 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); 582 } 583 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2", 584 (uintmax_t)inode); 585 #define SQUARE(a) ((a)*(a)) 586 DBG_DUMP_IBLK(&sblock, 587 comment, 588 i3blk, 589 howmany(rb, 590 SQUARE(howmany(sblock.fs_bsize, sizeof(ufs2_daddr_t))))); 591 #undef SQUARE 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]; 595 596 if (bread(&disk, fsbtodb(&sblock, *ind3ptr), (void *)&i2blk, 597 (size_t)sblock.fs_bsize) == -1) { 598 err(1, "bread: %s", disk.d_error); 599 } 600 snprintf(comment, sizeof(comment), 601 "Inode 0x%08jx: indirect 2->%d", 602 (uintmax_t)inode, ind3ctr); 603 DBG_DUMP_IBLK(&sblock, 604 comment, 605 i2blk, 606 howmany(rb, 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); 614 } 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)); 620 } 621 } 622 } 623 624 DBG_LEAVE; 625 return; 626 } 627 628 /* ************************************************************* usage ***** */ 629 /* 630 * Dump a line of usage. 631 */ 632 void 633 usage(void) 634 { 635 DBG_FUNC("usage") 636 637 DBG_ENTER; 638 639 fprintf(stderr, 640 "usage: ffsinfo [-g cylinder_group] [-i inode] [-l level] " 641 "[-o outfile]\n" 642 " special | file\n"); 643 644 DBG_LEAVE; 645 exit(1); 646 } 647