]> git.proxmox.com Git - mirror_zfs.git/blobdiff - module/zfs/zfs_vfsops.c
Add support for user/group dnode accounting & quota
[mirror_zfs.git] / module / zfs / zfs_vfsops.c
index d8b27461aacc9571275e3350ec5510793c42d43c..63b7f923087ff92d7d77d58ce5fb2b26f9bc44a8 100644 (file)
@@ -431,17 +431,22 @@ zfs_userquota_prop_to_obj(zfs_sb_t *zsb, 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_USERQUOTA:
                return (zsb->z_userquota_obj);
        case ZFS_PROP_GROUPQUOTA:
                return (zsb->z_groupquota_obj);
+       case ZFS_PROP_USEROBJQUOTA:
+               return (zsb->z_userobjquota_obj);
+       case ZFS_PROP_GROUPOBJQUOTA:
+               return (zsb->z_groupobjquota_obj);
        default:
-               return (SET_ERROR(ENOTSUP));
+               return (ZFS_NO_OBJECT);
        }
-       return (0);
 }
 
 int
@@ -453,16 +458,25 @@ zfs_userspace_many(zfs_sb_t *zsb, 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(zsb->z_os))
                return (SET_ERROR(ENOTSUP));
 
+       if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA) &&
+           !dmu_objset_userobjspace_present(zsb->z_os))
+               return (SET_ERROR(ENOTSUP));
+
        obj = zfs_userquota_prop_to_obj(zsb, type);
-       if (obj == 0) {
+       if (obj == ZFS_NO_OBJECT) {
                *bufsizep = 0;
                return (0);
        }
 
+       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED)
+               offset = DMU_OBJACCT_PREFIX_LEN;
+
        for (zap_cursor_init_serialized(&zc, zsb->z_os, obj, *cookiep);
            (error = zap_cursor_retrieve(&zc, &za)) == 0;
            zap_cursor_advance(&zc)) {
@@ -470,7 +484,15 @@ zfs_userspace_many(zfs_sb_t *zsb, zfs_userquota_prop_t type,
                    *bufsizep)
                        break;
 
-               fuidstr_to_sid(zsb, 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(zsb, za.za_name + offset,
                    buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid);
 
                buf->zu_space = za.za_first_integer;
@@ -511,7 +533,8 @@ int
 zfs_userspace_one(zfs_sb_t *zsb, 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;
 
@@ -520,11 +543,21 @@ zfs_userspace_one(zfs_sb_t *zsb, zfs_userquota_prop_t type,
        if (!dmu_objset_userspace_present(zsb->z_os))
                return (SET_ERROR(ENOTSUP));
 
+       if ((type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED ||
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA) &&
+           !dmu_objset_userobjspace_present(zsb->z_os))
+               return (SET_ERROR(ENOTSUP));
+
        obj = zfs_userquota_prop_to_obj(zsb, type);
-       if (obj == 0)
+       if (obj == ZFS_NO_OBJECT)
                return (0);
 
-       err = id_to_fuidstr(zsb, domain, rid, buf, B_FALSE);
+       if (type == ZFS_PROP_USEROBJUSED || type == ZFS_PROP_GROUPOBJUSED) {
+               strncpy(buf, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN);
+               offset = DMU_OBJACCT_PREFIX_LEN;
+       }
+
+       err = id_to_fuidstr(zsb, domain, rid, buf + offset, B_FALSE);
        if (err)
                return (err);
 
@@ -545,14 +578,25 @@ zfs_set_userquota(zfs_sb_t *zsb, 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 (zsb->z_version < ZPL_VERSION_USERSPACE)
                return (SET_ERROR(ENOTSUP));
 
-       objp = (type == ZFS_PROP_USERQUOTA) ? &zsb->z_userquota_obj :
-           &zsb->z_groupquota_obj;
+       switch (type) {
+       case ZFS_PROP_USERQUOTA:
+               objp = &zsb->z_userquota_obj;
+               break;
+       case ZFS_PROP_GROUPQUOTA:
+               objp = &zsb->z_groupquota_obj;
+               break;
+       case ZFS_PROP_USEROBJQUOTA:
+               objp = &zsb->z_userobjquota_obj;
+               break;
+       case ZFS_PROP_GROUPOBJQUOTA:
+               objp = &zsb->z_groupobjquota_obj;
+               break;
+       default:
+               return (SET_ERROR(EINVAL));
+       }
 
        err = id_to_fuidstr(zsb, domain, rid, buf, B_TRUE);
        if (err)
@@ -597,10 +641,40 @@ zfs_set_userquota(zfs_sb_t *zsb, zfs_userquota_prop_t type,
 }
 EXPORT_SYMBOL(zfs_set_userquota);
 
+boolean_t
+zfs_fuid_overobjquota(zfs_sb_t *zsb, boolean_t isgroup, uint64_t fuid)
+{
+       char buf[20 + DMU_OBJACCT_PREFIX_LEN];
+       uint64_t used, quota, usedobj, quotaobj;
+       int err;
+
+       if (!dmu_objset_userobjspace_present(zsb->z_os)) {
+               if (dmu_objset_userobjspace_upgradable(zsb->z_os))
+                       dmu_objset_userobjspace_upgrade(zsb->z_os);
+               return (B_FALSE);
+       }
+
+       usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT;
+       quotaobj = isgroup ? zsb->z_groupobjquota_obj : zsb->z_userobjquota_obj;
+       if (quotaobj == 0 || zsb->z_replay)
+               return (B_FALSE);
+
+       (void) sprintf(buf, "%llx", (longlong_t)fuid);
+       err = zap_lookup(zsb->z_os, quotaobj, buf, 8, 1, &quota);
+       if (err != 0)
+               return (B_FALSE);
+
+       (void) sprintf(buf, DMU_OBJACCT_PREFIX "%llx", (longlong_t)fuid);
+       err = zap_lookup(zsb->z_os, usedobj, buf, 8, 1, &used);
+       if (err != 0)
+               return (B_FALSE);
+       return (used >= quota);
+}
+
 boolean_t
 zfs_fuid_overquota(zfs_sb_t *zsb, boolean_t isgroup, uint64_t fuid)
 {
-       char buf[32];
+       char buf[20];
        uint64_t used, quota, usedobj, quotaobj;
        int err;
 
@@ -777,6 +851,18 @@ zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp)
        if (error && error != ENOENT)
                goto out;
 
+       error = zap_lookup(os, MASTER_NODE_OBJ,
+           zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA],
+           8, 1, &zsb->z_userobjquota_obj);
+       if (error && error != ENOENT)
+               goto out;
+
+       error = zap_lookup(os, MASTER_NODE_OBJ,
+           zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA],
+           8, 1, &zsb->z_groupobjquota_obj);
+       if (error && error != ENOENT)
+               goto out;
+
        error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1,
            &zsb->z_fuid_obj);
        if (error && error != ENOENT)