diff mbox series

[v2,1/1] btrfs-progs: add slack space for mkfs --shrink

Message ID 121133b547f15980bd02280328bb04017c495ec9.1741890798.git.loemra.dev@gmail.com (mailing list archive)
State New
Headers show
Series [v2,1/1] btrfs-progs: add slack space for mkfs --shrink | expand

Commit Message

Leo Martins March 13, 2025, 6:35 p.m. UTC
This patch adds a flag `--shrink-slack-size SIZE` to the mkfs.btrfs
allowing users to specify slack when shrinking the filesystem.
Previously if you wanted to use --shrink and include extra space in the
filesystem you would need to use btrfs resize, however, this requires
mounting the filesystem which requires CAP_SYS_ADMIN.

The new syntax is:
`mkfs.btrfs --shrink --shrink-slack-size SIZE`

Where slack size is an argument specifying the desired
free space to add to a shrunk fs. If not provided, the default
slack size is 0.

I have not added any upper bounds checking on SIZE as I'm not sure
it's necessary.

The following command will succeed without warning even if `$DEVICE` is
a block device smaller than 10T. However, mounting `$DEVICE` will fail.

`mkfs.btrfs -f $DEVICE --root $ROOT --shrink --shrink-slack-size 10T`

I don't know if this would be considered incorrect because $DEVICE could
also be a regular file that can ftruncate up to the appropriate size.
Should I add a warning message, or leave it to mount time to indicate
that something is wrong?

V2:
- change --shrink[=SLACK SIZE] to --shrink-slack-size SIZE
- check for slack size alignment
- fix formatting
- remove new_size > device size warning message


Signed-off-by: Leo Martins <loemra.dev@gmail.com>
---
 mkfs/main.c    | 25 ++++++++++++++++++++++++-
 mkfs/rootdir.c | 11 ++++++++++-
 mkfs/rootdir.h |  2 +-
 3 files changed, 35 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/mkfs/main.c b/mkfs/main.c
index dc73de47..ab5a1ced 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -461,6 +461,7 @@  static const char * const mkfs_usage[] = {
 	OPTLINE("", "- default - the SUBDIR will be a subvolume and also set as default (can be specified only once)"),
 	OPTLINE("", "- default-ro - like 'default' and is created as read-only subvolume (can be specified only once)"),
 	OPTLINE("--shrink", "(with --rootdir) shrink the filled filesystem to minimal size"),
+	OPTLINE("--shrink-slack-size SIZE", "(with --shrink) include extra slack space after shrinking (default 0)"),
 	OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"),
 	OPTLINE("-f|--force", "force overwrite of existing filesystem"),
 	"",
@@ -1173,6 +1174,7 @@  int BOX_MAIN(mkfs)(int argc, char **argv)
 	int i;
 	bool ssd = false;
 	bool shrink_rootdir = false;
+	u64 shrink_slack_size = 0;
 	u64 source_dir_size = 0;
 	u64 min_dev_size;
 	u64 shrink_size;
@@ -1217,6 +1219,7 @@  int BOX_MAIN(mkfs)(int argc, char **argv)
 		int c;
 		enum {
 			GETOPT_VAL_SHRINK = GETOPT_VAL_FIRST,
+			GETOPT_VAL_SHRINK_SLACK_SIZE,
 			GETOPT_VAL_CHECKSUM,
 			GETOPT_VAL_GLOBAL_ROOTS,
 			GETOPT_VAL_DEVICE_UUID,
@@ -1247,6 +1250,8 @@  int BOX_MAIN(mkfs)(int argc, char **argv)
 			{ "quiet", 0, NULL, 'q' },
 			{ "verbose", 0, NULL, 'v' },
 			{ "shrink", no_argument, NULL, GETOPT_VAL_SHRINK },
+			{ "shrink-slack-size", required_argument, NULL,
+			  GETOPT_VAL_SHRINK_SLACK_SIZE },
 			{ "compress", required_argument, NULL,
 				GETOPT_VAL_COMPRESS },
 #if EXPERIMENTAL
@@ -1383,6 +1388,9 @@  int BOX_MAIN(mkfs)(int argc, char **argv)
 			case GETOPT_VAL_SHRINK:
 				shrink_rootdir = true;
 				break;
+			case GETOPT_VAL_SHRINK_SLACK_SIZE:
+				shrink_slack_size = arg_strtou64_with_suffix(optarg);
+				break;
 			case GETOPT_VAL_CHECKSUM:
 				csum_type = parse_csum_type(optarg);
 				break;
@@ -1430,6 +1438,12 @@  int BOX_MAIN(mkfs)(int argc, char **argv)
 		ret = 1;
 		goto error;
 	}
+	if (shrink_slack_size > 0 && !shrink_rootdir) {
+		error("the option --shrink-slack-size must be used with --shrink");
+		ret = 1;
+		goto error;
+
+	}
 	if (!list_empty(&subvols) && source_dir == NULL) {
 		error("option --subvol must be used with --rootdir");
 		ret = 1;
@@ -2108,8 +2122,17 @@  raid_groups:
 
 		if (shrink_rootdir) {
 			pr_verbose(LOG_DEFAULT, "  Shrink:           yes\n");
+			if (shrink_slack_size > 0) {
+				pr_verbose(
+					LOG_DEFAULT,
+					"  Shrink slack:           %llu (%s)\n",
+					shrink_slack_size,
+					pretty_size(shrink_slack_size));
+			}
 			ret = btrfs_mkfs_shrink_fs(fs_info, &shrink_size,
-						   shrink_rootdir);
+						   shrink_rootdir,
+						   shrink_slack_size);
+
 			if (ret < 0) {
 				errno = -ret;
 				error("error while shrinking filesystem: %m");
diff --git a/mkfs/rootdir.c b/mkfs/rootdir.c
index 19273947..c14546b6 100644
--- a/mkfs/rootdir.c
+++ b/mkfs/rootdir.c
@@ -48,6 +48,7 @@ 
 #include "common/internal.h"
 #include "common/messages.h"
 #include "common/utils.h"
+#include "common/units.h"
 #include "common/extent-tree-utils.h"
 #include "common/root-tree-utils.h"
 #include "common/path-utils.h"
@@ -1924,7 +1925,7 @@  err:
 }
 
 int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,
-			 bool shrink_file_size)
+			 bool shrink_file_size, u64 slack_size)
 {
 	u64 new_size;
 	struct btrfs_device *device;
@@ -1954,6 +1955,14 @@  int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,
 		return -EUCLEAN;
 	}
 
+	if (!IS_ALIGNED(slack_size, fs_info->sectorsize)) {
+		error("slack size %llu not aligned to %u",
+				slack_size, fs_info->sectorsize);
+		return -EUCLEAN;
+	}
+
+	new_size += slack_size;
+
 	device = list_entry(fs_info->fs_devices->devices.next,
 			   struct btrfs_device, dev_list);
 	ret = set_device_size(fs_info, device, new_size);
diff --git a/mkfs/rootdir.h b/mkfs/rootdir.h
index b32fda5b..1eee3824 100644
--- a/mkfs/rootdir.h
+++ b/mkfs/rootdir.h
@@ -52,6 +52,6 @@  int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
 u64 btrfs_mkfs_size_dir(const char *dir_name, u32 sectorsize, u64 min_dev_size,
 			u64 meta_profile, u64 data_profile);
 int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret,
-			 bool shrink_file_size);
+			 bool shrink_file_size, u64 slack_size);
 
 #endif