summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/os/linux/zfs/sys/zfs_vfsops.h12
-rw-r--r--include/sys/Makefile.am1
-rw-r--r--include/sys/zfs_fuid.h2
-rw-r--r--include/sys/zfs_quota.h46
-rw-r--r--module/os/linux/zfs/zfs_acl.c1
-rw-r--r--module/os/linux/zfs/zfs_vfsops.c458
-rw-r--r--module/os/linux/zfs/zfs_vnops.c1
-rw-r--r--module/zfs/Makefile.in1
-rw-r--r--module/zfs/zcp_get.c1
-rw-r--r--module/zfs/zfs_fuid.c20
-rw-r--r--module/zfs/zfs_ioctl.c1
-rw-r--r--module/zfs/zfs_quota.c475
12 files changed, 552 insertions, 467 deletions
diff --git a/include/os/linux/zfs/sys/zfs_vfsops.h b/include/os/linux/zfs/sys/zfs_vfsops.h
index 26e49d7b4..20acc2414 100644
--- a/include/os/linux/zfs/sys/zfs_vfsops.h
+++ b/include/os/linux/zfs/sys/zfs_vfsops.h
@@ -198,18 +198,6 @@ extern int zfs_suspend_fs(zfsvfs_t *zfsvfs);
extern int zfs_resume_fs(zfsvfs_t *zfsvfs, struct dsl_dataset *ds);
extern int zfs_end_fs(zfsvfs_t *zfsvfs, struct dsl_dataset *ds);
extern void zfs_exit_fs(zfsvfs_t *zfsvfs);
-extern int zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
- const char *domain, uint64_t rid, uint64_t *valuep);
-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_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, boolean_t readony, zfsvfs_t **zfvp);
extern int zfsvfs_create_impl(zfsvfs_t **zfvp, zfsvfs_t *zfsvfs, objset_t *os);
diff --git a/include/sys/Makefile.am b/include/sys/Makefile.am
index 75f7d93e7..99e38acb2 100644
--- a/include/sys/Makefile.am
+++ b/include/sys/Makefile.am
@@ -107,6 +107,7 @@ COMMON_H = \
$(top_srcdir)/include/sys/zfs_file.h \
$(top_srcdir)/include/sys/zfs_fuid.h \
$(top_srcdir)/include/sys/zfs_project.h \
+ $(top_srcdir)/include/sys/zfs_quota.h \
$(top_srcdir)/include/sys/zfs_ratelimit.h \
$(top_srcdir)/include/sys/zfs_rlock.h \
$(top_srcdir)/include/sys/zfs_sa.h \
diff --git a/include/sys/zfs_fuid.h b/include/sys/zfs_fuid.h
index 5c56f7fcc..fa399f115 100644
--- a/include/sys/zfs_fuid.h
+++ b/include/sys/zfs_fuid.h
@@ -116,6 +116,8 @@ extern int zfs_fuid_find_by_domain(zfsvfs_t *, const char *domain,
char **retdomain, boolean_t addok);
extern const char *zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx);
extern void zfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx);
+extern int zfs_id_to_fuidstr(zfsvfs_t *zfsvfs, const char *domain, uid_t rid,
+ char *buf, boolean_t addok);
#endif
char *zfs_fuid_idx_domain(avl_tree_t *, uint32_t);
diff --git a/include/sys/zfs_quota.h b/include/sys/zfs_quota.h
new file mode 100644
index 000000000..ec4dc8f16
--- /dev/null
+++ b/include/sys/zfs_quota.h
@@ -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
+ */
+
+#ifndef _SYS_ZFS_QUOTA_H
+#define _SYS_ZFS_QUOTA_H
+
+#include <sys/dmu.h>
+#include <sys/fs/zfs.h>
+#include <sys/zfs_vfsops.h>
+
+extern int zfs_space_delta_cb(dmu_object_type_t bonustype, void *data,
+ uint64_t *userp, uint64_t *groupp, uint64_t *projectp);
+
+extern int zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
+ const char *domain, uint64_t rid, uint64_t *valuep);
+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_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj,
+ uint64_t id);
+extern boolean_t zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj,
+ uint64_t id);
+extern boolean_t zfs_id_overquota(zfsvfs_t *zfsvfs, uint64_t usedobj,
+ uint64_t id);
+
+#endif
diff --git a/module/os/linux/zfs/zfs_acl.c b/module/os/linux/zfs/zfs_acl.c
index ab813cf26..794c99551 100644
--- a/module/os/linux/zfs/zfs_acl.c
+++ b/module/os/linux/zfs/zfs_acl.c
@@ -42,6 +42,7 @@
#include <sys/zfs_fuid.h>
#include <sys/zfs_acl.h>
#include <sys/zfs_dir.h>
+#include <sys/zfs_quota.h>
#include <sys/zfs_vfsops.h>
#include <sys/dmu.h>
#include <sys/dnode.h>
diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c
index 02e37f964..dba52baeb 100644
--- a/module/os/linux/zfs/zfs_vfsops.c
+++ b/module/os/linux/zfs/zfs_vfsops.c
@@ -52,6 +52,7 @@
#include <sys/zfs_ioctl.h>
#include <sys/zfs_ctldir.h>
#include <sys/zfs_fuid.h>
+#include <sys/zfs_quota.h>
#include <sys/sunddi.h>
#include <sys/dmu_objset.h>
#include <sys/spa_boot.h>
@@ -608,454 +609,6 @@ zfs_get_temporary_prop(dsl_dataset_t *ds, zfs_prop_t zfs_prop, uint64_t *val,
return (0);
}
-static int
-zfs_space_delta_cb(dmu_object_type_t bonustype, void *data,
- 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?
- */
- if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
- return (SET_ERROR(ENOENT));
-
- /*
- * If we have a NULL data pointer
- * then assume the id's aren't changing and
- * return EEXIST to the dmu to let it know to
- * use the same ids
- */
- if (data == NULL)
- return (SET_ERROR(EEXIST));
-
- if (bonustype == DMU_OT_ZNODE) {
- 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 {
- VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
- }
-
- 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));
- flags = *((uint64_t *)((uintptr_t)data + hdrsize + SA_FLAGS_OFFSET));
- if (swap)
- flags = BSWAP_64(flags);
-
- 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);
-}
-
-static void
-fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr,
- char *domainbuf, int buflen, uid_t *ridp)
-{
- uint64_t fuid;
- const char *domain;
-
- fuid = zfs_strtonum(fuidstr, NULL);
-
- domain = zfs_fuid_find_by_idx(zfsvfs, FUID_INDEX(fuid));
- if (domain)
- (void) strlcpy(domainbuf, domain, buflen);
- else
- domainbuf[0] = '\0';
- *ridp = FUID_RID(fuid);
-}
-
-static uint64_t
-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);
- }
-}
-
-int
-zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
- uint64_t *cookiep, void *vbuf, uint64_t *bufsizep)
-{
- int error;
- zap_cursor_t zc;
- 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 == 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)) {
- if ((uintptr_t)buf - (uintptr_t)vbuf + sizeof (zfs_useracct_t) >
- *bufsizep)
- break;
-
- /*
- * 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;
- buf++;
- }
- if (error == ENOENT)
- error = 0;
-
- ASSERT3U((uintptr_t)buf - (uintptr_t)vbuf, <=, *bufsizep);
- *bufsizep = (uintptr_t)buf - (uintptr_t)vbuf;
- *cookiep = zap_cursor_serialize(&zc);
- zap_cursor_fini(&zc);
- return (error);
-}
-
-/*
- * buf must be big enough (eg, 32 bytes)
- */
-static int
-id_to_fuidstr(zfsvfs_t *zfsvfs, const char *domain, uid_t rid,
- char *buf, boolean_t addok)
-{
- uint64_t fuid;
- int domainid = 0;
-
- if (domain && domain[0]) {
- domainid = zfs_fuid_find_by_domain(zfsvfs, domain, NULL, addok);
- if (domainid == -1)
- return (SET_ERROR(ENOENT));
- }
- fuid = FUID_ENCODE(domainid, rid);
- (void) sprintf(buf, "%llx", (longlong_t)fuid);
- return (0);
-}
-
-int
-zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
- const char *domain, uint64_t rid, uint64_t *valp)
-{
- char buf[20 + DMU_OBJACCT_PREFIX_LEN];
- int offset = 0;
- int err;
- uint64_t obj;
-
- *valp = 0;
-
- 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 == ZFS_NO_OBJECT)
- return (0);
-
- if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
- type == ZFS_PROP_PROJECTOBJUSED) {
- strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1);
- offset = DMU_OBJACCT_PREFIX_LEN;
- }
-
- err = id_to_fuidstr(zfsvfs, domain, rid, buf + offset, B_FALSE);
- if (err)
- return (err);
-
- err = zap_lookup(zfsvfs->z_os, obj, buf, 8, 1, valp);
- if (err == ENOENT)
- err = 0;
- return (err);
-}
-
-int
-zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
- const char *domain, uint64_t rid, uint64_t quota)
-{
- char buf[32];
- int err;
- dmu_tx_t *tx;
- uint64_t *objp;
- boolean_t fuid_dirtied;
-
- if (zfsvfs->z_version < ZPL_VERSION_USERSPACE)
- return (SET_ERROR(ENOTSUP));
-
- 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)
- return (err);
- fuid_dirtied = zfsvfs->z_fuid_dirty;
-
- tx = dmu_tx_create(zfsvfs->z_os);
- dmu_tx_hold_zap(tx, *objp ? *objp : DMU_NEW_OBJECT, B_TRUE, NULL);
- if (*objp == 0) {
- dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE,
- zfs_userquota_prop_prefixes[type]);
- }
- if (fuid_dirtied)
- zfs_fuid_txhold(zfsvfs, tx);
- err = dmu_tx_assign(tx, TXG_WAIT);
- if (err) {
- dmu_tx_abort(tx);
- return (err);
- }
-
- mutex_enter(&zfsvfs->z_lock);
- if (*objp == 0) {
- *objp = zap_create(zfsvfs->z_os, DMU_OT_USERGROUP_QUOTA,
- DMU_OT_NONE, 0, tx);
- VERIFY(0 == zap_add(zfsvfs->z_os, MASTER_NODE_OBJ,
- zfs_userquota_prop_prefixes[type], 8, 1, objp, tx));
- }
- mutex_exit(&zfsvfs->z_lock);
-
- if (quota == 0) {
- err = zap_remove(zfsvfs->z_os, *objp, buf, tx);
- if (err == ENOENT)
- err = 0;
- } else {
- err = zap_update(zfsvfs->z_os, *objp, buf, 8, 1, &quota, tx);
- }
- ASSERT(err == 0);
- if (fuid_dirtied)
- zfs_fuid_sync(zfsvfs, tx);
- dmu_tx_commit(tx);
- return (err);
-}
-
-boolean_t
-zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
-{
- char buf[20 + DMU_OBJACCT_PREFIX_LEN];
- uint64_t used, quota, quotaobj;
- int err;
-
- if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) {
- if (dmu_objset_userobjspace_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);
- }
-
- 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)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);
- return (used >= quota);
-}
-
-boolean_t
-zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
-{
- char buf[20];
- uint64_t used, quota, quotaobj;
- int err;
-
- 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);
-
- (void) sprintf(buf, "%llx", (longlong_t)id);
- err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
- if (err != 0)
- return (B_FALSE);
-
- err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
- if (err != 0)
- return (B_FALSE);
- return (used >= quota);
-}
-
-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));
-}
-
/*
* Associate this zfsvfs with the given objset, which must be owned.
* This will cache a bunch of on-disk state from the objset in the
@@ -1454,7 +1007,8 @@ zfs_statfs_project(zfsvfs_t *zfsvfs, znode_t *zp, struct kstatfs *statp,
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);
+ err = zfs_id_to_fuidstr(zfsvfs, NULL, zp->z_projid, buf + offset,
+ B_FALSE);
if (err)
return (err);
@@ -2575,12 +2129,6 @@ zfs_fini(void)
#if defined(_KERNEL)
EXPORT_SYMBOL(zfs_suspend_fs);
EXPORT_SYMBOL(zfs_resume_fs);
-EXPORT_SYMBOL(zfs_userspace_one);
-EXPORT_SYMBOL(zfs_userspace_many);
-EXPORT_SYMBOL(zfs_set_userquota);
-EXPORT_SYMBOL(zfs_id_overblockquota);
-EXPORT_SYMBOL(zfs_id_overobjquota);
-EXPORT_SYMBOL(zfs_id_overquota);
EXPORT_SYMBOL(zfs_set_version);
EXPORT_SYMBOL(zfsvfs_create);
EXPORT_SYMBOL(zfsvfs_free);
diff --git a/module/os/linux/zfs/zfs_vnops.c b/module/os/linux/zfs/zfs_vnops.c
index b5e1a63ad..b1fa89167 100644
--- a/module/os/linux/zfs/zfs_vnops.c
+++ b/module/os/linux/zfs/zfs_vnops.c
@@ -61,6 +61,7 @@
#include <sys/sid.h>
#include <sys/zfs_ctldir.h>
#include <sys/zfs_fuid.h>
+#include <sys/zfs_quota.h>
#include <sys/zfs_sa.h>
#include <sys/zfs_vnops.h>
#include <sys/zfs_rlock.h>
diff --git a/module/zfs/Makefile.in b/module/zfs/Makefile.in
index c00c8e424..a98ba4fac 100644
--- a/module/zfs/Makefile.in
+++ b/module/zfs/Makefile.in
@@ -115,6 +115,7 @@ $(MODULE)-objs += zfs_fuid.o
$(MODULE)-objs += zfs_ioctl.o
$(MODULE)-objs += zfs_log.o
$(MODULE)-objs += zfs_onexit.o
+$(MODULE)-objs += zfs_quota.o
$(MODULE)-objs += zfs_ratelimit.o
$(MODULE)-objs += zfs_replay.o
$(MODULE)-objs += zfs_rlock.o
diff --git a/module/zfs/zcp_get.c b/module/zfs/zcp_get.c
index fdf566d27..44c4e82c2 100644
--- a/module/zfs/zcp_get.c
+++ b/module/zfs/zcp_get.c
@@ -39,6 +39,7 @@
#include <sys/zvol.h>
#ifdef _KERNEL
+#include <sys/zfs_quota.h>
#include <sys/zfs_vfsops.h>
#endif
diff --git a/module/zfs/zfs_fuid.c b/module/zfs/zfs_fuid.c
index 925d1934f..dbf9d31a9 100644
--- a/module/zfs/zfs_fuid.c
+++ b/module/zfs/zfs_fuid.c
@@ -772,4 +772,24 @@ zfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx)
FUID_SIZE_ESTIMATE(zfsvfs));
}
}
+
+/*
+ * buf must be big enough (eg, 32 bytes)
+ */
+int
+zfs_id_to_fuidstr(zfsvfs_t *zfsvfs, const char *domain, uid_t rid,
+ char *buf, boolean_t addok)
+{
+ uint64_t fuid;
+ int domainid = 0;
+
+ if (domain && domain[0]) {
+ domainid = zfs_fuid_find_by_domain(zfsvfs, domain, NULL, addok);
+ if (domainid == -1)
+ return (SET_ERROR(ENOENT));
+ }
+ fuid = FUID_ENCODE(domainid, rid);
+ (void) sprintf(buf, "%llx", (longlong_t)fuid);
+ return (0);
+}
#endif
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index 92df28b1f..2c1f0aab6 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -162,6 +162,7 @@
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/zfs_ioctl.h>
+#include <sys/zfs_quota.h>
#include <sys/zfs_vfsops.h>
#include <sys/zfs_znode.h>
#include <sys/zap.h>
diff --git a/module/zfs/zfs_quota.c b/module/zfs/zfs_quota.c
new file mode 100644
index 000000000..1acfbf1ea
--- /dev/null
+++ b/module/zfs/zfs_quota.c
@@ -0,0 +1,475 @@
+/*
+ * 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) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011 Pawel Jakub Dawidek <[email protected]>.
+ * All rights reserved.
+ * Copyright (c) 2012, 2015, 2018 by Delphix. All rights reserved.
+ * Copyright (c) 2014 Integros [integros.com]
+ * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/* Portions Copyright 2010 Robert Milkowski */
+
+#include <sys/avl.h>
+#include <sys/dmu_objset.h>
+#include <sys/sa.h>
+#include <sys/sa_impl.h>
+#include <sys/zap.h>
+#include <sys/zfs_project.h>
+#include <sys/zfs_quota.h>
+#include <sys/zfs_znode.h>
+
+int
+zfs_space_delta_cb(dmu_object_type_t bonustype, void *data,
+ 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?
+ */
+ if (bonustype != DMU_OT_ZNODE && bonustype != DMU_OT_SA)
+ return (SET_ERROR(ENOENT));
+
+ /*
+ * If we have a NULL data pointer
+ * then assume the id's aren't changing and
+ * return EEXIST to the dmu to let it know to
+ * use the same ids
+ */
+ if (data == NULL)
+ return (SET_ERROR(EEXIST));
+
+ if (bonustype == DMU_OT_ZNODE) {
+ 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 {
+ VERIFY3U(sa.sa_magic, ==, SA_MAGIC);
+ }
+
+ 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));
+ flags = *((uint64_t *)((uintptr_t)data + hdrsize + SA_FLAGS_OFFSET));
+ if (swap)
+ flags = BSWAP_64(flags);
+
+ 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);
+}
+
+static void
+fuidstr_to_sid(zfsvfs_t *zfsvfs, const char *fuidstr,
+ char *domainbuf, int buflen, uid_t *ridp)
+{
+ uint64_t fuid;
+ const char *domain;
+
+ fuid = zfs_strtonum(fuidstr, NULL);
+
+ domain = zfs_fuid_find_by_idx(zfsvfs, FUID_INDEX(fuid));
+ if (domain)
+ (void) strlcpy(domainbuf, domain, buflen);
+ else
+ domainbuf[0] = '\0';
+ *ridp = FUID_RID(fuid);
+}
+
+static uint64_t
+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);
+ }
+}
+
+int
+zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
+ uint64_t *cookiep, void *vbuf, uint64_t *bufsizep)
+{
+ int error;
+ zap_cursor_t zc;
+ 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 == 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)) {
+ if ((uintptr_t)buf - (uintptr_t)vbuf + sizeof (zfs_useracct_t) >
+ *bufsizep)
+ break;
+
+ /*
+ * 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;
+ buf++;
+ }
+ if (error == ENOENT)
+ error = 0;
+
+ ASSERT3U((uintptr_t)buf - (uintptr_t)vbuf, <=, *bufsizep);
+ *bufsizep = (uintptr_t)buf - (uintptr_t)vbuf;
+ *cookiep = zap_cursor_serialize(&zc);
+ zap_cursor_fini(&zc);
+ return (error);
+}
+
+int
+zfs_userspace_one(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
+ const char *domain, uint64_t rid, uint64_t *valp)
+{
+ char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+ int offset = 0;
+ int err;
+ uint64_t obj;
+
+ *valp = 0;
+
+ 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 == ZFS_NO_OBJECT)
+ return (0);
+
+ if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+ type == ZFS_PROP_PROJECTOBJUSED) {
+ strlcpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN + 1);
+ offset = DMU_OBJACCT_PREFIX_LEN;
+ }
+
+ err = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf + offset, B_FALSE);
+ if (err)
+ return (err);
+
+ err = zap_lookup(zfsvfs->z_os, obj, buf, 8, 1, valp);
+ if (err == ENOENT)
+ err = 0;
+ return (err);
+}
+
+int
+zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
+ const char *domain, uint64_t rid, uint64_t quota)
+{
+ char buf[32];
+ int err;
+ dmu_tx_t *tx;
+ uint64_t *objp;
+ boolean_t fuid_dirtied;
+
+ if (zfsvfs->z_version < ZPL_VERSION_USERSPACE)
+ return (SET_ERROR(ENOTSUP));
+
+ 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 = zfs_id_to_fuidstr(zfsvfs, domain, rid, buf, B_TRUE);
+ if (err)
+ return (err);
+ fuid_dirtied = zfsvfs->z_fuid_dirty;
+
+ tx = dmu_tx_create(zfsvfs->z_os);
+ dmu_tx_hold_zap(tx, *objp ? *objp : DMU_NEW_OBJECT, B_TRUE, NULL);
+ if (*objp == 0) {
+ dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE,
+ zfs_userquota_prop_prefixes[type]);
+ }
+ if (fuid_dirtied)
+ zfs_fuid_txhold(zfsvfs, tx);
+ err = dmu_tx_assign(tx, TXG_WAIT);
+ if (err) {
+ dmu_tx_abort(tx);
+ return (err);
+ }
+
+ mutex_enter(&zfsvfs->z_lock);
+ if (*objp == 0) {
+ *objp = zap_create(zfsvfs->z_os, DMU_OT_USERGROUP_QUOTA,
+ DMU_OT_NONE, 0, tx);
+ VERIFY(0 == zap_add(zfsvfs->z_os, MASTER_NODE_OBJ,
+ zfs_userquota_prop_prefixes[type], 8, 1, objp, tx));
+ }
+ mutex_exit(&zfsvfs->z_lock);
+
+ if (quota == 0) {
+ err = zap_remove(zfsvfs->z_os, *objp, buf, tx);
+ if (err == ENOENT)
+ err = 0;
+ } else {
+ err = zap_update(zfsvfs->z_os, *objp, buf, 8, 1, &quota, tx);
+ }
+ ASSERT(err == 0);
+ if (fuid_dirtied)
+ zfs_fuid_sync(zfsvfs, tx);
+ dmu_tx_commit(tx);
+ return (err);
+}
+
+boolean_t
+zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
+{
+ char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+ uint64_t used, quota, quotaobj;
+ int err;
+
+ if (!dmu_objset_userobjspace_present(zfsvfs->z_os)) {
+ if (dmu_objset_userobjspace_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);
+ }
+
+ 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)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);
+ return (used >= quota);
+}
+
+boolean_t
+zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
+{
+ char buf[20];
+ uint64_t used, quota, quotaobj;
+ int err;
+
+ 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);
+
+ (void) sprintf(buf, "%llx", (longlong_t)id);
+ err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
+ if (err != 0)
+ return (B_FALSE);
+
+ err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
+ if (err != 0)
+ return (B_FALSE);
+ return (used >= quota);
+}
+
+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));
+}
+
+EXPORT_SYMBOL(zfs_space_delta_cb);
+EXPORT_SYMBOL(zfs_userspace_one);
+EXPORT_SYMBOL(zfs_userspace_many);
+EXPORT_SYMBOL(zfs_set_userquota);
+EXPORT_SYMBOL(zfs_id_overblockquota);
+EXPORT_SYMBOL(zfs_id_overobjquota);
+EXPORT_SYMBOL(zfs_id_overquota);