summaryrefslogtreecommitdiff
path: root/usr/src
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
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')
-rw-r--r--usr/src/Makefile.master7
-rw-r--r--usr/src/boot/Makefile.version2
-rw-r--r--usr/src/boot/sys/boot/efi/libefi/efipart.c639
-rw-r--r--usr/src/cmd/make/bin/main.cc2
-rw-r--r--usr/src/cmd/make/include/mksh/misc.h2
-rw-r--r--usr/src/cmd/make/lib/mksh/dosys.cc36
-rw-r--r--usr/src/cmd/make/lib/mksh/misc.cc40
-rw-r--r--usr/src/cmd/make/lib/vroot/lock.cc44
-rw-r--r--usr/src/cmd/zdb/zdb.c20
-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
-rw-r--r--usr/src/cmd/zhack/zhack.c2
-rw-r--r--usr/src/common/zfs/zfeature_common.c20
-rw-r--r--usr/src/common/zfs/zfeature_common.h2
-rw-r--r--usr/src/common/zfs/zfs_deleg.c8
-rw-r--r--usr/src/common/zfs/zfs_deleg.h8
-rw-r--r--usr/src/common/zfs/zfs_prop.c10
-rw-r--r--usr/src/lib/libsecdb/policy.conf6
-rw-r--r--usr/src/lib/libzfs/common/libzfs_dataset.c159
-rw-r--r--usr/src/man/man1m/zfs.1m270
-rw-r--r--usr/src/man/man2/fork.26
-rw-r--r--usr/src/man/man3/Intro.319
-rw-r--r--usr/src/man/man4/policy.conf.418
-rw-r--r--usr/src/man/man5/zpool-features.565
-rw-r--r--usr/src/man/man9s/ddi_device_acc_attr.9s40
-rw-r--r--usr/src/pkg/manifests/system-test-zfstest.mf59
-rw-r--r--usr/src/test/zfs-tests/cmd/mkfiles/mkfiles.c6
-rw-r--r--usr/src/test/zfs-tests/runfiles/delphix.run26
-rw-r--r--usr/src/test/zfs-tests/runfiles/omnios.run28
-rw-r--r--usr/src/test/zfs-tests/runfiles/openindiana.run28
-rw-r--r--usr/src/test/zfs-tests/runfiles/smartos.run22
-rw-r--r--usr/src/test/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg2
-rw-r--r--usr/src/test/zfs-tests/tests/functional/privilege/setup.ksh4
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/Makefile21
-rwxr-xr-xusr/src/test/zfs-tests/tests/functional/projectquota/cleanup.ksh37
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectid_001_pos.ksh100
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectid_002_pos.ksh91
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectid_003_pos.ksh86
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota.cfg46
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_001_pos.ksh89
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_002_pos.ksh87
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_003_pos.ksh99
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_004_neg.ksh87
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_005_pos.ksh68
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_006_pos.ksh72
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_007_pos.ksh58
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_008_pos.ksh91
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_009_pos.ksh131
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_common.kshlib101
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_001_pos.ksh91
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_002_pos.ksh83
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_003_pos.ksh119
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_004_pos.ksh76
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_001_pos.ksh108
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_002_pos.ksh120
-rw-r--r--usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_003_neg.ksh103
-rwxr-xr-xusr/src/test/zfs-tests/tests/functional/projectquota/setup.ksh56
-rw-r--r--usr/src/test/zfs-tests/tests/functional/upgrade/Makefile21
-rw-r--r--usr/src/test/zfs-tests/tests/functional/upgrade/cleanup.ksh42
-rw-r--r--usr/src/test/zfs-tests/tests/functional/upgrade/setup.ksh43
-rw-r--r--usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_common.kshlib41
-rw-r--r--usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh125
-rw-r--r--usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos.ksh95
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/groupspace_003_pos.ksh104
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/userquota_001_pos.ksh4
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/userquota_004_pos.ksh15
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/userquota_010_pos.ksh2
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/userquota_013_pos.ksh79
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/userquota_common.kshlib6
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/userspace_002_pos.ksh3
-rw-r--r--usr/src/test/zfs-tests/tests/functional/userquota/userspace_003_pos.ksh117
-rw-r--r--usr/src/tools/make/Makefile.com1
-rw-r--r--usr/src/uts/common/fs/zfs/dbuf.c2
-rw-r--r--usr/src/uts/common/fs/zfs/dmu.c4
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_objset.c413
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_traverse.c26
-rw-r--r--usr/src/uts/common/fs/zfs/dnode.c22
-rw-r--r--usr/src/uts/common/fs/zfs/dnode_sync.c5
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_pool.c2
-rw-r--r--usr/src/uts/common/fs/zfs/dsl_scan.c6
-rw-r--r--usr/src/uts/common/fs/zfs/sa.c189
-rw-r--r--usr/src/uts/common/fs/zfs/spa.c12
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dmu.h9
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dmu_objset.h39
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dnode.h13
-rw-r--r--usr/src/uts/common/fs/zfs/sys/dsl_deleg.h8
-rw-r--r--usr/src/uts/common/fs/zfs/sys/sa.h1
-rw-r--r--usr/src/uts/common/fs/zfs/sys/spa_impl.h2
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_acl.h3
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_context.h1
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_project.h78
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_sa.h3
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h14
-rw-r--r--usr/src/uts/common/fs/zfs/sys/zfs_znode.h23
-rw-r--r--usr/src/uts/common/fs/zfs/vdev.c18
-rw-r--r--usr/src/uts/common/fs/zfs/vdev_disk.c138
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_acl.c12
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_dir.c2
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_ioctl.c87
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_log.c14
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_replay.c13
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_sa.c13
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vfsops.c417
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_vnops.c375
-rw-r--r--usr/src/uts/common/fs/zfs/zfs_znode.c57
-rw-r--r--usr/src/uts/common/fs/zfs/zio_crypt.c13
-rw-r--r--usr/src/uts/common/sys/fs/zfs.h8
-rw-r--r--usr/src/uts/common/sys/vnode.h9
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_elfload.c7
-rw-r--r--usr/src/uts/i86pc/dboot/dboot_startkern.c47
112 files changed, 6324 insertions, 881 deletions
diff --git a/usr/src/Makefile.master b/usr/src/Makefile.master
index e751a9f79f..e2531f0378 100644
--- a/usr/src/Makefile.master
+++ b/usr/src/Makefile.master
@@ -654,6 +654,9 @@ NATIVE_CFLAGS= $(COPTFLAG) $($(NATIVE_MACH)_CFLAGS) $(CCMODE) \
$(IROPTFLAG) $(CGLOBALSTATIC) $(CCNOAUTOINLINE) \
$(CCNOREORDER) $(CSOURCEDEBUGFLAGS) $(CUSERFLAGS)
+NATIVE_CCFLAGS= $(CCOPTFLAG) $($(NATIVE_MACH)_CCFLAGS) $(CCSOURCEDEBUGFLAGS) \
+ $(CCUSERFLAGS)
+
DTEXTDOM=-DTEXT_DOMAIN=\"$(TEXT_DOMAIN)\" # For messaging.
DTS_ERRNO=-D_TS_ERRNO
CPPFLAGS.first= # Please keep empty. Only lower makefiles should set this.
@@ -692,7 +695,7 @@ BUILD.po= $(XGETTEXT) $(XGETFLAGS) -d $(<F) $<.i ;\
#
POFILE= $(PROG).po
-sparc_CCFLAGS= -cg92 -compat=4 \
+sparc_CCFLAGS= $(sparc_XARCH) -cg92 -compat=4 \
-Qoption ccfe -messages=no%anachronism \
$(CCERRWARN)
sparcv9_CCFLAGS= $(sparcv9_XARCH) -dalign -compat=5 \
@@ -700,7 +703,7 @@ sparcv9_CCFLAGS= $(sparcv9_XARCH) -dalign -compat=5 \
-Qoption ccfe -features=no%conststrings \
$(CCCREGSYM) \
$(CCERRWARN)
-i386_CCFLAGS= -compat=4 \
+i386_CCFLAGS= $(i386_XARCH) -compat=4 \
-Qoption ccfe -messages=no%anachronism \
-Qoption ccfe -features=no%conststrings \
$(CCERRWARN)
diff --git a/usr/src/boot/Makefile.version b/usr/src/boot/Makefile.version
index 754b809163..78bf47e3b2 100644
--- a/usr/src/boot/Makefile.version
+++ b/usr/src/boot/Makefile.version
@@ -33,4 +33,4 @@ LOADER_VERSION = 1.1
# Use date like formatting here, YYYY.MM.DD.XX, without leading zeroes.
# The version is processed from left to right, the version number can only
# be increased.
-BOOT_VERSION = $(LOADER_VERSION)-2019.08.15.2
+BOOT_VERSION = $(LOADER_VERSION)-2019.08.16.1
diff --git a/usr/src/boot/sys/boot/efi/libefi/efipart.c b/usr/src/boot/sys/boot/efi/libefi/efipart.c
index f200f62314..e51f4b5d0b 100644
--- a/usr/src/boot/sys/boot/efi/libefi/efipart.c
+++ b/usr/src/boot/sys/boot/efi/libefi/efipart.c
@@ -52,7 +52,7 @@ static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
static int efipart_open(struct open_file *, ...);
static int efipart_close(struct open_file *);
-static int efipart_ioctl(struct open_file *, u_long, void *);
+static int efipart_ioctl(struct open_file *, unsigned long, void *);
static int efipart_printfd(int);
static int efipart_printcd(int);
@@ -99,12 +99,18 @@ struct devsw efipart_hddev = {
.dv_cleanup = NULL
};
-static pdinfo_list_t fdinfo;
-static pdinfo_list_t cdinfo;
-static pdinfo_list_t hdinfo;
+static pdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
+static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
+static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
-static EFI_HANDLE *efipart_handles = NULL;
-static UINTN efipart_nhandles = 0;
+/*
+ * efipart_inithandles() is used to build up the pdinfo list from
+ * block device handles. Then each devsw init callback is used to
+ * pick items from pdinfo and move to proper device list.
+ * In ideal world, we should end up with empty pdinfo once all
+ * devsw initializers are called.
+ */
+static pdinfo_list_t pdinfo = STAILQ_HEAD_INITIALIZER(pdinfo);
pdinfo_list_t *
efiblk_get_pdinfo_list(struct devsw *dev)
@@ -162,6 +168,10 @@ efiblk_get_pdinfo_by_handle(EFI_HANDLE h)
STAILQ_FOREACH(dp, &cdinfo, pd_link) {
if (same_handle(dp, h))
return (dp);
+ STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
+ if (same_handle(pp, h))
+ return (pp);
+ }
}
STAILQ_FOREACH(dp, &fdinfo, pd_link) {
if (same_handle(dp, h))
@@ -185,15 +195,16 @@ efiblk_pdinfo_count(pdinfo_list_t *pdi)
int
efipart_inithandles(void)
{
+ unsigned i, nin;
UINTN sz;
EFI_HANDLE *hin;
+ EFI_DEVICE_PATH *devpath;
+ EFI_BLOCK_IO *blkio;
EFI_STATUS status;
+ pdinfo_t *pd;
- if (efipart_nhandles != 0) {
- free(efipart_handles);
- efipart_handles = NULL;
- efipart_nhandles = 0;
- }
+ if (!STAILQ_EMPTY(&pdinfo))
+ return (0);
sz = 0;
hin = NULL;
@@ -208,93 +219,24 @@ efipart_inithandles(void)
if (EFI_ERROR(status))
return (efi_status_to_errno(status));
- efipart_handles = hin;
- efipart_nhandles = sz;
+ nin = sz / sizeof (*hin);
#ifdef EFIPART_DEBUG
- printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__,
- efipart_nhandles);
+ printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin);
#endif
- return (0);
-}
-
-static ACPI_HID_DEVICE_PATH *
-efipart_floppy(EFI_DEVICE_PATH *node)
-{
- ACPI_HID_DEVICE_PATH *acpi;
-
- if (DevicePathType(node) == ACPI_DEVICE_PATH &&
- DevicePathSubType(node) == ACPI_DP) {
- acpi = (ACPI_HID_DEVICE_PATH *) node;
- if (acpi->HID == EISA_PNP_ID(PNP0604) ||
- acpi->HID == EISA_PNP_ID(PNP0700) ||
- acpi->HID == EISA_PNP_ID(PNP0701)) {
- return (acpi);
- }
- }
- return (NULL);
-}
-/*
- * Determine if the provided device path is hdd.
- *
- * There really is no simple fool proof way to classify the devices.
- * Since we do build three lists of devices - floppy, cd and hdd, we
- * will try to see if the device is floppy or cd, and list anything else
- * as hdd.
- */
-static bool
-efipart_hdd(EFI_DEVICE_PATH *dp)
-{
- unsigned i, nin;
- EFI_DEVICE_PATH *devpath, *node;
- EFI_BLOCK_IO *blkio;
- EFI_STATUS status;
-
- if (dp == NULL)
- return (false);
-
- if ((node = efi_devpath_last_node(dp)) == NULL)
- return (false);
-
- if (efipart_floppy(node) != NULL)
- return (false);
-
- /*
- * Test every EFI BLOCK IO handle to make sure dp is not device path
- * for CD/DVD.
- */
- nin = efipart_nhandles / sizeof (*efipart_handles);
for (i = 0; i < nin; i++) {
- devpath = efi_lookup_devpath(efipart_handles[i]);
- if (devpath == NULL)
- return (false);
-
- /* Only continue testing when dp is prefix in devpath. */
- if (!efi_devpath_is_prefix(dp, devpath))
- continue;
-
/*
- * The device path has to have last node describing the
- * device, or we can not test the type.
+ * Get devpath and open protocol.
+ * We should not get errors here
*/
- if ((node = efi_devpath_last_node(devpath)) == NULL)
- return (false);
-
- if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
- DevicePathSubType(node) == MEDIA_CDROM_DP) {
- return (false);
- }
+ if ((devpath = efi_lookup_devpath(hin[i])) == NULL)
+ continue;
- /* Make sure we do have the media. */
- status = OpenProtocolByHandle(efipart_handles[i], &blkio_guid,
+ status = OpenProtocolByHandle(hin[i], &blkio_guid,
(void **)&blkio);
- if (EFI_ERROR(status))
- return (false);
-
- /* USB or SATA cd without the media. */
- if (blkio->Media->RemovableMedia &&
- !blkio->Media->MediaPresent) {
- return (false);
+ if (EFI_ERROR(status)) {
+ printf("error %lu\n", EFI_ERROR_CODE(status));
+ continue;
}
/*
@@ -304,65 +246,86 @@ efipart_hdd(EFI_DEVICE_PATH *dp)
*/
if (blkio->Media->BlockSize < 512 ||
!powerof2(blkio->Media->BlockSize)) {
- return (false);
+ continue;
}
- }
- return (true);
-}
-/*
- * Add or update entries with new handle data.
- */
-static int
-efipart_fdinfo_add(EFI_HANDLE handle, uint32_t uid, EFI_DEVICE_PATH *devpath)
-{
- pdinfo_t *fd;
+ /* This is bad. */
+ if ((pd = calloc(1, sizeof (*pd))) == NULL) {
+ printf("efipart_inithandles: Out of memory.\n");
+ free(hin);
+ return (ENOMEM);
+ }
+ STAILQ_INIT(&pd->pd_part);
- fd = calloc(1, sizeof(pdinfo_t));
- if (fd == NULL) {
- printf("Failed to register floppy %d, out of memory\n", uid);
- return (ENOMEM);
+ pd->pd_handle = hin[i];
+ pd->pd_devpath = devpath;
+ pd->pd_blkio = blkio;
+ STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link);
}
- STAILQ_INIT(&fd->pd_part);
-
- fd->pd_unit = uid;
- fd->pd_handle = handle;
- fd->pd_devpath = devpath;
- fd->pd_parent = NULL;
- fd->pd_devsw = &efipart_fddev;
- STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
+
+ free(hin);
return (0);
}
-static void
-efipart_updatefd(void)
+static ACPI_HID_DEVICE_PATH *
+efipart_floppy(EFI_DEVICE_PATH *node)
{
- EFI_DEVICE_PATH *devpath, *node;
ACPI_HID_DEVICE_PATH *acpi;
- int i, nin;
-
- nin = efipart_nhandles / sizeof (*efipart_handles);
- for (i = 0; i < nin; i++) {
- devpath = efi_lookup_devpath(efipart_handles[i]);
- if (devpath == NULL)
- continue;
- if ((node = efi_devpath_last_node(devpath)) == NULL)
- continue;
- if ((acpi = efipart_floppy(node)) != NULL) {
- efipart_fdinfo_add(efipart_handles[i], acpi->UID,
- devpath);
+ if (DevicePathType(node) == ACPI_DEVICE_PATH &&
+ DevicePathSubType(node) == ACPI_DP) {
+ acpi = (ACPI_HID_DEVICE_PATH *) node;
+ if (acpi->HID == EISA_PNP_ID(PNP0604) ||
+ acpi->HID == EISA_PNP_ID(PNP0700) ||
+ acpi->HID == EISA_PNP_ID(PNP0701)) {
+ return (acpi);
}
}
+ return (NULL);
+}
+
+static pdinfo_t *
+efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
+{
+ pdinfo_t *pd;
+
+ STAILQ_FOREACH(pd, pdi, pd_link) {
+ if (efi_devpath_is_prefix(pd->pd_devpath, devpath))
+ return (pd);
+ }
+ return (NULL);
}
static int
efipart_initfd(void)
{
+ EFI_DEVICE_PATH *node;
+ ACPI_HID_DEVICE_PATH *acpi;
+ pdinfo_t *parent, *fd;
+
+restart:
+ STAILQ_FOREACH(fd, &pdinfo, pd_link) {
+ if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
+ continue;
- STAILQ_INIT(&fdinfo);
+ if ((acpi = efipart_floppy(node)) == NULL)
+ continue;
- efipart_updatefd();
+ STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
+ parent = efipart_find_parent(&pdinfo, fd->pd_devpath);
+ if (parent != NULL) {
+ STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
+ parent->pd_alias = fd->pd_handle;
+ parent->pd_unit = acpi->UID;
+ free(fd);
+ fd = parent;
+ } else {
+ fd->pd_unit = acpi->UID;
+ }
+ fd->pd_devsw = &efipart_fddev;
+ STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
+ goto restart;
+ }
bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
return (0);
@@ -371,69 +334,90 @@ efipart_initfd(void)
/*
* Add or update entries with new handle data.
*/
-static int
-efipart_cdinfo_add(EFI_HANDLE handle, EFI_HANDLE alias,
- EFI_DEVICE_PATH *devpath)
+static void
+efipart_cdinfo_add(pdinfo_t *cd)
{
- int unit;
- pdinfo_t *cd;
- pdinfo_t *pd;
+ pdinfo_t *pd, *last;
- unit = 0;
STAILQ_FOREACH(pd, &cdinfo, pd_link) {
- if (efi_devpath_match(pd->pd_devpath, devpath) == true) {
- pd->pd_handle = handle;
- pd->pd_alias = alias;
- return (0);
+ if (efi_devpath_is_prefix(pd->pd_devpath, cd->pd_devpath)) {
+ last = STAILQ_LAST(&pd->pd_part, pdinfo, pd_link);
+ if (last != NULL)
+ cd->pd_unit = last->pd_unit + 1;
+ else
+ cd->pd_unit = 0;
+ cd->pd_parent = pd;
+ cd->pd_devsw = &efipart_cddev;
+ STAILQ_INSERT_TAIL(&pd->pd_part, cd, pd_link);
+ return;
}
- unit++;
}
- cd = calloc(1, sizeof(pdinfo_t));
- if (cd == NULL) {
- printf("Failed to add cd %d, out of memory\n", unit);
- return (ENOMEM);
- }
- STAILQ_INIT(&cd->pd_part);
+ last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
+ if (last != NULL)
+ cd->pd_unit = last->pd_unit + 1;
+ else
+ cd->pd_unit = 0;
- cd->pd_handle = handle;
- cd->pd_unit = unit;
- cd->pd_alias = alias;
- cd->pd_devpath = devpath;
cd->pd_parent = NULL;
cd->pd_devsw = &efipart_cddev;
STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
- return (0);
+}
+
+static bool
+efipart_testcd(EFI_DEVICE_PATH *node, EFI_BLOCK_IO *blkio)
+{
+ if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
+ DevicePathSubType(node) == MEDIA_CDROM_DP) {
+ return (true);
+ }
+
+ /* cd drive without the media. */
+ if (blkio->Media->RemovableMedia &&
+ !blkio->Media->MediaPresent) {
+ return (true);
+ }
+
+ return (false);
}
static void
efipart_updatecd(void)
{
- int i, nin;
- EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
- EFI_HANDLE handle;
- EFI_BLOCK_IO *blkio;
+ EFI_DEVICE_PATH *devpath, *node;
EFI_STATUS status;
+ pdinfo_t *parent, *cd;
- nin = efipart_nhandles / sizeof (*efipart_handles);
- for (i = 0; i < nin; i++) {
- devpath = efi_lookup_devpath(efipart_handles[i]);
- if (devpath == NULL)
- continue;
-
- if ((node = efi_devpath_last_node(devpath)) == NULL)
+restart:
+ STAILQ_FOREACH(cd, &pdinfo, pd_link) {
+ if ((node = efi_devpath_last_node(cd->pd_devpath)) == NULL)
continue;
if (efipart_floppy(node) != NULL)
continue;
- if (efipart_hdd(devpath))
- continue;
+ /* Is parent of this device already registered? */
+ parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
+ if (parent != NULL) {
+ STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
+ efipart_cdinfo_add(cd);
+ goto restart;
+ }
- status = OpenProtocolByHandle(efipart_handles[i], &blkio_guid,
- (void **)&blkio);
- if (EFI_ERROR(status))
+ if (!efipart_testcd(node, cd->pd_blkio))
continue;
+
+ /* Find parent and unlink both parent and cd from pdinfo */
+ STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
+ parent = efipart_find_parent(&pdinfo, cd->pd_devpath);
+ if (parent != NULL) {
+ STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
+ efipart_cdinfo_add(parent);
+ }
+
+ if (parent == NULL)
+ parent = efipart_find_parent(&cdinfo, cd->pd_devpath);
+
/*
* If we come across a logical partition of subtype CDROM
* it doesn't refer to the CD filesystem itself, but rather
@@ -442,132 +426,79 @@ efipart_updatecd(void)
* that will be the CD filesystem.
*/
if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
- DevicePathSubType(node) == MEDIA_CDROM_DP) {
- devpathcpy = efi_devpath_trim(devpath);
- if (devpathcpy == NULL)
- continue;
- tmpdevpath = devpathcpy;
- status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
- &handle);
- free(devpathcpy);
- if (EFI_ERROR(status))
- continue;
- devpath = efi_lookup_devpath(handle);
- efipart_cdinfo_add(handle, efipart_handles[i],
- devpath);
- continue;
- }
+ DevicePathSubType(node) == MEDIA_CDROM_DP &&
+ parent == NULL) {
+ parent = calloc(1, sizeof (*parent));
+ if (parent == NULL) {
+ printf("efipart_updatecd: out of memory\n");
+ /* this device is lost but try again. */
+ free(cd);
+ goto restart;
+ }
- if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
- DevicePathSubType(node) == MSG_ATAPI_DP) {
- efipart_cdinfo_add(efipart_handles[i], NULL,
- devpath);
- continue;
+ devpath = efi_devpath_trim(cd->pd_devpath);
+ if (devpath == NULL) {
+ printf("efipart_updatecd: out of memory\n");
+ /* this device is lost but try again. */
+ free(parent);
+ free(cd);
+ goto restart;
+ }
+ parent->pd_devpath = devpath;
+ status = BS->LocateDevicePath(&blkio_guid,
+ &parent->pd_devpath, &parent->pd_handle);
+ free(devpath);
+ if (EFI_ERROR(status)) {
+ printf("efipart_updatecd: error %lu\n",
+ EFI_ERROR_CODE(status));
+ free(parent);
+ free(cd);
+ goto restart;
+ }
+ parent->pd_devpath =
+ efi_lookup_devpath(parent->pd_handle);
+ efipart_cdinfo_add(parent);
}
- /* USB or SATA cd without the media. */
- if (blkio->Media->RemovableMedia &&
- !blkio->Media->MediaPresent) {
- efipart_cdinfo_add(efipart_handles[i], NULL,
- devpath);
- }
+ efipart_cdinfo_add(cd);
+ goto restart;
}
}
static int
efipart_initcd(void)
{
-
- STAILQ_INIT(&cdinfo);
-
efipart_updatecd();
bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
return (0);
}
-static int
-efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
+static void
+efipart_hdinfo_add(pdinfo_t *hd, HARDDRIVE_DEVICE_PATH *node)
{
- EFI_DEVICE_PATH *disk_devpath, *part_devpath;
- HARDDRIVE_DEVICE_PATH *node;
- int unit;
- pdinfo_t *hd, *pd, *last;
-
- disk_devpath = efi_lookup_devpath(disk_handle);
- if (disk_devpath == NULL)
- return (ENOENT);
-
- if (part_handle != NULL) {
- part_devpath = efi_lookup_devpath(part_handle);
- if (part_devpath == NULL)
- return (ENOENT);
- node = (HARDDRIVE_DEVICE_PATH *)
- efi_devpath_last_node(part_devpath);
- if (node == NULL)
- return (ENOENT); /* This should not happen. */
- } else {
- part_devpath = NULL;
- node = NULL;
- }
-
- pd = calloc(1, sizeof(pdinfo_t));
- if (pd == NULL) {
- printf("Failed to add disk, out of memory\n");
- return (ENOMEM);
- }
- STAILQ_INIT(&pd->pd_part);
-
- STAILQ_FOREACH(hd, &hdinfo, pd_link) {
- if (efi_devpath_match(hd->pd_devpath, disk_devpath) == true) {
- if (part_devpath == NULL)
- return (0);
+ pdinfo_t *pd, *last;
+ STAILQ_FOREACH(pd, &hdinfo, pd_link) {
+ if (efi_devpath_is_prefix(pd->pd_devpath, hd->pd_devpath)) {
/* Add the partition. */
- pd->pd_handle = part_handle;
- pd->pd_unit = node->PartitionNumber;
- pd->pd_devpath = part_devpath;
- pd->pd_parent = hd;
- pd->pd_devsw = &efipart_hddev;
- STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
- return (0);
+ hd->pd_unit = node->PartitionNumber;
+ hd->pd_parent = pd;
+ hd->pd_devsw = &efipart_hddev;
+ STAILQ_INSERT_TAIL(&pd->pd_part, hd, pd_link);
+ return;
}
}
last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
if (last != NULL)
- unit = last->pd_unit + 1;
+ hd->pd_unit = last->pd_unit + 1;
else
- unit = 0;
+ hd->pd_unit = 0;
/* Add the disk. */
- hd = pd;
- hd->pd_handle = disk_handle;
- hd->pd_unit = unit;
- hd->pd_devpath = disk_devpath;
- hd->pd_parent = NULL;
hd->pd_devsw = &efipart_hddev;
STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
-
- if (part_devpath == NULL)
- return (0);
-
- pd = calloc(1, sizeof(pdinfo_t));
- if (pd == NULL) {
- printf("Failed to add partition, out of memory\n");
- return (ENOMEM);
- }
- STAILQ_INIT(&pd->pd_part);
-
- /* Add the partition. */
- pd->pd_handle = part_handle;
- pd->pd_unit = node->PartitionNumber;
- pd->pd_devpath = part_devpath;
- pd->pd_parent = hd;
- pd->pd_devsw = &efipart_hddev;
- STAILQ_INSERT_TAIL(&hd->pd_part, pd, pd_link);
-
- return (0);
}
/*
@@ -576,40 +507,25 @@ efipart_hdinfo_add(EFI_HANDLE disk_handle, EFI_HANDLE part_handle)
* of typeN:M, where type is interface type, N is disk id
* and M is partition id.
*/
-static int
-efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
+static void
+efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node)
{
- EFI_DEVICE_PATH *devpath;
- FILEPATH_DEVICE_PATH *node;
char *pathname, *p;
- int unit, len;
- pdinfo_t *pd, *last;
-
- /* First collect and verify all the data */
- if ((devpath = efi_lookup_devpath(disk_handle)) == NULL)
- return (ENOENT);
- node = (FILEPATH_DEVICE_PATH *)efi_devpath_last_node(devpath);
- if (node == NULL)
- return (ENOENT); /* This should not happen. */
+ int len;
+ pdinfo_t *last;
- pd = calloc(1, sizeof(pdinfo_t));
- if (pd == NULL) {
- printf("Failed to add disk, out of memory\n");
- return (ENOMEM);
- }
- STAILQ_INIT(&pd->pd_part);
last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
if (last != NULL)
- unit = last->pd_unit + 1;
+ hd->pd_unit = last->pd_unit + 1;
else
- unit = 0;
+ hd->pd_unit = 0;
/* FILEPATH_DEVICE_PATH has 0 terminated string */
len = ucs2len(node->PathName);
if ((pathname = malloc(len + 1)) == NULL) {
printf("Failed to add disk, out of memory\n");
- free(pd);
- return (ENOMEM);
+ free(hd);
+ return;
}
cpy16to8(node->PathName, pathname, len + 1);
p = strchr(pathname, ':');
@@ -620,23 +536,19 @@ efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
* false, this code would need update.
*/
if (p == NULL) { /* no colon, add the disk */
- pd->pd_handle = disk_handle;
- pd->pd_unit = unit;
- pd->pd_devpath = devpath;
- pd->pd_parent = NULL;
- pd->pd_devsw = &efipart_hddev;
- STAILQ_INSERT_TAIL(&hdinfo, pd, pd_link);
+ hd->pd_devsw = &efipart_hddev;
+ STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
free(pathname);
- return (0);
+ return;
}
p++; /* skip the colon */
errno = 0;
- unit = (int)strtol(p, NULL, 0);
+ hd->pd_unit = (int)strtol(p, NULL, 0);
if (errno != 0) {
printf("Bad unit number for partition \"%s\"\n", pathname);
free(pathname);
- free(pd);
- return (EUNIT);
+ free(hd);
+ return;
}
/*
@@ -648,81 +560,98 @@ efipart_hdinfo_add_filepath(EFI_HANDLE disk_handle)
if (last == NULL) {
printf("BUG: No disk for partition \"%s\"\n", pathname);
free(pathname);
- free(pd);
- return (EINVAL);
+ free(hd);
+ return;
}
/* Add the partition. */
- pd->pd_handle = disk_handle;
- pd->pd_unit = unit;
- pd->pd_devpath = devpath;
- pd->pd_parent = last;
- pd->pd_devsw = &efipart_hddev;
- STAILQ_INSERT_TAIL(&last->pd_part, pd, pd_link);
+ hd->pd_parent = last;
+ hd->pd_devsw = &efipart_hddev;
+ STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link);
free(pathname);
- return (0);
}
static void
efipart_updatehd(void)
{
- int i, nin;
- EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
- EFI_HANDLE handle;
- EFI_BLOCK_IO *blkio;
+ EFI_DEVICE_PATH *devpath, *node;
EFI_STATUS status;
+ pdinfo_t *parent, *hd;
- nin = efipart_nhandles / sizeof (*efipart_handles);
- for (i = 0; i < nin; i++) {
- devpath = efi_lookup_devpath(efipart_handles[i]);
- if (devpath == NULL)
+restart:
+ STAILQ_FOREACH(hd, &pdinfo, pd_link) {
+ if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
continue;
- if ((node = efi_devpath_last_node(devpath)) == NULL)
+ if (efipart_floppy(node) != NULL)
continue;
- if (!efipart_hdd(devpath))
+ if (efipart_testcd(node, hd->pd_blkio))
continue;
- status = OpenProtocolByHandle(efipart_handles[i], &blkio_guid,
- (void **)&blkio);
- if (EFI_ERROR(status))
- continue;
+ if (DevicePathType(node) == HARDWARE_DEVICE_PATH &&
+ DevicePathSubType(node) == HW_PCI_DP) {
+ STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
+ efipart_hdinfo_add(hd, NULL);
+ goto restart;
+ }
if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
- efipart_hdinfo_add_filepath(efipart_handles[i]);
- continue;
+ STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
+ efipart_hdinfo_add_filepath(hd,
+ (FILEPATH_DEVICE_PATH *)node);
+ goto restart;
+ }
+
+ STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
+ parent = efipart_find_parent(&pdinfo, hd->pd_devpath);
+ if (parent != NULL) {
+ STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
+ efipart_hdinfo_add(parent, NULL);
+ } else {
+ parent = efipart_find_parent(&hdinfo, hd->pd_devpath);
}
if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
- DevicePathSubType(node) == MEDIA_HARDDRIVE_DP) {
- devpathcpy = efi_devpath_trim(devpath);
- if (devpathcpy == NULL)
- continue;
- tmpdevpath = devpathcpy;
- status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
- &handle);
- free(devpathcpy);
- if (EFI_ERROR(status))
- continue;
- /*
- * We do not support nested partitions.
- */
- devpathcpy = efi_lookup_devpath(handle);
- if (devpathcpy == NULL)
- continue;
- if ((node = efi_devpath_last_node(devpathcpy)) == NULL)
- continue;
+ DevicePathSubType(node) == MEDIA_HARDDRIVE_DP &&
+ parent == NULL) {
+ parent = calloc(1, sizeof (*parent));
+ if (parent == NULL) {
+ printf("efipart_updatehd: out of memory\n");
+ /* this device is lost but try again. */
+ free(hd);
+ goto restart;
+ }
- if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
- DevicePathSubType(node) == MEDIA_HARDDRIVE_DP)
- continue;
+ devpath = efi_devpath_trim(hd->pd_devpath);
+ if (devpath == NULL) {
+ printf("efipart_updatehd: out of memory\n");
+ /* this device is lost but try again. */
+ free(parent);
+ free(hd);
+ goto restart;
+ }
- efipart_hdinfo_add(handle, efipart_handles[i]);
- continue;
+ parent->pd_devpath = devpath;
+ status = BS->LocateDevicePath(&blkio_guid,
+ &parent->pd_devpath, &parent->pd_handle);
+ free(devpath);
+ if (EFI_ERROR(status)) {
+ printf("efipart_updatehd: error %lu\n",
+ EFI_ERROR_CODE(status));
+ free(parent);
+ free(hd);
+ goto restart;
+ }
+
+ parent->pd_devpath =
+ efi_lookup_devpath(&parent->pd_handle);
+
+ efipart_hdinfo_add(parent, NULL);
}
- efipart_hdinfo_add(efipart_handles[i], NULL);
+ efipart_hdinfo_add(hd, (HARDDRIVE_DEVICE_PATH *)node);
+ goto restart;
}
}
@@ -730,8 +659,6 @@ static int
efipart_inithd(void)
{
- STAILQ_INIT(&hdinfo);
-
efipart_updatehd();
bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
@@ -768,7 +695,7 @@ efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
break;
}
}
- snprintf(line, sizeof(line),
+ snprintf(line, sizeof (line),
" %s%d", dev->dv_name, pd->pd_unit);
printf("%s:", line);
status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio);
@@ -844,14 +771,14 @@ efipart_open(struct open_file *f, ...)
EFI_STATUS status;
va_start(args, f);
- dev = va_arg(args, struct disk_devdesc*);
+ dev = va_arg(args, struct disk_devdesc *);
va_end(args);
if (dev == NULL)
return (EINVAL);
pd = efiblk_get_pdinfo((struct devdesc *)dev);
if (pd == NULL)
- return (EINVAL);
+ return (EIO);
if (pd->pd_blkio == NULL) {
status = OpenProtocolByHandle(pd->pd_handle, &blkio_guid,
@@ -913,7 +840,7 @@ efipart_close(struct open_file *f)
}
static int
-efipart_ioctl(struct open_file *f, u_long cmd, void *data)
+efipart_ioctl(struct open_file *f, unsigned long cmd, void *data)
{
struct disk_devdesc *dev;
pdinfo_t *pd;
@@ -935,7 +862,7 @@ efipart_ioctl(struct open_file *f, u_long cmd, void *data)
switch (cmd) {
case DIOCGSECTORSIZE:
- *(u_int *)data = pd->pd_blkio->Media->BlockSize;
+ *(uint_t *)data = pd->pd_blkio->Media->BlockSize;
break;
case DIOCGMEDIASIZE:
*(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
diff --git a/usr/src/cmd/make/bin/main.cc b/usr/src/cmd/make/bin/main.cc
index c461b93d49..1320f68a4d 100644
--- a/usr/src/cmd/make/bin/main.cc
+++ b/usr/src/cmd/make/bin/main.cc
@@ -71,7 +71,7 @@ extern void job_adjust_fini();
#define LD_SUPPORT_ENV_VAR_32 "SGS_SUPPORT_32"
#define LD_SUPPORT_ENV_VAR_64 "SGS_SUPPORT_64"
#define LD_SUPPORT_MAKE_LIB "libmakestate.so.1"
-#ifdef __i386
+#ifdef __x86
#define LD_SUPPORT_MAKE_ARCH "i386"
#elif __sparc
#define LD_SUPPORT_MAKE_ARCH "sparc"
diff --git a/usr/src/cmd/make/include/mksh/misc.h b/usr/src/cmd/make/include/mksh/misc.h
index f3504bbcc4..d2048c391c 100644
--- a/usr/src/cmd/make/include/mksh/misc.h
+++ b/usr/src/cmd/make/include/mksh/misc.h
@@ -36,7 +36,7 @@ extern void fatal_mksh(const char *message, ...);
extern void fatal_reader_mksh(const char *pattern, ...);
extern char *get_current_path_mksh(void);
extern Property get_prop(register Property start, register Property_id type);
-extern char *getmem(register int size);
+extern char *getmem(size_t size);
extern Name getname_fn(wchar_t *name, register int len, register Boolean dont_enter, register Boolean * foundp = NULL);
extern void store_name(Name name);
extern void free_name(Name name);
diff --git a/usr/src/cmd/make/lib/mksh/dosys.cc b/usr/src/cmd/make/lib/mksh/dosys.cc
index 5ff0ab73b1..08ea785b73 100644
--- a/usr/src/cmd/make/lib/mksh/dosys.cc
+++ b/usr/src/cmd/make/lib/mksh/dosys.cc
@@ -65,7 +65,7 @@
/*
* File table of contents
*/
-static Boolean exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path);
+static Boolean exec_vp(char *name, char **argv, char **envp, Boolean ignore_error, pathpt vroot_path);
/*
* Workaround for NFS bug. Sometimes, when running 'open' on a remote
@@ -145,15 +145,15 @@ redirect_io(char *stdout_file, char *stderr_file)
* shell_name The Name "SHELL", used to get the path to shell
*/
int
-doshell(wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio)
+doshell(wchar_t *command, Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio)
{
char *argv[6];
int argv_index = 0;
int cmd_argv_index;
int length;
char nice_prio_buf[MAXPATHLEN];
- register Name shell = getvar(shell_name);
- register char *shellname;
+ Name shell = getvar(shell_name);
+ char *shellname;
char *tmp_mbs_buffer;
@@ -237,10 +237,10 @@ doshell(wchar_t *command, register Boolean ignore_error, char *stdout_file, char
* vroot_path The path used by the vroot package
*/
static Boolean
-exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path)
+exec_vp(char *name, char **argv, char **envp, Boolean ignore_error, pathpt vroot_path)
{
- register Name shell = getvar(shell_name);
- register char *shellname;
+ Name shell = getvar(shell_name);
+ char *shellname;
char *shargv[4];
Name tmp_shell;
@@ -310,15 +310,15 @@ exec_vp(register char *name, register char **argv, char **envp, register Boolean
* filter_stderr If -X is on we redirect stderr
*/
int
-doexec(register wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio)
+doexec(wchar_t *command, Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio)
{
int arg_count = 5;
char **argv;
int length;
char nice_prio_buf[MAXPATHLEN];
- register char **p;
+ char **p;
wchar_t *q;
- register wchar_t *t;
+ wchar_t *t;
char *tmp_mbs_buffer;
/*
@@ -430,14 +430,14 @@ doexec(register wchar_t *command, register Boolean ignore_error, char *stdout_fi
* filter_stderr Set if -X is on
*/
Boolean
-await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs_p, int job_msg_id)
+await(Boolean ignore_error, Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs_p, int job_msg_id)
{
int status;
char *buffer;
int core_dumped;
int exit_status;
FILE *outfp;
- register pid_t pid;
+ pid_t pid;
struct stat stat_buff;
int termination_signal;
char tmp_buf[MAXPATHLEN];
@@ -524,14 +524,14 @@ await(register Boolean ignore_error, register Boolean silent_error, Name target,
* Global variables used:
*/
void
-sh_command2string(register String command, register String destination)
+sh_command2string(String command, String destination)
{
- register FILE *fd;
- register int chr;
- int status;
- Boolean command_generated_output = false;
+ FILE *fd;
+ int chr;
+ int status;
+ Boolean command_generated_output = false;
- command->text.p = (int) nul_char;
+ command->text.p = NULL;
WCSTOMBS(mbs_buffer, command->buffer.start);
if ((fd = popen(mbs_buffer, "r")) == NULL) {
WCSTOMBS(mbs_buffer, command->buffer.start);
diff --git a/usr/src/cmd/make/lib/mksh/misc.cc b/usr/src/cmd/make/lib/mksh/misc.cc
index 4765102500..fe14fd874d 100644
--- a/usr/src/cmd/make/lib/mksh/misc.cc
+++ b/usr/src/cmd/make/lib/mksh/misc.cc
@@ -102,18 +102,16 @@ static void expand_string(register String string, register int length);
* Global variables used:
*/
char *
-getmem(register int size)
+getmem(size_t size)
{
- register char *result = (char *) malloc((unsigned) size);
+ char *result = (char *)malloc(size);
if (result == NULL) {
- char buf[FATAL_ERROR_MSG_SIZE];
- sprintf(buf, "*** Error: malloc(%d) failed: %s\n", size, strerror(errno));
- strcat(buf, gettext("mksh: Fatal error: Out of memory\n"));
- fputs(buf, stderr);
- exit_status = 1;
+ (void) fprintf(stderr, "*** Error: malloc(%d) failed: %s\n%s",
+ size, strerror(errno),
+ gettext("mksh: Fatal error: Out of memory\n"));
exit(1);
}
- return result;
+ return (result);
}
/*
@@ -338,26 +336,22 @@ setup_char_semantics(void)
*
* Parameters:
* errnum The number of the error we want to describe
- *
- * Global variables used:
- * sys_errlist A vector of error messages
- * sys_nerr The size of sys_errlist
*/
char *
errmsg(int errnum)
{
-
- extern int sys_nerr;
- char *errbuf;
-
- if ((errnum < 0) || (errnum > sys_nerr)) {
- errbuf = getmem(6+1+11+1);
- (void) sprintf(errbuf, gettext("Error %d"), errnum);
- return errbuf;
- } else {
- return strerror(errnum);
-
+ char *msg;
+ char *errbuf;
+
+ errno = 0;
+ msg = strerror(errnum);
+ if (errno == EINVAL) {
+ size_t size = 6 + 1 + 11 + 1;
+ errbuf = getmem(size);
+ (void) snprintf(errbuf, size, gettext("Error %d"), errnum);
+ return (errbuf);
}
+ return (msg);
}
static char static_buf[MAXPATHLEN*3];
diff --git a/usr/src/cmd/make/lib/vroot/lock.cc b/usr/src/cmd/make/lib/vroot/lock.cc
index d3389ce588..c142e3669e 100644
--- a/usr/src/cmd/make/lib/vroot/lock.cc
+++ b/usr/src/cmd/make/lib/vroot/lock.cc
@@ -36,10 +36,8 @@
#include <errno.h> /* errno */
#include <libintl.h>
-extern char *sys_errlist[];
-extern int sys_nerr;
-
-static void file_lock_error(char *msg, char *file, char *str, int arg1, int arg2);
+static void file_lock_error(char *msg, char *file, const char *str,
+ char *arg1, char * arg2);
#define BLOCK_INTERUPTS sigfillset(&newset) ; \
sigprocmask(SIG_SETMASK, &newset, &oldset)
@@ -108,8 +106,8 @@ file_lock(char *name, char *lockname, int *file_locked, int timeout)
UNBLOCK_INTERUPTS;
if (errno != EEXIST) {
- file_lock_error(msg, name, (char *)"symlink(%s, %s)",
- (int) name, (int) lockname);
+ file_lock_error(msg, name, "symlink(%s, %s)",
+ name, lockname);
fprintf(stderr, "%s", msg);
return errno;
}
@@ -129,15 +127,15 @@ file_lock(char *name, char *lockname, int *file_locked, int timeout)
if ((counter > 5) && (!printed_warning)) {
/* Print waiting message after 5 secs */
(void) getcwd(msg, MAXPATHLEN);
- fprintf(stderr,
- gettext("file_lock: file %s is already locked.\n"),
- name);
- fprintf(stderr,
- gettext("file_lock: will periodically check the lockfile %s for two minutes.\n"),
- lockname);
- fprintf(stderr,
- gettext("Current working directory %s\n"),
- msg);
+ fprintf(stderr, gettext(
+ "file_lock: file %s is already locked.\n"),
+ name);
+ fprintf(stderr, gettext(
+ "file_lock: will periodically check the "
+ "lockfile %s for two minutes.\n"),
+ lockname);
+ fprintf(stderr, gettext(
+ "Current working directory %s\n"), msg);
printed_warning = 1;
}
@@ -157,19 +155,23 @@ file_lock(char *name, char *lockname, int *file_locked, int timeout)
* Format a message telling why the lock could not be created.
*/
static void
-file_lock_error(char *msg, char *file, char *str, int arg1, int arg2)
+file_lock_error(char *msg, char *file, const char *str, char *arg1, char *arg2)
{
- int len;
+ int len, err;
+ char *ptr;
sprintf(msg, gettext("Could not lock file `%s'; "), file);
len = strlen(msg);
sprintf(&msg[len], str, arg1, arg2);
strcat(msg, gettext(" failed - "));
- if (errno < sys_nerr) {
- strcat(msg, strerror(errno));
+
+ err = errno;
+ errno = 0;
+ ptr = strerror(err);
+ if (errno != EINVAL) {
+ strcat(msg, ptr);
} else {
len = strlen(msg);
- sprintf(&msg[len], "errno %d", errno);
+ sprintf(&msg[len], "errno %d", err);
}
}
-
diff --git a/usr/src/cmd/zdb/zdb.c b/usr/src/cmd/zdb/zdb.c
index 08ba492991..7720a6cd0d 100644
--- a/usr/src/cmd/zdb/zdb.c
+++ b/usr/src/cmd/zdb/zdb.c
@@ -2041,6 +2041,13 @@ dump_znode(objset_t *os, uint64_t object, void *data, size_t size)
(void) printf("\tparent %llu\n", (u_longlong_t)parent);
(void) printf("\tlinks %llu\n", (u_longlong_t)links);
(void) printf("\tpflags %llx\n", (u_longlong_t)pflags);
+ if (dmu_objset_projectquota_enabled(os) && (pflags & ZFS_PROJID)) {
+ uint64_t projid;
+
+ if (sa_lookup(hdl, sa_attr_table[ZPL_PROJID], &projid,
+ sizeof (uint64_t)) == 0)
+ (void) printf("\tprojid %llu\n", (u_longlong_t)projid);
+ }
if (sa_lookup(hdl, sa_attr_table[ZPL_XATTR], &xattr,
sizeof (uint64_t)) == 0)
(void) printf("\txattr %llu\n", (u_longlong_t)xattr);
@@ -2103,8 +2110,8 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = {
dump_packed_nvlist, /* FUID nvlist size */
dump_zap, /* DSL dataset next clones */
dump_zap, /* DSL scrub queue */
- dump_zap, /* ZFS user/group used */
- dump_zap, /* ZFS user/group quota */
+ dump_zap, /* ZFS user/group/project used */
+ dump_zap, /* ZFS user/group/project quota */
dump_zap, /* snapshot refcount tags */
dump_ddt_zap, /* DDT ZAP object */
dump_zap, /* DDT statistics */
@@ -2217,11 +2224,13 @@ dump_object(objset_t *os, uint64_t object, int verbosity, int *print_header,
}
if (verbosity >= 4) {
- (void) printf("\tdnode flags: %s%s%s\n",
+ (void) printf("\tdnode flags: %s%s%s%s\n",
(dn->dn_phys->dn_flags & DNODE_FLAG_USED_BYTES) ?
"USED_BYTES " : "",
(dn->dn_phys->dn_flags & DNODE_FLAG_USERUSED_ACCOUNTED) ?
"USERUSED_ACCOUNTED " : "",
+ (dn->dn_phys->dn_flags & DNODE_FLAG_USEROBJUSED_ACCOUNTED) ?
+ "USEROBJUSED_ACCOUNTED " : "",
(dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR) ?
"SPILL_BLKPTR" : "");
(void) printf("\tdnode maxblkid: %llu\n",
@@ -2411,6 +2420,11 @@ dump_dir(objset_t *os)
NULL);
}
+ if (DMU_PROJECTUSED_DNODE(os) != NULL &&
+ DMU_PROJECTUSED_DNODE(os)->dn_type != 0)
+ dump_object(os, DMU_PROJECTUSED_OBJECT, verbosity,
+ &print_header, NULL);
+
object = 0;
while ((error = dmu_object_next(os, &object, B_FALSE, 0)) == 0) {
dump_object(os, object, verbosity, &print_header, &dnode_slots);
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 */
diff --git a/usr/src/cmd/zhack/zhack.c b/usr/src/cmd/zhack/zhack.c
index 6b8e9dc47c..1f90f97bdf 100644
--- a/usr/src/cmd/zhack/zhack.c
+++ b/usr/src/cmd/zhack/zhack.c
@@ -105,7 +105,7 @@ fatal(spa_t *spa, void *tag, const char *fmt, ...)
/* ARGSUSED */
static int
space_delta_cb(dmu_object_type_t bonustype, void *data,
- uint64_t *userp, uint64_t *groupp)
+ uint64_t *userp, uint64_t *groupp, uint64_t *projectp)
{
/*
* Is it a valid type of object to track?
diff --git a/usr/src/common/zfs/zfeature_common.c b/usr/src/common/zfs/zfeature_common.c
index 0eb681b723..78345bbd88 100644
--- a/usr/src/common/zfs/zfeature_common.c
+++ b/usr/src/common/zfs/zfeature_common.c
@@ -346,4 +346,24 @@ zpool_feature_init(void)
"com.datto:encryption", "encryption",
"Support for dataset level encryption",
ZFEATURE_FLAG_PER_DATASET, encryption_deps);
+
+ static const spa_feature_t userobj_accounting_deps[] = {
+ SPA_FEATURE_EXTENSIBLE_DATASET,
+ SPA_FEATURE_NONE
+ };
+ zfeature_register(SPA_FEATURE_USEROBJ_ACCOUNTING,
+ "org.zfsonlinux:userobj_accounting", "userobj_accounting",
+ "User/Group object accounting.",
+ ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
+ userobj_accounting_deps);
+
+ static const spa_feature_t project_quota_deps[] = {
+ SPA_FEATURE_EXTENSIBLE_DATASET,
+ SPA_FEATURE_NONE
+ };
+ zfeature_register(SPA_FEATURE_PROJECT_QUOTA,
+ "org.zfsonlinux:project_quota", "project_quota",
+ "space/object accounting based on project ID.",
+ ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET,
+ project_quota_deps);
}
diff --git a/usr/src/common/zfs/zfeature_common.h b/usr/src/common/zfs/zfeature_common.h
index adaa782f98..ab9ff50ff6 100644
--- a/usr/src/common/zfs/zfeature_common.h
+++ b/usr/src/common/zfs/zfeature_common.h
@@ -66,6 +66,8 @@ typedef enum spa_feature {
SPA_FEATURE_RESILVER_DEFER,
SPA_FEATURE_ENCRYPTION,
SPA_FEATURE_BOOKMARK_V2,
+ SPA_FEATURE_USEROBJ_ACCOUNTING,
+ SPA_FEATURE_PROJECT_QUOTA,
SPA_FEATURES
} spa_feature_t;
diff --git a/usr/src/common/zfs/zfs_deleg.c b/usr/src/common/zfs/zfs_deleg.c
index 76248de87d..2e8b65d8b0 100644
--- a/usr/src/common/zfs/zfs_deleg.c
+++ b/usr/src/common/zfs/zfs_deleg.c
@@ -64,10 +64,18 @@ zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = {
{ZFS_DELEG_PERM_GROUPQUOTA},
{ZFS_DELEG_PERM_USERUSED},
{ZFS_DELEG_PERM_GROUPUSED},
+ {ZFS_DELEG_PERM_USEROBJQUOTA},
+ {ZFS_DELEG_PERM_GROUPOBJQUOTA},
+ {ZFS_DELEG_PERM_USEROBJUSED},
+ {ZFS_DELEG_PERM_GROUPOBJUSED},
{ZFS_DELEG_PERM_HOLD},
{ZFS_DELEG_PERM_RELEASE},
{ZFS_DELEG_PERM_LOAD_KEY},
{ZFS_DELEG_PERM_CHANGE_KEY},
+ {ZFS_DELEG_PERM_PROJECTUSED},
+ {ZFS_DELEG_PERM_PROJECTQUOTA},
+ {ZFS_DELEG_PERM_PROJECTOBJUSED},
+ {ZFS_DELEG_PERM_PROJECTOBJQUOTA},
{NULL}
};
diff --git a/usr/src/common/zfs/zfs_deleg.h b/usr/src/common/zfs/zfs_deleg.h
index e97b1dae22..1bc8f4d135 100644
--- a/usr/src/common/zfs/zfs_deleg.h
+++ b/usr/src/common/zfs/zfs_deleg.h
@@ -63,6 +63,10 @@ typedef enum {
ZFS_DELEG_NOTE_GROUPQUOTA,
ZFS_DELEG_NOTE_USERUSED,
ZFS_DELEG_NOTE_GROUPUSED,
+ ZFS_DELEG_NOTE_USEROBJQUOTA,
+ ZFS_DELEG_NOTE_GROUPOBJQUOTA,
+ ZFS_DELEG_NOTE_USEROBJUSED,
+ ZFS_DELEG_NOTE_GROUPOBJUSED,
ZFS_DELEG_NOTE_HOLD,
ZFS_DELEG_NOTE_RELEASE,
ZFS_DELEG_NOTE_DIFF,
@@ -70,6 +74,10 @@ typedef enum {
ZFS_DELEG_NOTE_REMAP,
ZFS_DELEG_NOTE_LOAD_KEY,
ZFS_DELEG_NOTE_CHANGE_KEY,
+ ZFS_DELEG_NOTE_PROJECTUSED,
+ ZFS_DELEG_NOTE_PROJECTQUOTA,
+ ZFS_DELEG_NOTE_PROJECTOBJUSED,
+ ZFS_DELEG_NOTE_PROJECTOBJQUOTA,
ZFS_DELEG_NOTE_NONE
} zfs_deleg_note_t;
diff --git a/usr/src/common/zfs/zfs_prop.c b/usr/src/common/zfs/zfs_prop.c
index 6b7ca78c88..6f633147a3 100644
--- a/usr/src/common/zfs/zfs_prop.c
+++ b/usr/src/common/zfs/zfs_prop.c
@@ -54,7 +54,15 @@ const char *zfs_userquota_prop_prefixes[] = {
"userused@",
"userquota@",
"groupused@",
- "groupquota@"
+ "groupquota@",
+ "userobjused@",
+ "userobjquota@",
+ "groupobjused@",
+ "groupobjquota@",
+ "projectused@",
+ "projectquota@",
+ "projectobjused@",
+ "projectobjquota@"
};
zprop_desc_t *
diff --git a/usr/src/lib/libsecdb/policy.conf b/usr/src/lib/libsecdb/policy.conf
index db9b8cb1f5..60fea9f5f3 100644
--- a/usr/src/lib/libsecdb/policy.conf
+++ b/usr/src/lib/libsecdb/policy.conf
@@ -44,11 +44,11 @@ CRYPT_ALGORITHMS_ALLOW=1,2a,md5,5,6
#
#CRYPT_ALGORITHMS_DEPRECATE=__unix__
-# The OpenSolaris default is a SHA256 based algorithm. To revert to
-# the policy present in Solaris releases set CRYPT_DEFAULT=__unix__,
+# The illumos default is a SHA512 based algorithm. To revert to
+# the policy present in former Solaris releases set CRYPT_DEFAULT=__unix__,
# which is not listed in crypt.conf(4) since it is internal to libc.
#
-CRYPT_DEFAULT=5
+CRYPT_DEFAULT=6
#
# These settings determine the default privileges users have. If not set,
# the default privileges are taken from the inherited set.
diff --git a/usr/src/lib/libzfs/common/libzfs_dataset.c b/usr/src/lib/libzfs/common/libzfs_dataset.c
index 1cfe32dd67..2edbf8b837 100644
--- a/usr/src/lib/libzfs/common/libzfs_dataset.c
+++ b/usr/src/lib/libzfs/common/libzfs_dataset.c
@@ -1037,7 +1037,11 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
}
if (uqtype != ZFS_PROP_USERQUOTA &&
- uqtype != ZFS_PROP_GROUPQUOTA) {
+ uqtype != ZFS_PROP_GROUPQUOTA &&
+ uqtype != ZFS_PROP_USEROBJQUOTA &&
+ uqtype != ZFS_PROP_GROUPOBJQUOTA &&
+ uqtype != ZFS_PROP_PROJECTQUOTA &&
+ uqtype != ZFS_PROP_PROJECTOBJQUOTA) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' is readonly"),
propname);
@@ -1062,7 +1066,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
if (intval == 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"use 'none' to disable "
- "userquota/groupquota"));
+ "{user|group|project}quota"));
goto error;
}
} else {
@@ -3032,19 +3036,26 @@ out:
* convert the propname into parameters needed by kernel
* Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
* Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
+ * Eg: groupquota@staff -> ZFS_PROP_GROUPQUOTA, "", 1234
+ * Eg: groupused@staff -> ZFS_PROP_GROUPUSED, "", 1234
+ * Eg: projectquota@123 -> ZFS_PROP_PROJECTQUOTA, "", 123
+ * Eg: projectused@789 -> ZFS_PROP_PROJECTUSED, "", 789
*/
static int
userquota_propname_decode(const char *propname, boolean_t zoned,
zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
{
zfs_userquota_prop_t type;
- char *cp, *end;
- char *numericsid = NULL;
+ char *cp;
boolean_t isuser;
+ boolean_t isgroup;
+ boolean_t isproject;
+ struct passwd *pw;
+ struct group *gr;
domain[0] = '\0';
- *ridp = 0;
- /* Figure out the property type ({user|group}{quota|space}) */
+
+ /* Figure out the property type ({user|group|project}{quota|space}) */
for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
if (strncmp(propname, zfs_userquota_prop_prefixes[type],
strlen(zfs_userquota_prop_prefixes[type])) == 0)
@@ -3054,107 +3065,73 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
return (EINVAL);
*typep = type;
- isuser = (type == ZFS_PROP_USERQUOTA ||
- type == ZFS_PROP_USERUSED);
+ isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED ||
+ type == ZFS_PROP_USEROBJQUOTA ||
+ type == ZFS_PROP_USEROBJUSED);
+ isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED ||
+ type == ZFS_PROP_GROUPOBJQUOTA ||
+ type == ZFS_PROP_GROUPOBJUSED);
+ isproject = (type == ZFS_PROP_PROJECTQUOTA ||
+ type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTOBJQUOTA ||
+ type == ZFS_PROP_PROJECTOBJUSED);
cp = strchr(propname, '@') + 1;
- if (strchr(cp, '@')) {
+ if (isuser && (pw = getpwnam(cp)) != NULL) {
+ if (zoned && getzoneid() == GLOBAL_ZONEID)
+ return (ENOENT);
+ *ridp = pw->pw_uid;
+ } else if (isgroup && (gr = getgrnam(cp)) != NULL) {
+ if (zoned && getzoneid() == GLOBAL_ZONEID)
+ return (ENOENT);
+ *ridp = gr->gr_gid;
+ } else if (!isproject && strchr(cp, '@')) {
/*
* It's a SID name (eg "user@domain") that needs to be
* turned into S-1-domainID-RID.
*/
- int flag = 0;
- idmap_stat stat, map_stat;
- uid_t pid;
- idmap_rid_t rid;
- idmap_get_handle_t *gh = NULL;
-
- stat = idmap_get_create(&gh);
- if (stat != IDMAP_SUCCESS) {
- idmap_get_destroy(gh);
- return (ENOMEM);
- }
+ directory_error_t e;
+ char *numericsid = NULL;
+ char *end;
+
if (zoned && getzoneid() == GLOBAL_ZONEID)
return (ENOENT);
if (isuser) {
- stat = idmap_getuidbywinname(cp, NULL, flag, &pid);
- if (stat < 0)
- return (ENOENT);
- stat = idmap_get_sidbyuid(gh, pid, flag, &numericsid,
- &rid, &map_stat);
+ e = directory_sid_from_user_name(NULL,
+ cp, &numericsid);
} else {
- stat = idmap_getgidbywinname(cp, NULL, flag, &pid);
- if (stat < 0)
- return (ENOENT);
- stat = idmap_get_sidbygid(gh, pid, flag, &numericsid,
- &rid, &map_stat);
+ e = directory_sid_from_group_name(NULL,
+ cp, &numericsid);
}
- if (stat < 0) {
- idmap_get_destroy(gh);
- return (ENOENT);
- }
- stat = idmap_get_mappings(gh);
- idmap_get_destroy(gh);
-
- if (stat < 0) {
+ if (e != NULL) {
+ directory_error_free(e);
return (ENOENT);
}
if (numericsid == NULL)
return (ENOENT);
cp = numericsid;
- *ridp = rid;
- /* will be further decoded below */
- }
-
- if (strncmp(cp, "S-1-", 4) == 0) {
- /* It's a numeric SID (eg "S-1-234-567-89") */
(void) strlcpy(domain, cp, domainlen);
+ cp = strrchr(domain, '-');
+ *cp = '\0';
+ cp++;
+
errno = 0;
- if (*ridp == 0) {
- cp = strrchr(domain, '-');
- *cp = '\0';
- cp++;
- *ridp = strtoull(cp, &end, 10);
- } else {
- end = "";
- }
- if (numericsid) {
- free(numericsid);
- numericsid = NULL;
- }
+ *ridp = strtoull(cp, &end, 10);
+ free(numericsid);
+
if (errno != 0 || *end != '\0')
return (EINVAL);
- } else if (!isdigit(*cp)) {
- /*
- * It's a user/group name (eg "user") that needs to be
- * turned into a uid/gid
- */
- if (zoned && getzoneid() == GLOBAL_ZONEID)
- return (ENOENT);
- if (isuser) {
- struct passwd *pw;
- pw = getpwnam(cp);
- if (pw == NULL)
- return (ENOENT);
- *ridp = pw->pw_uid;
- } else {
- struct group *gr;
- gr = getgrnam(cp);
- if (gr == NULL)
- return (ENOENT);
- *ridp = gr->gr_gid;
- }
} else {
- /* It's a user/group ID (eg "12345"). */
+ /* It's a user/group/project ID (eg "12345"). */
+ char *end;
uid_t id = strtoul(cp, &end, 10);
- idmap_rid_t rid;
- char *mapdomain;
-
if (*end != '\0')
return (EINVAL);
- if (id > MAXUID) {
+ if (id > MAXUID && !isproject) {
/* It's an ephemeral ID. */
+ idmap_rid_t rid;
+ char *mapdomain;
+
if (idmap_id_to_numeric_domain_rid(id, isuser,
&mapdomain, &rid) != 0)
return (ENOENT);
@@ -3165,7 +3142,6 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
}
}
- ASSERT3P(numericsid, ==, NULL);
return (0);
}
@@ -3220,8 +3196,14 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
if (literal) {
(void) snprintf(propbuf, proplen, "%llu", propvalue);
} else if (propvalue == 0 &&
- (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
+ (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
+ type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
+ type == ZFS_PROP_PROJECTQUOTA || ZFS_PROP_PROJECTOBJQUOTA)) {
(void) strlcpy(propbuf, "none", proplen);
+ } else if (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
+ type == ZFS_PROP_USERUSED || type == ZFS_PROP_GROUPUSED ||
+ type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTQUOTA) {
+ zfs_nicenum(propvalue, propbuf, proplen);
} else {
zfs_nicenum(propvalue, propbuf, proplen);
}
@@ -4826,6 +4808,17 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
char errbuf[1024];
+ if ((errno == ENOTSUP &&
+ (type == ZFS_PROP_USEROBJUSED ||
+ type == ZFS_PROP_GROUPOBJUSED ||
+ type == ZFS_PROP_USEROBJQUOTA ||
+ type == ZFS_PROP_GROUPOBJQUOTA ||
+ type == ZFS_PROP_PROJECTOBJUSED ||
+ type == ZFS_PROP_PROJECTOBJQUOTA ||
+ type == ZFS_PROP_PROJECTUSED ||
+ type == ZFS_PROP_PROJECTQUOTA)))
+ break;
+
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN,
"cannot get used/quota for %s"), zc.zc_name);
diff --git a/usr/src/man/man1m/zfs.1m b/usr/src/man/man1m/zfs.1m
index b1ba39dd4c..99b34b5c36 100644
--- a/usr/src/man/man1m/zfs.1m
+++ b/usr/src/man/man1m/zfs.1m
@@ -148,6 +148,34 @@
.Oo Fl t Ar type Ns Oo , Ns Ar type Oc Ns ... Oc
.Ar filesystem Ns | Ns Ar snapshot
.Nm
+.Cm projectspace
+.Op Fl Hp
+.Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns ... Oc
+.Oo Fl s Ar field Oc Ns ...
+.Oo Fl S Ar field Oc Ns ...
+.Ar filesystem Ns | Ns Ar snapshot
+.Nm
+.Cm project
+.Oo Fl d Ns | Ns Fl r Ns Oc
+.Ar file Ns | Ns Ar directory Ns ...
+.Nm
+.Cm project
+.Fl C
+.Oo Fl kr Ns Oc
+.Ar file Ns | Ns Ar directory Ns ...
+.Nm
+.Cm project
+.Fl c
+.Oo Fl 0 Ns Oc
+.Oo Fl d Ns | Ns Fl r Ns Oc
+.Op Fl p Ar id
+.Ar file Ns | Ns Ar directory Ns ...
+.Nm
+.Cm project
+.Op Fl p Ar id
+.Oo Fl rs Ns Oc
+.Ar file Ns | Ns Ar directory Ns ...
+.Nm
.Cm mount
.Nm
.Cm mount
@@ -847,6 +875,24 @@ forms:
.Sy S-1-123-456-789
.Pc
.El
+.It Sy userobjused Ns @ Ns Em user
+The
+.Sy userobjused
+property is similar to
+.Sy userused
+but instead it counts the number of objects consumed by a user.
+This property counts all objects allocated on behalf of the user, it may
+differ from the results of system tools such as
+.Nm df Fl i .
+.Pp
+When the property
+.Sy xattr=on
+is set on a file system additional objects will be created per-file to store
+extended attributes.
+These additional objects are reflected in the
+.Sy userobjused
+value and are counted against the user's
+.Sy userobjquota .
.It Sy userrefs
This property is set to the number of user holds on this snapshot.
User holds are set by using the
@@ -866,6 +912,66 @@ The root user, or a user who has been granted the
privilege with
.Nm zfs Cm allow ,
can access all groups' usage.
+.It Sy groupobjused Ns @ Ns Em group
+The number of objects consumed by the specified group in this dataset.
+Multiple objects may be charged to the group for each file when extended
+attributes are in use.
+See the
+.Sy userobjused Ns @ Ns Em user
+property for more information.
+.Pp
+Unprivileged users can only access their own groups' space usage.
+The root user, or a user who has been granted the
+.Sy groupobjused
+privilege with
+.Nm zfs Cm allow ,
+can access all groups' usage.
+.It Sy projectused Ns @ Ns Em project
+The amount of space consumed by the specified project in this dataset.
+Project is identified via the project identifier (ID) that is object-based
+numeral attribute.
+An object can inherit the project ID from its parent object (if the
+parent has the flag of inherit project ID that can be set and changed via
+.Nm zfs project Fl s )
+when being created.
+The privileged user can set and change object's project
+ID via
+.Nm zfs project Fl s
+anytime.
+Space is charged to the project of each file, as displayed by
+.Nm zfs project .
+See the
+.Sy userused Ns @ Ns Em user
+property for more information.
+.Pp
+The root user, or a user who has been granted the
+.Sy projectused
+privilege with
+.Nm zfs allow ,
+can access all projects' usage.
+.It Sy projectobjused Ns @ Ns Em project
+The
+.Sy projectobjused
+is similar to
+.Sy projectused
+but instead it counts the number of objects consumed by project.
+When the property
+.Sy xattr=on
+is set on a fileset, ZFS will create additional objects per-file to store
+extended attributes.
+These additional objects are reflected in the
+.Sy projectobjused
+value and are counted against the project's
+.Sy projectobjquota .
+See the
+.Sy userobjused Ns @ Ns Em user
+property for more information.
+.Pp
+The root user, or a user who has been granted the
+.Sy projectobjused
+privilege with
+.Nm zfs allow ,
+can access all projects' objects usage.
.It Sy volblocksize
For volumes, specifies the block size of the volume.
The
@@ -1409,6 +1515,15 @@ symbol, using one of the following forms:
.Sy S-1-123-456-789
.Pc
.El
+.It Sy userobjquota@ Ns Em user Ns = Ns Em size Ns | Ns Sy none
+The
+.Sy userobjquota
+is similar to
+.Sy userquota
+but it limits the number of objects a user can create.
+Please refer to
+.Sy userobjused
+for more information about how objects are counted.
.It Sy groupquota@ Ns Em group Ns = Ns Em size Ns | Ns Sy none
Limits the amount of space consumed by the specified group.
Group space consumption is identified by the
@@ -1421,6 +1536,38 @@ The root user, or a user who has been granted the
privilege with
.Nm zfs Cm allow ,
can get and set all groups' quotas.
+.It Sy groupobjquota@ Ns Em group Ns = Ns Em size Ns | Ns Sy none
+The
+.Sy groupobjquota
+is similar to
+.Sy groupquota
+but it limits the number of objects a group can consume.
+Please refer to
+.Sy userobjused
+for more information about how objects are counted.
+.It Sy projectquota@ Ns Em project Ns = Ns Em size Ns | Ns Sy none
+Limits the amount of space consumed by the specified project.
+Project space consumption is identified by the
+.Sy projectused@ Ns Em project
+property.
+Please refer to
+.Sy projectused
+for more information about how project is identified and set or changed.
+.Pp
+The root user, or a user who has been granted the
+.Sy projectquota
+privilege with
+.Nm zfs allow ,
+can access all projects' quotas.
+.It Sy projectobjquota@ Ns Em project Ns = Ns Em size Ns | Ns Sy none
+The
+.Sy projectobjquota
+is similar to
+.Sy projectquota
+but it limits the number of objects a project can consume.
+Please refer to
+.Sy userobjused
+for more information about how objects are counted.
.It Sy readonly Ns = Ns Sy on Ns | Ns Sy off
Controls whether this dataset can be modified.
The default value is
@@ -2830,9 +2977,11 @@ Upgrade the specified file system and all descendent file systems.
Displays space consumed by, and quotas on, each user in the specified filesystem
or snapshot.
This corresponds to the
-.Sy userused@ Ns Em user
+.Sy userused@ Ns Em user ,
+.Sy userobjused@ Ns Em user ,
+.Sy userquota@ Ns Em user,
and
-.Sy userquota@ Ns Em user
+.Sy userobjquota@ Ns Em user
properties.
.Bl -tag -width "-H"
.It Fl H
@@ -2915,6 +3064,118 @@ except that the default types to display are
.Fl t Sy posixgroup Ns \&, Ns Sy smbgroup .
.It Xo
.Nm
+.Cm projectspace
+.Op Fl Hp
+.Oo Fl o Ar field Ns Oo , Ns Ar field Oc Ns ... Oc
+.Oo Fl s Ar field Oc Ns ...
+.Oo Fl S Ar field Oc Ns ...
+.Ar filesystem Ns | Ns Ar snapshot
+.Xc
+Displays space consumed by, and quotas on, each project in the specified
+filesystem or snapshot.
+This subcommand is identical to
+.Nm zfs Cm userspace ,
+except that the project identifier is numeral, not name.
+So need neither the option
+.Sy -i
+for SID to POSIX ID nor
+.Sy -n
+for numeric ID, nor
+.Sy -t
+for types.
+.It Xo
+.Nm
+.Cm project
+.Oo Fl d Ns | Ns Fl r Ns Oc
+.Ar file Ns | Ns Ar directory Ns ...
+.Xc
+List project identifier (ID) and inherit flag of files or directories.
+.Bl -tag -width "-d"
+.It Fl d
+Show the directory project ID and inherit flag, not its children.
+It will overwrite the former specified
+.Fl r
+option.
+.It Fl r
+Show on subdirectories recursively.
+It will overwrite the former specified
+.Fl d
+option.
+.El
+.It Xo
+.Nm
+.Cm project
+.Fl C
+.Oo Fl kr Ns Oc
+.Ar file Ns | Ns Ar directory Ns ...
+.Xc
+Clear project inherit flag and/or ID on the files or directories.
+.Bl -tag -width "-k"
+.It Fl k
+Keep the project ID unchanged.
+If not specified, the project ID will be reset as zero.
+.It Fl r
+Clear on subdirectories recursively.
+.El
+.It Xo
+.Nm
+.Cm project
+.Fl c
+.Oo Fl 0 Ns Oc
+.Oo Fl d Ns | Ns Fl r Ns Oc
+.Op Fl p Ar id
+.Ar file Ns | Ns Ar directory Ns ...
+.Xc
+Check project ID and inherit flag on the files or directories, report the
+entries without project inherit flag or with different project IDs from the
+specified (via
+.Fl p
+option) value or the target directory's project ID.
+.Bl -tag -width "-0"
+.It Fl 0
+Print file name with a trailing NUL instead of newline (by default), like
+"find -print0".
+.It Fl d
+Check the directory project ID and inherit flag, not its children.
+It will overwrite the former specified
+.Fl r
+option.
+.It Fl p
+Specify the referenced ID for comparing with the target files or directories'
+project IDs.
+If not specified, the target (top) directory's project ID will be used as the
+referenced one.
+.It Fl r
+Check on subdirectories recursively.
+It will overwrite the former specified
+.Fl d
+option.
+.El
+.It Xo
+.Nm
+.Cm project
+.Op Fl p Ar id
+.Oo Fl rs Ns Oc
+.Ar file Ns | Ns Ar directory Ns ...
+.Xc
+Set project ID and/or inherit flag on the files or directories.
+.Bl -tag -width "-p"
+.It Fl p
+Set the files' or directories' project ID with the given value.
+.It Fl r
+Set on subdirectories recursively.
+.It Fl s
+Set project inherit flag on the given files or directories.
+It is usually used for setup tree quota on the directory target with
+.Fl r
+option specified together.
+When setup tree quota, by default the directory's project ID will be set to
+all its descendants unless you specify the project ID via
+.Fl p
+option explicitly.
+.El
+.It Xo
+.Nm
.Cm mount
.Xc
Displays all ZFS file systems currently mounted.
@@ -3799,6 +4060,11 @@ userprop other Allows changing any user property
userquota other Allows accessing any userquota@...
property
userused other Allows reading any userused@... property
+projectobjquota other Allows accessing any projectobjquota@...
+ property
+projectquota other Allows accessing any projectquota@... property
+projectobjused other Allows reading any projectobjused@... property
+projectused other Allows reading any projectused@... property
aclinherit property
aclmode property
diff --git a/usr/src/man/man2/fork.2 b/usr/src/man/man2/fork.2
index 69b180861a..35c72f6034 100644
--- a/usr/src/man/man2/fork.2
+++ b/usr/src/man/man2/fork.2
@@ -44,7 +44,7 @@
.\" Portions Copyright (c) 1994, X/Open Company Limited. All Rights Reserved.
.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved.
.\"
-.TH FORK 2 "Nov 26, 2017"
+.TH FORK 2 "Aug 18, 2019"
.SH NAME
fork, fork1, forkall, forkx, forkallx \- create a new process
.SH SYNOPSIS
@@ -425,8 +425,8 @@ child. The application should use \fBpthread_atfork\fR(3C) to ensure safety
with respect to this deadlock. Should there be any outstanding mutexes
throughout the process, the application should call \fBpthread_atfork()\fR to
wait for and acquire those mutexes prior to calling \fBfork()\fR,
-\fBfork1()\fR, or \fBforkx()\fR. See "MT-Level of Libraries" on the
-\fBattributes\fR(5) manual page.
+\fBfork1()\fR, or \fBforkx()\fR. See "MT-Level" on the \fBattributes\fR(5)
+manual page.
.sp
.LP
The \fBpthread_atfork()\fR mechanism is used to protect the locks that
diff --git a/usr/src/man/man3/Intro.3 b/usr/src/man/man3/Intro.3
index 6de3b60526..fa0469da2b 100644
--- a/usr/src/man/man3/Intro.3
+++ b/usr/src/man/man3/Intro.3
@@ -1,15 +1,14 @@
'\" te
-.\" Copyright 2017 Peter Tribble
+.\" Copyright 2019 Peter Tribble
.\" Copyright 2015 Joyent, Inc.
.\" Copyright (c) 2009, Sun Microsystems, Inc. All Rights Reserved.
.\" 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]
-.TH INTRO 3 "Nov 26, 2017"
+.TH INTRO 3 "Aug 19, 2019"
.SH NAME
Intro, intro \- introduction to functions and libraries
.SH DESCRIPTION
-.LP
This section describes functions found in various Solaris libraries, other than
those functions described in Section 2 of this manual that directly invoke UNIX
system primitives. Function declarations can be obtained from the
@@ -20,7 +19,6 @@ volumes as described below. The first volume contains pages describing the
contents of each shared library and each header used by the functions, macros,
and external variables described in the remaining volumes.
.SS "Library Interfaces and Headers"
-.LP
This volume describes the contents of each shared library and each header used
by functions, macros, and external variables described in the remaining
volumes.
@@ -58,7 +56,6 @@ development system; they do have to be present on the target execution system.
.RE
.SS "Basic Library Functions"
-.LP
The functions described in this volume are the core C library functions that
are basic to application development.
.sp
@@ -117,7 +114,6 @@ link with, respectively, \fBlibmalloc\fR, \fBlibbsdmalloc\fR,
.RE
.SS "Networking Library Functions"
-.LP
The functions described in this volume comprise the various networking
libraries.
.sp
@@ -313,7 +309,6 @@ portability is not required, the sockets interfaces in \fBlibsocket\fR and
TLI APIs, the \fBXTI\fR interfaces (available with \fBlibxnet\fR) are
recommended over the \fBTLI\fR interfaces (available with \fBlibnsl\fR).
.SS "Curses Library Functions"
-.LP
The functions described in this volume comprise the libraries that provide
graphics and character screen updating capabilities.
.sp
@@ -396,7 +391,6 @@ X/Open Extended Curses specification. See \fBlibcurses\fR(3XCURSES).
.RE
.SS "Extended Library Functions"
-.LP
The functions described in this volume comprise the following specialized
libraries:
.sp
@@ -830,7 +824,7 @@ automatically linked by the C compilation system. Specify \fB-lrsm\fR on the
.ad
.sp .6
.RS 4n
-These functions constitute the object-caching memory allocation library,
+These functions constitute the service configuration facility library,
\fBlibscf\fR. This library is implemented as a shared object, \fBlibscf.so\fR,
but is not automatically linked by the C compilation system. Specify
\fB-lscf\fR on the \fBcc\fR command line to link with this library. See
@@ -965,7 +959,6 @@ automatically linked by the C compilation system. Specify \fB-lvolmgt\fR on the
.RE
.SH DEFINITIONS
-.LP
A character is any bit pattern able to fit into a byte on the machine. In some
international languages, however, a "character" might require more than one
byte, and is represented in multi-bytes.
@@ -982,7 +975,6 @@ return pointers return \fINULL\fR to indicate an error. The macro \fINULL\fR is
defined in <\fBstdio.h\fR>. Types of the form \fBsize_t\fR are defined in the
appropriate headers.
.SH MULTITHREADED APPLICATIONS
-.LP
Both POSIX threads and Solaris threads can be used within the same application.
Their implementations are completely compatible with each other; however, only
POSIX threads guarantee portability to other POSIX-conforming environments.
@@ -1046,7 +1038,6 @@ and libraries manual pages (see \fBattributes\fR(5)). If a manual page does not
state explicitly that an interface is MT-Safe, the user should assume that the
interface is unsafe.
.SH REALTIME APPLICATIONS
-.LP
The environment variable \fBLD_BIND_NOW\fR must be set to a non-null value to
enable early binding. Refer to the "When Relocations are Processed" chapter in
\fILinker and Libraries Guide\fR for additional information.
@@ -1079,7 +1070,6 @@ shared libraries
.RE
.SH ACKNOWLEDGMENTS
-.LP
Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission to
reproduce portions of its copyrighted documentation. Original documentation
from The Open Group can be obtained online at
@@ -1107,7 +1097,6 @@ http://www.opengroup.org/unix/online.html\&.
.LP
This notice shall appear on any product containing this material.
.SH SEE ALSO
-.LP
\fBar\fR(1), \fBld\fR(1), \fBfork\fR(2), \fBstdio\fR(3C), \fBattributes\fR(5),
\fBstandards\fR(5)
.sp
@@ -1120,7 +1109,6 @@ This notice shall appear on any product containing this material.
.LP
\fIANSI C Programmer's Guide\fR
.SH DIAGNOSTICS
-.LP
For functions that return floating-point values, error handling varies
according to compilation mode. Under the \fB-Xt\fR (default) option to
\fBcc\fR, these functions return the conventional values \fB0\fR,
@@ -1130,7 +1118,6 @@ arguments or when the value is not representable. In the \fB-Xa\fR and
\fB\(+-HUGE\fR\&. (\fBHUGE_VAL\fR and \fBHUGE\fR are defined in \fBmath.h\fR to
be infinity and the largest-magnitude single-precision number, respectively.)
.SH NOTES
-.LP
None of the functions, external variables, or macros should be redefined in the
user's programs. Any other name can be redefined without affecting the behavior
of other library functions, but such redefinition might conflict with a
diff --git a/usr/src/man/man4/policy.conf.4 b/usr/src/man/man4/policy.conf.4
index 65b6d04494..4c933fbfc4 100644
--- a/usr/src/man/man4/policy.conf.4
+++ b/usr/src/man/man4/policy.conf.4
@@ -13,7 +13,7 @@ policy.conf \- configuration file for security policy
.fi
.SH DESCRIPTION
-.sp
+
.LP
The \fBpolicy.conf\fR file provides the security policy configuration for
user-level attributes. Each entry consists of a key/value pair in the form:
@@ -132,10 +132,10 @@ another algorithm, such as \fBCRYPT_DEFAULT=1\fR for BSD and Linux MD5.
.ad
.sp .6
.RS 4n
-Specify the default algorithm for new passwords. The Solaris default is the
-traditional UNIX algorithm. This is not listed in \fBcrypt.conf\fR(4) since it
-is internal to \fBlibc\fR. The reserved name \fB__unix__\fR is used to refer to
-it.
+Specify the default algorithm for new passwords. The Solaris default was once
+the traditional UNIX algorithm. This is not listed in \fBcrypt.conf\fR(4) since
+it is internal to \fBlibc\fR. The reserved name \fB__unix__\fR is used to refer
+to it.
.RE
.sp
@@ -186,7 +186,7 @@ specifications are unaffected by any future addition of privileges that might
occur.
.SH FILES
-.sp
+
.ne 2
.na
\fB\fB/etc/user_attr\fR\fR
@@ -223,7 +223,7 @@ Defines policy for the system.
.RE
.SH ATTRIBUTES
-.sp
+
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -239,13 +239,13 @@ Interface Stability Committed
.TE
.SH SEE ALSO
-.sp
+
.LP
\fBlogin\fR(1), \fBpfexec\fR(1), \fBchkauthattr\fR(3SECDB),
\fBgetexecuser\fR(3SECDB), \fBauth_attr\fR(4), \fBcrypt.conf\fR(4),
\fBprof_attr\fR(4), \fBuser_attr\fR(4), \fBattributes\fR(5),
\fBprivileges\fR(5)
.SH NOTES
-.sp
+
.LP
The \fIconsole user\fR is defined as the owner of \fB/dev/console\fR.
diff --git a/usr/src/man/man5/zpool-features.5 b/usr/src/man/man5/zpool-features.5
index 88efb3d543..21a5369799 100644
--- a/usr/src/man/man5/zpool-features.5
+++ b/usr/src/man/man5/zpool-features.5
@@ -708,7 +708,7 @@ vdevs from an allocation class are removed.
.sp
.ne 2
.na
-\fB\fBcom.datto:encryption\fR\fR
+\fB\fBencryption\fR\fR
.ad
.RS 4n
.TS
@@ -744,6 +744,69 @@ running one to be immediately restarted from the beginning.
This feature becomes \fBactive\fR once a resilver has been deferred, and
returns to being \fBenabled\fR when the deferred resilver begins.
+.RE
+
+.sp
+.ne 2
+.na
+\fBuserobj_accounting\fR
+.ad
+.RS 4n
+.TS
+l l .
+GUID org.zfsonlinux:userobj_accounting
+READ\-ONLY COMPATIBLE yes
+DEPENDENCIES extensible_dataset
+.TE
+
+This feature allows administrators to account the object usage information
+by user and group.
+
+This feature becomes \fBactive\fR as soon as it is enabled and will never
+return to being \fBenabled\fR.
+Each filesystem will be upgraded automatically when remounted, or when new
+files are created under that filesystem.
+The upgrade can also be started manually on filesystems by running
+`zfs set version=current <pool/fs>`.
+The upgrade process runs in the background and may take a while to complete
+for filesystems containing a large number of files.
+.RE
+
+.sp
+.ne 2
+.na
+\fBproject_quota\fR
+.ad
+.RS 4n
+.TS
+l l .
+GUID org.zfsonlinux:project_quota
+READ\-ONLY COMPATIBLE yes
+DEPENDENCIES extensible_dataset
+.TE
+
+This feature allows administrators to account the space and object usage
+information against the project identifier (ID).
+
+The project ID is a new object-based attribute.
+When upgrading an existing filesystem, an object without a project ID
+attribute will be assigned a zero project ID.
+After this feature is enabled, a newly created object will inherit
+its parent directory's project ID if the parent's inherit flag is set (via
+\fBzfs project [-s|-C]\fR).
+Otherwise, the new object's project ID will be set as zero.
+An object's project ID can be changed at any time by the owner (or privileged
+user) via \fBzfs project -p $prjid\fR.
+
+This feature will become \fBactive\fR as soon as it is enabled and will never
+return to being \fBdisabled\fR.
+Each filesystem will be upgraded automatically when remounted or when a new file
+is created under that filesystem.
+The upgrade can also be triggered on filesystems via `zfs set version=current
+<pool/fs>`.
+The upgrade process runs in the background and may take a while to complete
+for the filesystems containing a large number of files.
+.RE
.SH "SEE ALSO"
\fBzfs\fR(1M), \fBzpool\fR(1M)
diff --git a/usr/src/man/man9s/ddi_device_acc_attr.9s b/usr/src/man/man9s/ddi_device_acc_attr.9s
index ff48e71a97..0730aadca3 100644
--- a/usr/src/man/man9s/ddi_device_acc_attr.9s
+++ b/usr/src/man/man9s/ddi_device_acc_attr.9s
@@ -1,9 +1,10 @@
'\" te
.\" Copyright (c) 2007, Sun Microsystems, Inc., All Rights Reserved
+.\" Copyright 2019 Joyent, Inc.
.\" 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]
-.TH DDI_DEVICE_ACC_ATTR 9S "May 13, 2007"
+.TH DDI_DEVICE_ACC_ATTR 9S "August 18, 2019"
.SH NAME
ddi_device_acc_attr \- data access attributes structure
.SH SYNOPSIS
@@ -14,16 +15,13 @@ ddi_device_acc_attr \- data access attributes structure
.fi
.SH INTERFACE LEVEL
-.sp
.LP
Solaris DDI specific (Solaris DDI)
.SH DESCRIPTION
-.sp
.LP
The \fBddi_device_acc_attr\fR structure describes the data access
characteristics and requirements of the device.
.SH STRUCTURE MEMBERS
-.sp
.in +2
.nf
ushort_t devacc_attr_version;
@@ -36,7 +34,7 @@ uchar_t devacc_attr_access;
.sp
.LP
The \fBdevacc_attr_version\fR member identifies the version number of this
-structure. The current version number is \fBDDI_DEVICE_ATTR_V0\fR.
+structure. The current version number is \fBDDI_DEVICE_ATTR_V1\fR.
.sp
.LP
The \fBdevacc_attr_endian_flags\fR member describes the endian characteristics
@@ -209,7 +207,6 @@ additional fault-recovery code.
.RE
.SH EXAMPLES
-.sp
.LP
The following examples illustrate the use of device register address mapping
setup functions and different data access functions.
@@ -219,8 +216,8 @@ setup functions and different data access functions.
.sp
.LP
This example demonstrates the use of the \fBddi_device_acc_attr()\fR structure
-in \fBddi_regs_map_setup\fR(9F). It also shows the use of \fBddi_getw\fR(9F)
-and \fBddi_putw\fR(9F) functions in accessing the register contents.
+in \fBddi_regs_map_setup\fR(9F). It also shows the use of \fBddi_get16\fR(9F)
+and \fBddi_put16\fR(9F) functions in accessing the register contents.
.sp
.in +2
@@ -240,9 +237,10 @@ ddi_acc_handle_t handle;
* setup the device attribute structure for little endian,
* strict ordering and 16-bit word access.
*/
-dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V1;
dev_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
dev_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
+dev_attr.devacc_attr_access = DDI_DEFAULT_ACC;
/*
* set up the device registers address mapping
@@ -251,11 +249,11 @@ ddi_regs_map_setup(dip, rnumber, (caddr_t *)&dev_addr, offset, len,
&dev_attr, &handle);
/* read a 16-bit word command register from the device */
-dev_command = ddi_getw(handle, dev_addr);
+dev_command = ddi_get16(handle, dev_addr);
dev_command |= DEV_INTR_ENABLE;
/* store a new value back to the device command register */
-ddi_putw(handle, dev_addr, dev_command);
+ddi_put16(handle, dev_addr, dev_command);
.fi
.in -2
@@ -288,9 +286,10 @@ uchar_t buf[256];
* setup the device attribute structure for never swap,
* unordered and 32-bit word access.
*/
-dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V1;
dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
dev_attr.devacc_attr_dataorder = DDI_UNORDERED_OK_ACC;
+dev_attr.devacc_attr_access = DDI_DEFAULT_ACC;
/*
* map in the RGB big-endian aperture
@@ -304,7 +303,7 @@ ddi_regs_map_setup(dip, 1, (caddr_t *)&dev_addr, 96*1024, 32*1024,
* Write to the screen buffer
* first 1K bytes words, each size 4 bytes
*/
-ddi_rep_putl(handle, buf, dev_addr, 256, DDI_DEV_AUTOINCR);
+ddi_rep_put32(handle, buf, dev_addr, 256, DDI_DEV_AUTOINCR);
.fi
.in -2
@@ -336,28 +335,28 @@ uchar_t buf[256];
* setup the device attribute structure for never swap,
* strict ordering and 32-bit word access.
*/
-dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
+dev_attr.devacc_attr_version = DDI_DEVICE_ATTR_V1;
dev_attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
dev_attr.devacc_attr_dataorder= DDI_STRICTORDER_ACC;
+dev_attr.devacc_attr_access = DDI_DEFAULT_ACC;
ddi_regs_map_setup(dip, 1, (caddr_t *)&dev_blkp, 0, 0,
&dev_attr, &handle);
/* write command to the 16-bit command register */
-ddi_putw(handle, &dev_blkp->d_command, START_XFER);
+ddi_put16(handle, &dev_blkp->d_command, START_XFER);
/* Read the 16-bit status register */
-status = ddi_getw(handle, &dev_blkp->d_status);
+status = ddi_get16(handle, &dev_blkp->d_status);
if (status & DATA_READY)
/* Read 1K bytes off the 32-bit data register */
- ddi_rep_getl(handle, buf, &dev_blkp->d_data,
+ ddi_rep_get32(handle, buf, &dev_blkp->d_data,
256, DDI_DEV_NO_AUTOINCR);
.fi
.in -2
.SH ATTRIBUTES
-.sp
.LP
See \fBattributes\fR(5) for descriptions of the following attributes:
.sp
@@ -373,10 +372,9 @@ Interface Stability Committed
.TE
.SH SEE ALSO
-.sp
.LP
-\fBattributes\fR(5), \fBddi_fm_acc_err_get\fR(9F), \fBddi_getw\fR(9F),
-\fBddi_putw\fR(9F), \fBddi_regs_map_setup\fR(9F)
+\fBattributes\fR(5), \fBddi_fm_acc_err_get\fR(9F), \fBddi_get16\fR(9F),
+\fBddi_put16\fR(9F), \fBddi_regs_map_setup\fR(9F)
.sp
.LP
\fIWriting Device Drivers\fR
diff --git a/usr/src/pkg/manifests/system-test-zfstest.mf b/usr/src/pkg/manifests/system-test-zfstest.mf
index 53f9b77f35..a4448972d6 100644
--- a/usr/src/pkg/manifests/system-test-zfstest.mf
+++ b/usr/src/pkg/manifests/system-test-zfstest.mf
@@ -137,6 +137,7 @@ dir path=opt/zfs-tests/tests/functional/pool_checkpoint
dir path=opt/zfs-tests/tests/functional/pool_names
dir path=opt/zfs-tests/tests/functional/poolversion
dir path=opt/zfs-tests/tests/functional/privilege
+dir path=opt/zfs-tests/tests/functional/projectquota
dir path=opt/zfs-tests/tests/functional/quota
dir path=opt/zfs-tests/tests/functional/redundancy
dir path=opt/zfs-tests/tests/functional/refquota
@@ -154,6 +155,7 @@ dir path=opt/zfs-tests/tests/functional/snapused
dir path=opt/zfs-tests/tests/functional/sparse
dir path=opt/zfs-tests/tests/functional/threadsappend
dir path=opt/zfs-tests/tests/functional/truncate
+dir path=opt/zfs-tests/tests/functional/upgrade
dir path=opt/zfs-tests/tests/functional/userquota
dir path=opt/zfs-tests/tests/functional/utils_test
dir path=opt/zfs-tests/tests/functional/vdev_zaps
@@ -2632,6 +2634,51 @@ file path=opt/zfs-tests/tests/functional/privilege/cleanup mode=0555
file path=opt/zfs-tests/tests/functional/privilege/privilege_001_pos mode=0555
file path=opt/zfs-tests/tests/functional/privilege/privilege_002_pos mode=0555
file path=opt/zfs-tests/tests/functional/privilege/setup mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/cleanup mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectid_001_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectid_002_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectid_003_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota.cfg \
+ mode=0444
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_001_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_002_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_003_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_004_neg \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_005_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_006_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_007_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_008_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectquota_009_pos \
+ mode=0555
+file \
+ path=opt/zfs-tests/tests/functional/projectquota/projectquota_common.kshlib \
+ mode=0444
+file path=opt/zfs-tests/tests/functional/projectquota/projectspace_001_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectspace_002_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectspace_003_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projectspace_004_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projecttree_001_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projecttree_002_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/projecttree_003_neg \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/projectquota/setup mode=0555
file path=opt/zfs-tests/tests/functional/quota/cleanup mode=0555
file path=opt/zfs-tests/tests/functional/quota/quota.cfg mode=0444
file path=opt/zfs-tests/tests/functional/quota/quota.kshlib mode=0444
@@ -2920,11 +2967,21 @@ file path=opt/zfs-tests/tests/functional/truncate/setup mode=0555
file path=opt/zfs-tests/tests/functional/truncate/truncate.cfg mode=0444
file path=opt/zfs-tests/tests/functional/truncate/truncate_001_pos mode=0555
file path=opt/zfs-tests/tests/functional/truncate/truncate_002_pos mode=0555
+file path=opt/zfs-tests/tests/functional/upgrade/cleanup mode=0555
+file path=opt/zfs-tests/tests/functional/upgrade/setup mode=0555
+file path=opt/zfs-tests/tests/functional/upgrade/upgrade_common.kshlib \
+ mode=0444
+file path=opt/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos \
+ mode=0555
+file path=opt/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos \
+ mode=0555
file path=opt/zfs-tests/tests/functional/userquota/cleanup mode=0555
file path=opt/zfs-tests/tests/functional/userquota/groupspace_001_pos \
mode=0555
file path=opt/zfs-tests/tests/functional/userquota/groupspace_002_pos \
mode=0555
+file path=opt/zfs-tests/tests/functional/userquota/groupspace_003_pos \
+ mode=0555
file path=opt/zfs-tests/tests/functional/userquota/setup mode=0555
file path=opt/zfs-tests/tests/functional/userquota/userquota.cfg mode=0444
file path=opt/zfs-tests/tests/functional/userquota/userquota_001_pos mode=0555
@@ -2939,10 +2996,12 @@ file path=opt/zfs-tests/tests/functional/userquota/userquota_009_pos mode=0555
file path=opt/zfs-tests/tests/functional/userquota/userquota_010_pos mode=0555
file path=opt/zfs-tests/tests/functional/userquota/userquota_011_pos mode=0555
file path=opt/zfs-tests/tests/functional/userquota/userquota_012_neg mode=0555
+file path=opt/zfs-tests/tests/functional/userquota/userquota_013_pos mode=0555
file path=opt/zfs-tests/tests/functional/userquota/userquota_common.kshlib \
mode=0444
file path=opt/zfs-tests/tests/functional/userquota/userspace_001_pos mode=0555
file path=opt/zfs-tests/tests/functional/userquota/userspace_002_pos mode=0555
+file path=opt/zfs-tests/tests/functional/userquota/userspace_003_pos mode=0555
file path=opt/zfs-tests/tests/functional/utils_test/cleanup mode=0555
file path=opt/zfs-tests/tests/functional/utils_test/setup mode=0555
file path=opt/zfs-tests/tests/functional/utils_test/utils_test.cfg mode=0444
diff --git a/usr/src/test/zfs-tests/cmd/mkfiles/mkfiles.c b/usr/src/test/zfs-tests/cmd/mkfiles/mkfiles.c
index 58c7d5f509..8fb3227d65 100644
--- a/usr/src/test/zfs-tests/cmd/mkfiles/mkfiles.c
+++ b/usr/src/test/zfs-tests/cmd/mkfiles/mkfiles.c
@@ -36,6 +36,9 @@ main(int argc, char **argv)
{
unsigned int numfiles = 0;
unsigned int first_file = 0;
+ unsigned int i;
+ char buf[MAXPATHLEN];
+
if (argc < 3 || argc > 4)
usage("Invalid number of arguments", -1);
@@ -48,8 +51,7 @@ main(int argc, char **argv)
if (numfiles < first_file)
usage("First file larger than last file", -3);
- char buf[MAXPATHLEN];
- for (unsigned int i = first_file; i <= numfiles; i++) {
+ for (i = first_file; i < first_file + numfiles; i++) {
int fd;
(void) snprintf(buf, MAXPATHLEN, "%s%u", argv[1], i);
if ((fd = open(buf, O_CREAT | O_EXCL, O_RDWR)) == -1) {
diff --git a/usr/src/test/zfs-tests/runfiles/delphix.run b/usr/src/test/zfs-tests/runfiles/delphix.run
index c81678d933..8eb3862220 100644
--- a/usr/src/test/zfs-tests/runfiles/delphix.run
+++ b/usr/src/test/zfs-tests/runfiles/delphix.run
@@ -544,6 +544,15 @@ tests = ['poolversion_001_pos', 'poolversion_002_pos']
[/opt/zfs-tests/tests/functional/privilege]
tests = ['privilege_001_pos', 'privilege_002_pos']
+[/opt/zfs-tests/tests/functional/projectquota]
+tests = ['projectid_001_pos', 'projectid_002_pos', 'projectid_003_pos',
+ 'projectquota_001_pos', 'projectquota_002_pos', 'projectquota_003_pos',
+ 'projectquota_004_neg', 'projectquota_005_pos', 'projectquota_006_pos',
+ 'projectquota_007_pos', 'projectquota_008_pos', 'projectquota_009_pos',
+ 'projectspace_001_pos', 'projectspace_002_pos', 'projectspace_003_pos',
+ 'projectspace_004_pos',
+ 'projecttree_002_pos', 'projecttree_003_neg' ]
+
[/opt/zfs-tests/tests/functional/quota]
tests = ['quota_001_pos', 'quota_002_pos', 'quota_003_pos', 'quota_004_pos',
'quota_005_pos', 'quota_006_neg']
@@ -645,13 +654,18 @@ tests = ['threadsappend_001_pos']
[/opt/zfs-tests/tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos']
+[/opt/zfs-tests/tests/functional/upgrade]
+tests = ['upgrade_userobj_001_pos', 'upgrade_projectquota_001_pos']
+
[/opt/zfs-tests/tests/functional/userquota]
-tests = ['groupspace_001_pos', 'groupspace_002_pos', 'userquota_001_pos',
- 'userquota_002_pos', 'userquota_003_pos', 'userquota_004_pos',
- 'userquota_005_neg', 'userquota_006_pos', 'userquota_007_pos',
- 'userquota_008_pos', 'userquota_009_pos', 'userquota_010_pos',
- 'userquota_011_pos', 'userquota_012_neg', 'userspace_001_pos',
- 'userspace_002_pos']
+tests = [
+ 'userquota_001_pos', 'userquota_002_pos', 'userquota_003_pos',
+ 'userquota_004_pos', 'userquota_005_neg', 'userquota_006_pos',
+ 'userquota_007_pos', 'userquota_008_pos', 'userquota_009_pos',
+ 'userquota_010_pos', 'userquota_011_pos', 'userquota_012_neg',
+ 'userquota_013_pos',
+ 'userspace_001_pos', 'userspace_002_pos', 'userspace_003_pos',
+ 'groupspace_001_pos', 'groupspace_002_pos', 'groupspace_003_pos' ]
[/opt/zfs-tests/tests/functional/utils_test]
tests = ['utils_test_001_pos', 'utils_test_002_pos', 'utils_test_003_pos',
diff --git a/usr/src/test/zfs-tests/runfiles/omnios.run b/usr/src/test/zfs-tests/runfiles/omnios.run
index 2df5cd612a..e8120a415f 100644
--- a/usr/src/test/zfs-tests/runfiles/omnios.run
+++ b/usr/src/test/zfs-tests/runfiles/omnios.run
@@ -162,7 +162,7 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos',
'zfs_mount_004_pos', 'zfs_mount_005_pos', 'zfs_mount_006_pos',
'zfs_mount_007_pos', 'zfs_mount_008_pos', 'zfs_mount_009_neg',
'zfs_mount_010_neg', 'zfs_mount_011_neg', 'zfs_mount_012_neg',
- 'zfs_mount_all_001_pos', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints'
+ 'zfs_mount_all_001_pos', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints',
'zfs_mount_encrypted']
[/opt/zfs-tests/tests/functional/cli_root/zfs_program]
@@ -544,6 +544,15 @@ tests = ['poolversion_001_pos', 'poolversion_002_pos']
[/opt/zfs-tests/tests/functional/privilege]
tests = ['privilege_001_pos', 'privilege_002_pos']
+[/opt/zfs-tests/tests/functional/projectquota]
+tests = ['projectid_001_pos', 'projectid_002_pos', 'projectid_003_pos',
+ 'projectquota_001_pos', 'projectquota_002_pos', 'projectquota_003_pos',
+ 'projectquota_004_neg', 'projectquota_005_pos', 'projectquota_006_pos',
+ 'projectquota_007_pos', 'projectquota_008_pos', 'projectquota_009_pos',
+ 'projectspace_001_pos', 'projectspace_002_pos', 'projectspace_003_pos',
+ 'projectspace_004_pos',
+ 'projecttree_002_pos', 'projecttree_003_neg' ]
+
[/opt/zfs-tests/tests/functional/quota]
tests = ['quota_001_pos', 'quota_002_pos', 'quota_003_pos', 'quota_004_pos',
'quota_005_pos', 'quota_006_neg']
@@ -645,13 +654,18 @@ tests = ['threadsappend_001_pos']
[/opt/zfs-tests/tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos']
+[/opt/zfs-tests/tests/functional/upgrade]
+tests = ['upgrade_userobj_001_pos', 'upgrade_projectquota_001_pos']
+
[/opt/zfs-tests/tests/functional/userquota]
-tests = ['groupspace_001_pos', 'groupspace_002_pos', 'userquota_001_pos',
- 'userquota_002_pos', 'userquota_003_pos', 'userquota_004_pos',
- 'userquota_005_neg', 'userquota_006_pos', 'userquota_007_pos',
- 'userquota_008_pos', 'userquota_009_pos', 'userquota_010_pos',
- 'userquota_011_pos', 'userquota_012_neg', 'userspace_001_pos',
- 'userspace_002_pos']
+tests = [
+ 'userquota_001_pos', 'userquota_002_pos', 'userquota_003_pos',
+ 'userquota_004_pos', 'userquota_005_neg', 'userquota_006_pos',
+ 'userquota_007_pos', 'userquota_008_pos', 'userquota_009_pos',
+ 'userquota_010_pos', 'userquota_011_pos', 'userquota_012_neg',
+ 'userquota_013_pos',
+ 'userspace_001_pos', 'userspace_002_pos', 'userspace_003_pos',
+ 'groupspace_001_pos', 'groupspace_002_pos', 'groupspace_003_pos' ]
[/opt/zfs-tests/tests/functional/utils_test]
tests = ['utils_test_001_pos', 'utils_test_002_pos', 'utils_test_003_pos',
diff --git a/usr/src/test/zfs-tests/runfiles/openindiana.run b/usr/src/test/zfs-tests/runfiles/openindiana.run
index ce1a96c5e0..a4c5917abe 100644
--- a/usr/src/test/zfs-tests/runfiles/openindiana.run
+++ b/usr/src/test/zfs-tests/runfiles/openindiana.run
@@ -162,7 +162,7 @@ tests = ['zfs_mount_001_pos', 'zfs_mount_002_pos', 'zfs_mount_003_pos',
'zfs_mount_004_pos', 'zfs_mount_005_pos', 'zfs_mount_006_pos',
'zfs_mount_007_pos', 'zfs_mount_008_pos', 'zfs_mount_009_neg',
'zfs_mount_010_neg', 'zfs_mount_011_neg', 'zfs_mount_012_neg',
- 'zfs_mount_all_001_pos', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints'
+ 'zfs_mount_all_001_pos', 'zfs_mount_all_fail', 'zfs_mount_all_mountpoints',
'zfs_mount_encrypted']
[/opt/zfs-tests/tests/functional/cli_root/zfs_program]
@@ -544,6 +544,15 @@ tests = ['poolversion_001_pos', 'poolversion_002_pos']
[/opt/zfs-tests/tests/functional/privilege]
tests = ['privilege_001_pos', 'privilege_002_pos']
+[/opt/zfs-tests/tests/functional/projectquota]
+tests = ['projectid_001_pos', 'projectid_002_pos', 'projectid_003_pos',
+ 'projectquota_001_pos', 'projectquota_002_pos', 'projectquota_003_pos',
+ 'projectquota_004_neg', 'projectquota_005_pos', 'projectquota_006_pos',
+ 'projectquota_007_pos', 'projectquota_008_pos', 'projectquota_009_pos',
+ 'projectspace_001_pos', 'projectspace_002_pos', 'projectspace_003_pos',
+ 'projectspace_004_pos',
+ 'projecttree_002_pos', 'projecttree_003_neg' ]
+
[/opt/zfs-tests/tests/functional/quota]
tests = ['quota_001_pos', 'quota_002_pos', 'quota_003_pos', 'quota_004_pos',
'quota_005_pos', 'quota_006_neg']
@@ -645,13 +654,18 @@ tests = ['threadsappend_001_pos']
[/opt/zfs-tests/tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos']
+[/opt/zfs-tests/tests/functional/upgrade]
+tests = ['upgrade_userobj_001_pos', 'upgrade_projectquota_001_pos']
+
[/opt/zfs-tests/tests/functional/userquota]
-tests = ['groupspace_001_pos', 'groupspace_002_pos', 'userquota_001_pos',
- 'userquota_002_pos', 'userquota_003_pos', 'userquota_004_pos',
- 'userquota_005_neg', 'userquota_006_pos', 'userquota_007_pos',
- 'userquota_008_pos', 'userquota_009_pos', 'userquota_010_pos',
- 'userquota_011_pos', 'userquota_012_neg', 'userspace_001_pos',
- 'userspace_002_pos']
+tests = [
+ 'userquota_001_pos', 'userquota_002_pos', 'userquota_003_pos',
+ 'userquota_004_pos', 'userquota_005_neg', 'userquota_006_pos',
+ 'userquota_007_pos', 'userquota_008_pos', 'userquota_009_pos',
+ 'userquota_010_pos', 'userquota_011_pos', 'userquota_012_neg',
+ 'userquota_013_pos',
+ 'userspace_001_pos', 'userspace_002_pos', 'userspace_003_pos',
+ 'groupspace_001_pos', 'groupspace_002_pos', 'groupspace_003_pos' ]
[/opt/zfs-tests/tests/functional/utils_test]
tests = ['utils_test_001_pos', 'utils_test_002_pos', 'utils_test_003_pos',
diff --git a/usr/src/test/zfs-tests/runfiles/smartos.run b/usr/src/test/zfs-tests/runfiles/smartos.run
index 45af16b2ad..872bb8f74c 100644
--- a/usr/src/test/zfs-tests/runfiles/smartos.run
+++ b/usr/src/test/zfs-tests/runfiles/smartos.run
@@ -472,6 +472,15 @@ post =
[/opt/zfs-tests/tests/functional/poolversion]
tests = ['poolversion_001_pos', 'poolversion_002_pos']
+[/opt/zfs-tests/tests/functional/projectquota]
+tests = ['projectid_001_pos', 'projectid_002_pos', 'projectid_003_pos',
+ 'projectquota_001_pos', 'projectquota_002_pos', 'projectquota_003_pos',
+ 'projectquota_004_neg', 'projectquota_005_pos', 'projectquota_006_pos',
+ 'projectquota_007_pos', 'projectquota_008_pos', 'projectquota_009_pos',
+ 'projectspace_001_pos', 'projectspace_002_pos', 'projectspace_003_pos',
+ 'projectspace_004_pos',
+ 'projecttree_002_pos', 'projecttree_003_neg' ]
+
[/opt/zfs-tests/tests/functional/quota]
tests = ['quota_001_pos', 'quota_002_pos', 'quota_003_pos', 'quota_004_pos',
'quota_005_pos', 'quota_006_neg']
@@ -554,6 +563,19 @@ tests = ['threadsappend_001_pos']
[/opt/zfs-tests/tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos']
+[/opt/zfs-tests/tests/functional/upgrade]
+tests = ['upgrade_userobj_001_pos', 'upgrade_projectquota_001_pos']
+
+[/opt/zfs-tests/tests/functional/userquota]
+tests = [
+ 'userquota_001_pos', 'userquota_002_pos', 'userquota_003_pos',
+ 'userquota_004_pos', 'userquota_005_neg', 'userquota_006_pos',
+ 'userquota_007_pos', 'userquota_008_pos', 'userquota_009_pos',
+ 'userquota_010_pos', 'userquota_011_pos', 'userquota_012_neg',
+ 'userquota_013_pos',
+ 'userspace_001_pos', 'userspace_002_pos', 'userspace_003_pos',
+ 'groupspace_001_pos', 'groupspace_002_pos', 'groupspace_003_pos' ]
+
[/opt/zfs-tests/tests/functional/utils_test]
tests = ['utils_test_001_pos', 'utils_test_002_pos', 'utils_test_003_pos',
'utils_test_004_pos', 'utils_test_005_pos', 'utils_test_006_pos',
diff --git a/usr/src/test/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg b/usr/src/test/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg
index 998fe0e2b3..0db15d67a7 100644
--- a/usr/src/test/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg
+++ b/usr/src/test/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg
@@ -81,4 +81,6 @@ typeset -a properties=(
"feature@resilver_defer"
"feature@encryption"
"feature@bookmark_v2"
+ "feature@userobj_accounting"
+ "feature@project_quota"
)
diff --git a/usr/src/test/zfs-tests/tests/functional/privilege/setup.ksh b/usr/src/test/zfs-tests/tests/functional/privilege/setup.ksh
index aa5f0aeb73..af38b4dfdd 100644
--- a/usr/src/test/zfs-tests/tests/functional/privilege/setup.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/privilege/setup.ksh
@@ -31,6 +31,10 @@
. $STF_SUITE/include/libtest.shlib
+if is_linux; then
+ log_unsupported "Requires pfexec command"
+fi
+
ZFS_USER=zfsrbac
USES_NIS=false
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/Makefile b/usr/src/test/zfs-tests/tests/functional/projectquota/Makefile
new file mode 100644
index 0000000000..18c90fca34
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/Makefile
@@ -0,0 +1,21 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/zfs-tests
+TARGETDIR = $(ROOTOPTPKG)/tests/functional/projectquota
+
+include $(SRC)/test/zfs-tests/Makefile.com
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/cleanup.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/cleanup.ksh
new file mode 100755
index 0000000000..0440e3d8af
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/cleanup.ksh
@@ -0,0 +1,37 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+log_must cleanup_projectquota
+log_must del_user $PUSER
+log_must del_group $PGROUP
+default_cleanup
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_001_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_001_pos.ksh
new file mode 100644
index 0000000000..188892f7be
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_001_pos.ksh
@@ -0,0 +1,100 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+# Check project ID/flags can be set/inherited properly
+#
+#
+# STRATEGY:
+# 1. Create a regular file and a directroy.
+# 2. Set project ID on both directroy and regular file.
+# 3. New created subdir or regular file should inherit its parent's
+# project ID if its parent has project inherit flag.
+# 4. New created subdir should inherit its parent project's inherit flag.
+#
+
+function cleanup
+{
+ log_must rm -f $PRJFILE
+ log_must rm -rf $PRJDIR
+}
+
+log_onexit cleanup
+
+log_assert "Check project ID/flags can be set/inherited properly"
+
+log_must touch $PRJFILE
+log_must mkdir $PRJDIR
+
+# log_must chattr -p $PRJID1 $PRJFILE
+log_must zfs project -s -p $PRJID1 $PRJFILE
+# log_must eval "lsattr -p $PRJFILE | grep $PRJID1 | grep -v '\-P[- ]* '"
+log_must eval "zfs project $PRJFILE | grep $PRJID1"
+# log_must chattr -p $PRJID1 $PRJDIR
+log_must zfs project -s -p $PRJID1 $PRJDIR
+# log_must eval "lsattr -pd $PRJDIR | grep $PRJID1 | grep -v '\-P[- ]* '"
+log_must eval "zfs project $PRJDIR | grep $PRJID1"
+
+# "-1" is invalid project ID, should be denied
+# log_mustnot chattr -p -1 $PRJFILE
+log_mustnot zfs project -s -p -1 $PRJFILE
+# log_must eval "lsattr -p $PRJFILE | grep $PRJID1 | grep -v '\-P[- ]* '"
+log_must eval "zfs project $PRJFILE | grep $PRJID1"
+
+log_must mkdir $PRJDIR/dchild
+# log_must eval "lsattr -pd $PRJDIR/dchild | grep $PRJID1 | grep '\-P[- ]* '"
+log_must eval "zfs project -d $PRJDIR/dchild | grep $PRJID1"
+log_must touch $PRJDIR/fchild
+# log_must eval "lsattr -p $PRJDIR/fchild | grep $PRJID1"
+log_must eval "zfs project $PRJDIR/fchild | grep $PRJID1"
+
+log_must touch $PRJDIR/dchild/foo
+# log_must eval "lsattr -p $PRJDIR/dchild/foo | grep $PRJID1"
+log_must eval "zfs project $PRJDIR/dchild/foo | grep $PRJID1"
+
+# do not support project ID/flag on block special file
+log_must mknod $PRJDIR/dchild/b_foo b 124 124
+# log_mustnot lsattr -p $PRJDIR/dchild/b_foo
+# log_mustnot chattr -p 123 $PRJDIR/dchild/b_foo
+log_mustnot zfs project -s -p 123 $PRJDIR/dchild/b_foo
+
+# do not support project ID/flag on character special file
+log_must mknod $PRJDIR/dchild/c_foo c 125 125
+# log_mustnot lsattr -p $PRJDIR/dchild/c_foo
+# log_mustnot chattr -p 123 $PRJDIR/dchild/c_foo
+log_mustnot zfs project -s -p 123 $PRJDIR/dchild/c_foo
+
+log_pass "Check project ID/flags can be set/inherited properly"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_002_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_002_pos.ksh
new file mode 100644
index 0000000000..b877f488ae
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_002_pos.ksh
@@ -0,0 +1,91 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+# Project ID affects POSIX behavior
+#
+#
+# STRATEGY:
+# 1. Create three directories
+# 2. Set tdir1 and tdir3 project ID as PRJID1,
+# set tdir2 project ID as PRJID2.
+# 3. Create regular file under tdir1. It inherits tdir1 proejct ID.
+# 4. Hardlink from tdir1's child to tdir2 should be denied,
+# move tdir1's child to tdir2 will be object recreated.
+# 5. Hardlink from tdir1's child to tdir3 should succeed.
+#
+
+function cleanup
+{
+ log_must rm -rf $PRJDIR1
+ log_must rm -rf $PRJDIR2
+ log_must rm -rf $PRJDIR3
+}
+
+log_onexit cleanup
+
+log_assert "Project ID affects POSIX behavior"
+
+log_must mkdir $PRJDIR1
+log_must mkdir $PRJDIR2
+log_must mkdir $PRJDIR3
+log_must mkdir $PRJDIR3/dir
+
+# log_must chattr +P -p $PRJID1 $PRJDIR1
+log_must zfs project -s -p $PRJID1 $PRJDIR1
+# log_must chattr +P -p $PRJID2 $PRJDIR2
+log_must zfs project -s -p $PRJID2 $PRJDIR2
+
+log_must touch $PRJDIR1/tfile1
+log_must touch $PRJDIR1/tfile2
+# log_must eval "lsattr -p $PRJDIR1/tfile1 | grep $PRJID1"
+log_must eval "zfs project $PRJDIR1/tfile1 | grep $PRJID1"
+
+log_mustnot ln $PRJDIR1/tfile1 $PRJDIR2/tfile2
+
+log_must mv $PRJDIR1/tfile1 $PRJDIR2/tfile2
+# log_must eval "lsattr -p $PRJDIR2/tfile2 | grep $PRJID2"
+log_must eval "zfs project $PRJDIR2/tfile2 | grep $PRJID2"
+
+log_must mv $PRJDIR3/dir $PRJDIR2/
+# log_must eval "lsattr -dp $PRJDIR2/dir | grep $PRJID2"
+log_must eval "zfs project -d $PRJDIR2/dir | grep $PRJID2"
+
+# log_must chattr +P -p $PRJID1 $PRJDIR3
+log_must zfs project -s -p $PRJID1 $PRJDIR3
+log_must ln $PRJDIR1/tfile2 $PRJDIR3/tfile3
+
+log_pass "Project ID affects POSIX behavior"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_003_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_003_pos.ksh
new file mode 100644
index 0000000000..8a1cd0ca3e
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectid_003_pos.ksh
@@ -0,0 +1,86 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. Fan rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check changing project ID for the file with directory-based
+# extended attributes.
+#
+#
+# STRATEGY:
+# 1. create new file with default project ID
+# 2. set non-ACL extended attributes on the file
+# 3. use zfs projectspace to check the object usage
+# 4. change the file's project ID
+# 5. use zfs projectspace to check the object usage again
+#
+
+function cleanup
+{
+ log_must rm -f $PRJGUARD
+ log_must rm -f $PRJFILE
+}
+
+log_onexit cleanup
+
+log_assert "Check changing project ID with directory-based extended attributes"
+
+log_must zfs set xattr=on $QFS
+
+log_must touch $PRJGUARD
+# log_must chattr -p $PRJID1 $PRJGUARD
+log_must zfs project -s -p $PRJID1 $PRJGUARD
+log_must touch $PRJFILE
+
+# log_must setfattr -n trusted.ea1 -v val1 $PRJFILE
+# log_must setfattr -n trusted.ea2 -v val2 $PRJFILE
+# log_must setfattr -n trusted.ea3 -v val3 $PRJFILE
+echo "dummy attribute data" >/tmp/attr.$$
+log_must runat $PRJFILE cp /tmp/attr.$$ trusted.ea1
+log_must runat $PRJFILE cp /tmp/attr.$$ trusted.ea2
+log_must runat $PRJFILE cp /tmp/attr.$$ trusted.ea3
+rm /tmp/attr.$$
+
+sync_pool
+typeset prj_bef=$(project_obj_count $QFS $PRJID1)
+
+# log_must chattr -p $PRJID1 $PRJFILE
+log_must zfs project -s -p $PRJID1 $PRJFILE
+sync_pool
+typeset prj_aft=$(project_obj_count $QFS $PRJID1)
+
+[[ $prj_aft -ge $((prj_bef + 5)) ]] ||
+ log_fail "new value ($prj_aft) is NOT 5 largr than old one ($prj_bef)"
+
+log_pass "Changing project ID with directory-based extended attributes pass"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota.cfg b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota.cfg
new file mode 100644
index 0000000000..564ab3ef96
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota.cfg
@@ -0,0 +1,46 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+export PUSER=puser
+export PGROUP=pgroup
+
+export PRJID1=1001
+export PRJID2=1002
+
+export QFS=$TESTPOOL/$TESTFS
+export PRJFILE=$TESTDIR/tfile
+export PRJGUARD=$TESTDIR/guard
+export PRJDIR=$TESTDIR/tdir
+export PRJDIR1=$TESTDIR/tdir1
+export PRJDIR2=$TESTDIR/tdir2
+export PRJDIR3=$TESTDIR/tdir3
+
+export PQUOTA_LIMIT=1000000
+export PQUOTA_OBJLIMIT=1000
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_001_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_001_pos.ksh
new file mode 100644
index 0000000000..3c19d16194
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_001_pos.ksh
@@ -0,0 +1,89 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+# Check the basic function of the project{obj}quota
+#
+#
+# STRATEGY:
+# 1. Set projectquota and overwrite the quota size.
+# 2. The write operation should fail with Disc quota exceeded
+# 3. Set projectobjquota and create up to the quota size.
+# 4. More create should fail with Disc quota exceeded
+# 5. More chattr to such project should fail with Disc quota exceeded
+#
+
+function cleanup
+{
+ cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "If operation exceeds project{obj}quota size, it will fail"
+
+mkmount_writable $QFS
+
+log_note "Check the projectquota@$PRJID1"
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID1 $PRJDIR
+log_must zfs project -s -p $PRJID1 $PRJDIR
+
+log_must zfs set projectquota@$PRJID1=$PQUOTA_LIMIT $QFS
+log_must user_run $PUSER mkfile $PQUOTA_LIMIT $PRJDIR/qf
+sync_pool
+log_mustnot user_run $PUSER mkfile 1 $PRJDIR/of
+
+log_must rm -rf $PRJDIR
+
+log_note "Check the projectobjquota@$PRJID2"
+log_must zfs set xattr=on $QFS
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID2 $PRJDIR
+log_must zfs project -s -p $PRJID2 $PRJDIR
+
+log_must zfs set projectobjquota@$PRJID2=$PQUOTA_OBJLIMIT $QFS
+log_must user_run $PUSER mkfiles $PRJDIR/qf_ $((PQUOTA_OBJLIMIT - 1))
+sync_pool
+log_mustnot user_run $PUSER mkfile 1 $PRJDIR/of
+
+log_must user_run $PUSER touch $PRJFILE
+# log_must user_run $PUSER chattr -p 123 $PRJFILE
+log_must user_run $PUSER zfs project -s -p 123 $PRJFILE
+# log_mustnot user_run $PUSER chattr -p $PRJID2 $PRJFILE
+log_mustnot user_run $PUSER zfs project -s -p $PRJID2 $PRJFILE
+
+log_pass "Operation exceeds project{obj}quota size failed as expect"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_002_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_002_pos.ksh
new file mode 100644
index 0000000000..75d1bf2858
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_002_pos.ksh
@@ -0,0 +1,87 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# The project{obj}quota can be set during zpool or zfs creation
+#
+#
+# STRATEGY:
+# 1. Set project{obj}quota via "zpool -O or zfs create -o"
+#
+
+verify_runnable "global"
+
+function cleanup
+{
+ if poolexists $TESTPOOL1; then
+ log_must zpool destroy $TESTPOOL1
+ fi
+
+ if [[ -f $pool_vdev ]]; then
+ rm -f $pool_vdev
+ fi
+}
+
+log_onexit cleanup
+
+log_assert "The project{obj}quota can be set during zpool,zfs creation"
+
+typeset pool_vdev=$TEST_BASE_DIR/pool_dev.$$
+
+log_must mkfile 500m $pool_vdev
+
+if poolexists $TESTPOOL1; then
+ zpool destroy $TESTPOOL1
+fi
+
+log_must zpool create -O projectquota@$PRJID1=$PQUOTA_LIMIT \
+ -O projectobjquota@$PRJID2=$PQUOTA_OBJLIMIT $TESTPOOL1 $pool_vdev
+
+log_must zfs create -o projectquota@$PRJID1=$PQUOTA_LIMIT \
+ -o projectobjquota@$PRJID2=$PQUOTA_OBJLIMIT $TESTPOOL1/fs
+
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL1 > /dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL1 "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL1 "$PQUOTA_OBJLIMIT"
+
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL1 > /dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL1/fs "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL1/fs "$PQUOTA_OBJLIMIT"
+
+log_pass "The project{obj}quota can be set during zpool,zfs creation"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_003_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_003_pos.ksh
new file mode 100644
index 0000000000..a7f89c19c2
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_003_pos.ksh
@@ -0,0 +1,99 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the basic function project{obj}used
+#
+#
+# STRATEGY:
+# 1. Write data to fs with some project then check the project{obj}used
+#
+
+function cleanup
+{
+ cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check the basic function of project{obj}used"
+
+sync_pool
+typeset project_used=$(get_value "projectused@$PRJID1" $QFS)
+typeset file_size='10m'
+
+if [[ $project_used -ge 8192 ]]; then
+ log_fail "FAIL: projectused is $project_used, should be less than 8k"
+fi
+
+mkmount_writable $QFS
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID1 $PRJDIR
+log_must zfs project -s -p $PRJID1 $PRJDIR
+log_must user_run $PUSER mkfile $file_size $PRJDIR/qf
+sync_pool
+project_used=$(get_value "projectused@$PRJID1" $QFS)
+# get_value() reads the exact byte value which is slightly more than 10m
+if [[ "$(($project_used/1024/1024))m" != "$file_size" ]]; then
+ log_note "project $PRJID1 used is $project_used"
+ log_fail "projectused for project $PRJID1 expected to be $file_size, " \
+ "not $project_used"
+fi
+
+log_must rm -rf $PRJDIR
+typeset project_obj_used=$(get_value "projectobjused@$PRJID2" $QFS)
+typeset file_count=100
+
+if [[ $project_obj_used -ge 2 ]]; then
+ log_fail "FAIL: projectobjused is $project_obj_used, should be " \
+ "less than 2"
+fi
+
+log_must zfs set xattr=on $QFS
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID2 $PRJDIR
+log_must zfs project -s -p $PRJID2 $PRJDIR
+# $PRJDIR has already used one object with the $PRJID2
+log_must user_run $PUSER mkfiles $PRJDIR/qf_ $((file_count - 1))
+sync_pool
+project_obj_used=$(get_value "projectobjused@$PRJID2" $QFS)
+exp_count=$((file_count + 1))
+if [[ $project_obj_used -gt $exp_count ]]; then
+ log_note "project $PRJID2 used is $project_obj_used"
+ log_fail "projectobjused for project $PRJID2 expected to be less than" \
+ "$exp_count, not $project_obj_used"
+fi
+
+log_pass "Check the basic function of project{obj}used pass as expected"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_004_neg.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_004_neg.ksh
new file mode 100644
index 0000000000..da3b8c1826
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_004_neg.ksh
@@ -0,0 +1,87 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the invalid parameter of zfs set project{obj}quota
+#
+#
+# STRATEGY:
+# 1. check the invalid zfs set project{obj}quota to fs
+# 2. check the valid zfs set project{obj}quota to snapshots
+#
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must zfs destroy $snap_fs
+ fi
+
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check the invalid parameter of zfs set project{obj}quota"
+typeset snap_fs=$QFS@snap
+
+log_must zfs snapshot $snap_fs
+
+set -A no_prjs "mms1234" "ss@#" "root-122" "-1"
+for prj in "${no_prjs[@]}"; do
+ log_mustnot zfs set projectquota@$prj=100m $QFS
+done
+
+log_note "can set all numeric id even that id does not exist"
+log_must zfs set projectquota@12345678=100m $QFS
+
+set -A sizes "100mfsd" "m0.12m" "GGM" "-1234-m" "123m-m"
+for size in "${sizes[@]}"; do
+ log_note "can not set projectquota with invalid size parameter"
+ log_mustnot zfs set projectquota@$PRJID1=$size $QFS
+done
+
+log_note "can not set projectquota to snapshot $snap_fs"
+log_mustnot zfs set projectquota@$PRJID1=100m $snap_fs
+
+for prj in "${no_prjs[@]}"; do
+ log_mustnot zfs set projectobjquota@$prj=100 $QFS
+done
+
+log_note "can not set projectobjquota with invalid size parameter"
+log_mustnot zfs set projectobjquota@$PRJID2=100msfsd $QFS
+
+log_note "can not set projectobjquota to snapshot $snap_fs"
+log_mustnot zfs set projectobjquota@$PRJID2=100m $snap_fs
+
+log_pass "Check the invalid parameter of zfs set project{obj}quota"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_005_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_005_pos.ksh
new file mode 100644
index 0000000000..b52f302f78
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_005_pos.ksh
@@ -0,0 +1,68 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the invalid parameter of zfs get project{obj}quota
+#
+#
+# STRATEGY:
+# 1. check the invalid zfs get project{obj}quota to fs
+# 2. check the valid zfs get project{obj}quota to snapshots
+#
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must zfs destroy $snap_fs
+ fi
+
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check the invalid parameter of zfs get project{obj}quota"
+typeset snap_fs=$QFS@snap
+
+log_must zfs snapshot $snap_fs
+
+set -A no_prjs "mms1234" "ss@#" "root-122"
+for prj in "${no_prjs[@]}"; do
+ log_must eval "zfs get projectquota@$prj $QFS >/dev/null 2>&1"
+ log_must eval "zfs get projectquota@$prj $snap_fs >/dev/null 2>&1"
+ log_must eval "zfs get projectobjquota@$prj $QFS >/dev/null 2>&1"
+ log_must eval "zfs get projectobjquota@$prj $snap_fs >/dev/null 2>&1"
+done
+
+log_pass "Check the invalid parameter of zfs get project{obj}quota"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_006_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_006_pos.ksh
new file mode 100644
index 0000000000..0b5488bfe1
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_006_pos.ksh
@@ -0,0 +1,72 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Projectquota can be set beyond the fs quota.
+# Pprojectquota can be set at a smaller size than its current usage.
+#
+# STRATEGY:
+# 1. set quota to a fs and set a larger size of projectquota
+# 2. write some data to the fs and set a smaller projectquota
+#
+
+function cleanup
+{
+ log_must cleanup_projectquota
+ log_must zfs set quota=none $QFS
+}
+
+log_onexit cleanup
+
+log_assert "Check set projectquota to larger than the quota size of a fs"
+
+log_must zfs set quota=200m $QFS
+log_must zfs set projectquota@$PRJID1=500m $QFS
+
+log_must zfs get projectquota@$PRJID1 $QFS
+
+log_note "write some data to the $QFS"
+mkmount_writable $QFS
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID1 $PRJDIR
+log_must zfs project -s -p $PRJID1 $PRJDIR
+log_must user_run $PUSER mkfile 100m $PRJDIR/qf
+sync
+
+log_note "set projectquota at a smaller size than its current usage"
+log_must zfs set projectquota@$PRJID1=90m $QFS
+
+log_must zfs get projectquota@$PRJID1 $QFS
+
+log_pass "set projectquota to larger than quota size of a fs"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_007_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_007_pos.ksh
new file mode 100644
index 0000000000..3572e0118f
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_007_pos.ksh
@@ -0,0 +1,58 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# zfs get all <fs> does not print out project{obj}quota
+#
+# STRATEGY:
+# 1. set project{obj}quota to a fs
+# 2. check zfs get all fs
+#
+
+function cleanup
+{
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check zfs get all will not print out project{obj}quota"
+
+log_must zfs set projectquota@$PRJID1=50m $QFS
+log_must zfs set projectobjquota@$PRJID2=100 $QFS
+
+log_mustnot eval "zfs get all $QFS | grep projectquota"
+log_mustnot eval "zfs get all $QFS | grep projectobjquota"
+
+log_pass "zfs get all will not print out project{obj}quota"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_008_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_008_pos.ksh
new file mode 100644
index 0000000000..365b5627e8
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_008_pos.ksh
@@ -0,0 +1,91 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check project{obj}quota to snapshot that:
+# 1) can not set project{obj}quota to snapshot directly
+# 2) snapshot can inherit the parent fs's project{obj}quota
+# 3) the project{obj}quota will not change even the parent quota changed.
+#
+#
+# STRATEGY:
+# 1. create a snapshot of a fs
+# 2. set the project{obj}quota to snapshot and expect fail
+# 3. set project{obj}quota to fs and check the snapshot
+# 4. re-set project{obj}quota to fs and check the snapshot's value
+#
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must zfs destroy $snap_fs
+ fi
+
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check the snapshot's project{obj}quota"
+typeset snap_fs=$QFS@snap
+
+
+log_must zfs set projectquota@$PRJID1=$PQUOTA_LIMIT $QFS
+log_must check_quota "projectquota@$PRJID1" $QFS "$PQUOTA_LIMIT"
+
+log_must zfs set projectobjquota@$PRJID2=$PQUOTA_OBJLIMIT $QFS
+log_must check_quota "projectobjquota@$PRJID2" $QFS "$PQUOTA_OBJLIMIT"
+
+log_must zfs snapshot $snap_fs
+
+log_note "check the snapshot $snap_fs project{obj}quota"
+log_must check_quota "projectquota@$PRJID1" $snap_fs "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $snap_fs "$PQUOTA_OBJLIMIT"
+
+log_note "set project{obj}quota to $snap_fs which will fail"
+log_mustnot zfs set projectquota@$PRJID1=100m $snap_fs
+log_mustnot zfs set projectobjquota@$PRJID2=100 $snap_fs
+
+log_note "change the parent's project{obj}quota"
+log_must zfs set projectquota@$PRJID1=$((PQUOTA_LIMIT * 2)) $QFS
+log_must zfs set projectobjquota@$PRJID2=50 $QFS
+
+log_must check_quota "projectquota@$PRJID1" $QFS $((PQUOTA_LIMIT * 2))
+log_must check_quota "projectobjquota@$PRJID2" $QFS 50
+
+log_note "check the snapshot $snap_fs project{obj}quota"
+log_must check_quota "projectquota@$PRJID1" $snap_fs "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $snap_fs "$PQUOTA_OBJLIMIT"
+
+log_pass "Check the snapshot's project{obj}quota"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_009_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_009_pos.ksh
new file mode 100644
index 0000000000..a867b538c1
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_009_pos.ksh
@@ -0,0 +1,131 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# The project{obj}quota will not change during zfs actions, such as
+# snapshot,clone,rename,upgrade,send,receive.
+#
+#
+# STRATEGY:
+# 1. Create a pool, and create fs with preset project{obj}quota
+# 2. Check set project{obj}quota via zfs snapshot|clone|list -o
+# 3. Check the project{obj}quota can not change during zfs
+# rename|upgrade|promote
+# 4. Check the project{obj}quota can not change during zfs clone
+# 5. Check the project{obj}quota can not change during zfs send/receive
+#
+
+function cleanup
+{
+ for ds in $TESTPOOL/fs $TESTPOOL/fs-rename $TESTPOOL/fs-clone; do
+ if datasetexists $ds; then
+ log_must zfs destroy -rRf $ds
+ fi
+ done
+}
+
+log_onexit cleanup
+
+log_assert "the project{obj}quota can't change during zfs actions"
+
+cleanup
+
+log_must zfs create -o projectquota@$PRJID1=$PQUOTA_LIMIT \
+ -o projectobjquota@$PRJID2=$PQUOTA_OBJLIMIT $TESTPOOL/fs
+
+log_must zfs snapshot $TESTPOOL/fs@snap
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL/fs@snap "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL/fs@snap \
+ "$PQUOTA_OBJLIMIT"
+
+
+log_note "clone fs gets its parent's project{obj}quota initially"
+log_must zfs clone -o projectquota@$PRJID1=$PQUOTA_LIMIT \
+ -o projectobjquota@$PRJID2=$PQUOTA_OBJLIMIT \
+ $TESTPOOL/fs@snap $TESTPOOL/fs-clone
+
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL/fs-clone "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL/fs-clone \
+ "$PQUOTA_OBJLIMIT"
+
+log_must eval "zfs list -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL/fs-clone >/dev/null 2>&1"
+
+log_note "zfs promote can not change the previously set project{obj}quota"
+log_must zfs promote $TESTPOOL/fs-clone
+
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL/fs-clone "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL/fs-clone \
+ "$PQUOTA_OBJLIMIT"
+
+log_note "zfs send receive can not change the previously set project{obj}quota"
+log_must zfs send $TESTPOOL/fs-clone@snap | zfs receive $TESTPOOL/fs-rev
+
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL/fs-rev "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL/fs-rev \
+ "$PQUOTA_OBJLIMIT"
+
+log_note "zfs rename can not change the previously set project{obj}quota"
+log_must zfs rename $TESTPOOL/fs-rev $TESTPOOL/fs-rename
+
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL/fs-rename "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL/fs-rename \
+ "$PQUOTA_OBJLIMIT"
+
+log_note "zfs upgrade can not change the previously set project{obj}quota"
+log_must zfs upgrade $TESTPOOL/fs-rename
+
+log_must eval "zfs list -r -o projectquota@$PRJID1,projectobjquota@$PRJID2 \
+ $TESTPOOL >/dev/null 2>&1"
+
+log_must check_quota "projectquota@$PRJID1" $TESTPOOL/fs-rename "$PQUOTA_LIMIT"
+log_must check_quota "projectobjquota@$PRJID2" $TESTPOOL/fs-rename \
+ "$PQUOTA_OBJLIMIT"
+
+log_pass "the project{obj}quota can't change during zfs actions"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_common.kshlib b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_common.kshlib
new file mode 100644
index 0000000000..23f7c2a506
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectquota_common.kshlib
@@ -0,0 +1,101 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/projectquota/projectquota.cfg
+
+#
+# reset the projectquota and delete temporary files
+#
+function cleanup_projectquota
+{
+ if datasetexists $QFS; then
+ typeset mntp=$(get_prop mountpoint $QFS)
+
+ log_must zfs set projectquota@$PRJID1=none $QFS
+ log_must zfs set projectobjquota@$PRJID1=none $QFS
+ log_must zfs set projectquota@$PRJID2=none $QFS
+ log_must zfs set projectobjquota@$PRJID2=none $QFS
+ log_must chmod 0755 $mntp
+ fi
+
+ [[ -f $PRJFILE ]] && log_must rm -f $PRJFILE
+ [[ -d $PRJDIR ]] && log_must rm -rf $PRJDIR
+ [[ -d $PRJDIR1 ]] && log_must rm -rf $PRJDIR1
+ [[ -d $PRJDIR2 ]] && log_must rm -rf $PRJDIR2
+ [[ -d $PRJDIR3 ]] && log_must rm -rf $PRJDIR3
+ sync
+
+ return 0
+}
+
+function mkmount_writable
+{
+ typeset fs=$1
+ typeset mntp=$(get_prop mountpoint $fs)
+ log_must chmod 0777 $mntp
+}
+
+function check_quota
+{
+ typeset fs=$2
+ typeset prop=$1
+ typeset expected=$3
+ typeset value=$(get_prop $prop $fs)
+
+ if (($value != $expected)); then
+ return 1
+ fi
+}
+
+function get_value
+{
+ typeset prop_val
+ typeset prop=$1
+ typeset dataset=$2
+
+ prop_val=$(zfs get -H -p -o value $prop $dataset 2>/dev/null)
+ if [[ $? -ne 0 ]]; then
+ log_note "Unable to get $prop property for dataset $dataset"
+ return 1
+ fi
+
+ echo $prop_val
+}
+
+function project_obj_count
+{
+ typeset fs=$1
+ typeset prj=$2
+ typeset cnt=$(zfs projectspace -oname,objused $fs |
+ awk /$prj/'{print $2}')
+ [[ "$cnt" == "-" ]] && cnt=0 || true
+ echo $cnt
+}
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_001_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_001_pos.ksh
new file mode 100644
index 0000000000..fdbf5b61b4
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_001_pos.ksh
@@ -0,0 +1,91 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. Fan rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the zfs projectspace with kinds of parameters
+#
+#
+# STRATEGY:
+# 1. set zfs projectspace to a fs
+# 2. write some data to the fs with specified project ID
+# 3. use zfs projectspace with all possible parameters to check the result
+# 4. use zfs projectspace with some bad parameters to check the result
+#
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must zfs destroy $snap_fs
+ fi
+
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs projectspace with all possible parameters"
+
+set -A good_params -- "-H" "-p" "-o type,name,used,quota" "-o name,used,quota" \
+ "-o used,quota" "-o objused" "-o quota" "-s type" "-s name" "-s used" \
+ "-s quota" "-S type" "-S name" "-S used" "-S quota"
+
+typeset snap_fs=$QFS@snap
+
+log_must zfs set projectquota@$PRJID1=100m $QFS
+log_must zfs set projectobjquota@$PRJID1=100 $QFS
+mkmount_writable $QFS
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID1 $PRJDIR
+log_must zfs project -s -p $PRJID1 $PRJDIR
+log_must user_run $PUSER mkfile 50m $PRJDIR/qf
+sync
+
+log_must zfs snapshot $snap_fs
+
+for param in "${good_params[@]}"; do
+ log_must eval "zfs projectspace $param $QFS >/dev/null 2>&1"
+ log_must eval "zfs projectspace $param $snap_fs >/dev/null 2>&1"
+done
+
+log_assert "Check the zfs projectspace with some bad parameters"
+
+set -A bad_params -- "-i" "-n" "-P" "-t posixuser"
+
+for param in "${bad_params[@]}"; do
+ log_mustnot eval "zfs projectspace $param $QFS >/dev/null 2>&1"
+ log_mustnot eval "zfs projectspace $param $snap_fs >/dev/null 2>&1"
+done
+
+log_pass "zfs projectspace with kinds of parameters pass"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_002_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_002_pos.ksh
new file mode 100644
index 0000000000..719740c018
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_002_pos.ksh
@@ -0,0 +1,83 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the project used size and quota in zfs projectspace
+#
+#
+# STRATEGY:
+# 1. set zfs projectquota to a fs
+# 2. write some data to the fs with specified project and size
+# 3. use zfs projectspace to check the used size and quota size
+#
+
+function cleanup
+{
+ if datasetexists $snapfs; then
+ log_must zfs destroy $snapfs
+ fi
+
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs projectspace used and quota"
+
+log_must zfs set projectquota@$PRJID1=100m $QFS
+
+mkmount_writable $QFS
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID1 $PRJDIR
+log_must zfs project -s -p $PRJID1 $PRJDIR
+log_must user_run $PUSER mkfile 50m $PRJDIR/qf
+sync
+
+typeset snapfs=$QFS@snap
+
+log_must zfs snapshot $snapfs
+
+log_must eval "zfs projectspace $QFS >/dev/null 2>&1"
+log_must eval "zfs projectspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+ log_note "check the quota size in zfs projectspace $fs"
+ log_must eval "zfs projectspace $fs | grep $PRJID1 | grep 100M"
+
+ log_note "check the project used size in zfs projectspace $fs"
+ log_must eval "zfs projectspace $fs | grep $PRJID1 | grep 50\\.\*M"
+done
+
+log_pass "Check the zfs projectspace used and quota"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_003_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_003_pos.ksh
new file mode 100644
index 0000000000..c653e6b922
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_003_pos.ksh
@@ -0,0 +1,119 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the project used object accounting in zfs projectspace
+#
+#
+# STRATEGY:
+# 1. create a bunch of files by specific project
+# 2. use zfs projectspace to check the used objects
+# 3. change the project ID of test files and verify object count
+# 4. delete files and verify object count
+#
+
+function cleanup
+{
+ if datasetexists $snapfs; then
+ log_must zfs destroy $snapfs
+ fi
+
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs projectspace object used"
+
+mkmount_writable $QFS
+log_must zfs set xattr=on $QFS
+log_must user_run $PUSER mkdir $PRJDIR1
+log_must user_run $PUSER mkdir $PRJDIR2
+# log_must chattr +P -p $PRJID1 $PRJDIR1
+# log_must chattr +P -p $PRJID2 $PRJDIR2
+log_must zfs project -s -p $PRJID1 $PRJDIR1
+log_must zfs project -s -p $PRJID2 $PRJDIR2
+
+((prj_cnt1 = RANDOM % 100 + 2))
+((prj_cnt2 = RANDOM % 100 + 2))
+
+log_must user_run $PUSER mkfiles $PRJDIR1/qf $((prj_cnt1 - 1))
+log_must user_run $PUSER mkfiles $PRJDIR2/qf $((prj_cnt2 - 1))
+sync_pool
+
+typeset snapfs=$QFS@snap
+
+log_must zfs snapshot $snapfs
+
+log_must eval "zfs projectspace $QFS >/dev/null 2>&1"
+log_must eval "zfs projectspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+ log_note "check the project used objects in zfs projectspace $fs"
+ prjused=$(project_obj_count $fs $PRJID1)
+ [[ $prjused -ge $prj_cnt1 ]] ||
+ log_fail "($PRJID1) expected $prj_cnt1, got $prjused"
+ prjused=$(project_obj_count $fs $PRJID2)
+ [[ $prjused -ge $prj_cnt2 ]] ||
+ log_fail "($PRJID2) expected $prj_cnt2, got $prjused"
+done
+
+log_note "change the project of files"
+# log_must chattr -p $PRJID2 $PRJDIR1/qf*
+log_must zfs project -s -p $PRJID2 $PRJDIR1/qf*
+sync_pool
+
+prjused=$(project_obj_count $QFS $PRJID1)
+[[ $prjused -lt 10 ]] ||
+ log_fail "expected less than 10 for project $PRJID1, got $prjused"
+
+prjused=$(project_obj_count $snapfs $PRJID1)
+[[ $prjused -ge $prj_cnt1 ]] ||
+ log_fail "expected $prj_cnt1 for $PRJID1 in snapfs, got $prjused"
+
+prjused=$(project_obj_count $QFS $PRJID2)
+[[ $prjused -ge $((prj_cnt1 + prj_cnt2 - 1)) ]] ||
+ log_fail "($PRJID2) expected $((prj_cnt1 + prj_cnt2 - 1)), got $prjused"
+
+log_note "file removal"
+log_must rm -rf $PRJDIR1
+sync_pool
+
+prjused=$(project_obj_count $QFS $PRJID1)
+[[ $prjused -lt 10 ]] || log_fail "expected less than 10 for $PRJID1, " \
+ "got $prjused"
+
+cleanup
+log_pass "Check the zfs projectspace object used"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_004_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_004_pos.ksh
new file mode 100644
index 0000000000..9c4d37f0f7
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projectspace_004_pos.ksh
@@ -0,0 +1,76 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. Fan rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check 'df' command on the directory with INHERIT (project ID) flag
+#
+#
+# STRATEGY:
+# 1. set project [obj]quota on the directory
+# 2. set project ID and inherit flag on the directoty
+# 3. run 'df [-i]' on the directory and check the result
+#
+
+function cleanup
+{
+ if datasetexists $snap_fs; then
+ log_must zfs destroy $snap_fs
+ fi
+
+ log_must cleanup_projectquota
+}
+
+log_onexit cleanup
+
+log_assert "Check 'df' on dir with inherit project shows the project quota/used"
+
+log_must zfs set projectquota@$PRJID1=100m $QFS
+log_must zfs set projectobjquota@$PRJID1=100 $QFS
+mkmount_writable $QFS
+log_must user_run $PUSER mkdir $PRJDIR
+# log_must chattr +P -p $PRJID1 $PRJDIR
+log_must zfs project -s -p $PRJID1 $PRJDIR
+log_must user_run $PUSER mkfile 50m $PRJDIR/qf
+sync_pool
+
+total=$(df -b $PRJDIR | tail -n 1 | awk '{ print $2 }')
+[[ $total -ge 9590000 && $total -le 9598900 ]] || \
+ log_fail "expect '9590000-9598900' resource, but got '$total'"
+
+# -i invalid on illumos
+# used=$(df -i $PRJDIR | tail -n 1 | awk '{ print $5 }')
+# [[ "$used" == "2%" ]] || log_fail "expect '2%' used, but got '$used'"
+
+log_pass "'df' on the directory with inherit project ID flag pass as expect"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_001_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_001_pos.ksh
new file mode 100644
index 0000000000..0402e345df
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_001_pos.ksh
@@ -0,0 +1,108 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+# Check 'zfs project' is compatible with chattr/lsattr
+#
+#
+# STRATEGY:
+# Verify the following:
+# 1. "zfs project -p" behaviours the same as "chattr -p"
+# 2. "zfs project" behaviours the same as "lsattr -p"
+# 3. "zfs project -d" behaviours the same as "lsattr -p -d"
+# 4. "zfs project -s" behaviours the same as "chattr +P"
+# 5. "zfs project -s -p" behaviours the same as "chattr +P -p"
+# 6. "zfs project -C" behaviours the same as "chattr -P"
+#
+
+function cleanup
+{
+ log_must rm -rf $PRJDIR
+}
+
+if ! lsattr -pd > /dev/null 2>&1; then
+ log_unsupported "Current e2fsprogs does not support set/show project ID"
+fi
+
+#
+# e2fsprogs-1.44.4 incorrectly reports verity 'V' bit when the project 'P'
+# bit is set. Skip this test when 1.44.4 is installed to prevent failures.
+#
+# https://github.com/tytso/e2fsprogs/commit/7e5a95e3d
+#
+if lsattr -V 2>&1 | grep "lsattr 1.44.4"; then
+ log_unsupported "Current e2fsprogs incorrectly reports 'V' verity bit"
+fi
+
+log_onexit cleanup
+
+log_assert "Check 'zfs project' is compatible with chattr/lsattr"
+
+log_must mkdir $PRJDIR
+log_must mkdir $PRJDIR/a1
+log_must mkdir $PRJDIR/a2
+log_must touch $PRJDIR/a3
+
+log_must chattr -p $PRJID1 $PRJDIR/a3
+log_must eval "zfs project $PRJDIR/a3 | grep '$PRJID1 \-'"
+
+log_must zfs project -p $PRJID2 $PRJDIR/a3
+log_must eval "lsattr -p $PRJDIR/a3 | grep $PRJID2 | grep -v '\-P[- ]* '"
+
+log_must chattr -p $PRJID1 $PRJDIR/a1
+log_must eval "zfs project -d $PRJDIR/a1 | grep '$PRJID1 \-'"
+
+log_must zfs project -p $PRJID2 $PRJDIR/a1
+log_must eval "lsattr -pd $PRJDIR/a1 | grep $PRJID2 | grep -v '\-P[- ]* '"
+
+log_must chattr +P $PRJDIR/a2
+log_must eval "zfs project -d $PRJDIR/a2 | grep '0 P'"
+
+log_must zfs project -s $PRJDIR/a2
+log_must eval "lsattr -pd $PRJDIR/a2 | grep 0 | grep '\-P[- ]* '"
+
+log_must chattr +P -p $PRJID1 $PRJDIR/a1
+log_must eval "zfs project -d $PRJDIR/a1 | grep '$PRJID1 P'"
+
+log_must zfs project -s -p $PRJID2 $PRJDIR/a2
+log_must eval "lsattr -pd $PRJDIR/a2 | grep $PRJID2 | grep '\-P[- ]* '"
+
+log_must chattr -P $PRJDIR/a1
+log_must eval "zfs project -d $PRJDIR/a1 | grep '$PRJID1 \-'"
+
+log_must zfs project -C -k $PRJDIR/a2
+log_must eval "lsattr -pd $PRJDIR/a2 | grep $PRJID2 | grep -v '\-P[- ]* '"
+
+log_pass "Check 'zfs project' is compatible with chattr/lsattr"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_002_pos.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_002_pos.ksh
new file mode 100644
index 0000000000..d610192427
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_002_pos.ksh
@@ -0,0 +1,120 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+# Check project ID/flag can be operated via "zfs project"
+#
+#
+# STRATEGY:
+# 1. Create a tree with 4 level directories.
+# 2. Set project ID on both directory and regular file via
+# "zfs project -p".
+# 3. Check the project ID via "zfs project".
+# 4. Set project inherit flag on kinds of level directories (and its
+# descendants for some)) via "zfs project -s [-r]".
+# 5. Check the project ID and inherit flag via "zfs project -r".
+# 6. Clear the project inherit flag from some directories (and its
+# descendants for some) via "zfs project -C [-r]".
+# 7. Check the project ID and inherit flag via "zfs project -r".
+#
+
+function cleanup
+{
+ log_must rm -rf $PRJDIR
+}
+
+log_onexit cleanup
+
+log_assert "Check project ID/flag can be operated via 'zfs project'"
+
+log_must mkdir $PRJDIR
+
+log_must mkdir $PRJDIR/a1
+log_must mkdir $PRJDIR/b1
+log_must touch $PRJDIR/c1
+
+log_must mkdir $PRJDIR/a1/a2
+log_must mkdir $PRJDIR/a1/b2
+log_must touch $PRJDIR/a1/c2
+
+log_must mkdir $PRJDIR/b1/a2
+log_must mkdir $PRJDIR/b1/b2
+log_must touch $PRJDIR/b1/c2
+
+log_must mkdir $PRJDIR/a1/a2/a3
+log_must mkdir $PRJDIR/a1/a2/b3
+log_must touch $PRJDIR/a1/a2/c3
+
+log_must mkdir $PRJDIR/b1/a2/a3
+
+log_must touch $PRJDIR/a1/a2/a3/c4
+log_must touch $PRJDIR/a1/a2/a3/d4
+
+log_must zfs project -p $PRJID1 $PRJDIR/a1/c2
+log_must eval "zfs project $PRJDIR/a1/c2 | grep $PRJID1"
+
+log_must zfs project -p $PRJID2 $PRJDIR/a1/a2/a3
+log_must eval "zfs project -d $PRJDIR/a1/a2/a3 | grep $PRJID2"
+
+log_must zfs project -s $PRJDIR/b1/a2
+log_must eval "zfs project -d $PRJDIR/b1/a2 | grep ' P '"
+log_must eval "zfs project -d $PRJDIR/b1/a2/a3 | grep ' \- '"
+
+log_must zfs project -s -r -p $PRJID2 $PRJDIR/a1/a2
+log_must zfs project -c -r $PRJDIR/a1/a2
+log_must eval "zfs project -d $PRJDIR/a1/a2/a3 | grep ' P '"
+log_must eval "zfs project $PRJDIR/a1/a2/a3/c4 | grep $PRJID2"
+
+log_must zfs project -C $PRJDIR/a1/a2/a3
+log_must eval "zfs project -cr $PRJDIR/a1/a2 | grep 'inherit flag is not set'"
+log_must eval "zfs project $PRJDIR/a1/a2/a3/c4 | grep $PRJID2 | grep -v not"
+log_must zfs project -p 123 $PRJDIR/a1/a2/a3/c4
+log_must eval "zfs project -c -r $PRJDIR/a1/a2 | grep 123 | grep 'not set'"
+log_mustnot eval "zfs project -cr -p 123 $PRJDIR/a1/a2 | grep c4 | grep -v not"
+
+log_must zfs project -C -r $PRJDIR/a1/a2/a3
+log_must eval "zfs project -cr $PRJDIR/a1/a2 | grep a3 | grep 'not set'"
+log_must eval "zfs project -cr $PRJDIR/a1/a2 | grep d4 | grep 'not set'"
+log_must eval "zfs project $PRJDIR/a1/a2/a3/d4 | grep '0 \-'"
+
+log_must eval \
+ "zfs project -cr -0 $PRJDIR/a1/a2 | xargs -0 zfs project -s -p $PRJID2"
+log_mustnot eval "zfs project -cr $PRJDIR/a1/a2 | grep a3 | grep 'not set'"
+log_mustnot eval "zfs project -cr $PRJDIR/a1/a2 | grep d4 | grep 'not set'"
+
+log_must zfs project -C -r -k $PRJDIR/a1/a2
+log_must eval "zfs project -d $PRJDIR/a1/a2/b3 | grep '$PRJID2 \- '"
+
+log_pass "Check project ID/flag can be operated via 'zfs project'"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_003_neg.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_003_neg.ksh
new file mode 100644
index 0000000000..33382fdbe9
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/projecttree_003_neg.ksh
@@ -0,0 +1,103 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+# Check 'zfs project' invalid options combinations
+#
+#
+# STRATEGY:
+# Verify the following:
+# 1. "-c" only supports "-d", "-p", "-r" and "-0".
+# 2. "-C" only supports "-r" and "-k".
+# 3. "-s" only supports "-r" and "-p".
+# 4. "-c", "-C" and "-s" can NOT be specified together.
+# 5. "-d" can overwirte former "-r".
+# 6. "-r" can overwirte former "-d".
+# 7. "-0" must be together with "-c".
+# 8. "-d" must be on directory.
+# 9. "-r" must be on directory.
+# 10. "-p" must be together with "-c -r" or "-s".
+#
+
+function cleanup
+{
+ log_must rm -rf $PRJDIR
+}
+
+log_onexit cleanup
+
+log_assert "Check 'zfs project' invalid options combinations"
+
+log_must mkdir $PRJDIR
+log_must mkdir $PRJDIR/a1
+log_must touch $PRJDIR/a2
+
+log_mustnot zfs project -c
+log_mustnot zfs project -c -k $PRJDIR/a1
+log_mustnot zfs project -c -C $PRJDIR/a1
+log_mustnot zfs project -c -s $PRJDIR/a1
+log_must zfs project -c -d -r $PRJDIR/a1
+log_must zfs project -c -r -d $PRJDIR/a1
+log_mustnot zfs project -c -d $PRJDIR/a2
+log_mustnot zfs project -c -r $PRJDIR/a2
+
+log_mustnot zfs project -C
+log_mustnot zfs project -C -c $PRJDIR/a1
+log_mustnot zfs project -C -d $PRJDIR/a1
+log_mustnot zfs project -C -p 100 $PRJDIR/a1
+log_mustnot zfs project -C -s $PRJDIR/a1
+log_mustnot zfs project -C -r -0 $PRJDIR/a1
+log_mustnot zfs project -C -0 $PRJDIR/a1
+
+log_mustnot zfs project -s
+log_mustnot zfs project -s -d $PRJDIR/a1
+log_mustnot zfs project -s -k $PRJDIR/a1
+log_mustnot zfs project -s -r -0 $PRJDIR/a1
+log_mustnot zfs project -s -0 $PRJDIR/a1
+log_mustnot zfs project -s -r $PRJDIR/a2
+
+log_mustnot zfs project -p 100
+log_mustnot zfs project -p -1 $PRJDIR/a2
+log_mustnot zfs project -p 100 -d $PRJDIR/a1
+log_mustnot zfs project -p 100 -k $PRJDIR/a1
+log_mustnot zfs project -p 100 -0 $PRJDIR/a1
+log_mustnot zfs project -p 100 -r -0 $PRJDIR/a1
+
+log_mustnot zfs project
+log_mustnot zfs project -0 $PRJDIR/a2
+log_mustnot zfs project -k $PRJDIR/a2
+log_mustnot zfs project -S $PRJDIR/a1
+
+log_pass "Check 'zfs project' invalid options combinations"
diff --git a/usr/src/test/zfs-tests/tests/functional/projectquota/setup.ksh b/usr/src/test/zfs-tests/tests/functional/projectquota/setup.ksh
new file mode 100755
index 0000000000..d16d27fb51
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/projectquota/setup.ksh
@@ -0,0 +1,56 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/tests/functional/projectquota/projectquota_common.kshlib
+
+verify_runnable "both"
+
+del_user $PUSER
+del_group $PGROUP
+log_must add_group $PGROUP
+log_must add_user $PGROUP $PUSER
+
+#
+# Verify the test user can execute the zfs utilities. This may not
+# be possible due to default permissions on the user home directory.
+# This can be resolved by granting group read access.
+#
+# chmod 0750 $HOME
+#
+user_run $PUSER zfs list
+if [ $? -ne 0 ]; then
+ log_unsupported "Test user $PUSER cannot execute zfs utilities"
+fi
+
+DISK=${DISKS%% *}
+default_setup_noexit $DISK
+
+log_pass
diff --git a/usr/src/test/zfs-tests/tests/functional/upgrade/Makefile b/usr/src/test/zfs-tests/tests/functional/upgrade/Makefile
new file mode 100644
index 0000000000..5af265d505
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/upgrade/Makefile
@@ -0,0 +1,21 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
+#
+
+include $(SRC)/Makefile.master
+
+ROOTOPTPKG = $(ROOT)/opt/zfs-tests
+TARGETDIR = $(ROOTOPTPKG)/tests/functional/upgrade
+
+include $(SRC)/test/zfs-tests/Makefile.com
diff --git a/usr/src/test/zfs-tests/tests/functional/upgrade/cleanup.ksh b/usr/src/test/zfs-tests/tests/functional/upgrade/cleanup.ksh
new file mode 100644
index 0000000000..1f0c9b63d9
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/upgrade/cleanup.ksh
@@ -0,0 +1,42 @@
+#!/bin/ksh -p
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. No rights reserved.
+#
+
+. $STF_SUITE/tests/functional/upgrade/upgrade_common.kshlib
+
+verify_runnable "global"
+
+log_must rm -f $TMPDEV
+
+default_cleanup
diff --git a/usr/src/test/zfs-tests/tests/functional/upgrade/setup.ksh b/usr/src/test/zfs-tests/tests/functional/upgrade/setup.ksh
new file mode 100644
index 0000000000..c25d25df6b
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/upgrade/setup.ksh
@@ -0,0 +1,43 @@
+#!/bin/ksh -p
+#
+# 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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. No rights reserved.
+#
+
+. $STF_SUITE/tests/functional/upgrade/upgrade_common.kshlib
+
+verify_runnable "global"
+
+# create a pool without any features
+log_must mkfile 128m $TMPDEV
+
+log_pass
diff --git a/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_common.kshlib b/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_common.kshlib
new file mode 100644
index 0000000000..679ff30492
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_common.kshlib
@@ -0,0 +1,41 @@
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2017 by Fan Yong. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+export TMPDEV=$TEST_BASE_DIR/zpool_upgrade_test.dat
+
+function cleanup_upgrade
+{
+ datasetexists $TESTPOOL/fs1 && log_must zfs destroy $TESTPOOL/fs1
+ datasetexists $TESTPOOL/fs2 && log_must zfs destroy $TESTPOOL/fs2
+ datasetexists $TESTPOOL/fs3 && log_must zfs destroy $TESTPOOL/fs3
+ datasetexists $TESTPOOL && log_must zpool destroy $TESTPOOL
+}
diff --git a/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh b/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh
new file mode 100644
index 0000000000..1dc5003c58
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh
@@ -0,0 +1,125 @@
+#!/bin/ksh -p
+#
+# 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 by Fan Yong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/upgrade/upgrade_common.kshlib
+
+#
+# DESCRIPTION:
+#
+# Check whether zfs upgrade for project quota works or not.
+# The project quota is per dataset based feature. This test
+# will create multiple datasets and try different upgrade methods.
+#
+# STRATEGY:
+# 1. Create a pool with all features disabled
+# 2. Create a few dataset for testing
+# 3. Make sure automatic upgrade work
+# 4. Make sure manual upgrade work
+#
+
+verify_runnable "global"
+
+log_assert "pool upgrade for projectquota should work"
+log_onexit cleanup_upgrade
+
+log_must zpool create -d -m $TESTDIR $TESTPOOL $TMPDEV
+
+log_must mkfiles $TESTDIR/tf $((RANDOM % 100 + 1))
+log_must zfs create $TESTPOOL/fs1
+log_must mkfiles $TESTDIR/fs1/tf $((RANDOM % 100 + 1))
+log_must zfs umount $TESTPOOL/fs1
+
+log_must zfs create $TESTPOOL/fs2
+log_must mkdir $TESTDIR/fs2/dir
+log_must mkfiles $TESTDIR/fs2/tf $((RANDOM % 100 + 1))
+
+log_must zfs create $TESTPOOL/fs3
+log_must mkdir $TESTDIR/fs3/dir
+log_must mkfiles $TESTDIR/fs3/tf $((RANDOM % 100 + 1))
+
+# Make sure project quota is disabled
+zfs projectspace -o used $TESTPOOL | grep -q "USED" &&
+ log_fail "project quota should be disabled initially"
+
+# set projectquota before upgrade will fail
+log_mustnot zfs set projectquota@100=100m $TESTDIR/fs3
+
+# set projectobjquota before upgrade will fail
+log_mustnot zfs set projectobjquota@100=1000 $TESTDIR/fs3
+
+# setting a project should fail before upgrade
+# log_mustnot chattr -p 100 $TESTDIR/fs3/dir
+log_mustnot zfs project -s -p 100 $TESTDIR/fs3/dir
+
+# Upgrade zpool to support all features
+log_must zpool upgrade $TESTPOOL
+
+# Double check project quota is disabled
+zfs projectspace -o used $TESTPOOL | grep -q "USED" &&
+ log_fail "project quota should be disabled after pool upgrade"
+
+# Mount dataset should trigger upgrade
+log_must zfs mount $TESTPOOL/fs1
+log_must sleep 3 # upgrade done in the background so let's wait for a while
+zfs projectspace -o used $TESTPOOL/fs1 | grep -q "USED" ||
+ log_fail "project quota should be enabled for $TESTPOOL/fs1"
+
+# Create file should trigger dataset upgrade
+log_must mkfile 1m $TESTDIR/fs2/dir/tf
+log_must sleep 3 # upgrade done in the background so let's wait for a while
+zfs projectspace -o used $TESTPOOL/fs2 | grep -q "USED" ||
+ log_fail "project quota should be enabled for $TESTPOOL/fs2"
+
+# reading projects should NOT trigger upgrade
+# log_must lsattr -p -d $TESTDIR/fs3/dir
+log_must eval "zfs project $TESTDIR/fs3/dir"
+zfs projectspace -o used $TESTPOOL/fs3 | grep -q "USED" &&
+ log_fail "project quota should not active for $TESTPOOL/fs3"
+
+# setting a project should trigger dataset upgrade
+# log_must chattr -p 100 $TESTDIR/fs3/dir
+log_must zfs project -s -p 100 $TESTDIR/fs3/dir
+log_must sleep 5 # upgrade done in the background so let's wait for a while
+zfs projectspace -o used $TESTPOOL/fs3 | grep -q "USED" ||
+ log_fail "project quota should be enabled for $TESTPOOL/fs3"
+cnt=$(zfs get -H projectobjused@100 $TESTPOOL/fs3 | awk '{print $3}')
+# if 'xattr=on', then 'cnt = 2'
+[[ $cnt -ne 1 ]] && [[ $cnt -ne 2 ]] &&
+ log_fail "projectquota accounting failed $cnt"
+
+# All in all, after having been through this, the dataset for testpool
+# still shouldn't be upgraded
+zfs projectspace -o used $TESTPOOL | grep -q "USED" &&
+ log_fail "project quota should be disabled for $TESTPOOL"
+
+# Manual upgrade root dataset
+# uses an ioctl which will wait for the upgrade to be done before returning
+log_must zfs set version=current $TESTPOOL
+zfs projectspace -o used $TESTPOOL | grep -q "USED" ||
+ log_fail "project quota should be enabled for $TESTPOOL"
+
+log_pass "Project Quota upgrade done"
diff --git a/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos.ksh b/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos.ksh
new file mode 100644
index 0000000000..250ed95893
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/upgrade/upgrade_userobj_001_pos.ksh
@@ -0,0 +1,95 @@
+#!/bin/ksh -p
+#
+# 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) 2013 by Jinshan Xiong. No rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/tests/functional/upgrade/upgrade_common.kshlib
+
+#
+# DESCRIPTION:
+#
+# Check that zfs upgrade for object count accounting works.
+# Since userobjaccounting is a per dataset feature, this test case
+# will create multiple dataset and try different upgrade method.
+#
+# STRATEGY:
+# 1. Create a pool with all features disabled
+# 2. Create a few dataset for testing
+# 3. Make sure automatic upgrade work
+# 4. Make sure manual upgrade work
+#
+
+verify_runnable "global"
+
+log_assert "pool upgrade for userobj accounting should work"
+log_onexit cleanup_upgrade
+
+log_must zpool create -d -m $TESTDIR $TESTPOOL $TMPDEV
+
+log_must mkfiles $TESTDIR/tf $((RANDOM % 1000 + 1))
+log_must zfs create $TESTPOOL/fs1
+log_must mkfiles $TESTDIR/fs1/tf $((RANDOM % 1000 + 1))
+log_must zfs create $TESTPOOL/fs2
+log_must mkfiles $TESTDIR/fs2/tf $((RANDOM % 1000 + 1))
+log_must zfs umount $TESTPOOL/fs2
+
+# Make sure userobj accounting is disabled
+zfs userspace -o objused -H $TESTPOOL | head -n 1 | grep -q "-" ||
+ log_fail "userobj accounting should be disabled initially"
+
+# Upgrade zpool to support all features
+log_must zpool upgrade $TESTPOOL
+
+# Make sure userobj accounting is disabled again
+zfs userspace -o objused -H $TESTPOOL | head -n 1 | grep -q "-" ||
+ log_fail "userobj accounting should be disabled after pool upgrade"
+
+# Create a file in fs1 should trigger dataset upgrade
+log_must mkfile 1m $TESTDIR/fs1/tf
+sync_pool
+
+# Make sure userobj accounting is working for fs1
+zfs userspace -o objused -H $TESTPOOL/fs1 | head -n 1 | grep -q "-" &&
+ log_fail "userobj accounting should be enabled for $TESTPOOL/fs1"
+
+# Mount a dataset should trigger upgrade
+log_must zfs mount $TESTPOOL/fs2
+sync_pool
+
+# Make sure userobj accounting is working for fs2
+zfs userspace -o objused -H $TESTPOOL/fs2 | head -n 1 | grep -q "-" &&
+ log_fail "userobj accounting should be enabled for $TESTPOOL/fs2"
+
+# All in all, after having been through this, the dataset for testpool
+# still shouldn't be upgraded
+zfs userspace -o objused -H $TESTPOOL | head -n 1 | grep -q "-" ||
+ log_fail "userobj accounting should be disabled for $TESTPOOL"
+
+# Manual upgrade root dataset
+log_must zfs set version=current $TESTPOOL
+zfs userspace -o objused -H $TESTPOOL | head -n 1 | grep -q "-" &&
+ log_fail "userobj accounting should be enabled for $TESTPOOL"
+
+log_pass "all tests passed - what a lucky day!"
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/groupspace_003_pos.ksh b/usr/src/test/zfs-tests/tests/functional/userquota/groupspace_003_pos.ksh
new file mode 100644
index 0000000000..f4c5002675
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/groupspace_003_pos.ksh
@@ -0,0 +1,104 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2013 by Delphix. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the user used and groupspace object counts in zfs groupspace
+#
+#
+# STRATEGY:
+# 1. set zfs groupquota to a fs
+# 2. create objects for different users in the same group
+# 3. use zfs groupspace to check the object count
+#
+
+function cleanup
+{
+ if datasetexists $snapfs; then
+ log_must zfs destroy $snapfs
+ fi
+
+ log_must rm -f ${QFILE}_*
+ log_must cleanup_quota
+}
+
+function group_object_count
+{
+ typeset fs=$1
+ typeset user=$2
+ typeset cnt=$(zfs groupspace -oname,objused $fs | grep $user |
+ awk '{print $2}')
+ echo $cnt
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs groupspace object used"
+
+mkmount_writable $QFS
+log_must zfs set xattr=on $QFS
+
+((user1_cnt = RANDOM % 100 + 1))
+((user2_cnt = RANDOM % 100 + 1))
+log_must user_run $QUSER1 mkfiles ${QFILE}_1 $user1_cnt
+log_must user_run $QUSER2 mkfiles ${QFILE}_2 $user2_cnt
+((grp_cnt = user1_cnt + user2_cnt))
+sync_pool
+
+typeset snapfs=$QFS@snap
+
+log_must zfs snapshot $snapfs
+
+log_must eval "zfs groupspace $QFS >/dev/null 2>&1"
+log_must eval "zfs groupspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+ log_note "check the object count in zfs groupspace $fs"
+ [[ $(group_object_count $fs $QGROUP) -eq $grp_cnt ]] ||
+ log_fail "expected $grp_cnt"
+done
+
+log_note "file removal"
+log_must rm ${QFILE}_*
+sync_pool
+
+[[ $(group_object_count $QFS $QGROUP) -eq 0 ]] ||
+ log_fail "expected 0 files for $QGROUP"
+
+[[ $(group_object_count $snapfs $QGROUP) -eq $grp_cnt ]] ||
+ log_fail "expected $grp_cnt files for $QGROUP"
+
+cleanup
+log_pass "Check the zfs groupspace object used pass as expected"
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_001_pos.ksh b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_001_pos.ksh
index 65ba2e0e62..acc78d23be 100644
--- a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_001_pos.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_001_pos.ksh
@@ -58,7 +58,7 @@ mkmount_writable $QFS
log_note "Check the userquota@$QUSER1"
log_must zfs set userquota@$QUSER1=$UQUOTA_SIZE $QFS
log_must user_run $QUSER1 mkfile $UQUOTA_SIZE $QFILE
-sync
+sync_pool
log_mustnot user_run $QUSER1 mkfile 1 $OFILE
cleanup_quota
@@ -66,7 +66,7 @@ log_note "Check the groupquota@$QGROUP"
log_must zfs set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
mkmount_writable $QFS
log_must user_run $QUSER1 mkfile $GQUOTA_SIZE $QFILE
-sync
+sync_pool
log_mustnot user_run $QUSER1 mkfile 1 $OFILE
cleanup_quota
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_004_pos.ksh b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_004_pos.ksh
index ce5ed720cc..a7b2ab17f1 100644
--- a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_004_pos.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_004_pos.ksh
@@ -27,6 +27,7 @@
#
# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
+$ Copyright 2019 Joyent, Inc.
#
. $STF_SUITE/include/libtest.shlib
@@ -50,26 +51,28 @@ log_onexit cleanup
log_assert "Check the basic function of {user|group} used"
+sync_pool
typeset user_used=$(get_value "userused@$QUSER1" $QFS)
typeset group_used=$(get_value "groupused@$QGROUP" $QFS)
-if [[ $user_used != 0 ]]; then
- log_fail "FAIL: userused is $user_used, should be 0"
+if [[ $user_used != "none" ]]; then
+ log_fail "FAIL: userused is $user_used, should be none"
fi
-if [[ $group_used != 0 ]]; then
- log_fail "FAIL: groupused is $group_used, should be 0"
+if [[ $group_used != "none" ]]; then
+ log_fail "FAIL: groupused is $group_used, should be none"
fi
mkmount_writable $QFS
log_must user_run $QUSER1 mkfile 100m $QFILE
-sync
+sync_pool
user_used=$(get_value "userused@$QUSER1" $QFS)
group_used=$(get_value "groupused@$QGROUP" $QFS)
if [[ $user_used != "100M" ]]; then
log_note "user $QUSER1 used is $user_used"
- log_fail "userused for user $QUSER1 expected to be 50.0M, not $user_used"
+ log_fail "userused for user $QUSER1 expected to be 50.0M, " \
+ "not $user_used"
fi
if [[ $user_used != $group_used ]]; then
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_010_pos.ksh b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_010_pos.ksh
index 09cb833551..9968ed945f 100644
--- a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_010_pos.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_010_pos.ksh
@@ -57,7 +57,7 @@ log_must zfs set groupquota@$QGROUP=$GQUOTA_SIZE $QFS
mkmount_writable $QFS
log_must user_run $QUSER1 mkfile $UQUOTA_SIZE $QFILE
-sync
+sync_pool
log_must eval "zfs get -p userused@$QUSER1 $QFS >/dev/null 2>&1"
log_must eval "zfs get -p groupused@$GROUPUSED $QFS >/dev/null 2>&1"
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_013_pos.ksh b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_013_pos.ksh
new file mode 100644
index 0000000000..bdb0bafaab
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_013_pos.ksh
@@ -0,0 +1,79 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
+
+#
+#
+# DESCRIPTION:
+# Check the basic function of the userobjquota and groupobjquota
+#
+#
+# STRATEGY:
+# 1. Set userobjquota and overwrite the quota size
+# 2. Creating new object should fail with Disc quota exceeded
+# 3. Set groupobjquota and overwrite the quota size
+# 4. Creating new object should fail with Disc quota exceeded
+#
+#
+
+function cleanup
+{
+ log_must rm -f ${QFILE}_*
+ cleanup_quota
+}
+
+log_onexit cleanup
+
+log_assert "If creating object exceeds {user|group}objquota count, it will fail"
+
+mkmount_writable $QFS
+log_must zfs set xattr=on $QFS
+
+log_note "Check the userobjquota@$QUSER1"
+log_must zfs set userobjquota@$QUSER1=100 $QFS
+log_must user_run $QUSER1 mkfiles ${QFILE}_1 100
+sync_pool
+log_mustnot user_run $QUSER1 mkfile 1 $OFILE
+cleanup_quota
+
+log_note "Check the groupobjquota@$QGROUP"
+log_must zfs set groupobjquota@$QGROUP=200 $QFS
+mkmount_writable $QFS
+log_must user_run $QUSER1 mkfiles ${QFILE}_2 100
+sync_pool
+log_mustnot user_run $QUSER2 mkfile 1 $OFILE
+
+cleanup
+log_pass "Creating objects exceeds {user|group}objquota count, passes as" \
+ "expected"
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_common.kshlib b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_common.kshlib
index c38308c4d1..931fb61004 100644
--- a/usr/src/test/zfs-tests/tests/functional/userquota/userquota_common.kshlib
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/userquota_common.kshlib
@@ -26,6 +26,7 @@
#
# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
. $STF_SUITE/include/libtest.shlib
@@ -38,8 +39,11 @@ function cleanup_quota
{
if datasetexists $QFS; then
log_must zfs set userquota@$QUSER1=none $QFS
+ log_must zfs set userobjquota@$QUSER1=none $QFS
log_must zfs set userquota@$QUSER2=none $QFS
+ log_must zfs set userobjquota@$QUSER2=none $QFS
log_must zfs set groupquota@$QGROUP=none $QFS
+ log_must zfs set groupobjquota@$QGROUP=none $QFS
recovery_writable $QFS
fi
@@ -47,7 +51,7 @@ function cleanup_quota
[[ -f $OFILE ]] && log_must rm -f $OFILE
sync
- return 0
+ return 0
}
#
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/userspace_002_pos.ksh b/usr/src/test/zfs-tests/tests/functional/userquota/userspace_002_pos.ksh
index b6f8425179..032115feff 100644
--- a/usr/src/test/zfs-tests/tests/functional/userquota/userspace_002_pos.ksh
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/userspace_002_pos.ksh
@@ -27,6 +27,7 @@
#
# Copyright (c) 2013, 2016 by Delphix. All rights reserved.
+# Copyright 2019 Joyent, Inc.
#
. $STF_SUITE/include/libtest.shlib
@@ -75,7 +76,7 @@ for fs in "$QFS" "$snapfs"; do
log_must eval "zfs userspace $fs | grep $QUSER1 | grep 100M"
log_note "check the user used size in zfs userspace $fs"
- log_must eval "zfs userspace $fs | grep $QUSER1 | grep 50.0M"
+ log_must eval "zfs userspace $fs | grep $QUSER1 | grep 50\\.\*M"
done
log_pass "Check the zfs userspace used and quota"
diff --git a/usr/src/test/zfs-tests/tests/functional/userquota/userspace_003_pos.ksh b/usr/src/test/zfs-tests/tests/functional/userquota/userspace_003_pos.ksh
new file mode 100644
index 0000000000..5c4454642d
--- /dev/null
+++ b/usr/src/test/zfs-tests/tests/functional/userquota/userspace_003_pos.ksh
@@ -0,0 +1,117 @@
+#!/bin/ksh -p
+#
+# 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 2009 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#
+# Copyright (c) 2016 by Jinshan Xiong. All rights reserved.
+# Copyright 2019 Joyent, Inc.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
+
+#
+# DESCRIPTION:
+# Check the user used object accounting in zfs userspace
+#
+#
+# STRATEGY:
+# 1. create a bunch of files by specific users
+# 2. use zfs userspace to check the used objects
+# 3. change the owner of test files and verify object count
+# 4. delete files and verify object count
+#
+
+function cleanup
+{
+ if datasetexists $snapfs; then
+ log_must zfs destroy $snapfs
+ fi
+
+ log_must rm -f ${QFILE}_*
+ log_must cleanup_quota
+}
+
+function user_object_count
+{
+ typeset fs=$1
+ typeset user=$2
+ typeset cnt=$(zfs userspace -oname,objused $fs |
+ awk /$user/'{print $2}')
+ echo $cnt
+}
+
+log_onexit cleanup
+
+log_assert "Check the zfs userspace object used"
+
+mkmount_writable $QFS
+log_must zfs set xattr=on $QFS
+
+((user1_cnt = RANDOM % 100 + 1))
+((user2_cnt = RANDOM % 100 + 1))
+
+log_must user_run $QUSER1 mkfiles ${QFILE}_1 $user1_cnt
+log_must user_run $QUSER2 mkfiles ${QFILE}_2 $user2_cnt
+sync_pool
+
+typeset snapfs=$QFS@snap
+
+log_must zfs snapshot $snapfs
+
+log_must eval "zfs userspace $QFS >/dev/null 2>&1"
+log_must eval "zfs userspace $snapfs >/dev/null 2>&1"
+
+for fs in "$QFS" "$snapfs"; do
+ log_note "check the user used objects in zfs userspace $fs"
+ [[ $(user_object_count $fs $QUSER1) -eq $user1_cnt ]] ||
+ log_fail "expected $user1_cnt"
+ [[ $(user_object_count $fs $QUSER2) -eq $user2_cnt ]] ||
+ log_fail "expected $user2_cnt"
+done
+
+log_note "change the owner of files"
+log_must chown $QUSER2 ${QFILE}_1*
+sync_pool
+
+[[ $(user_object_count $QFS $QUSER1) -eq 0 ]] ||
+ log_fail "expected 0 files for $QUSER1"
+
+[[ $(user_object_count $snapfs $QUSER1) -eq $user1_cnt ]] ||
+ log_fail "expected $user_cnt files for $QUSER1 in snapfs"
+
+[[ $(user_object_count $QFS $QUSER2) -eq $((user1_cnt+user2_cnt)) ]] ||
+ log_fail "expected $((user1_cnt+user2_cnt)) files for $QUSER2"
+
+log_note "file removal"
+log_must rm ${QFILE}_*
+sync_pool
+
+[[ $(user_object_count $QFS $QUSER2) -eq 0 ]] ||
+ log_fail "expected 0 files for $QUSER2"
+
+cleanup
+log_pass "Check the zfs userspace object used"
diff --git a/usr/src/tools/make/Makefile.com b/usr/src/tools/make/Makefile.com
index 05ff964cd4..97c32780bf 100644
--- a/usr/src/tools/make/Makefile.com
+++ b/usr/src/tools/make/Makefile.com
@@ -17,4 +17,5 @@ CC = $(NATIVECC)
CCC = $(NATIVECCC)
CFLAGS = $(NATIVE_CFLAGS)
CFLAGS += $(CCVERBOSE)
+CCFLAGS = $(NATIVE_CCFLAGS)
CPPFLAGS = -I$(MAKE_INCLUDE) $(MAKE_DEFS)
diff --git a/usr/src/uts/common/fs/zfs/dbuf.c b/usr/src/uts/common/fs/zfs/dbuf.c
index a7d191e7dd..ae0b1fc878 100644
--- a/usr/src/uts/common/fs/zfs/dbuf.c
+++ b/usr/src/uts/common/fs/zfs/dbuf.c
@@ -2378,7 +2378,7 @@ dbuf_destroy(dmu_buf_impl_t *db)
/*
* Note: While bpp will always be updated if the function returns success,
* parentp will not be updated if the dnode does not have dn_dbuf filled in;
- * this happens when the dnode is the meta-dnode, or a userused or groupused
+ * this happens when the dnode is the meta-dnode, or {user|group|project}used
* object.
*/
static int
diff --git a/usr/src/uts/common/fs/zfs/dmu.c b/usr/src/uts/common/fs/zfs/dmu.c
index 01de534581..322666626a 100644
--- a/usr/src/uts/common/fs/zfs/dmu.c
+++ b/usr/src/uts/common/fs/zfs/dmu.c
@@ -135,8 +135,8 @@ const dmu_object_type_info_t dmu_ot[DMU_OT_NUMTYPES] = {
{ DMU_BSWAP_UINT64, TRUE, FALSE, FALSE, "FUID table size" },
{ DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "DSL dataset next clones" },
{ DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "scan work queue" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS user/group used" },
- { DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS user/group quota" },
+ { DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS user/group/project used"},
+ { DMU_BSWAP_ZAP, TRUE, FALSE, TRUE, "ZFS user/group/proj quota"},
{ DMU_BSWAP_ZAP, TRUE, TRUE, FALSE, "snapshot refcount tags" },
{ DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "DDT ZAP algorithm" },
{ DMU_BSWAP_ZAP, TRUE, FALSE, FALSE, "DDT statistics" },
diff --git a/usr/src/uts/common/fs/zfs/dmu_objset.c b/usr/src/uts/common/fs/zfs/dmu_objset.c
index 8174222826..3e11d73cad 100644
--- a/usr/src/uts/common/fs/zfs/dmu_objset.c
+++ b/usr/src/uts/common/fs/zfs/dmu_objset.c
@@ -23,7 +23,7 @@
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2017 by Delphix. All rights reserved.
* Copyright (c) 2013 by Saso Kiselkov. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright (c) 2015, STRATO AG, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
@@ -32,6 +32,7 @@
/* Portions Copyright 2010 Robert Milkowski */
+#include <sys/zfeature.h>
#include <sys/cred.h>
#include <sys/zfs_context.h>
#include <sys/dmu_objset.h>
@@ -54,7 +55,9 @@
#include <sys/dsl_destroy.h>
#include <sys/vdev.h>
#include <sys/zfeature.h>
+#include <sys/spa_impl.h>
#include <sys/dmu_recv.h>
+#include <sys/zfs_project.h>
#include "zfs_namecheck.h"
/*
@@ -80,6 +83,9 @@ int dmu_rescan_dnode_threshold = 131072;
static void dmu_objset_find_dp_cb(void *arg);
+static void dmu_objset_upgrade(objset_t *os, dmu_objset_upgrade_cb_t cb);
+static void dmu_objset_upgrade_stop(objset_t *os);
+
void
dmu_objset_init(void)
{
@@ -343,14 +349,17 @@ dmu_objset_byteswap(void *buf, size_t size)
{
objset_phys_t *osp = buf;
- ASSERT(size == OBJSET_OLD_PHYS_SIZE || size == sizeof (objset_phys_t));
+ ASSERT(size == OBJSET_PHYS_SIZE_V1 || size == OBJSET_PHYS_SIZE_V2 ||
+ size == sizeof (objset_phys_t));
dnode_byteswap(&osp->os_meta_dnode);
byteswap_uint64_array(&osp->os_zil_header, sizeof (zil_header_t));
osp->os_type = BSWAP_64(osp->os_type);
osp->os_flags = BSWAP_64(osp->os_flags);
- if (size == sizeof (objset_phys_t)) {
+ if (size >= OBJSET_PHYS_SIZE_V2) {
dnode_byteswap(&osp->os_userused_dnode);
dnode_byteswap(&osp->os_groupused_dnode);
+ if (size >= sizeof (objset_phys_t))
+ dnode_byteswap(&osp->os_projectused_dnode);
}
}
@@ -419,6 +428,7 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
if (!BP_IS_HOLE(os->os_rootbp)) {
arc_flags_t aflags = ARC_FLAG_WAIT;
zbookmark_phys_t zb;
+ int size;
enum zio_flag zio_flags = ZIO_FLAG_CANFAIL;
SET_BOOKMARK(&zb, ds ? ds->ds_object : DMU_META_OBJSET,
ZB_ROOT_OBJECT, ZB_ROOT_LEVEL, ZB_ROOT_BLKID);
@@ -444,12 +454,19 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
return (err);
}
+ if (spa_version(spa) < SPA_VERSION_USERSPACE)
+ size = OBJSET_PHYS_SIZE_V1;
+ else if (!spa_feature_is_enabled(spa,
+ SPA_FEATURE_PROJECT_QUOTA))
+ size = OBJSET_PHYS_SIZE_V2;
+ else
+ size = sizeof (objset_phys_t);
+
/* Increase the blocksize if we are permitted. */
- if (spa_version(spa) >= SPA_VERSION_USERSPACE &&
- arc_buf_size(os->os_phys_buf) < sizeof (objset_phys_t)) {
+ if (arc_buf_size(os->os_phys_buf) < size) {
arc_buf_t *buf = arc_alloc_buf(spa, &os->os_phys_buf,
- ARC_BUFC_METADATA, sizeof (objset_phys_t));
- bzero(buf->b_data, sizeof (objset_phys_t));
+ ARC_BUFC_METADATA, size);
+ bzero(buf->b_data, size);
bcopy(os->os_phys_buf->b_data, buf->b_data,
arc_buf_size(os->os_phys_buf));
arc_buf_destroy(os->os_phys_buf, &os->os_phys_buf);
@@ -460,7 +477,7 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
os->os_flags = os->os_phys->os_flags;
} else {
int size = spa_version(spa) >= SPA_VERSION_USERSPACE ?
- sizeof (objset_phys_t) : OBJSET_OLD_PHYS_SIZE;
+ sizeof (objset_phys_t) : OBJSET_PHYS_SIZE_V1;
os->os_phys_buf = arc_alloc_buf(spa, &os->os_phys_buf,
ARC_BUFC_METADATA, size);
os->os_phys = os->os_phys_buf->b_data;
@@ -604,13 +621,19 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
dnode_special_open(os, &os->os_phys->os_meta_dnode,
DMU_META_DNODE_OBJECT, &os->os_meta_dnode);
- if (arc_buf_size(os->os_phys_buf) >= sizeof (objset_phys_t)) {
+ if (OBJSET_BUF_HAS_USERUSED(os->os_phys_buf)) {
dnode_special_open(os, &os->os_phys->os_userused_dnode,
DMU_USERUSED_OBJECT, &os->os_userused_dnode);
dnode_special_open(os, &os->os_phys->os_groupused_dnode,
DMU_GROUPUSED_OBJECT, &os->os_groupused_dnode);
+ if (OBJSET_BUF_HAS_PROJECTUSED(os->os_phys_buf))
+ dnode_special_open(os,
+ &os->os_phys->os_projectused_dnode,
+ DMU_PROJECTUSED_OBJECT, &os->os_projectused_dnode);
}
+ mutex_init(&os->os_upgrade_lock, NULL, MUTEX_DEFAULT, NULL);
+
*osp = os;
return (0);
}
@@ -749,8 +772,19 @@ dmu_objset_own(const char *name, dmu_objset_type_t type,
return (err);
}
- dsl_pool_rele(dp, FTAG);
+ /*
+ * User accounting requires the dataset to be decrypted and rw.
+ * We also don't begin user accounting during claiming to help
+ * speed up pool import times and to keep this txg reserved
+ * completely for recovery work.
+ */
+ if ((dmu_objset_userobjspace_upgradable(*osp) ||
+ dmu_objset_projectquota_upgradable(*osp)) &&
+ !readonly && !dp->dp_spa->spa_claiming &&
+ (ds->ds_dir->dd_crypto_obj == 0 || decrypt))
+ dmu_objset_id_quota_upgrade(*osp);
+ dsl_pool_rele(dp, FTAG);
return (0);
}
@@ -826,6 +860,10 @@ dmu_objset_refresh_ownership(dsl_dataset_t *ds, dsl_dataset_t **newds,
void
dmu_objset_disown(objset_t *os, boolean_t decrypt, void *tag)
{
+ /*
+ * Stop upgrading thread
+ */
+ dmu_objset_upgrade_stop(os);
dsl_dataset_disown(os->os_dsl_dataset,
(decrypt) ? DS_HOLD_FLAG_DECRYPT : 0, tag);
}
@@ -861,6 +899,8 @@ dmu_objset_evict_dbufs(objset_t *os)
mutex_exit(&os->os_lock);
if (DMU_USERUSED_DNODE(os) != NULL) {
+ if (DMU_PROJECTUSED_DNODE(os) != NULL)
+ dnode_evict_dbufs(DMU_PROJECTUSED_DNODE(os));
dnode_evict_dbufs(DMU_GROUPUSED_DNODE(os));
dnode_evict_dbufs(DMU_USERUSED_DNODE(os));
}
@@ -915,6 +955,8 @@ dmu_objset_evict_done(objset_t *os)
dnode_special_close(&os->os_meta_dnode);
if (DMU_USERUSED_DNODE(os)) {
+ if (DMU_PROJECTUSED_DNODE(os))
+ dnode_special_close(&os->os_projectused_dnode);
dnode_special_close(&os->os_userused_dnode);
dnode_special_close(&os->os_groupused_dnode);
}
@@ -1024,6 +1066,18 @@ dmu_objset_create_impl_dnstats(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
if (dmu_objset_userused_enabled(os) &&
(!os->os_encrypted || !dmu_objset_is_receiving(os))) {
os->os_phys->os_flags |= OBJSET_FLAG_USERACCOUNTING_COMPLETE;
+ if (dmu_objset_userobjused_enabled(os)) {
+ ds->ds_feature_activation_needed[
+ SPA_FEATURE_USEROBJ_ACCOUNTING] = B_TRUE;
+ os->os_phys->os_flags |=
+ OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE;
+ }
+ if (dmu_objset_projectquota_enabled(os)) {
+ ds->ds_feature_activation_needed[
+ SPA_FEATURE_PROJECT_QUOTA] = B_TRUE;
+ os->os_phys->os_flags |=
+ OBJSET_FLAG_PROJECTQUOTA_COMPLETE;
+ }
os->os_flags = os->os_phys->os_flags;
}
@@ -1415,6 +1469,58 @@ dmu_objset_snapshot_one(const char *fsname, const char *snapname)
}
static void
+dmu_objset_upgrade_task_cb(void *data)
+{
+ objset_t *os = data;
+
+ mutex_enter(&os->os_upgrade_lock);
+ os->os_upgrade_status = EINTR;
+ if (!os->os_upgrade_exit) {
+ mutex_exit(&os->os_upgrade_lock);
+
+ os->os_upgrade_status = os->os_upgrade_cb(os);
+ mutex_enter(&os->os_upgrade_lock);
+ }
+ os->os_upgrade_exit = B_TRUE;
+ os->os_upgrade_id = 0;
+ mutex_exit(&os->os_upgrade_lock);
+}
+
+static void
+dmu_objset_upgrade(objset_t *os, dmu_objset_upgrade_cb_t cb)
+{
+ if (os->os_upgrade_id != 0)
+ return;
+
+ mutex_enter(&os->os_upgrade_lock);
+ if (os->os_upgrade_id == 0 && os->os_upgrade_status == 0) {
+ os->os_upgrade_exit = B_FALSE;
+ os->os_upgrade_cb = cb;
+ os->os_upgrade_id = taskq_dispatch(
+ os->os_spa->spa_upgrade_taskq,
+ dmu_objset_upgrade_task_cb, os, TQ_SLEEP);
+ if (os->os_upgrade_id == 0)
+ os->os_upgrade_status = ENOMEM;
+ }
+ mutex_exit(&os->os_upgrade_lock);
+}
+
+static void
+dmu_objset_upgrade_stop(objset_t *os)
+{
+ mutex_enter(&os->os_upgrade_lock);
+ os->os_upgrade_exit = B_TRUE;
+ if (os->os_upgrade_id != 0) {
+ os->os_upgrade_id = 0;
+ mutex_exit(&os->os_upgrade_lock);
+
+ taskq_wait(os->os_spa->spa_upgrade_taskq);
+ } else {
+ mutex_exit(&os->os_upgrade_lock);
+ }
+}
+
+static void
dmu_objset_sync_dnodes(multilist_sublist_t *list, dmu_tx_t *tx)
{
dnode_t *dn;
@@ -1471,7 +1577,7 @@ dmu_objset_write_ready(zio_t *zio, arc_buf_t *abuf, void *arg)
* Update rootbp fill count: it should be the number of objects
* allocated in the object set (not counting the "special"
* objects that are stored in the objset_phys_t -- the meta
- * dnode and user/group accounting objects).
+ * dnode and user/group/project accounting objects).
*/
for (int i = 0; i < dnp->dn_nblkptr; i++)
fill += BP_GET_FILL(&dnp->dn_blkptr[i]);
@@ -1600,6 +1706,12 @@ dmu_objset_sync(objset_t *os, zio_t *pio, dmu_tx_t *tx)
dnode_sync(DMU_GROUPUSED_DNODE(os), tx);
}
+ if (DMU_PROJECTUSED_DNODE(os) &&
+ DMU_PROJECTUSED_DNODE(os)->dn_type != DMU_OT_NONE) {
+ DMU_PROJECTUSED_DNODE(os)->dn_zio = zio;
+ dnode_sync(DMU_PROJECTUSED_DNODE(os), tx);
+ }
+
txgoff = tx->tx_txg & TXG_MASK;
if (dmu_objset_userused_enabled(os) &&
@@ -1676,15 +1788,32 @@ dmu_objset_userused_enabled(objset_t *os)
DMU_USERUSED_DNODE(os) != NULL);
}
+boolean_t
+dmu_objset_userobjused_enabled(objset_t *os)
+{
+ return (dmu_objset_userused_enabled(os) &&
+ spa_feature_is_enabled(os->os_spa, SPA_FEATURE_USEROBJ_ACCOUNTING));
+}
+
+boolean_t
+dmu_objset_projectquota_enabled(objset_t *os)
+{
+ return (used_cbs[os->os_phys->os_type] != NULL &&
+ DMU_PROJECTUSED_DNODE(os) != NULL &&
+ spa_feature_is_enabled(os->os_spa, SPA_FEATURE_PROJECT_QUOTA));
+}
+
typedef struct userquota_node {
- uint64_t uqn_id;
- int64_t uqn_delta;
- avl_node_t uqn_node;
+ /* must be in the first field, see userquota_update_cache() */
+ char uqn_id[20 + DMU_OBJACCT_PREFIX_LEN];
+ int64_t uqn_delta;
+ avl_node_t uqn_node;
} userquota_node_t;
typedef struct userquota_cache {
avl_tree_t uqc_user_deltas;
avl_tree_t uqc_group_deltas;
+ avl_tree_t uqc_project_deltas;
} userquota_cache_t;
static int
@@ -1692,12 +1821,15 @@ userquota_compare(const void *l, const void *r)
{
const userquota_node_t *luqn = l;
const userquota_node_t *ruqn = r;
+ int rv;
- if (luqn->uqn_id < ruqn->uqn_id)
- return (-1);
- if (luqn->uqn_id > ruqn->uqn_id)
- return (1);
- return (0);
+ /*
+ * NB: can only access uqn_id because userquota_update_cache() doesn't
+ * pass in an entire userquota_node_t.
+ */
+ rv = strcmp(luqn->uqn_id, ruqn->uqn_id);
+
+ return (AVL_ISIGN(rv));
}
static void
@@ -1717,7 +1849,7 @@ do_userquota_cacheflush(objset_t *os, userquota_cache_t *cache, dmu_tx_t *tx)
* is not thread-safe (i.e. not atomic).
*/
mutex_enter(&os->os_userused_lock);
- VERIFY0(zap_increment_int(os, DMU_USERUSED_OBJECT,
+ VERIFY0(zap_increment(os, DMU_USERUSED_OBJECT,
uqn->uqn_id, uqn->uqn_delta, tx));
mutex_exit(&os->os_userused_lock);
kmem_free(uqn, sizeof (*uqn));
@@ -1728,40 +1860,96 @@ do_userquota_cacheflush(objset_t *os, userquota_cache_t *cache, dmu_tx_t *tx)
while ((uqn = avl_destroy_nodes(&cache->uqc_group_deltas,
&cookie)) != NULL) {
mutex_enter(&os->os_userused_lock);
- VERIFY0(zap_increment_int(os, DMU_GROUPUSED_OBJECT,
+ VERIFY0(zap_increment(os, DMU_GROUPUSED_OBJECT,
uqn->uqn_id, uqn->uqn_delta, tx));
mutex_exit(&os->os_userused_lock);
kmem_free(uqn, sizeof (*uqn));
}
avl_destroy(&cache->uqc_group_deltas);
+
+ if (dmu_objset_projectquota_enabled(os)) {
+ cookie = NULL;
+ while ((uqn = avl_destroy_nodes(&cache->uqc_project_deltas,
+ &cookie)) != NULL) {
+ mutex_enter(&os->os_userused_lock);
+ VERIFY0(zap_increment(os, DMU_PROJECTUSED_OBJECT,
+ uqn->uqn_id, uqn->uqn_delta, tx));
+ mutex_exit(&os->os_userused_lock);
+ kmem_free(uqn, sizeof (*uqn));
+ }
+ avl_destroy(&cache->uqc_project_deltas);
+ }
}
static void
-userquota_update_cache(avl_tree_t *avl, uint64_t id, int64_t delta)
+userquota_update_cache(avl_tree_t *avl, const char *id, int64_t delta)
{
- userquota_node_t search = { .uqn_id = id };
+ userquota_node_t *uqn;
avl_index_t idx;
- userquota_node_t *uqn = avl_find(avl, &search, &idx);
+ ASSERT(strlen(id) < sizeof (uqn->uqn_id));
+ /*
+ * Use id directly for searching because uqn_id is the first field of
+ * userquota_node_t and fields after uqn_id won't be accessed in
+ * avl_find().
+ */
+ uqn = avl_find(avl, (const void *)id, &idx);
if (uqn == NULL) {
uqn = kmem_zalloc(sizeof (*uqn), KM_SLEEP);
- uqn->uqn_id = id;
+ (void) strlcpy(uqn->uqn_id, id, sizeof (uqn->uqn_id));
avl_insert(avl, uqn, idx);
}
uqn->uqn_delta += delta;
}
static void
-do_userquota_update(userquota_cache_t *cache, uint64_t used, uint64_t flags,
- uint64_t user, uint64_t group, boolean_t subtract)
+do_userquota_update(objset_t *os, userquota_cache_t *cache, uint64_t used,
+ uint64_t flags, uint64_t user, uint64_t group, uint64_t project,
+ boolean_t subtract)
{
if ((flags & DNODE_FLAG_USERUSED_ACCOUNTED)) {
int64_t delta = DNODE_MIN_SIZE + used;
+ char name[20];
+
if (subtract)
delta = -delta;
- userquota_update_cache(&cache->uqc_user_deltas, user, delta);
- userquota_update_cache(&cache->uqc_group_deltas, group, delta);
+ (void) sprintf(name, "%llx", (longlong_t)user);
+ userquota_update_cache(&cache->uqc_user_deltas, name, delta);
+
+ (void) sprintf(name, "%llx", (longlong_t)group);
+ userquota_update_cache(&cache->uqc_group_deltas, name, delta);
+
+ if (dmu_objset_projectquota_enabled(os)) {
+ (void) sprintf(name, "%llx", (longlong_t)project);
+ userquota_update_cache(&cache->uqc_project_deltas,
+ name, delta);
+ }
+ }
+}
+
+static void
+do_userobjquota_update(objset_t *os, userquota_cache_t *cache, uint64_t flags,
+ uint64_t user, uint64_t group, uint64_t project, boolean_t subtract)
+{
+ if (flags & DNODE_FLAG_USEROBJUSED_ACCOUNTED) {
+ char name[20 + DMU_OBJACCT_PREFIX_LEN];
+ int delta = subtract ? -1 : 1;
+
+ (void) snprintf(name, sizeof (name), DMU_OBJACCT_PREFIX "%llx",
+ (longlong_t)user);
+ userquota_update_cache(&cache->uqc_user_deltas, name, delta);
+
+ (void) snprintf(name, sizeof (name), DMU_OBJACCT_PREFIX "%llx",
+ (longlong_t)group);
+ userquota_update_cache(&cache->uqc_group_deltas, name, delta);
+
+ if (dmu_objset_projectquota_enabled(os)) {
+ (void) snprintf(name, sizeof (name),
+ DMU_OBJACCT_PREFIX "%llx", (longlong_t)project);
+ userquota_update_cache(&cache->uqc_project_deltas,
+ name, delta);
+ }
}
}
@@ -1789,6 +1977,10 @@ userquota_updates_task(void *arg)
sizeof (userquota_node_t), offsetof(userquota_node_t, uqn_node));
avl_create(&cache.uqc_group_deltas, userquota_compare,
sizeof (userquota_node_t), offsetof(userquota_node_t, uqn_node));
+ if (dmu_objset_projectquota_enabled(os))
+ avl_create(&cache.uqc_project_deltas, userquota_compare,
+ sizeof (userquota_node_t), offsetof(userquota_node_t,
+ uqn_node));
while ((dn = multilist_sublist_head(list)) != NULL) {
int flags;
@@ -1800,15 +1992,21 @@ userquota_updates_task(void *arg)
flags = dn->dn_id_flags;
ASSERT(flags);
if (flags & DN_ID_OLD_EXIST) {
- do_userquota_update(&cache,
- dn->dn_oldused, dn->dn_oldflags,
- dn->dn_olduid, dn->dn_oldgid, B_TRUE);
+ do_userquota_update(os, &cache, dn->dn_oldused,
+ dn->dn_oldflags, dn->dn_olduid, dn->dn_oldgid,
+ dn->dn_oldprojid, B_TRUE);
+ do_userobjquota_update(os, &cache, dn->dn_oldflags,
+ dn->dn_olduid, dn->dn_oldgid,
+ dn->dn_oldprojid, B_TRUE);
}
if (flags & DN_ID_NEW_EXIST) {
- do_userquota_update(&cache,
- DN_USED_BYTES(dn->dn_phys),
- dn->dn_phys->dn_flags, dn->dn_newuid,
- dn->dn_newgid, B_FALSE);
+ do_userquota_update(os, &cache,
+ DN_USED_BYTES(dn->dn_phys), dn->dn_phys->dn_flags,
+ dn->dn_newuid, dn->dn_newgid,
+ dn->dn_newprojid, B_FALSE);
+ do_userobjquota_update(os, &cache,
+ dn->dn_phys->dn_flags, dn->dn_newuid, dn->dn_newgid,
+ dn->dn_newprojid, B_FALSE);
}
mutex_enter(&dn->dn_mtx);
@@ -1817,6 +2015,7 @@ userquota_updates_task(void *arg)
if (dn->dn_id_flags & DN_ID_NEW_EXIST) {
dn->dn_olduid = dn->dn_newuid;
dn->dn_oldgid = dn->dn_newgid;
+ dn->dn_oldprojid = dn->dn_newprojid;
dn->dn_id_flags |= DN_ID_OLD_EXIST;
if (dn->dn_bonuslen == 0)
dn->dn_id_flags |= DN_ID_CHKED_SPILL;
@@ -1842,11 +2041,17 @@ dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx)
if (!dmu_objset_userused_enabled(os))
return;
- /* if this is a raw receive just return and handle accounting later */
+ /*
+ * If this is a raw receive just return and handle accounting
+ * later when we have the keys loaded. We also don't do user
+ * accounting during claiming since the datasets are not owned
+ * for the duration of claiming and this txg should only be
+ * used for recovery.
+ */
if (os->os_encrypted && dmu_objset_is_receiving(os))
return;
- /* Allocate the user/groupused objects if necessary. */
+ /* Allocate the user/group/project used objects if necessary. */
if (DMU_USERUSED_DNODE(os)->dn_type == DMU_OT_NONE) {
VERIFY0(zap_create_claim(os,
DMU_USERUSED_OBJECT,
@@ -1856,6 +2061,12 @@ dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx)
DMU_OT_USERGROUP_USED, DMU_OT_NONE, 0, tx));
}
+ if (dmu_objset_projectquota_enabled(os) &&
+ DMU_PROJECTUSED_DNODE(os)->dn_type == DMU_OT_NONE) {
+ VERIFY0(zap_create_claim(os, DMU_PROJECTUSED_OBJECT,
+ DMU_OT_USERGROUP_USED, DMU_OT_NONE, 0, tx));
+ }
+
for (int i = 0;
i < multilist_get_num_sublists(os->os_synced_dnodes); i++) {
userquota_updates_arg_t *uua =
@@ -1918,6 +2129,7 @@ dmu_objset_userquota_get_ids(dnode_t *dn, boolean_t before, dmu_tx_t *tx)
dmu_buf_impl_t *db = NULL;
uint64_t *user = NULL;
uint64_t *group = NULL;
+ uint64_t *project = NULL;
int flags = dn->dn_id_flags;
int error;
boolean_t have_spill = B_FALSE;
@@ -1975,9 +2187,11 @@ dmu_objset_userquota_get_ids(dnode_t *dn, boolean_t before, dmu_tx_t *tx)
ASSERT(data);
user = &dn->dn_olduid;
group = &dn->dn_oldgid;
+ project = &dn->dn_oldprojid;
} else if (data) {
user = &dn->dn_newuid;
group = &dn->dn_newgid;
+ project = &dn->dn_newprojid;
}
/*
@@ -1985,7 +2199,7 @@ dmu_objset_userquota_get_ids(dnode_t *dn, boolean_t before, dmu_tx_t *tx)
* type has changed and that type isn't an object type to track
*/
error = used_cbs[os->os_phys->os_type](dn->dn_bonustype, data,
- user, group);
+ user, group, project);
/*
* Preserve existing uid/gid when the callback can't determine
@@ -1998,9 +2212,11 @@ dmu_objset_userquota_get_ids(dnode_t *dn, boolean_t before, dmu_tx_t *tx)
if (flags & DN_ID_OLD_EXIST) {
dn->dn_newuid = dn->dn_olduid;
dn->dn_newgid = dn->dn_oldgid;
+ dn->dn_newgid = dn->dn_oldprojid;
} else {
dn->dn_newuid = 0;
dn->dn_newgid = 0;
+ dn->dn_newprojid = ZFS_DEFAULT_PROJID;
}
error = 0;
}
@@ -2031,19 +2247,26 @@ dmu_objset_userspace_present(objset_t *os)
OBJSET_FLAG_USERACCOUNTING_COMPLETE);
}
-int
-dmu_objset_userspace_upgrade(objset_t *os)
+boolean_t
+dmu_objset_userobjspace_present(objset_t *os)
+{
+ return (os->os_phys->os_flags &
+ OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE);
+}
+
+boolean_t
+dmu_objset_projectquota_present(objset_t *os)
+{
+ return (os->os_phys->os_flags &
+ OBJSET_FLAG_PROJECTQUOTA_COMPLETE);
+}
+
+static int
+dmu_objset_space_upgrade(objset_t *os)
{
uint64_t obj;
int err = 0;
- if (dmu_objset_userspace_present(os))
- return (0);
- if (!dmu_objset_userused_enabled(os))
- return (SET_ERROR(ENOTSUP));
- if (dmu_objset_is_snapshot(os))
- return (SET_ERROR(EINVAL));
-
/*
* We simply need to mark every object dirty, so that it will be
* synced out and now accounted. If this is called
@@ -2057,8 +2280,21 @@ dmu_objset_userspace_upgrade(objset_t *os)
dmu_buf_t *db;
int objerr;
- if (issig(JUSTLOOKING) && issig(FORREAL))
- return (SET_ERROR(EINTR));
+ mutex_enter(&os->os_upgrade_lock);
+ if (os->os_upgrade_exit)
+ err = SET_ERROR(EINTR);
+ mutex_exit(&os->os_upgrade_lock);
+ if (err != 0)
+ return (err);
+
+ /*
+ * The following is only valid on Linux since we cannot send
+ * a signal to a kernel thread on illumos (because we have no
+ * lwp and never return to user-land).
+ *
+ * if (issig(JUSTLOOKING) && issig(FORREAL))
+ * return (SET_ERROR(EINTR));
+ */
objerr = dmu_bonus_hold(os, obj, FTAG, &db);
if (objerr != 0)
@@ -2074,12 +2310,89 @@ dmu_objset_userspace_upgrade(objset_t *os)
dmu_buf_rele(db, FTAG);
dmu_tx_commit(tx);
}
+ return (0);
+}
+
+int
+dmu_objset_userspace_upgrade(objset_t *os)
+{
+ int err = 0;
+
+ if (dmu_objset_userspace_present(os))
+ return (0);
+ if (dmu_objset_is_snapshot(os))
+ return (SET_ERROR(EINVAL));
+ if (!dmu_objset_userused_enabled(os))
+ return (SET_ERROR(ENOTSUP));
+
+ err = dmu_objset_space_upgrade(os);
+ if (err)
+ return (err);
os->os_flags |= OBJSET_FLAG_USERACCOUNTING_COMPLETE;
txg_wait_synced(dmu_objset_pool(os), 0);
return (0);
}
+static int
+dmu_objset_id_quota_upgrade_cb(objset_t *os)
+{
+ int err = 0;
+
+ if (dmu_objset_userobjspace_present(os) &&
+ dmu_objset_projectquota_present(os))
+ return (0);
+ if (dmu_objset_is_snapshot(os))
+ return (SET_ERROR(EINVAL));
+ if (!dmu_objset_userobjused_enabled(os))
+ return (SET_ERROR(ENOTSUP));
+ if (!dmu_objset_projectquota_enabled(os) &&
+ dmu_objset_userobjspace_present(os))
+ return (SET_ERROR(ENOTSUP));
+
+ dmu_objset_ds(os)->ds_feature_activation_needed[
+ SPA_FEATURE_USEROBJ_ACCOUNTING] = B_TRUE;
+ if (dmu_objset_projectquota_enabled(os))
+ dmu_objset_ds(os)->ds_feature_activation_needed[
+ SPA_FEATURE_PROJECT_QUOTA] = B_TRUE;
+
+ err = dmu_objset_space_upgrade(os);
+ if (err)
+ return (err);
+
+ os->os_flags |= OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE;
+ if (dmu_objset_projectquota_enabled(os))
+ os->os_flags |= OBJSET_FLAG_PROJECTQUOTA_COMPLETE;
+
+ txg_wait_synced(dmu_objset_pool(os), 0);
+ return (0);
+}
+
+void
+dmu_objset_id_quota_upgrade(objset_t *os)
+{
+ dmu_objset_upgrade(os, dmu_objset_id_quota_upgrade_cb);
+}
+
+boolean_t
+dmu_objset_userobjspace_upgradable(objset_t *os)
+{
+ return (dmu_objset_type(os) == DMU_OST_ZFS &&
+ !dmu_objset_is_snapshot(os) &&
+ dmu_objset_userobjused_enabled(os) &&
+ !dmu_objset_userobjspace_present(os) &&
+ spa_writeable(dmu_objset_spa(os)));
+}
+
+boolean_t
+dmu_objset_projectquota_upgradable(objset_t *os)
+{
+ return (dmu_objset_type(os) == DMU_OST_ZFS &&
+ !dmu_objset_is_snapshot(os) &&
+ dmu_objset_projectquota_enabled(os) &&
+ !dmu_objset_projectquota_present(os));
+}
+
void
dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
uint64_t *usedobjsp, uint64_t *availobjsp)
diff --git a/usr/src/uts/common/fs/zfs/dmu_traverse.c b/usr/src/uts/common/fs/zfs/dmu_traverse.c
index 0547a09498..c8e4899d07 100644
--- a/usr/src/uts/common/fs/zfs/dmu_traverse.c
+++ b/usr/src/uts/common/fs/zfs/dmu_traverse.c
@@ -376,7 +376,11 @@ traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp,
if (osp->os_meta_dnode.dn_maxblkid == 0)
td->td_realloc_possible = B_FALSE;
- if (arc_buf_size(buf) >= sizeof (objset_phys_t)) {
+ if (OBJSET_BUF_HAS_USERUSED(buf)) {
+ if (OBJSET_BUF_HAS_PROJECTUSED(buf))
+ prefetch_dnode_metadata(td,
+ &osp->os_projectused_dnode,
+ zb->zb_objset, DMU_PROJECTUSED_OBJECT);
prefetch_dnode_metadata(td, &osp->os_groupused_dnode,
zb->zb_objset, DMU_GROUPUSED_OBJECT);
prefetch_dnode_metadata(td, &osp->os_userused_dnode,
@@ -385,13 +389,19 @@ traverse_visitbp(traverse_data_t *td, const dnode_phys_t *dnp,
err = traverse_dnode(td, &osp->os_meta_dnode, zb->zb_objset,
DMU_META_DNODE_OBJECT);
- if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) {
- err = traverse_dnode(td, &osp->os_groupused_dnode,
- zb->zb_objset, DMU_GROUPUSED_OBJECT);
- }
- if (err == 0 && arc_buf_size(buf) >= sizeof (objset_phys_t)) {
- err = traverse_dnode(td, &osp->os_userused_dnode,
- zb->zb_objset, DMU_USERUSED_OBJECT);
+ if (err == 0 && OBJSET_BUF_HAS_USERUSED(buf)) {
+ if (OBJSET_BUF_HAS_PROJECTUSED(buf))
+ err = traverse_dnode(td,
+ &osp->os_projectused_dnode, zb->zb_objset,
+ DMU_PROJECTUSED_OBJECT);
+ if (err == 0)
+ err = traverse_dnode(td,
+ &osp->os_groupused_dnode, zb->zb_objset,
+ DMU_GROUPUSED_OBJECT);
+ if (err == 0)
+ err = traverse_dnode(td,
+ &osp->os_userused_dnode, zb->zb_objset,
+ DMU_USERUSED_OBJECT);
}
}
diff --git a/usr/src/uts/common/fs/zfs/dnode.c b/usr/src/uts/common/fs/zfs/dnode.c
index 5a86650d28..90f425a800 100644
--- a/usr/src/uts/common/fs/zfs/dnode.c
+++ b/usr/src/uts/common/fs/zfs/dnode.c
@@ -39,6 +39,7 @@
#include <sys/zio.h>
#include <sys/dmu_zfetch.h>
#include <sys/range_tree.h>
+#include <sys/zfs_project.h>
dnode_stats_t dnode_stats = {
{ "dnode_hold_dbuf_hold", KSTAT_DATA_UINT64 },
@@ -159,8 +160,10 @@ dnode_cons(void *arg, void *unused, int kmflag)
dn->dn_oldflags = 0;
dn->dn_olduid = 0;
dn->dn_oldgid = 0;
+ dn->dn_oldprojid = ZFS_DEFAULT_PROJID;
dn->dn_newuid = 0;
dn->dn_newgid = 0;
+ dn->dn_newprojid = ZFS_DEFAULT_PROJID;
dn->dn_id_flags = 0;
dn->dn_dbufs_count = 0;
@@ -213,8 +216,10 @@ dnode_dest(void *arg, void *unused)
ASSERT0(dn->dn_oldflags);
ASSERT0(dn->dn_olduid);
ASSERT0(dn->dn_oldgid);
+ ASSERT0(dn->dn_oldprojid);
ASSERT0(dn->dn_newuid);
ASSERT0(dn->dn_newgid);
+ ASSERT0(dn->dn_newprojid);
ASSERT0(dn->dn_id_flags);
ASSERT0(dn->dn_dbufs_count);
@@ -554,8 +559,10 @@ dnode_destroy(dnode_t *dn)
dn->dn_oldflags = 0;
dn->dn_olduid = 0;
dn->dn_oldgid = 0;
+ dn->dn_oldprojid = ZFS_DEFAULT_PROJID;
dn->dn_newuid = 0;
dn->dn_newgid = 0;
+ dn->dn_newprojid = ZFS_DEFAULT_PROJID;
dn->dn_id_flags = 0;
dmu_zfetch_fini(&dn->dn_zfetch);
@@ -815,8 +822,10 @@ dnode_move_impl(dnode_t *odn, dnode_t *ndn)
ndn->dn_oldflags = odn->dn_oldflags;
ndn->dn_olduid = odn->dn_olduid;
ndn->dn_oldgid = odn->dn_oldgid;
+ ndn->dn_oldprojid = odn->dn_oldprojid;
ndn->dn_newuid = odn->dn_newuid;
ndn->dn_newgid = odn->dn_newgid;
+ ndn->dn_newprojid = odn->dn_newprojid;
ndn->dn_id_flags = odn->dn_id_flags;
dmu_zfetch_init(&ndn->dn_zfetch, NULL);
list_move_tail(&ndn->dn_zfetch.zf_stream, &odn->dn_zfetch.zf_stream);
@@ -876,8 +885,10 @@ dnode_move_impl(dnode_t *odn, dnode_t *ndn)
odn->dn_oldflags = 0;
odn->dn_olduid = 0;
odn->dn_oldgid = 0;
+ odn->dn_oldprojid = ZFS_DEFAULT_PROJID;
odn->dn_newuid = 0;
odn->dn_newgid = 0;
+ odn->dn_newprojid = ZFS_DEFAULT_PROJID;
odn->dn_id_flags = 0;
/*
@@ -1289,9 +1300,14 @@ dnode_hold_impl(objset_t *os, uint64_t object, int flag, int slots,
ASSERT((flag & DNODE_MUST_BE_ALLOCATED) || (flag & DNODE_MUST_BE_FREE));
- if (object == DMU_USERUSED_OBJECT || object == DMU_GROUPUSED_OBJECT) {
- dn = (object == DMU_USERUSED_OBJECT) ?
- DMU_USERUSED_DNODE(os) : DMU_GROUPUSED_DNODE(os);
+ if (object == DMU_USERUSED_OBJECT || object == DMU_GROUPUSED_OBJECT ||
+ object == DMU_PROJECTUSED_OBJECT) {
+ if (object == DMU_USERUSED_OBJECT)
+ dn = DMU_USERUSED_DNODE(os);
+ else if (object == DMU_GROUPUSED_OBJECT)
+ dn = DMU_GROUPUSED_DNODE(os);
+ else
+ dn = DMU_PROJECTUSED_DNODE(os);
if (dn == NULL)
return (SET_ERROR(ENOENT));
type = dn->dn_type;
diff --git a/usr/src/uts/common/fs/zfs/dnode_sync.c b/usr/src/uts/common/fs/zfs/dnode_sync.c
index f5ee8a290d..dc7317b411 100644
--- a/usr/src/uts/common/fs/zfs/dnode_sync.c
+++ b/usr/src/uts/common/fs/zfs/dnode_sync.c
@@ -622,12 +622,17 @@ dnode_sync(dnode_t *dn, dmu_tx_t *tx)
dn->dn_oldused = DN_USED_BYTES(dn->dn_phys);
dn->dn_oldflags = dn->dn_phys->dn_flags;
dn->dn_phys->dn_flags |= DNODE_FLAG_USERUSED_ACCOUNTED;
+ if (dmu_objset_userobjused_enabled(dn->dn_objset))
+ dn->dn_phys->dn_flags |=
+ DNODE_FLAG_USEROBJUSED_ACCOUNTED;
mutex_exit(&dn->dn_mtx);
dmu_objset_userquota_get_ids(dn, B_FALSE, tx);
} else {
/* Once we account for it, we should always account for it */
ASSERT(!(dn->dn_phys->dn_flags &
DNODE_FLAG_USERUSED_ACCOUNTED));
+ ASSERT(!(dn->dn_phys->dn_flags &
+ DNODE_FLAG_USEROBJUSED_ACCOUNTED));
}
mutex_enter(&dn->dn_mtx);
diff --git a/usr/src/uts/common/fs/zfs/dsl_pool.c b/usr/src/uts/common/fs/zfs/dsl_pool.c
index a58b3d4f52..c09cec15a5 100644
--- a/usr/src/uts/common/fs/zfs/dsl_pool.c
+++ b/usr/src/uts/common/fs/zfs/dsl_pool.c
@@ -660,7 +660,7 @@ dsl_pool_sync(dsl_pool_t *dp, uint64_t txg)
/*
* After the data blocks have been written (ensured by the zio_wait()
- * above), update the user/group space accounting. This happens
+ * above), update the user/group/project space accounting. This happens
* in tasks dispatched to dp_sync_taskq, so wait for them before
* continuing.
*/
diff --git a/usr/src/uts/common/fs/zfs/dsl_scan.c b/usr/src/uts/common/fs/zfs/dsl_scan.c
index 73634e33e2..22e808dfd9 100644
--- a/usr/src/uts/common/fs/zfs/dsl_scan.c
+++ b/usr/src/uts/common/fs/zfs/dsl_scan.c
@@ -1826,11 +1826,15 @@ dsl_scan_recurse(dsl_scan_t *scn, dsl_dataset_t *ds, dmu_objset_type_t ostype,
if (OBJSET_BUF_HAS_USERUSED(buf)) {
/*
- * We also always visit user/group accounting
+ * We also always visit user/group/project accounting
* objects, and never skip them, even if we are
* suspending. This is necessary so that the space
* deltas from this txg get integrated.
*/
+ if (OBJSET_BUF_HAS_PROJECTUSED(buf))
+ dsl_scan_visitdnode(scn, ds, osp->os_type,
+ &osp->os_projectused_dnode,
+ DMU_PROJECTUSED_OBJECT, tx);
dsl_scan_visitdnode(scn, ds, osp->os_type,
&osp->os_groupused_dnode,
DMU_GROUPUSED_OBJECT, tx);
diff --git a/usr/src/uts/common/fs/zfs/sa.c b/usr/src/uts/common/fs/zfs/sa.c
index 10822a9c00..3bab3d568d 100644
--- a/usr/src/uts/common/fs/zfs/sa.c
+++ b/usr/src/uts/common/fs/zfs/sa.c
@@ -26,6 +26,7 @@
* Copyright (c) 2014 Spectra Logic Corporation, All rights reserved.
* Copyright (c) 2015 Joyent, Inc. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
+ * Copyright 2019 Joyent, Inc.
*/
#include <sys/zfs_context.h>
@@ -47,6 +48,10 @@
#include <sys/errno.h>
#include <sys/zfs_context.h>
+#ifdef _KERNEL
+#include <sys/zfs_znode.h>
+#endif
+
/*
* ZFS System attributes:
*
@@ -1441,8 +1446,9 @@ sa_lookup_impl(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count)
return (sa_attr_op(hdl, bulk, count, SA_LOOKUP, NULL));
}
-int
-sa_lookup(sa_handle_t *hdl, sa_attr_type_t attr, void *buf, uint32_t buflen)
+static int
+sa_lookup_locked(sa_handle_t *hdl, sa_attr_type_t attr, void *buf,
+ uint32_t buflen)
{
int error;
sa_bulk_attr_t bulk;
@@ -1453,9 +1459,19 @@ sa_lookup(sa_handle_t *hdl, sa_attr_type_t attr, void *buf, uint32_t buflen)
bulk.sa_data_func = NULL;
ASSERT(hdl);
- mutex_enter(&hdl->sa_lock);
error = sa_lookup_impl(hdl, &bulk, 1);
+ return (error);
+}
+
+int
+sa_lookup(sa_handle_t *hdl, sa_attr_type_t attr, void *buf, uint32_t buflen)
+{
+ int error;
+
+ mutex_enter(&hdl->sa_lock);
+ error = sa_lookup_locked(hdl, attr, buf, buflen);
mutex_exit(&hdl->sa_lock);
+
return (error);
}
@@ -1481,6 +1497,173 @@ sa_lookup_uio(sa_handle_t *hdl, sa_attr_type_t attr, uio_t *uio)
return (error);
}
+
+/*
+ * For the existing object that is upgraded from old system, its ondisk layout
+ * has no slot for the project ID attribute. But quota accounting logic needs
+ * to access related slots by offset directly. So we need to adjust these old
+ * objects' layout to make the project ID to some unified and fixed offset.
+ */
+int
+sa_add_projid(sa_handle_t *hdl, dmu_tx_t *tx, uint64_t projid)
+{
+ znode_t *zp = sa_get_userdata(hdl);
+ dmu_buf_t *db = sa_get_db(hdl);
+ zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ int count = 0, err = 0;
+ sa_bulk_attr_t *bulk, *attrs;
+ zfs_acl_locator_cb_t locate = { 0 };
+ uint64_t uid, gid, mode, rdev, xattr = 0, parent, gen, links;
+ uint64_t crtime[2], mtime[2], ctime[2], atime[2];
+ zfs_acl_phys_t znode_acl = { 0 };
+ char scanstamp[AV_SCANSTAMP_SZ];
+
+ if (zp->z_acl_cached == NULL) {
+ zfs_acl_t *aclp;
+
+ mutex_enter(&zp->z_acl_lock);
+ err = zfs_acl_node_read(zp, B_FALSE, &aclp, B_FALSE);
+ mutex_exit(&zp->z_acl_lock);
+ if (err != 0 && err != ENOENT)
+ return (err);
+ }
+
+ bulk = kmem_zalloc(sizeof (sa_bulk_attr_t) * ZPL_END, KM_SLEEP);
+ attrs = kmem_zalloc(sizeof (sa_bulk_attr_t) * ZPL_END, KM_SLEEP);
+ mutex_enter(&hdl->sa_lock);
+ mutex_enter(&zp->z_lock);
+
+ err = sa_lookup_locked(hdl, SA_ZPL_PROJID(zfsvfs), &projid,
+ sizeof (uint64_t));
+ if (unlikely(err == 0))
+ /* Someone has added project ID attr by race. */
+ err = EEXIST;
+ if (err != ENOENT)
+ goto out;
+
+ /* First do a bulk query of the attributes that aren't cached */
+ if (zp->z_is_sa) {
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL,
+ &mode, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GEN(zfsvfs), NULL,
+ &gen, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zfsvfs), NULL,
+ &uid, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs), NULL,
+ &gid, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PARENT(zfsvfs), NULL,
+ &parent, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL,
+ &atime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL,
+ &mtime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
+ &ctime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL,
+ &crtime, 16);
+ if (S_ISBLK(zp->z_mode) || S_ISCHR(zp->z_mode))
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_RDEV(zfsvfs), NULL,
+ &rdev, 8);
+ } else {
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ATIME(zfsvfs), NULL,
+ &atime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MTIME(zfsvfs), NULL,
+ &mtime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CTIME(zfsvfs), NULL,
+ &ctime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_CRTIME(zfsvfs), NULL,
+ &crtime, 16);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GEN(zfsvfs), NULL,
+ &gen, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_MODE(zfsvfs), NULL,
+ &mode, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PARENT(zfsvfs), NULL,
+ &parent, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_XATTR(zfsvfs), NULL,
+ &xattr, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_RDEV(zfsvfs), NULL,
+ &rdev, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zfsvfs), NULL,
+ &uid, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs), NULL,
+ &gid, 8);
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_ZNODE_ACL(zfsvfs), NULL,
+ &znode_acl, 88);
+ }
+ err = sa_bulk_lookup_locked(hdl, bulk, count);
+ if (err != 0)
+ goto out;
+
+ err = sa_lookup_locked(hdl, SA_ZPL_XATTR(zfsvfs), &xattr, 8);
+ if (err != 0 && err != ENOENT)
+ goto out;
+
+ zp->z_projid = projid;
+ zp->z_pflags |= ZFS_PROJID;
+ links = zp->z_links;
+ count = 0;
+ err = 0;
+
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_MODE(zfsvfs), NULL, &mode, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_SIZE(zfsvfs), NULL,
+ &zp->z_size, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_GEN(zfsvfs), NULL, &gen, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_UID(zfsvfs), NULL, &uid, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_GID(zfsvfs), NULL, &gid, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_PARENT(zfsvfs), NULL, &parent, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_FLAGS(zfsvfs), NULL,
+ &zp->z_pflags, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_ATIME(zfsvfs), NULL, &atime, 16);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_MTIME(zfsvfs), NULL, &mtime, 16);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_CTIME(zfsvfs), NULL, &ctime, 16);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_CRTIME(zfsvfs), NULL,
+ &crtime, 16);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_LINKS(zfsvfs), NULL, &links, 8);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_PROJID(zfsvfs), NULL, &projid, 8);
+
+ if (S_ISBLK(zp->z_mode) || S_ISCHR(zp->z_mode))
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_RDEV(zfsvfs), NULL,
+ &rdev, 8);
+
+ if (zp->z_acl_cached != NULL) {
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_DACL_COUNT(zfsvfs), NULL,
+ &zp->z_acl_cached->z_acl_count, 8);
+ if (zp->z_acl_cached->z_version < ZFS_ACL_VERSION_FUID)
+ zfs_acl_xform(zp, zp->z_acl_cached, CRED());
+ locate.cb_aclp = zp->z_acl_cached;
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_DACL_ACES(zfsvfs),
+ zfs_acl_data_locator, &locate,
+ zp->z_acl_cached->z_acl_bytes);
+ }
+
+ if (xattr)
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_XATTR(zfsvfs), NULL,
+ &xattr, 8);
+
+ if (zp->z_pflags & ZFS_BONUS_SCANSTAMP) {
+ bcopy((caddr_t)db->db_data + ZFS_OLD_ZNODE_PHYS_SIZE,
+ scanstamp, AV_SCANSTAMP_SZ);
+ SA_ADD_BULK_ATTR(attrs, count, SA_ZPL_SCANSTAMP(zfsvfs), NULL,
+ scanstamp, AV_SCANSTAMP_SZ);
+ zp->z_pflags &= ~ZFS_BONUS_SCANSTAMP;
+ }
+
+ VERIFY(dmu_set_bonustype(db, DMU_OT_SA, tx) == 0);
+ VERIFY(sa_replace_all_by_template_locked(hdl, attrs, count, tx) == 0);
+ if (znode_acl.z_acl_extern_obj) {
+ VERIFY(0 == dmu_object_free(zfsvfs->z_os,
+ znode_acl.z_acl_extern_obj, tx));
+ }
+
+ zp->z_is_sa = B_TRUE;
+
+out:
+ mutex_exit(&zp->z_lock);
+ mutex_exit(&hdl->sa_lock);
+ kmem_free(attrs, sizeof (sa_bulk_attr_t) * ZPL_END);
+ kmem_free(bulk, sizeof (sa_bulk_attr_t) * ZPL_END);
+ return (err);
+}
#endif
static sa_idx_tab_t *
diff --git a/usr/src/uts/common/fs/zfs/spa.c b/usr/src/uts/common/fs/zfs/spa.c
index 8d36ce13e1..7f865d9e97 100644
--- a/usr/src/uts/common/fs/zfs/spa.c
+++ b/usr/src/uts/common/fs/zfs/spa.c
@@ -1210,6 +1210,13 @@ spa_activate(spa_t *spa, int mode)
offsetof(spa_error_entry_t, se_avl));
spa_keystore_init(&spa->spa_keystore);
+
+ /*
+ * The taskq to upgrade datasets in this pool. Currently used by
+ * feature SPA_FEATURE_USEROBJ_ACCOUNTING/SPA_FEATURE_PROJECT_QUOTA.
+ */
+ spa->spa_upgrade_taskq = taskq_create("z_upgrade", boot_ncpus,
+ minclsyspri, 1, INT_MAX, TASKQ_DYNAMIC);
}
/*
@@ -1226,6 +1233,11 @@ spa_deactivate(spa_t *spa)
spa_evicting_os_wait(spa);
+ if (spa->spa_upgrade_taskq) {
+ taskq_destroy(spa->spa_upgrade_taskq);
+ spa->spa_upgrade_taskq = NULL;
+ }
+
txg_list_destroy(&spa->spa_vdev_txg_list);
list_destroy(&spa->spa_config_dirty_list);
diff --git a/usr/src/uts/common/fs/zfs/sys/dmu.h b/usr/src/uts/common/fs/zfs/sys/dmu.h
index ffce616cbc..1001f52864 100644
--- a/usr/src/uts/common/fs/zfs/sys/dmu.h
+++ b/usr/src/uts/common/fs/zfs/sys/dmu.h
@@ -306,6 +306,13 @@ void zfs_znode_byteswap(void *buf, size_t size);
#define DMU_USERUSED_OBJECT (-1ULL)
#define DMU_GROUPUSED_OBJECT (-2ULL)
+#define DMU_PROJECTUSED_OBJECT (-3ULL)
+
+/*
+ * Zap prefix for object accounting in DMU_{USER,GROUP,PROJECT}USED_OBJECT.
+ */
+#define DMU_OBJACCT_PREFIX "obj-"
+#define DMU_OBJACCT_PREFIX_LEN 4
/*
* artificial blkids for bonus buffer and spill blocks
@@ -1006,7 +1013,7 @@ extern int dmu_dir_list_next(objset_t *os, int namelen, char *name,
uint64_t *idp, uint64_t *offp);
typedef int objset_used_cb_t(dmu_object_type_t bonustype,
- void *bonus, uint64_t *userp, uint64_t *groupp);
+ void *bonus, uint64_t *userp, uint64_t *groupp, uint64_t *projectp);
extern void dmu_objset_register_type(dmu_objset_type_t ost,
objset_used_cb_t *cb);
extern void dmu_objset_set_user(objset_t *os, void *user_ptr);
diff --git a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h
index 41ae18a8b9..dac448d161 100644
--- a/usr/src/uts/common/fs/zfs/sys/dmu_objset.h
+++ b/usr/src/uts/common/fs/zfs/sys/dmu_objset.h
@@ -51,13 +51,18 @@ struct dsl_pool;
struct dsl_dataset;
struct dmu_tx;
-#define OBJSET_PHYS_SIZE 2048
-#define OBJSET_OLD_PHYS_SIZE 1024
+#define OBJSET_PHYS_SIZE_V1 1024
+#define OBJSET_PHYS_SIZE_V2 2048
+#define OBJSET_PHYS_SIZE_V3 4096
#define OBJSET_BUF_HAS_USERUSED(buf) \
- (arc_buf_size(buf) > OBJSET_OLD_PHYS_SIZE)
+ (arc_buf_size(buf) >= OBJSET_PHYS_SIZE_V2)
+#define OBJSET_BUF_HAS_PROJECTUSED(buf) \
+ (arc_buf_size(buf) >= OBJSET_PHYS_SIZE_V3)
-#define OBJSET_FLAG_USERACCOUNTING_COMPLETE (1ULL<<0)
+#define OBJSET_FLAG_USERACCOUNTING_COMPLETE (1ULL << 0)
+#define OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE (1ULL << 1)
+#define OBJSET_FLAG_PROJECTQUOTA_COMPLETE (1ULL << 2)
/* all flags are currently non-portable */
#define OBJSET_CRYPT_PORTABLE_FLAGS_MASK (0)
@@ -69,13 +74,18 @@ typedef struct objset_phys {
uint64_t os_flags;
uint8_t os_portable_mac[ZIO_OBJSET_MAC_LEN];
uint8_t os_local_mac[ZIO_OBJSET_MAC_LEN];
- char os_pad[OBJSET_PHYS_SIZE - sizeof (dnode_phys_t)*3 -
+ char os_pad0[OBJSET_PHYS_SIZE_V2 - sizeof (dnode_phys_t)*3 -
sizeof (zil_header_t) - sizeof (uint64_t)*2 -
2*ZIO_OBJSET_MAC_LEN];
dnode_phys_t os_userused_dnode;
dnode_phys_t os_groupused_dnode;
+ dnode_phys_t os_projectused_dnode;
+ char os_pad1[OBJSET_PHYS_SIZE_V3 - OBJSET_PHYS_SIZE_V2 -
+ sizeof (dnode_phys_t)];
} objset_phys_t;
+typedef int (*dmu_objset_upgrade_cb_t)(objset_t *);
+
#define OBJSET_PROP_UNINITIALIZED ((uint64_t)-1)
struct objset {
/* Immutable: */
@@ -94,6 +104,7 @@ struct objset {
dnode_handle_t os_meta_dnode;
dnode_handle_t os_userused_dnode;
dnode_handle_t os_groupused_dnode;
+ dnode_handle_t os_projectused_dnode;
zilog_t *os_zil;
list_node_t os_evicting_node;
@@ -159,13 +170,20 @@ struct objset {
list_t os_dnodes;
list_t os_downgraded_dbufs;
- /* Protects changes to DMU_{USER,GROUP}USED_OBJECT */
+ /* Protects changes to DMU_{USER,GROUP,PROJECT}USED_OBJECT */
kmutex_t os_userused_lock;
/* stuff we store for the user */
kmutex_t os_user_ptr_lock;
void *os_user_ptr;
sa_os_t *os_sa;
+
+ /* kernel thread to upgrade this dataset */
+ kmutex_t os_upgrade_lock;
+ taskqid_t os_upgrade_id;
+ dmu_objset_upgrade_cb_t os_upgrade_cb;
+ boolean_t os_upgrade_exit;
+ int os_upgrade_status;
};
#define DMU_META_OBJSET 0
@@ -174,6 +192,7 @@ struct objset {
#define DMU_META_DNODE(os) ((os)->os_meta_dnode.dnh_dnode)
#define DMU_USERUSED_DNODE(os) ((os)->os_userused_dnode.dnh_dnode)
#define DMU_GROUPUSED_DNODE(os) ((os)->os_groupused_dnode.dnh_dnode)
+#define DMU_PROJECTUSED_DNODE(os) ((os)->os_projectused_dnode.dnh_dnode)
#define DMU_OS_IS_L2CACHEABLE(os) \
((os)->os_secondary_cache == ZFS_CACHE_ALL || \
@@ -225,6 +244,14 @@ void dmu_objset_userquota_get_ids(dnode_t *dn, boolean_t before, dmu_tx_t *tx);
boolean_t dmu_objset_userused_enabled(objset_t *os);
int dmu_objset_userspace_upgrade(objset_t *os);
boolean_t dmu_objset_userspace_present(objset_t *os);
+boolean_t dmu_objset_userobjspace_upgradable(objset_t *os);
+boolean_t dmu_objset_userobjused_enabled(objset_t *os);
+boolean_t dmu_objset_userobjspace_present(objset_t *os);
+boolean_t dmu_objset_projectquota_enabled(objset_t *os);
+boolean_t dmu_objset_projectquota_present(objset_t *os);
+boolean_t dmu_objset_projectquota_upgradable(objset_t *os);
+void dmu_objset_id_quota_upgrade(objset_t *os);
+
boolean_t dmu_objset_incompatible_encryption_version(objset_t *os);
int dmu_fsname(const char *snapname, char *buf);
diff --git a/usr/src/uts/common/fs/zfs/sys/dnode.h b/usr/src/uts/common/fs/zfs/sys/dnode.h
index da72903113..6c8ec5e229 100644
--- a/usr/src/uts/common/fs/zfs/sys/dnode.h
+++ b/usr/src/uts/common/fs/zfs/sys/dnode.h
@@ -142,11 +142,14 @@ enum dnode_dirtycontext {
};
/* Is dn_used in bytes? if not, it's in multiples of SPA_MINBLOCKSIZE */
-#define DNODE_FLAG_USED_BYTES (1<<0)
-#define DNODE_FLAG_USERUSED_ACCOUNTED (1<<1)
+#define DNODE_FLAG_USED_BYTES (1 << 0)
+#define DNODE_FLAG_USERUSED_ACCOUNTED (1 << 1)
/* Does dnode have a SA spill blkptr in bonus? */
-#define DNODE_FLAG_SPILL_BLKPTR (1<<2)
+#define DNODE_FLAG_SPILL_BLKPTR (1 << 2)
+
+/* User/Group/Project dnode accounting */
+#define DNODE_FLAG_USEROBJUSED_ACCOUNTED (1 << 3)
/*
* VARIABLE-LENGTH (LARGE) DNODES
@@ -338,8 +341,8 @@ struct dnode {
/* used in syncing context */
uint64_t dn_oldused; /* old phys used bytes */
uint64_t dn_oldflags; /* old phys dn_flags */
- uint64_t dn_olduid, dn_oldgid;
- uint64_t dn_newuid, dn_newgid;
+ uint64_t dn_olduid, dn_oldgid, dn_oldprojid;
+ uint64_t dn_newuid, dn_newgid, dn_newprojid;
int dn_id_flags;
/* holds prefetch structure */
diff --git a/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h
index dadbda324e..bb28014ac3 100644
--- a/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h
+++ b/usr/src/uts/common/fs/zfs/sys/dsl_deleg.h
@@ -51,8 +51,12 @@ extern "C" {
#define ZFS_DELEG_PERM_VSCAN "vscan"
#define ZFS_DELEG_PERM_USERQUOTA "userquota"
#define ZFS_DELEG_PERM_GROUPQUOTA "groupquota"
+#define ZFS_DELEG_PERM_USEROBJQUOTA "userobjquota"
+#define ZFS_DELEG_PERM_GROUPOBJQUOTA "groupobjquota"
#define ZFS_DELEG_PERM_USERUSED "userused"
#define ZFS_DELEG_PERM_GROUPUSED "groupused"
+#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"
@@ -60,6 +64,10 @@ extern "C" {
#define ZFS_DELEG_PERM_REMAP "remap"
#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"
/*
* Note: the names of properties that are marked delegatable are also
diff --git a/usr/src/uts/common/fs/zfs/sys/sa.h b/usr/src/uts/common/fs/zfs/sys/sa.h
index fe2291c50c..fa44775e13 100644
--- a/usr/src/uts/common/fs/zfs/sys/sa.h
+++ b/usr/src/uts/common/fs/zfs/sys/sa.h
@@ -155,6 +155,7 @@ void sa_handle_unlock(sa_handle_t *);
#ifdef _KERNEL
int sa_lookup_uio(sa_handle_t *, sa_attr_type_t, uio_t *);
+int sa_add_projid(sa_handle_t *, dmu_tx_t *, uint64_t);
#endif
#ifdef __cplusplus
diff --git a/usr/src/uts/common/fs/zfs/sys/spa_impl.h b/usr/src/uts/common/fs/zfs/sys/spa_impl.h
index 06e35bdab0..400e73156c 100644
--- a/usr/src/uts/common/fs/zfs/sys/spa_impl.h
+++ b/usr/src/uts/common/fs/zfs/sys/spa_impl.h
@@ -409,6 +409,8 @@ struct spa {
*/
spa_config_lock_t spa_config_lock[SCL_LOCKS]; /* config changes */
zfs_refcount_t spa_refcount; /* number of opens */
+
+ taskq_t *spa_upgrade_taskq; /* taskq for upgrade jobs */
};
extern const char *spa_config_path;
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h
index 4eefdc563f..6e75cb5756 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_acl.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_acl.h
@@ -207,7 +207,7 @@ struct zfsvfs;
int zfs_acl_ids_create(struct znode *, int, vattr_t *,
cred_t *, vsecattr_t *, zfs_acl_ids_t *);
void zfs_acl_ids_free(zfs_acl_ids_t *);
-boolean_t zfs_acl_ids_overquota(struct zfsvfs *, zfs_acl_ids_t *);
+boolean_t zfs_acl_ids_overquota(struct zfsvfs *, zfs_acl_ids_t *, uint64_t);
int zfs_getacl(struct znode *, vsecattr_t *, boolean_t, cred_t *);
int zfs_setacl(struct znode *, vsecattr_t *, boolean_t, cred_t *);
void zfs_acl_rele(void *);
@@ -236,6 +236,7 @@ void zfs_acl_xform(struct znode *, zfs_acl_t *, cred_t *);
void zfs_acl_data_locator(void **, uint32_t *, uint32_t, boolean_t, void *);
uint64_t zfs_mode_compute(uint64_t, zfs_acl_t *,
uint64_t *, uint64_t, uint64_t);
+int zfs_acl_node_read(struct znode *, boolean_t, zfs_acl_t **, boolean_t);
int zfs_acl_chown_setattr(struct znode *);
#endif
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_context.h b/usr/src/uts/common/fs/zfs/sys/zfs_context.h
index ebcdc7f111..da0ea3ab2e 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_context.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_context.h
@@ -80,6 +80,7 @@ extern "C" {
#endif
#define likely(x) _zfs_expect((x) != 0, 1)
+#define unlikely(x) _zfs_expect((x) != 0, 0)
#define CPU_SEQID (CPU->cpu_seqid)
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_project.h b/usr/src/uts/common/fs/zfs/sys/zfs_project.h
new file mode 100644
index 0000000000..741c8f322a
--- /dev/null
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_project.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ * Copyright 2019 Joyent, Inc.
+ */
+
+#ifndef _SYS_ZFS_PROJECT_H
+#define _SYS_ZFS_PROJECT_H
+
+#ifndef _KERNEL
+#ifndef _SYS_MOUNT_H
+/* XXX: some hack to avoid include sys/mount.h */
+#define _SYS_MOUNT_H
+#endif
+#endif
+
+#ifdef FS_PROJINHERIT_FL
+#define ZFS_PROJINHERIT_FL FS_PROJINHERIT_FL
+#else
+#define ZFS_PROJINHERIT_FL 0x20000000
+#endif
+
+#ifdef FS_IOC_FSGETXATTR
+typedef struct fsxattr zfsxattr_t;
+
+#define ZFS_IOC_FSGETXATTR FS_IOC_FSGETXATTR
+#define ZFS_IOC_FSSETXATTR FS_IOC_FSSETXATTR
+#else
+#include <sys/ioccom.h>
+typedef struct zfsxattr {
+ uint32_t fsx_xflags; /* xflags field value (get/set) */
+ uint32_t fsx_projid; /* project identifier (get/set) */
+} zfsxattr_t;
+
+#define ZFS_IOC_FSGETXATTR _IOR('X', 31, zfsxattr_t)
+#define ZFS_IOC_FSSETXATTR _IOW('X', 32, zfsxattr_t)
+#endif
+
+#define ZFS_DEFAULT_PROJID (0ULL)
+/*
+ * It is NOT ondisk project ID value. Just means either the object has
+ * no project ID or the operation does not touch project ID attribute.
+ */
+#define ZFS_INVALID_PROJID (-1ULL)
+
+static inline boolean_t
+zpl_is_valid_projid(uint32_t projid)
+{
+ /*
+ * zfsxattr::fsx_projid is 32-bits, when convert to uint64_t,
+ * the higher 32-bits will be set as zero, so cannot directly
+ * compare with ZFS_INVALID_PROJID (-1ULL)
+ */
+ if ((uint32_t)ZFS_INVALID_PROJID == projid)
+ return (B_FALSE);
+ return (B_TRUE);
+}
+
+#endif /* _SYS_ZFS_PROJECT_H */
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_sa.h b/usr/src/uts/common/fs/zfs/sys/zfs_sa.h
index cd312b27a9..7877c8f730 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_sa.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_sa.h
@@ -73,6 +73,7 @@ typedef enum zpl_attr {
ZPL_SYMLINK,
ZPL_SCANSTAMP,
ZPL_DACL_ACES,
+ ZPL_PROJID,
ZPL_END
} zpl_attr_t;
@@ -86,6 +87,8 @@ typedef enum zpl_attr {
#define SA_UID_OFFSET 24
#define SA_GID_OFFSET 32
#define SA_PARENT_OFFSET 40
+#define SA_FLAGS_OFFSET 48
+#define SA_PROJID_OFFSET 128
extern sa_attr_reg_t zfs_attr_table[ZPL_END + 1];
extern sa_attr_reg_t zfs_legacy_attr_table[ZPL_END + 1];
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h
index 25d9d19dd6..13be805ccd 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_vfsops.h
@@ -80,6 +80,10 @@ struct zfsvfs {
kmutex_t z_lock;
uint64_t z_userquota_obj;
uint64_t z_groupquota_obj;
+ uint64_t z_userobjquota_obj;
+ uint64_t z_groupobjquota_obj;
+ uint64_t z_projectquota_obj;
+ uint64_t z_projectobjquota_obj;
uint64_t z_replay_eof; /* New end of file - replay only */
sa_attr_type_t *z_attr_table; /* SA attr mapping->id */
#define ZFS_OBJ_MTX_SZ 64
@@ -144,10 +148,12 @@ extern int zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
uint64_t *cookiep, void *vbuf, uint64_t *bufsizep);
extern int zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
const char *domain, uint64_t rid, uint64_t quota);
-extern boolean_t zfs_owner_overquota(zfsvfs_t *zfsvfs, struct znode *,
- boolean_t isgroup);
-extern boolean_t zfs_fuid_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup,
- uint64_t fuid);
+extern boolean_t zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj,
+ uint64_t id);
+extern boolean_t zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj,
+ uint64_t id);
+extern boolean_t zfs_id_overquota(zfsvfs_t *zfsvfs, uint64_t usedobj,
+ uint64_t id);
extern int zfs_set_version(zfsvfs_t *zfsvfs, uint64_t newvers);
extern int zfsvfs_create(const char *name, zfsvfs_t **zfvp);
extern int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os);
diff --git a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h
index a9f9876530..c70eeec4ba 100644
--- a/usr/src/uts/common/fs/zfs/sys/zfs_znode.h
+++ b/usr/src/uts/common/fs/zfs/sys/zfs_znode.h
@@ -43,6 +43,7 @@
#endif
#include <sys/zfs_acl.h>
#include <sys/zil.h>
+#include <sys/zfs_project.h>
#ifdef __cplusplus
extern "C" {
@@ -50,7 +51,7 @@ extern "C" {
/*
* Additional file level attributes, that are stored
- * in the upper half of zp_flags
+ * in the upper half of z_pflags
*/
#define ZFS_READONLY 0x0000000100000000
#define ZFS_HIDDEN 0x0000000200000000
@@ -66,6 +67,16 @@ extern "C" {
#define ZFS_REPARSE 0x0000080000000000
#define ZFS_OFFLINE 0x0000100000000000
#define ZFS_SPARSE 0x0000200000000000
+/*
+ * PROJINHERIT attribute is used to indicate that the child object under the
+ * directory which has the PROJINHERIT attribute needs to inherit its parent
+ * project ID that is used by project quota.
+ */
+#define ZFS_PROJINHERIT 0x0000400000000000
+/*
+ * PROJID attr is used internally to indicate that the object has project ID.
+ */
+#define ZFS_PROJID 0x0000800000000000
#define ZFS_ATTR_SET(zp, attr, value, pflags, tx) \
{ \
@@ -110,6 +121,7 @@ extern "C" {
#define SA_ZPL_SIZE(z) z->z_attr_table[ZPL_SIZE]
#define SA_ZPL_ZNODE_ACL(z) z->z_attr_table[ZPL_ZNODE_ACL]
#define SA_ZPL_PAD(z) z->z_attr_table[ZPL_PAD]
+#define SA_ZPL_PROJID(z) z->z_attr_table[ZPL_PROJID]
/*
* Is ID ephemeral?
@@ -128,7 +140,7 @@ extern "C" {
/*
* Special attributes for master node.
- * "userquota@" and "groupquota@" are also valid (from
+ * "userquota@", "groupquota@" and "projectquota@" are also valid (from
* zfs_userquota_prop_prefixes[]).
*/
#define ZFS_FSID "FSID"
@@ -197,11 +209,18 @@ typedef struct znode {
uint32_t z_sync_cnt; /* synchronous open count */
kmutex_t z_acl_lock; /* acl data lock */
zfs_acl_t *z_acl_cached; /* cached acl */
+ uint64_t z_projid; /* project ID */
list_node_t z_link_node; /* all znodes in fs link */
sa_handle_t *z_sa_hdl; /* handle to sa data */
boolean_t z_is_sa; /* are we native sa? */
} znode_t;
+static inline uint64_t
+zfs_inherit_projid(znode_t *dzp)
+{
+ return ((dzp->z_pflags & ZFS_PROJINHERIT) ? dzp->z_projid :
+ ZFS_DEFAULT_PROJID);
+}
/*
* Range locking rules
diff --git a/usr/src/uts/common/fs/zfs/vdev.c b/usr/src/uts/common/fs/zfs/vdev.c
index 3bf145a4ac..2c84c0cd5b 100644
--- a/usr/src/uts/common/fs/zfs/vdev.c
+++ b/usr/src/uts/common/fs/zfs/vdev.c
@@ -4230,9 +4230,21 @@ vdev_is_bootable(vdev_t *vd)
if (strcmp(vdev_type, VDEV_TYPE_ROOT) == 0 &&
vd->vdev_children > 1) {
- return (B_FALSE);
- } else if (strcmp(vdev_type, VDEV_TYPE_MISSING) == 0 ||
- strcmp(vdev_type, VDEV_TYPE_INDIRECT) == 0) {
+ int non_indirect = 0;
+
+ for (int c = 0; c < vd->vdev_children; c++) {
+ vdev_type =
+ vd->vdev_child[c]->vdev_ops->vdev_op_type;
+ if (strcmp(vdev_type, VDEV_TYPE_INDIRECT) != 0)
+ non_indirect++;
+ }
+ /*
+ * non_indirect > 1 means we have more than one
+ * top-level vdev, so we stop here.
+ */
+ if (non_indirect > 1)
+ return (B_FALSE);
+ } else if (strcmp(vdev_type, VDEV_TYPE_MISSING) == 0) {
return (B_FALSE);
}
}
diff --git a/usr/src/uts/common/fs/zfs/vdev_disk.c b/usr/src/uts/common/fs/zfs/vdev_disk.c
index 8c12595811..325cee7f94 100644
--- a/usr/src/uts/common/fs/zfs/vdev_disk.c
+++ b/usr/src/uts/common/fs/zfs/vdev_disk.c
@@ -300,7 +300,6 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
dev_t dev;
int otyp;
boolean_t validate_devid = B_FALSE;
- ddi_devid_t devid;
uint64_t capacity = 0, blksz = 0, pbsize;
/*
@@ -404,9 +403,20 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
/*
* Compare the devid to the stored value.
*/
- if (error == 0 && vd->vdev_devid != NULL &&
- ldi_get_devid(dvd->vd_lh, &devid) == 0) {
- if (ddi_devid_compare(devid, dvd->vd_devid) != 0) {
+ if (error == 0 && vd->vdev_devid != NULL) {
+ ddi_devid_t devid = NULL;
+
+ if (ldi_get_devid(dvd->vd_lh, &devid) != 0) {
+ /*
+ * We expected a devid on this device but it no
+ * longer appears to have one. The validation
+ * step may need to remove it from the
+ * configuration.
+ */
+ validate_devid = B_TRUE;
+
+ } else if (ddi_devid_compare(devid, dvd->vd_devid) !=
+ 0) {
/*
* A mismatch here is unexpected, log it.
*/
@@ -425,7 +435,10 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
kcred);
dvd->vd_lh = NULL;
}
- ddi_devid_free(devid);
+
+ if (devid != NULL) {
+ ddi_devid_free(devid);
+ }
}
/*
@@ -455,26 +468,27 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
* as reliable as the devid, this will give us something, and the higher
* level vdev validation will prevent us from opening the wrong device.
*/
- if (error) {
- if (vd->vdev_devid != NULL)
- validate_devid = B_TRUE;
+ if (error != 0) {
+ validate_devid = B_TRUE;
if (vd->vdev_physpath != NULL &&
- (dev = ddi_pathname_to_dev_t(vd->vdev_physpath)) != NODEV)
+ (dev = ddi_pathname_to_dev_t(vd->vdev_physpath)) != NODEV) {
error = ldi_open_by_dev(&dev, OTYP_BLK, spa_mode(spa),
kcred, &dvd->vd_lh, zfs_li);
+ }
/*
* Note that we don't support the legacy auto-wholedisk support
* as above. This hasn't been used in a very long time and we
* don't need to propagate its oddities to this edge condition.
*/
- if (error && vd->vdev_path != NULL)
+ if (error != 0 && vd->vdev_path != NULL) {
error = ldi_open_by_name(vd->vdev_path, spa_mode(spa),
kcred, &dvd->vd_lh, zfs_li);
+ }
}
- if (error) {
+ if (error != 0) {
vd->vdev_stat.vs_aux = VDEV_AUX_OPEN_FAILED;
vdev_dbgmsg(vd, "vdev_disk_open: failed to open [error=%d]",
error);
@@ -485,22 +499,100 @@ vdev_disk_open(vdev_t *vd, uint64_t *psize, uint64_t *max_psize,
* Now that the device has been successfully opened, update the devid
* if necessary.
*/
- if (validate_devid && spa_writeable(spa) &&
- ldi_get_devid(dvd->vd_lh, &devid) == 0) {
- if (ddi_devid_compare(devid, dvd->vd_devid) != 0) {
- char *vd_devid;
+ if (validate_devid) {
+ ddi_devid_t devid = NULL;
+ char *minorname = NULL;
+ char *vd_devid = NULL;
+ boolean_t remove = B_FALSE, update = B_FALSE;
+
+ /*
+ * Get the current devid and minor name for the device we
+ * opened.
+ */
+ if (ldi_get_devid(dvd->vd_lh, &devid) != 0 ||
+ ldi_get_minor_name(dvd->vd_lh, &minorname) != 0) {
+ /*
+ * If we are unable to get the devid or the minor name
+ * for the device, we need to remove them from the
+ * configuration to prevent potential inconsistencies.
+ */
+ if (dvd->vd_minor != NULL || dvd->vd_devid != NULL ||
+ vd->vdev_devid != NULL) {
+ /*
+ * We only need to remove the devid if one
+ * exists.
+ */
+ remove = B_TRUE;
+ }
- vd_devid = ddi_devid_str_encode(devid, dvd->vd_minor);
+ } else if (dvd->vd_devid == NULL || dvd->vd_minor == NULL) {
+ /*
+ * There was previously no devid at all so we need to
+ * add one.
+ */
+ update = B_TRUE;
+
+ } else if (ddi_devid_compare(devid, dvd->vd_devid) != 0 ||
+ strcmp(minorname, dvd->vd_minor) != 0) {
+ /*
+ * The devid or minor name on file does not match the
+ * one from the opened device.
+ */
+ update = B_TRUE;
+ }
+
+ if (update) {
+ /*
+ * Render the new devid and minor name as a string for
+ * logging and to store in the vdev configuration.
+ */
+ vd_devid = ddi_devid_str_encode(devid, minorname);
+ }
+
+ if (update || remove) {
vdev_dbgmsg(vd, "vdev_disk_open: update devid from "
- "'%s' to '%s'", vd->vdev_devid, vd_devid);
+ "'%s' to '%s'",
+ vd->vdev_devid != NULL ? vd->vdev_devid : "<none>",
+ vd_devid != NULL ? vd_devid : "<none>");
cmn_err(CE_NOTE, "vdev_disk_open %s: update devid "
- "from '%s' to '%s'", vd->vdev_path != NULL ?
- vd->vdev_path : "?", vd->vdev_devid, vd_devid);
- spa_strfree(vd->vdev_devid);
- vd->vdev_devid = spa_strdup(vd_devid);
- ddi_devid_str_free(vd_devid);
+ "from '%s' to '%s'",
+ vd->vdev_path != NULL ? vd->vdev_path : "?",
+ vd->vdev_devid != NULL ? vd->vdev_devid : "<none>",
+ vd_devid != NULL ? vd_devid : "<none>");
+
+ /*
+ * Remove and free any existing values.
+ */
+ if (dvd->vd_minor != NULL) {
+ ddi_devid_str_free(dvd->vd_minor);
+ dvd->vd_minor = NULL;
+ }
+ if (dvd->vd_devid != NULL) {
+ ddi_devid_free(dvd->vd_devid);
+ dvd->vd_devid = NULL;
+ }
+ if (vd->vdev_devid != NULL) {
+ spa_strfree(vd->vdev_devid);
+ vd->vdev_devid = NULL;
+ }
+ }
+
+ if (update) {
+ /*
+ * Install the new values.
+ */
+ vd->vdev_devid = vd_devid;
+ dvd->vd_minor = minorname;
+ dvd->vd_devid = devid;
+
+ } else {
+ if (devid != NULL) {
+ ddi_devid_free(devid);
+ }
+ if (minorname != NULL) {
+ kmem_free(minorname, strlen(minorname) + 1);
+ }
}
- ddi_devid_free(devid);
}
/*
diff --git a/usr/src/uts/common/fs/zfs/zfs_acl.c b/usr/src/uts/common/fs/zfs/zfs_acl.c
index be97c64514..0570da6427 100644
--- a/usr/src/uts/common/fs/zfs/zfs_acl.c
+++ b/usr/src/uts/common/fs/zfs/zfs_acl.c
@@ -1057,8 +1057,8 @@ zfs_mode_compute(uint64_t fmode, zfs_acl_t *aclp,
* Read an external acl object. If the intent is to modify, always
* create a new acl and leave any cached acl in place.
*/
-static int
-zfs_acl_node_read(znode_t *zp, boolean_t have_lock, zfs_acl_t **aclpp,
+int
+zfs_acl_node_read(struct znode *zp, boolean_t have_lock, zfs_acl_t **aclpp,
boolean_t will_modify)
{
zfs_acl_t *aclp;
@@ -1771,10 +1771,12 @@ zfs_acl_ids_free(zfs_acl_ids_t *acl_ids)
}
boolean_t
-zfs_acl_ids_overquota(zfsvfs_t *zfsvfs, zfs_acl_ids_t *acl_ids)
+zfs_acl_ids_overquota(zfsvfs_t *zv, zfs_acl_ids_t *acl_ids, uint64_t projid)
{
- return (zfs_fuid_overquota(zfsvfs, B_FALSE, acl_ids->z_fuid) ||
- zfs_fuid_overquota(zfsvfs, B_TRUE, acl_ids->z_fgid));
+ return (zfs_id_overquota(zv, DMU_USERUSED_OBJECT, acl_ids->z_fuid) ||
+ zfs_id_overquota(zv, DMU_GROUPUSED_OBJECT, acl_ids->z_fgid) ||
+ (projid != ZFS_DEFAULT_PROJID && projid != ZFS_INVALID_PROJID &&
+ zfs_id_overquota(zv, DMU_PROJECTUSED_OBJECT, projid)));
}
/*
diff --git a/usr/src/uts/common/fs/zfs/zfs_dir.c b/usr/src/uts/common/fs/zfs/zfs_dir.c
index 8c4c05a1d1..ac262161ae 100644
--- a/usr/src/uts/common/fs/zfs/zfs_dir.c
+++ b/usr/src/uts/common/fs/zfs/zfs_dir.c
@@ -981,7 +981,7 @@ zfs_make_xattrdir(znode_t *zp, vattr_t *vap, vnode_t **xvpp, cred_t *cr)
if ((error = zfs_acl_ids_create(zp, IS_XATTR, vap, cr, NULL,
&acl_ids)) != 0)
return (error);
- if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) {
+ if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zp->z_projid)) {
zfs_acl_ids_free(&acl_ids);
return (SET_ERROR(EDQUOT));
}
diff --git a/usr/src/uts/common/fs/zfs/zfs_ioctl.c b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
index b28f847b10..45de48cfda 100644
--- a/usr/src/uts/common/fs/zfs/zfs_ioctl.c
+++ b/usr/src/uts/common/fs/zfs/zfs_ioctl.c
@@ -248,9 +248,18 @@ static const char *userquota_perms[] = {
ZFS_DELEG_PERM_USERQUOTA,
ZFS_DELEG_PERM_GROUPUSED,
ZFS_DELEG_PERM_GROUPQUOTA,
+ ZFS_DELEG_PERM_USEROBJUSED,
+ ZFS_DELEG_PERM_USEROBJQUOTA,
+ ZFS_DELEG_PERM_GROUPOBJUSED,
+ ZFS_DELEG_PERM_GROUPOBJQUOTA,
+ ZFS_DELEG_PERM_PROJECTUSED,
+ ZFS_DELEG_PERM_PROJECTQUOTA,
+ ZFS_DELEG_PERM_PROJECTOBJUSED,
+ ZFS_DELEG_PERM_PROJECTOBJQUOTA,
};
static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
+static int zfs_ioc_id_quota_upgrade(zfs_cmd_t *zc);
static int zfs_check_settable(const char *name, nvpair_t *property,
cred_t *cr);
static int zfs_check_clearable(char *dataset, nvlist_t *props,
@@ -1209,13 +1218,19 @@ zfs_secpolicy_userspace_one(zfs_cmd_t *zc, nvlist_t *innvl, cred_t *cr)
* themself, allow it.
*/
if (zc->zc_objset_type == ZFS_PROP_USERUSED ||
- zc->zc_objset_type == ZFS_PROP_USERQUOTA) {
+ zc->zc_objset_type == ZFS_PROP_USERQUOTA ||
+ zc->zc_objset_type == ZFS_PROP_USEROBJUSED ||
+ zc->zc_objset_type == ZFS_PROP_USEROBJQUOTA) {
if (zc->zc_guid == crgetuid(cr))
return (0);
- } else {
+ } else if (zc->zc_objset_type == ZFS_PROP_GROUPUSED ||
+ zc->zc_objset_type == ZFS_PROP_GROUPQUOTA ||
+ zc->zc_objset_type == ZFS_PROP_GROUPOBJUSED ||
+ zc->zc_objset_type == ZFS_PROP_GROUPOBJQUOTA) {
if (groupmember(zc->zc_guid, cr))
return (0);
}
+ /* else is for project quota/used */
}
return (zfs_secpolicy_write_perms(zc->zc_name,
@@ -2587,6 +2602,7 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source,
zc = kmem_zalloc(sizeof (zfs_cmd_t), KM_SLEEP);
(void) strcpy(zc->zc_name, dsname);
(void) zfs_ioc_userspace_upgrade(zc);
+ (void) zfs_ioc_id_quota_upgrade(zc);
kmem_free(zc, sizeof (zfs_cmd_t));
}
break;
@@ -4081,15 +4097,35 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr)
zfs_userquota_prop_prefixes[ZFS_PROP_USERQUOTA];
const char *gq_prefix =
zfs_userquota_prop_prefixes[ZFS_PROP_GROUPQUOTA];
+ const char *uiq_prefix =
+ zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA];
+ const char *giq_prefix =
+ zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA];
+ const char *pq_prefix =
+ zfs_userquota_prop_prefixes[ZFS_PROP_PROJECTQUOTA];
+ const char *piq_prefix = zfs_userquota_prop_prefixes[\
+ ZFS_PROP_PROJECTOBJQUOTA];
if (strncmp(propname, uq_prefix,
strlen(uq_prefix)) == 0) {
perm = ZFS_DELEG_PERM_USERQUOTA;
+ } else if (strncmp(propname, uiq_prefix,
+ strlen(uiq_prefix)) == 0) {
+ perm = ZFS_DELEG_PERM_USEROBJQUOTA;
} else if (strncmp(propname, gq_prefix,
strlen(gq_prefix)) == 0) {
perm = ZFS_DELEG_PERM_GROUPQUOTA;
+ } else if (strncmp(propname, giq_prefix,
+ strlen(giq_prefix)) == 0) {
+ perm = ZFS_DELEG_PERM_GROUPOBJQUOTA;
+ } else if (strncmp(propname, pq_prefix,
+ strlen(pq_prefix)) == 0) {
+ perm = ZFS_DELEG_PERM_PROJECTQUOTA;
+ } else if (strncmp(propname, piq_prefix,
+ strlen(piq_prefix)) == 0) {
+ perm = ZFS_DELEG_PERM_PROJECTOBJQUOTA;
} else {
- /* USERUSED and GROUPUSED are read-only */
+ /* {USER|GROUP|PROJECT}USED are read-only */
return (SET_ERROR(EINVAL));
}
@@ -5282,7 +5318,7 @@ zfs_ioc_promote(zfs_cmd_t *zc)
}
/*
- * Retrieve a single {user|group}{used|quota}@... property.
+ * Retrieve a single {user|group|project}{used|quota}@... property.
*
* inputs:
* zc_name name of filesystem
@@ -5401,6 +5437,49 @@ zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
}
/*
+ * inputs:
+ * zc_name name of filesystem
+ *
+ * outputs:
+ * none
+ */
+static int
+zfs_ioc_id_quota_upgrade(zfs_cmd_t *zc)
+{
+ objset_t *os;
+ int error;
+
+ error = dmu_objset_hold(zc->zc_name, FTAG, &os);
+ if (error != 0)
+ return (error);
+
+ dsl_dataset_long_hold(dmu_objset_ds(os), FTAG);
+ dsl_pool_rele(dmu_objset_pool(os), FTAG);
+
+ if (dmu_objset_userobjspace_upgradable(os) ||
+ dmu_objset_projectquota_upgradable(os)) {
+ mutex_enter(&os->os_upgrade_lock);
+ if (os->os_upgrade_id == 0) {
+ /* clear potential error code and retry */
+ os->os_upgrade_status = 0;
+ mutex_exit(&os->os_upgrade_lock);
+
+ dmu_objset_id_quota_upgrade(os);
+ } else {
+ mutex_exit(&os->os_upgrade_lock);
+ }
+
+ taskq_wait_id(os->os_spa->spa_upgrade_taskq, os->os_upgrade_id);
+ error = os->os_upgrade_status;
+ }
+
+ dsl_dataset_long_rele(dmu_objset_ds(os), FTAG);
+ dsl_dataset_rele(dmu_objset_ds(os), FTAG);
+
+ return (error);
+}
+
+/*
* We don't want to have a hard dependency
* against some special symbols in sharefs
* nfs, and smbsrv. Determine them if needed when
diff --git a/usr/src/uts/common/fs/zfs/zfs_log.c b/usr/src/uts/common/fs/zfs/zfs_log.c
index 1afaa8434b..7d3e1cc42a 100644
--- a/usr/src/uts/common/fs/zfs/zfs_log.c
+++ b/usr/src/uts/common/fs/zfs/zfs_log.c
@@ -166,8 +166,17 @@ zfs_log_xvattr(lr_attr_t *lrattr, xvattr_t *xvap)
XAT0_AV_MODIFIED;
if (XVA_ISSET_REQ(xvap, XAT_CREATETIME))
ZFS_TIME_ENCODE(&xoap->xoa_createtime, crtime);
- if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
+ if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) {
+ ASSERT(!XVA_ISSET_REQ(xvap, XAT_PROJID));
+
bcopy(xoap->xoa_av_scanstamp, scanstamp, AV_SCANSTAMP_SZ);
+ } else if (XVA_ISSET_REQ(xvap, XAT_PROJID)) {
+ /*
+ * XAT_PROJID and XAT_AV_SCANSTAMP will never be valid
+ * at the same time, so we can share the same space.
+ */
+ bcopy(&xoap->xoa_projid, scanstamp, sizeof (uint64_t));
+ }
if (XVA_ISSET_REQ(xvap, XAT_REPARSE))
*attrs |= (xoap->xoa_reparse == 0) ? 0 :
XAT0_REPARSE;
@@ -177,6 +186,9 @@ zfs_log_xvattr(lr_attr_t *lrattr, xvattr_t *xvap)
if (XVA_ISSET_REQ(xvap, XAT_SPARSE))
*attrs |= (xoap->xoa_sparse == 0) ? 0 :
XAT0_SPARSE;
+ if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT))
+ *attrs |= (xoap->xoa_projinherit == 0) ? 0 :
+ XAT0_PROJINHERIT;
}
static void *
diff --git a/usr/src/uts/common/fs/zfs/zfs_replay.c b/usr/src/uts/common/fs/zfs/zfs_replay.c
index f75ec48cd7..969a56dc9b 100644
--- a/usr/src/uts/common/fs/zfs/zfs_replay.c
+++ b/usr/src/uts/common/fs/zfs/zfs_replay.c
@@ -125,14 +125,25 @@ zfs_replay_xvattr(lr_attr_t *lrattr, xvattr_t *xvap)
((*attrs & XAT0_AV_QUARANTINED) != 0);
if (XVA_ISSET_REQ(xvap, XAT_CREATETIME))
ZFS_TIME_DECODE(&xoap->xoa_createtime, crtime);
- if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
+ if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) {
+ ASSERT(!XVA_ISSET_REQ(xvap, XAT_PROJID));
+
bcopy(scanstamp, xoap->xoa_av_scanstamp, AV_SCANSTAMP_SZ);
+ } else if (XVA_ISSET_REQ(xvap, XAT_PROJID)) {
+ /*
+ * XAT_PROJID and XAT_AV_SCANSTAMP will never be valid
+ * at the same time, so we can share the same space.
+ */
+ bcopy(scanstamp, &xoap->xoa_projid, sizeof (uint64_t));
+ }
if (XVA_ISSET_REQ(xvap, XAT_REPARSE))
xoap->xoa_reparse = ((*attrs & XAT0_REPARSE) != 0);
if (XVA_ISSET_REQ(xvap, XAT_OFFLINE))
xoap->xoa_offline = ((*attrs & XAT0_OFFLINE) != 0);
if (XVA_ISSET_REQ(xvap, XAT_SPARSE))
xoap->xoa_sparse = ((*attrs & XAT0_SPARSE) != 0);
+ if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT))
+ xoap->xoa_projinherit = ((*attrs & XAT0_PROJINHERIT) != 0);
}
static int
diff --git a/usr/src/uts/common/fs/zfs/zfs_sa.c b/usr/src/uts/common/fs/zfs/zfs_sa.c
index a39cff1a7b..4202fb5f3d 100644
--- a/usr/src/uts/common/fs/zfs/zfs_sa.c
+++ b/usr/src/uts/common/fs/zfs/zfs_sa.c
@@ -27,6 +27,8 @@
#include <sys/sa.h>
#include <sys/zfs_acl.h>
#include <sys/zfs_sa.h>
+#include <sys/dmu_objset.h>
+#include <sys/sa_impl.h>
/*
* ZPL attribute registration table.
@@ -62,6 +64,7 @@ sa_attr_reg_t zfs_attr_table[ZPL_END+1] = {
{"ZPL_SYMLINK", 0, SA_UINT8_ARRAY, 0},
{"ZPL_SCANSTAMP", 32, SA_UINT8_ARRAY, 0},
{"ZPL_DACL_ACES", 0, SA_ACL, 0},
+ {"ZPL_PROJID", sizeof (uint64_t), SA_UINT64_ARRAY, 0},
{NULL, 0, 0, 0}
};
@@ -196,7 +199,7 @@ zfs_sa_upgrade(sa_handle_t *hdl, dmu_tx_t *tx)
dmu_buf_t *db = sa_get_db(hdl);
znode_t *zp = sa_get_userdata(hdl);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
- sa_bulk_attr_t bulk[20];
+ sa_bulk_attr_t bulk[22];
int count = 0;
sa_bulk_attr_t sa_attrs[20] = { 0 };
zfs_acl_locator_cb_t locate = { 0 };
@@ -247,6 +250,11 @@ zfs_sa_upgrade(sa_handle_t *hdl, dmu_tx_t *tx)
if (sa_bulk_lookup_locked(hdl, bulk, count) != 0)
goto done;
+ if (dmu_objset_projectquota_enabled(hdl->sa_os) &&
+ !(zp->z_pflags & ZFS_PROJID)) {
+ zp->z_pflags |= ZFS_PROJID;
+ zp->z_projid = ZFS_DEFAULT_PROJID;
+ }
/*
* While the order here doesn't matter its best to try and organize
@@ -274,6 +282,9 @@ zfs_sa_upgrade(sa_handle_t *hdl, dmu_tx_t *tx)
&crtime, 16);
SA_ADD_BULK_ATTR(sa_attrs, count, SA_ZPL_LINKS(zfsvfs), NULL,
&zp->z_links, 8);
+ if (dmu_objset_projectquota_enabled(hdl->sa_os))
+ SA_ADD_BULK_ATTR(sa_attrs, count, SA_ZPL_PROJID(zfsvfs), NULL,
+ &zp->z_projid, 8);
if (zp->z_vnode->v_type == VBLK || zp->z_vnode->v_type == VCHR)
SA_ADD_BULK_ATTR(sa_attrs, count, SA_ZPL_RDEV(zfsvfs), NULL,
&rdev, 8);
diff --git a/usr/src/uts/common/fs/zfs/zfs_vfsops.c b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
index 3ba6db1049..571df521d4 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vfsops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vfsops.c
@@ -24,6 +24,7 @@
* Copyright (c) 2012, 2015 by Delphix. All rights reserved.
* Copyright (c) 2014 Integros [integros.com]
* Copyright 2016 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
*/
/* Portions Copyright 2010 Robert Milkowski */
@@ -565,8 +566,14 @@ unregister:
static int
zfs_space_delta_cb(dmu_object_type_t bonustype, void *data,
- uint64_t *userp, uint64_t *groupp)
+ uint64_t *userp, uint64_t *groupp, uint64_t *projectp)
{
+ sa_hdr_phys_t sa;
+ sa_hdr_phys_t *sap = data;
+ uint64_t flags;
+ int hdrsize;
+ boolean_t swap = B_FALSE;
+
/*
* Is it a valid type of object to track?
*/
@@ -586,42 +593,49 @@ zfs_space_delta_cb(dmu_object_type_t bonustype, void *data,
znode_phys_t *znp = data;
*userp = znp->zp_uid;
*groupp = znp->zp_gid;
+ *projectp = ZFS_DEFAULT_PROJID;
+ return (0);
+ }
+
+ if (sap->sa_magic == 0) {
+ /*
+ * This should only happen for newly created files
+ * that haven't had the znode data filled in yet.
+ */
+ *userp = 0;
+ *groupp = 0;
+ *projectp = ZFS_DEFAULT_PROJID;
+ return (0);
+ }
+
+ sa = *sap;
+ if (sa.sa_magic == BSWAP_32(SA_MAGIC)) {
+ sa.sa_magic = SA_MAGIC;
+ sa.sa_layout_info = BSWAP_16(sa.sa_layout_info);
+ swap = B_TRUE;
} else {
- int hdrsize;
- sa_hdr_phys_t *sap = data;
- sa_hdr_phys_t sa = *sap;
- boolean_t swap = B_FALSE;
+ VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
+ }
- ASSERT(bonustype == DMU_OT_SA);
+ hdrsize = sa_hdrsize(&sa);
+ VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t));
- if (sa.sa_magic == 0) {
- /*
- * This should only happen for newly created
- * files that haven't had the znode data filled
- * in yet.
- */
- *userp = 0;
- *groupp = 0;
- return (0);
- }
- if (sa.sa_magic == BSWAP_32(SA_MAGIC)) {
- sa.sa_magic = SA_MAGIC;
- sa.sa_layout_info = BSWAP_16(sa.sa_layout_info);
- swap = B_TRUE;
- } else {
- VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
- }
+ *userp = *((uint64_t *)((uintptr_t)data + hdrsize + SA_UID_OFFSET));
+ *groupp = *((uint64_t *)((uintptr_t)data + hdrsize + SA_GID_OFFSET));
+ flags = *((uint64_t *)((uintptr_t)data + hdrsize + SA_FLAGS_OFFSET));
+ if (swap)
+ flags = BSWAP_64(flags);
- hdrsize = sa_hdrsize(&sa);
- VERIFY3U(hdrsize, >=, sizeof (sa_hdr_phys_t));
- *userp = *((uint64_t *)((uintptr_t)data + hdrsize +
- SA_UID_OFFSET));
- *groupp = *((uint64_t *)((uintptr_t)data + hdrsize +
- SA_GID_OFFSET));
- if (swap) {
- *userp = BSWAP_64(*userp);
- *groupp = BSWAP_64(*groupp);
- }
+ if (flags & ZFS_PROJID)
+ *projectp = *((uint64_t *)((uintptr_t)data + hdrsize +
+ SA_PROJID_OFFSET));
+ else
+ *projectp = ZFS_DEFAULT_PROJID;
+
+ if (swap) {
+ *userp = BSWAP_64(*userp);
+ *groupp = BSWAP_64(*groupp);
+ *projectp = BSWAP_64(*projectp);
}
return (0);
}
@@ -648,15 +662,29 @@ zfs_userquota_prop_to_obj(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type)
{
switch (type) {
case ZFS_PROP_USERUSED:
+ case ZFS_PROP_USEROBJUSED:
return (DMU_USERUSED_OBJECT);
case ZFS_PROP_GROUPUSED:
+ case ZFS_PROP_GROUPOBJUSED:
return (DMU_GROUPUSED_OBJECT);
+ case ZFS_PROP_PROJECTUSED:
+ case ZFS_PROP_PROJECTOBJUSED:
+ return (DMU_PROJECTUSED_OBJECT);
case ZFS_PROP_USERQUOTA:
return (zfsvfs->z_userquota_obj);
case ZFS_PROP_GROUPQUOTA:
return (zfsvfs->z_groupquota_obj);
+ case ZFS_PROP_USEROBJQUOTA:
+ return (zfsvfs->z_userobjquota_obj);
+ case ZFS_PROP_GROUPOBJQUOTA:
+ return (zfsvfs->z_groupobjquota_obj);
+ case ZFS_PROP_PROJECTQUOTA:
+ return (zfsvfs->z_projectquota_obj);
+ case ZFS_PROP_PROJECTOBJQUOTA:
+ return (zfsvfs->z_projectobjquota_obj);
+ default:
+ return (ZFS_NO_OBJECT);
}
- return (0);
}
int
@@ -668,16 +696,34 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
zap_attribute_t za;
zfs_useracct_t *buf = vbuf;
uint64_t obj;
+ int offset = 0;
if (!dmu_objset_userspace_present(zfsvfs->z_os))
return (SET_ERROR(ENOTSUP));
+ if ((type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
+ type == ZFS_PROP_PROJECTOBJQUOTA ||
+ type == ZFS_PROP_PROJECTOBJUSED) &&
+ !dmu_objset_projectquota_present(zfsvfs->z_os))
+ return (SET_ERROR(ENOTSUP));
+
+ if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+ type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
+ type == ZFS_PROP_PROJECTOBJUSED ||
+ type == ZFS_PROP_PROJECTOBJQUOTA) &&
+ !dmu_objset_userobjspace_present(zfsvfs->z_os))
+ return (SET_ERROR(ENOTSUP));
+
obj = zfs_userquota_prop_to_obj(zfsvfs, type);
- if (obj == 0) {
+ if (obj == ZFS_NO_OBJECT) {
*bufsizep = 0;
return (0);
}
+ if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+ type == ZFS_PROP_PROJECTOBJUSED)
+ offset = DMU_OBJACCT_PREFIX_LEN;
+
for (zap_cursor_init_serialized(&zc, zfsvfs->z_os, obj, *cookiep);
(error = zap_cursor_retrieve(&zc, &za)) == 0;
zap_cursor_advance(&zc)) {
@@ -685,7 +731,15 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
*bufsizep)
break;
- fuidstr_to_sid(zfsvfs, za.za_name,
+ /*
+ * skip object quota (with zap name prefix DMU_OBJACCT_PREFIX)
+ * when dealing with block quota and vice versa.
+ */
+ if ((offset > 0) != (strncmp(za.za_name, DMU_OBJACCT_PREFIX,
+ DMU_OBJACCT_PREFIX_LEN) == 0))
+ continue;
+
+ fuidstr_to_sid(zfsvfs, za.za_name + offset,
buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid);
buf->zu_space = za.za_first_integer;
@@ -725,7 +779,8 @@ int
zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
const char *domain, uint64_t rid, uint64_t *valp)
{
- char buf[32];
+ char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+ int offset = 0;
int err;
uint64_t obj;
@@ -734,11 +789,33 @@ zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
if (!dmu_objset_userspace_present(zfsvfs->z_os))
return (SET_ERROR(ENOTSUP));
+ if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+ type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
+ type == ZFS_PROP_PROJECTOBJUSED ||
+ type == ZFS_PROP_PROJECTOBJQUOTA) &&
+ !dmu_objset_userobjspace_present(zfsvfs->z_os))
+ return (SET_ERROR(ENOTSUP));
+
+ if (type == ZFS_PROP_PROJECTQUOTA || type == ZFS_PROP_PROJECTUSED ||
+ type == ZFS_PROP_PROJECTOBJQUOTA ||
+ type == ZFS_PROP_PROJECTOBJUSED) {
+ if (!dmu_objset_projectquota_present(zfsvfs->z_os))
+ return (SET_ERROR(ENOTSUP));
+ if (!zpl_is_valid_projid(rid))
+ return (SET_ERROR(EINVAL));
+ }
+
obj = zfs_userquota_prop_to_obj(zfsvfs, type);
- if (obj == 0)
+ if (obj == ZFS_NO_OBJECT)
return (0);
- err = id_to_fuidstr(zfsvfs, domain, rid, buf, B_FALSE);
+ if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+ type == ZFS_PROP_PROJECTOBJUSED) {
+ strncpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN);
+ offset = DMU_OBJACCT_PREFIX_LEN;
+ }
+
+ err = id_to_fuidstr(zfsvfs, domain, rid, buf + offset, B_FALSE);
if (err)
return (err);
@@ -758,14 +835,41 @@ zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
uint64_t *objp;
boolean_t fuid_dirtied;
- if (type != ZFS_PROP_USERQUOTA && type != ZFS_PROP_GROUPQUOTA)
- return (SET_ERROR(EINVAL));
-
if (zfsvfs->z_version < ZPL_VERSION_USERSPACE)
return (SET_ERROR(ENOTSUP));
- objp = (type == ZFS_PROP_USERQUOTA) ? &zfsvfs->z_userquota_obj :
- &zfsvfs->z_groupquota_obj;
+ switch (type) {
+ case ZFS_PROP_USERQUOTA:
+ objp = &zfsvfs->z_userquota_obj;
+ break;
+ case ZFS_PROP_GROUPQUOTA:
+ objp = &zfsvfs->z_groupquota_obj;
+ break;
+ case ZFS_PROP_USEROBJQUOTA:
+ objp = &zfsvfs->z_userobjquota_obj;
+ break;
+ case ZFS_PROP_GROUPOBJQUOTA:
+ objp = &zfsvfs->z_groupobjquota_obj;
+ break;
+ case ZFS_PROP_PROJECTQUOTA:
+ if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
+ return (SET_ERROR(ENOTSUP));
+ if (!zpl_is_valid_projid(rid))
+ return (SET_ERROR(EINVAL));
+
+ objp = &zfsvfs->z_projectquota_obj;
+ break;
+ case ZFS_PROP_PROJECTOBJQUOTA:
+ if (!dmu_objset_projectquota_enabled(zfsvfs->z_os))
+ return (SET_ERROR(ENOTSUP));
+ if (!zpl_is_valid_projid(rid))
+ return (SET_ERROR(EINVAL));
+
+ objp = &zfsvfs->z_projectobjquota_obj;
+ break;
+ default:
+ return (SET_ERROR(EINVAL));
+ }
err = id_to_fuidstr(zfsvfs, domain, rid, buf, B_TRUE);
if (err)
@@ -810,23 +914,46 @@ zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
}
boolean_t
-zfs_fuid_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid)
+zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
{
- char buf[32];
- uint64_t used, quota, usedobj, quotaobj;
+ char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+ uint64_t used, quota, quotaobj;
int err;
- usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT;
- quotaobj = isgroup ? zfsvfs->z_groupquota_obj : zfsvfs->z_userquota_obj;
+ if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) {
+ if (dmu_objset_userobjspace_upgradable(zfsvfs->z_os))
+ dmu_objset_id_quota_upgrade(zfsvfs->z_os);
+ return (B_FALSE);
+ }
+ if (usedobj == DMU_PROJECTUSED_OBJECT) {
+ if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
+ if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
+ dsl_pool_config_enter(
+ dmu_objset_pool(zfsvfs->z_os), FTAG);
+ dmu_objset_id_quota_upgrade(zfsvfs->z_os);
+ dsl_pool_config_exit(
+ dmu_objset_pool(zfsvfs->z_os), FTAG);
+ }
+ return (B_FALSE);
+ }
+ quotaobj = zfsvfs->z_projectobjquota_obj;
+ } else if (usedobj == DMU_USERUSED_OBJECT) {
+ quotaobj = zfsvfs->z_userobjquota_obj;
+ } else if (usedobj == DMU_GROUPUSED_OBJECT) {
+ quotaobj = zfsvfs->z_groupobjquota_obj;
+ } else {
+ return (B_FALSE);
+ }
if (quotaobj == 0 || zfsvfs->z_replay)
return (B_FALSE);
- (void) sprintf(buf, "%llx", (longlong_t)fuid);
+ (void) sprintf(buf, "%llx", (longlong_t)id);
err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
if (err != 0)
return (B_FALSE);
+ (void) sprintf(buf, DMU_OBJACCT_PREFIX "%llx", (longlong_t)id);
err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
if (err != 0)
return (B_FALSE);
@@ -834,19 +961,50 @@ zfs_fuid_overquota(zfsvfs_t *zfsvfs, boolean_t isgroup, uint64_t fuid)
}
boolean_t
-zfs_owner_overquota(zfsvfs_t *zfsvfs, znode_t *zp, boolean_t isgroup)
+zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
{
- uint64_t fuid;
- uint64_t quotaobj;
+ char buf[20];
+ uint64_t used, quota, quotaobj;
+ int err;
- quotaobj = isgroup ? zfsvfs->z_groupquota_obj : zfsvfs->z_userquota_obj;
+ if (usedobj == DMU_PROJECTUSED_OBJECT) {
+ if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
+ if (dmu_objset_projectquota_upgradable(zfsvfs->z_os)) {
+ dsl_pool_config_enter(
+ dmu_objset_pool(zfsvfs->z_os), FTAG);
+ dmu_objset_id_quota_upgrade(zfsvfs->z_os);
+ dsl_pool_config_exit(
+ dmu_objset_pool(zfsvfs->z_os), FTAG);
+ }
+ return (B_FALSE);
+ }
+ quotaobj = zfsvfs->z_projectquota_obj;
+ } else if (usedobj == DMU_USERUSED_OBJECT) {
+ quotaobj = zfsvfs->z_userquota_obj;
+ } else if (usedobj == DMU_GROUPUSED_OBJECT) {
+ quotaobj = zfsvfs->z_groupquota_obj;
+ } else {
+ return (B_FALSE);
+ }
+ if (quotaobj == 0 || zfsvfs->z_replay)
+ return (B_FALSE);
- fuid = isgroup ? zp->z_gid : zp->z_uid;
+ (void) sprintf(buf, "%llx", (longlong_t)id);
+ err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
+ if (err != 0)
+ return (B_FALSE);
- if (quotaobj == 0 || zfsvfs->z_replay)
+ err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
+ if (err != 0)
return (B_FALSE);
+ return (used >= quota);
+}
- return (zfs_fuid_overquota(zfsvfs, isgroup, fuid));
+boolean_t
+zfs_id_overquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
+{
+ return (zfs_id_overblockquota(zfsvfs, usedobj, id) ||
+ zfs_id_overobjquota(zfsvfs, usedobj, id));
}
/*
@@ -945,6 +1103,38 @@ zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os)
else if (error != 0)
return (error);
+ error = zap_lookup(os, MASTER_NODE_OBJ,
+ zfs_userquota_prop_prefixes[ZFS_PROP_PROJECTQUOTA],
+ 8, 1, &zfsvfs->z_projectquota_obj);
+ if (error == ENOENT)
+ zfsvfs->z_projectquota_obj = 0;
+ else if (error != 0)
+ return (error);
+
+ error = zap_lookup(os, MASTER_NODE_OBJ,
+ zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA],
+ 8, 1, &zfsvfs->z_userobjquota_obj);
+ if (error == ENOENT)
+ zfsvfs->z_userobjquota_obj = 0;
+ else if (error != 0)
+ return (error);
+
+ error = zap_lookup(os, MASTER_NODE_OBJ,
+ zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA],
+ 8, 1, &zfsvfs->z_groupobjquota_obj);
+ if (error == ENOENT)
+ zfsvfs->z_groupobjquota_obj = 0;
+ else if (error != 0)
+ return (error);
+
+ error = zap_lookup(os, MASTER_NODE_OBJ,
+ zfs_userquota_prop_prefixes[ZFS_PROP_PROJECTOBJQUOTA],
+ 8, 1, &zfsvfs->z_projectobjquota_obj);
+ if (error == ENOENT)
+ zfsvfs->z_projectobjquota_obj = 0;
+ else if (error != 0)
+ return (error);
+
error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1,
&zfsvfs->z_fuid_obj);
if (error == ENOENT)
@@ -968,15 +1158,11 @@ zfsvfs_create(const char *osname, zfsvfs_t **zfvp)
objset_t *os;
zfsvfs_t *zfsvfs;
int error;
+ boolean_t ro = (strchr(osname, '@') != NULL);
zfsvfs = kmem_zalloc(sizeof (zfsvfs_t), KM_SLEEP);
- /*
- * We claim to always be readonly so we can open snapshots;
- * other ZPL code will prevent us from writing to snapshots.
- */
- error = dmu_objset_own(osname, DMU_OST_ZFS, B_TRUE, B_TRUE, zfsvfs,
- &os);
+ error = dmu_objset_own(osname, DMU_OST_ZFS, ro, B_TRUE, zfsvfs, &os);
if (error != 0) {
kmem_free(zfsvfs, sizeof (zfsvfs_t));
return (error);
@@ -1339,6 +1525,83 @@ zfs_check_global_label(const char *dsname, const char *hexsl)
return (SET_ERROR(EACCES));
}
+static int
+zfs_statfs_project(zfsvfs_t *zfsvfs, znode_t *zp, struct statvfs64 *statp,
+ uint32_t bshift)
+{
+ char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+ uint64_t offset = DMU_OBJACCT_PREFIX_LEN;
+ uint64_t quota;
+ uint64_t used;
+ int err;
+
+ strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1);
+ err = id_to_fuidstr(zfsvfs, NULL, zp->z_projid, buf + offset, B_FALSE);
+ if (err)
+ return (err);
+
+ if (zfsvfs->z_projectquota_obj == 0)
+ goto objs;
+
+ err = zap_lookup(zfsvfs->z_os, zfsvfs->z_projectquota_obj,
+ buf + offset, 8, 1, &quota);
+ if (err == ENOENT)
+ goto objs;
+ else if (err)
+ return (err);
+
+ err = zap_lookup(zfsvfs->z_os, DMU_PROJECTUSED_OBJECT,
+ buf + offset, 8, 1, &used);
+ if (unlikely(err == ENOENT)) {
+ uint32_t blksize;
+ u_longlong_t nblocks;
+
+ /*
+ * Quota accounting is async, so it is possible race case.
+ * There is at least one object with the given project ID.
+ */
+ sa_object_size(zp->z_sa_hdl, &blksize, &nblocks);
+ if (unlikely(zp->z_blksz == 0))
+ blksize = zfsvfs->z_max_blksz;
+
+ used = blksize * nblocks;
+ } else if (err) {
+ return (err);
+ }
+
+ statp->f_blocks = quota >> bshift;
+ statp->f_bfree = (quota > used) ? ((quota - used) >> bshift) : 0;
+ statp->f_bavail = statp->f_bfree;
+
+objs:
+ if (zfsvfs->z_projectobjquota_obj == 0)
+ return (0);
+
+ err = zap_lookup(zfsvfs->z_os, zfsvfs->z_projectobjquota_obj,
+ buf + offset, 8, 1, &quota);
+ if (err == ENOENT)
+ return (0);
+ else if (err)
+ return (err);
+
+ err = zap_lookup(zfsvfs->z_os, DMU_PROJECTUSED_OBJECT,
+ buf, 8, 1, &used);
+ if (unlikely(err == ENOENT)) {
+ /*
+ * Quota accounting is async, so it is possible race case.
+ * There is at least one object with the given project ID.
+ */
+ used = 1;
+ } else if (err) {
+ return (err);
+ }
+
+ statp->f_files = quota;
+ statp->f_ffree = (quota > used) ? (quota - used) : 0;
+
+ return (0);
+}
+
/*
* Determine whether the mount is allowed according to MAC check.
* by comparing (where appropriate) label of the dataset against
@@ -1663,6 +1926,7 @@ zfs_statvfs(vfs_t *vfsp, struct statvfs64 *statp)
zfsvfs_t *zfsvfs = vfsp->vfs_data;
dev32_t d32;
uint64_t refdbytes, availbytes, usedobjs, availobjs;
+ int err = 0;
ZFS_ENTER(zfsvfs);
@@ -1717,8 +1981,33 @@ zfs_statvfs(vfs_t *vfsp, struct statvfs64 *statp)
*/
bzero(statp->f_fstr, sizeof (statp->f_fstr));
+ if (dmu_objset_projectquota_enabled(zfsvfs->z_os) &&
+ dmu_objset_projectquota_present(zfsvfs->z_os)) {
+ znode_t *zp;
+
+ /*
+ * In ZoL, zfs_statvfs is passed a Linux dentry (directory
+ * entry), instead of a vfsp. The ZoL code uses the dentry
+ * to get the znode from the dentry's inode. This represents
+ * whatever filename was passed to the user-level statvfs
+ * syscall.
+ *
+ * We're using the VFS root znode here, so this represents a
+ * potential difference from ZoL.
+ */
+ if (zfs_zget(zfsvfs, zfsvfs->z_root, &zp) == 0) {
+ uint32_t bshift = ddi_fls(statp->f_bsize) - 1;
+
+ if (zp->z_pflags & ZFS_PROJINHERIT && zp->z_projid &&
+ zpl_is_valid_projid(zp->z_projid))
+ err = zfs_statfs_project(zfsvfs, zp, statp,
+ bshift);
+ VN_RELE(ZTOV(zp));
+ }
+ }
+
ZFS_EXIT(zfsvfs);
- return (0);
+ return (err);
}
static int
diff --git a/usr/src/uts/common/fs/zfs/zfs_vnops.c b/usr/src/uts/common/fs/zfs/zfs_vnops.c
index 2940769eb4..b94bae51c0 100644
--- a/usr/src/uts/common/fs/zfs/zfs_vnops.c
+++ b/usr/src/uts/common/fs/zfs/zfs_vnops.c
@@ -84,6 +84,8 @@
#include <sys/cred.h>
#include <sys/attr.h>
#include <sys/zil.h>
+#include <sys/sa_impl.h>
+#include <sys/zfs_project.h>
/*
* Programming rules.
@@ -296,6 +298,57 @@ zfs_holey(vnode_t *vp, int cmd, offset_t *off)
return (error);
}
+static int
+zfs_ioctl_getxattr(vnode_t *vp, intptr_t data, int flag, cred_t *cr,
+ caller_context_t *ct)
+{
+ zfsxattr_t fsx = { 0 };
+ znode_t *zp = VTOZ(vp);
+
+ if (zp->z_pflags & ZFS_PROJINHERIT)
+ fsx.fsx_xflags = ZFS_PROJINHERIT_FL;
+ if (zp->z_pflags & ZFS_PROJID)
+ fsx.fsx_projid = zp->z_projid;
+ if (ddi_copyout(&fsx, (void *)data, sizeof (fsx), flag))
+ return (SET_ERROR(EFAULT));
+
+ return (0);
+}
+
+static int zfs_setattr(vnode_t *, vattr_t *, int, cred_t *, caller_context_t *);
+
+static int
+zfs_ioctl_setxattr(vnode_t *vp, intptr_t data, int flags, cred_t *cr,
+ caller_context_t *ct)
+{
+ znode_t *zp = VTOZ(vp);
+ zfsxattr_t fsx;
+ xvattr_t xva;
+ xoptattr_t *xoap;
+ int err;
+
+ if (ddi_copyin((void *)data, &fsx, sizeof (fsx), flags))
+ return (SET_ERROR(EFAULT));
+
+ if (!zpl_is_valid_projid(fsx.fsx_projid))
+ return (SET_ERROR(EINVAL));
+
+ if (fsx.fsx_xflags & ~ZFS_PROJINHERIT_FL)
+ return (SET_ERROR(EOPNOTSUPP));
+
+ xva_init(&xva);
+ xoap = xva_getxoptattr(&xva);
+
+ XVA_SET_REQ(&xva, XAT_PROJINHERIT);
+ if (fsx.fsx_xflags & ZFS_PROJINHERIT_FL)
+ xoap->xoa_projinherit = B_TRUE;
+
+ XVA_SET_REQ(&xva, XAT_PROJID);
+ xoap->xoa_projid = fsx.fsx_projid;
+
+ return (zfs_setattr(vp, (vattr_t *)&xva, flags, cr, ct));
+}
+
/* ARGSUSED */
static int
zfs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred,
@@ -383,6 +436,10 @@ zfs_ioctl(vnode_t *vp, int com, intptr_t data, int flag, cred_t *cred,
return (SET_ERROR(EFAULT));
return (0);
}
+ case ZFS_IOC_FSGETXATTR:
+ return (zfs_ioctl_getxattr(vp, data, flag, cred, ct));
+ case ZFS_IOC_FSSETXATTR:
+ return (zfs_ioctl_setxattr(vp, data, flag, cred, ct));
}
return (SET_ERROR(ENOTTY));
}
@@ -811,17 +868,21 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
* and allows us to do more fine-grained space accounting.
*/
while (n > 0) {
- abuf = NULL;
woff = uio->uio_loffset;
- if (zfs_owner_overquota(zfsvfs, zp, B_FALSE) ||
- zfs_owner_overquota(zfsvfs, zp, B_TRUE)) {
- if (abuf != NULL)
- dmu_return_arcbuf(abuf);
+
+ if (zfs_id_overblockquota(zfsvfs, DMU_USERUSED_OBJECT,
+ zp->z_uid) ||
+ zfs_id_overblockquota(zfsvfs, DMU_GROUPUSED_OBJECT,
+ zp->z_gid) ||
+ (zp->z_projid != ZFS_DEFAULT_PROJID &&
+ zfs_id_overblockquota(zfsvfs, DMU_PROJECTUSED_OBJECT,
+ zp->z_projid))) {
error = SET_ERROR(EDQUOT);
break;
}
- if (xuio && abuf == NULL) {
+ arc_buf_t *abuf = NULL;
+ if (xuio) {
ASSERT(i_iov < iovcnt);
aiov = &iovp[i_iov];
abuf = dmu_xuio_arcbuf(xuio, i_iov);
@@ -832,8 +893,7 @@ zfs_write(vnode_t *vp, uio_t *uio, int ioflag, cred_t *cr, caller_context_t *ct)
((char *)aiov->iov_base - (char *)abuf->b_data +
aiov->iov_len == arc_buf_size(abuf)));
i_iov++;
- } else if (abuf == NULL && n >= max_blksz &&
- woff >= zp->z_size &&
+ } else if (n >= max_blksz && woff >= zp->z_size &&
P2PHASE(woff, max_blksz) == 0 &&
zp->z_blksz == max_blksz) {
/*
@@ -1490,6 +1550,7 @@ top:
if (zp == NULL) {
uint64_t txtype;
+ uint64_t projid = ZFS_DEFAULT_PROJID;
/*
* Create a new file object and update the directory
@@ -1519,7 +1580,9 @@ top:
goto out;
have_acl = B_TRUE;
- if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) {
+ if (vap->va_type == VREG || vap->va_type == VDIR)
+ projid = zfs_inherit_projid(dzp);
+ if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, projid)) {
zfs_acl_ids_free(&acl_ids);
error = SET_ERROR(EDQUOT);
goto out;
@@ -1984,7 +2047,7 @@ top:
return (error);
}
- if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) {
+ if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, zfs_inherit_projid(dzp))) {
zfs_acl_ids_free(&acl_ids);
zfs_dirent_unlock(dl);
ZFS_EXIT(zfsvfs);
@@ -2691,6 +2754,17 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
((zp->z_pflags & ZFS_SPARSE) != 0);
XVA_SET_RTN(xvap, XAT_SPARSE);
}
+
+ if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT)) {
+ xoap->xoa_projinherit =
+ ((zp->z_pflags & ZFS_PROJINHERIT) != 0);
+ XVA_SET_RTN(xvap, XAT_PROJINHERIT);
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_PROJID)) {
+ xoap->xoa_projid = zp->z_projid;
+ XVA_SET_RTN(xvap, XAT_PROJID);
+ }
}
ZFS_TIME_DECODE(&vap->va_atime, zp->z_atime);
@@ -2713,6 +2787,119 @@ zfs_getattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
}
/*
+ * For the operation of changing file's user/group/project, we need to
+ * handle not only the main object that is assigned to the file directly,
+ * but also the ones that are used by the file via hidden xattr directory.
+ *
+ * Because the xattr directory may contain many EA entries, it may be
+ * impossible to change all of them in the same transaction as changing the
+ * main object's user/group/project attributes. If so, we have to change them
+ * via other multiple independent transactions one by one. It may be not a good
+ * solution, but we have no better idea yet.
+ */
+static int
+zfs_setattr_dir(znode_t *dzp)
+{
+ zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
+ objset_t *os = zfsvfs->z_os;
+ zap_cursor_t zc;
+ zap_attribute_t zap;
+ zfs_dirlock_t *dl;
+ znode_t *zp = NULL;
+ dmu_tx_t *tx = NULL;
+ sa_bulk_attr_t bulk[4];
+ int count = 0;
+ int err;
+
+ zap_cursor_init(&zc, os, dzp->z_id);
+ while ((err = zap_cursor_retrieve(&zc, &zap)) == 0) {
+ if (zap.za_integer_length != 8 || zap.za_num_integers != 1) {
+ err = ENXIO;
+ break;
+ }
+
+ err = zfs_dirent_lock(&dl, dzp, (char *)zap.za_name, &zp,
+ ZEXISTS, NULL, NULL);
+ if (err == ENOENT)
+ goto next;
+ if (err)
+ break;
+
+ if (zp->z_uid == dzp->z_uid &&
+ zp->z_gid == dzp->z_gid &&
+ zp->z_projid == dzp->z_projid)
+ goto next;
+
+ tx = dmu_tx_create(os);
+ if (!(zp->z_pflags & ZFS_PROJID))
+ dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
+ else
+ dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
+
+ err = dmu_tx_assign(tx, TXG_WAIT);
+ if (err)
+ break;
+
+ mutex_enter(&dzp->z_lock);
+
+ if (zp->z_uid != dzp->z_uid) {
+ zp->z_uid = dzp->z_uid;
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_UID(zfsvfs), NULL,
+ &dzp->z_uid, sizeof (dzp->z_uid));
+ }
+
+ if (zp->z_gid != dzp->z_gid) {
+ zp->z_gid = dzp->z_gid;
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs), NULL,
+ &dzp->z_gid, sizeof (dzp->z_gid));
+ }
+
+ if (zp->z_projid != dzp->z_projid) {
+ if (!(zp->z_pflags & ZFS_PROJID)) {
+ zp->z_pflags |= ZFS_PROJID;
+ SA_ADD_BULK_ATTR(bulk, count,
+ SA_ZPL_FLAGS(zfsvfs), NULL, &zp->z_pflags,
+ sizeof (zp->z_pflags));
+ }
+
+ zp->z_projid = dzp->z_projid;
+ SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PROJID(zfsvfs),
+ NULL, &zp->z_projid, sizeof (zp->z_projid));
+ }
+
+ mutex_exit(&dzp->z_lock);
+
+ if (likely(count > 0)) {
+ err = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
+ dmu_tx_commit(tx);
+ } else {
+ dmu_tx_abort(tx);
+ }
+ tx = NULL;
+ if (err != 0 && err != ENOENT)
+ break;
+
+next:
+ if (zp) {
+ VN_RELE(ZTOV(zp));
+ zp = NULL;
+ zfs_dirent_unlock(dl);
+ }
+ zap_cursor_advance(&zc);
+ }
+
+ if (tx)
+ dmu_tx_abort(tx);
+ if (zp) {
+ VN_RELE(ZTOV(zp));
+ zfs_dirent_unlock(dl);
+ }
+ zap_cursor_fini(&zc);
+
+ return (err == ENOENT ? 0 : err);
+}
+
+/*
* Set the file attributes to the values contained in the
* vattr structure.
*
@@ -2736,6 +2923,7 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
{
znode_t *zp = VTOZ(vp);
zfsvfs_t *zfsvfs = zp->z_zfsvfs;
+ objset_t *os = zfsvfs->z_os;
zilog_t *zilog;
dmu_tx_t *tx;
vattr_t oldva;
@@ -2747,16 +2935,18 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
uint64_t new_uid, new_gid;
uint64_t xattr_obj;
uint64_t mtime[2], ctime[2];
+ uint64_t projid = ZFS_INVALID_PROJID;
znode_t *attrzp;
int need_policy = FALSE;
- int err, err2;
+ int err, err2 = 0;
zfs_fuid_info_t *fuidp = NULL;
xvattr_t *xvap = (xvattr_t *)vap; /* vap may be an xvattr_t * */
xoptattr_t *xoap;
zfs_acl_t *aclp;
boolean_t skipaclchk = (flags & ATTR_NOACLCHECK) ? B_TRUE : B_FALSE;
boolean_t fuid_dirtied = B_FALSE;
- sa_bulk_attr_t bulk[7], xattr_bulk[7];
+ boolean_t handle_eadir = B_FALSE;
+ sa_bulk_attr_t bulk[8], xattr_bulk[8];
int count = 0, xattr_count = 0;
if (mask == 0)
@@ -2768,6 +2958,39 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
ZFS_ENTER(zfsvfs);
ZFS_VERIFY_ZP(zp);
+ /*
+ * If this is a xvattr_t, then get a pointer to the structure of
+ * optional attributes. If this is NULL, then we have a vattr_t.
+ */
+ xoap = xva_getxoptattr(xvap);
+ if (xoap != NULL && (mask & AT_XVATTR)) {
+ if (XVA_ISSET_REQ(xvap, XAT_PROJID)) {
+ if (!dmu_objset_projectquota_enabled(os) ||
+ (vp->v_type != VREG && vp->v_type != VDIR)) {
+ ZFS_EXIT(zfsvfs);
+ return (SET_ERROR(ENOTSUP));
+ }
+
+ projid = xoap->xoa_projid;
+ if (unlikely(projid == ZFS_INVALID_PROJID)) {
+ ZFS_EXIT(zfsvfs);
+ return (SET_ERROR(EINVAL));
+ }
+
+ if (projid == zp->z_projid && zp->z_pflags & ZFS_PROJID)
+ projid = ZFS_INVALID_PROJID;
+ else
+ need_policy = TRUE;
+ }
+
+ if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT) &&
+ (!dmu_objset_projectquota_enabled(os) ||
+ (vp->v_type != VREG && vp->v_type != VDIR))) {
+ ZFS_EXIT(zfsvfs);
+ return (SET_ERROR(ENOTSUP));
+ }
+ }
+
zilog = zfsvfs->z_log;
/*
@@ -2793,12 +3016,6 @@ zfs_setattr(vnode_t *vp, vattr_t *vap, int flags, cred_t *cr,
return (SET_ERROR(EINVAL));
}
- /*
- * If this is an xvattr_t, then get a pointer to the structure of
- * optional attributes. If this is NULL, then we have a vattr_t.
- */
- xoap = xva_getxoptattr(xvap);
-
xva_init(&tmpxvattr);
/*
@@ -2950,6 +3167,16 @@ top:
}
}
+ if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT)) {
+ if (xoap->xoa_projinherit !=
+ ((zp->z_pflags & ZFS_PROJINHERIT) != 0)) {
+ need_policy = TRUE;
+ } else {
+ XVA_CLR_REQ(xvap, XAT_PROJINHERIT);
+ XVA_SET_REQ(&tmpxvattr, XAT_PROJINHERIT);
+ }
+ }
+
if (XVA_ISSET_REQ(xvap, XAT_NOUNLINK)) {
if (xoap->xoa_nounlink !=
((zp->z_pflags & ZFS_NOUNLINK) != 0)) {
@@ -3061,7 +3288,8 @@ top:
*/
mask = vap->va_mask;
- if ((mask & (AT_UID | AT_GID))) {
+ if ((mask & (AT_UID | AT_GID)) || projid != ZFS_INVALID_PROJID) {
+ handle_eadir = B_TRUE;
err = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs),
&xattr_obj, sizeof (xattr_obj));
@@ -3074,7 +3302,8 @@ top:
new_uid = zfs_fuid_create(zfsvfs,
(uint64_t)vap->va_uid, cr, ZFS_OWNER, &fuidp);
if (new_uid != zp->z_uid &&
- zfs_fuid_overquota(zfsvfs, B_FALSE, new_uid)) {
+ zfs_id_overquota(zfsvfs, DMU_USERUSED_OBJECT,
+ new_uid)) {
if (attrzp)
VN_RELE(ZTOV(attrzp));
err = SET_ERROR(EDQUOT);
@@ -3086,15 +3315,24 @@ top:
new_gid = zfs_fuid_create(zfsvfs, (uint64_t)vap->va_gid,
cr, ZFS_GROUP, &fuidp);
if (new_gid != zp->z_gid &&
- zfs_fuid_overquota(zfsvfs, B_TRUE, new_gid)) {
+ zfs_id_overquota(zfsvfs, DMU_GROUPUSED_OBJECT,
+ new_gid)) {
if (attrzp)
VN_RELE(ZTOV(attrzp));
err = SET_ERROR(EDQUOT);
goto out2;
}
}
+
+ if (projid != ZFS_INVALID_PROJID &&
+ zfs_id_overquota(zfsvfs, DMU_PROJECTUSED_OBJECT, projid)) {
+ if (attrzp)
+ VN_RELE(ZTOV(attrzp));
+ err = EDQUOT;
+ goto out2;
+ }
}
- tx = dmu_tx_create(zfsvfs->z_os);
+ tx = dmu_tx_create(os);
if (mask & AT_MODE) {
uint64_t pmode = zp->z_mode;
@@ -3134,8 +3372,10 @@ top:
mutex_exit(&zp->z_lock);
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
} else {
- if ((mask & AT_XVATTR) &&
- XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
+ if (((mask & AT_XVATTR) &&
+ XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP)) ||
+ (projid != ZFS_INVALID_PROJID &&
+ !(zp->z_pflags & ZFS_PROJID)))
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_TRUE);
else
dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
@@ -3164,6 +3404,26 @@ top:
* updated as a side-effect of calling this function.
*/
+ if (projid != ZFS_INVALID_PROJID && !(zp->z_pflags & ZFS_PROJID)) {
+ /*
+ * For the existing object that is upgraded from old system,
+ * its on-disk layout has no slot for the project ID attribute.
+ * But quota accounting logic needs to access related slots by
+ * offset directly. So we need to adjust old objects' layout
+ * to make the project ID to some unified and fixed offset.
+ */
+ if (attrzp)
+ err = sa_add_projid(attrzp->z_sa_hdl, tx, projid);
+ if (err == 0)
+ err = sa_add_projid(zp->z_sa_hdl, tx, projid);
+
+ if (unlikely(err == EEXIST))
+ err = 0;
+ else if (err != 0)
+ goto out;
+ else
+ projid = ZFS_INVALID_PROJID;
+ }
if (mask & (AT_UID|AT_GID|AT_MODE))
mutex_enter(&zp->z_acl_lock);
@@ -3179,6 +3439,12 @@ top:
SA_ADD_BULK_ATTR(xattr_bulk, xattr_count,
SA_ZPL_FLAGS(zfsvfs), NULL, &attrzp->z_pflags,
sizeof (attrzp->z_pflags));
+ if (projid != ZFS_INVALID_PROJID) {
+ attrzp->z_projid = projid;
+ SA_ADD_BULK_ATTR(xattr_bulk, xattr_count,
+ SA_ZPL_PROJID(zfsvfs), NULL, &attrzp->z_projid,
+ sizeof (attrzp->z_projid));
+ }
}
if (mask & (AT_UID|AT_GID)) {
@@ -3266,6 +3532,14 @@ top:
mtime, ctime, B_TRUE);
}
}
+
+ if (projid != ZFS_INVALID_PROJID) {
+ zp->z_projid = projid;
+ SA_ADD_BULK_ATTR(bulk, count,
+ SA_ZPL_PROJID(zfsvfs), NULL, &zp->z_projid,
+ sizeof (zp->z_projid));
+ }
+
/*
* Do this after setting timestamps to prevent timestamp
* update from toggling bit
@@ -3296,6 +3570,9 @@ top:
if (XVA_ISSET_REQ(&tmpxvattr, XAT_AV_QUARANTINED)) {
XVA_SET_REQ(xvap, XAT_AV_QUARANTINED);
}
+ if (XVA_ISSET_REQ(&tmpxvattr, XAT_PROJINHERIT)) {
+ XVA_SET_REQ(xvap, XAT_PROJINHERIT);
+ }
if (XVA_ISSET_REQ(xvap, XAT_AV_SCANSTAMP))
ASSERT(vp->v_type == VREG);
@@ -3319,15 +3596,12 @@ top:
mutex_exit(&attrzp->z_lock);
}
out:
- if (err == 0 && attrzp) {
+ if (err == 0 && xattr_count > 0) {
err2 = sa_bulk_update(attrzp->z_sa_hdl, xattr_bulk,
xattr_count, tx);
ASSERT(err2 == 0);
}
- if (attrzp)
- VN_RELE(ZTOV(attrzp));
-
if (aclp)
zfs_acl_free(aclp);
@@ -3338,15 +3612,23 @@ out:
if (err) {
dmu_tx_abort(tx);
+ if (attrzp)
+ VN_RELE(ZTOV(attrzp));
if (err == ERESTART)
goto top;
} else {
- err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
+ if (count > 0)
+ err2 = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx);
dmu_tx_commit(tx);
+ if (attrzp) {
+ if (err2 == 0 && handle_eadir)
+ err2 = zfs_setattr_dir(attrzp);
+ VN_RELE(ZTOV(attrzp));
+ }
}
out2:
- if (zfsvfs->z_os->os_sync == ZFS_SYNC_ALWAYS)
+ if (os->os_sync == ZFS_SYNC_ALWAYS)
zil_commit(zilog, 0);
ZFS_EXIT(zfsvfs);
@@ -3646,6 +3928,19 @@ top:
}
/*
+ * If we are using project inheritance, it means if the directory has
+ * ZFS_PROJINHERIT set, then its descendant directories will inherit
+ * not only the project ID, but also the ZFS_PROJINHERIT flag. Under
+ * such case, we only allow renames into our tree when the project
+ * IDs are the same.
+ */
+ if (tdzp->z_pflags & ZFS_PROJINHERIT &&
+ tdzp->z_projid != szp->z_projid) {
+ error = SET_ERROR(EXDEV);
+ goto out;
+ }
+
+ /*
* Must have write access at the source to remove the old entry
* and write access at the target to create the new entry.
* Note that if target and source are the same, this can be
@@ -3752,6 +4047,8 @@ top:
error = zfs_link_create(tdl, szp, tx, ZRENAMING);
if (error == 0) {
szp->z_pflags |= ZFS_AV_MODIFIED;
+ if (tdzp->z_pflags & ZFS_PROJINHERIT)
+ szp->z_pflags |= ZFS_PROJINHERIT;
error = sa_update(szp->z_sa_hdl, SA_ZPL_FLAGS(zfsvfs),
(void *)&szp->z_pflags, sizeof (uint64_t), tx);
@@ -3893,7 +4190,7 @@ top:
return (error);
}
- if (zfs_acl_ids_overquota(zfsvfs, &acl_ids)) {
+ if (zfs_acl_ids_overquota(zfsvfs, &acl_ids, ZFS_DEFAULT_PROJID)) {
zfs_acl_ids_free(&acl_ids);
zfs_dirent_unlock(dl);
ZFS_EXIT(zfsvfs);
@@ -4067,6 +4364,18 @@ zfs_link(vnode_t *tdvp, vnode_t *svp, char *name, cred_t *cr,
ZFS_VERIFY_ZP(szp);
/*
+ * If we are using project inheritance, it means if the directory has
+ * ZFS_PROJINHERIT set, then its descendant directories will inherit
+ * not only the project ID, but also the ZFS_PROJINHERIT flag. Under
+ * such case, we only allow hard link creation in our tree when the
+ * project IDs are the same.
+ */
+ if (dzp->z_pflags & ZFS_PROJINHERIT && dzp->z_projid != szp->z_projid) {
+ ZFS_EXIT(zfsvfs);
+ return (SET_ERROR(EXDEV));
+ }
+
+ /*
* We check z_zfsvfs rather than v_vfsp here, because snapshots and the
* ctldir appear to have the same v_vfsp.
*/
@@ -4248,8 +4557,8 @@ zfs_putapage(vnode_t *vp, page_t *pp, u_offset_t *offp,
len = zp->z_size - off;
}
- if (zfs_owner_overquota(zfsvfs, zp, B_FALSE) ||
- zfs_owner_overquota(zfsvfs, zp, B_TRUE)) {
+ if (zfs_id_overblockquota(zfsvfs, DMU_USERUSED_OBJECT, zp->z_uid) ||
+ zfs_id_overblockquota(zfsvfs, DMU_GROUPUSED_OBJECT, zp->z_gid)) {
err = SET_ERROR(EDQUOT);
goto out;
}
diff --git a/usr/src/uts/common/fs/zfs/zfs_znode.c b/usr/src/uts/common/fs/zfs/zfs_znode.c
index b56cb7bd70..9abfc025d5 100644
--- a/usr/src/uts/common/fs/zfs/zfs_znode.c
+++ b/usr/src/uts/common/fs/zfs/zfs_znode.c
@@ -519,6 +519,7 @@ zfs_create_share_dir(zfsvfs_t *zfsvfs, dmu_tx_t *tx)
sharezp->z_atime_dirty = 0;
sharezp->z_zfsvfs = zfsvfs;
sharezp->z_is_sa = zfsvfs->z_use_sa;
+ sharezp->z_pflags = 0;
vp = ZTOV(sharezp);
vn_reinit(vp);
@@ -656,7 +657,8 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz,
vnode_t *vp;
uint64_t mode;
uint64_t parent;
- sa_bulk_attr_t bulk[9];
+ uint64_t projid = ZFS_DEFAULT_PROJID;
+ sa_bulk_attr_t bulk[11];
int count = 0;
zp = kmem_cache_alloc(znode_cache, KM_SLEEP);
@@ -699,13 +701,17 @@ zfs_znode_alloc(zfsvfs_t *zfsvfs, dmu_buf_t *db, int blksz,
SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_GID(zfsvfs), NULL,
&zp->z_gid, 8);
- if (sa_bulk_lookup(zp->z_sa_hdl, bulk, count) != 0 || zp->z_gen == 0) {
+ if (sa_bulk_lookup(zp->z_sa_hdl, bulk, count) != 0 || zp->z_gen == 0 ||
+ (dmu_objset_projectquota_enabled(zfsvfs->z_os) &&
+ (zp->z_pflags & ZFS_PROJID) &&
+ sa_lookup(zp->z_sa_hdl, SA_ZPL_PROJID(zfsvfs), &projid, 8) != 0)) {
if (hdl == NULL)
sa_handle_destroy(zp->z_sa_hdl);
kmem_cache_free(znode_cache, zp);
return (NULL);
}
+ zp->z_projid = projid;
zp->z_mode = mode;
vp->v_vfsp = zfsvfs->z_parent->z_vfs;
@@ -794,6 +800,7 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
uint64_t crtime[2], atime[2], mtime[2], ctime[2];
uint64_t mode, size, links, parent, pflags;
uint64_t dzp_pflags = 0;
+ uint64_t projid = ZFS_DEFAULT_PROJID;
uint64_t rdev = 0;
zfsvfs_t *zfsvfs = dzp->z_zfsvfs;
dmu_buf_t *db;
@@ -868,14 +875,12 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
*/
if (flag & IS_ROOT_NODE) {
dzp->z_id = obj;
- } else {
- dzp_pflags = dzp->z_pflags;
}
/*
* If parent is an xattr, so am I.
*/
- if (dzp_pflags & ZFS_XATTR) {
+ if (dzp->z_pflags & ZFS_XATTR) {
flag |= IS_XATTR;
}
@@ -900,6 +905,23 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
if (flag & IS_XATTR)
pflags |= ZFS_XATTR;
+ if (vap->va_type == VREG || vap->va_type == VDIR) {
+ /*
+ * With ZFS_PROJID flag, we can easily know whether there is
+ * project ID stored on disk or not. See zfs_space_delta_cb().
+ */
+ if (obj_type != DMU_OT_ZNODE &&
+ dmu_objset_projectquota_enabled(zfsvfs->z_os))
+ pflags |= ZFS_PROJID;
+
+ /*
+ * Inherit project ID from parent if required.
+ */
+ projid = zfs_inherit_projid(dzp);
+ if (dzp->z_pflags & ZFS_PROJINHERIT)
+ pflags |= ZFS_PROJINHERIT;
+ }
+
/*
* No execs denied will be deterimed when zfs_mode_compute() is called.
*/
@@ -981,6 +1003,10 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
if (obj_type == DMU_OT_ZNODE) {
SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_XATTR(zfsvfs), NULL,
&empty_xattr, 8);
+ } else if (dmu_objset_projectquota_enabled(zfsvfs->z_os) &&
+ pflags & ZFS_PROJID) {
+ SA_ADD_BULK_ATTR(sa_attrs, cnt, SA_ZPL_PROJID(zfsvfs),
+ NULL, &projid, 8);
}
if (obj_type == DMU_OT_ZNODE ||
(vap->va_type == VBLK || vap->va_type == VCHR)) {
@@ -1028,6 +1054,7 @@ zfs_mknode(znode_t *dzp, vattr_t *vap, dmu_tx_t *tx, cred_t *cr,
(*zpp)->z_pflags = pflags;
(*zpp)->z_mode = mode;
(*zpp)->z_dnodesize = dnodesize;
+ (*zpp)->z_projid = projid;
if (vap->va_mask & AT_XVATTR)
zfs_xvattr_set(*zpp, (xvattr_t *)vap, tx);
@@ -1133,6 +1160,11 @@ zfs_xvattr_set(znode_t *zp, xvattr_t *xvap, dmu_tx_t *tx)
zp->z_pflags, tx);
XVA_SET_RTN(xvap, XAT_SPARSE);
}
+ if (XVA_ISSET_REQ(xvap, XAT_PROJINHERIT)) {
+ ZFS_ATTR_SET(zp, ZFS_PROJINHERIT, xoap->xoa_projinherit,
+ zp->z_pflags, tx);
+ XVA_SET_RTN(xvap, XAT_PROJINHERIT);
+ }
}
int
@@ -1222,10 +1254,11 @@ zfs_rezget(znode_t *zp)
dmu_buf_t *db;
uint64_t obj_num = zp->z_id;
uint64_t mode;
- sa_bulk_attr_t bulk[8];
+ sa_bulk_attr_t bulk[10];
int err;
int count = 0;
uint64_t gen;
+ uint64_t projid = ZFS_DEFAULT_PROJID;
ZFS_OBJ_HOLD_ENTER(zfsvfs, obj_num);
@@ -1279,6 +1312,17 @@ zfs_rezget(znode_t *zp)
return (SET_ERROR(EIO));
}
+ if (dmu_objset_projectquota_enabled(zfsvfs->z_os)) {
+ err = sa_lookup(zp->z_sa_hdl, SA_ZPL_PROJID(zfsvfs),
+ &projid, 8);
+ if (err != 0 && err != ENOENT) {
+ zfs_znode_dmu_fini(zp);
+ ZFS_OBJ_HOLD_EXIT(zfsvfs, obj_num);
+ return (SET_ERROR(err));
+ }
+ }
+
+ zp->z_projid = projid;
zp->z_mode = mode;
if (gen != zp->z_gen) {
@@ -1870,6 +1914,7 @@ zfs_create_fs(objset_t *os, cred_t *cr, nvlist_t *zplprops, dmu_tx_t *tx)
rootzp->z_unlinked = 0;
rootzp->z_atime_dirty = 0;
rootzp->z_is_sa = USE_SA(version, os);
+ rootzp->z_pflags = 0;
vp = ZTOV(rootzp);
vn_reinit(vp);
diff --git a/usr/src/uts/common/fs/zfs/zio_crypt.c b/usr/src/uts/common/fs/zfs/zio_crypt.c
index 1d6b8286e3..78c26e3e90 100644
--- a/usr/src/uts/common/fs/zfs/zio_crypt.c
+++ b/usr/src/uts/common/fs/zfs/zio_crypt.c
@@ -1213,12 +1213,17 @@ zio_crypt_do_objset_hmacs(zio_crypt_key_t *key, void *data, uint_t datalen,
bcopy(raw_portable_mac, portable_mac, ZIO_OBJSET_MAC_LEN);
/*
- * The local MAC protects the user and group accounting. If these
- * objects are not present, the local MAC is zeroed out.
+ * The local MAC protects the user, group and project accounting.
+ * If these objects are not present, the local MAC is zeroed out.
*/
- if ((osp->os_userused_dnode.dn_type == DMU_OT_NONE &&
+ if ((datalen >= OBJSET_PHYS_SIZE_V3 &&
+ osp->os_userused_dnode.dn_type == DMU_OT_NONE &&
+ osp->os_groupused_dnode.dn_type == DMU_OT_NONE &&
+ osp->os_projectused_dnode.dn_type == DMU_OT_NONE) ||
+ (datalen >= OBJSET_PHYS_SIZE_V2 &&
+ osp->os_userused_dnode.dn_type == DMU_OT_NONE &&
osp->os_groupused_dnode.dn_type == DMU_OT_NONE) ||
- (datalen <= OBJSET_OLD_PHYS_SIZE)) {
+ (datalen <= OBJSET_PHYS_SIZE_V1)) {
bzero(local_mac, ZIO_OBJSET_MAC_LEN);
return (0);
}
diff --git a/usr/src/uts/common/sys/fs/zfs.h b/usr/src/uts/common/sys/fs/zfs.h
index a5b311e4f1..f0136aa148 100644
--- a/usr/src/uts/common/sys/fs/zfs.h
+++ b/usr/src/uts/common/sys/fs/zfs.h
@@ -182,6 +182,14 @@ typedef enum {
ZFS_PROP_USERQUOTA,
ZFS_PROP_GROUPUSED,
ZFS_PROP_GROUPQUOTA,
+ ZFS_PROP_USEROBJUSED,
+ ZFS_PROP_USEROBJQUOTA,
+ ZFS_PROP_GROUPOBJUSED,
+ ZFS_PROP_GROUPOBJQUOTA,
+ ZFS_PROP_PROJECTUSED,
+ ZFS_PROP_PROJECTQUOTA,
+ ZFS_PROP_PROJECTOBJUSED,
+ ZFS_PROP_PROJECTOBJQUOTA,
ZFS_NUM_USERQUOTA_PROPS
} zfs_userquota_prop_t;
diff --git a/usr/src/uts/common/sys/vnode.h b/usr/src/uts/common/sys/vnode.h
index b527558895..494264731b 100644
--- a/usr/src/uts/common/sys/vnode.h
+++ b/usr/src/uts/common/sys/vnode.h
@@ -469,6 +469,8 @@ typedef struct xoptattr {
uint64_t xoa_generation;
uint8_t xoa_offline;
uint8_t xoa_sparse;
+ uint8_t xoa_projinherit;
+ uint64_t xoa_projid;
} xoptattr_t;
/*
@@ -651,11 +653,14 @@ typedef vattr_t vattr32_t;
#define XAT0_GEN 0x00004000 /* object generation number */
#define XAT0_OFFLINE 0x00008000 /* offline */
#define XAT0_SPARSE 0x00010000 /* sparse */
+#define XAT0_PROJINHERIT 0x00020000 /* Create with parent projid */
+#define XAT0_PROJID 0x00040000 /* Project ID */
#define XAT0_ALL_ATTRS (XAT0_CREATETIME|XAT0_ARCHIVE|XAT0_SYSTEM| \
XAT0_READONLY|XAT0_HIDDEN|XAT0_NOUNLINK|XAT0_IMMUTABLE|XAT0_APPENDONLY| \
XAT0_NODUMP|XAT0_OPAQUE|XAT0_AV_QUARANTINED| XAT0_AV_MODIFIED| \
- XAT0_AV_SCANSTAMP|XAT0_REPARSE|XATO_GEN|XAT0_OFFLINE|XAT0_SPARSE)
+ XAT0_AV_SCANSTAMP|XAT0_REPARSE|XATO_GEN|XAT0_OFFLINE|XAT0_SPARSE| \
+ XAT0_PROJINHERIT | XAT0_PROJID)
/* Support for XAT_* optional attributes */
#define XVA_MASK 0xffffffff /* Used to mask off 32 bits */
@@ -692,6 +697,8 @@ typedef vattr_t vattr32_t;
#define XAT_GEN ((XAT0_INDEX << XVA_SHFT) | XAT0_GEN)
#define XAT_OFFLINE ((XAT0_INDEX << XVA_SHFT) | XAT0_OFFLINE)
#define XAT_SPARSE ((XAT0_INDEX << XVA_SHFT) | XAT0_SPARSE)
+#define XAT_PROJINHERIT ((XAT0_INDEX << XVA_SHFT) | XAT0_PROJINHERIT)
+#define XAT_PROJID ((XAT0_INDEX << XVA_SHFT) | XAT0_PROJID)
/*
* The returned attribute map array (xva_rtnattrmap[]) is located past the
diff --git a/usr/src/uts/i86pc/dboot/dboot_elfload.c b/usr/src/uts/i86pc/dboot/dboot_elfload.c
index 937bf9c144..c03f66145f 100644
--- a/usr/src/uts/i86pc/dboot/dboot_elfload.c
+++ b/usr/src/uts/i86pc/dboot/dboot_elfload.c
@@ -182,10 +182,11 @@ dboot_elfload64(uintptr_t file_image)
/* zero out bss */
if (shdr->sh_type == SHT_NOBITS) {
if (prom_debug)
- dboot_printf("zeroing BSS %ld bytes from "
- "physaddr 0x%llx (end=0x%llx)\n",
+ dboot_printf("zeroing BSS %lu bytes from "
+ "physaddr 0x%" PRIx64
+ " (end=0x%" PRIx64 ")\n",
(ulong_t)shdr->sh_size,
- (long long unsigned)next_addr,
+ next_addr,
next_addr + shdr->sh_size);
(void) memset((void *)(uintptr_t)next_addr, 0,
shdr->sh_size);
diff --git a/usr/src/uts/i86pc/dboot/dboot_startkern.c b/usr/src/uts/i86pc/dboot/dboot_startkern.c
index 5ab2db151b..5b4eade43e 100644
--- a/usr/src/uts/i86pc/dboot/dboot_startkern.c
+++ b/usr/src/uts/i86pc/dboot/dboot_startkern.c
@@ -683,17 +683,19 @@ dboot_loader_mmap_entries(void)
DBG(mb_info->flags);
if (mb_info->flags & 0x40) {
mb_memory_map_t *mmap;
+ caddr32_t mmap_addr;
DBG(mb_info->mmap_addr);
DBG(mb_info->mmap_length);
check_higher(mb_info->mmap_addr + mb_info->mmap_length);
- for (mmap = (mb_memory_map_t *)mb_info->mmap_addr;
- (uint32_t)mmap < mb_info->mmap_addr +
+ for (mmap_addr = mb_info->mmap_addr;
+ mmap_addr < mb_info->mmap_addr +
mb_info->mmap_length;
- mmap = (mb_memory_map_t *)((uint32_t)mmap +
- mmap->size + sizeof (mmap->size)))
+ mmap_addr += mmap->size + sizeof (mmap->size)) {
+ mmap = (mb_memory_map_t *)(uintptr_t)mmap_addr;
++num_entries;
+ }
num_entries_set = B_TRUE;
}
@@ -719,16 +721,17 @@ dboot_loader_mmap_get_type(int index)
{
#if !defined(__xpv)
mb_memory_map_t *mp, *mpend;
+ caddr32_t mmap_addr;
int i;
switch (multiboot_version) {
case 1:
- mp = (mb_memory_map_t *)mb_info->mmap_addr;
- mpend = (mb_memory_map_t *)
+ mp = (mb_memory_map_t *)(uintptr_t)mb_info->mmap_addr;
+ mpend = (mb_memory_map_t *)(uintptr_t)
(mb_info->mmap_addr + mb_info->mmap_length);
for (i = 0; mp < mpend && i != index; i++)
- mp = (mb_memory_map_t *)((uint32_t)mp + mp->size +
+ mp = (mb_memory_map_t *)((uintptr_t)mp + mp->size +
sizeof (mp->size));
if (mp >= mpend) {
dboot_panic("dboot_loader_mmap_get_type(): index "
@@ -765,7 +768,7 @@ dboot_loader_mmap_get_base(int index)
(mb_info->mmap_addr + mb_info->mmap_length);
for (i = 0; mp < mpend && i != index; i++)
- mp = (mb_memory_map_t *)((uint32_t)mp + mp->size +
+ mp = (mb_memory_map_t *)((uintptr_t)mp + mp->size +
sizeof (mp->size));
if (mp >= mpend) {
dboot_panic("dboot_loader_mmap_get_base(): index "
@@ -804,7 +807,7 @@ dboot_loader_mmap_get_length(int index)
(mb_info->mmap_addr + mb_info->mmap_length);
for (i = 0; mp < mpend && i != index; i++)
- mp = (mb_memory_map_t *)((uint32_t)mp + mp->size +
+ mp = (mb_memory_map_t *)((uintptr_t)mp + mp->size +
sizeof (mp->size));
if (mp >= mpend) {
dboot_panic("dboot_loader_mmap_get_length(): index "
@@ -1722,6 +1725,7 @@ process_efi32(EFI_SYSTEM_TABLE32 *efi)
{
uint32_t entries;
EFI_CONFIGURATION_TABLE32 *config;
+ efi_guid_t VendorGuid;
int i;
entries = efi->NumberOfTableEntries;
@@ -1729,21 +1733,23 @@ process_efi32(EFI_SYSTEM_TABLE32 *efi)
efi->ConfigurationTable;
for (i = 0; i < entries; i++) {
- if (dboot_same_guids(&config[i].VendorGuid, &smbios3)) {
+ (void) memcpy(&VendorGuid, &config[i].VendorGuid,
+ sizeof (VendorGuid));
+ if (dboot_same_guids(&VendorGuid, &smbios3)) {
bi->bi_smbios = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
if (bi->bi_smbios == 0 &&
- dboot_same_guids(&config[i].VendorGuid, &smbios)) {
+ dboot_same_guids(&VendorGuid, &smbios)) {
bi->bi_smbios = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
- if (dboot_same_guids(&config[i].VendorGuid, &acpi2)) {
+ if (dboot_same_guids(&VendorGuid, &acpi2)) {
bi->bi_acpi_rsdp = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
if (bi->bi_acpi_rsdp == 0 &&
- dboot_same_guids(&config[i].VendorGuid, &acpi1)) {
+ dboot_same_guids(&VendorGuid, &acpi1)) {
bi->bi_acpi_rsdp = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
@@ -1755,6 +1761,7 @@ process_efi64(EFI_SYSTEM_TABLE64 *efi)
{
uint64_t entries;
EFI_CONFIGURATION_TABLE64 *config;
+ efi_guid_t VendorGuid;
int i;
entries = efi->NumberOfTableEntries;
@@ -1762,22 +1769,24 @@ process_efi64(EFI_SYSTEM_TABLE64 *efi)
efi->ConfigurationTable;
for (i = 0; i < entries; i++) {
- if (dboot_same_guids(&config[i].VendorGuid, &smbios3)) {
+ (void) memcpy(&VendorGuid, &config[i].VendorGuid,
+ sizeof (VendorGuid));
+ if (dboot_same_guids(&VendorGuid, &smbios3)) {
bi->bi_smbios = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
if (bi->bi_smbios == 0 &&
- dboot_same_guids(&config[i].VendorGuid, &smbios)) {
+ dboot_same_guids(&VendorGuid, &smbios)) {
bi->bi_smbios = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
/* Prefer acpi v2+ over v1. */
- if (dboot_same_guids(&config[i].VendorGuid, &acpi2)) {
+ if (dboot_same_guids(&VendorGuid, &acpi2)) {
bi->bi_acpi_rsdp = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
if (bi->bi_acpi_rsdp == 0 &&
- dboot_same_guids(&config[i].VendorGuid, &acpi1)) {
+ dboot_same_guids(&VendorGuid, &acpi1)) {
bi->bi_acpi_rsdp = (native_ptr_t)(uintptr_t)
config[i].VendorTable;
}
@@ -1909,7 +1918,7 @@ print_efi64(EFI_SYSTEM_TABLE64 *efi)
dboot_printf("%c", (char)data[i]);
dboot_printf("\nEFI firmware revision: ");
dboot_print_efi_version(efi->FirmwareRevision);
- dboot_printf("EFI system table number of entries: %lld\n",
+ dboot_printf("EFI system table number of entries: %" PRIu64 "\n",
efi->NumberOfTableEntries);
conf = (EFI_CONFIGURATION_TABLE64 *)(uintptr_t)
efi->ConfigurationTable;
@@ -2259,7 +2268,7 @@ dboot_loader_name(void)
switch (multiboot_version) {
case 1:
- return ((char *)mb_info->boot_loader_name);
+ return ((char *)(uintptr_t)mb_info->boot_loader_name);
case 2:
tag = dboot_multiboot2_find_tag(mb2_info,