[PATCH 1/2] growfs: support unit postfixes when specifying sizes

Christoph Hellwig <[email protected]> Thu, 30 Jul 2026 14:25:13 +0200
Newsgroups org.kernel.vger.linux-xfs
Message-ID <[email protected]>
Try using cvtnum to parse the sizes for the -D, -L and -R arguments,
and only fall back to plain integer parsing and interpreting it as
blocks when that fails.  This matches the mkfs UI and makes specifying
a size significantly easier.

Signed-off-by: Christoph Hellwig <[email protected]>
---
 growfs/xfs_growfs.c   | 32 ++++++++++++++++++++++++++++----
 man/man8/xfs_growfs.8 | 39 +++++++++++++++++++++++++++++++--------
 2 files changed, 59 insertions(+), 12 deletions(-)

diff --git a/growfs/xfs_growfs.c b/growfs/xfs_growfs.c
index 0d0b2ae3e739..184631b1735b 100644
--- a/growfs/xfs_growfs.c
+++ b/growfs/xfs_growfs.c
@@ -5,6 +5,7 @@
  */
 
 #include "libxfs.h"
+#include "libfrog/convert.h"
 #include "libfrog/paths.h"
 #include "libfrog/fsgeom.h"
 
@@ -31,6 +32,19 @@ Options:\n\
 	exit(2);
 }
 
+static long long
+parse_size(
+	struct xfs_fsop_geom	*geo,
+	const char		*size_str)
+{
+	long long		size;
+
+	size = cvtnum(geo->blocksize, geo->sectsize, size_str);
+	if (size == -1)
+		return strtoll(size_str, NULL, 10);
+	return size / geo->blocksize;
+}
+
 int
 main(int argc, char **argv)
 {
@@ -55,6 +69,9 @@ main(int argc, char **argv)
 	struct xfs_fsop_geom	ngeo;	/* new fs geometry */
 	int			rflag;	/* -r flag */
 	long long		rsize;	/* new rt size in fs blocks */
+	const char		*dsize_str = NULL;
+	const char		*lsize_str = NULL;
+	const char		*rsize_str = NULL;
 	int			xflag;	/* -x flag */
 	char			*fname;	/* mount point name */
 	char			*datadev; /* data device name */
@@ -77,7 +94,7 @@ main(int argc, char **argv)
 	while ((c = getopt(argc, argv, "dD:e:ilL:m:np:rR:t:xV")) != EOF) {
 		switch (c) {
 		case 'D':
-			dsize = strtoll(optarg, NULL, 10);
+			dsize_str = strdup(optarg);
 			fallthrough;
 		case 'd':
 			dflag = 1;
@@ -90,7 +107,7 @@ main(int argc, char **argv)
 			lflag = iflag = 1;
 			break;
 		case 'L':
-			lsize = strtoll(optarg, NULL, 10);
+			lsize_str = strdup(optarg);
 			fallthrough;
 		case 'l':
 			lflag = 1;
@@ -106,7 +123,7 @@ main(int argc, char **argv)
 			progname = optarg;
 			break;
 		case 'R':
-			rsize = strtoll(optarg, NULL, 10);
+			rsize_str = strdup(optarg);
 			fallthrough;
 		case 'r':
 			rflag = 1;
@@ -202,7 +219,7 @@ main(int argc, char **argv)
 			progname, fname);
 		exit(1);
 	}
-	if (rflag && (!xi.rt.dev && !geo.rtstart)) {
+	if (rflag && (!xi.rt.dev && !xi.nr_raid_devices & !geo.rtstart)) {
 		fprintf(stderr,
 			_("%s: failed to access realtime device for %s\n"),
 			progname, fname);
@@ -211,6 +228,13 @@ main(int argc, char **argv)
 
 	xfs_report_geom(&geo, datadev, logdev, rtdev);
 
+	if (dsize_str)
+		dsize = parse_size(&geo, dsize_str);
+	if (lsize_str)
+		lsize = parse_size(&geo, lsize_str);
+	if (rsize_str)
+		rsize = parse_size(&geo, rsize_str);
+
 	if (geo.rtstart) {
 		xfs_daddr_t rtstart = geo.rtstart * (geo.blocksize / BBSIZE);
 
diff --git a/man/man8/xfs_growfs.8 b/man/man8/xfs_growfs.8
index 2e329fa61758..d6e5fe13e943 100644
--- a/man/man8/xfs_growfs.8
+++ b/man/man8/xfs_growfs.8
@@ -57,6 +57,32 @@ The filesystem must be mounted to be grown (see
 .BR mount (8)).
 The existing contents of the filesystem are undisturbed, and the added space
 becomes available for additional file storage.
+The following lists possible multiplication suffixes for any argument specifying
+sizes.
+.RS
+.PD 0
+.HP
+.BR s "\ \-\ multiply by sector size (default = 512, see " \-s
+option below).
+.HP
+.BR b "\ \-\ multiply by filesystem block size (default = 4K, see " \-b
+option below).
+.HP
+.BR k "\ \-\ multiply by one kilobyte (1,024 bytes)."
+.HP
+.BR m "\ \-\ multiply by one megabyte (1,048,576 bytes)."
+.HP
+.BR g "\ \-\ multiply by one gigabyte (1,073,741,824 bytes)."
+.HP
+.BR t "\ \-\ multiply by one terabyte (1,099,511,627,776 bytes)."
+.HP
+.BR p "\ \-\ multiply by one petabyte (1,024 terabytes)."
+.HP
+.BR e "\ \-\ multiply by one exabyte (1,048,576 terabytes)."
+.PD
+.RE
+If no suffix is specified, the sizes are in file system blocks.
+.RE
 .SH OPTIONS
 .TP
 .BI "\-d | \-D " size
@@ -67,9 +93,8 @@ option is given, the data section is changed to that
 .IR size ,
 otherwise the data section is grown to the largest size possible with the
 .B \-d
-option. The size is expressed in filesystem blocks. A filesystem with only
-1 AG cannot be shrunk further, and a filesystem cannot be shrunk to the point
-where it would only have 1 AG.
+option. A filesystem with only 1 AG cannot be shrunk further, and a
+filesystem cannot be shrunk to the point where it would only have 1 AG.
 .B [NOTE: Only shrinking the last AG without removing it is implemented]
 .TP
 .B \-e
@@ -90,8 +115,7 @@ shrunk, or moved. If the
 .I size
 option is given, the log section is changed to be that
 .IR size ,
-if possible. The size is expressed in filesystem blocks.
-The size of an internal log must be smaller than the size
+if possible. The size of an internal log must be smaller than the size
 of an allocation group (this value is printed at
 .BR mkfs (8)
 time). If neither
@@ -124,9 +148,8 @@ Specifies that the real-time section of the filesystem should be grown. If the
 option is given, the real-time section is grown to that size, otherwise
 the real-time section is grown to the largest size possible with the
 .B \-r
-option. The size is expressed in filesystem blocks.
-The filesystem does not need to have contained a real-time section before
-the
+option. The filesystem does not need to have contained a real-time section
+before the
 .B xfs_growfs
 operation.
 .TP
-- 
2.53.0