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