From a11b7b009aa17eb8aaa1250f191e5fa0f9ee880e Mon Sep 17 00:00:00 2001 From: tomsoft Date: Sat, 9 Dec 2000 15:27:35 +0000 Subject: added growfs(8) including ffsinfo(8) to the freebsd base system Reviewed by: grog --- sbin/ffsinfo/Makefile | 19 ++ sbin/ffsinfo/ffsinfo.8 | 132 +++++++++++ sbin/ffsinfo/ffsinfo.c | 615 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 766 insertions(+) create mode 100644 sbin/ffsinfo/Makefile create mode 100644 sbin/ffsinfo/ffsinfo.8 create mode 100644 sbin/ffsinfo/ffsinfo.c (limited to 'sbin/ffsinfo') diff --git a/sbin/ffsinfo/Makefile b/sbin/ffsinfo/Makefile new file mode 100644 index 0000000..8098d89 --- /dev/null +++ b/sbin/ffsinfo/Makefile @@ -0,0 +1,19 @@ +# @(#)Makefile 8.8 (Berkeley) 6/21/2000 +# +# $TSHeader: src/sbin/ffsinfo/Makefile,v 1.3 2000/12/05 19:45:10 tomsoft Exp $ +# $FreeBSD$ +# + +MAINTAINER= tomsoft@FreeBSD.ORG, chm@FreeBSD.ORG + +#CFLAGS+=${BDECFLAGS} + +PROG= ffsinfo +SRCS= ffsinfo.c debug.c +MAN8= ffsinfo.8 + +GROWFS= ${.CURDIR}/../growfs +CFLAGS+=-DFS_DEBUG -I${GROWFS} +.PATH: ${GROWFS} + +.include diff --git a/sbin/ffsinfo/ffsinfo.8 b/sbin/ffsinfo/ffsinfo.8 new file mode 100644 index 0000000..bfdcdc7 --- /dev/null +++ b/sbin/ffsinfo/ffsinfo.8 @@ -0,0 +1,132 @@ +.\" Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz +.\" Copyright (c) 1980, 1989, 1993 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgment: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors, as well as Christoph +.\" Herrmann and Thomas-Henning von Kamptz. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $TSHeader: src/sbin/ffsinfo/ffsinfo.8,v 1.2 2000/12/09 15:12:31 tomsoft Exp $ +.\" $FreeBSD$ +.\" +.Dd September 8, 2000 +.Dt FSINFO 8 +.Os BSD 4.4 +.Sh NAME +.Nm ffsinfo +.Nd dump all meta information of an existing ufs file system +.Sh SYNOPSIS +.Nm ffsinfo +.Op Fl L +.Op Fl g Ar cylinder group +.Op Fl i Ar inode +.Op Fl l Ar level +.Op Fl o Ar outfile +.Ar < special | file > +.Sh DESCRIPTION +.Nm Ffsinfo +extends the +.Xr dumpfs 8 +program. +.Pp +The output is generated into the file +.Nm outfile . +Also expect the output file to be rather large. Up to 2 percent of the size +of the specified filesystem is not uncommon. +.Nm Ffsinfo +has some options to allow the defaults to be overridden. +.\".Pp +.Bl -tag -width indent +.It Fl L +Specifying this options skips the tests of the disklabel. This is automatically +done, if the specified filename to dump is a plain file. +.It Fl g Ar cylinder group +This restrictes the dump to infomation about this cylinder group only. Here +0 means the first cylinder group and -1 the last one. +.It Fl i Ar inode +This restrictes the dump to infomation about this particular inode only. Here +the minimum acceptable inode is 2. If this option is omitted but a cylinder +group is defined then only inodes within that cylinder group are dumped. +.It Fl l Ar level +The level of detail which will be dumped. This value defaults +to 255 and is the bitwise and of the following table: +.Bd -literal -offset left +0x001 - initial superblock +0x002 - superblock copys in each cylindergroup +0x004 - cylinder group summary in initial cylinder group +0x008 - cylinder group information +0x010 - inode allocation bitmap +0x020 - fragment allocation bitmap +0x040 - cluster maps and summary +0x080 - rotational layout tables +0x100 - inode information +0x200 - indirect block dump +.Ed +.It Fl o Ar outfile +This allows to change the output filename where the dump is written to. The +current default is +.Nm /var/tmp/ffsinfo . +.El +.Sh EXAMPLES +.Pp +.Dl ffsinfo -l 1023 /dev/vinum/testvol +.Pp +will dump /dev/vinum/testvol with all available information. +.Sh BUGS +Currently +.Nm +can only dump unmounted file systems. Do not try dumping a mounted file system, +your system may panic and you will not be able to use the file system any +longer. +.Pp +Also snapshots are handled like plain files. They should get a their own +level to provide for independent control of the amount of what gets dumped. It +probably also makes sense to some extend to dump the snapshot as a filesystem. +.Sh SEE ALSO +.Xr vinum 8 , +.Xr disklabel 8 , +.Xr fsck 8 , +.Xr newfs 8 , +.Xr tunefs 8 , +.Xr growfs 8 , +.Xr dumpfs 8 +.\".Rs +.\".Re +.Sh AUTHORS +.An Christoph Herrmann Aq chm@FreeBSD.ORG +.An Thomas-Henning von Kamptz Aq tomsoft@FreeBSD.ORG +.An The GROWFS team Aq growfs@Tomsoft.COM +.Sh HISTORY +The +.Nm +command appeared first in +.Bx Free +5.0 diff --git a/sbin/ffsinfo/ffsinfo.c b/sbin/ffsinfo/ffsinfo.c new file mode 100644 index 0000000..ef5e94f --- /dev/null +++ b/sbin/ffsinfo/ffsinfo.c @@ -0,0 +1,615 @@ +/* + * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz + * Copyright (c) 1980, 1989, 1993 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors, as well as Christoph + * Herrmann and Thomas-Henning von Kamptz. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.3 2000/12/09 15:12:31 tomsoft Exp $ + * $FreeBSD$ + * + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\ +Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\ +All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +/* ********************************************************** INCLUDES ***** */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" + +/* *********************************************************** GLOBALS ***** */ +#ifdef FS_DEBUG +int _dbg_lvl_ = (DL_INFO); /* DL_TRC */ +#endif /* FS_DEBUG */ + +static union { + struct fs fs; + char pad[SBSIZE]; +} fsun1, fsun2; +#define sblock fsun1.fs +#define osblock fsun2.fs + +static union { + struct cg cg; + char pad[MAXBSIZE]; +} cgun1; +#define acg cgun1.cg + +static char ablk[MAXBSIZE]; +static char i1blk[MAXBSIZE]; +static char i2blk[MAXBSIZE]; +static char i3blk[MAXBSIZE]; + +static struct csum *fscs; + +/* ******************************************************** PROTOTYPES ***** */ +static void rdfs(daddr_t, int, char *, int); +static void usage(char *); +static struct disklabel *get_disklabel(int); +static struct dinode *ginode(ino_t, int); +static void dump_whole_inode(ino_t, int, int); + +/* ************************************************************** rdfs ***** */ +/* + * Here we read some block(s) from disk. + */ +void +rdfs(daddr_t bno, int size, char *bf, int fsi) +{ + DBG_FUNC("rdfs") + int n; + + DBG_ENTER; + + if (lseek(fsi, (off_t)bno * DEV_BSIZE, 0) < 0) { + fprintf(stderr, "seek error: %ld\n", (long)bno); + err(33, "rdfs"); + } + n = read(fsi, bf, (size_t)size); + if (n != size) { + fprintf(stderr, "read error: %ld\n", (long)bno); + err(34, "rdfs"); + } + + DBG_LEAVE; + return; +} + +/* ************************************************************** main ***** */ +/* + * 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) +{ + DBG_FUNC("main") + char *a0, *device, *special, *cp; + char ch; + size_t len; + struct stat st; + struct disklabel *lp; + struct partition *pp; + 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; + int Lflag=0; + + DBG_ENTER; + + cfg_lv=0xff; + cfg_in=-2; + cfg_cg=-2; + out_file=strdup("/var/tmp/ffsinfo"); + + a0=*argv; /* save argv[0] for usage() */ + 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(a0); + } + break; + case 'i': + cfg_in=atol(optarg); + if(cfg_in < 0) { + usage(a0); + } + break; + case 'l': + cfg_lv=atol(optarg); + if(cfg_lv < 0x1||cfg_lv > 0x3ff) { + usage(a0); + } + break; + case 'o': + free(out_file); + out_file=strdup(optarg); + break; + case '?': + /* FALLTHROUGH */ + default: + usage(a0); + } + } + argc -= optind; + argv += optind; + + if(argc != 1) { + usage(a0); + } + 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); + 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) { + fprintf(stderr, "%s: %s\n", device, strerror(errno)); + exit(-1); + } + + 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. + */ + cp=device+strlen(device)-1; + lp = get_disklabel(fsi); + if(lp->d_type == DTYPE_VINUM) { + pp = &lp->d_partitions[0]; + } else if (isdigit(*cp)) { + pp = &lp->d_partitions[2]; + } else if (*cp>='a' && *cp<='h') { + pp = &lp->d_partitions[*cp - 'a']; + } else { + fprintf(stderr, "unknown device\n"); + exit(-1); + } + + /* + * Check if that partition looks suited for dumping. + */ + if (pp->p_size < 1) { + fprintf(stderr, "partition is unavailable\n"); + exit(-1); + } + if (pp->p_fstype != FS_BSDFFS) { + fprintf(stderr, "partition not 4.2BSD\n"); + exit(-1); + } + } + + /* + * Read the current superblock. + */ + rdfs((daddr_t)(SBOFF/DEV_BSIZE), SBSIZE, (char *)&(sblock), fsi); + if (sblock.fs_magic != FS_MAGIC) { + fprintf(stderr, "superblock not recognized\n"); + exit(-1); + } + + 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_cgdi_nlink==0) { + DBG_LEAVE; + return; /* inode not in use */ + } + + /* + * Dump the main inode structure. + */ + snprintf(comment, 80, "Inode 0x%08x", inode); + if (level & 0x100) { + DBG_DUMP_INO(&sblock, comment, ino); + } + + if (!(level & 0x200)) { + DBG_LEAVE; + return; + } + + /* + * Ok, now prepare for dumping all direct and indirect pointers. + */ + rb=howmany(ino->di_size, sblock.fs_bsize)-NDADDR; + if(rb>0) { + /* + * Dump single indirect block. + */ + rdfs(fsbtodb(&sblock, ino->di_ib[0]), sblock.fs_bsize, i1blk, + fsi); + snprintf(comment, 80, "Inode 0x%08x: indirect 0", inode); + DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); + rb-=howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)); + } + if(rb>0) { + /* + * Dump double indirect blocks. + */ + rdfs(fsbtodb(&sblock, ino->di_ib[1]), sblock.fs_bsize, i2blk, + fsi); + snprintf(comment, 80, "Inode 0x%08x: indirect 1", inode); + DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, + howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)))); + for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)))&&(rb>0)); ind2ctr++) { + ind2ptr=&((ufs_daddr_t *)&i2blk)[ind2ctr]; + + rdfs(fsbtodb(&sblock, *ind2ptr), sblock.fs_bsize, + i1blk, fsi); + snprintf(comment, 80, "Inode 0x%08x: indirect 1->%d", + inode, ind2ctr); + DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); + rb-=howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)); + } + } + if(rb>0) { + /* + * Dump triple indirect blocks. + */ + rdfs(fsbtodb(&sblock, ino->di_ib[2]), sblock.fs_bsize, i3blk, + fsi); + snprintf(comment, 80, "Inode 0x%08x: indirect 2", inode); +#define SQUARE(a) ((a)*(a)) + DBG_DUMP_IBLK(&sblock, comment, i3blk, howmany(rb, + SQUARE(howmany(sblock.fs_bsize, sizeof(ufs_daddr_t))))); +#undef SQUARE + for(ind3ctr=0; ((ind3ctr < howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)))&&(rb>0)); ind3ctr ++) { + ind3ptr=&((ufs_daddr_t *)&i3blk)[ind3ctr]; + + rdfs(fsbtodb(&sblock, *ind3ptr), sblock.fs_bsize, + i2blk, fsi); + snprintf(comment, 80, "Inode 0x%08x: indirect 2->%d", + inode, ind3ctr); + DBG_DUMP_IBLK(&sblock, comment, i2blk, howmany(rb, + howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)))); + for(ind2ctr=0; ((ind2ctr < howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)))&&(rb>0)); ind2ctr ++) { + ind2ptr=&((ufs_daddr_t *)&i2blk)[ind2ctr]; + + rdfs(fsbtodb(&sblock, *ind2ptr), + sblock.fs_bsize, i1blk, fsi); + snprintf(comment, 80, + "Inode 0x%08x: indirect 2->%d->%d", inode, + ind3ctr, ind3ctr); + DBG_DUMP_IBLK(&sblock, comment, i1blk, (size_t)rb); + rb-=howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)); + } + } + } + + DBG_LEAVE; + return; +} + +/* ***************************************************** get_disklabel ***** */ +/* + * Read the disklabel from disk. + */ +struct disklabel * +get_disklabel(int fd) +{ + DBG_FUNC("get_disklabel") + static struct disklabel *lab; + + DBG_ENTER; + + lab=(struct disklabel *)malloc(sizeof(struct disklabel)); + if (!lab) { + fprintf(stderr, "malloc failed\n"); + exit(-1); + } + if (ioctl(fd, DIOCGDINFO, (char *)lab) < 0) { + fprintf(stderr, "DIOCGDINFO failed\n"); + exit(-1); + } + + DBG_LEAVE; + return (lab); +} + + +/* ************************************************************* usage ***** */ +/* + * Dump a line of usage. + */ +void +usage(char *name) +{ + DBG_FUNC("usage") + char *basename; + + DBG_ENTER; + + basename=strrchr(name, '/'); + if(!basename) { + basename=name; + } else { + basename++; + } + fprintf(stderr, + "usage:\t%s\t[-L] [-g cylgrp] [-i inode] [-l level] [-o outfile]\n" + "\t\t< special | file >\n", + basename); + + DBG_LEAVE; + exit(-1); +} + +/* ************************************************************ ginode ***** */ +/* + * This function provides access to an individual inode. We find out in which + * block the requested inode is located, read it from disk if needed, and + * return the pointer into that block. We maintain a cache of one block to + * not read the same block again and again if we iterate lineary over all + * inodes. + */ +struct dinode * +ginode(ino_t inumber, int fsi) +{ + DBG_FUNC("ginode") + ufs_daddr_t iblk; + static ino_t startinum=0; /* first inode in cached block */ + struct dinode *pi; + + DBG_ENTER; + + pi=(struct dinode *)ablk; + if (startinum == 0 || inumber < startinum || + inumber >= startinum + INOPB(&sblock)) { + /* + * The block needed is not cached, so we have to read it from + * disk now. + */ + iblk = ino_to_fsba(&sblock, inumber); + rdfs(fsbtodb(&sblock, iblk), sblock.fs_bsize, (char *)&ablk, + fsi); + startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); + } + + DBG_LEAVE; + return (&(pi[inumber % INOPB(&sblock)])); +} + -- cgit v1.1