diff --git a/utils/Makefile b/utils/Makefile index ad146e5b..8116bcd8 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -47,7 +47,7 @@ endif $(BIN): $(OBJ) $(QU) [BIN $@] - $(VE)gcc -o $@ $^ -luuid -lm -lcrypto + $(VE)gcc -o $@ $^ -luuid -lm -lcrypto -lblkid %.o %.d: %.c Makefile sparse.sh $(QU) [CC $<] diff --git a/utils/scoutfs-utils.spec.in b/utils/scoutfs-utils.spec.in index 35219721..42bb86c5 100644 --- a/utils/scoutfs-utils.spec.in +++ b/utils/scoutfs-utils.spec.in @@ -16,6 +16,7 @@ BuildRequires: git BuildRequires: gzip BuildRequires: libuuid-devel BuildRequires: openssl-devel +BuildRequires: libblkid-devel #Requires: kmod-scoutfs = %{version} diff --git a/utils/src/blkid.c b/utils/src/blkid.c new file mode 100644 index 00000000..7dd19574 --- /dev/null +++ b/utils/src/blkid.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "util.h" +#include "format.h" +#include "blkid.h" + +static int check_bdev_blkid(int fd, char *devname, char *usage) +{ + blkid_probe pr; + int ret = 0; + + pr = blkid_new_probe_from_filename(devname); + if (!pr) { + fprintf(stderr, "%s: failed to create a new libblkid probe\n", devname); + goto out; + } + + /* enable partitions probing (superblocks are enabled by default) */ + ret = blkid_probe_enable_partitions(pr, true); + if (ret == -1) { + fprintf(stderr, "%s: blkid_probe_enable_partitions() failed\n", devname); + goto out; + } + + ret = blkid_do_fullprobe(pr); + if (ret == -1) { + fprintf(stderr, "%s: blkid_do_fullprobe() failed", devname); + goto out; + } else if (ret == 0) { + const char *type; + + if (!blkid_probe_lookup_value(pr, "TYPE", &type, NULL)) { + fprintf(stderr, "%s: appears to contain an existing " + "%s superblock\n", devname, type); + ret = -1; + goto out; + } + + if (!blkid_probe_lookup_value(pr, "PTTYPE", &type, NULL)) { + fprintf(stderr, "%s: appears to contain a partition " + "table (%s)\n", devname, type); + ret = -1; + goto out; + } + } else { + /* return 0 if ok */ + ret = 0; + } + +out: + blkid_free_probe(pr); + + return ret; +} + +static int check_bdev_scoutfs(int fd, char *devname, char *usage) +{ + struct scoutfs_super_block *super = NULL; + int ret; + + ret = read_block(fd, SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, (void **)&super); + if (ret) + return ret; + + if (le32_to_cpu(super->hdr.magic) == SCOUTFS_SUPER_MAGIC) { + fprintf(stderr, "%s: appears to contain an existing " + "ScoutFS superblock\n", devname); + ret = -EINVAL; + } + + free(super); + + return ret; +} + + +/* + * Returns -1 on error, 0 otherwise. + */ +int check_bdev(int fd, char *devname, char *usage) +{ + return check_bdev_blkid(fd, devname, usage) ?: + /* Our sig is not in blkid (yet) so check explicitly for us. */ + check_bdev_scoutfs(fd, devname, usage); +} diff --git a/utils/src/blkid.h b/utils/src/blkid.h new file mode 100644 index 00000000..ddb708c4 --- /dev/null +++ b/utils/src/blkid.h @@ -0,0 +1,6 @@ +#ifndef _BLKID_H_ +#define _BLKID_H_ + +int check_bdev(int fd, char *path, char *usage); + +#endif diff --git a/utils/src/mkfs.c b/utils/src/mkfs.c index 8d5e4dff..d9c04dd4 100644 --- a/utils/src/mkfs.c +++ b/utils/src/mkfs.c @@ -11,12 +11,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include "sparse.h" #include "cmd.h" @@ -30,6 +30,7 @@ #include "bitops.h" #include "btree.h" #include "leaf_item_hash.h" +#include "blkid.h" static int write_raw_block(int fd, u64 blkno, int shift, void *blk) { @@ -99,6 +100,16 @@ static int write_alloc_root(struct scoutfs_super_block *super, int fd, return write_raw_block(fd, blkno, SCOUTFS_BLOCK_LG_SHIFT, bt); } +struct mkfs_args { + unsigned long long quorum_count; + char *meta_device; + char *data_device; + unsigned long long max_meta_size; + unsigned long long max_data_size; + bool force; + char __pad[7]; +}; + /* * Make a new file system by writing: * - super blocks @@ -108,19 +119,18 @@ static int write_alloc_root(struct scoutfs_super_block *super, int fd, * Superblock is written to both metadata and data devices, everything else is * written only to the metadata device. */ -static int write_new_fs(char *meta_path, char *data_path, - int meta_fd, int data_fd, - u8 quorum_count, - u64 max_meta_size, u64 max_data_size) +static int do_mkfs(struct mkfs_args *args) { - struct scoutfs_super_block *super; + struct scoutfs_super_block *super = NULL; struct scoutfs_inode inode; struct scoutfs_alloc_list_block *lblk; - struct scoutfs_btree_block *bt; + struct scoutfs_btree_block *bt = NULL; struct scoutfs_key key; struct timeval tv; + int meta_fd = -1; + int data_fd = -1; char uuid_str[37]; - void *zeros; + void *zeros = NULL; u64 blkno; u64 meta_size; u64 data_size; @@ -135,6 +145,33 @@ static int write_new_fs(char *meta_path, char *data_path, gettimeofday(&tv, NULL); + meta_fd = open(args->meta_device, O_RDWR | O_EXCL); + if (meta_fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + args->meta_device, strerror(errno), errno); + goto out; + } + if (!args->force) { + ret = check_bdev(meta_fd, args->meta_device, "meta"); + if (ret) + return ret; + } + + data_fd = open(args->data_device, O_RDWR | O_EXCL); + if (data_fd < 0) { + ret = -errno; + fprintf(stderr, "failed to open '%s': %s (%d)\n", + args->data_device, strerror(errno), errno); + goto out; + } + if (!args->force) { + ret = check_bdev(data_fd, args->data_device, "data"); + if (ret) + return ret; + } + + super = calloc(1, SCOUTFS_BLOCK_SM_SIZE); bt = calloc(1, SCOUTFS_BLOCK_LG_SIZE); zeros = calloc(1, SCOUTFS_BLOCK_SM_SIZE); @@ -145,13 +182,13 @@ static int write_new_fs(char *meta_path, char *data_path, goto out; } - ret = device_size(meta_path, meta_fd, 2ULL * (1024 * 1024 * 1024), - max_meta_size, "meta", &meta_size); + ret = device_size(args->meta_device, meta_fd, 2ULL * (1024 * 1024 * 1024), + args->max_meta_size, "meta", &meta_size); if (ret) goto out; - ret = device_size(data_path, data_fd, 8ULL * (1024 * 1024 * 1024), - max_data_size, "data", &data_size); + ret = device_size(args->data_device, data_fd, 8ULL * (1024 * 1024 * 1024), + args->max_data_size, "data", &data_size); if (ret) goto out; @@ -179,7 +216,7 @@ static int write_new_fs(char *meta_path, char *data_path, super->total_data_blocks = cpu_to_le64(last_data - first_data + 1); super->first_data_blkno = cpu_to_le64(first_data); super->last_data_blkno = cpu_to_le64(last_data); - super->quorum_count = quorum_count; + super->quorum_count = args->quorum_count; /* fs root starts with root inode and its index items */ blkno = next_meta++; @@ -293,7 +330,7 @@ static int write_new_fs(char *meta_path, char *data_path, if (fsync(data_fd)) { ret = -errno; fprintf(stderr, "failed to fsync '%s': %s (%d)\n", - data_path, strerror(errno), errno); + args->data_device, strerror(errno), errno); goto out; } @@ -306,7 +343,7 @@ static int write_new_fs(char *meta_path, char *data_path, if (fsync(meta_fd)) { ret = -errno; fprintf(stderr, "failed to fsync '%s': %s (%d)\n", - meta_path, strerror(errno), errno); + args->meta_device, strerror(errno), errno); goto out; } @@ -321,8 +358,8 @@ static int write_new_fs(char *meta_path, char *data_path, " 64KB metadata blocks: "SIZE_FMT"\n" " 4KB data blocks: "SIZE_FMT"\n" " quorum count: %u\n", - meta_path, - data_path, + args->meta_device, + args->data_device, le64_to_cpu(super->hdr.fsid), le64_to_cpu(super->format_hash), uuid_str, @@ -340,102 +377,105 @@ out: free(bt); if (zeros) free(zeros); + if (meta_fd != -1) + close(meta_fd); + if (data_fd != -1) + close(data_fd); return ret; } -static struct option long_ops[] = { - { "quorum_count", 1, NULL, 'Q' }, - { NULL, 0, NULL, 0} +static int parse_opt(int key, char *arg, struct argp_state *state) +{ + struct mkfs_args *args = state->input; + int ret; + + switch (key) { + case 'Q': + ret = parse_u64(arg, &args->quorum_count); + if (ret) + return ret; + break; + case 'f': + args->force = true; + break; + case 'm': /* max-meta-size */ + { + u64 prev_val; + ret = parse_human(arg, &args->max_meta_size); + if (ret) + return ret; + prev_val = args->max_meta_size; + args->max_meta_size = round_down(args->max_meta_size, SCOUTFS_BLOCK_LG_SIZE); + if (args->max_meta_size != prev_val) + fprintf(stderr, "Meta dev size %llu rounded down to %llu bytes\n", + prev_val, args->max_meta_size); + break; + } + case 'd': /* max-data-size */ + { + u64 prev_val; + ret = parse_human(arg, &args->max_data_size); + if (ret) + return ret; + prev_val = args->max_data_size; + args->max_data_size = round_down(args->max_data_size, SCOUTFS_BLOCK_SM_SIZE); + if (args->max_data_size != prev_val) + fprintf(stderr, "Data dev size %llu rounded down to %llu bytes\n", + prev_val, args->max_data_size); + break; + } + case ARGP_KEY_ARG: + if (!args->meta_device) + args->meta_device = strdup_or_error(state, arg); + else if (!args->data_device) + args->data_device = strdup_or_error(state, arg); + else + argp_error(state, "more than two arguments given"); + break; + case ARGP_KEY_FINI: + if (!args->quorum_count) + argp_error(state, "must provide nonzero quorum count with --quorum-count|-Q option"); + if (!args->meta_device) + argp_error(state, "no metadata device argument given"); + if (!args->data_device) + argp_error(state, "no data device argument given"); + break; + default: + break; + } + + return 0; +} + +static struct argp_option options[] = { + { "quorum-count", 'Q', "NUM", 0, "Number of voters required to use the filesystem [Required]"}, + { "force", 'f', NULL, 0, "Overwrite existing data on block devices"}, + { "max-meta-size", 'm', "SIZE", 0, "Use a size less than the base metadata device size (bytes or KMGTP units)"}, + { "max-data-size", 'd', "SIZE", 0, "Use a size less than the base data device size (bytes or KMGTP units)"}, + { NULL } }; -static int mkfs_func(int argc, char *argv[]) +static int mkfs_cmd(int argc, char *argv[]) { - unsigned long long ull; - u8 quorum_count = 0; - u64 max_data_size = 0; - u64 max_meta_size = 0; - char *end = NULL; - char *meta_path; - char *data_path; - int meta_fd; - int data_fd; + struct argp argp = { + options, + parse_opt, + "META-DEVICE DATA-DEVICE", + "Initialize a new ScoutFS filesystem" + }; + struct mkfs_args mkfs_args = {0}; int ret; - int c; - while ((c = getopt_long(argc, argv, "Q:D:M:", long_ops, NULL)) != -1) { - switch (c) { - case 'Q': - ull = strtoull(optarg, &end, 0); - if (*end != '\0' || ull == 0 || - ull > SCOUTFS_QUORUM_MAX_COUNT) { - printf("scoutfs: invalid quorum count '%s'\n", - optarg); - return -EINVAL; - } - quorum_count = ull; - break; - case 'D': - ret = parse_human(optarg, &max_data_size); - if (ret < 0) { - printf("scoutfs: invalid data device size '%s'\n", - optarg); - return ret; - } - break; - case 'M': - ret = parse_human(optarg, &max_meta_size); - if (ret < 0) { - printf("scoutfs: invalid meta device size '%s'\n", - optarg); - return ret; - } - break; - case '?': - default: - return -EINVAL; - } - } - - if (optind + 2 != argc) { - printf("scoutfs: mkfs: paths to metadata and data devices are required\n"); - return -EINVAL; - } - - meta_path = argv[optind]; - data_path = argv[optind + 1]; - - if (!quorum_count) { - printf("provide quorum count with --quorum_count|-Q option\n"); - return -EINVAL; - } - - meta_fd = open(meta_path, O_RDWR | O_EXCL); - if (meta_fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open metadata device '%s': %s (%d)\n", - meta_path, strerror(errno), errno); + ret = argp_parse(&argp, argc, argv, 0, NULL, &mkfs_args); + if (ret) return ret; - } - data_fd = open(data_path, O_RDWR | O_EXCL); - if (data_fd < 0) { - ret = -errno; - fprintf(stderr, "failed to open data device '%s': %s (%d)\n", - data_path, strerror(errno), errno); - return ret; - } - - ret = write_new_fs(meta_path, data_path, meta_fd, data_fd, - quorum_count, max_meta_size, max_data_size); - close(meta_fd); - close(data_fd); - - return ret; + return do_mkfs(&mkfs_args); } static void __attribute__((constructor)) mkfs_ctor(void) { - cmd_register("mkfs", "", "write a new file system", mkfs_func); + cmd_register("mkfs", " ", "write a new file system", mkfs_cmd); /* for lack of some other place to put these.. */ build_assert(sizeof(uuid_t) == SCOUTFS_UUID_BYTES); diff --git a/utils/src/print.c b/utils/src/print.c index d7e548ef..faacb467 100644 --- a/utils/src/print.c +++ b/utils/src/print.c @@ -26,34 +26,6 @@ #include "srch.h" #include "leaf_item_hash.h" -static int read_block(int fd, u64 blkno, int shift, void **ret_val) -{ - size_t size = 1ULL << shift; - void *buf; - int ret; - - *ret_val = NULL; - - buf = malloc(size); - if (!buf) - return -ENOMEM; - - ret = pread(fd, buf, size, blkno << shift); - if (ret == -1) { - fprintf(stderr, "read blkno %llu returned %d: %s (%d)\n", - blkno, ret, strerror(errno), errno); - free(buf); - return -errno; - } else if (ret != size) { - fprintf(stderr, "incomplete pread\n"); - free(buf); - return -EINVAL; - } else { - *ret_val = buf; - return 0; - } -} - static void print_block_header(struct scoutfs_block_header *hdr, int size) { u32 crc = crc_block(hdr, size); diff --git a/utils/src/util.c b/utils/src/util.c index dfa948ff..d30e44ba 100644 --- a/utils/src/util.c +++ b/utils/src/util.c @@ -68,3 +68,31 @@ int get_path(char *path, int flags) return ret; } + +int read_block(int fd, u64 blkno, int shift, void **ret_val) +{ + size_t size = 1ULL << shift; + void *buf; + int ret; + + *ret_val = NULL; + + buf = malloc(size); + if (!buf) + return -ENOMEM; + + ret = pread(fd, buf, size, blkno << shift); + if (ret == -1) { + fprintf(stderr, "read blkno %llu returned %d: %s (%d)\n", + blkno, ret, strerror(errno), errno); + free(buf); + return -errno; + } else if (ret != size) { + fprintf(stderr, "incomplete pread\n"); + free(buf); + return -EINVAL; + } else { + *ret_val = buf; + return 0; + } +} diff --git a/utils/src/util.h b/utils/src/util.h index c70ddbb5..ab195d9d 100644 --- a/utils/src/util.h +++ b/utils/src/util.h @@ -112,5 +112,6 @@ static inline int memcmp_lens(const void *a, int a_len, } int get_path(char *path, int flags); +int read_block(int fd, u64 blkno, int shift, void **ret_val); #endif