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 597