Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/ffsinfo/ffsinfo.c
39475 views
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
633