From 7c54c86c38801a05f3bf9baea3171eb84329b37c Mon Sep 17 00:00:00 2001 From: Andy Grover Date: Thu, 3 Dec 2020 13:57:26 -0800 Subject: [PATCH] Implement argp support for setattr Signed-off-by: Andy Grover --- utils/src/setattr.c | 164 ++++++++++++++++++++++++++------------------ 1 file changed, 99 insertions(+), 65 deletions(-) diff --git a/utils/src/setattr.c b/utils/src/setattr.c index e9ab3b34..23922531 100644 --- a/utils/src/setattr.c +++ b/utils/src/setattr.c @@ -7,8 +7,9 @@ #include #include #include -#include #include +#include +#include #include "sparse.h" #include "util.h" @@ -17,83 +18,41 @@ #include "parse.h" #include "cmd.h" -static struct option long_ops[] = { - { "ctime", 1, NULL, 'c' }, - { "data_version", 1, NULL, 'd' }, - { "file", 1, NULL, 'f' }, - { "offline", 0, NULL, 'o' }, - { "i_size", 1, NULL, 's' }, - { NULL, 0, NULL, 0} +struct setattr_args { + char *filename; + struct timespec ctime; + u64 data_version; + u64 i_size; + bool offline; + char __pad[7]; }; -static int setattr_more_cmd(int argc, char **argv) +static int do_setattr(struct setattr_args *args) { - struct scoutfs_ioctl_setattr_more sm; - struct timespec ctime; - char *path = NULL; - int ret; + struct scoutfs_ioctl_setattr_more sm = {0}; int fd = -1; - int c; + int ret; - memset(&sm, 0, sizeof(sm)); - - while ((c = getopt_long(argc, argv, "c:d:f:os:", long_ops, NULL)) != -1) { - switch (c) { - case 'c': - ret = parse_timespec(optarg, &ctime); - if (ret) - goto out; - break; - case 'd': - ret = parse_u64(optarg, &sm.data_version); - if (ret) - goto out; - break; - case 'f': - path = strdup(optarg); - if (!path) { - fprintf(stderr, "path mem alloc failed\n"); - ret = -ENOMEM; - goto out; - } - break; - case 'o': - sm.flags |= SCOUTFS_IOC_SETATTR_MORE_OFFLINE; - break; - case 's': - ret = parse_u64(optarg, &sm.i_size); - if (ret) - goto out; - break; - case '?': - default: - ret = -EINVAL; - goto out; - } - } - - if (path == NULL) { - fprintf(stderr, "must specify -f path to file\n"); - ret = -EINVAL; - goto out; - } - - fd = open(path, O_WRONLY); + fd = open(args->filename, O_WRONLY); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", - path, strerror(errno), errno); + args->filename, strerror(errno), errno); goto out; } - sm.ctime_sec = ctime.tv_sec; - sm.ctime_nsec = ctime.tv_nsec; + sm.ctime_sec = args->ctime.tv_sec; + sm.ctime_nsec = args->ctime.tv_nsec; + sm.data_version = args->data_version; + if (args->offline) + sm.flags |= SCOUTFS_IOC_SETATTR_MORE_OFFLINE; + sm.i_size = args->i_size; ret = ioctl(fd, SCOUTFS_IOC_SETATTR_MORE, &sm); if (ret < 0) { ret = -errno; fprintf(stderr, "setattr_more ioctl failed on '%s': " - "%s (%d)\n", path, strerror(errno), errno); + "%s (%d)\n", args->filename, strerror(errno), errno); goto out; } @@ -104,9 +63,84 @@ out: return ret; } +static int parse_opt(int key, char *arg, struct argp_state *state) +{ + struct setattr_args *args = state->input; + int ret; + + switch (key) { + case 't': /* timespec */ + ret = parse_timespec(arg, &args->ctime); + if (ret) + return ret; + break; + case 'V': /* data version */ + ret = parse_u64(arg, &args->data_version); + if (ret) + return ret; + if (args->data_version == 0) + argp_error(state, "data version must not be 0"); + break; + case 's': /* size */ + ret = parse_human(arg, &args->i_size); + if (ret) + return ret; + break; + case 'o': /* offline */ + args->offline = true; + break; + case ARGP_KEY_ARG: + if (!args->filename) + args->filename = strdup_or_error(state, arg); + else + argp_error(state, "more than one argument given"); + break; + case ARGP_KEY_FINI: + if (!args->filename) + argp_error(state, "no filename given"); + if (args->i_size && !args->data_version) { + argp_error(state, "must provide data-version if using --size option"); + } + if (!args->i_size && args->offline) { + argp_error(state, "must provide size if using --offline option"); + } + break; + default: + break; + } + + return 0; +} + +static struct argp_option options[] = { + { "ctime", 't', "TIMESPEC", 0, "Set creation time using \".\" format"}, + { "data-version", 'V', "VERSION", 0, "Set data version"}, + { "size", 's', "SIZE", 0, "Set file size (bytes or KMGTP units). Requires --data-version"}, + { "offline", 'o', NULL, 0, "Set file contents as offline, not sparse. Requires --size"}, + { NULL } +}; + +static int setattr_cmd(int argc, char **argv) +{ + struct argp argp = { + options, + parse_opt, + "FILE", + "Set attributes on newly-created zero-length file" + }; + struct setattr_args setattr_args = {NULL}; + int ret; + + ret = argp_parse(&argp, argc, argv, 0, NULL, &setattr_args); + if (ret) + return ret; + + return do_setattr(&setattr_args); +} + static void __attribute__((constructor)) setattr_more_ctor(void) { - cmd_register("setattr", "-c ctime -d data_version -o -s i_size -f ", - "set attributes on file with no data", - setattr_more_cmd); + cmd_register("setattr", "", + "set attributes on newly-created zero-length file", + setattr_cmd); }