btrfs-progs: Add minimum device size check
authorQu Wenruo <quwenruo@cn.fujitsu.com>
Fri, 4 Jul 2014 07:29:17 +0000 (15:29 +0800)
committerDavid Sterba <dsterba@suse.cz>
Fri, 22 Aug 2014 12:43:11 +0000 (14:43 +0200)
Btrfs has global block reservation, so even mkfs.btrfs can execute
without problem, there is still a possibility that the filesystem can't
be mounted.
For example when mkfs.btrfs on a 8M file on x86_64 platform, kernel will
refuse to mount due to ENOSPC, since system block group takes 4M and
mixed block group takes 4M, and global block reservation will takes all
the 4M from mixed block group, which makes btrfs unable to create uuid
tree.

This patch will add minimum device size check before actually mkfs.
The minimum size calculation uses a simplified one:
minimum_size_for_each_dev = 2 * (system block group + global block rsv)
and global block rsv = leafsize << 10

Signed-off-by: Qu Wenruo <quwenruo@cn.fujitsu.com>
Signed-off-by: David Sterba <dsterba@suse.cz>
mkfs.c
utils.c
utils.h

diff --git a/mkfs.c b/mkfs.c
index 7695c015dd425f2031caae99366bae3f6fab6bd5..52b9a8d2a73da9bea44e9b8b79e53a9a212141e5 100644 (file)
--- a/mkfs.c
+++ b/mkfs.c
@@ -1451,6 +1451,36 @@ int main(int ac, char **av)
                }
        }
 
+       /* Check device/block_count after the leafsize is determined */
+       if (block_count && block_count < btrfs_min_dev_size(leafsize)) {
+               fprintf(stderr,
+                       "Size '%llu' is too small to make a usable filesystem\n",
+                       block_count);
+               fprintf(stderr,
+                       "Minimum size for btrfs filesystem is %llu\n",
+                       btrfs_min_dev_size(leafsize));
+               exit(1);
+       }
+       for (i = saved_optind; i < saved_optind + dev_cnt; i++) {
+               char *path;
+
+               path = av[i];
+               ret = test_minimum_size(path, leafsize);
+               if (ret < 0) {
+                       fprintf(stderr, "Failed to check size for '%s': %s\n",
+                               path, strerror(-ret));
+                       exit (1);
+               }
+               if (ret > 0) {
+                       fprintf(stderr,
+                               "'%s' is too small to make a usable filesystem\n",
+                               path);
+                       fprintf(stderr,
+                               "Minimum size for each btrfs device is %llu.\n",
+                               btrfs_min_dev_size(leafsize));
+                       exit(1);
+               }
+       }
        ret = test_num_disk_vs_raid(metadata_profile, data_profile,
                        dev_cnt, mixed, estr);
        if (ret) {
diff --git a/utils.c b/utils.c
index 46c3a4313c0c307817b21f0ed3fb710f78a7c76b..c139eb2ee59d64d11bcaadb5157a0997c0677e7c 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -2344,3 +2344,23 @@ int find_mount_root(const char *path, char **mount_root)
        free(longest_match);
        return ret;
 }
+
+int test_minimum_size(const char *file, u32 leafsize)
+{
+       int fd;
+       struct stat statbuf;
+
+       fd = open(file, O_RDONLY);
+       if (fd < 0)
+               return -errno;
+       if (stat(file, &statbuf) < 0) {
+               close(fd);
+               return -errno;
+       }
+       if (btrfs_device_size(fd, &statbuf) < btrfs_min_dev_size(leafsize)) {
+               close(fd);
+               return 1;
+       }
+       close(fd);
+       return 0;
+}
diff --git a/utils.h b/utils.h
index 37fe1ba02730915e0d5229cf010adf697ab48365..6599ee4d4a943d261f56f7bce1d87c560f17cc61 100644 (file)
--- a/utils.h
+++ b/utils.h
@@ -105,4 +105,26 @@ int get_device_info(int fd, u64 devid,
                struct btrfs_ioctl_dev_info_args *di_args);
 int test_uuid_unique(char *fs_uuid);
 
+int test_minimum_size(const char *file, u32 leafsize);
+
+/*
+ * Btrfs minimum size calculation is complicated, it should include at least:
+ * 1. system group size
+ * 2. minimum global block reserve
+ * 3. metadata used at mkfs
+ * 4. space reservation to create uuid for first mount.
+ * Also, raid factor should also be taken into consideration.
+ * To avoid the overkill calculation, (system group + global block rsv) * 2
+ * for *EACH* device should be good enough.
+ */
+static inline u64 btrfs_min_global_blk_rsv_size(u32 leafsize)
+{
+       return leafsize << 10;
+}
+static inline u64 btrfs_min_dev_size(u32 leafsize)
+{
+       return 2 * (BTRFS_MKFS_SYSTEM_GROUP_SIZE +
+                   btrfs_min_global_blk_rsv_size(leafsize));
+}
+
 #endif