Files
scoutfs/utils/src/move_blocks.c
Andy Grover 0deb232d3f Support O_TMPFILE and allow MOVE_BLOCKS into released extents
Support O_TMPFILE: Create an unlinked file and put it on the orphan list.
If it ever gains a link, take it off the orphan list.

Change MOVE_BLOCKS ioctl to allow moving blocks into offline extent ranges.
Ioctl callers must set a new flag to enable this operation mode.

RH-compat: tmpfile support it actually backported by RH into 3.10 kernel.
We need to use some of their kabi-maintaining wrappers to use it:
use a struct inode_operations_wrapper instead of base struct
inode_operations, set S_IOPS_WRAPPER flag in i_flags. This lets
RH's modified vfs_tmpfile() find our tmpfile fn pointer.

Add a test that tests both creating tmpfiles as well as moving their
contents into a destination file via MOVE_BLOCKS.

xfstests common/004 now runs because tmpfile is supported.

Signed-off-by: Andy Grover <agrover@versity.com>
2021-04-05 14:23:44 -07:00

162 lines
3.6 KiB
C

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <assert.h>
#include <argp.h>
#include "sparse.h"
#include "util.h"
#include "format.h"
#include "ioctl.h"
#include "cmd.h"
#include "parse.h"
struct move_blocks_args {
char *from_path;
u64 from_offset;
u64 length;
char *to_path;
u64 to_offset;
unsigned from_off_set:1,
len_set:1,
to_off_set:1;
};
static int do_move_blocks(struct move_blocks_args *args)
{
struct scoutfs_ioctl_move_blocks mb = {0};
int from_fd = -1;
int to_fd = -1;
int ret;
from_fd = open(args->from_path, O_RDWR);
if (from_fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
args->from_path, strerror(errno), errno);
goto out;
}
to_fd = open(args->to_path, O_RDWR);
if (to_fd < 0) {
ret = -errno;
fprintf(stderr, "failed to open '%s': %s (%d)\n",
args->to_path, strerror(errno), errno);
goto out;
}
mb.from_fd = from_fd;
mb.from_off = args->from_offset;
mb.len = args->length;
mb.to_off = args->to_offset;
ret = ioctl(to_fd, SCOUTFS_IOC_MOVE_BLOCKS, &mb);
if (ret < 0) {
ret = -errno;
fprintf(stderr, "ioctl failed on '%s': %s (%d)\n",
args->to_path, strerror(errno), errno);
}
out:
if (from_fd >= 0)
close(from_fd);
if (to_fd >= 0)
close(to_fd);
return ret;
}
static int parse_move_blocks_opts(int key, char *arg, struct argp_state *state)
{
struct move_blocks_args *args = state->input;
int ret;
switch (key) {
case 'f':
ret = parse_u64(arg, &args->from_offset);
if (ret)
return ret;
args->from_off_set = 1;
break;
case 'l':
ret = parse_human(arg, &args->length);
if (ret)
return ret;
args->len_set = 1;
break;
case 't':
ret = parse_human(arg, &args->to_offset);
if (ret)
return ret;
args->to_off_set = 1;
break;
case ARGP_KEY_ARG:
if (args->to_path)
argp_error(state, "more than two file path arguments given");
if (args->from_path)
args->to_path = strdup_or_error(state, arg);
else
args->from_path = strdup_or_error(state, arg);
break;
case ARGP_KEY_FINI:
if (!args->from_path)
argp_error(state, "must provide from file path");
if (!args->to_path)
argp_error(state, "must provide to file path");
if (!args->from_off_set)
argp_error(state, "must provide from file offset --from-offset");
if (!args->len_set)
argp_error(state, "must provide region length --length");
if (!args->to_off_set)
argp_error(state, "must provide to file offset --to-offset");
break;
default:
break;
}
return 0;
}
static struct argp_option move_blocks_options[] = {
{ "from-offset", 'f', "OFFSET", 0,
"Byte offset in from file of region to move [Required]"},
{ "length", 'l', "LENGTH", 0,
"Length in bytes of region to move between files [Required]"},
{ "to-offset", 't', "OFFSET", 0,
"Byte offset in to file where region will be moved to [Required]"},
{ NULL }
};
static struct argp move_blocks_argp = {
move_blocks_options,
parse_move_blocks_opts,
"FROM_FILE --from-offset OFFSET --length LENGTH TO_FILE --to-offset OFFSET",
"Move a fixed-size region of extents from one regular file to another",
};
static int move_blocks_cmd(int argc, char **argv)
{
struct move_blocks_args args = {NULL};
int ret;
ret = argp_parse(&move_blocks_argp, argc, argv, 0, NULL, &args);
if (ret)
return ret;
return do_move_blocks(&args);
}
static void __attribute__((constructor)) move_blocks_ctor(void)
{
cmd_register_argp("move-blocks", &move_blocks_argp, GROUP_AGENT,
move_blocks_cmd);
}