summaryrefslogtreecommitdiffstats
path: root/sbin/growfs
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2012-04-30 16:08:02 +0000
committertrasz <trasz@FreeBSD.org>2012-04-30 16:08:02 +0000
commit65de4208e7a6ee983dcf61496ffbeddb38ec21d2 (patch)
tree8dca1894dca34468ac08f338fa5045dac9f0f999 /sbin/growfs
parent14c0cbcf622b074b9aa6b0784df0b0536abb4f67 (diff)
downloadFreeBSD-src-65de4208e7a6ee983dcf61496ffbeddb38ec21d2.zip
FreeBSD-src-65de4208e7a6ee983dcf61496ffbeddb38ec21d2.tar.gz
Improve growfs(8) in a few ways; unfortunately, it's somewhat hard to untangle
them and commit separately. 1. Rewrite the way growfs(8) finds the device and mount point. This makes it possible to use e.g. "growfs /mnt"; it's also used to display more helpful messages. 2. Be more user-friendly, using descriptive messages, like this: OK to grow filesystem on /dev/md0, mounted on /mnt, from 9.8GB to 20GB? [Yes/No]" 3. Allow to specify the size (-s option) just like with mdconfig(8), i.e. with postfixes ("mdconfig -s 10g"). 4. Reload read-only filesystem after growing. Reviewed by: kib, mckusick (earlier version) Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'sbin/growfs')
-rw-r--r--sbin/growfs/Makefile8
-rw-r--r--sbin/growfs/growfs.8102
-rw-r--r--sbin/growfs/growfs.c339
3 files changed, 263 insertions, 186 deletions
diff --git a/sbin/growfs/Makefile b/sbin/growfs/Makefile
index a875ce5..f464ed7 100644
--- a/sbin/growfs/Makefile
+++ b/sbin/growfs/Makefile
@@ -6,12 +6,18 @@
#GFSDBG=
+.PATH: ${.CURDIR}/../mount
+
PROG= growfs
-SRCS= growfs.c
+SRCS= growfs.c getmntopts.c
MAN= growfs.8
+CFLAGS+=-I${.CURDIR}/../mount
.if defined(GFSDBG)
SRCS+= debug.c
.endif
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
.include <bsd.prog.mk>
diff --git a/sbin/growfs/growfs.8 b/sbin/growfs/growfs.8
index 819e0bd..f18c00e 100644
--- a/sbin/growfs/growfs.8
+++ b/sbin/growfs/growfs.8
@@ -37,7 +37,7 @@
.\" $TSHeader: src/sbin/growfs/growfs.8,v 1.3 2000/12/12 19:31:00 tomsoft Exp $
.\" $FreeBSD$
.\"
-.Dd June 29, 2011
+.Dd April 30, 2012
.Dt GROWFS 8
.Os
.Sh NAME
@@ -47,41 +47,20 @@
.Nm
.Op Fl Ny
.Op Fl s Ar size
-.Ar special
+.Ar special | filesystem
.Sh DESCRIPTION
The
.Nm
-utility extends the
-.Xr newfs 8
-program.
-Before starting
+utility makes it possible to expand an UFS file system.
+Before running
.Nm
-the disk must be labeled to a bigger size using
-.Xr bsdlabel 8 .
-If you wish to grow a file system beyond the boundary of
-the slice it resides in, you must re-size the slice using
-.Xr gpart 8
-before running
-.Nm .
+the partition or slice containing the file system must be extended using
+.Xr gpart 8 .
If you are using volumes you must enlarge them by using
.Xr gvinum 8 .
The
.Nm
utility extends the size of the file system on the specified special file.
-Currently
-.Nm
-can only enlarge unmounted file systems.
-Do not try enlarging a mounted file system, your system may panic and you will
-not be able to use the file system any longer.
-Most of the
-.Xr newfs 8
-options cannot be changed by
-.Nm .
-In fact, you can only increase the size of the file system.
-Use
-.Xr tunefs 8
-for other changes.
-.Pp
The following options are available:
.Bl -tag -width indent
.It Fl N
@@ -103,6 +82,13 @@ So use this option with great care!
Determines the
.Ar size
of the file system after enlarging in sectors.
+.Ar Size
+is the number of 512 byte sectors unless suffixed with a
+.Cm b , k , m , g ,
+or
+.Cm t
+which
+denotes byte, kilobyte, megabyte, gigabyte and terabyte respectively.
This value defaults to the size of the raw partition specified in
.Ar special
(in other words,
@@ -110,19 +96,18 @@ This value defaults to the size of the raw partition specified in
will enlarge the file system to the size of the entire partition).
.El
.Sh EXAMPLES
-.Dl growfs -s 4194304 /dev/vinum/testvol
+.Dl growfs -s 2G /dev/ada0p1
.Pp
will enlarge
-.Pa /dev/vinum/testvol
+.Pa /dev/ada0p1
up to 2GB if there is enough space in
-.Pa /dev/vinum/testvol .
+.Pa /dev/ada0p1 .
.Sh SEE ALSO
-.Xr bsdlabel 8 ,
.Xr dumpfs 8 ,
.Xr ffsinfo 8 ,
.Xr fsck 8 ,
+.Xr fsdb 8 ,
.Xr gpart 8 ,
-.Xr gvinum 8 ,
.Xr newfs 8 ,
.Xr tunefs 8
.Sh HISTORY
@@ -134,61 +119,12 @@ utility first appeared in
.An Christoph Herrmann Aq chm@FreeBSD.org
.An Thomas-Henning von Kamptz Aq tomsoft@FreeBSD.org
.An The GROWFS team Aq growfs@Tomsoft.COM
+.An Edward Tomasz Napierala Aq trasz@FreeBSD.org
.Sh BUGS
-The
-.Nm
-utility works starting with
-.Fx
-3.x.
-There may be cases on
-.Fx
-3.x only, when
-.Nm
-does not recognize properly whether or not the file system is mounted and
-exits with an error message.
-Then please use
-.Nm
-.Fl y
-if you are sure that the file system is not mounted.
-It is also recommended to always use
-.Xr fsck 8
-after enlarging (just to be on the safe side).
-.Pp
-For enlarging beyond certain limits, it is essential to have some free blocks
-available in the first cylinder group.
-If that space is not available in the first cylinder group, a critical data
-structure has to be relocated into one of the new available cylinder groups.
-On
-.Fx
-3.x this will cause problems with
-.Xr fsck 8
-afterwards.
-So
-.Xr fsck 8
-needs to be patched if you want to use
-.Nm
-for
-.Fx
-3.x.
-This patch is already integrated in
-.Fx
-starting with
-.Fx 4.4 .
-To avoid an unexpected relocation of that structure it is possible to use
-.Nm ffsinfo
-.Fl g Ar 0
-.Fl l Ar 4
-on the first cylinder group to verify that
-.Em nbfree
-in the CYLINDER SUMMARY (internal cs) of the CYLINDER GROUP
-.Em cgr0
-has enough blocks.
-As a rule of thumb for default file system parameters one block is needed for
-every 2 GB of total file system size.
.Pp
Normally
.Nm
-writes this critical structure to disk and reads it again later for doing more
+writes cylinder group summary to disk and reads it again later for doing more
updates.
This read operation will provide unexpected data when using
.Fl N .
diff --git a/sbin/growfs/growfs.c b/sbin/growfs/growfs.c
index 7d04345..aee4c40 100644
--- a/sbin/growfs/growfs.c
+++ b/sbin/growfs/growfs.c
@@ -1,11 +1,15 @@
/*
- * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
* Copyright (c) 1980, 1989, 1993 The Regents of the University of California.
+ * Copyright (c) 2000 Christoph Herrmann, Thomas-Henning von Kamptz
+ * Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Christoph Herrmann and Thomas-Henning von Kamptz, Munich and Frankfurt.
*
+ * Portions of this software were developed by Edward Tomasz Napierala
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -53,13 +57,18 @@ __FBSDID("$FreeBSD$");
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/disk.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
#include <stdio.h>
#include <paths.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
+#include <fstab.h>
+#include <inttypes.h>
#include <limits.h>
+#include <mntopts.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
@@ -67,6 +76,7 @@ __FBSDID("$FreeBSD$");
#include <unistd.h>
#include <ufs/ufs/dinode.h>
#include <ufs/ffs/fs.h>
+#include <libutil.h>
#include "debug.h"
@@ -109,7 +119,7 @@ static void updjcg(int, time_t, int, int, unsigned int);
static void updcsloc(time_t, int, int, unsigned int);
static void frag_adjust(ufs2_daddr_t, int);
static void updclst(int);
-static void get_dev_size(int, int *);
+static void mount_reload(const struct statfs *stfs);
/*
* Here we actually start growing the file system. We basically read the
@@ -177,6 +187,7 @@ growfs(int fsi, int fso, unsigned int Nflag)
/*
* Dump out summary information about file system.
*/
+#ifdef FS_DEBUG
#define B2MBFACTOR (1 / (1024.0 * 1024.0))
printf("growfs: %.1fMB (%jd sectors) block size %d, fragment size %d\n",
(float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
@@ -188,6 +199,7 @@ growfs(int fsi, int fso, unsigned int Nflag)
if (sblock.fs_flags & FS_DOSOFTDEP)
printf("\twith soft updates\n");
#undef B2MBFACTOR
+#endif /* FS_DEBUG */
/*
* Now build the cylinders group blocks and
@@ -774,7 +786,7 @@ updjcg(int cylno, time_t modtime, int fsi, int fso, unsigned int Nflag)
/*
* Here we update the location of the cylinder summary. We have two possible
- * ways of growing the cylinder summary.
+ * 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
@@ -1238,24 +1250,104 @@ charsperline(void)
return (columns);
}
+static int
+is_dev(const char *name)
+{
+ struct stat devstat;
+
+ if (stat(name, &devstat) != 0)
+ return (0);
+ if (!S_ISCHR(devstat.st_mode))
+ return (0);
+ return (1);
+}
+
/*
- * Get the size of the partition.
- */
-static void
-get_dev_size(int fd, int *size)
+ * Return mountpoint on which the device is currently mounted.
+ */
+static const struct statfs *
+dev_to_statfs(const char *dev)
{
- int sectorsize;
- off_t mediasize;
+ struct stat devstat, mntdevstat;
+ struct statfs *mntbuf, *statfsp;
+ char device[MAXPATHLEN];
+ char *mntdevname;
+ int i, mntsize;
- if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) == -1)
- err(1,"DIOCGSECTORSIZE");
- if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1)
- err(1,"DIOCGMEDIASIZE");
+ /*
+ * First check the mounted filesystems.
+ */
+ if (stat(dev, &devstat) != 0)
+ return (NULL);
+ if (!S_ISCHR(devstat.st_mode) && !S_ISBLK(devstat.st_mode))
+ return (NULL);
+
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++) {
+ statfsp = &mntbuf[i];
+ mntdevname = statfsp->f_mntfromname;
+ if (*mntdevname != '/') {
+ strcpy(device, _PATH_DEV);
+ strcat(device, mntdevname);
+ mntdevname = device;
+ }
+ if (stat(mntdevname, &mntdevstat) == 0 &&
+ mntdevstat.st_rdev == devstat.st_rdev)
+ return (statfsp);
+ }
- if (sectorsize <= 0)
- errx(1, "bogus sectorsize: %d", sectorsize);
+ return (NULL);
+}
+
+static const char *
+mountpoint_to_dev(const char *mountpoint)
+{
+ struct statfs *mntbuf, *statfsp;
+ struct fstab *fs;
+ int i, mntsize;
+
+ /*
+ * First check the mounted filesystems.
+ */
+ mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+ for (i = 0; i < mntsize; i++) {
+ statfsp = &mntbuf[i];
+
+ if (strcmp(statfsp->f_mntonname, mountpoint) == 0)
+ return (statfsp->f_mntfromname);
+ }
+
+ /*
+ * Check the fstab.
+ */
+ fs = getfsfile(mountpoint);
+ if (fs != NULL)
+ return (fs->fs_spec);
+
+ return (NULL);
+}
+
+static const char *
+getdev(const char *name)
+{
+ static char device[MAXPATHLEN];
+ const char *cp, *dev;
- *size = mediasize / sectorsize;
+ if (is_dev(name))
+ return (name);
+
+ cp = strrchr(name, '/');
+ if (cp == 0) {
+ snprintf(device, sizeof(device), "%s%s", _PATH_DEV, name);
+ if (is_dev(device))
+ return (device);
+ }
+
+ dev = mountpoint_to_dev(name);
+ if (dev != NULL && is_dev(dev))
+ return (dev);
+
+ return (NULL);
}
/*
@@ -1283,17 +1375,13 @@ int
main(int argc, char **argv)
{
DBG_FUNC("main")
- char *device, *special;
- int ch;
- unsigned int size = 0;
- size_t len;
- unsigned int Nflag = 0;
- int ExpertFlag = 0;
- struct stat st;
- int i, fsi, fso;
- u_int32_t p_size;
- char reply[5];
- int j;
+ const char *device;
+ const struct statfs *statfsp;
+ uint64_t size = 0;
+ off_t mediasize;
+ int error, i, j, fsi, fso, ch, Nflag = 0, yflag = 0;
+ char *p, reply[5], oldsizebuf[6], newsizebuf[6];
+ void *testbuf;
DBG_ENTER;
@@ -1303,14 +1391,27 @@ main(int argc, char **argv)
Nflag = 1;
break;
case 's':
- size = (size_t)atol(optarg);
- if (size < 1)
- usage();
+ size = (off_t)strtoumax(optarg, &p, 0);
+ if (p == NULL || *p == '\0')
+ size *= DEV_BSIZE;
+ else if (*p == 'b' || *p == 'B')
+ ; /* do nothing */
+ else if (*p == 'k' || *p == 'K')
+ size <<= 10;
+ else if (*p == 'm' || *p == 'M')
+ size <<= 20;
+ else if (*p == 'g' || *p == 'G')
+ size <<= 30;
+ else if (*p == 't' || *p == 'T') {
+ size <<= 30;
+ size <<= 10;
+ } else
+ errx(1, "unknown suffix on -s argument");
break;
case 'v': /* for compatibility to newfs */
break;
case 'y':
- ExpertFlag = 1;
+ yflag = 1;
break;
case '?':
/* FALLTHROUGH */
@@ -1324,71 +1425,29 @@ main(int argc, char **argv)
if (argc != 1)
usage();
- device = *argv;
-
/*
- * Now try to guess the (raw)device name.
+ * Now try to guess the 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);
- if (special == NULL)
- errx(1, "malloc failed");
- snprintf(special, len, "%sr%s", _PATH_DEV, device);
- if (stat(special, &st) == -1) {
- snprintf(special, len, "%s%s", _PATH_DEV, device);
- if (stat(special, &st) == -1) {
- snprintf(special, len, "%svinum/r%s",
- _PATH_DEV, device);
- if (stat(special, &st) == -1) {
- /* For now this is the 'last resort' */
- snprintf(special, len, "%svinum/%s",
- _PATH_DEV, device);
- }
- }
- }
- device = special;
- }
+ device = getdev(*argv);
+ if (device == NULL)
+ errx(1, "cannot find special device for %s", *argv);
- /*
- * Try to access our devices for writing ...
- */
- if (Nflag) {
- fso = -1;
- } else {
- fso = open(device, O_WRONLY);
- if (fso < 0)
- err(1, "%s", device);
- }
+ statfsp = dev_to_statfs(device);
- /*
- * ... and reading.
- */
fsi = open(device, O_RDONLY);
if (fsi < 0)
err(1, "%s", device);
/*
- * Try to guess the slice if not specified. This code should guess
- * the right thing and avoid to bother the user with the task
- * of specifying the option -v on vinum volumes.
+ * Try to guess the slice size if not specified.
*/
- get_dev_size(fsi, &p_size);
+ if (ioctl(fsi, DIOCGMEDIASIZE, &mediasize) == -1)
+ err(1,"DIOCGMEDIASIZE");
/*
* Check if that partition is suitable for growing a file system.
*/
- if (p_size < 1)
+ if (mediasize < 1)
errx(1, "partition is unavailable");
/*
@@ -1414,16 +1473,36 @@ main(int argc, char **argv)
/*
* Determine size to grow to. Default to the device size.
*/
- sblock.fs_size = dbtofsb(&osblock, p_size);
- if (size != 0) {
- if (size > p_size)
- errx(1, "there is not enough space (%d < %d)",
- p_size, size);
- sblock.fs_size = dbtofsb(&osblock, size);
+ if (size == 0)
+ size = mediasize;
+ else {
+ if (size > (uint64_t)mediasize) {
+ humanize_number(oldsizebuf, sizeof(oldsizebuf), size,
+ "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ humanize_number(newsizebuf, sizeof(newsizebuf),
+ mediasize,
+ "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+
+ errx(1, "requested size %s is larger "
+ "than the available %s", oldsizebuf, newsizebuf);
+ }
}
+ if (size <= (uint64_t)(osblock.fs_size * osblock.fs_fsize)) {
+ humanize_number(oldsizebuf, sizeof(oldsizebuf),
+ osblock.fs_size * osblock.fs_fsize,
+ "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ humanize_number(newsizebuf, sizeof(newsizebuf), size,
+ "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+
+ errx(1, "requested size %s is not larger than the current "
+ "filesystem size %s", newsizebuf, oldsizebuf);
+ }
+
+ sblock.fs_size = dbtofsb(&osblock, size / DEV_BSIZE);
+
/*
- * Are we really growing ?
+ * Are we really growing?
*/
if (osblock.fs_size >= sblock.fs_size) {
errx(1, "we are not growing (%jd->%jd)",
@@ -1433,7 +1512,7 @@ main(int argc, char **argv)
/*
* Check if we find an active snapshot.
*/
- if (ExpertFlag == 0) {
+ if (yflag == 0) {
for (j = 0; j < FSMAXSNAP; j++) {
if (sblock.fs_snapinum[j]) {
errx(1, "active snapshot found in file system; "
@@ -1445,10 +1524,23 @@ main(int argc, char **argv)
}
}
- if (ExpertFlag == 0 && Nflag == 0) {
- printf("We strongly recommend you to make a backup "
+ if (yflag == 0 && Nflag == 0) {
+ if (statfsp != NULL && (statfsp->f_flags & MNT_RDONLY) == 0)
+ errx(1, "%s is mounted read-write on %s",
+ statfsp->f_mntfromname, statfsp->f_mntonname);
+ printf("It's strongly recommended to make a backup "
"before growing the file system.\n"
- "Did you backup your data (Yes/No)? ");
+ "OK to grow filesystem on %s", device);
+ if (statfsp != NULL)
+ printf(", mounted on %s,", statfsp->f_mntonname);
+ humanize_number(oldsizebuf, sizeof(oldsizebuf),
+ osblock.fs_size * osblock.fs_fsize,
+ "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ humanize_number(newsizebuf, sizeof(newsizebuf),
+ sblock.fs_size * sblock.fs_fsize,
+ "B", HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ printf(" from %s to %s? [Yes/No] ", oldsizebuf, newsizebuf);
+ fflush(stdout);
fgets(reply, (int)sizeof(reply), stdin);
if (strcmp(reply, "Yes\n")){
printf("\nNothing done\n");
@@ -1456,15 +1548,30 @@ main(int argc, char **argv)
}
}
- printf("New file system size is %jd frags\n", (intmax_t)sblock.fs_size);
+ /*
+ * Try to access our device for writing. If it's not mounted,
+ * or mounted read-only, simply open it; otherwise, use UFS
+ * suspension mechanism.
+ */
+ if (Nflag) {
+ fso = -1;
+ } else {
+ fso = open(device, O_WRONLY);
+ if (fso < 0)
+ err(1, "%s", device);
+ }
/*
- * Try to access our new last block in the file system. 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.
+ * Try to access our new last block in the file system.
*/
- wtfs((ufs2_daddr_t)p_size - 1, (size_t)DEV_BSIZE, (void *)&sblock,
- fso, Nflag);
+ testbuf = malloc(sblock.fs_fsize);
+ if (testbuf == NULL)
+ err(1, "malloc");
+ rdfs((ufs2_daddr_t)((size / DEV_BSIZE) - sblock.fs_fsize),
+ sblock.fs_fsize, testbuf, fsi);
+ wtfs((ufs2_daddr_t)((size / DEV_BSIZE) - sblock.fs_fsize),
+ sblock.fs_fsize, testbuf, fso, Nflag);
+ free(testbuf);
/*
* Now calculate new superblock values and check for reasonable
@@ -1520,8 +1627,13 @@ main(int argc, char **argv)
growfs(fsi, fso, Nflag);
close(fsi);
- if (fso > -1)
- close(fso);
+ if (fso > -1) {
+ error = close(fso);
+ if (error != 0)
+ err(1, "close");
+ }
+ if (statfsp != NULL)
+ mount_reload(statfsp);
DBG_CLOSE;
@@ -1539,7 +1651,7 @@ usage(void)
DBG_ENTER;
- fprintf(stderr, "usage: growfs [-Ny] [-s size] special\n");
+ fprintf(stderr, "usage: growfs [-Ny] [-s size] special | filesystem\n");
DBG_LEAVE;
exit(1);
@@ -1586,3 +1698,26 @@ updclst(int block)
DBG_LEAVE;
return;
}
+
+static void
+mount_reload(const struct statfs *stfs)
+{
+ char errmsg[255];
+ struct iovec *iov;
+ int iovlen;
+
+ iov = NULL;
+ iovlen = 0;
+ *errmsg = '\0';
+ build_iovec(&iov, &iovlen, "fstype", __DECONST(char *, "ffs"), 4);
+ build_iovec(&iov, &iovlen, "fspath", __DECONST(char *, stfs->f_mntonname), (size_t)-1);
+ build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
+ build_iovec(&iov, &iovlen, "update", NULL, 0);
+ build_iovec(&iov, &iovlen, "reload", NULL, 0);
+
+ if (nmount(iov, iovlen, stfs->f_flags) < 0) {
+ errmsg[sizeof(errmsg) - 1] = '\0';
+ err(9, "%s: cannot reload filesystem%s%s", stfs->f_mntonname,
+ *errmsg != '\0' ? ": " : "", errmsg);
+ }
+}
OpenPOWER on IntegriCloud