diff options
| author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2019-08-21 11:21:18 +0000 |
|---|---|---|
| committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2019-08-21 11:21:18 +0000 |
| commit | e700298f01e2c414e66a1e494999ed372e4bacd9 (patch) | |
| tree | b1c4fb6dd99ddb059fe51f0bb5b32bf9fe2676bb /usr/src/cmd/zfs | |
| parent | f0a2865453f053f6ff0041c6326877f1149d89ec (diff) | |
| parent | a99cb9618990662acbd3bab1b4a5b05a6ca62556 (diff) | |
| download | illumos-joyent-e700298f01e2c414e66a1e494999ed372e4bacd9.tar.gz | |
[illumos-gate merge]
commit a99cb9618990662acbd3bab1b4a5b05a6ca62556
11531 tools/make should set NATIVE_CCFLAGS
commit 7e6ac63905653744ac6703d7bc31790c363cf5e7
11539 dboot: build errors with gcc9
commit 889229bb296dd6f2b73fa6bb3247c58a71282836
4705 fork(2): See "MT-Level of Libraries"
commit a3bfe1dcd8a83b2661def9170b5005a830e8780d
11497 ddi_device_acc_attr(9S) has the wrong version number
commit 634942f535e93dad348fa175c9bc116e7bf936ba
11582 3SCF isn't the object-caching memory allocation library
commit 356ba08c15b26adbde3440aa89d8b31cd39fc526
11533 tools/make: gcc9 build errors
11534 tools/make: remove sys_nerr
11535 tools/make: amd64 arch is not supported
11536 tools/make: getmem() should take size_t
commit 21c878fe36c0151617d2f009615f5d18fddb207e
11559 zfs: device removal should not block bootability
commit 6af23589e78469fc9f90db8558854d1a822aaa72
10623 ZFS should be more aggressive in updating vdev devid
commit 0f2f3e995cde8dabd9edf8bb05b957a50bc7cc20
11517 loader.efi: efipart should be more careful about constructing block device lists
commit a9370e9f996b7ce61bb1a9612a0625161a922320
9096 passwords (policy.conf) should default to sha512
commit f67950b21e185934ccabe311516f4dcbdb00ef79
11479 zfs project support
Diffstat (limited to 'usr/src/cmd/zfs')
| -rw-r--r-- | usr/src/cmd/zfs/Makefile | 8 | ||||
| -rw-r--r-- | usr/src/cmd/zfs/zfs_main.c | 421 | ||||
| -rw-r--r-- | usr/src/cmd/zfs/zfs_project.c | 304 | ||||
| -rw-r--r-- | usr/src/cmd/zfs/zfs_projectutil.h | 49 |
4 files changed, 757 insertions, 25 deletions
diff --git a/usr/src/cmd/zfs/Makefile b/usr/src/cmd/zfs/Makefile index a3d61c7ee4..a65371609c 100644 --- a/usr/src/cmd/zfs/Makefile +++ b/usr/src/cmd/zfs/Makefile @@ -24,12 +24,13 @@ # Copyright 2010 Nexenta Systems, Inc. All rights reserved. # Copyright (c) 2012, 2015 by Delphix. All rights reserved. # Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>. +# Copyright 2019 Joyent, Inc. # PROG= zfs -OBJS= zfs_main.o zfs_iter.o +OBJS= zfs_main.o zfs_iter.o zfs_project.o SRCS= $(OBJS:%.o=%.c) -POFILES= zfs_main.po zfs_iter.po +POFILES= zfs_main.po zfs_iter.po zfs_project.po POFILE= zfs.po include ../Makefile.cmd @@ -41,8 +42,11 @@ ROOTETCFSTYPE= $(ROOTETC)/fs/$(FSTYPE) USRLIBFSTYPE= $(ROOTLIB)/fs/$(FSTYPE) LDLIBS += -lzfs_core -lzfs -luutil -lumem -lnvpair -lsec -lidmap +# cmdutils has list(9F) functions used by the project code. +LDLIBS += -lcmdutils INCS += -I../../common/zfs +INCS += -I$(SRC)/uts/common/fs/zfs CSTD= $(CSTD_GNU99) C99LMODE= -Xc99=%all diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c index 9ca6c03be7..faf6b7c473 100644 --- a/usr/src/cmd/zfs/zfs_main.c +++ b/usr/src/cmd/zfs/zfs_main.c @@ -61,6 +61,7 @@ #include <sys/fs/zfs.h> #include <sys/types.h> #include <time.h> +#include <sys/zfs_project.h> #include <synch.h> #include <libzfs.h> @@ -76,6 +77,7 @@ #include "zfs_iter.h" #include "zfs_util.h" #include "zfs_comutil.h" +#include "zfs_projectutil.h" libzfs_handle_t *g_zfs; @@ -114,6 +116,7 @@ static int zfs_do_channel_program(int argc, char **argv); static int zfs_do_load_key(int argc, char **argv); static int zfs_do_unload_key(int argc, char **argv); static int zfs_do_change_key(int argc, char **argv); +static int zfs_do_project(int argc, char **argv); /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. @@ -156,6 +159,8 @@ typedef enum { HELP_UNALLOW, HELP_USERSPACE, HELP_GROUPSPACE, + HELP_PROJECTSPACE, + HELP_PROJECT, HELP_HOLD, HELP_HOLDS, HELP_RELEASE, @@ -201,8 +206,12 @@ static zfs_command_t command_table[] = { { "get", zfs_do_get, HELP_GET }, { "inherit", zfs_do_inherit, HELP_INHERIT }, { "upgrade", zfs_do_upgrade, HELP_UPGRADE }, + { NULL }, { "userspace", zfs_do_userspace, HELP_USERSPACE }, { "groupspace", zfs_do_userspace, HELP_GROUPSPACE }, + { "projectspace", zfs_do_userspace, HELP_PROJECTSPACE }, + { NULL }, + { "project", zfs_do_project, HELP_PROJECT }, { NULL }, { "mount", zfs_do_mount, HELP_MOUNT }, { "unmount", zfs_do_unmount, HELP_UNMOUNT }, @@ -333,6 +342,15 @@ get_usage(zfs_help_t idx) "[-s field] ...\n" "\t [-S field] ... [-t type[,...]] " "<filesystem|snapshot>\n")); + case HELP_PROJECTSPACE: + return (gettext("\tprojectspace [-Hp] [-o field[,...]] " + "[-s field] ... \n" + "\t [-S field] ... <filesystem|snapshot>\n")); + case HELP_PROJECT: + return (gettext("\tproject [-d|-r] <directory|file ...>\n" + "\tproject -c [-0] [-d|-r] [-p id] <directory|file ...>\n" + "\tproject -C [-k] [-r] <directory ...>\n" + "\tproject [-p id] [-r] [-s] <directory ...>\n")); case HELP_HOLD: return (gettext("\thold [-r] <tag> <snapshot> ...\n")); case HELP_HOLDS: @@ -496,10 +514,26 @@ usage(boolean_t requested) (void) fprintf(fp, " NO NO <size>\n"); (void) fprintf(fp, "\t%-15s ", "groupused@..."); (void) fprintf(fp, " NO NO <size>\n"); + (void) fprintf(fp, "\t%-15s ", "projectused@..."); + (void) fprintf(fp, " NO NO <size>\n"); + (void) fprintf(fp, "\t%-15s ", "userobjused@..."); + (void) fprintf(fp, " NO NO <size>\n"); + (void) fprintf(fp, "\t%-15s ", "groupobjused@..."); + (void) fprintf(fp, " NO NO <size>\n"); + (void) fprintf(fp, "\t%-15s ", "projectobjused@..."); + (void) fprintf(fp, " NO NO <size>\n"); (void) fprintf(fp, "\t%-15s ", "userquota@..."); (void) fprintf(fp, "YES NO <size> | none\n"); (void) fprintf(fp, "\t%-15s ", "groupquota@..."); (void) fprintf(fp, "YES NO <size> | none\n"); + (void) fprintf(fp, "\t%-15s ", "projectquota@..."); + (void) fprintf(fp, "YES NO <size> | none\n"); + (void) fprintf(fp, "\t%-15s ", "userobjquota@..."); + (void) fprintf(fp, "YES NO <size> | none\n"); + (void) fprintf(fp, "\t%-15s ", "groupobjquota@..."); + (void) fprintf(fp, "YES NO <size> | none\n"); + (void) fprintf(fp, "\t%-15s ", "projectobjquota@..."); + (void) fprintf(fp, "YES NO <size> | none\n"); (void) fprintf(fp, "\t%-15s ", "written@<snap>"); (void) fprintf(fp, " NO NO <size>\n"); @@ -507,9 +541,9 @@ usage(boolean_t requested) "with standard units such as K, M, G, etc.\n")); (void) fprintf(fp, gettext("\nUser-defined properties can " "be specified by using a name containing a colon (:).\n")); - (void) fprintf(fp, gettext("\nThe {user|group}{used|quota}@ " - "properties must be appended with\n" - "a user or group specifier of one of these forms:\n" + (void) fprintf(fp, gettext("\nThe {user|group|project}" + "[obj]{used|quota}@ properties must be appended with\n" + "a user|group|project specifier of one of these forms:\n" " POSIX name (eg: \"matt\")\n" " POSIX id (eg: \"126829\")\n" " SMB name@domain (eg: \"matt@sun\")\n" @@ -2404,6 +2438,8 @@ zfs_do_upgrade(int argc, char **argv) * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot * zfs groupspace [-Hinp] [-o field[,...]] [-s field [-s field]...] * [-S field [-S field]...] [-t type[,...]] filesystem | snapshot + * zfs projectspace [-Hp] [-o field[,...]] [-s field [-s field]...] + * [-S field [-S field]...] filesystem | snapshot * * -H Scripted mode; elide headers and separate columns by tabs. * -i Translate SID to POSIX ID. @@ -2423,18 +2459,24 @@ enum us_field_types { USFIELD_TYPE, USFIELD_NAME, USFIELD_USED, - USFIELD_QUOTA + USFIELD_QUOTA, + USFIELD_OBJUSED, + USFIELD_OBJQUOTA }; -static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA" }; -static char *us_field_names[] = { "type", "name", "used", "quota" }; +static char *us_field_hdr[] = { "TYPE", "NAME", "USED", "QUOTA", + "OBJUSED", "OBJQUOTA" }; +static char *us_field_names[] = { "type", "name", "used", "quota", + "objused", "objquota" }; #define USFIELD_LAST (sizeof (us_field_names) / sizeof (char *)) #define USTYPE_PSX_GRP (1 << 0) #define USTYPE_PSX_USR (1 << 1) #define USTYPE_SMB_GRP (1 << 2) #define USTYPE_SMB_USR (1 << 3) +#define USTYPE_PROJ (1 << 4) #define USTYPE_ALL \ - (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR) + (USTYPE_PSX_GRP | USTYPE_PSX_USR | USTYPE_SMB_GRP | USTYPE_SMB_USR | \ + USTYPE_PROJ) static int us_type_bits[] = { USTYPE_PSX_GRP, @@ -2572,6 +2614,27 @@ us_compare(const void *larg, const void *rarg, void *unused) return (0); } +static boolean_t +zfs_prop_is_user(unsigned p) +{ + return (p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA || + p == ZFS_PROP_USEROBJUSED || p == ZFS_PROP_USEROBJQUOTA); +} + +static boolean_t +zfs_prop_is_group(unsigned p) +{ + return (p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA || + p == ZFS_PROP_GROUPOBJUSED || p == ZFS_PROP_GROUPOBJQUOTA); +} + +static boolean_t +zfs_prop_is_project(unsigned p) +{ + return (p == ZFS_PROP_PROJECTUSED || p == ZFS_PROP_PROJECTQUOTA || + p == ZFS_PROP_PROJECTOBJUSED || p == ZFS_PROP_PROJECTOBJQUOTA); +} + static inline const char * us_type2str(unsigned field_type) { @@ -2584,6 +2647,8 @@ us_type2str(unsigned field_type) return ("SMB User"); case USTYPE_SMB_GRP: return ("SMB Group"); + case USTYPE_PROJ: + return ("Project"); default: return ("Undefined"); } @@ -2656,7 +2721,7 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) if (cb->cb_sid2posix || domain == NULL || domain[0] == '\0') { /* POSIX or -i */ - if (prop == ZFS_PROP_GROUPUSED || prop == ZFS_PROP_GROUPQUOTA) { + if (zfs_prop_is_group(prop)) { type = USTYPE_PSX_GRP; if (!cb->cb_numname) { struct group *g; @@ -2664,7 +2729,7 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) if ((g = getgrgid(rid)) != NULL) name = g->gr_name; } - } else { + } else if (zfs_prop_is_user(prop)) { type = USTYPE_PSX_USR; if (!cb->cb_numname) { struct passwd *p; @@ -2672,6 +2737,8 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) if ((p = getpwuid(rid)) != NULL) name = p->pw_name; } + } else { + type = USTYPE_PROJ; } } @@ -2721,19 +2788,42 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space) } /* Calculate/update width of USED/QUOTA fields */ - if (cb->cb_nicenum) - zfs_nicenum(space, sizebuf, sizeof (sizebuf)); - else + if (cb->cb_nicenum) { + if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED || + prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA || + prop == ZFS_PROP_PROJECTUSED || + prop == ZFS_PROP_PROJECTQUOTA) { + zfs_nicenum(space, sizebuf, sizeof (sizebuf)); + } else { + zfs_nicenum(space, sizebuf, sizeof (sizebuf)); + } + } else { (void) snprintf(sizebuf, sizeof (sizebuf), "%llu", space); + } sizelen = strlen(sizebuf); - if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED) { + if (prop == ZFS_PROP_USERUSED || prop == ZFS_PROP_GROUPUSED || + prop == ZFS_PROP_PROJECTUSED) { propname = "used"; if (!nvlist_exists(props, "quota")) (void) nvlist_add_uint64(props, "quota", 0); - } else { + } else if (prop == ZFS_PROP_USERQUOTA || prop == ZFS_PROP_GROUPQUOTA || + prop == ZFS_PROP_PROJECTQUOTA) { propname = "quota"; if (!nvlist_exists(props, "used")) (void) nvlist_add_uint64(props, "used", 0); + } else if (prop == ZFS_PROP_USEROBJUSED || + prop == ZFS_PROP_GROUPOBJUSED || prop == ZFS_PROP_PROJECTOBJUSED) { + propname = "objused"; + if (!nvlist_exists(props, "objquota")) + (void) nvlist_add_uint64(props, "objquota", 0); + } else if (prop == ZFS_PROP_USEROBJQUOTA || + prop == ZFS_PROP_GROUPOBJQUOTA || + prop == ZFS_PROP_PROJECTOBJQUOTA) { + propname = "objquota"; + if (!nvlist_exists(props, "objused")) + (void) nvlist_add_uint64(props, "objused", 0); + } else { + return (-1); } sizeidx = us_field_index(propname); if (sizelen > cb->cb_width[sizeidx]) @@ -2766,7 +2856,7 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, data_type_t type; uint32_t val32; uint64_t val64; - char *strval = NULL; + char *strval = "-"; while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { if (strcmp(nvpair_name(nvp), @@ -2774,7 +2864,7 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, break; } - type = nvpair_type(nvp); + type = nvp == NULL ? DATA_TYPE_UNKNOWN : nvpair_type(nvp); switch (type) { case DATA_TYPE_UINT32: (void) nvpair_value_uint32(nvp, &val32); @@ -2785,13 +2875,16 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, case DATA_TYPE_STRING: (void) nvpair_value_string(nvp, &strval); break; + case DATA_TYPE_UNKNOWN: + break; default: (void) fprintf(stderr, "invalid data type\n"); } switch (field) { case USFIELD_TYPE: - strval = (char *)us_type2str(val32); + if (type == DATA_TYPE_UINT32) + strval = (char *)us_type2str(val32); break; case USFIELD_NAME: if (type == DATA_TYPE_UINT64) { @@ -2801,6 +2894,8 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, break; case USFIELD_USED: case USFIELD_QUOTA: + case USFIELD_OBJUSED: + case USFIELD_OBJQUOTA: if (type == DATA_TYPE_UINT64) { if (parsable) { (void) sprintf(valstr, "%llu", val64); @@ -2808,7 +2903,8 @@ print_us_node(boolean_t scripted, boolean_t parsable, int *fields, int types, zfs_nicenum(val64, valstr, sizeof (valstr)); } - if (field == USFIELD_QUOTA && + if ((field == USFIELD_QUOTA || + field == USFIELD_OBJQUOTA) && strcmp(valstr, "0") == 0) strval = "none"; else @@ -2880,7 +2976,7 @@ zfs_do_userspace(int argc, char **argv) uu_avl_t *avl_tree; uu_avl_walk_t *walk; char *delim; - char deffields[] = "type,name,used,quota"; + char deffields[] = "type,name,used,quota,objused,objquota"; char *ofield = NULL; char *tfield = NULL; int cfield = 0; @@ -2905,13 +3001,22 @@ zfs_do_userspace(int argc, char **argv) if (argc < 2) usage(B_FALSE); - if (strcmp(argv[0], "groupspace") == 0) + if (strcmp(argv[0], "groupspace") == 0) { /* Toggle default group types */ types = USTYPE_PSX_GRP | USTYPE_SMB_GRP; + } else if (strcmp(argv[0], "projectspace") == 0) { + types = USTYPE_PROJ; + prtnum = B_TRUE; + } while ((c = getopt(argc, argv, "nHpo:s:S:t:i")) != -1) { switch (c) { case 'n': + if (types == USTYPE_PROJ) { + (void) fprintf(stderr, + gettext("invalid option 'n'\n")); + usage(B_FALSE); + } prtnum = B_TRUE; break; case 'H': @@ -2933,9 +3038,19 @@ zfs_do_userspace(int argc, char **argv) } break; case 't': + if (types == USTYPE_PROJ) { + (void) fprintf(stderr, + gettext("invalid option 't'\n")); + usage(B_FALSE); + } tfield = optarg; break; case 'i': + if (types == USTYPE_PROJ) { + (void) fprintf(stderr, + gettext("invalid option 'i'\n")); + usage(B_FALSE); + } sid2posix = B_TRUE; break; case ':': @@ -3029,11 +3144,13 @@ zfs_do_userspace(int argc, char **argv) cb.cb_width[i] = strlen(gettext(us_field_hdr[i])); for (p = 0; p < ZFS_NUM_USERQUOTA_PROPS; p++) { - if (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA) && + if ((zfs_prop_is_user(p) && !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || - ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) && - !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP)))) + (zfs_prop_is_group(p) && + !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))) || + (zfs_prop_is_project(p) && types != USTYPE_PROJ)) continue; + cb.cb_prop = p; if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) return (ret); @@ -4355,6 +4472,11 @@ zfs_do_receive(int argc, char **argv) #define ZFS_DELEG_PERM_GROUPQUOTA "groupquota" #define ZFS_DELEG_PERM_USERUSED "userused" #define ZFS_DELEG_PERM_GROUPUSED "groupused" +#define ZFS_DELEG_PERM_USEROBJQUOTA "userobjquota" +#define ZFS_DELEG_PERM_GROUPOBJQUOTA "groupobjquota" +#define ZFS_DELEG_PERM_USEROBJUSED "userobjused" +#define ZFS_DELEG_PERM_GROUPOBJUSED "groupobjused" + #define ZFS_DELEG_PERM_HOLD "hold" #define ZFS_DELEG_PERM_RELEASE "release" #define ZFS_DELEG_PERM_DIFF "diff" @@ -4363,6 +4485,11 @@ zfs_do_receive(int argc, char **argv) #define ZFS_DELEG_PERM_LOAD_KEY "load-key" #define ZFS_DELEG_PERM_CHANGE_KEY "change-key" +#define ZFS_DELEG_PERM_PROJECTUSED "projectused" +#define ZFS_DELEG_PERM_PROJECTQUOTA "projectquota" +#define ZFS_DELEG_PERM_PROJECTOBJUSED "projectobjused" +#define ZFS_DELEG_PERM_PROJECTOBJQUOTA "projectobjquota" + #define ZFS_NUM_DELEG_NOTES ZFS_DELEG_NOTE_NONE static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { @@ -4391,6 +4518,14 @@ static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = { { ZFS_DELEG_PERM_USERPROP, ZFS_DELEG_NOTE_USERPROP }, { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_NOTE_USERQUOTA }, { ZFS_DELEG_PERM_USERUSED, ZFS_DELEG_NOTE_USERUSED }, + { ZFS_DELEG_PERM_USEROBJQUOTA, ZFS_DELEG_NOTE_USEROBJQUOTA }, + { ZFS_DELEG_PERM_USEROBJUSED, ZFS_DELEG_NOTE_USEROBJUSED }, + { ZFS_DELEG_PERM_GROUPOBJQUOTA, ZFS_DELEG_NOTE_GROUPOBJQUOTA }, + { ZFS_DELEG_PERM_GROUPOBJUSED, ZFS_DELEG_NOTE_GROUPOBJUSED }, + { ZFS_DELEG_PERM_PROJECTUSED, ZFS_DELEG_NOTE_PROJECTUSED }, + { ZFS_DELEG_PERM_PROJECTQUOTA, ZFS_DELEG_NOTE_PROJECTQUOTA }, + { ZFS_DELEG_PERM_PROJECTOBJUSED, ZFS_DELEG_NOTE_PROJECTOBJUSED }, + { ZFS_DELEG_PERM_PROJECTOBJQUOTA, ZFS_DELEG_NOTE_PROJECTOBJQUOTA }, { NULL, ZFS_DELEG_NOTE_NONE } }; @@ -4468,6 +4603,14 @@ deleg_perm_type(zfs_deleg_note_t note) case ZFS_DELEG_NOTE_USERPROP: case ZFS_DELEG_NOTE_USERQUOTA: case ZFS_DELEG_NOTE_USERUSED: + case ZFS_DELEG_NOTE_USEROBJQUOTA: + case ZFS_DELEG_NOTE_USEROBJUSED: + case ZFS_DELEG_NOTE_GROUPOBJQUOTA: + case ZFS_DELEG_NOTE_GROUPOBJUSED: + case ZFS_DELEG_NOTE_PROJECTUSED: + case ZFS_DELEG_NOTE_PROJECTQUOTA: + case ZFS_DELEG_NOTE_PROJECTOBJUSED: + case ZFS_DELEG_NOTE_PROJECTOBJQUOTA: /* other */ return (gettext("other")); default: @@ -4979,6 +5122,33 @@ deleg_perm_comment(zfs_deleg_note_t note) case ZFS_DELEG_NOTE_USERUSED: str = gettext("Allows reading any userused@... property"); break; + case ZFS_DELEG_NOTE_USEROBJQUOTA: + str = gettext("Allows accessing any userobjquota@... property"); + break; + case ZFS_DELEG_NOTE_GROUPOBJQUOTA: + str = gettext("Allows accessing any \n\t\t\t\t" + "groupobjquota@... property"); + break; + case ZFS_DELEG_NOTE_GROUPOBJUSED: + str = gettext("Allows reading any groupobjused@... property"); + break; + case ZFS_DELEG_NOTE_USEROBJUSED: + str = gettext("Allows reading any userobjused@... property"); + break; + case ZFS_DELEG_NOTE_PROJECTQUOTA: + str = gettext("Allows accessing any projectquota@... property"); + break; + case ZFS_DELEG_NOTE_PROJECTOBJQUOTA: + str = gettext("Allows accessing any \n\t\t\t\t" + "projectobjquota@... property"); + break; + case ZFS_DELEG_NOTE_PROJECTUSED: + str = gettext("Allows reading any projectused@... property"); + break; + case ZFS_DELEG_NOTE_PROJECTOBJUSED: + str = gettext("Allows accessing any \n\t\t\t\t" + "projectobjused@... property"); + break; /* other */ default: str = ""; @@ -7761,6 +7931,211 @@ zfs_do_change_key(int argc, char **argv) return (0); } +/* + * 1) zfs project [-d|-r] <file|directory ...> + * List project ID and inherit flag of file(s) or directories. + * -d: List the directory itself, not its children. + * -r: List subdirectories recursively. + * + * 2) zfs project -C [-k] [-r] <file|directory ...> + * Clear project inherit flag and/or ID on the file(s) or directories. + * -k: Keep the project ID unchanged. If not specified, the project ID + * will be reset as zero. + * -r: Clear on subdirectories recursively. + * + * 3) zfs project -c [-0] [-d|-r] [-p id] <file|directory ...> + * Check project ID and inherit flag on the file(s) or directories, + * report the outliers. + * -0: Print file name followed by a NUL instead of newline. + * -d: Check the directory itself, not its children. + * -p: Specify the referenced ID for comparing with the target file(s) + * or directories' project IDs. If not specified, the target (top) + * directory's project ID will be used as the referenced one. + * -r: Check subdirectories recursively. + * + * 4) zfs project [-p id] [-r] [-s] <file|directory ...> + * Set project ID and/or inherit flag on the file(s) or directories. + * -p: Set the project ID as the given id. + * -r: Set on subdirectory recursively. If not specify "-p" option, + * it will use top-level directory's project ID as the given id, + * then set both project ID and inherit flag on all descendants + * of the top-level directory. + * -s: Set project inherit flag. + */ +static int +zfs_do_project(int argc, char **argv) +{ + zfs_project_control_t zpc = { + .zpc_expected_projid = ZFS_INVALID_PROJID, + .zpc_op = ZFS_PROJECT_OP_DEFAULT, + .zpc_dironly = B_FALSE, + .zpc_keep_projid = B_FALSE, + .zpc_newline = B_TRUE, + .zpc_recursive = B_FALSE, + .zpc_set_flag = B_FALSE, + }; + int ret = 0, c; + + if (argc < 2) + usage(B_FALSE); + + while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) { + switch (c) { + case '0': + zpc.zpc_newline = B_FALSE; + break; + case 'C': + if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) { + (void) fprintf(stderr, gettext("cannot " + "specify '-C' '-c' '-s' together\n")); + usage(B_FALSE); + } + + zpc.zpc_op = ZFS_PROJECT_OP_CLEAR; + break; + case 'c': + if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) { + (void) fprintf(stderr, gettext("cannot " + "specify '-C' '-c' '-s' together\n")); + usage(B_FALSE); + } + + zpc.zpc_op = ZFS_PROJECT_OP_CHECK; + break; + case 'd': + zpc.zpc_dironly = B_TRUE; + /* overwrite "-r" option */ + zpc.zpc_recursive = B_FALSE; + break; + case 'k': + zpc.zpc_keep_projid = B_TRUE; + break; + case 'p': { + char *endptr; + + errno = 0; + zpc.zpc_expected_projid = strtoull(optarg, &endptr, 0); + if (errno != 0 || *endptr != '\0') { + (void) fprintf(stderr, + gettext("project ID must be less than " + "%u\n"), UINT32_MAX); + usage(B_FALSE); + } + if (zpc.zpc_expected_projid >= UINT32_MAX) { + (void) fprintf(stderr, + gettext("invalid project ID\n")); + usage(B_FALSE); + } + break; + } + case 'r': + zpc.zpc_recursive = B_TRUE; + /* overwrite "-d" option */ + zpc.zpc_dironly = B_FALSE; + break; + case 's': + if (zpc.zpc_op != ZFS_PROJECT_OP_DEFAULT) { + (void) fprintf(stderr, gettext("cannot " + "specify '-C' '-c' '-s' together\n")); + usage(B_FALSE); + } + + zpc.zpc_set_flag = B_TRUE; + zpc.zpc_op = ZFS_PROJECT_OP_SET; + break; + default: + (void) fprintf(stderr, gettext("invalid option '%c'\n"), + optopt); + usage(B_FALSE); + } + } + + if (zpc.zpc_op == ZFS_PROJECT_OP_DEFAULT) { + if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) + zpc.zpc_op = ZFS_PROJECT_OP_SET; + else + zpc.zpc_op = ZFS_PROJECT_OP_LIST; + } + + switch (zpc.zpc_op) { + case ZFS_PROJECT_OP_LIST: + if (zpc.zpc_keep_projid) { + (void) fprintf(stderr, + gettext("'-k' is only valid together with '-C'\n")); + usage(B_FALSE); + } + if (!zpc.zpc_newline) { + (void) fprintf(stderr, + gettext("'-0' is only valid together with '-c'\n")); + usage(B_FALSE); + } + break; + case ZFS_PROJECT_OP_CHECK: + if (zpc.zpc_keep_projid) { + (void) fprintf(stderr, + gettext("'-k' is only valid together with '-C'\n")); + usage(B_FALSE); + } + break; + case ZFS_PROJECT_OP_CLEAR: + if (zpc.zpc_dironly) { + (void) fprintf(stderr, + gettext("'-d' is useless together with '-C'\n")); + usage(B_FALSE); + } + if (!zpc.zpc_newline) { + (void) fprintf(stderr, + gettext("'-0' is only valid together with '-c'\n")); + usage(B_FALSE); + } + if (zpc.zpc_expected_projid != ZFS_INVALID_PROJID) { + (void) fprintf(stderr, + gettext("'-p' is useless together with '-C'\n")); + usage(B_FALSE); + } + break; + case ZFS_PROJECT_OP_SET: + if (zpc.zpc_dironly) { + (void) fprintf(stderr, + gettext("'-d' is useless for set project ID and/or " + "inherit flag\n")); + usage(B_FALSE); + } + if (zpc.zpc_keep_projid) { + (void) fprintf(stderr, + gettext("'-k' is only valid together with '-C'\n")); + usage(B_FALSE); + } + if (!zpc.zpc_newline) { + (void) fprintf(stderr, + gettext("'-0' is only valid together with '-c'\n")); + usage(B_FALSE); + } + break; + default: + ASSERT(0); + break; + } + + argv += optind; + argc -= optind; + if (argc == 0) { + (void) fprintf(stderr, + gettext("missing file or directory target(s)\n")); + usage(B_FALSE); + } + + for (int i = 0; i < argc; i++) { + int err; + + err = zfs_project_handle(argv[i], &zpc); + if (err && !ret) + ret = err; + } + + return (ret); +} + int main(int argc, char **argv) { diff --git a/usr/src/cmd/zfs/zfs_project.c b/usr/src/cmd/zfs/zfs_project.c new file mode 100644 index 0000000000..7b7652f786 --- /dev/null +++ b/usr/src/cmd/zfs/zfs_project.c @@ -0,0 +1,304 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2017, Intle Corporation. All rights reserved. + * Copyright 2019 Joyent, Inc. + */ + +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <strings.h> +#include <unistd.h> +#include <fcntl.h> +#include <dirent.h> +#include <stddef.h> +#include <libintl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/list.h> +#include <limits.h> +#include <sys/debug.h> +#include <sys/stat.h> +#include <sys/zfs_project.h> + +#include "zfs_util.h" +#include "zfs_projectutil.h" + +typedef struct zfs_project_item { + list_node_t zpi_list; + char zpi_name[0]; +} zfs_project_item_t; + +static void +zfs_project_item_alloc(list_t *head, const char *name) +{ + zfs_project_item_t *zpi; + + zpi = safe_malloc(sizeof (zfs_project_item_t) + strlen(name) + 1); + (void) strcpy(zpi->zpi_name, name); + list_insert_tail(head, zpi); +} + +static int +zfs_project_sanity_check(const char *name, zfs_project_control_t *zpc, + struct stat *st) +{ + int ret; + + ret = stat(name, st); + if (ret) { + (void) fprintf(stderr, gettext("failed to stat %s: %s\n"), + name, strerror(errno)); + return (ret); + } + + if (!S_ISREG(st->st_mode) && !S_ISDIR(st->st_mode)) { + (void) fprintf(stderr, gettext("only support project quota on " + "regular file or directory\n")); + return (-1); + } + + if (!S_ISDIR(st->st_mode)) { + if (zpc->zpc_dironly) { + (void) fprintf(stderr, gettext( + "'-d' option on non-dir target %s\n"), name); + return (-1); + } + + if (zpc->zpc_recursive) { + (void) fprintf(stderr, gettext( + "'-r' option on non-dir target %s\n"), name); + return (-1); + } + } + + return (0); +} + +static int +zfs_project_load_projid(const char *name, zfs_project_control_t *zpc) +{ + zfsxattr_t fsx; + int ret, fd; + + fd = open(name, O_RDONLY | O_NOCTTY); + if (fd < 0) { + (void) fprintf(stderr, gettext("failed to open %s: %s\n"), + name, strerror(errno)); + return (fd); + } + + ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx); + if (ret) + (void) fprintf(stderr, + gettext("failed to get xattr for %s: %s\n"), + name, strerror(errno)); + else + zpc->zpc_expected_projid = fsx.fsx_projid; + + (void) close(fd); + return (ret); +} + +static int +zfs_project_handle_one(const char *name, zfs_project_control_t *zpc) +{ + zfsxattr_t fsx; + int ret, fd; + + fd = open(name, O_RDONLY | O_NOCTTY); + if (fd < 0) { + if (errno == ENOENT && zpc->zpc_ignore_noent) + return (0); + + (void) fprintf(stderr, gettext("failed to open %s: %s\n"), + name, strerror(errno)); + return (fd); + } + + ret = ioctl(fd, ZFS_IOC_FSGETXATTR, &fsx); + if (ret) { + (void) fprintf(stderr, + gettext("failed to get xattr for %s: %s\n"), + name, strerror(errno)); + goto out; + } + + switch (zpc->zpc_op) { + case ZFS_PROJECT_OP_LIST: + (void) printf("%5u %c %s\n", fsx.fsx_projid, + (fsx.fsx_xflags & ZFS_PROJINHERIT_FL) ? 'P' : '-', name); + goto out; + case ZFS_PROJECT_OP_CHECK: + if (fsx.fsx_projid == zpc->zpc_expected_projid && + fsx.fsx_xflags & ZFS_PROJINHERIT_FL) + goto out; + + if (!zpc->zpc_newline) { + char c = '\0'; + + (void) printf("%s%c", name, c); + goto out; + } + + if (fsx.fsx_projid != zpc->zpc_expected_projid) + (void) printf("%s - project ID is not set properly " + "(%u/%u)\n", name, fsx.fsx_projid, + (uint32_t)zpc->zpc_expected_projid); + + if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) + (void) printf("%s - project inherit flag is not set\n", + name); + + goto out; + case ZFS_PROJECT_OP_CLEAR: + if (!(fsx.fsx_xflags & ZFS_PROJINHERIT_FL) && + (zpc->zpc_keep_projid || + fsx.fsx_projid == ZFS_DEFAULT_PROJID)) + goto out; + + fsx.fsx_xflags &= ~ZFS_PROJINHERIT_FL; + if (!zpc->zpc_keep_projid) + fsx.fsx_projid = ZFS_DEFAULT_PROJID; + break; + case ZFS_PROJECT_OP_SET: + if (fsx.fsx_projid == zpc->zpc_expected_projid && + (!zpc->zpc_set_flag || fsx.fsx_xflags & ZFS_PROJINHERIT_FL)) + goto out; + + fsx.fsx_projid = zpc->zpc_expected_projid; + if (zpc->zpc_set_flag) + fsx.fsx_xflags |= ZFS_PROJINHERIT_FL; + break; + default: + ASSERT(0); + break; + } + + ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx); + if (ret) + (void) fprintf(stderr, + gettext("failed to set xattr for %s: %s\n"), + name, strerror(errno)); + +out: + (void) close(fd); + return (ret); +} + +static int +zfs_project_handle_dir(const char *name, zfs_project_control_t *zpc, + list_t *head) +{ + char fullname[PATH_MAX]; + struct dirent *ent; + DIR *dir; + int ret = 0; + + dir = opendir(name); + if (dir == NULL) { + if (errno == ENOENT && zpc->zpc_ignore_noent) + return (0); + + ret = -errno; + (void) fprintf(stderr, gettext("failed to opendir %s: %s\n"), + name, strerror(errno)); + return (ret); + } + + /* Non-top item, ignore the case of being removed or renamed by race. */ + zpc->zpc_ignore_noent = B_TRUE; + errno = 0; + while (!ret && (ent = readdir(dir)) != NULL) { + /* skip "." and ".." */ + if (strcmp(ent->d_name, ".") == 0 || + strcmp(ent->d_name, "..") == 0) + continue; + + if (strlen(ent->d_name) + strlen(name) >= + sizeof (fullname) + 1) { + errno = ENAMETOOLONG; + break; + } + + (void) sprintf(fullname, "%s/%s", name, ent->d_name); + ret = zfs_project_handle_one(fullname, zpc); + if (!ret && zpc->zpc_recursive) { + struct stat64 sb; + + if (stat64(fullname, &sb) == 0 && + (sb.st_mode & S_IFMT) == S_IFDIR) + zfs_project_item_alloc(head, fullname); + } + } + + if (errno && !ret) { + ret = -errno; + (void) fprintf(stderr, gettext("failed to readdir %s: %s\n"), + name, strerror(errno)); + } + + (void) closedir(dir); + return (ret); +} + +int +zfs_project_handle(const char *name, zfs_project_control_t *zpc) +{ + zfs_project_item_t *zpi; + struct stat st; + list_t head; + int ret; + + ret = zfs_project_sanity_check(name, zpc, &st); + if (ret) + return (ret); + + if ((zpc->zpc_op == ZFS_PROJECT_OP_SET || + zpc->zpc_op == ZFS_PROJECT_OP_CHECK) && + zpc->zpc_expected_projid == ZFS_INVALID_PROJID) { + ret = zfs_project_load_projid(name, zpc); + if (ret) + return (ret); + } + + zpc->zpc_ignore_noent = B_FALSE; + ret = zfs_project_handle_one(name, zpc); + if (ret || !S_ISDIR(st.st_mode) || zpc->zpc_dironly || + (!zpc->zpc_recursive && + zpc->zpc_op != ZFS_PROJECT_OP_LIST && + zpc->zpc_op != ZFS_PROJECT_OP_CHECK)) + return (ret); + + list_create(&head, sizeof (zfs_project_item_t), + offsetof(zfs_project_item_t, zpi_list)); + zfs_project_item_alloc(&head, name); + while ((zpi = list_remove_head(&head)) != NULL) { + if (!ret) + ret = zfs_project_handle_dir(zpi->zpi_name, zpc, &head); + free(zpi); + } + + return (ret); +} diff --git a/usr/src/cmd/zfs/zfs_projectutil.h b/usr/src/cmd/zfs/zfs_projectutil.h new file mode 100644 index 0000000000..1792a3383a --- /dev/null +++ b/usr/src/cmd/zfs/zfs_projectutil.h @@ -0,0 +1,49 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2017, Intel Corporation. All rights reserved. + */ + +#ifndef _ZFS_PROJECTUTIL_H +#define _ZFS_PROJECTUTIL_H + +typedef enum { + ZFS_PROJECT_OP_DEFAULT = 0, + ZFS_PROJECT_OP_LIST = 1, + ZFS_PROJECT_OP_CHECK = 2, + ZFS_PROJECT_OP_CLEAR = 3, + ZFS_PROJECT_OP_SET = 4, +} zfs_project_ops_t; + +typedef struct zfs_project_control { + uint64_t zpc_expected_projid; + zfs_project_ops_t zpc_op; + boolean_t zpc_dironly; + boolean_t zpc_ignore_noent; + boolean_t zpc_keep_projid; + boolean_t zpc_newline; + boolean_t zpc_recursive; + boolean_t zpc_set_flag; +} zfs_project_control_t; + +int zfs_project_handle(const char *name, zfs_project_control_t *zpc); + +#endif /* _ZFS_PROJECTUTIL_H */ |
