mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-03 19:04:00 +00:00
Add prepare-empty-data-device scoutfs command
Add a command for writing a super block to a new data device after reading the metadata device to ensure that there's no existing data on the old data device. Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
@@ -328,6 +328,46 @@ The range of supported versions is visible in the output of
|
||||
.RE
|
||||
.PD
|
||||
|
||||
.TP
|
||||
.BI "prepare-empty-data-device {-c|--check} META-DEVICE DATA-DEVICE"
|
||||
.sp
|
||||
Prepare an unused device for use as the data device for an existing file
|
||||
system. This will write an initialized super block to the specified
|
||||
data device, destroying any existing contents. The specified metadata
|
||||
device will not be modified. The file system must be fully unmounted
|
||||
and any client mount recovery must be complete.
|
||||
.sp
|
||||
The existing metadata device is read to ensure that it's safe to stop
|
||||
using the old data device. The data block allocators must indicate that
|
||||
all data blocks are free. If there are still data blocks referenced by
|
||||
files then the command will fail. The contents of these files must be
|
||||
freed for the command to proceed.
|
||||
.sp
|
||||
A new super block is written to the new data device. The device can
|
||||
then be used as the data device to mount the file system. As this
|
||||
switch is made all client mounts must refer to the new device. The old
|
||||
device is not modified and still contains a valid data super block that
|
||||
could be mounted, creating data device writes that wouldn't be read by
|
||||
mounts using the new device.
|
||||
.sp
|
||||
The number of data blocks available to the file system will not change
|
||||
as the new data device is used. The new device must be large enough to
|
||||
store all the data blocks that were available on the old device. If the
|
||||
new device is larger then its added capacity can be used by growing the
|
||||
new data device with the resize-devices command once it is mounted.
|
||||
.RS 1.0i
|
||||
.PD 0
|
||||
.TP
|
||||
.sp
|
||||
.B "-c, --check"
|
||||
Only check for errors that would prevent a new empty data device from
|
||||
being used. No changes will be made to the data device. If the data
|
||||
device is provided then its size will be checked to make sure that it is
|
||||
large enough. This can be used to test the metadata for data references
|
||||
before destroying an old empty data device.
|
||||
.RE
|
||||
.PD
|
||||
|
||||
.TP
|
||||
.BI "print {-S|--skip-likely-huge} META-DEVICE"
|
||||
.sp
|
||||
|
||||
247
utils/src/prepare_empty_data_device.c
Normal file
247
utils/src/prepare_empty_data_device.c
Normal file
@@ -0,0 +1,247 @@
|
||||
#define _GNU_SOURCE /* O_DIRECT */
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.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"
|
||||
#include "util.h"
|
||||
#include "format.h"
|
||||
#include "parse.h"
|
||||
#include "crc.h"
|
||||
#include "rand.h"
|
||||
#include "dev.h"
|
||||
#include "key.h"
|
||||
#include "bitops.h"
|
||||
#include "btree.h"
|
||||
#include "leaf_item_hash.h"
|
||||
#include "blkid.h"
|
||||
#include "quorum.h"
|
||||
|
||||
struct prepare_empty_data_dev_args {
|
||||
char *meta_device;
|
||||
char *data_device;
|
||||
bool check;
|
||||
};
|
||||
|
||||
static int do_prepare_empty_data_dev(struct prepare_empty_data_dev_args *args)
|
||||
{
|
||||
struct scoutfs_super_block *meta_super = NULL;
|
||||
struct scoutfs_super_block *data_super = NULL;
|
||||
char uuid_str[37];
|
||||
int meta_fd = -1;
|
||||
int data_fd = -1;
|
||||
u64 data_blocks;
|
||||
u64 data_size;
|
||||
u64 in_use;
|
||||
int ret;
|
||||
|
||||
ret = posix_memalign((void **)&data_super, SCOUTFS_BLOCK_SM_SIZE, SCOUTFS_BLOCK_SM_SIZE);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to allocate data super block: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
meta_fd = open(args->meta_device, O_DIRECT | O_SYNC | O_RDONLY | O_EXCL);
|
||||
if (meta_fd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open meta device '%s': %s (%d)\n",
|
||||
args->meta_device, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = read_block_verify(meta_fd, SCOUTFS_BLOCK_MAGIC_SUPER, 0, SCOUTFS_SUPER_BLKNO,
|
||||
SCOUTFS_BLOCK_SM_SHIFT, (void **)&meta_super);
|
||||
if (ret) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to read meta super block: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = meta_super_in_use(meta_fd, meta_super);
|
||||
if (ret < 0) {
|
||||
if (ret == -EBUSY)
|
||||
fprintf(stderr, "The filesystem must be fully recovered and cleanly unmounted to determine if the data device is empty.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
in_use = (le64_to_cpu(meta_super->total_data_blocks) - SCOUTFS_DATA_DEV_START_BLKNO) -
|
||||
le64_to_cpu(meta_super->data_alloc.total_len);
|
||||
if (in_use) {
|
||||
fprintf(stderr, "Data block allocator metadata shows "SIZE_FMT" data blocks used by files. They must be removed, truncated, or released before a new empty data device can be used.\n",
|
||||
SIZE_ARGS(in_use, SCOUTFS_BLOCK_SM_SIZE));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (args->data_device) {
|
||||
data_fd = open(args->data_device, O_DIRECT | O_EXCL |
|
||||
(args->check ? O_RDONLY : O_RDWR | O_SYNC));
|
||||
if (data_fd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open data device '%s': %s (%d)\n",
|
||||
args->data_device, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = get_device_size(args->data_device, data_fd, &data_size);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
data_blocks = data_size >> SCOUTFS_BLOCK_SM_SHIFT;
|
||||
|
||||
if (data_blocks < le64_to_cpu(meta_super->total_data_blocks)) {
|
||||
fprintf(stderr, "new data device %s of size "BASE_SIZE_FMT" has %llu 4KiB blocks, it needs at least "SIZE_FMT" blocks.\n",
|
||||
args->data_device,
|
||||
BASE_SIZE_ARGS(data_size),
|
||||
data_blocks,
|
||||
SIZE_ARGS(le64_to_cpu(meta_super->total_data_blocks),
|
||||
SCOUTFS_BLOCK_SM_SIZE));
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (args->check) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* the data device superblock only needs fs identifying fields */
|
||||
memset(data_super, 0, sizeof(struct scoutfs_super_block));
|
||||
data_super->id = meta_super->id;
|
||||
data_super->fmt_vers = meta_super->fmt_vers;
|
||||
data_super->flags = meta_super->flags &~ cpu_to_le64(SCOUTFS_FLAG_IS_META_BDEV);
|
||||
memcpy(data_super->uuid, meta_super->uuid,sizeof(data_super->uuid));
|
||||
data_super->seq = meta_super->seq;
|
||||
data_super->total_meta_blocks = meta_super->total_meta_blocks;
|
||||
data_super->total_data_blocks = meta_super->total_data_blocks;
|
||||
|
||||
ret = write_block(data_fd, SCOUTFS_BLOCK_MAGIC_SUPER, meta_super->hdr.fsid, 1,
|
||||
SCOUTFS_SUPER_BLKNO, SCOUTFS_BLOCK_SM_SHIFT, &data_super->hdr);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "Error writing super block to new data device '%s': %s (%d)\n",
|
||||
args->data_device, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
uuid_unparse(meta_super->uuid, uuid_str);
|
||||
|
||||
printf("Successfully initialized empty data device for scoutfs filesystem:\n"
|
||||
" meta device path: %s\n"
|
||||
" data device path: %s\n"
|
||||
" fsid: %llx\n"
|
||||
" uuid: %s\n"
|
||||
" format version: %llu\n"
|
||||
" 64KB metadata blocks: "SIZE_FMT"\n"
|
||||
" 4KB data blocks: "SIZE_FMT"\n",
|
||||
args->meta_device,
|
||||
args->data_device,
|
||||
le64_to_cpu(meta_super->hdr.fsid),
|
||||
uuid_str,
|
||||
le64_to_cpu(meta_super->fmt_vers),
|
||||
SIZE_ARGS(le64_to_cpu(meta_super->total_meta_blocks),
|
||||
SCOUTFS_BLOCK_LG_SIZE),
|
||||
SIZE_ARGS(le64_to_cpu(meta_super->total_data_blocks),
|
||||
SCOUTFS_BLOCK_SM_SIZE));
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
if (args->check) {
|
||||
if (ret == 0)
|
||||
printf("All checks passed.\n");
|
||||
else
|
||||
printf("Errors were found that must be addressed before a new empty data device could be prepared and used.\n");
|
||||
}
|
||||
|
||||
if (meta_super)
|
||||
free(meta_super);
|
||||
if (data_super)
|
||||
free(data_super);
|
||||
if (meta_fd != -1)
|
||||
close(meta_fd);
|
||||
if (data_fd != -1)
|
||||
close(data_fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int parse_opt(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct prepare_empty_data_dev_args *args = state->input;
|
||||
|
||||
switch (key) {
|
||||
case 'c':
|
||||
args->check = true;
|
||||
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 device arguments given");
|
||||
break;
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->meta_device)
|
||||
argp_error(state, "no metadata device argument given");
|
||||
if (!args->data_device && !args->check)
|
||||
argp_error(state, "no data device argument given");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct argp_option options[] = {
|
||||
{ "check", 'c', NULL, 0, "Only check for errors and do not write", },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct argp argp = {
|
||||
options,
|
||||
parse_opt,
|
||||
"META-DEVICE DATA-DEVICE",
|
||||
"Prepare empty data device for use with an existing ScoutFS filesystem"
|
||||
};
|
||||
|
||||
static int prepare_empty_data_dev_cmd(int argc, char *argv[])
|
||||
{
|
||||
struct prepare_empty_data_dev_args prepare_empty_data_dev_args = {
|
||||
.check = false,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&argp, argc, argv, 0, NULL, &prepare_empty_data_dev_args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_prepare_empty_data_dev(&prepare_empty_data_dev_args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) prepare_empty_data_dev_ctor(void)
|
||||
{
|
||||
cmd_register_argp("prepare-empty-data-device", &argp, GROUP_CORE,
|
||||
prepare_empty_data_dev_cmd);
|
||||
}
|
||||
Reference in New Issue
Block a user