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:
Andy Grover
2020-11-30 16:07:41 -08:00
parent 10df01eb7a
commit 0f17ecb9e3

View File

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