#include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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 #ifndef BCHFS_IOC_REINHERIT_ATTRS #define BCHFS_IOC_REINHERIT_ATTRS _IOR(0xbc, 64, const char *) #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 do_setproject(int fd, unsigned id) { 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")); } static void setproject_recurse(int dirfd, unsigned id, unsigned dev) { DIR *dir = fdopendir(dirfd); struct dirent *d; while ((errno = 0), (d = readdir(dir))) { if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; struct stat st; if (fstatat(dirfd, d->d_name, &st, AT_SYMLINK_NOFOLLOW)) { errstr(_("error statting %s: %m"), d->d_name); errno = 0; continue; } if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) continue; int fd = openat(dirfd, d->d_name, AT_SYMLINK_NOFOLLOW); if (fd < 0) { errstr(_("error opening %s: %m"), d->d_name); errno = 0; continue; } if (st.st_dev != dev || ioctl(dirfd, BCHFS_IOC_REINHERIT_ATTRS, d->d_name) < 0) do_setproject(fd, id); if (S_ISDIR(st.st_mode)) setproject_recurse(fd, id, st.st_dev); close(fd); } } static void setproject(const char *path, unsigned id) { int fd = open(path, O_RDONLY|O_NOATIME); if (fd < 0) { errstr(_("error opening %s: %m"), path); return; } struct stat st; if (fstat(fd, &st)) { errstr(_("error statting %s: %m"), path); return; } if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) return; do_setproject(fd, id); if (S_ISDIR(st.st_mode)) setproject_recurse(fd, id, st.st_dev); 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(argv[i], project_id); return 0; }