Merge branch 'vendor/FILE'
[dragonfly.git] / sbin / ffsinfo / ffsinfo.c
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  * $FreeBSD: src/sbin/ffsinfo/ffsinfo.c,v 1.3.2.1 2001/07/16 15:01:56 tomsoft Exp $
40  * $DragonFly: src/sbin/ffsinfo/ffsinfo.c,v 1.5 2007/05/20 23:21:35 dillon Exp $
41  *
42  * @(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz Copyright (c) 1980, 1989, 1993 The Regents of the University of California. All rights reserved.
43  * $FreeBSD: src/sbin/ffsinfo/ffsinfo.c,v 1.3.2.1 2001/07/16 15:01:56 tomsoft Exp $
44  */
45
46 /* ********************************************************** INCLUDES ***** */
47 #include <sys/param.h>
48 #include <sys/diskslice.h>
49 #include <sys/stat.h>
50
51 #include <stdio.h>
52 #include <paths.h>
53 #include <ctype.h>
54 #include <err.h>
55 #include <fcntl.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59
60 #include "debug.h"
61
62 /* *********************************************************** GLOBALS ***** */
63 #ifdef FS_DEBUG
64 int     _dbg_lvl_ = (DL_INFO); /* DL_TRC */
65 #endif /* FS_DEBUG */
66
67 static union {
68         struct fs       fs;
69         char    pad[SBSIZE];
70 } fsun1, fsun2;
71 #define sblock  fsun1.fs
72 #define osblock fsun2.fs
73
74 static union {
75         struct cg       cg;
76         char    pad[MAXBSIZE];
77 } cgun1;
78 #define acg     cgun1.cg
79
80 static char     ablk[MAXBSIZE];
81 static char     i1blk[MAXBSIZE];
82 static char     i2blk[MAXBSIZE];
83 static char     i3blk[MAXBSIZE];
84
85 static struct csum      *fscs;
86
87 /* ******************************************************** PROTOTYPES ***** */
88 static void     rdfs(daddr_t, size_t, void *, int);
89 static void     usage(void);
90 static struct ufs1_dinode       *ginode(ino_t, int);
91 static void     dump_whole_inode(ino_t, int, int);
92
93 /* ************************************************************** rdfs ***** */
94 /*
95  * Here we read some block(s) from disk.
96  */
97 void
98 rdfs(daddr_t bno, size_t size, void *bf, int fsi)
99 {
100         DBG_FUNC("rdfs")
101         ssize_t n;
102
103         DBG_ENTER;
104
105         if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) {
106                 err(33, "rdfs: seek error: %ld", (long)bno);
107         }
108         n = read(fsi, bf, size);
109         if (n != (ssize_t)size) {
110                 err(34, "rdfs: read error: %ld", (long)bno);
111         }
112
113         DBG_LEAVE;
114         return;
115 }
116
117 /* ************************************************************** main ***** */
118 /*
119  * ffsinfo(8) is a tool to dump all metadata of a filesystem. It helps to find
120  * errors is the filesystem much easier. You can run ffsinfo before and  after
121  * an  fsck(8),  and compare the two ascii dumps easy with diff, and  you  see
122  * directly where the problem is. You can control how much detail you want  to
123  * see  with some command line arguments. You can also easy check  the  status
124  * of  a filesystem, like is there is enough space for growing  a  filesystem,
125  * or  how  many active snapshots do we have. It provides much  more  detailed
126  * information  then dumpfs. Snapshots, as they are very new, are  not  really
127  * supported.  They  are just mentioned currently, but it is  planned  to  run
128  * also over active snapshots, to even get that output.
129  */
130 int
131 main(int argc, char **argv)
132 {
133         DBG_FUNC("main")
134         char    *device, *special, *cp;
135         char    ch;
136         size_t  len;
137         struct stat     st;
138         struct partinfo pinfo;
139         int     fsi;
140         struct csum     *dbg_csp;
141         int     dbg_csc;
142         char    dbg_line[80];
143         int     cylno,i;
144         int     cfg_cg, cfg_in, cfg_lv;
145         int     cg_start, cg_stop;
146         ino_t   in;
147         char    *out_file = NULL;
148         int     Lflag=0;
149
150         DBG_ENTER;
151
152         cfg_lv=0xff;
153         cfg_in=-2;
154         cfg_cg=-2;
155
156         while ((ch=getopt(argc, argv, "Lg:i:l:o:")) != -1) {
157                 switch(ch) {
158                 case 'L':
159                         Lflag=1;
160                         break;
161                 case 'g':
162                         cfg_cg=atol(optarg);
163                         if(cfg_cg < -1) {
164                                 usage();
165                         }
166                         break;
167                 case 'i':
168                         cfg_in=atol(optarg);
169                         if(cfg_in < 0) {
170                                 usage();
171                         }
172                         break; 
173                 case 'l':
174                         cfg_lv=atol(optarg);
175                         if(cfg_lv < 0x1||cfg_lv > 0x3ff) {
176                                 usage();
177                         }
178                         break;
179                 case 'o':
180                         if (out_file)
181                                 free(out_file);
182                         out_file = strdup(optarg);
183                         break;
184                 case '?':
185                         /* FALLTHROUGH */
186                 default:
187                         usage();
188                 }
189         }
190         argc -= optind;
191         argv += optind;
192
193         if(argc != 1) {
194                 usage();
195         }
196         device=*argv;
197         
198         /*
199          * Now we try to guess the (raw)device name.
200          */
201         if (0 == strrchr(device, '/') && (stat(device, &st) == -1)) {
202                 /*
203                  * No path prefix was given, so try in that order:
204                  *     /dev/r%s
205                  *     /dev/%s
206                  *     /dev/vinum/r%s
207                  *     /dev/vinum/%s.
208                  * 
209                  * FreeBSD now doesn't distinguish between raw and  block
210                  * devices any longer, but it should still work this way.
211                  */
212                 len=strlen(device)+strlen(_PATH_DEV)+2+strlen("vinum/");
213                 special=(char *)malloc(len);
214                 if(special == NULL) {
215                         errx(1, "malloc failed");
216                 }
217                 snprintf(special, len, "%sr%s", _PATH_DEV, device);
218                 if (stat(special, &st) == -1) {
219                         snprintf(special, len, "%s%s", _PATH_DEV, device);
220                         if (stat(special, &st) == -1) {
221                                 snprintf(special, len, "%svinum/r%s",
222                                     _PATH_DEV, device);
223                                 if (stat(special, &st) == -1) {
224                                         /*
225                                          * For now this is the 'last resort'.
226                                          */
227                                         snprintf(special, len, "%svinum/%s",
228                                             _PATH_DEV, device);
229                                 }
230                         }
231                 }
232                 device = special;
233         }
234
235         /*
236          * Open our device for reading.
237          */
238         fsi = open(device, O_RDONLY);
239         if (fsi < 0) {
240                 err(1, "%s", device);
241         }
242
243         stat(device, &st);
244         
245         if(S_ISREG(st.st_mode)) { /* label check not supported for files */
246                 Lflag=1;
247         }
248
249         if(!Lflag) {
250                 /*
251                  * Try  to read a label and gess the slice if not  specified.
252                  * This code should guess the right thing and avaid to bother
253                  * the user user with the task of specifying the option -v on
254                  * vinum volumes.
255                  */
256                 cp = device+strlen(device)-1;
257                 if (ioctl(fsi, DIOCGPART, &pinfo) < 0) {
258                         pinfo.media_size = st.st_size;
259                         pinfo.media_blksize = DEV_BSIZE;
260                         pinfo.media_blocks = pinfo.media_size / DEV_BSIZE;
261                 }
262         
263                 /*
264                  * Check if that partition looks suited for dumping.
265                  */
266                 if (pinfo.media_size == 0) {
267                         errx(1, "partition is unavailable");
268                 }
269         }
270
271         /*
272          * Read the current superblock.
273          */
274         rdfs((daddr_t)(SBOFF/DEV_BSIZE), (size_t)SBSIZE, (void *)&sblock, fsi);
275         if (sblock.fs_magic != FS_MAGIC) {
276                 errx(1, "superblock not recognized");
277         }
278
279         DBG_OPEN(out_file); /* already here we need a superblock */
280
281         if(cfg_lv & 0x001) {
282                 DBG_DUMP_FS(&sblock,
283                     "primary sblock");
284         }
285
286         /*
287          * Determine here what cylinder groups to dump.
288          */
289         if(cfg_cg==-2) {
290                 cg_start=0;
291                 cg_stop=sblock.fs_ncg;
292         } else if (cfg_cg==-1) {
293                 cg_start=sblock.fs_ncg-1;
294                 cg_stop=sblock.fs_ncg;
295         } else if (cfg_cg<sblock.fs_ncg) {
296                 cg_start=cfg_cg;
297                 cg_stop=cfg_cg+1;
298         } else {
299                 cg_start=sblock.fs_ncg;
300                 cg_stop=sblock.fs_ncg;
301         }
302
303         if (cfg_lv & 0x004) {
304                 fscs = (struct csum *)calloc((size_t)1,
305                     (size_t)sblock.fs_cssize);
306                 if(fscs == NULL) {
307                         errx(1, "calloc failed");
308                 }
309
310                 /*
311                  * Get the cylinder summary into the memory ...
312                  */
313                 for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
314                         rdfs(fsbtodb(&sblock, sblock.fs_csaddr +
315                             numfrags(&sblock, i)), (size_t)(sblock.fs_cssize-i<
316                             sblock.fs_bsize ? sblock.fs_cssize - i :
317                             sblock.fs_bsize), (void *)(((char *)fscs)+i), fsi);
318                 }
319
320                 dbg_csp=fscs;
321                 /*
322                  * ... and dump it.
323                  */
324                 for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) {
325                         snprintf(dbg_line, sizeof(dbg_line),
326                             "%d. csum in fscs", dbg_csc);
327                         DBG_DUMP_CSUM(&sblock,
328                             dbg_line,
329                             dbg_csp++);
330                 }
331         }
332
333         /*
334          * For each requested cylinder group ...
335          */
336         for(cylno=cg_start; cylno<cg_stop; cylno++) {
337                 snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno);
338                 if(cfg_lv & 0x002) {
339                         /*
340                          * ... dump the superblock copies ...
341                          */
342                         rdfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
343                             (size_t)SBSIZE, (void *)&osblock, fsi);
344                         DBG_DUMP_FS(&osblock,
345                             dbg_line);
346                 }
347                 /*
348                  * ... read the cylinder group and dump whatever was requested.
349                  */
350                 rdfs(fsbtodb(&sblock, cgtod(&sblock, cylno)),
351                     (size_t)sblock.fs_cgsize, (void *)&acg, fsi);
352                 if(cfg_lv & 0x008) {
353                         DBG_DUMP_CG(&sblock,
354                             dbg_line,
355                             &acg);
356                 }
357                 if(cfg_lv & 0x010) {
358                         DBG_DUMP_INMAP(&sblock,
359                             dbg_line,
360                             &acg);
361                 }
362                 if(cfg_lv & 0x020) {
363                         DBG_DUMP_FRMAP(&sblock,
364                             dbg_line,
365                             &acg);
366                 }
367                 if(cfg_lv & 0x040) {
368                         DBG_DUMP_CLMAP(&sblock,
369                             dbg_line,
370                             &acg);
371                         DBG_DUMP_CLSUM(&sblock,
372                             dbg_line,
373                             &acg);
374                 }
375                 if(cfg_lv & 0x080) {
376                         DBG_DUMP_SPTBL(&sblock,
377                             dbg_line,
378                             &acg);
379                 }
380         }
381         /*
382          * Dump the requested inode(s).
383          */
384         if(cfg_in != -2) {
385                 dump_whole_inode((ino_t)cfg_in, fsi, cfg_lv);
386         } else {
387                 for(in=cg_start*sblock.fs_ipg; in<(ino_t)cg_stop*sblock.fs_ipg;
388                     in++) {
389                         dump_whole_inode(in, fsi, cfg_lv);
390                 }
391         }
392
393         DBG_CLOSE;
394
395         close(fsi);
396
397         DBG_LEAVE;
398         return 0;
399 }
400
401 /* ************************************************** dump_whole_inode ***** */
402 /*
403  * Here we dump a list of all blocks allocated by this inode. We follow
404  * all indirect blocks.
405  */
406 void
407 dump_whole_inode(ino_t inode, int fsi, int level)
408 {
409         DBG_FUNC("dump_whole_inode")
410         struct ufs1_dinode      *ino;
411         int     rb;
412         unsigned int    ind2ctr, ind3ctr;
413         ufs_daddr_t     *ind2ptr, *ind3ptr;
414         char    comment[80];
415         
416         DBG_ENTER;
417
418         /*
419          * Read the inode from disk/cache.
420          */
421         ino=ginode(inode, fsi);
422
423         if(ino->di_nlink==0) {
424                 DBG_LEAVE;
425                 return; /* inode not in use */
426         }
427
428         /*
429          * Dump the main inode structure.
430          */
431         snprintf(comment, sizeof(comment), "Inode 0x%08jx", (uintmax_t)inode);
432         if (level & 0x100) {
433                 DBG_DUMP_INO(&sblock,
434                     comment,
435                     ino);
436         }
437
438         if (!(level & 0x200)) {
439                 DBG_LEAVE;
440                 return;
441         }
442
443         /*
444          * Ok, now prepare for dumping all direct and indirect pointers.
445          */
446         rb=howmany(ino->di_size, sblock.fs_bsize)-NDADDR;
447         if(rb>0) {
448                 /*
449                  * Dump single indirect block.
450                  */
451                 rdfs(fsbtodb(&sblock, ino->di_ib[0]), (size_t)sblock.fs_bsize,
452                     (void *)&i1blk, fsi);
453                 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 0",
454                     (uintmax_t)inode);
455                 DBG_DUMP_IBLK(&sblock,
456                     comment,
457                     i1blk,
458                     (size_t)rb);
459                 rb-=howmany(sblock.fs_bsize, sizeof(ufs_daddr_t));
460         }
461         if(rb>0) {
462                 /*
463                  * Dump double indirect blocks.
464                  */
465                 rdfs(fsbtodb(&sblock, ino->di_ib[1]), (size_t)sblock.fs_bsize,
466                     (void *)&i2blk, fsi);
467                 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 1",
468                     (uintmax_t)inode);
469                 DBG_DUMP_IBLK(&sblock,
470                     comment,
471                     i2blk,
472                     howmany(rb, howmany(sblock.fs_bsize, sizeof(ufs_daddr_t))));
473                 for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
474                     sizeof(ufs_daddr_t)))&&(rb>0)); ind2ctr++) {
475                         ind2ptr=&((ufs_daddr_t *)(void *)&i2blk)[ind2ctr];
476
477                         rdfs(fsbtodb(&sblock, *ind2ptr),
478                             (size_t)sblock.fs_bsize, (void *)&i1blk, fsi);
479                         snprintf(comment, sizeof(comment),
480                             "Inode 0x%08jx: indirect 1->%d", (uintmax_t)inode,
481                             ind2ctr);
482                         DBG_DUMP_IBLK(&sblock,
483                             comment,
484                             i1blk,
485                             (size_t)rb);
486                         rb-=howmany(sblock.fs_bsize, sizeof(ufs_daddr_t));
487                 }
488         }
489         if(rb>0) {
490                 /*
491                  * Dump triple indirect blocks.
492                  */
493                 rdfs(fsbtodb(&sblock, ino->di_ib[2]), (size_t)sblock.fs_bsize,
494                     (void *)&i3blk, fsi);
495                 snprintf(comment, sizeof(comment), "Inode 0x%08jx: indirect 2",
496                     (uintmax_t)inode);
497 #define SQUARE(a) ((a)*(a))
498                 DBG_DUMP_IBLK(&sblock,
499                     comment,
500                     i3blk,
501                     howmany(rb,
502                       SQUARE(howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)))));
503 #undef SQUARE
504                 for(ind3ctr=0; ((ind3ctr < howmany(sblock.fs_bsize,
505                     sizeof(ufs_daddr_t)))&&(rb>0)); ind3ctr ++) {
506                         ind3ptr=&((ufs_daddr_t *)(void *)&i3blk)[ind3ctr];
507
508                         rdfs(fsbtodb(&sblock, *ind3ptr),
509                             (size_t)sblock.fs_bsize, (void *)&i2blk, fsi);
510                         snprintf(comment, sizeof(comment),
511                             "Inode 0x%08jx: indirect 2->%d", (uintmax_t)inode,
512                             ind3ctr);
513                         DBG_DUMP_IBLK(&sblock,
514                             comment,
515                             i2blk,
516                             howmany(rb,
517                               howmany(sblock.fs_bsize, sizeof(ufs_daddr_t))));
518                         for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize,
519                             sizeof(ufs_daddr_t)))&&(rb>0)); ind2ctr ++) {
520                                 ind2ptr=&((ufs_daddr_t *)(void *)&i2blk)
521                                     [ind2ctr];
522                                 rdfs(fsbtodb(&sblock, *ind2ptr),
523                                     (size_t)sblock.fs_bsize, (void *)&i1blk,
524                                     fsi);
525                                 snprintf(comment, sizeof(comment),
526                                     "Inode 0x%08jx: indirect 2->%d->%d",
527                                     (uintmax_t)inode, ind3ctr, ind3ctr);
528                                 DBG_DUMP_IBLK(&sblock,
529                                     comment,
530                                     i1blk,
531                                     (size_t)rb);
532                                 rb-=howmany(sblock.fs_bsize,
533                                     sizeof(ufs_daddr_t));
534                         }
535                 }
536         }
537
538         DBG_LEAVE;
539         return;
540 }
541
542 /* ************************************************************* usage ***** */
543 /*
544  * Dump a line of usage.
545  */
546 void
547 usage(void)
548 {
549         DBG_FUNC("usage")       
550
551         DBG_ENTER;
552
553         fprintf(stderr,
554             "usage: ffsinfo [-L] [-g cylgrp] [-i inode] [-l level] "
555             "[-o outfile]\n"
556             "               special | file\n");
557
558         DBG_LEAVE;
559         exit(1);
560 }
561
562 /* ************************************************************ ginode ***** */
563 /*
564  * This function provides access to an individual inode. We find out in which
565  * block  the  requested inode is located, read it from disk if  needed,  and
566  * return  the pointer into that block. We maintain a cache of one  block  to
567  * not  read the same block again and again if we iterate linearly  over  all
568  * inodes.
569  */
570 struct ufs1_dinode *
571 ginode(ino_t inumber, int fsi)
572 {
573         DBG_FUNC("ginode")
574         ufs_daddr_t     iblk;
575         static ino_t    startinum=0;    /* first inode in cached block */
576         struct ufs1_dinode      *pi;
577
578         DBG_ENTER;
579
580         pi=(struct ufs1_dinode *)(void *)ablk;
581         if (startinum == 0 || inumber < startinum ||
582             inumber >= startinum + INOPB(&sblock)) {
583                 /*
584                  * The block needed is not cached, so we have to read it from
585                  * disk now.
586                  */
587                 iblk = ino_to_fsba(&sblock, inumber);
588                 rdfs(fsbtodb(&sblock, iblk), (size_t)sblock.fs_bsize,
589                     (void *)&ablk, fsi);
590                 startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock);
591         }
592
593         DBG_LEAVE;
594         return (&(pi[inumber % INOPB(&sblock)]));
595 }
596