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
main(int argc,char ** argv)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
dump_whole_ufs1_inode(ino_t inode,int level)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
dump_whole_ufs2_inode(ino_t inode,int level)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
usage(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