diff --git a/mkfs/main.c b/mkfs/main.c index 4c2ce98c7..00c6b7677 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -464,6 +464,8 @@ 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"), "", @@ -1176,6 +1178,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; @@ -1220,6 +1223,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, @@ -1250,6 +1254,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 @@ -1386,6 +1392,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; @@ -1433,6 +1442,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; @@ -2104,8 +2119,17 @@ int BOX_MAIN(mkfs)(int argc, char **argv) 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 5f4cfb93c..c3e39036c 100644 --- a/mkfs/rootdir.c +++ b/mkfs/rootdir.c @@ -17,6 +17,8 @@ */ #include "kerncompat.h" +#include +#include #include #include #include @@ -52,6 +54,7 @@ #include "common/root-tree-utils.h" #include "common/path-utils.h" #include "common/rbtree-utils.h" +#include "common/units.h" #include "mkfs/rootdir.h" #define LZO_LEN 4 @@ -1925,9 +1928,10 @@ static int set_device_size(struct btrfs_fs_info *fs_info, } 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; + u64 blk_device_size; struct btrfs_device *device; struct list_head *cur; struct stat file_stat; @@ -1955,6 +1959,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); @@ -1969,6 +1981,15 @@ int btrfs_mkfs_shrink_fs(struct btrfs_fs_info *fs_info, u64 *new_size_ret, error("failed to stat devid %llu: %m", device->devid); return ret; } + if (S_ISBLK(file_stat.st_mode)) { + ioctl(device->fd, BLKGETSIZE64, &blk_device_size); + if (blk_device_size < new_size) { + warning("blkdev size %llu (%s) is smaller than fs size %llu (%s)", + blk_device_size, + pretty_size(blk_device_size), new_size, + pretty_size(new_size)); + } + } if (!S_ISREG(file_stat.st_mode)) return ret; ret = ftruncate(device->fd, new_size); diff --git a/mkfs/rootdir.h b/mkfs/rootdir.h index b32fda5bf..1eee38245 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