diff --git a/mkfs.c b/mkfs.c index 7695c015..52b9a8d2 100644 --- 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 46c3a431..c139eb2e 100644 --- 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 37fe1ba0..6599ee4d 100644 --- 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