/* * ffsinfo(8) is a tool to dump all metadata of a filesystem. It helps to find * errors is the filesystem much easier. You can run ffsinfo before and after * an fsck(8), and compare the two ascii dumps easy with diff, and you see * directly where the problem is. You can control how much detail you want to * see with some command line arguments. You can also easy check the status * of a filesystem, like is there is enough space for growing a filesystem, * or how many active snapshots do we have. It provides much more detailed * information then dumpfs. Snapshots, as they are very new, are not really * supported. They are just mentioned currently, but it is planned to run * also over active snapshots, to even get that output. */ int main(int argc, char **argv) { char *device, *special; char ch; size_t len; struct stat st; struct partinfo pinfo; int fsi; struct csum *dbg_csp; int dbg_csc; char dbg_line[80]; int cylno,i; int cfg_cg, cfg_in, cfg_lv; int cg_start, cg_stop; ino_t in; char *out_file = NULL; int Lflag=0; DBG_ENTER; cfg_lv=0xff; cfg_in=-2; cfg_cg=-2; while ((ch=getopt(argc, argv, "Lg:i:l:o:")) != -1) { switch(ch) { case 'L': Lflag=1; break; case 'g': cfg_cg=atol(optarg); if(cfg_cg < -1) { usage(); } break; case 'i': cfg_in=atol(optarg); if(cfg_in < 0) { usage(); } break; case 'l': cfg_lv=atol(optarg); if(cfg_lv < 0x1||cfg_lv > 0x3ff) { usage(); } break; case 'o': if (out_file) free(out_file); out_file = strdup(optarg); break; case '?': /* FALLTHROUGH */ default: usage(); } } argc -= optind; argv += optind; if(argc != 1) { usage(); } device=*argv; /* * Now we try to guess the (raw)device name. */ if (0 == strrchr(device, '/') && (stat(device, &st) == -1)) { /* * No path prefix was given, so try in that order: * /dev/r%s * /dev/%s * /dev/vinum/r%s * /dev/vinum/%s. * * FreeBSD now doesn't distinguish between raw and block * devices any longer, but it should still work this way. */ len=strlen(device)+strlen(_PATH_DEV)+2+strlen("vinum/"); special=(char *)malloc(len); if(special == NULL) { errx(1, "malloc failed"); } snprintf(special, len, "%sr%s", _PATH_DEV, device); if (stat(special, &st) == -1) { snprintf(special, len, "%s%s", _PATH_DEV, device); if (stat(special, &st) == -1) { snprintf(special, len, "%svinum/r%s", _PATH_DEV, device); if (stat(special, &st) == -1) { /* * For now this is the 'last resort'. */ snprintf(special, len, "%svinum/%s", _PATH_DEV, device); } } } device = special; } /* * Open our device for reading. */ fsi = open(device, O_RDONLY); if (fsi < 0) { err(1, "%s", device); } stat(device, &st); if(S_ISREG(st.st_mode)) { /* label check not supported for files */ Lflag=1; } if(!Lflag) { /* * Try to read a label and gess the slice if not specified. * This code should guess the right thing and avaid to bother * the user user with the task of specifying the option -v on * vinum volumes. */ if (ioctl(fsi, DIOCGPART, &pinfo) < 0) { pinfo.media_size = st.st_size; pinfo.media_blksize = DEV_BSIZE; pinfo.media_blocks = pinfo.media_size / DEV_BSIZE; } /* * Check if that partition looks suited for dumping. */ if (pinfo.media_size == 0) { errx(1, "partition is unavailable"); } } /* * Read the current superblock. */ rdfs((daddr_t)(SBOFF/DEV_BSIZE), (size_t)SBSIZE, &sblock, fsi); if (sblock.fs_magic != FS_MAGIC) { errx(1, "superblock not recognized"); } DBG_OPEN(out_file); /* already here we need a superblock */ if(cfg_lv & 0x001) { DBG_DUMP_FS(&sblock, "primary sblock"); } /* * Determine here what cylinder groups to dump. */ if(cfg_cg==-2) { cg_start=0; cg_stop=sblock.fs_ncg; } else if (cfg_cg==-1) { cg_start=sblock.fs_ncg-1; cg_stop=sblock.fs_ncg; } else if (cfg_cg<sblock.fs_ncg) { cg_start=cfg_cg; cg_stop=cfg_cg+1; } else { cg_start=sblock.fs_ncg; cg_stop=sblock.fs_ncg; } if (cfg_lv & 0x004) { fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); if(fscs == NULL) { errx(1, "calloc failed"); } /* * Get the cylinder summary into the memory ... */ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { rdfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)), (size_t)(sblock.fs_cssize-i< sblock.fs_bsize ? sblock.fs_cssize - i : sblock.fs_bsize), (void *)(((char *)fscs)+i), fsi); } dbg_csp=fscs; /* * ... and dump it. */ for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) { snprintf(dbg_line, sizeof(dbg_line), "%d. csum in fscs", dbg_csc); DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++); } } /* * For each requested cylinder group ... */ for(cylno=cg_start; cylno<cg_stop; cylno++) { snprintf(dbg_line, sizeof(dbg_line), "cgr %d", cylno); if(cfg_lv & 0x002) { /* * ... dump the superblock copies ... */ rdfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), (size_t)SBSIZE, &osblock, fsi); DBG_DUMP_FS(&osblock, dbg_line); } /* * ... read the cylinder group and dump whatever was requested. */ rdfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), (size_t)sblock.fs_cgsize, &acg, fsi); if(cfg_lv & 0x008) { DBG_DUMP_CG(&sblock, dbg_line, &acg); } if(cfg_lv & 0x010) { DBG_DUMP_INMAP(&sblock, dbg_line, &acg); } if(cfg_lv & 0x020) { DBG_DUMP_FRMAP(&sblock, dbg_line, &acg); } if(cfg_lv & 0x040) { DBG_DUMP_CLMAP(&sblock, dbg_line, &acg); DBG_DUMP_CLSUM(&sblock, dbg_line, &acg); } if(cfg_lv & 0x080) { DBG_DUMP_SPTBL(&sblock, dbg_line, &acg); } } /* * Dump the requested inode(s). */ if(cfg_in != -2) { dump_whole_inode((ino_t)cfg_in, fsi, cfg_lv); } else { for(in=cg_start*sblock.fs_ipg; in<(ino_t)cg_stop*sblock.fs_ipg; in++) { dump_whole_inode(in, fsi, cfg_lv); } } DBG_CLOSE; close(fsi); DBG_LEAVE; return 0; }
/* * Here we actually start growing the file system. We basically read the * cylinder summary from the first cylinder group as we want to update * this on the fly during our various operations. First we handle the * changes in the former last cylinder group. Afterwards we create all new * cylinder groups. Now we handle the cylinder group containing the * cylinder summary which might result in a relocation of the whole * structure. In the end we write back the updated cylinder summary, the * new superblock, and slightly patched versions of the super block * copies. */ static void growfs(int fsi, int fso, unsigned int Nflag) { DBG_FUNC("growfs") time_t modtime; uint cylno; int i, j, width; char tmpbuf[100]; DBG_ENTER; time(&modtime); /* * Get the cylinder summary into the memory. */ fscs = (struct csum *)calloc((size_t)1, (size_t)sblock.fs_cssize); if (fscs == NULL) errx(1, "calloc failed"); for (i = 0; i < osblock.fs_cssize; i += osblock.fs_bsize) { rdfs(fsbtodb(&osblock, osblock.fs_csaddr + numfrags(&osblock, i)), (size_t)MIN(osblock.fs_cssize - i, osblock.fs_bsize), (void *)(((char *)fscs) + i), fsi); } #ifdef FS_DEBUG { struct csum *dbg_csp; u_int32_t dbg_csc; char dbg_line[80]; dbg_csp = fscs; for (dbg_csc = 0; dbg_csc < osblock.fs_ncg; dbg_csc++) { snprintf(dbg_line, sizeof(dbg_line), "%d. old csum in old location", dbg_csc); DBG_DUMP_CSUM(&osblock, dbg_line, dbg_csp++); } } #endif /* FS_DEBUG */ DBG_PRINT0("fscs read\n"); /* * Do all needed changes in the former last cylinder group. */ updjcg(osblock.fs_ncg - 1, modtime, fsi, fso, Nflag); /* * Dump out summary information about file system. */ #ifdef FS_DEBUG #define B2MBFACTOR (1 / (1024.0 * 1024.0)) printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n", (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR, (intmax_t)fsbtodb(&sblock, sblock.fs_size), sblock.fs_bsize, sblock.fs_fsize); printf("\tusing %d cylinder groups of %.2fMB, %d blks, %d inodes.\n", sblock.fs_ncg, (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR, sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg); if (sblock.fs_flags & FS_DOSOFTDEP) printf("\twith soft updates\n"); #undef B2MBFACTOR #endif /* FS_DEBUG */ /* * Now build the cylinders group blocks and * then print out indices of cylinder groups. */ printf("super-block backups (for fsck_ffs -b #) at:\n"); i = 0; width = charsperline(); /* * Iterate for only the new cylinder groups. */ for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) { initcg(cylno, modtime, fso, Nflag); j = sprintf(tmpbuf, " %jd%s", (intmax_t)fsbtodb(&sblock, cgsblock(&sblock, cylno)), cylno < (sblock.fs_ncg - 1) ? "," : "" ); if (i + j >= width) { printf("\n"); i = 0; } i += j; printf("%s", tmpbuf); fflush(stdout); } printf("\n"); /* * Do all needed changes in the first cylinder group. * allocate blocks in new location */ updcsloc(modtime, fsi, fso, Nflag); /* * Now write the cylinder summary back to disk. */ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) { wtfs(fsbtodb(&sblock, sblock.fs_csaddr + numfrags(&sblock, i)), (size_t)MIN(sblock.fs_cssize - i, sblock.fs_bsize), (void *)(((char *)fscs) + i), fso, Nflag); } DBG_PRINT0("fscs written\n"); #ifdef FS_DEBUG { struct csum *dbg_csp; u_int32_t dbg_csc; char dbg_line[80]; dbg_csp = fscs; for (dbg_csc = 0; dbg_csc < sblock.fs_ncg; dbg_csc++) { snprintf(dbg_line, sizeof(dbg_line), "%d. new csum in new location", dbg_csc); DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++); } } #endif /* FS_DEBUG */ /* * Now write the new superblock back to disk. */ sblock.fs_time = modtime; wtfs(sblockloc, (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag); DBG_PRINT0("sblock written\n"); DBG_DUMP_FS(&sblock, "new initial sblock"); /* * Clean up the dynamic fields in our superblock copies. */ sblock.fs_fmod = 0; sblock.fs_clean = 1; sblock.fs_ronly = 0; sblock.fs_cgrotor = 0; sblock.fs_state = 0; memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt)); sblock.fs_flags &= FS_DOSOFTDEP; /* * XXX * The following fields are currently distributed from the superblock * to the copies: * fs_minfree * fs_rotdelay * fs_maxcontig * fs_maxbpg * fs_minfree, * fs_optim * fs_flags regarding SOFTPDATES * * We probably should rather change the summary for the cylinder group * statistics here to the value of what would be in there, if the file * system were created initially with the new size. Therefor we still * need to find an easy way of calculating that. * Possibly we can try to read the first superblock copy and apply the * "diffed" stats between the old and new superblock by still copying * certain parameters onto that. */ /* * Write out the duplicate super blocks. */ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) { wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), (size_t)SBLOCKSIZE, (void *)&sblock, fso, Nflag); } DBG_PRINT0("sblock copies written\n"); DBG_DUMP_FS(&sblock, "new other sblocks"); DBG_LEAVE; return; }