From owner-svn-src-all@FreeBSD.ORG Sat Jun 23 19:15:13 2012 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 49B19106566C; Sat, 23 Jun 2012 19:15:13 +0000 (UTC) (envelope-from trasz@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 32F6E8FC0C; Sat, 23 Jun 2012 19:15:13 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id q5NJFDEn059634; Sat, 23 Jun 2012 19:15:13 GMT (envelope-from trasz@svn.freebsd.org) Received: (from trasz@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id q5NJFDt9059630; Sat, 23 Jun 2012 19:15:13 GMT (envelope-from trasz@svn.freebsd.org) Message-Id: <201206231915.q5NJFDt9059630@svn.freebsd.org> From: Edward Tomasz Napierala Date: Sat, 23 Jun 2012 19:15:13 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org X-SVN-Group: stable-9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r237501 - stable/9/sbin/growfs X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 23 Jun 2012 19:15:13 -0000 Author: trasz Date: Sat Jun 23 19:15:12 2012 New Revision: 237501 URL: http://svn.freebsd.org/changeset/base/237501 Log: MFC r234846: 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 MFC r235079: Fix offset calculation to actually rewrite the _last_ block. Modified: stable/9/sbin/growfs/Makefile stable/9/sbin/growfs/growfs.8 stable/9/sbin/growfs/growfs.c Directory Properties: stable/9/sbin/growfs/ (props changed) Modified: stable/9/sbin/growfs/Makefile ============================================================================== --- stable/9/sbin/growfs/Makefile Sat Jun 23 19:08:24 2012 (r237500) +++ stable/9/sbin/growfs/Makefile Sat Jun 23 19:15:12 2012 (r237501) @@ -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 Modified: stable/9/sbin/growfs/growfs.8 ============================================================================== --- stable/9/sbin/growfs/growfs.8 Sat Jun 23 19:08:24 2012 (r237500) +++ stable/9/sbin/growfs/growfs.8 Sat Jun 23 19:15:12 2012 (r237501) @@ -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 -.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 . +utility makes it possible to expand an UFS file system. +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 r 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 . Modified: stable/9/sbin/growfs/growfs.c ============================================================================== --- stable/9/sbin/growfs/growfs.c Sat Jun 23 19:08:24 2012 (r237500) +++ stable/9/sbin/growfs/growfs.c Sat Jun 23 19:15:12 2012 (r237501) @@ -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 #include #include +#include +#include #include #include #include #include #include +#include +#include #include +#include #include #include #include @@ -67,6 +76,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include "debug.h" @@ -109,7 +119,7 @@ static void updjcg(int, time_t, int, 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 Nf /* * 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 Nf 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 fs /* * 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; + + /* + * 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 (ioctl(fd, DIOCGSECTORSIZE, §orsize) == -1) - err(1,"DIOCGSECTORSIZE"); - if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) == -1) - err(1,"DIOCGMEDIASIZE"); + return (NULL); +} - if (sectorsize <= 0) - errx(1, "bogus sectorsize: %d", sectorsize); +static const char * +mountpoint_to_dev(const char *mountpoint) +{ + struct statfs *mntbuf, *statfsp; + struct fstab *fs; + int i, mntsize; - *size = mediasize / sectorsize; + /* + * 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; + + 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 - sblock.fs_fsize) / DEV_BSIZE), + sblock.fs_fsize, testbuf, fsi); + wtfs((ufs2_daddr_t)((size - sblock.fs_fsize) / DEV_BSIZE), + 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); + } +}