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