Implement argp support for mkfs and add --force

Support max-meta-size and max-data-size using KMGTP units with rounding.

Detect other fs signatures using blkid library.

Detect ScoutFS super using magic value.

Move read_block() from print.c into util.c since blkid also needs it.

Signed-off-by: Andy Grover <agrover@versity.com>
This commit is contained in:
Andy Grover
2020-12-02 12:48:55 -08:00
parent 6b5ddf2b3a
commit 7befc61482
8 changed files with 270 additions and 128 deletions

View File

@@ -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 $<]

View File

@@ -16,6 +16,7 @@ BuildRequires: git
BuildRequires: gzip
BuildRequires: libuuid-devel
BuildRequires: openssl-devel
BuildRequires: libblkid-devel
#Requires: kmod-scoutfs = %{version}

94
utils/src/blkid.c Normal file
View File

@@ -0,0 +1,94 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <blkid/blkid.h>
#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);
}

6
utils/src/blkid.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef _BLKID_H_
#define _BLKID_H_
int check_bdev(int fd, char *path, char *usage);
#endif

View File

@@ -11,12 +11,12 @@
#include <sys/stat.h>
#include <unistd.h>
#include <assert.h>
#include <getopt.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <inttypes.h>
#include <argp.h>
#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", "<path>", "write a new file system", mkfs_func);
cmd_register("mkfs", "<meta-device> <data-device>", "write a new file system", mkfs_cmd);
/* for lack of some other place to put these.. */
build_assert(sizeof(uuid_t) == SCOUTFS_UUID_BYTES);

View File

@@ -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);

View File

@@ -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;
}
}

View File

@@ -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