mirror of
https://github.com/versity/scoutfs.git
synced 2026-02-10 12:40:09 +00:00
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>
162 lines
3.6 KiB
C
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);
|
|
}
|