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