summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2018-07-16 03:19:33 -0400
committerKent Overstreet <kent.overstreet@gmail.com>2018-12-12 08:16:48 -0500
commitfcd17b11f1a0287453f1a9161985990f24ae827c (patch)
tree1d773d5ed1958123aa867acc195c2250c01fc81c
parent211222e56a9d3914c34c6b866c3b9779d363836d (diff)
Simple utility for setting projects on files/directories
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r--Makefile.am7
-rw-r--r--setproject.c198
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;
+}