diff --git a/sbin/Makefile b/sbin/Makefile index 6cb4d8fc0ff..102f8b3cae5 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -16,11 +16,13 @@ SUBDIR= adjkerntz \ dump \ dumpfs \ dumpon \ + ffsinfo \ fsck \ fsck_ifs \ fsck_ffs \ fsdb \ fsirand \ + growfs \ ifconfig \ init \ ip6fw \ diff --git a/sbin/ffsinfo/Makefile b/sbin/ffsinfo/Makefile new file mode 100644 index 00000000000..8098d89ed0e --- /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 00000000000..bfdcdc72463 --- /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 00000000000..ef5e94f920c --- /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)])); +} + diff --git a/sbin/fsck_ffs/pass1.c b/sbin/fsck_ffs/pass1.c index fcd596b9cc2..1045a5f0520 100644 --- a/sbin/fsck_ffs/pass1.c +++ b/sbin/fsck_ffs/pass1.c @@ -73,12 +73,16 @@ pass1() cgd = cgdmin(&sblock, c); if (c == 0) { i = cgbase(&sblock, c); - cgd += howmany(sblock.fs_cssize, sblock.fs_fsize); } else i = cgsblock(&sblock, c); for (; i < cgd; i++) setbmap(i); } + i = sblock.fs_csaddr; + cgd = i + howmany(sblock.fs_cssize, sblock.fs_fsize); + for (; i < cgd; i++) + setbmap(i); + /* * Find all allocated blocks. */ diff --git a/sbin/growfs/Makefile b/sbin/growfs/Makefile new file mode 100644 index 00000000000..50dccfff246 --- /dev/null +++ b/sbin/growfs/Makefile @@ -0,0 +1,21 @@ +# @(#)Makefile 8.8 (Berkeley) 6/21/2000 +# +# $TSHeader: src/sbin/growfs/Makefile,v 1.4 2000/12/05 19:45:24 tomsoft Exp $ +# $FreeBSD$ +# + +MAINTAINER= tomsoft@FreeBSD.ORG, chm@FreeBSD.ORG + +#GFSDBG=YES +#CFLAGS+=${BDECFLAGS} + +PROG= growfs +SRCS= growfs.c +MAN8= growfs.8 + +.if defined(GFSDBG) +SRCS+= debug.c +CFLAGS+=-DFS_DEBUG +.endif + +.include diff --git a/sbin/growfs/debug.c b/sbin/growfs/debug.c new file mode 100644 index 00000000000..3dacee0e9c5 --- /dev/null +++ b/sbin/growfs/debug.c @@ -0,0 +1,699 @@ +/* + * 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/growfs/debug.c,v 1.2 2000/11/16 18:43:49 tom Exp $ + * $FreeBSD$ + * + */ + +#ifndef lint +static const char rcsid[] = + "$TSHeader: src/sbin/growfs/debug.c,v 1.2 2000/11/16 18:43:49 tom Exp $"; +#endif /* not lint */ + +/* ********************************************************** INCLUDES ***** */ +#include + +#include +#include +#include + +#include "debug.h" + +#ifdef FS_DEBUG + +/* *********************************************************** GLOBALS ***** */ +static FILE *dbg_log=NULL; +static unsigned int indent=0; + +/* + * prototypes not done here, as they come with debug.h + */ + +/* ********************************************************** dbg_open ***** */ +/* + * Open the filehandle where all debug output has to go. + */ +void +dbg_open(const char *fn) +{ + + dbg_log=fopen(fn, "a"); + + return; +} + +/* ********************************************************* dbg_close ***** */ +/* + * Close the filehandle where all debug output went to. + */ +void +dbg_close(void) +{ + + if(dbg_log) { + fclose(dbg_log); + dbg_log=NULL; + } + + return; +} + +/* ****************************************************** dbg_dump_hex ***** */ +/* + * Dump out a full filesystem block in hex. + */ +void +dbg_dump_hex(struct fs *sb, const char *comment, unsigned char *mem) +{ + int i, j, k; + + if(!dbg_log) { + return; + } + fprintf(dbg_log, "===== START HEXDUMP =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)mem, comment); + indent++; + for (i=0; ifs_bsize; i+=24) { + for (j=0; j<3; j++) { + for (k=0; k<8; k++) { + fprintf(dbg_log, "%02x ", *mem++); + } + fprintf(dbg_log, " "); + } + fprintf(dbg_log, "\n"); + } + indent--; + fprintf(dbg_log, "===== END HEXDUMP =====\n"); + + return; +} + +/* ******************************************************* dbg_dump_fs ***** */ +/* + * Dump the superblock. + */ +void +dbg_dump_fs(struct fs *sb, const char *comment) +{ +#ifdef FSMAXSNAP + int j; +#endif /* FSMAXSNAP */ + + if(!dbg_log) { + return; + } + + fprintf(dbg_log, "===== START SUPERBLOCK =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)sb, comment); + indent++; + + fprintf(dbg_log, "sblkno ufs_daddr_t 0x%08x\n", + sb->fs_sblkno); + fprintf(dbg_log, "cblkno ufs_daddr_t 0x%08x\n", + sb->fs_cblkno); + fprintf(dbg_log, "iblkno ufs_daddr_t 0x%08x\n", + sb->fs_iblkno); + fprintf(dbg_log, "dblkno ufs_daddr_t 0x%08x\n", + sb->fs_dblkno); + + fprintf(dbg_log, "cgoffset int32_t 0x%08x\n", + sb->fs_cgoffset); + fprintf(dbg_log, "cgmask int32_t 0x%08x\n", + sb->fs_cgmask); + fprintf(dbg_log, "time time_t %10lu\n", + sb->fs_time); + fprintf(dbg_log, "size int32_t 0x%08x\n", + sb->fs_size); + fprintf(dbg_log, "dsize int32_t 0x%08x\n", + sb->fs_dsize); + fprintf(dbg_log, "ncg int32_t 0x%08x\n", + sb->fs_ncg); + fprintf(dbg_log, "bsize int32_t 0x%08x\n", + sb->fs_bsize); + fprintf(dbg_log, "fsize int32_t 0x%08x\n", + sb->fs_fsize); + fprintf(dbg_log, "frag int32_t 0x%08x\n", + sb->fs_frag); + + fprintf(dbg_log, "minfree int32_t 0x%08x\n", + sb->fs_minfree); + fprintf(dbg_log, "rotdelay int32_t 0x%08x\n", + sb->fs_rotdelay); + fprintf(dbg_log, "rps int32_t 0x%08x\n", + sb->fs_rps); + + fprintf(dbg_log, "bmask int32_t 0x%08x\n", + sb->fs_bmask); + fprintf(dbg_log, "fmask int32_t 0x%08x\n", + sb->fs_fmask); + fprintf(dbg_log, "bshift int32_t 0x%08x\n", + sb->fs_bshift); + fprintf(dbg_log, "fshift int32_t 0x%08x\n", + sb->fs_fshift); + + fprintf(dbg_log, "maxcontig int32_t 0x%08x\n", + sb->fs_maxcontig); + fprintf(dbg_log, "maxbpg int32_t 0x%08x\n", + sb->fs_maxbpg); + + fprintf(dbg_log, "fragshift int32_t 0x%08x\n", + sb->fs_fragshift); + fprintf(dbg_log, "fsbtodb int32_t 0x%08x\n", + sb->fs_fsbtodb); + fprintf(dbg_log, "sbsize int32_t 0x%08x\n", + sb->fs_sbsize); + fprintf(dbg_log, "csmask int32_t 0x%08x\n", + sb->fs_csmask); + fprintf(dbg_log, "csshift int32_t 0x%08x\n", + sb->fs_csshift); + fprintf(dbg_log, "nindir int32_t 0x%08x\n", + sb->fs_nindir); + fprintf(dbg_log, "inopb int32_t 0x%08x\n", + sb->fs_inopb); + fprintf(dbg_log, "nspf int32_t 0x%08x\n", + sb->fs_nspf); + + fprintf(dbg_log, "optim int32_t 0x%08x\n", + sb->fs_optim); + + fprintf(dbg_log, "npsect int32_t 0x%08x\n", + sb->fs_npsect); + fprintf(dbg_log, "interleave int32_t 0x%08x\n", + sb->fs_interleave); + fprintf(dbg_log, "trackskew int32_t 0x%08x\n", + sb->fs_trackskew); + + fprintf(dbg_log, "id int32_t[2] %08x %08x\n", + sb->fs_id[0], sb->fs_id[1]); + + fprintf(dbg_log, "csaddr ufs_daddr_t 0x%08x\n", + sb->fs_csaddr); + fprintf(dbg_log, "cssize int32_t 0x%08x\n", + sb->fs_cssize); + fprintf(dbg_log, "cgsize int32_t 0x%08x\n", + sb->fs_cgsize); + + fprintf(dbg_log, "ntrak int32_t 0x%08x\n", + sb->fs_ntrak); + fprintf(dbg_log, "nsect int32_t 0x%08x\n", + sb->fs_nsect); + fprintf(dbg_log, "spc int32_t 0x%08x\n", + sb->fs_spc); + + fprintf(dbg_log, "ncyl int32_t 0x%08x\n", + sb->fs_ncyl); + + fprintf(dbg_log, "cpg int32_t 0x%08x\n", + sb->fs_cpg); + fprintf(dbg_log, "ipg int32_t 0x%08x\n", + sb->fs_ipg); + fprintf(dbg_log, "fpg int32_t 0x%08x\n", + sb->fs_fpg); + + dbg_dump_csum("internal cstotal", &sb->fs_cstotal); + + fprintf(dbg_log, "fmod int8_t 0x%02x\n", + sb->fs_fmod); + fprintf(dbg_log, "clean int8_t 0x%02x\n", + sb->fs_clean); + fprintf(dbg_log, "ronly int8_t 0x%02x\n", + sb->fs_ronly); + fprintf(dbg_log, "flags int8_t 0x%02x\n", + sb->fs_flags); + fprintf(dbg_log, "fsmnt u_char[MAXMNTLEN] \"%s\"\n", + sb->fs_fsmnt); + + fprintf(dbg_log, "cgrotor int32_t 0x%08x\n", + sb->fs_cgrotor); +/* + * struct csum[MAXCSBUFS] - is only maintained in memory + */ +/* fprintf(dbg_log, " int32_t\n", sb->*fs_maxcluster);*/ + fprintf(dbg_log, "cpc int32_t 0x%08x\n", + sb->fs_cpc); +/* + * int16_t fs_opostbl[16][8] - is dumped when used in dbg_dump_sptbl + */ +#ifdef FSMAXSNAP + for(j=0; jfs_snapinum[j]); + if(!sb->fs_snapinum[j]) { /* list is dense */ + break; + } + } +#endif /* FSMAXSNAP */ + fprintf(dbg_log, "contigsumsize int32_t 0x%08x\n", + sb->fs_contigsumsize); + fprintf(dbg_log, "maxsymlinklen int32_t 0x%08x\n", + sb->fs_maxsymlinklen); + fprintf(dbg_log, "inodefmt int32_t 0x%08x\n", + sb->fs_inodefmt); + fprintf(dbg_log, "maxfilesize u_int64_t 0x%08x%08x\n", + ((unsigned int *)&(sb->fs_maxfilesize))[1], + ((unsigned int *)&(sb->fs_maxfilesize))[0]); + fprintf(dbg_log, "qbmask int64_t 0x%08x%08x\n", + ((unsigned int *)&(sb->fs_qbmask))[1], + ((unsigned int *)&(sb->fs_qbmask))[0]); + fprintf(dbg_log, "qfmask int64_t 0x%08x%08x\n", + ((unsigned int *)&(sb->fs_qfmask))[1], + ((unsigned int *)&(sb->fs_qfmask))[0]); + fprintf(dbg_log, "state int32_t 0x%08x\n", + sb->fs_state); + fprintf(dbg_log, "postblformat int32_t 0x%08x\n", + sb->fs_postblformat); + fprintf(dbg_log, "nrpos int32_t 0x%08x\n", + sb->fs_nrpos); + fprintf(dbg_log, "postbloff int32_t 0x%08x\n", + sb->fs_postbloff); + fprintf(dbg_log, "rotbloff int32_t 0x%08x\n", + sb->fs_rotbloff); + fprintf(dbg_log, "magic int32_t 0x%08x\n", + sb->fs_magic); + + indent--; + fprintf(dbg_log, "===== END SUPERBLOCK =====\n"); + + return; +} + +/* ******************************************************* dbg_dump_cg ***** */ +/* + * Dump a cylinder group. + */ +void +dbg_dump_cg(const char *comment, struct cg *cgr) +{ + int j; + + if(!dbg_log) { + return; + } + + fprintf(dbg_log, "===== START CYLINDER GROUP =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); + indent++; + + fprintf(dbg_log, "magic int32_t 0x%08x\n", cgr->cg_magic); + fprintf(dbg_log, "time time_t %10lu\n", cgr->cg_time); + fprintf(dbg_log, "cgx int32_t 0x%08x\n", cgr->cg_cgx); + fprintf(dbg_log, "ncyl int16_t 0x%04x\n", cgr->cg_ncyl); + fprintf(dbg_log, "niblk int16_t 0x%04x\n", cgr->cg_niblk); + fprintf(dbg_log, "ndblk int32_t 0x%08x\n", cgr->cg_ndblk); + dbg_dump_csum("internal cs", &cgr->cg_cs); + fprintf(dbg_log, "rotor int32_t 0x%08x\n", cgr->cg_rotor); + fprintf(dbg_log, "frotor int32_t 0x%08x\n", cgr->cg_frotor); + fprintf(dbg_log, "irotor int32_t 0x%08x\n", cgr->cg_irotor); + for(j=0; jcg_frsum[j]); + } + fprintf(dbg_log, "btotoff int32_t 0x%08x\n", cgr->cg_btotoff); + fprintf(dbg_log, "boff int32_t 0x%08x\n", cgr->cg_boff); + fprintf(dbg_log, "iusedoff int32_t 0x%08x\n", cgr->cg_iusedoff); + fprintf(dbg_log, "freeoff int32_t 0x%08x\n", cgr->cg_freeoff); + fprintf(dbg_log, "nextfreeoff int32_t 0x%08x\n", + cgr->cg_nextfreeoff); + fprintf(dbg_log, "clustersumoff int32_t 0x%08x\n", + cgr->cg_clustersumoff); + fprintf(dbg_log, "clusterof int32_t 0x%08x\n", + cgr->cg_clusteroff); + fprintf(dbg_log, "nclusterblks int32_t 0x%08x\n", + cgr->cg_nclusterblks); + + indent--; + fprintf(dbg_log, "===== END CYLINDER GROUP =====\n"); + + return; +} + +/* ***************************************************** dbg_dump_csum ***** */ +/* + * Dump a cylinder summary. + */ +void +dbg_dump_csum(const char *comment, struct csum *cs) +{ + + if(!dbg_log) { + return; + } + + fprintf(dbg_log, "===== START CYLINDER SUMMARY =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cs, comment); + indent++; + + fprintf(dbg_log, "ndir int32_t 0x%08x\n", cs->cs_ndir); + fprintf(dbg_log, "nbfree int32_t 0x%08x\n", cs->cs_nbfree); + fprintf(dbg_log, "nifree int32_t 0x%08x\n", cs->cs_nifree); + fprintf(dbg_log, "nffree int32_t 0x%08x\n", cs->cs_nffree); + + indent--; + fprintf(dbg_log, "===== END CYLINDER SUMMARY =====\n"); + + return; +} + +/* **************************************************** dbg_dump_inmap ***** */ +/* + * Dump the inode allocation map in one cylinder group. + */ +void +dbg_dump_inmap(struct fs *sb, const char *comment, struct cg *cgr) +{ + int j,k,l,e; + unsigned char *cp; + + if(!dbg_log) { + return; + } + + fprintf(dbg_log, "===== START INODE ALLOCATION MAP =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); + indent++; + + cp=(unsigned char *)cg_inosused(cgr); + e=sb->fs_ipg/8; + for(j=0; jfs_cpg * sb->fs_spc / NSPF(sb)), NBBY); + for(j=0; jfs_cpg * sb->fs_spc / NSPB(sb), NBBY); + for(j=0; jfs_contigsumsize; j++) { + fprintf(dbg_log, "%02d: %8ld\n", j, *lp++); + } + + indent--; + fprintf(dbg_log, "===== END CLUSTER SUMMARY =====\n"); + + return; +} + +/* **************************************************** dbg_dump_sptbl ***** */ +/* + * Dump the block summary, and the rotational layout table. + */ +void +dbg_dump_sptbl(struct fs *sb, const char *comment, struct cg *cgr) +{ + int j,k; + long *lp; + + if(!dbg_log) { + return; + } + + fprintf(dbg_log, + "===== START BLOCK SUMMARY AND POSITION TABLE =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment); + indent++; + + lp=(long *)cg_blktot(cgr); + for(j=0; jfs_cpg; j++) { + fprintf(dbg_log, "%2d: %5ld = ", j, *lp++); + for(k=0; kfs_nrpos; k++) { + fprintf(dbg_log, "%4d", cg_blks(sb, cgr, j)[k]); + if(kfs_nrpos-1) { + fprintf(dbg_log, " + "); + } + } + fprintf(dbg_log, "\n"); + } + + indent--; + fprintf(dbg_log, "===== END BLOCK SUMMARY AND POSITION TABLE =====\n"); + + return; +} + +/* ****************************************************** dbg_dump_ino ***** */ +/* + * Dump an inode structure. + */ +void +dbg_dump_ino(struct fs *sb, const char *comment, struct dinode *ino) +{ + int ictr; + int remaining_blocks; + + if(!dbg_log) { + return; + } + + fprintf(dbg_log, "===== START INODE DUMP =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)ino, comment); + indent++; + + fprintf(dbg_log, "mode u_int16_t 0%o\n", ino->di_mode); + fprintf(dbg_log, "nlink int16_t 0x%04x\n", ino->di_nlink); + fprintf(dbg_log, "size u_int64_t 0x%08x%08x\n", + ((unsigned int *)&(ino->di_size))[1], + ((unsigned int *)&(ino->di_size))[0]); + fprintf(dbg_log, "atime int32_t 0x%08x\n", ino->di_atime); + fprintf(dbg_log, "atimensec int32_t 0x%08x\n", + ino->di_atimensec); + fprintf(dbg_log, "mtime int32_t 0x%08x\n", + ino->di_mtime); + fprintf(dbg_log, "mtimensec int32_t 0x%08x\n", + ino->di_mtimensec); + fprintf(dbg_log, "ctime int32_t 0x%08x\n", ino->di_ctime); + fprintf(dbg_log, "ctimensec int32_t 0x%08x\n", + ino->di_ctimensec); + + remaining_blocks=howmany(ino->di_size, sb->fs_bsize); /* XXX ts - +1? */ + for(ictr=0; ictr < MIN(NDADDR, remaining_blocks); ictr++) { + fprintf(dbg_log, "db ufs_daddr_t[%x] 0x%08x\n", ictr, + ino->di_db[ictr]); + } + remaining_blocks-=NDADDR; + if(remaining_blocks>0) { + fprintf(dbg_log, "ib ufs_daddr_t[0] 0x%08x\n", + ino->di_ib[0]); + } + remaining_blocks-=howmany(sb->fs_bsize, sizeof(ufs_daddr_t)); + if(remaining_blocks>0) { + fprintf(dbg_log, "ib ufs_daddr_t[1] 0x%08x\n", + ino->di_ib[1]); + } +#define SQUARE(a) ((a)*(a)) + remaining_blocks-=SQUARE(howmany(sb->fs_bsize, sizeof(ufs_daddr_t))); +#undef SQUARE + if(remaining_blocks>0) { + fprintf(dbg_log, "ib ufs_daddr_t[2] 0x%08x\n", + ino->di_ib[2]); + } + + fprintf(dbg_log, "flags u_int32_t 0x%08x\n", ino->di_flags); + fprintf(dbg_log, "blocks int32_t 0x%08x\n", ino->di_blocks); + fprintf(dbg_log, "gen int32_t 0x%08x\n", ino->di_gen); + fprintf(dbg_log, "uid u_int32_t 0x%08x\n", ino->di_uid); + fprintf(dbg_log, "gid u_int32_t 0x%08x\n", ino->di_gid); + + indent--; + fprintf(dbg_log, "===== END INODE DUMP =====\n"); + + return; +} + +/* ***************************************************** dbg_dump_iblk ***** */ +/* + * Dump an indirect block. The iteration to dump a full file has to be + * written around. + */ +void +dbg_dump_iblk(struct fs *sb, const char *comment, char *block, size_t length) +{ + unsigned int *mem; + int i, j; + + if(!dbg_log) { + return; + } + + fprintf(dbg_log, "===== START INDIRECT BLOCK DUMP =====\n"); + fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)block, + comment); + indent++; + + mem=(unsigned int *)block; + for (i=0; (size_t)ifs_bsize, sizeof(ufs_daddr_t)), + length); i+=8) { + fprintf(dbg_log, "%04x: ", i); + for (j=0; j<8; j++) { + if((size_t)(i+j) + +#include +#include + +void dbg_open(const char *); +void dbg_close(void); +void dbg_dump_hex(struct fs *, const char *, unsigned char *); +void dbg_dump_fs(struct fs *, const char *); +void dbg_dump_cg(const char *, struct cg *); +void dbg_dump_csum(const char *, struct csum *); +void dbg_dump_ino(struct fs *, const char *, struct dinode *); +void dbg_dump_iblk(struct fs *, const char *, char *, size_t); +void dbg_dump_inmap(struct fs *, const char *, struct cg *); +void dbg_dump_frmap(struct fs *, const char *, struct cg *); +void dbg_dump_clmap(struct fs *, const char *, struct cg *); +void dbg_dump_clsum(struct fs *, const char *, struct cg *); +void dbg_dump_sptbl(struct fs *, const char *, struct cg *); + +#define DBG_OPEN(P) dbg_open((P)) +#define DBG_CLOSE dbg_close() +#define DBG_DUMP_HEX(F,C,M) dbg_dump_hex((F),(C),(M)) +#define DBG_DUMP_FS(F,C) dbg_dump_fs((F),(C)) +#define DBG_DUMP_CG(F,C,M) dbg_dump_cg((C),(M)) +#define DBG_DUMP_CSUM(F,C,M) dbg_dump_csum((C),(M)) +#define DBG_DUMP_INO(F,C,M) dbg_dump_ino((F),(C),(M)) +#define DBG_DUMP_IBLK(F,C,M,L) dbg_dump_iblk((F),(C),(M),(L)) +#define DBG_DUMP_INMAP(F,C,M) dbg_dump_inmap((F),(C),(M)) +#define DBG_DUMP_FRMAP(F,C,M) dbg_dump_frmap((F),(C),(M)) +#define DBG_DUMP_CLMAP(F,C,M) dbg_dump_clmap((F),(C),(M)) +#define DBG_DUMP_CLSUM(F,C,M) dbg_dump_clsum((F),(C),(M)) +#define DBG_DUMP_SPTBL(F,C,M) dbg_dump_sptbl((F),(C),(M)) + +#define DL_TRC 0x01 +#define DL_INFO 0x02 +extern int _dbg_lvl_; + +#define DBG_FUNC(N) char __FKT__[] = (N); +#define DBG_ENTER if(_dbg_lvl_ & DL_TRC) { \ + fprintf(stderr, "~>%s: %s\n", __FILE__, __FKT__ ); \ + } +#define DBG_LEAVE if(_dbg_lvl_ & DL_TRC) { \ + fprintf(stderr, "~<%s[%d]: %s\n", __FILE__, __LINE__, __FKT__ ); \ + } +#define DBG_TRC if(_dbg_lvl_ & DL_TRC) { \ + fprintf(stderr, "~=%s[%d]: %s\n", __FILE__, __LINE__, __FKT__ ); \ + } +#define DBG_PRINT0(A) if(_dbg_lvl_ & DL_INFO) { \ + fprintf(stderr, "~ %s", (A)); \ + } +#define DBG_PRINT1(A,B) if(_dbg_lvl_ & DL_INFO) { \ + fprintf(stderr, "~ "); \ + fprintf(stderr, (A), (B)); \ + } +#define DBG_PRINT2(A,B,C) if(_dbg_lvl_ & DL_INFO) { \ + fprintf(stderr, "~ "); \ + fprintf(stderr, (A), (B), (C)); \ + } +#define DBG_PRINT3(A,B,C,D) if(_dbg_lvl_ & DL_INFO) { \ + fprintf(stderr, "~ "); \ + fprintf(stderr, (A), (B), (C), (D)); \ + } +#define DBG_PRINT4(A,B,C,D,E) if(_dbg_lvl_ & DL_INFO) { \ + fprintf(stderr, "~ "); \ + fprintf(stderr, (A), (B), (C), (D), (E)); \ + } +#else /* not FS_DEBUG */ + +#define DBG_OPEN(P) +#define DBG_CLOSE +#define DBG_DUMP_HEX(F,C,M) +#define DBG_DUMP_FS(F,C) +#define DBG_DUMP_CG(F,C,M) +#define DBG_DUMP_CSUM(F,C,M) +#define DBG_DUMP_INO(F,C,M) +#define DBG_DUMP_IBLK(F,C,M,L) +#define DBG_DUMP_INMAP(F,C,M) +#define DBG_DUMP_FRMAP(F,C,M) +#define DBG_DUMP_CLMAP(F,C,M) +#define DBG_DUMP_CLSUM(F,C,M) +#define DBG_DUMP_SPTBL(F,C,M) +#define DBG_FUNC(N) +#define DBG_ENTER +#define DBG_TRC +#define DBG_LEAVE +#define DBG_PRINT0(A) +#define DBG_PRINT1(A,B) +#define DBG_PRINT2(A,B,C) +#define DBG_PRINT3(A,B,C,D) +#define DBG_PRINT4(A,B,C,D,E) + +#endif /* FS_DEBUG */ diff --git a/sbin/growfs/growfs.8 b/sbin/growfs/growfs.8 new file mode 100644 index 00000000000..5393a4f45dc --- /dev/null +++ b/sbin/growfs/growfs.8 @@ -0,0 +1,154 @@ +.\" 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/growfs/growfs.8,v 1.2 2000/12/09 15:12:33 tomsoft Exp $ +.\" $FreeBSD$ +.\" +.Dd September 8, 2000 +.Dt GROWFS 8 +.Os BSD 4.4 +.Sh NAME +.Nm growfs +.Nd grow size of an existing ufs file system +.Sh SYNOPSIS +.Nm growfs +.Op Fl Ny +.Op Fl s Ar size +.Ar special +.Sh DESCRIPTION +.Nm Growfs +extends the +.Xr newfs 8 +program. +Before running +.Nm +the disk must be labeled to a bigger size using +.Xr disklabel 8 . +If you are using volumes you must grow them using +.Xr vinum 8 . +.Nm Growfs +extends the size of the file system on the specified special file. +Currently +.Nm +can only grow unmounted file systems. Do not try growing an mounted file system, +your system may panic and you will not be able to use the file system any longer. +Most of the options you have used with +.Xr newfs 8 +once can not be changed. In fact you can only increase the size of the file +system. Use +.Xr tunefs 8 +for other changes. +Typically the defaults are reasonable, however +.Nm +has some options to allow the defaults to be overridden. +.\".Pp +.Bl -tag -width indent +.It Fl N +Cause the new file system parameters to be printed out +without realy growing the file system. +.It Fl y +.Dq Expert mode +Normally +.Nm +will ask you, if you took a backup of your data before und will do some tests +if +.Ar special +is currently mounted or if there are any active snapshots on the filesystem +specified. This will be supressed. So use this option with care! +.It Fl s Ar size +The size of the file system after growing in sectors. This value defaults +to the size of the raw partition specified in +.Ar special +(in other words, +.Nm +will grow the file system to the size of the entire partition). +.El +.Sh EXAMPLES +.Pp +.Dl growfs -s 4194304 /dev/vinum/testvol +.Pp +will grow /dev/vinum/testvol up to 2GB if there is enough space in +/dev/vinum/testvol . +.Sh BUGS +In some cases on +.Bx Free +3.x it is possible, that +.Nm +did not recognize exactly, if the file system is mounted or not and +exits with an error message, then use +.Nm +-y if you are sure, that the file system is not mounted. +It is also recommended to always use +.Nm fsck +after growing just to be on the safe side. +.Pp +Pay attention, as in certain cases we have to change the location of an file +system internal structure which had never been moved before. Almost everything +works perfect with this relocated structure except the +.Nm fsck(8) +utility. There is a patch available for +.Nm fsck(8) . +For growing above certain limits it is essential to have some free blocks +available in the first cylinder group. To avoid the relocation of that +structure it is currently recommended to use +.Nm ffsinfo -c 0 +on the first cylinder group and check that +.Nm nbfree +in the CYLINDER SUMMARY (internal cs) of the CYLINDER GROUP +.Nm cgr0 +has enough blocks. As a rule of thumb for default filesystem parameters a block +is needed for every 2 GB of total filesystem size. +.Pp +.Sh SEE ALSO +.Xr vinum 8 , +.Xr disklabel 8 , +.Xr fsck 8 , +.Xr newfs 8 , +.Xr tunefs 8 , +.Xr dumpfs 8 , +.Xr ffsinfo 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 first appeared first in +.Bx Free +5.0. diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c new file mode 100644 index 00000000000..6642d4160fe --- /dev/null +++ b/sbin/growfs/growfs.c @@ -0,0 +1,2476 @@ +/* + * 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/growfs/growfs.c,v 1.4 2000/12/09 15:12:33 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 & TYPES ***** */ +#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 /* the new superblock */ +#define osblock fsun2.fs /* the old superblock */ + +static union { + struct cg cg; + char pad[MAXBSIZE]; +} cgun1, cgun2; +#define acg cgun1.cg /* a cylinder cgroup (new) */ +#define aocg cgun2.cg /* an old cylinder group */ + +static char ablk[MAXBSIZE]; /* a block */ +static char i1blk[MAXBSIZE]; /* some indirect blocks */ +static char i2blk[MAXBSIZE]; +static char i3blk[MAXBSIZE]; + + /* where to write back updated blocks */ +static daddr_t in_src, i1_src, i2_src, i3_src; + + /* what object contains the reference */ +enum pointer_source { + GFS_PS_INODE, + GFS_PS_IND_BLK_LVL1, + GFS_PS_IND_BLK_LVL2, + GFS_PS_IND_BLK_LVL3 +}; + +static struct csum *fscs; /* cylinder summary */ + +static struct dinode zino[MAXBSIZE/sizeof(struct dinode)]; /* some inodes */ + +/* + * An array of elements of type struct gfs_bpp describes all blocks to + * be relocated in order to free the space needed for the cylinder group + * summary for all cylinder groups located in the first cylinder group. + */ +struct gfs_bpp { + daddr_t old; /* old block number */ + daddr_t new; /* new block number */ +#define GFS_FL_FIRST 1 +#define GFS_FL_LAST 2 + unsigned long flags; /* special handling required */ + int found; /* how many references were updated */ +}; + +/* ******************************************************** PROTOTYPES ***** */ +static void rdfs(daddr_t, int, char *, int); +static void wtfs(daddr_t, int, char *, int, int); +static daddr_t alloc(void); +static int charsperline(void); +static void usage(char *); +static int isblock(struct fs *, unsigned char *, int); +static void clrblock(struct fs *, unsigned char *, int); +static void setblock(struct fs *, unsigned char *, int); +static void initcg(int, time_t, int, int); +static void updjcg(int, time_t, int, int, int); +static void updcsloc(time_t, int, int, int); +static struct disklabel *get_disklabel(int); +static void return_disklabel(int, struct disklabel *, int); +static struct dinode *ginode(ino_t, int, int); +static void frag_adjust(daddr_t, int); +static void cond_bl_upd(ufs_daddr_t *, struct gfs_bpp *, + enum pointer_source, int, int); +static void updclst(int); +static void updrefs(int, ino_t, struct gfs_bpp *, int, int, int); + +/* ************************************************************ growfs ***** */ +/* + * Here we actually start growing the filesystem. We basically read the + * cylinder summary from the first cylinder group as we wan't 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, int Nflag) +{ + DBG_FUNC("growfs") + long i; + long cylno, j; + time_t utime; + int width; + char tmpbuf[100]; +#ifdef FSIRAND + static int randinit=0; + + DBG_ENTER; + + if (!randinit) { + randinit = 1; + srandomdev(); + } +#else /* not FSIRAND */ + + DBG_ENTER; + +#endif /* FSIRAND */ + time(&utime); + + /* + * Get the cylinder summary into the memory. + */ + fscs = (struct csum *)calloc(1, (size_t)sblock.fs_cssize); + for (i = 0; i < osblock.fs_cssize; i += osblock.fs_bsize) { + rdfs(fsbtodb(&osblock, osblock.fs_csaddr + + numfrags(&osblock, i)), MIN(osblock.fs_cssize - i, + osblock.fs_bsize), ((char *)fscs) + i, fsi); + } + +#ifdef FS_DEBUG +{ + struct csum *dbg_csp; + int dbg_csc; + char dbg_line[80]; + + dbg_csp=fscs; + for(dbg_csc=0; dbg_csc= 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(utime, 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)), + MIN(sblock.fs_cssize - i, sblock.fs_bsize), + ((char *)fscs) + i, fso, Nflag); + } + DBG_PRINT0("fscs written\n"); + +#ifdef FS_DEBUG +{ + struct csum *dbg_csp; + int dbg_csc; + char dbg_line[80]; + + dbg_csp=fscs; + for(dbg_csc=0; dbg_csc sblock.fs_size) { + dmax = sblock.fs_size; + } + dlower = cgsblock(&sblock, cylno) - cbase; + dupper = cgdmin(&sblock, cylno) - cbase; + if (cylno == 0) { + dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); + } + cs = fscs + cylno; + memset(&acg, 0, (size_t)sblock.fs_cgsize); + acg.cg_time = utime; + acg.cg_magic = CG_MAGIC; + acg.cg_cgx = cylno; + if (cylno == sblock.fs_ncg - 1) { + acg.cg_ncyl = sblock.fs_ncyl % sblock.fs_cpg; + } else { + acg.cg_ncyl = sblock.fs_cpg; + } + acg.cg_niblk = sblock.fs_ipg; + acg.cg_ndblk = dmax - cbase; + if (sblock.fs_contigsumsize > 0) { + acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; + } + acg.cg_btotoff = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield); + acg.cg_boff = acg.cg_btotoff + sblock.fs_cpg * sizeof(int32_t); + acg.cg_iusedoff = acg.cg_boff + + sblock.fs_cpg * sblock.fs_nrpos * sizeof(u_int16_t); + acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, NBBY); + if (sblock.fs_contigsumsize <= 0) { + acg.cg_nextfreeoff = acg.cg_freeoff + + howmany(sblock.fs_cpg* sblock.fs_spc/ NSPF(&sblock), NBBY); + } else { + acg.cg_clustersumoff = acg.cg_freeoff + howmany + (sblock.fs_cpg * sblock.fs_spc / NSPF(&sblock), NBBY) - + sizeof(u_int32_t); + acg.cg_clustersumoff = + roundup(acg.cg_clustersumoff, sizeof(u_int32_t)); + acg.cg_clusteroff = acg.cg_clustersumoff + + (sblock.fs_contigsumsize + 1) * sizeof(u_int32_t); + acg.cg_nextfreeoff = acg.cg_clusteroff + howmany + (sblock.fs_cpg * sblock.fs_spc / NSPB(&sblock), NBBY); + } + if (acg.cg_nextfreeoff-(long)(&acg.cg_firstfield) > sblock.fs_cgsize) { + /* + * XXX This should never happen as we would have had that panic + * already on filesystem creation + */ + fprintf(stderr, "Panic: cylinder group too big\n"); + exit(37); + } + acg.cg_cs.cs_nifree += sblock.fs_ipg; + if (cylno == 0) + for (i = 0; (size_t)i < ROOTINO; i++) { + setbit(cg_inosused(&acg), i); + acg.cg_cs.cs_nifree--; + } + for (i = 0; i < sblock.fs_ipg / INOPF(&sblock); i += sblock.fs_frag) { +#ifdef FSIRAND + for (j = 0; j < sblock.fs_bsize / sizeof(struct dinode); j++) { + zino[j].di_gen = random(); + } +#endif + wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i), + sblock.fs_bsize, (char *)zino, fso, Nflag); + } + for (d = 0; d < dlower; d += sblock.fs_frag) { + blkno = d / sblock.fs_frag; + setblock(&sblock, cg_blksfree(&acg), blkno); + if (sblock.fs_contigsumsize > 0) { + setbit(cg_clustersfree(&acg), blkno); + } + acg.cg_cs.cs_nbfree++; + cg_blktot(&acg)[cbtocylno(&sblock, d)]++; + cg_blks(&sblock, &acg, cbtocylno(&sblock, d)) + [cbtorpos(&sblock, d)]++; + } + sblock.fs_dsize += dlower; + sblock.fs_dsize += acg.cg_ndblk - dupper; + if ((i = dupper % sblock.fs_frag)) { + acg.cg_frsum[sblock.fs_frag - i]++; + for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) { + setbit(cg_blksfree(&acg), dupper); + acg.cg_cs.cs_nffree++; + } + } + for (d = dupper; d + sblock.fs_frag <= dmax - cbase; ) { + blkno = d / sblock.fs_frag; + setblock(&sblock, cg_blksfree(&acg), blkno); + if (sblock.fs_contigsumsize > 0) { + setbit(cg_clustersfree(&acg), blkno); + } + acg.cg_cs.cs_nbfree++; + cg_blktot(&acg)[cbtocylno(&sblock, d)]++; + cg_blks(&sblock, &acg, cbtocylno(&sblock, d)) + [cbtorpos(&sblock, d)]++; + d += sblock.fs_frag; + } + if (d < dmax - cbase) { + acg.cg_frsum[dmax - cbase - d]++; + for (; d < dmax - cbase; d++) { + setbit(cg_blksfree(&acg), d); + acg.cg_cs.cs_nffree++; + } + } + if (sblock.fs_contigsumsize > 0) { + int32_t *sump = cg_clustersum(&acg); + u_char *mapp = cg_clustersfree(&acg); + int map = *mapp++; + int bit = 1; + int run = 0; + + for (i = 0; i < acg.cg_nclusterblks; i++) { + if ((map & bit) != 0) { + run++; + } else if (run != 0) { + if (run > sblock.fs_contigsumsize) { + run = sblock.fs_contigsumsize; + } + sump[run]++; + run = 0; + } + if ((i & (NBBY - 1)) != (NBBY - 1)) { + bit <<= 1; + } else { + map = *mapp++; + bit = 1; + } + } + if (run != 0) { + if (run > sblock.fs_contigsumsize) { + run = sblock.fs_contigsumsize; + } + sump[run]++; + } + } + sblock.fs_cstotal.cs_ndir += acg.cg_cs.cs_ndir; + sblock.fs_cstotal.cs_nffree += acg.cg_cs.cs_nffree; + sblock.fs_cstotal.cs_nbfree += acg.cg_cs.cs_nbfree; + sblock.fs_cstotal.cs_nifree += acg.cg_cs.cs_nifree; + *cs = acg.cg_cs; + wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), + sblock.fs_bsize, (char *)&acg, fso, Nflag); + DBG_DUMP_CG(&sblock, "new cg", &acg); + + DBG_LEAVE; + return; +} + +/* ******************************************************* frag_adjust ***** */ +/* + * Here we add or subtract (sign +1/-1) the available fragments in a given + * block to or from the fragment statistics. By subtracting before and adding + * after an operation on the free frag map we can easy update the fragment + * statistic, which seems to be otherwise an rather complex operation. + */ +static void +frag_adjust(daddr_t frag, int sign) +{ + DBG_FUNC("frag_adjust") + int fragsize; + int f; + + DBG_ENTER; + + fragsize=0; + /* + * Here frag only needs to point to any fragment in the block we want + * to examine. + */ + for(f=rounddown(frag, sblock.fs_frag); + fold) { /* for all old blocks */ + if(*block/sblock.fs_frag == f->old) { + /* + * The fragment is part of the block, so update. + */ + *block=(f->new*sblock.fs_frag+(*block%sblock.fs_frag)); + f->found++; + DBG_PRINT3("scg (%d->%d)[%d] reference updated\n", f->old, f->new, *block%sblock.fs_frag); + + /* Write the block back to disk immediately */ + switch (source) { + case GFS_PS_INODE: + src=ablk; + dst=in_src; + break; + case GFS_PS_IND_BLK_LVL1: + src=i1blk; + dst=i1_src; + break; + case GFS_PS_IND_BLK_LVL2: + src=i2blk; + dst=i2_src; + break; + case GFS_PS_IND_BLK_LVL3: + src=i3blk; + dst=i3_src; + break; + default: /* error */ + src=NULL; + break; + } + if(src) { + /* + * XXX If src is not of type inode we have to + * implement copy on write here in case + * of active snapshots. + */ + wtfs(dst, sblock.fs_bsize, src, fso, Nflag); + } + + /* + * The same block can't be found again in this loop. + */ + break; + } + f++; + } + + DBG_LEAVE; + return; +} + +/* ************************************************************ updjcg ***** */ +/* + * Here we do all needed work for the former last cylinder group. It has to be + * changed in any case, even if the filesystem ended exactly on the end of + * this group, as there is some slightly inconsistent handling of the number + * of cylinders in the cylinder group. We start again by reading the cylinder + * group from disk. If the last block was not fully available, we first handle + * the missing fragments, then we handle all new full blocks in that file + * system and finally we handle the new last fragmented block in the file + * system. We again have to handle the fragment statistics rotational layout + * tables and cluster summary during all those operations. + */ +static void +updjcg(int cylno, time_t utime, int fsi, int fso, int Nflag) +{ + DBG_FUNC("updjcg") + daddr_t cbase, dmax, dupper; + struct csum *cs; + int i,k; + int j=0; + + DBG_ENTER; + + /* + * Read the former last (joining) cylinder group from disk, and make + * a copy. + */ + rdfs(fsbtodb(&osblock, cgtod(&osblock, cylno)), osblock.fs_cgsize, + (char *)&aocg, fsi); + DBG_PRINT0("jcg read\n"); + DBG_DUMP_CG(&sblock, "old joining cg", &aocg); + + memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); + + /* + * If the cylinder group had already it's new final size almost + * nothing is to be done ... except: + * For some reason the value of cg_ncyl in the last cylinder group has + * to be zero instead of fs_cpg. As this is now no longer the last + * cylinder group we have to change that value now to fs_cpg. + */ + + if(cgbase(&osblock, cylno+1) == osblock.fs_size) { + acg.cg_ncyl=sblock.fs_cpg; + + wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), sblock.fs_cgsize, + (char *)&acg, fso, Nflag); + DBG_PRINT0("jcg written\n"); + DBG_DUMP_CG(&sblock, "new joining cg", &acg); + + DBG_LEAVE; + return; + } + + /* + * Set up some variables needed later. + */ + cbase = cgbase(&sblock, cylno); + dmax = cbase + sblock.fs_fpg; + if (dmax > sblock.fs_size) + dmax = sblock.fs_size; + dupper = cgdmin(&sblock, cylno) - cbase; + if (cylno == 0) { + dupper += howmany(sblock.fs_cssize, sblock.fs_fsize); + } + + /* + * Set pointer to the cylinder summary for our cylinder group. + */ + cs = fscs + cylno; + + /* + * Touch the cylinder group, update all fields in the cylinder group as + * needed, update the free space in the superblock. + */ + acg.cg_time = utime; + if (cylno == sblock.fs_ncg - 1) { + /* + * This is still the last cylinder group. + */ + acg.cg_ncyl = sblock.fs_ncyl % sblock.fs_cpg; + } else { + acg.cg_ncyl = sblock.fs_cpg; + } + DBG_PRINT4("jcg dbg: %d %u %d %u\n", cylno, sblock.fs_ncg, acg.cg_ncyl, sblock.fs_cpg); + acg.cg_ndblk = dmax - cbase; + sblock.fs_dsize += acg.cg_ndblk-aocg.cg_ndblk; + if (sblock.fs_contigsumsize > 0) { + acg.cg_nclusterblks = acg.cg_ndblk / sblock.fs_frag; + } + + /* + * Now we have to update the free fragment bitmap for our new free + * space. There again we have to handle the fragmentation and also + * the rotational layout tables and the cluster summary. This is + * also done per fragment for the first new block if the old file + * system end was not on a block boundary, per fragment for the new + * last block if the new file system end is not on a block boundary, + * and per block for all space in between. + * + * Handle the first new block here if it was partially available + * before. + */ + if(osblock.fs_size % sblock.fs_frag) { + if(roundup(osblock.fs_size, sblock.fs_frag)<=sblock.fs_size) { + /* + * The new space is enough to fill at least this + * block + */ + j=0; + for(i=roundup(osblock.fs_size-cbase, sblock.fs_frag)-1; + i>=osblock.fs_size-cbase; + i--) { + setbit(cg_blksfree(&acg), i); + acg.cg_cs.cs_nffree++; + j++; + } + + /* + * Check if the fragment just created could join an + * already existing fragment at the former end of the + * file system. + */ + if(isblock(&sblock, cg_blksfree(&acg), + ((osblock.fs_size - cgbase(&sblock, cylno))/ + sblock.fs_frag))) { + /* + * The block is now completely available + */ + DBG_PRINT0("block was\n"); + acg.cg_frsum[osblock.fs_size%sblock.fs_frag]--; + acg.cg_cs.cs_nbfree++; + acg.cg_cs.cs_nffree-=sblock.fs_frag; + k=rounddown(osblock.fs_size-cbase, + sblock.fs_frag); + cg_blktot(&acg)[cbtocylno(&sblock, k)]++; + cg_blks(&sblock, &acg, cbtocylno(&sblock, k)) + [cbtorpos(&sblock, k)]++; + updclst((osblock.fs_size-cbase)/sblock.fs_frag); + } else { + /* + * Lets rejoin a possible partially growed + * fragment. + */ + k=0; + while(isset(cg_blksfree(&acg), i) && + (i>=rounddown(osblock.fs_size-cbase, + sblock.fs_frag))) { + i--; + k++; + } + if(k) { + acg.cg_frsum[k]--; + } + acg.cg_frsum[k+j]++; + } + } else { + /* + * We only grow by some fragments within this last + * block. + */ + for(i=sblock.fs_size-cbase-1; + i>=osblock.fs_size-cbase; + i--) { + setbit(cg_blksfree(&acg), i); + acg.cg_cs.cs_nffree++; + j++; + } + /* + * Lets rejoin a possible partially growed fragment. + */ + k=0; + while(isset(cg_blksfree(&acg), i) && + (i>=rounddown(osblock.fs_size-cbase, + sblock.fs_frag))) { + i--; + k++; + } + if(k) { + acg.cg_frsum[k]--; + } + acg.cg_frsum[k+j]++; + } + } + + /* + * Handle all new complete blocks here. + */ + for(i=roundup(osblock.fs_size-cbase, sblock.fs_frag); + i+sblock.fs_frag<=dmax-cbase; /* XXX <= or only < ? */ + i+=sblock.fs_frag) { + j = i / sblock.fs_frag; + setblock(&sblock, cg_blksfree(&acg), j); + updclst(j); + acg.cg_cs.cs_nbfree++; + cg_blktot(&acg)[cbtocylno(&sblock, i)]++; + cg_blks(&sblock, &acg, cbtocylno(&sblock, i)) + [cbtorpos(&sblock, i)]++; + } + + /* + * Handle the last new block if there are stll some new fragments left. + * Here we don't have to bother about the cluster summary or the even + * the rotational layout table. + */ + if (i < (dmax - cbase)) { + acg.cg_frsum[dmax - cbase - i]++; + for (; i < dmax - cbase; i++) { + setbit(cg_blksfree(&acg), i); + acg.cg_cs.cs_nffree++; + } + } + + sblock.fs_cstotal.cs_nffree += + (acg.cg_cs.cs_nffree - aocg.cg_cs.cs_nffree); + sblock.fs_cstotal.cs_nbfree += + (acg.cg_cs.cs_nbfree - aocg.cg_cs.cs_nbfree); + /* + * The following statistics are not changed here: + * sblock.fs_cstotal.cs_ndir + * sblock.fs_cstotal.cs_nifree + * As the statistics for this cylinder group are ready, copy it to + * the summary information array. + */ + *cs = acg.cg_cs; + + /* + * Write the updated "joining" cylinder group back to disk. + */ + wtfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), sblock.fs_cgsize, + (char *)&acg, fso, Nflag); + DBG_PRINT0("jcg written\n"); + DBG_DUMP_CG(&sblock, "new joining cg", &acg); + + DBG_LEAVE; + return; +} + +/* ********************************************************** updcsloc ***** */ +/* + * Here we update the location of the cylinder summary. We have two possible + * ways of growing the cylinder summary. + * (1) We can try to grow the summary in the current location, and relocate + * possibly used blocks within the current cylinder group. + * (2) Alternatively we can relocate the whole cylinder summary to the first + * new completely empty cylinder group. Once the cylinder summary is no + * longer in the beginning of the first cylinder group you should never + * use a version of fsck which is not aware of the possibility to have + * this structure in a non standard place. + * Option (1) is considered to be less intrusive to the structure of the file- + * system. So we try to stick to that whenever possible. If there is not enough + * space in the cylinder group containing the cylinder summary we have to use + * method (2). In case of active snapshots in the filesystem we probably can + * completely avoid implementing copy on write if we stick to method (2) only. + */ +static void +updcsloc(time_t utime, int fsi, int fso, int Nflag) +{ + DBG_FUNC("updcsloc") + struct csum *cs; + int ocscg, ncscg; + int blocks; + daddr_t cbase, dupper, odupper, d, f, g; + int ind; + int cylno, inc; + struct gfs_bpp *bp; + int i, l; + int lcs=0; + int block; + + DBG_ENTER; + + if(howmany(sblock.fs_cssize, sblock.fs_fsize) == + howmany(osblock.fs_cssize, osblock.fs_fsize)) { + /* + * No new fragment needed. + */ + DBG_LEAVE; + return; + } + ocscg=dtog(&osblock, osblock.fs_csaddr); + cs=fscs+ocscg; + blocks = 1+howmany(sblock.fs_cssize, sblock.fs_bsize)- + howmany(osblock.fs_cssize, osblock.fs_bsize); + + /* + * Read original cylinder group from disk, and make a copy. + */ + rdfs(fsbtodb(&osblock, cgtod(&osblock, ocscg)), osblock.fs_cgsize, + (char *)&aocg, fsi); + DBG_PRINT0("oscg read\n"); + DBG_DUMP_CG(&sblock, "old summary cg", &aocg); + + memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); + + /* + * Touch the cylinder group, set up local variables needed later + * and update the superblock. + */ + acg.cg_time = utime; + + /* + * XXX In the case of having active snapshots we may need much more + * blocks for the copy on write. We need each block twice, and + * also up to 8*3 blocks for indirect blocks for all possible + * references. + */ + if(/*((int)sblock.fs_time&0x3)>0||*/ cs->cs_nbfree < blocks) { + /* + * There is not enough space in the old cylinder group to + * relocate all blocks as needed, so we relocate the whole + * cylinder group summary to a new group. We try to use the + * first complete new cylinder group just created. Within the + * cylinder group we allign the area immediately after the + * cylinder group information location in order to be as + * close as possible to the original implementation of ffs. + * + * First we have to make sure we'll find enough space in the + * new cylinder group. If not, then we currently give up. + * We start with freeing everything which was used by the + * fragments of the old cylinder summary in the current group. + * Now we write back the group meta data, read in the needed + * meta data from the new cylinder group, and start allocating + * within that group. Here we can assume, the group to be + * completely empty. Which makes the handling of fragments and + * clusters a lot easier. + */ + DBG_TRC; + if(sblock.fs_ncg-osblock.fs_ncg < 2) { + fprintf(stderr, "growfs: panic, not enough space\n"); + exit(2); + } + + /* + * Point "d" to the first fragment not used by the cylinder + * summary. + */ + d=osblock.fs_csaddr+(osblock.fs_cssize/osblock.fs_fsize); + + /* + * Set up last cluster size ("lcs") already here. Calculate + * the size for the trailing cluster just behind where "d" + * points to. + */ + if(sblock.fs_contigsumsize > 0) { + for(block=howmany(d%sblock.fs_fpg, sblock.fs_frag), + lcs=0; lcs 0) { + setbit(cg_clustersfree(&acg), + (d%sblock.fs_fpg)/sblock.fs_frag); + if(lcs < sblock.fs_contigsumsize) { + if(lcs) { + cg_clustersum(&acg) + [lcs]--; + } + lcs++; + cg_clustersum(&acg)[lcs]++; + } + } + } + /* + * Point "d" to the first fragment of the block before + * the last incomplete block. + */ + d--; + } + + DBG_PRINT1("d=%d\n",d); + for(d=rounddown(d, sblock.fs_frag); d >= osblock.fs_csaddr; + d-=sblock.fs_frag) { + DBG_TRC; + DBG_PRINT1("d=%d\n",d); + setblock(&sblock, cg_blksfree(&acg), + (d%sblock.fs_fpg)/sblock.fs_frag); + acg.cg_cs.cs_nbfree++; + sblock.fs_cstotal.cs_nbfree++; + cg_blktot(&acg)[cbtocylno(&sblock, d%sblock.fs_fpg)]++; + cg_blks(&sblock, &acg, cbtocylno(&sblock, + d%sblock.fs_fpg))[cbtorpos(&sblock, + d%sblock.fs_fpg)]++; + if(sblock.fs_contigsumsize > 0) { + setbit(cg_clustersfree(&acg), + (d%sblock.fs_fpg)/sblock.fs_frag); + /* + * The last cluster size is already set up. + */ + if(lcs < sblock.fs_contigsumsize) { + if(lcs) { + cg_clustersum(&acg)[lcs]--; + } + lcs++; + cg_clustersum(&acg)[lcs]++; + } + } + } + *cs = acg.cg_cs; + + /* + * Now write the former cylinder group containing the cylinder + * summary back to disk. + */ + wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)), sblock.fs_cgsize, + (char *)&acg, fso, Nflag); + DBG_PRINT0("oscg written\n"); + DBG_DUMP_CG(&sblock, "old summary cg", &acg); + + /* + * Find the beginning of the new cylinder group containing the + * cylinder summary. + */ + sblock.fs_csaddr=cgdmin(&sblock, osblock.fs_ncg); + ncscg=dtog(&sblock, sblock.fs_csaddr); + cs=fscs+ncscg; + + /* + * Read the future cylinder group containing the cylinder + * summary from disk, and make a copy. + */ + rdfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), + sblock.fs_cgsize, (char *)&aocg, fsi); + DBG_PRINT0("nscg read\n"); + DBG_DUMP_CG(&sblock, "new summary cg", &aocg); + + memcpy((void *)&cgun1, (void *)&cgun2, sizeof(cgun2)); + + /* + * Allocate all complete blocks used by the new cylinder + * summary. + */ + for(d=sblock.fs_csaddr; d+sblock.fs_frag <= + sblock.fs_csaddr+(sblock.fs_cssize/sblock.fs_fsize); + d+=sblock.fs_frag) { + clrblock(&sblock, cg_blksfree(&acg), + (d%sblock.fs_fpg)/sblock.fs_frag); + acg.cg_cs.cs_nbfree--; + sblock.fs_cstotal.cs_nbfree--; + cg_blktot(&acg)[cbtocylno(&sblock, d%sblock.fs_fpg)]--; + cg_blks(&sblock, &acg, cbtocylno(&sblock, + d%sblock.fs_fpg))[cbtorpos(&sblock, + d%sblock.fs_fpg)]--; + if(sblock.fs_contigsumsize > 0) { + clrbit(cg_clustersfree(&acg), + (d%sblock.fs_fpg)/sblock.fs_frag); + } + } + + /* + * Allocate all fragments used by the cylinder summary in the + * last block. + */ + if(d 0) { + clrbit(cg_clustersfree(&acg), + (d%sblock.fs_fpg)/sblock.fs_frag); + } + + frag_adjust(d%sblock.fs_fpg, +1); + } + /* + * XXX Handle the cluster statistics here in the case this + * cylinder group is now almost full, and the remaining + * space is less then the maximum cluster size. This is + * probably not needed, as you would hardly find a file + * system which has only MAXCSBUFS+FS_MAXCONTIG of free + * space right behind the cylinder group information in + * any new cylinder group. + */ + + /* + * Update our statistics in the cylinder summary. + */ + *cs = acg.cg_cs; + + /* + * Write the new cylinder group containing the cylinder summary + * back to disk. + */ + wtfs(fsbtodb(&sblock, cgtod(&sblock, ncscg)), sblock.fs_cgsize, + (char *)&acg, fso, Nflag); + DBG_PRINT0("nscg written\n"); + DBG_DUMP_CG(&sblock, "new summary cg", &acg); + + DBG_LEAVE; + return; + } + /* + * We have got enough of space in the current cylinder group, so we + * can relocate just a few blocks, and let the summary information + * grow in place where it is right now. + */ + DBG_TRC; + + cbase = cgbase(&osblock, ocscg); /* old and new are equal */ + dupper = sblock.fs_csaddr - cbase + + howmany(sblock.fs_cssize, sblock.fs_fsize); + odupper = osblock.fs_csaddr - cbase + + howmany(osblock.fs_cssize, osblock.fs_fsize); + + sblock.fs_dsize -= dupper-odupper; + + /* + * Allocate the space for the array of blocks to be relocated. + */ + bp=(struct gfs_bpp *)malloc(((dupper-odupper)/sblock.fs_frag+2)* + sizeof(struct gfs_bpp)); + memset((char *)bp, 0, sizeof(struct gfs_bpp)); + + /* + * Lock all new frags needed for the cylinder group summary. This is + * done per fragment in the first and last block of the new required + * area, and per block for all other blocks. + * + * Handle the first new block here (but only if some fragments where + * already used for the cylinder summary). + */ + ind=0; + frag_adjust(odupper, -1); + for(d=odupper; ((d= dupper) { + bp[ind].flags|=GFS_FL_LAST; + } + ind++; + } + } else { + clrbit(cg_blksfree(&acg), d); + acg.cg_cs.cs_nffree--; + sblock.fs_cstotal.cs_nffree--; + } + /* + * No cluster handling is needed here, as there was at least + * one fragment in use by the cylinder summary in the old + * file system. + * No block-free counter handling here as this block was not + * a free block. + */ + } + frag_adjust(odupper, 1); + + /* + * Handle all needed complete blocks here. + */ + for(; d+sblock.fs_frag<=dupper; d+=sblock.fs_frag) { + DBG_PRINT1("scg block check loop d=%d\n", d); + if(!isblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag)) { + for(f=d; f 0) { + clrbit(cg_clustersfree(&acg), d/sblock.fs_frag); + for(lcs=0, l=(d/sblock.fs_frag)+1; + lcs 0) { + clrbit(cg_clustersfree(&acg), d/sblock.fs_frag); + for(lcs=0, l=(d/sblock.fs_frag)+1; + lcs%d) block relocated\n", bp[i].old, bp[i].new); + } + + /* + * Now we have to update all references to any fragment which + * belongs to any block relocated. We iterate now over all + * cylinder groups, within those over all non zero length + * inodes. + */ + for(cylno=0; cylno=0 ; inc--) { + updrefs(cylno, (ino_t)inc, bp, fsi, fso, Nflag); + } + } + + /* + * All inodes are checked, now make sure the number of + * references found make sense. + */ + for(i=0; isblock.fs_frag)) { + fprintf(stderr, + "error: %d refs found for block %d.\n", + bp[i].found, bp[i].old); + } + + } + } + /* + * The following statistics are not changed here: + * sblock.fs_cstotal.cs_ndir + * sblock.fs_cstotal.cs_nifree + * The following statistics were already updated on the fly: + * sblock.fs_cstotal.cs_nffree + * sblock.fs_cstotal.cs_nbfree + * As the statistics for this cylinder group are ready, copy it to + * the summary information array. + */ + + *cs = acg.cg_cs; + + /* + * Write summary cylinder group back to disk. + */ + wtfs(fsbtodb(&sblock, cgtod(&sblock, ocscg)), sblock.fs_cgsize, + (char *)&acg, fso, Nflag); + DBG_PRINT0("scg written\n"); + DBG_DUMP_CG(&sblock, "new summary cg", &acg); + + DBG_LEAVE; + return; +} + +/* ************************************************************** rdfs ***** */ +/* + * Here we read some block(s) from disk. + */ +static 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; +} + +/* ************************************************************** wtfs ***** */ +/* + * Here we write some block(s) to disk. + */ +static void +wtfs(daddr_t bno, int size, char *bf, int fso, int Nflag) +{ + DBG_FUNC("wtfs") + int n; + + DBG_ENTER; + + if (Nflag) { + DBG_LEAVE; + return; + } + if (lseek(fso, (off_t)bno * DEV_BSIZE, SEEK_SET) < 0) { + fprintf(stderr, "seek error: %ld\n", (long)bno); + err(35, "wtfs"); + } + n = write(fso, bf, (size_t)size); + if (n != size) { + fprintf(stderr, "write error: %ld\n", (long)bno); + err(36, "wtfs"); + } + + DBG_LEAVE; + return; +} + +/* ************************************************************* alloc ***** */ +/* + * Here we allocate a free block in the current cylinder group. It is assumed, + * that acg contains the current cylinder group. As we may take a block from + * somewhere in the filesystem we have to handle cluster summary here. + */ +static daddr_t +alloc(void) +{ + DBG_FUNC("alloc") + daddr_t d, blkno; + int lcs1, lcs2; + int l; + int csmin, csmax; + int dlower, dupper, dmax; + + DBG_ENTER; + + if (acg.cg_magic != CG_MAGIC) { + fprintf(stderr, "acg: bad magic number\n"); + DBG_LEAVE; + return (0); + } + if (acg.cg_cs.cs_nbfree == 0) { + fprintf(stderr, "error: cylinder group ran out of space\n"); + DBG_LEAVE; + return (0); + } + /* + * We start seeking for free blocks only from the space available after + * the end of the new grown cylinder summary. Otherwise we allocate a + * block here which we have to relocate a couple of seconds later again + * again, and we are not prepared to to this anyway. + */ + blkno=-1; + dlower=cgsblock(&sblock, acg.cg_cgx)-cgbase(&sblock, acg.cg_cgx); + dupper=cgdmin(&sblock, acg.cg_cgx)-cgbase(&sblock, acg.cg_cgx); + dmax=cgbase(&sblock, acg.cg_cgx)+sblock.fs_fpg; + if (dmax > sblock.fs_size) { + dmax = sblock.fs_size; + } + dmax-=cgbase(&sblock, acg.cg_cgx); /* retransform into cg */ + csmin=sblock.fs_csaddr-cgbase(&sblock, acg.cg_cgx); + csmax=csmin+howmany(sblock.fs_cssize, sblock.fs_fsize); + DBG_PRINT3("seek range: dl=%d, du=%d, dm=%d\n", dlower, dupper, dmax); + DBG_PRINT2("range cont: csmin=%d, csmax=%d\n", csmin, csmax); + + for(d=0; (d=csmin && d<=csmax) { + continue; + } + if(isblock(&sblock, cg_blksfree(&acg), fragstoblks(&sblock, + d))) { + blkno = fragstoblks(&sblock, d);/* Yeah found a block */ + break; + } + } + for(d=dupper; (d=csmin && d<=csmax) { + continue; + } + if(isblock(&sblock, cg_blksfree(&acg), fragstoblks(&sblock, + d))) { + blkno = fragstoblks(&sblock, d);/* Yeah found a block */ + break; + } + } + if(blkno==-1) { + fprintf(stderr, + "internal error: couldn't find promised block in cg\n"); + DBG_LEAVE; + return (0); + } + + /* + * This is needed if the block was found already in the first loop. + */ + d=blkstofrags(&sblock, blkno); + + clrblock(&sblock, cg_blksfree(&acg), blkno); + if (sblock.fs_contigsumsize > 0) { + /* + * Handle the cluster allocation bitmap. + */ + clrbit(cg_clustersfree(&acg), blkno); + /* + * We possibly have split a cluster here, so we have to do + * recalculate the sizes of the remaining cluster halfes now, + * and use them for updating the cluster summary information. + * + * Lets start with the blocks before our allocated block ... + */ + for(lcs1=0, l=blkno-1; lcs1fs_frag) { + case 8: + DBG_LEAVE; + return (cp[h] == 0xff); + case 4: + mask = 0x0f << ((h & 0x1) << 2); + DBG_LEAVE; + return ((cp[h >> 1] & mask) == mask); + case 2: + mask = 0x03 << ((h & 0x3) << 1); + DBG_LEAVE; + return ((cp[h >> 2] & mask) == mask); + case 1: + mask = 0x01 << (h & 0x7); + DBG_LEAVE; + return ((cp[h >> 3] & mask) == mask); + default: + fprintf(stderr, "isblock bad fs_frag %d\n", fs->fs_frag); + DBG_LEAVE; + return (0); + } +} + +/* ********************************************************** clrblock ***** */ +/* + * Here we allocate a complete block in the block map. For more details again + * please see the source of newfs(8), as this function is taken over almost + * unchanged. + */ +static void +clrblock(struct fs *fs, unsigned char *cp, int h) +{ + DBG_FUNC("clrblock") + + DBG_ENTER; + + switch ((fs)->fs_frag) { + case 8: + cp[h] = 0; + break; + case 4: + cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2)); + break; + case 2: + cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1)); + break; + case 1: + cp[h >> 3] &= ~(0x01 << (h & 0x7)); + break; + default: + fprintf(stderr, "clrblock bad fs_frag %d\n", fs->fs_frag); + break; + } + + DBG_LEAVE; + return; +} + +/* ********************************************************** setblock ***** */ +/* + * Here we free a complete block in the free block map. For more details again + * please see the source of newfs(8), as this function is taken over almost + * unchanged. + */ +static void +setblock(struct fs *fs, unsigned char *cp, int h) +{ + DBG_FUNC("setblock") + + DBG_ENTER; + + switch (fs->fs_frag) { + case 8: + cp[h] = 0xff; + break; + case 4: + cp[h >> 1] |= (0x0f << ((h & 0x1) << 2)); + break; + case 2: + cp[h >> 2] |= (0x03 << ((h & 0x3) << 1)); + break; + case 1: + cp[h >> 3] |= (0x01 << (h & 0x7)); + break; + default: + fprintf(stderr, "setblock bad fs_frag %d\n", fs->fs_frag); + break; + } + + DBG_LEAVE; + return; +} + +/* ************************************************************ 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. + */ +static struct dinode * +ginode(ino_t inumber, int fsi, int cg) +{ + 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; + inumber+=(cg * sblock.fs_ipg); + 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); + in_src=fsbtodb(&sblock, iblk); + rdfs(in_src, sblock.fs_bsize, (char *)&ablk, fsi); + startinum = (inumber / INOPB(&sblock)) * INOPB(&sblock); + } + + DBG_LEAVE; + return (&(pi[inumber % INOPB(&sblock)])); +} + +/* ****************************************************** charsperline ***** */ +/* + * Figure out how many lines our current terminal has. For more details again + * please see the source of newfs(8), as this function is taken over almost + * unchanged. + */ +static int +charsperline(void) +{ + DBG_FUNC("charsperline") + int columns; + char *cp; + struct winsize ws; + + DBG_ENTER; + + columns = 0; + if (ioctl(0, TIOCGWINSZ, &ws) != -1) { + columns = ws.ws_col; + } + if (columns == 0 && (cp = getenv("COLUMNS"))) { + columns = atoi(cp); + } + if (columns == 0) { + columns = 80; /* last resort */ + } + + DBG_LEAVE; + return columns; +} + +/* ************************************************************** main ***** */ +/* + * growfs(8) is a utility which allows to increase the size of an existing + * ufs filesystem. Currently this can only be done on unmounted file system. + * It recognizes some command line options to specify the new desired size, + * and it does some basic checkings. The old file system size is determined + * and after some more checks like we can really access the new last block + * on the disk etc. we calculate the new parameters for the superblock. After + * having done this we just call growfs() which will do the work. Before + * we finish the only thing left is to update the disklabel. + * We still have to provide support for snapshots. Therefore we first have to + * understand what data structures are always replicated in the snapshot on + * creation, for all other blocks we touch during our procedure, we have to + * keep the old blocks unchanged somewere available for the snapshots. If we + * are lucky, then we only have to handle our blocks to be relocated in that + * way. + * Also we have to consider in what order we actually update the critical + * data structures of the filesystem to make sure, that in case of a desaster + * fsck(8) is still able to restore any lost data. + * The forseen last step then will be to provide for growing even mounted + * file systems. There we have to extend the mount() systemcall to provide + * userland access to the file system locking facility. + */ +int +main(int argc, char **argv) +{ + DBG_FUNC("main") + char *a0, *device, *special, *cp; + char ch; + unsigned long size=0; + size_t len; + int Nflag=0; + int ExpertFlag=0; + struct stat st; + struct disklabel *lp; + struct partition *pp; + int fsi,fso; + char reply[5]; +#ifdef FSMAXSNAP + int j; +#endif /* FSMAXSNAP */ + + DBG_ENTER; + + a0=*argv; /* save argv[0] for usage() */ + while((ch=getopt(argc, argv, "Ns:vy")) != -1) { + switch(ch) { + case 'N': + Nflag=1; + break; + case 's': + size=(size_t)atol(optarg); + if(size<1) { + usage(a0); + } + break; + case 'v': /* for compatibility to newfs */ + break; + case 'y': + ExpertFlag=1; + break; + case '?': + /* FALLTHROUGH */ + default: + usage(a0); + } + } + argc -= optind; + argv += optind; + + if(argc != 1) { + usage(a0); + } + device=*argv; + + /* + * Now try to guess the (raw)device name. + */ + if (0 == strrchr(device, '/')) { + /* + * 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; + } + + /* + * Try to access our devices for writing ... + */ + if (Nflag) { + fso = -1; + } else { + fso = open(device, O_WRONLY); + if (fso < 0) { + fprintf(stderr, "%s: %s\n", device, strerror(errno)); + exit(-1); + } + } + + /* + * ... and reading. + */ + fsi = open(device, O_RDONLY); + if (fsi < 0) { + fprintf(stderr, "%s: %s\n", device, strerror(errno)); + exit(-1); + } + + /* + * 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 growing a file system. + */ + 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, and take a backup. + */ + rdfs((daddr_t)(SBOFF/DEV_BSIZE), SBSIZE, (char *)&(osblock), fsi); + if (osblock.fs_magic != FS_MAGIC) { + fprintf(stderr, "superblock not recognized\n"); + exit(-1); + } + memcpy((void *)&fsun1, (void *)&fsun2, sizeof(fsun2)); + + DBG_OPEN("/tmp/growfs.debug"); /* already here we need a superblock */ + DBG_DUMP_FS(&sblock, "old sblock"); + + /* + * Determine size to grow to. Default to the full size specified in + * the disk label. + */ + sblock.fs_size = dbtofsb(&osblock, pp->p_size); + if (size != 0) { + if (size > pp->p_size){ + fprintf(stderr, + "There is not enough space (%d < %ld)\n", + pp->p_size, size); + exit(-1); + } + sblock.fs_size = dbtofsb(&osblock, size); + } + + /* + * Are we really growing ? + */ + if(osblock.fs_size >= sblock.fs_size) { + fprintf(stderr, "we are not growing (%d->%d)\n", + osblock.fs_size, sblock.fs_size); + exit(-1); + } + + +#ifdef FSMAXSNAP + /* + * Check if we find an active snapshot. + */ + if(ExpertFlag == 0) { + for(j=0; jp_size-1, DEV_BSIZE, (char *)&sblock, fso, Nflag); + + /* + * Now calculate new superblock values and check for reasonable + * bound for new file system size: + * fs_size: is derived from label or user input + * fs_dsize: should get updated in the routines creating or + * updating the cylinder groups on the fly + * fs_cstotal: should get updated in the routines creating or + * updating the cylinder groups + */ + + /* + * Update the number of cylinders in the filesystem. + */ + sblock.fs_ncyl = sblock.fs_size * NSPF(&sblock) / sblock.fs_spc; + if (sblock.fs_size * NSPF(&sblock) > sblock.fs_ncyl * sblock.fs_spc) { + sblock.fs_ncyl++; + } + + /* + * Update the number of cylinder groups in the filesystem. + */ + sblock.fs_ncg = sblock.fs_ncyl / sblock.fs_cpg; + if (sblock.fs_ncyl % sblock.fs_cpg) { + sblock.fs_ncg++; + } + + if ((sblock.fs_size - (sblock.fs_ncg-1) * sblock.fs_fpg) < + sblock.fs_fpg && cgdmin(&sblock, (sblock.fs_ncg-1))- + cgbase(&sblock, (sblock.fs_ncg-1)) > (sblock.fs_size - + (sblock.fs_ncg-1) * sblock.fs_fpg )) { + /* + * The space in the new last cylinder group is too small, + * so revert back. + */ + sblock.fs_ncg--; +#if 1 /* this is a bit more safe */ + sblock.fs_ncyl = sblock.fs_ncg * sblock.fs_cpg; +#else + sblock.fs_ncyl -= sblock.fs_ncyl % sblock.fs_cpg; +#endif + sblock.fs_ncyl -= sblock.fs_ncyl % sblock.fs_cpg; + printf( "Warning: %d sector(s) cannot be allocated.\n", + (sblock.fs_size-(sblock.fs_ncg)*sblock.fs_fpg) * + NSPF(&sblock)); + sblock.fs_size = sblock.fs_ncyl * sblock.fs_spc / NSPF(&sblock); + } + + /* + * Update the space for the cylinder group summary information in the + * respective cylinder group data area. + */ + sblock.fs_cssize = + fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum)); + + if(osblock.fs_size >= sblock.fs_size) { + fprintf(stderr, "not enough new space\n"); + exit(-1); + } + + DBG_PRINT0("sblock calculated\n"); + + /* + * Ok, everything prepared, so now let's do the tricks. + */ + growfs(fsi, fso, Nflag); + + /* + * Update the disk label. + */ + pp->p_fsize = sblock.fs_fsize; + pp->p_frag = sblock.fs_frag; + pp->p_cpg = sblock.fs_cpg; + + return_disklabel(fso, lp, Nflag); + DBG_PRINT0("label rewritten\n"); + + close(fsi); + if(fso>-1) close(fso); + + DBG_CLOSE; + + DBG_LEAVE; + return 0; +} + +/* ************************************************** return_disklabel ***** */ +/* + * Write the updated disklabel back to disk. + */ +static void +return_disklabel(int fd, struct disklabel *lp, int Nflag) +{ + DBG_FUNC("return_disklabel") + u_short sum; + u_short *ptr; + + DBG_ENTER; + + if(!lp) { + DBG_LEAVE; + return; + } + if(!Nflag) { + lp->d_checksum=0; + sum = 0; + ptr=(u_short *)lp; + + /* + * recalculate checksum + */ + while(ptr < (u_short *)&lp->d_partitions[lp->d_npartitions]) { + sum ^= *ptr++; + } + lp->d_checksum=sum; + + if (ioctl(fd, DIOCWDINFO, (char *)lp) < 0) { + fprintf(stderr, "DIOCWDINFO failed\n"); + exit(-1); + } + } + free(lp); + + DBG_LEAVE; + return ; +} + +/* ***************************************************** get_disklabel ***** */ +/* + * Read the disklabel from disk. + */ +static 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. + */ +static void +usage(char *name) +{ + DBG_FUNC("usage") + char *basename; + + DBG_ENTER; + + basename=strrchr(name, '/'); + if(!basename) { + basename=name; + } else { + basename++; + } + fprintf(stderr, "usage: %s [-Ny] [-s size] special_file\n", basename); + DBG_LEAVE; + exit(-1); +} + +/* *********************************************************** updclst ***** */ +/* + * This updates most paramters and the bitmap related to cluster. We have to + * assume, that sblock, osblock, acg are set up. + */ +static void +updclst(int block) +{ + DBG_FUNC("updclst") + static int lcs=0; + + DBG_ENTER; + + if(sblock.fs_contigsumsize < 1) { /* no clustering */ + return; + } + /* + * update cluster allocation map + */ + setbit(cg_clustersfree(&acg), block); + + /* + * update cluster summary table + */ + if(!lcs) { + /* + * calculate size for the trailing cluster + */ + for(block--; lcsdi_mode & IFMT)==IFDIR || (ino->di_mode & IFMT)==IFREG || + (ino->di_mode & IFMT)==IFLNK)) { + DBG_LEAVE; + return; /* only check DIR, FILE, LINK */ + } + if(((ino->di_mode & IFMT)==IFLNK) && (ino->di_sizedi_size) { + DBG_LEAVE; + return; /* skip empty file */ + } + if(!ino->di_blocks) { + DBG_LEAVE; + return; /* skip empty swiss cheesy file or old fastlink */ + } + DBG_PRINT2("scg checking inode (%d in %d)\n", in, cg); + + /* + * Start checking all direct blocks. + */ + remaining_blocks=howmany(ino->di_size, sblock.fs_bsize); + for(ictr=0; ictr < MIN(NDADDR, (unsigned int)remaining_blocks); + ictr++) { + iptr=&(ino->di_db[ictr]); + if(*iptr) { + cond_bl_upd(iptr, bp, GFS_PS_INODE, fso, Nflag); + } + } + DBG_PRINT0("~~scg direct blocks checked\n"); + + remaining_blocks-=NDADDR; + if(remaining_blocks<0) { + DBG_LEAVE; + return; + } + if(ino->di_ib[0]) { + /* + * Start checking first indirect block + */ + cond_bl_upd(&(ino->di_ib[0]), bp, GFS_PS_INODE, fso, Nflag); + i1_src=fsbtodb(&sblock, ino->di_ib[0]); + rdfs(i1_src, sblock.fs_bsize, (char *)&i1blk, fsi); + for(ictr=0; ictr < MIN(howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)), (unsigned int)remaining_blocks); + ictr++) { + iptr=&((ufs_daddr_t *)&i1blk)[ictr]; + if(*iptr) { + cond_bl_upd(iptr, bp, GFS_PS_IND_BLK_LVL1, + fso, Nflag); + } + } + } + DBG_PRINT0("scg indirect_1 blocks checked\n"); + + remaining_blocks-= howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)); + if(remaining_blocks<0) { + DBG_LEAVE; + return; + } + if(ino->di_ib[1]) { + /* + * Start checking second indirect block + */ + cond_bl_upd(&(ino->di_ib[1]), bp, GFS_PS_INODE, fso, Nflag); + i2_src=fsbtodb(&sblock, ino->di_ib[1]); + rdfs(i2_src, sblock.fs_bsize, (char *)&i2blk, fsi); + for(ind2ctr=0; ind2ctr < howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)); ind2ctr++) { + ind2ptr=&((ufs_daddr_t *)&i2blk)[ind2ctr]; + if(!*ind2ptr) { + continue; + } + cond_bl_upd(ind2ptr, bp, GFS_PS_IND_BLK_LVL2, fso, + Nflag); + i1_src=fsbtodb(&sblock, *ind2ptr); + rdfs(i1_src, sblock.fs_bsize, (char *)&i1blk, fsi); + for(ictr=0; ictrdi_ib[2]) { + /* + * Start checking third indirect block + */ + cond_bl_upd(&(ino->di_ib[2]), bp, GFS_PS_INODE, fso, Nflag); + i3_src=fsbtodb(&sblock, ino->di_ib[2]); + rdfs(i3_src, sblock.fs_bsize, (char *)&i3blk, fsi); + for(ind3ctr=0; ind3ctr < howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)); ind3ctr ++) { + ind3ptr=&((ufs_daddr_t *)&i3blk)[ind3ctr]; + if(!*ind3ptr) { + continue; + } + cond_bl_upd(ind3ptr, bp, GFS_PS_IND_BLK_LVL3, fso, + Nflag); + i2_src=fsbtodb(&sblock, *ind3ptr); + rdfs(i2_src, sblock.fs_bsize, (char *)&i2blk, fsi); + for(ind2ctr=0; ind2ctr < howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)); ind2ctr ++) { + ind2ptr=&((ufs_daddr_t *)&i2blk)[ind2ctr]; + if(!*ind2ptr) { + continue; + } + cond_bl_upd(ind2ptr, bp, GFS_PS_IND_BLK_LVL2, + fso, Nflag); + i1_src=fsbtodb(&sblock, *ind2ptr); + rdfs(i1_src, sblock.fs_bsize, (char *)&i1blk, + fsi); + for(ictr=0; ictr < MIN(howmany(sblock.fs_bsize, + sizeof(ufs_daddr_t)), + (unsigned int)remaining_blocks); ictr++) { + iptr=&((ufs_daddr_t *)&i1blk)[ictr]; + if(*iptr) { + cond_bl_upd(iptr, bp, + GFS_PS_IND_BLK_LVL1, fso, + Nflag); + } + } + } + } + } + + DBG_PRINT0("scg indirect_3 blocks checked\n"); + + DBG_LEAVE; + return; +} +