diff --git a/utils/src/stage_release.c b/utils/src/stage_release.c index 2e1ec342..8683afd1 100644 --- a/utils/src/stage_release.c +++ b/utils/src/stage_release.c @@ -8,82 +8,50 @@ #include #include #include +#include +#include #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", " ", - "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", " -V ", + "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", " <4K block offset> ", + cmd_register("release", " ", "mark file region offline and free extents", release_cmd); }