diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2018-07-16 03:19:33 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2018-12-12 08:16:48 -0500 |
commit | fcd17b11f1a0287453f1a9161985990f24ae827c (patch) | |
tree | 1d773d5ed1958123aa867acc195c2250c01fc81c | |
parent | 211222e56a9d3914c34c6b866c3b9779d363836d (diff) |
Simple utility for setting projects on files/directories
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | setproject.c | 198 |
2 files changed, 204 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index a7262ea..d6afbb5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -121,7 +121,8 @@ sbin_PROGRAMS = \ xqmstats \ edquota \ setquota \ - convertquota + convertquota \ + setproject if WITH_RPC sbin_PROGRAMS += \ rpc.rquotad @@ -232,6 +233,10 @@ convertquota_LDADD = \ $(RPCLIBS) \ $(TIRPC_LIBS) +setproject_SOURCES = setproject.c +setproject_LDADD = \ + libquota.a + if WITH_RPC rpc_rquotad_SOURCES = \ rquota_server.c \ diff --git a/setproject.c b/setproject.c new file mode 100644 index 0000000..b1c0c2a --- /dev/null +++ b/setproject.c @@ -0,0 +1,198 @@ + +#include "config.h" + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#include <linux/fs.h> + +#ifndef FS_IOC_FSGETXATTR +#define FS_IOC_FSGETXATTR _IOR ('X', 31, struct fsxattr) +#define FS_IOC_FSSETXATTR _IOW ('X', 32, struct fsxattr) + +struct fsxattr { + __u32 fsx_xflags; /* xflags field value (get/set) */ + __u32 fsx_extsize; /* extsize field value (get/set)*/ + __u32 fsx_nextents; /* nextents field value (get) */ + __u32 fsx_projid; /* project identifier (get/set) */ + __u32 fsx_cowextsize; /* CoW extsize field value (get/set)*/ + unsigned char fsx_pad[8]; +}; +#endif + +#include "pot.h" +#include "common.h" + +#define min(x,y) (((x) < (y)) ? (x) : (y)) +#define max(x,y) (((x) > (y)) ? (x) : (y)) + +char *progname; + +static bool __project_name_to_id(FILE *f, const char *name, unsigned *id) +{ + char *line = NULL; + size_t n = 0; + + *id = 0; + + while (getline(&line, &n, f) >= 0) { + char *p = line; + char *projname_str = strsep(&p, ":"); + char *projid_str = strsep(&p, "\n"); + + if (!projid_str || !projname_str) + continue; + + errno = 0; + unsigned projid = strtoul(projid_str, NULL, 0); + + if (errno) + continue; + + *id = max(*id, projid); + + if (!strcmp(projname_str, name)) { + free(line); + *id = projid; + return true; + } + } + + free(line); + return false; +} + +static unsigned project_name_to_id(const char *name, bool create) +{ + FILE *f = fopen("/etc/projid", "a+"); + unsigned id; + + if (!f) + die(1, _("error opening projid file\n")); + + if (__project_name_to_id(f, name, &id)) + goto success; + + if (!create) + die(1, _("project %s does not exist\n"), name); + + fprintf(f, "%s:%u\n", name, ++id); + fclose(f); +success: + return id; +} + +static void setproject_recurse(const char *path, unsigned id) +{ + struct stat st; + if (stat(path, &st)) { + errstr(_("error statting %s: %m"), path); + return; + } + + if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) + return; + + int fd = open(path, O_RDONLY|O_NOATIME); + if (fd < 0) { + errstr(_("error opening %s: %m"), path); + return; + } + + struct fsxattr fa; + + if (ioctl(fd, FS_IOC_FSGETXATTR, &fa)) + die(1, _("FS_IOC_FSGETXATTR error\n")); + + fa.fsx_projid = id; + + if (ioctl(fd, FS_IOC_FSSETXATTR, &fa)) + die(1, _("FS_IOC_FSSETXATTR error\n")); + + if (S_ISDIR(st.st_mode)) { + DIR *dir = fdopendir(fd); + struct dirent *d; + + while ((errno = 0), (d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || + !strcmp(d->d_name, "..")) + continue; + + char *child = malloc(strlen(path) + strlen(d->d_name) + 2); + if (!child) + die(1, _("malloc error\n")); + + sprintf(child, "%s/%s", path, d->d_name); + setproject_recurse(child, id); + free(child); + } + } + + close(fd); +} + +static void usage(void) +{ + errstr( "%s%s", + _("Usage: setproject [ -c ] -P project files...\n"), + _("\n\ +-P, --project project name\n\ +-c, --create create project if it does not exist\n\ +-h, --help display this help message and exit\n\ +-V, --version display version information and exit\n\n")); + fprintf(stderr, _("Bugs to: %s\n"), PACKAGE_BUGREPORT); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct option long_opts[] = { + { "project", 1, NULL, 'P' }, + { "create", 0, NULL, 'c' }, + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; + const char *project_name = NULL; + bool create = false; + int ret; + + gettexton(); + progname = basename(argv[0]); + + while ((ret = getopt_long(argc, argv, "P:c", long_opts, NULL)) != -1) { + switch (ret) { + case 'P': + project_name = optarg; + break; + case 'c': + create = true; + break; + case 'h': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (!project_name) + die(1, _("No project specified.\n")); + + unsigned i, project_id = project_name_to_id(project_name, create); + + for (i = 0; i < argc; i++) + setproject_recurse(argv[i], project_id); + + return 0; +} |