summaryrefslogtreecommitdiffstats
path: root/sbin
diff options
context:
space:
mode:
authortomsoft <tomsoft@FreeBSD.org>2000-12-09 15:27:35 +0000
committertomsoft <tomsoft@FreeBSD.org>2000-12-09 15:27:35 +0000
commita11b7b009aa17eb8aaa1250f191e5fa0f9ee880e (patch)
tree49941b9fe788fe17d4b570f5200728f318545895 /sbin
parent861f7be1085c61e97f6164d6bd4ffe5555b64d57 (diff)
downloadFreeBSD-src-a11b7b009aa17eb8aaa1250f191e5fa0f9ee880e.zip
FreeBSD-src-a11b7b009aa17eb8aaa1250f191e5fa0f9ee880e.tar.gz
added growfs(8) including ffsinfo(8) to the freebsd base system
Reviewed by: grog
Diffstat (limited to 'sbin')
-rw-r--r--sbin/Makefile2
-rw-r--r--sbin/ffsinfo/Makefile19
-rw-r--r--sbin/ffsinfo/ffsinfo.8132
-rw-r--r--sbin/ffsinfo/ffsinfo.c615
-rw-r--r--sbin/fsck_ffs/pass1.c6
-rw-r--r--sbin/growfs/Makefile21
-rw-r--r--sbin/growfs/debug.c699
-rw-r--r--sbin/growfs/debug.h137
-rw-r--r--sbin/growfs/growfs.8154
-rw-r--r--sbin/growfs/growfs.c2476
10 files changed, 4260 insertions, 1 deletions
diff --git a/sbin/Makefile b/sbin/Makefile
index 6cb4d8f..102f8b3 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 0000000..8098d89
--- /dev/null
+++ b/sbin/ffsinfo/Makefile
@@ -0,0 +1,19 @@
+# @(#)Makefile 8.8 (Berkeley) 6/21/2000
+#
+# $TSHeader: src/sbin/ffsinfo/Makefile,v 1.3 2000/12/05 19:45:10 tomsoft Exp $
+# $FreeBSD$
+#
+
+MAINTAINER= tomsoft@FreeBSD.ORG, chm@FreeBSD.ORG
+
+#CFLAGS+=${BDECFLAGS}
+
+PROG= ffsinfo
+SRCS= ffsinfo.c debug.c
+MAN8= ffsinfo.8
+
+GROWFS= ${.CURDIR}/../growfs
+CFLAGS+=-DFS_DEBUG -I${GROWFS}
+.PATH: ${GROWFS}
+
+.include <bsd.prog.mk>
diff --git a/sbin/ffsinfo/ffsinfo.8 b/sbin/ffsinfo/ffsinfo.8
new file mode 100644
index 0000000..bfdcdc7
--- /dev/null
+++ b/sbin/ffsinfo/ffsinfo.8
@@ -0,0 +1,132 @@
+.\" Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+.\" Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgment:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors, as well as Christoph
+.\" Herrmann and Thomas-Henning von Kamptz.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $TSHeader: src/sbin/ffsinfo/ffsinfo.8,v 1.2 2000/12/09 15:12:31 tomsoft Exp $
+.\" $FreeBSD$
+.\"
+.Dd September 8, 2000
+.Dt FSINFO 8
+.Os BSD 4.4
+.Sh NAME
+.Nm ffsinfo
+.Nd dump all meta information of an existing ufs file system
+.Sh SYNOPSIS
+.Nm ffsinfo
+.Op Fl L
+.Op Fl g Ar cylinder group
+.Op Fl i Ar inode
+.Op Fl l Ar level
+.Op Fl o Ar outfile
+.Ar < special | file >
+.Sh DESCRIPTION
+.Nm Ffsinfo
+extends the
+.Xr dumpfs 8
+program.
+.Pp
+The output is generated into the file
+.Nm outfile .
+Also expect the output file to be rather large. Up to 2 percent of the size
+of the specified filesystem is not uncommon.
+.Nm Ffsinfo
+has some options to allow the defaults to be overridden.
+.\".Pp
+.Bl -tag -width indent
+.It Fl L
+Specifying this options skips the tests of the disklabel. This is automatically
+done, if the specified filename to dump is a plain file.
+.It Fl g Ar cylinder group
+This restrictes the dump to infomation about this cylinder group only. Here
+0 means the first cylinder group and -1 the last one.
+.It Fl i Ar inode
+This restrictes the dump to infomation about this particular inode only. Here
+the minimum acceptable inode is 2. If this option is omitted but a cylinder
+group is defined then only inodes within that cylinder group are dumped.
+.It Fl l Ar level
+The level of detail which will be dumped. This value defaults
+to 255 and is the bitwise and of the following table:
+.Bd -literal -offset left
+0x001 - initial superblock
+0x002 - superblock copys in each cylindergroup
+0x004 - cylinder group summary in initial cylinder group
+0x008 - cylinder group information
+0x010 - inode allocation bitmap
+0x020 - fragment allocation bitmap
+0x040 - cluster maps and summary
+0x080 - rotational layout tables
+0x100 - inode information
+0x200 - indirect block dump
+.Ed
+.It Fl o Ar outfile
+This allows to change the output filename where the dump is written to. The
+current default is
+.Nm /var/tmp/ffsinfo .
+.El
+.Sh EXAMPLES
+.Pp
+.Dl ffsinfo -l 1023 /dev/vinum/testvol
+.Pp
+will dump /dev/vinum/testvol with all available information.
+.Sh BUGS
+Currently
+.Nm
+can only dump unmounted file systems. Do not try dumping a mounted file system,
+your system may panic and you will not be able to use the file system any
+longer.
+.Pp
+Also snapshots are handled like plain files. They should get a their own
+level to provide for independent control of the amount of what gets dumped. It
+probably also makes sense to some extend to dump the snapshot as a filesystem.
+.Sh SEE ALSO
+.Xr vinum 8 ,
+.Xr disklabel 8 ,
+.Xr fsck 8 ,
+.Xr newfs 8 ,
+.Xr tunefs 8 ,
+.Xr growfs 8 ,
+.Xr dumpfs 8
+.\".Rs
+.\".Re
+.Sh AUTHORS
+.An Christoph Herrmann Aq chm@FreeBSD.ORG
+.An Thomas-Henning von Kamptz Aq tomsoft@FreeBSD.ORG
+.An The GROWFS team Aq growfs@Tomsoft.COM
+.Sh HISTORY
+The
+.Nm
+command appeared first in
+.Bx Free
+5.0
diff --git a/sbin/ffsinfo/ffsinfo.c b/sbin/ffsinfo/ffsinfo.c
new file mode 100644
index 0000000..ef5e94f
--- /dev/null
+++ b/sbin/ffsinfo/ffsinfo.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+ * Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgment:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors, as well as Christoph
+ * Herrmann and Thomas-Henning von Kamptz.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $TSHeader: src/sbin/ffsinfo/ffsinfo.c,v 1.3 2000/12/09 15:12:31 tomsoft Exp $
+ * $FreeBSD$
+ *
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz\n\
+Copyright (c) 1980, 1989, 1993 The Regents of the University of California.\n\
+All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/* ********************************************************** INCLUDES ***** */
+#include <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <paths.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <machine/param.h>
+
+#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_cg<sblock.fs_ncg) {
+ cg_start=cfg_cg;
+ cg_stop=cfg_cg+1;
+ } else {
+ cg_start=sblock.fs_ncg;
+ cg_stop=sblock.fs_ncg;
+ }
+
+ if (cfg_lv & 0x004) {
+ fscs = (struct csum *)calloc(1, (size_t)sblock.fs_cssize);
+
+ /*
+ * Get the cylinder summary into the memory ...
+ */
+ for (i = 0; i < sblock.fs_cssize; i += sblock.fs_bsize) {
+ rdfs(fsbtodb(&sblock, sblock.fs_csaddr +
+ numfrags(&sblock, i)), sblock.fs_cssize - i <
+ sblock.fs_bsize ? sblock.fs_cssize - i :
+ sblock.fs_bsize, ((char *)fscs) + i, fsi);
+ }
+
+ dbg_csp=fscs;
+ /*
+ * ... and dump it.
+ */
+ for(dbg_csc=0; dbg_csc<sblock.fs_ncg; dbg_csc++) {
+ snprintf(dbg_line, 80, "%d. csum in fscs",
+ dbg_csc);
+ DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++);
+ }
+ }
+
+ /*
+ * For each requested cylinder group ...
+ */
+ for(cylno=cg_start; cylno<cg_stop; cylno++) {
+ snprintf(dbg_line, 80, "cgr %d", cylno);
+ if(cfg_lv & 0x002) {
+ /*
+ * ... dump the superblock copies ...
+ */
+ rdfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ SBSIZE, (char *)&osblock, fsi);
+ DBG_DUMP_FS(&osblock, dbg_line);
+ }
+ /*
+ * ... read the cylinder group and dump whatever was requested.
+ */
+ rdfs(fsbtodb(&sblock, cgtod(&sblock, cylno)), sblock.fs_cgsize,
+ (char *)&acg, fsi);
+ if(cfg_lv & 0x008) {
+ DBG_DUMP_CG(&sblock, dbg_line, &acg);
+ }
+ if(cfg_lv & 0x010) {
+ DBG_DUMP_INMAP(&sblock, dbg_line, &acg);
+ }
+ if(cfg_lv & 0x020) {
+ DBG_DUMP_FRMAP(&sblock, dbg_line, &acg);
+ }
+ if(cfg_lv & 0x040) {
+ DBG_DUMP_CLMAP(&sblock, dbg_line, &acg);
+ DBG_DUMP_CLSUM(&sblock, dbg_line, &acg);
+ }
+ if(cfg_lv & 0x080) {
+ DBG_DUMP_SPTBL(&sblock, dbg_line, &acg);
+ }
+ }
+ /*
+ * Dump the requested inode(s).
+ */
+ if(cfg_in != -2) {
+ dump_whole_inode((ino_t)cfg_in, fsi, cfg_lv);
+ } else {
+ for(in=cg_start*sblock.fs_ipg; in<(ino_t)cg_stop*sblock.fs_ipg;
+ in++) {
+ dump_whole_inode(in, fsi, cfg_lv);
+ }
+ }
+
+ DBG_CLOSE;
+
+ close(fsi);
+
+ DBG_LEAVE;
+ return 0;
+}
+
+/* ************************************************** dump_whole_inode ***** */
+/*
+ * Here we dump a list of all blocks allocated by this inode. We follow
+ * all indirect blocks.
+ */
+void
+dump_whole_inode(ino_t inode, int fsi, int level)
+{
+ DBG_FUNC("dump_whole_inode")
+ struct dinode *ino;
+ int rb;
+ unsigned int ind2ctr, ind3ctr;
+ ufs_daddr_t *ind2ptr, *ind3ptr;
+ char comment[80];
+
+ DBG_ENTER;
+
+ /*
+ * Read the inode from disk/cache.
+ */
+ ino=ginode(inode, fsi);
+
+ if(ino->di_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 fcd596b..1045a5f 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 0000000..50dccff
--- /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 <bsd.prog.mk>
diff --git a/sbin/growfs/debug.c b/sbin/growfs/debug.c
new file mode 100644
index 0000000..3dacee0
--- /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 <sys/param.h>
+
+#include <stdio.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#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; i<sb->fs_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; j<FSMAXSNAP; j++) {
+ fprintf(dbg_log, "snapinum int32_t[%2d] 0x%08x\n",
+ j, sb->fs_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; j<MAXFRAG; j++) {
+ fprintf(dbg_log, "frsum int32_t[%d] 0x%08x\n", j,
+ cgr->cg_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; j<e; j+=32) {
+ fprintf(dbg_log, "%08x: ", j);
+ for(k=0; k<32; k+=8) {
+ if(j+k+8<e) {
+ fprintf(dbg_log,
+ "%02x%02x%02x%02x%02x%02x%02x%02x ",
+ cp[0], cp[1], cp[2], cp[3],
+ cp[4], cp[5], cp[6], cp[7]);
+ } else {
+ for(l=0; (l<8)&&(j+k+l<e); l++) {
+ fprintf(dbg_log, "%02x", cp[l]);
+ }
+ }
+ cp+=8;
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END INODE ALLOCATION MAP =====\n");
+
+ return;
+}
+
+
+/* **************************************************** dbg_dump_frmap ***** */
+/*
+ * Dump the fragment allocation map in one cylinder group.
+ */
+void
+dbg_dump_frmap(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 FRAGMENT ALLOCATION MAP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ cp=(unsigned char *)cg_blksfree(cgr);
+ e=howmany((sb->fs_cpg * sb->fs_spc / NSPF(sb)), NBBY);
+ for(j=0; j<e; j+=32) {
+ fprintf(dbg_log, "%08x: ", j);
+ for(k=0; k<32; k+=8) {
+ if(j+k+8<e) {
+ fprintf(dbg_log,
+ "%02x%02x%02x%02x%02x%02x%02x%02x ",
+ cp[0], cp[1], cp[2], cp[3],
+ cp[4], cp[5], cp[6], cp[7]);
+ } else {
+ for(l=0; (l<8)&&(j+k+l<e); l++) {
+ fprintf(dbg_log, "%02x", cp[l]);
+ }
+ }
+ cp+=8;
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END FRAGMENT ALLOCATION MAP =====\n");
+
+ return;
+}
+
+/* **************************************************** dbg_dump_clmap ***** */
+/*
+ * Dump the cluster allocation map in one cylinder group.
+ */
+void
+dbg_dump_clmap(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 CLUSTER ALLOCATION MAP =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ cp=(unsigned char *)cg_clustersfree(cgr);
+ e=howmany(sb->fs_cpg * sb->fs_spc / NSPB(sb), NBBY);
+ for(j=0; j<e; j+=32) {
+ fprintf(dbg_log, "%08x: ", j);
+ for(k=0; k<32; k+=8) {
+ if(j+k+8<e) {
+ fprintf(dbg_log,
+ "%02x%02x%02x%02x%02x%02x%02x%02x ",
+ cp[0], cp[1], cp[2], cp[3],
+ cp[4], cp[5], cp[6], cp[7]);
+ } else {
+ for(l=0; (l<8)&&(j+k+l<e); l++) {
+ fprintf(dbg_log, "%02x", cp[l]);
+ }
+ }
+ cp+=8;
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END CLUSTER ALLOCATION MAP =====\n");
+
+ return;
+}
+
+/* **************************************************** dbg_dump_clsum ***** */
+/*
+ * Dump the cluster availability summary of one cylinder group.
+ */
+void
+dbg_dump_clsum(struct fs *sb, const char *comment, struct cg *cgr)
+{
+ int j;
+ long *lp;
+
+ if(!dbg_log) {
+ return;
+ }
+
+ fprintf(dbg_log, "===== START CLUSTER SUMMARY =====\n");
+ fprintf(dbg_log, "# %d@%lx: %s\n", indent, (unsigned long)cgr, comment);
+ indent++;
+
+ lp=(long *)cg_clustersum(cgr);
+ for(j=0; j<=sb->fs_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; j<sb->fs_cpg; j++) {
+ fprintf(dbg_log, "%2d: %5ld = ", j, *lp++);
+ for(k=0; k<sb->fs_nrpos; k++) {
+ fprintf(dbg_log, "%4d", cg_blks(sb, cgr, j)[k]);
+ if(k<sb->fs_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)i<MIN(howmany(sb->fs_bsize, sizeof(ufs_daddr_t)),
+ length); i+=8) {
+ fprintf(dbg_log, "%04x: ", i);
+ for (j=0; j<8; j++) {
+ if((size_t)(i+j)<length) {
+ fprintf(dbg_log, "%08X ", *mem++);
+ }
+ }
+ fprintf(dbg_log, "\n");
+ }
+
+ indent--;
+ fprintf(dbg_log, "===== END INDIRECT BLOCK DUMP =====\n");
+
+ return;
+}
+
+#endif /* FS_DEBUG */
+
diff --git a/sbin/growfs/debug.h b/sbin/growfs/debug.h
new file mode 100644
index 0000000..78ac625
--- /dev/null
+++ b/sbin/growfs/debug.h
@@ -0,0 +1,137 @@
+/*
+ * 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.h,v 1.2 2000/11/16 18:43:50 tom Exp $
+ * $FreeBSD$
+ *
+ */
+
+#ifdef FS_DEBUG
+
+/* ********************************************************** INCLUDES ***** */
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+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 0000000..5393a4f
--- /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 0000000..6642d41
--- /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 <sys/param.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <paths.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include <machine/param.h>
+
+#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<osblock.fs_ncg; dbg_csc++) {
+ snprintf(dbg_line, 80, "%d. old csum in old location", dbg_csc);
+ DBG_DUMP_CSUM(&osblock, dbg_line, dbg_csp++);
+ }
+}
+#endif /* FS_DEBUG */
+ DBG_PRINT0("fscs read\n");
+
+ /*
+ * Do all needed changes in the former last cylinder group.
+ */
+ updjcg(osblock.fs_ncg-1, utime, fsi, fso, Nflag);
+
+ /*
+ * Dump out summary information about file system.
+ */
+ printf("growfs:\t%d sectors in %d %s of %d tracks, %d sectors\n",
+ sblock.fs_size * NSPF(&sblock), sblock.fs_ncyl,
+ "cylinders", sblock.fs_ntrak, sblock.fs_nsect);
+#define B2MBFACTOR (1 / (1024.0 * 1024.0))
+ printf("\t%.1fMB in %d cyl groups (%d c/g, %.2fMB/g, %d i/g)\n",
+ (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_ncg, sblock.fs_cpg,
+ (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+ sblock.fs_ipg);
+#undef B2MBFACTOR
+
+ /*
+ * Now build the cylinders group blocks and
+ * then print out indices of cylinder groups.
+ */
+ printf("super-block backups (for fsck -b #) at:\n");
+ i = 0;
+ width = charsperline();
+
+ /*
+ * Iterate for only the new cylinder groups.
+ */
+ for (cylno = osblock.fs_ncg; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, utime, fso, Nflag);
+ j = sprintf(tmpbuf, " %ld%s",
+ fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ cylno < (sblock.fs_ncg-1) ? "," : "" );
+ if (i + j >= width) {
+ printf("\n");
+ i = 0;
+ }
+ i += j;
+ printf("%s", tmpbuf);
+ fflush(stdout);
+ }
+ printf("\n");
+
+ /*
+ * Do all needed changes in the first cylinder group.
+ * allocate blocks in new location
+ */
+ updcsloc(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_ncg; dbg_csc++) {
+ snprintf(dbg_line, 80, "%d. new csum in new location", dbg_csc);
+ DBG_DUMP_CSUM(&sblock, dbg_line, dbg_csp++);
+ }
+}
+#endif /* FS_DEBUG */
+
+ /*
+ * Now write the new superblock back to disk.
+ */
+ sblock.fs_time = utime;
+ wtfs((int)SBOFF / DEV_BSIZE, SBSIZE, (char *)&sblock, fso, Nflag);
+ DBG_PRINT0("sblock written\n");
+ DBG_DUMP_FS(&sblock, "new initial sblock");
+
+ /*
+ * Clean up the dynamic fields in our superblock copies.
+ */
+ sblock.fs_fmod = 0;
+ sblock.fs_clean = 1;
+ sblock.fs_ronly = 0;
+ sblock.fs_cgrotor = 0;
+ sblock.fs_state = 0;
+ memset((void *)&sblock.fs_fsmnt, 0, sizeof(sblock.fs_fsmnt));
+ sblock.fs_flags &= FS_DOSOFTDEP;
+
+ /*
+ * XXX
+ * The following fields are currently distributed from the superblock
+ * to the copies:
+ * fs_minfree
+ * fs_rotdelay
+ * fs_maxcontig
+ * fs_maxbpg
+ * fs_minfree,
+ * fs_optim
+ * fs_flags regarding SOFTPDATES
+ *
+ * We probably should rather change the summary for the cylinder group
+ * statistics here to the value of what would be in there, if the file
+ * system were created initially with the new size. Therefor we still
+ * need to find an easy way of calculating that.
+ * Possibly we can try to read the first superblock copy and apply the
+ * "diffed" stats between the old and new superblock by still copying
+ * certain parameters onto that.
+ */
+
+ /*
+ * Write out the duplicate super blocks.
+ */
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)),
+ SBSIZE, (char *)&sblock, fso, Nflag);
+ }
+ DBG_PRINT0("sblock copies written\n");
+ DBG_DUMP_FS(&sblock, "new other sblocks");
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ************************************************************ initcg ***** */
+/*
+ * This creates a new cylinder group structure, for more details please see
+ * the source of newfs(8), as this function is taken over almost unchanged.
+ * As this is never called for the first cylinder group, the special
+ * provisions for that case are removed here.
+ */
+static void
+initcg(int cylno, time_t utime, int fso, int Nflag)
+{
+ DBG_FUNC("initcg")
+ daddr_t cbase, d, dlower, dupper, dmax, blkno;
+ long i;
+ register struct csum *cs;
+#ifdef FSIRAND
+ long j;
+#endif
+
+ DBG_ENTER;
+
+ /*
+ * Determine block bounds for cylinder group.
+ */
+ cbase = cgbase(&sblock, cylno);
+ dmax = cbase + sblock.fs_fpg;
+ if (dmax > 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);
+ f<roundup(frag+1, sblock.fs_frag);
+ f++) {
+ /*
+ * Count contiguos free fragments.
+ */
+ if(isset(cg_blksfree(&acg), f)) {
+ fragsize++;
+ } else {
+ if(fragsize && fragsize<sblock.fs_frag) {
+ /*
+ * We found something in between.
+ */
+ acg.cg_frsum[fragsize]+=sign;
+ DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign);
+ }
+ fragsize=0;
+ }
+ }
+ if(fragsize && fragsize<sblock.fs_frag) {
+ /*
+ * We found something.
+ */
+ acg.cg_frsum[fragsize]+=sign;
+ DBG_PRINT2("frag_adjust [%d]+=%d\n", fragsize, sign);
+ }
+ DBG_PRINT2("frag_adjust [[%d]]+=%d\n", fragsize, sign);
+
+ DBG_LEAVE;
+ return;
+}
+
+/* ******************************************************* cond_bl_upd ***** */
+/*
+ * Here we conditionally update a pointer to a fragment. We check for all
+ * relocated blocks if any of it's fragments is referenced by the current
+ * field, and update the pointer to the respective fragment in our new
+ * block. If we find a reference we write back the block immediately,
+ * as there is no easy way for our general block reading engine to figure
+ * out if a write back operation is needed.
+ */
+static void
+cond_bl_upd(ufs_daddr_t *block, struct gfs_bpp *field,
+ enum pointer_source source, int fso, int Nflag)
+{
+ DBG_FUNC("cond_bl_upd")
+ struct gfs_bpp *f;
+ char *src;
+ daddr_t dst=0;
+
+ DBG_ENTER;
+
+ f=field;
+ while(f->old) { /* 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<sblock.fs_contigsumsize;
+ block++, lcs++) {
+ if(isclr(cg_clustersfree(&acg), block)){
+ break;
+ }
+ }
+ }
+
+ /*
+ * Point "d" to the last frag used by the cylinder summary.
+ */
+ d--;
+
+ DBG_PRINT1("d=%d\n",d);
+ if((d+1)%sblock.fs_frag) {
+ /*
+ * The end of the cylinder summary is not a complete
+ * block.
+ */
+ DBG_TRC;
+ frag_adjust(d%sblock.fs_fpg, -1);
+ for(; (d+1)%sblock.fs_frag; d--) {
+ DBG_PRINT1("d=%d\n",d);
+ setbit(cg_blksfree(&acg), d%sblock.fs_fpg);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ /*
+ * Point "d" to the last fragment of the last
+ * (incomplete) block of the clinder summary.
+ */
+ d++;
+ frag_adjust(d%sblock.fs_fpg, 1);
+
+ if(isblock(&sblock, cg_blksfree(&acg),
+ (d%sblock.fs_fpg)/sblock.fs_frag)) {
+ DBG_PRINT1("d=%d\n",d);
+ acg.cg_cs.cs_nffree-=sblock.fs_frag;
+ acg.cg_cs.cs_nbfree++;
+ sblock.fs_cstotal.cs_nffree-=sblock.fs_frag;
+ 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);
+ 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<sblock.fs_csaddr+(sblock.fs_cssize/sblock.fs_fsize)) {
+ for(; d-sblock.fs_csaddr<
+ sblock.fs_cssize/sblock.fs_fsize;
+ d++) {
+ clrbit(cg_blksfree(&acg), d%sblock.fs_fpg);
+ acg.cg_cs.cs_nffree--;
+ sblock.fs_cstotal.cs_nffree--;
+ }
+ acg.cg_cs.cs_nbfree--;
+ acg.cg_cs.cs_nffree+=sblock.fs_frag;
+ sblock.fs_cstotal.cs_nbfree--;
+ sblock.fs_cstotal.cs_nffree+=sblock.fs_frag;
+ 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);
+ }
+
+ 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)&&(d%sblock.fs_frag)); d++) {
+ DBG_PRINT1("scg first frag check loop d=%d\n", d);
+ if(isclr(cg_blksfree(&acg), d)) {
+ if (!ind) {
+ bp[ind].old=d/sblock.fs_frag;
+ bp[ind].flags|=GFS_FL_FIRST;
+ if(roundup(d, sblock.fs_frag) >= 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<d+sblock.fs_frag; f++) {
+ if(isset(cg_blksfree(&aocg), f)) {
+ acg.cg_cs.cs_nffree--;
+ sblock.fs_cstotal.cs_nffree--;
+ }
+ }
+ clrblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag);
+ bp[ind].old=d/sblock.fs_frag;
+ ind++;
+ } else {
+ clrblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag);
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
+ [cbtorpos(&sblock, d)]--;
+ if(sblock.fs_contigsumsize > 0) {
+ clrbit(cg_clustersfree(&acg), d/sblock.fs_frag);
+ for(lcs=0, l=(d/sblock.fs_frag)+1;
+ lcs<sblock.fs_contigsumsize;
+ l++, lcs++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+ if(lcs < sblock.fs_contigsumsize) {
+ cg_clustersum(&acg)[lcs+1]--;
+ if(lcs) {
+ cg_clustersum(&acg)[lcs]++;
+ }
+ }
+ }
+ }
+ /*
+ * No fragment counter handling is needed here, as this finally
+ * doesn't change after the relocation.
+ */
+ }
+
+ /*
+ * Handle all fragments needed in the last new affected block.
+ */
+ if(d<dupper) {
+ frag_adjust(dupper-1, -1);
+
+ if(isblock(&sblock, cg_blksfree(&acg), d/sblock.fs_frag)) {
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ acg.cg_cs.cs_nffree+=sblock.fs_frag;
+ sblock.fs_cstotal.cs_nffree+=sblock.fs_frag;
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))
+ [cbtorpos(&sblock, d)]--;
+ if(sblock.fs_contigsumsize > 0) {
+ clrbit(cg_clustersfree(&acg), d/sblock.fs_frag);
+ for(lcs=0, l=(d/sblock.fs_frag)+1;
+ lcs<sblock.fs_contigsumsize;
+ l++, lcs++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+ if(lcs < sblock.fs_contigsumsize) {
+ cg_clustersum(&acg)[lcs+1]--;
+ if(lcs) {
+ cg_clustersum(&acg)[lcs]++;
+ }
+ }
+ }
+ }
+
+ for(; d<dupper; d++) {
+ DBG_PRINT1("scg second frag check loop d=%d\n", d);
+ if(isclr(cg_blksfree(&acg), d)) {
+ bp[ind].old=d/sblock.fs_frag;
+ bp[ind].flags|=GFS_FL_LAST;
+ } else {
+ clrbit(cg_blksfree(&acg), d);
+ acg.cg_cs.cs_nffree--;
+ sblock.fs_cstotal.cs_nffree--;
+ }
+ }
+ if(bp[ind].flags & GFS_FL_LAST) { /* we have to advance here */
+ ind++;
+ }
+ frag_adjust(dupper-1, 1);
+ }
+
+ /*
+ * If we found a block to relocate just do so.
+ */
+ if(ind) {
+ for(i=0; i<ind; i++) {
+ if(!bp[i].old) { /* no more blocks listed */
+ /*
+ * XXX A relative blocknumber should not be
+ * zero, which is not explicitly
+ * guaranteed by our code.
+ */
+ break;
+ }
+ /*
+ * Allocate a complete block in the same (current)
+ * cylinder group.
+ */
+ bp[i].new=alloc()/sblock.fs_frag;
+
+ /*
+ * There is no frag_adjust() needed for the new block
+ * as it will have no fragments yet :-).
+ */
+ for(f=bp[i].old*sblock.fs_frag,
+ g=bp[i].new*sblock.fs_frag;
+ f<(bp[i].old+1)*sblock.fs_frag;
+ f++, g++) {
+ if(isset(cg_blksfree(&aocg), f)) {
+ setbit(cg_blksfree(&acg), g);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ }
+
+ /*
+ * Special handling is required if this was the first
+ * block. We have to consider the fragments which were
+ * used by the cylinder summary in the original block
+ * which re to be free in the copy of our block. We
+ * have to be careful if this first block happens to
+ * be also the last block to be relocated.
+ */
+ if(bp[i].flags & GFS_FL_FIRST) {
+ for(f=bp[i].old*sblock.fs_frag,
+ g=bp[i].new*sblock.fs_frag;
+ f<odupper;
+ f++, g++) {
+ setbit(cg_blksfree(&acg), g);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ if(!(bp[i].flags & GFS_FL_LAST)) {
+ frag_adjust(bp[i].new*sblock.fs_frag,1);
+ }
+
+ }
+
+ /*
+ * Special handling is required if this is the last
+ * block to be relocated.
+ */
+ if(bp[i].flags & GFS_FL_LAST) {
+ frag_adjust(bp[i].new*sblock.fs_frag, 1);
+ frag_adjust(bp[i].old*sblock.fs_frag, -1);
+ for(f=dupper;
+ f<roundup(dupper, sblock.fs_frag);
+ f++) {
+ if(isclr(cg_blksfree(&acg), f)) {
+ setbit(cg_blksfree(&acg), f);
+ acg.cg_cs.cs_nffree++;
+ sblock.fs_cstotal.cs_nffree++;
+ }
+ }
+ frag_adjust(bp[i].old*sblock.fs_frag, 1);
+ }
+
+ /*
+ * !!! Attach the cylindergroup offset here.
+ */
+ bp[i].old+=cbase/sblock.fs_frag;
+ bp[i].new+=cbase/sblock.fs_frag;
+
+ /*
+ * Copy the content of the block.
+ */
+ /*
+ * XXX Here we will have to implement a copy on write
+ * in the case we have any active snapshots.
+ */
+ rdfs(fsbtodb(&sblock, bp[i].old*sblock.fs_frag),
+ sblock.fs_bsize, (char *)&ablk, fsi);
+ wtfs(fsbtodb(&sblock, bp[i].new*sblock.fs_frag),
+ sblock.fs_bsize, (char *)&ablk, fso, Nflag);
+ DBG_DUMP_HEX(&sblock, "copied full block", (unsigned char *)&ablk);
+
+ DBG_PRINT2("scg (%d->%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<osblock.fs_ncg; cylno++) {
+ DBG_PRINT1("scg doing cg (%d)\n", cylno);
+ for(inc=osblock.fs_ipg-1 ; inc>=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; i<ind; i++) {
+ if(!bp[i].found || (bp[i].found>sblock.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<dlower && blkno==-1); d+=sblock.fs_frag) {
+ if(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<dmax && blkno==-1); d+=sblock.fs_frag) {
+ if(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; lcs1<sblock.fs_contigsumsize;
+ l--, lcs1++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+ /*
+ * ... and continue with the blocks right after our allocated
+ * block.
+ */
+ for(lcs2=0, l=blkno+1; lcs2<sblock.fs_contigsumsize;
+ l++, lcs2++ ) {
+ if(isclr(cg_clustersfree(&acg),l)){
+ break;
+ }
+ }
+
+ /*
+ * Now update all counters.
+ */
+ cg_clustersum(&acg)[MIN(lcs1+lcs2+1,sblock.fs_contigsumsize)]--;
+ if(lcs1) {
+ cg_clustersum(&acg)[lcs1]++;
+ }
+ if(lcs2) {
+ cg_clustersum(&acg)[lcs2]++;
+ }
+ }
+ /*
+ * Update all statistics based on blocks.
+ */
+ acg.cg_cs.cs_nbfree--;
+ sblock.fs_cstotal.cs_nbfree--;
+ cg_blktot(&acg)[cbtocylno(&sblock, d)]--;
+ cg_blks(&sblock, &acg, cbtocylno(&sblock, d))[cbtorpos(&sblock, d)]--;
+
+ DBG_LEAVE;
+ return (d);
+}
+
+/* *********************************************************** isblock ***** */
+/*
+ * Here we check if all frags of a block are free. For more details again
+ * please see the source of newfs(8), as this function is taken over almost
+ * unchanged.
+ */
+static int
+isblock(struct fs *fs, unsigned char *cp, int h)
+{
+ DBG_FUNC("isblock")
+ unsigned char mask;
+
+ DBG_ENTER;
+
+ switch (fs->fs_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; j<FSMAXSNAP; j++) {
+ if(sblock.fs_snapinum[j]) {
+ fprintf(stderr,
+ "active snapshot found in filesystem\n"
+ " please remove all snapshots before "
+ "using growfs\n");
+ exit(-1);
+ }
+ if(!sblock.fs_snapinum[j]) { /* list is dense */
+ break;
+ }
+ }
+ }
+#endif
+
+ if (ExpertFlag == 0 && Nflag == 0) {
+ printf("We strongly recommend you to make a backup "
+ "before growing the Filesystem\n\n"
+ " Did you backup your data (Yes/No) ? ");
+ fgets(reply, sizeof(reply), stdin);
+ if (strcmp(reply, "Yes\n")){
+ printf("\n Nothing done \n");
+ exit (0);
+ }
+ }
+
+ printf("new filesystemsize is: %d frags\n", sblock.fs_size);
+
+ /*
+ * Try to access our new last block in the filesystem. Even if we
+ * later on realize we have to abort our operation, on that block
+ * there should be no data, so we can't destroy something yet.
+ */
+ wtfs((daddr_t)pp->p_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--; lcs<sblock.fs_contigsumsize; block--, lcs++ ) {
+ if(isclr(cg_clustersfree(&acg), block)){
+ break;
+ }
+ }
+ }
+ if(lcs < sblock.fs_contigsumsize) {
+ if(lcs) {
+ cg_clustersum(&acg)[lcs]--;
+ }
+ lcs++;
+ cg_clustersum(&acg)[lcs]++;
+ }
+
+ DBG_LEAVE;
+ return;
+}
+
+/* *********************************************************** updrefs ***** */
+/*
+ * This updates all references to relocated blocks for the given inode. The
+ * inode is given as number within the cylinder group, and the number of the
+ * cylinder group.
+ */
+static void
+updrefs(int cg, ino_t in, struct gfs_bpp *bp, int fsi, int fso, int Nflag)
+{
+ DBG_FUNC("updrefs")
+ unsigned int ictr, ind2ctr, ind3ctr;
+ ufs_daddr_t *iptr, *ind2ptr, *ind3ptr;
+ struct dinode *ino;
+ int remaining_blocks;
+
+ DBG_ENTER;
+
+ /*
+ * XXX We should skip unused inodes even from beeing read from disk
+ * here by using the bitmap.
+ */
+ ino=ginode(in, fsi, cg);
+ if(!((ino->di_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_size<MAXSYMLINKLEN)) {
+ DBG_LEAVE;
+ return; /* skip short symlinks */
+ }
+ if(!ino->di_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; ictr<MIN(howmany((unsigned int)
+ 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_2 blocks checked\n");
+
+#define SQUARE(a) ((a)*(a))
+ remaining_blocks-=SQUARE(howmany(sblock.fs_bsize, sizeof(ufs_daddr_t)));
+#undef SQUARE
+ if(remaining_blocks<0) {
+ DBG_LEAVE;
+ return;
+ }
+
+ if(ino->di_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;
+}
+
OpenPOWER on IntegriCloud