mirror of
https://github.com/versity/scoutfs.git
synced 2026-01-07 12:35:28 +00:00
Implement argp support for stage/release
Make offset and length optional. Allow size units (KMGTP) to be used for offset/length. release: Since off/len no longer given in 4k blocks, round offset and length to to 4KiB, down and up respectively. Emit a message if rounding occurs. Make version a required option. stage: change ordering to src (the archive file) then the dest (the staged file). Signed-off-by: Andy Grover <agrover@versity.com>
This commit is contained in:
@@ -8,82 +8,50 @@
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
#include <argp.h>
|
||||
|
||||
#include "sparse.h"
|
||||
#include "util.h"
|
||||
#include "format.h"
|
||||
#include "ioctl.h"
|
||||
#include "parse.h"
|
||||
#include "cmd.h"
|
||||
|
||||
static int stage_cmd(int argc, char **argv)
|
||||
struct stage_args {
|
||||
char *archive_path;
|
||||
char *path;
|
||||
u64 data_version;
|
||||
u64 offset;
|
||||
u64 length;
|
||||
};
|
||||
|
||||
static int do_stage(struct stage_args *args)
|
||||
{
|
||||
struct scoutfs_ioctl_stage args;
|
||||
struct scoutfs_ioctl_stage ioctl_args;
|
||||
unsigned int buf_len = 1024 * 1024;
|
||||
unsigned int bytes;
|
||||
char *endptr = NULL;
|
||||
char *buf = NULL;
|
||||
int afd = -1;
|
||||
int fd = -1;
|
||||
u64 offset;
|
||||
u64 count;
|
||||
u64 vers;
|
||||
int ret;
|
||||
|
||||
if (argc != 6) {
|
||||
fprintf(stderr, "must specify moar args\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
||||
argv[1], strerror(errno), errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vers = strtoull(argv[2], &endptr, 0);
|
||||
if (*endptr != '\0' ||
|
||||
((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) {
|
||||
fprintf(stderr, "error parsing data version '%s'\n",
|
||||
argv[2]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
offset = strtoull(argv[3], &endptr, 0);
|
||||
if (*endptr != '\0' ||
|
||||
((offset == LLONG_MIN || offset == LLONG_MAX) && errno == ERANGE)) {
|
||||
fprintf(stderr, "error parsing offset '%s'\n",
|
||||
argv[3]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
count = strtoull(argv[4], &endptr, 0);
|
||||
if (*endptr != '\0' ||
|
||||
((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) {
|
||||
fprintf(stderr, "error parsing count '%s'\n",
|
||||
argv[4]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (count > INT_MAX) {
|
||||
fprintf(stderr, "count %llu too large, limited to %d\n",
|
||||
count, INT_MAX);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
afd = open(argv[5], O_RDONLY);
|
||||
afd = open(args->archive_path, O_RDONLY);
|
||||
if (afd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
||||
argv[5], strerror(errno), errno);
|
||||
args->archive_path, strerror(errno), errno);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = open(args->path, O_RDWR);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
||||
args->path, strerror(errno), errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf = malloc(buf_len);
|
||||
if (!buf) {
|
||||
fprintf(stderr, "couldn't allocate %u byte buffer\n", buf_len);
|
||||
@@ -91,9 +59,9 @@ static int stage_cmd(int argc, char **argv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (count) {
|
||||
while (args->length) {
|
||||
|
||||
bytes = min(count, buf_len);
|
||||
bytes = min(args->length, buf_len);
|
||||
|
||||
ret = read(afd, buf, bytes);
|
||||
if (ret <= 0) {
|
||||
@@ -105,15 +73,15 @@ static int stage_cmd(int argc, char **argv)
|
||||
|
||||
bytes = ret;
|
||||
|
||||
args.data_version = vers;
|
||||
args.buf_ptr = (unsigned long)buf;
|
||||
args.offset = offset;
|
||||
args.count = bytes;
|
||||
ioctl_args.data_version = args->data_version;
|
||||
ioctl_args.buf_ptr = (unsigned long)buf;
|
||||
ioctl_args.offset = args->offset;
|
||||
ioctl_args.count = bytes;
|
||||
|
||||
count -= bytes;
|
||||
offset += bytes;
|
||||
args->length -= bytes;
|
||||
args->offset += bytes;
|
||||
|
||||
ret = ioctl(fd, SCOUTFS_IOC_STAGE, &args);
|
||||
ret = ioctl(fd, SCOUTFS_IOC_STAGE, &ioctl_args);
|
||||
if (ret != bytes) {
|
||||
fprintf(stderr, "stage returned %d, not %u: error %s (%d)\n",
|
||||
ret, bytes, strerror(errno), errno);
|
||||
@@ -132,79 +100,203 @@ out:
|
||||
return ret;
|
||||
};
|
||||
|
||||
static void __attribute__((constructor)) stage_ctor(void)
|
||||
static int parse_stage_opts(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
cmd_register("stage", "<file> <vers> <offset> <count> <archive file>",
|
||||
"write archive file contents to offline region", stage_cmd);
|
||||
struct stage_args *args = state->input;
|
||||
int ret;
|
||||
|
||||
switch (key) {
|
||||
case 'V':
|
||||
ret = parse_u64(arg, &args->data_version);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'o': /* offset */
|
||||
ret = parse_human(arg, &args->offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'l': /* length */
|
||||
ret = parse_human(arg, &args->length);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (!args->archive_path)
|
||||
args->archive_path = strdup_or_error(state, arg);
|
||||
else if (!args->path)
|
||||
args->path = strdup_or_error(state, arg);
|
||||
else
|
||||
argp_error(state, "more than two arguments given");
|
||||
break;
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->archive_path) {
|
||||
argp_error(state, "must provide archive file path");
|
||||
}
|
||||
if (!args->path) {
|
||||
argp_error(state, "must provide to-stage file path");
|
||||
}
|
||||
if (!args->data_version) {
|
||||
argp_error(state, "must provide file version with --data-version");
|
||||
}
|
||||
if (!args->length) {
|
||||
struct stat statbuf = {0};
|
||||
|
||||
ret = stat(args->archive_path, &statbuf);
|
||||
if (ret < 0)
|
||||
argp_failure(state, 1, -errno, "Could not get file size");
|
||||
|
||||
args->length = statbuf.st_size;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_cmd(int argc, char **argv)
|
||||
static struct argp_option options[] = {
|
||||
{ "data-version", 'V', "VERSION", 0, "Data version of the file [Required]"},
|
||||
{ "offset", 'o', "OFFSET", 0, "Offset (bytes or KMGTP units) in file to stage (default: 0)"},
|
||||
{ "length", 'l', "LENGTH", 0, "Length of range (bytes or KMGTP units) of file to stage. (default: size of ARCHIVE-FILE)"},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static int stage_cmd(int argc, char **argv)
|
||||
{
|
||||
struct scoutfs_ioctl_release args;
|
||||
char *endptr = NULL;
|
||||
u64 block;
|
||||
u64 count;
|
||||
u64 vers;
|
||||
struct argp argp = {
|
||||
options,
|
||||
parse_stage_opts,
|
||||
"ARCHIVE-FILE STAGE-FILE --data-version VERSION",
|
||||
"Write archive file contents to an offline file"
|
||||
};
|
||||
struct stage_args stage_args = {NULL};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&argp, argc, argv, 0, NULL, &stage_args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_stage(&stage_args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) stage_ctor(void)
|
||||
{
|
||||
cmd_register("stage", "<archive file> <file> -V <version>",
|
||||
"write archive file contents to an offline file", stage_cmd);
|
||||
}
|
||||
|
||||
struct release_args {
|
||||
char *path;
|
||||
u64 data_version;
|
||||
u64 offset;
|
||||
u64 length;
|
||||
};
|
||||
|
||||
static int do_release(struct release_args *args)
|
||||
{
|
||||
struct scoutfs_ioctl_release ioctl_args = {0};
|
||||
int ret;
|
||||
int fd;
|
||||
|
||||
if (argc != 5) {
|
||||
fprintf(stderr, "must specify path, data version, offset, and count\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = open(argv[1], O_RDWR);
|
||||
fd = open(args->path, O_RDWR);
|
||||
if (fd < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "failed to open '%s': %s (%d)\n",
|
||||
argv[1], strerror(errno), errno);
|
||||
args->path, strerror(errno), errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
vers = strtoull(argv[2], &endptr, 0);
|
||||
if (*endptr != '\0' ||
|
||||
((vers == LLONG_MIN || vers == LLONG_MAX) && errno == ERANGE)) {
|
||||
fprintf(stderr, "error parsing data version '%s'\n",
|
||||
argv[2]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
assert(args->offset % SCOUTFS_BLOCK_SM_SIZE == 0);
|
||||
assert(args->length % SCOUTFS_BLOCK_SM_SIZE == 0);
|
||||
|
||||
block = strtoull(argv[3], &endptr, 0);
|
||||
if (*endptr != '\0' ||
|
||||
((block == LLONG_MIN || block == LLONG_MAX) && errno == ERANGE)) {
|
||||
fprintf(stderr, "error parsing starting 4K block offset '%s'\n",
|
||||
argv[3]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ioctl_args.block = args->offset / SCOUTFS_BLOCK_SM_SIZE;
|
||||
ioctl_args.count = args->length / SCOUTFS_BLOCK_SM_SIZE;
|
||||
ioctl_args.data_version = args->data_version;
|
||||
|
||||
count = strtoull(argv[4], &endptr, 0);
|
||||
if (*endptr != '\0' ||
|
||||
((count == LLONG_MIN || count == LLONG_MAX) && errno == ERANGE)) {
|
||||
fprintf(stderr, "error parsing length '%s'\n",
|
||||
argv[4]);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
args.block = block;
|
||||
args.count = count;
|
||||
args.data_version = vers;
|
||||
|
||||
ret = ioctl(fd, SCOUTFS_IOC_RELEASE, &args);
|
||||
ret = ioctl(fd, SCOUTFS_IOC_RELEASE, &ioctl_args);
|
||||
if (ret < 0) {
|
||||
ret = -errno;
|
||||
fprintf(stderr, "release ioctl failed: %s (%d)\n",
|
||||
strerror(errno), errno);
|
||||
}
|
||||
out:
|
||||
|
||||
close(fd);
|
||||
return ret;
|
||||
};
|
||||
|
||||
static int parse_release_opts(int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
struct release_args *args = state->input;
|
||||
int ret;
|
||||
|
||||
switch (key) {
|
||||
case 'V':
|
||||
ret = parse_u64(arg, &args->data_version);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'o': /* offset */
|
||||
ret = parse_human(arg, &args->offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case 'l': /* length */
|
||||
ret = parse_human(arg, &args->length);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (args->path)
|
||||
argp_error(state, "more than one argument given");
|
||||
args->path = strdup_or_error(state, arg);
|
||||
break;
|
||||
case ARGP_KEY_FINI:
|
||||
if (!args->path) {
|
||||
argp_error(state, "must provide file path");
|
||||
}
|
||||
if (!args->data_version) {
|
||||
argp_error(state, "must provide file version --data-version");
|
||||
}
|
||||
if (!args->length) {
|
||||
int ret;
|
||||
struct stat statbuf = {0};
|
||||
|
||||
ret = stat(args->path, &statbuf);
|
||||
if (ret < 0)
|
||||
argp_failure(state, 1, -errno, "Could not get file size");
|
||||
|
||||
args->length = round_up(statbuf.st_size, SCOUTFS_BLOCK_SM_SIZE);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int release_cmd(int argc, char **argv)
|
||||
{
|
||||
struct argp argp = {
|
||||
options,
|
||||
parse_release_opts,
|
||||
"FILE --data-version VERSION",
|
||||
"Mark file region offline and free extents"
|
||||
};
|
||||
struct release_args release_args = {NULL};
|
||||
int ret;
|
||||
|
||||
ret = argp_parse(&argp, argc, argv, 0, NULL, &release_args);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_release(&release_args);
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) release_ctor(void)
|
||||
{
|
||||
cmd_register("release", "<path> <vers> <4K block offset> <block count>",
|
||||
cmd_register("release", "<path> <vers>",
|
||||
"mark file region offline and free extents", release_cmd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user