summaryrefslogtreecommitdiff
path: root/usr/src/cmd/zfs
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2019-08-21 11:21:18 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2019-08-21 11:21:18 +0000
commite700298f01e2c414e66a1e494999ed372e4bacd9 (patch)
treeb1c4fb6dd99ddb059fe51f0bb5b32bf9fe2676bb /usr/src/cmd/zfs
parentf0a2865453f053f6ff0041c6326877f1149d89ec (diff)
parenta99cb9618990662acbd3bab1b4a5b05a6ca62556 (diff)
downloadillumos-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/Makefile8
-rw-r--r--usr/src/cmd/zfs/zfs_main.c421
-rw-r--r--usr/src/cmd/zfs/zfs_project.c304
-rw-r--r--usr/src/cmd/zfs/zfs_projectutil.h49
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 */