#include #include #include #include #include #include #include #include #include #include #include #include #include "sparse.h" #include "util.h" #include "format.h" #include "ioctl.h" #include "parse.h" #include "cmd.h" struct setattr_args { char *filename; struct timespec ctime; struct timespec crtime; u64 data_version; u64 i_size; bool offline; }; static int do_setattr(struct setattr_args *args) { struct scoutfs_ioctl_setattr_more sm = {0}; int fd = -1; int ret; fd = open(args->filename, O_WRONLY); if (fd < 0) { ret = -errno; fprintf(stderr, "failed to open '%s': %s (%d)\n", args->filename, strerror(errno), errno); goto out; } sm.ctime_sec = args->ctime.tv_sec; sm.ctime_nsec = args->ctime.tv_nsec; sm.crtime_sec = args->crtime.tv_sec; sm.crtime_nsec = args->crtime.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", args->filename, strerror(errno), errno); goto out; } ret = 0; out: if (fd >= 0) close(fd); 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 'r': /* timespec */ ret = parse_timespec(arg, &args->crtime); 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 change time using \".\" format"}, { "crtime", 'r', "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 struct argp argp = { options, parse_opt, "FILE", "Set attributes on newly-created zero-length file" }; static int setattr_cmd(int argc, char **argv) { 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_argp("setattr", &argp, GROUP_AGENT, setattr_cmd); }