diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index 18378c4e6b6d..efb57b5196ee 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -1934,11 +1934,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_USERDNUSED_ACCOUNTED) ? + "USERDNUSED_ACCOUNTED " : "", (dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR) ? "SPILL_BLKPTR" : ""); (void) printf("\tdnode maxblkid: %llu\n", diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 6fb35595ccc9..e6415431f2c7 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -2119,6 +2119,7 @@ zfs_do_upgrade(int argc, char **argv) (void) printf(gettext(" 4 userquota, groupquota " "properties\n")); (void) printf(gettext(" 5 System attributes\n")); + (void) printf(gettext(" 6 user/group dnode quota\n")); (void) printf(gettext("\nFor more information on a particular " "version, including supported releases,\n")); (void) printf("see the ZFS Administration Guide.\n\n"); @@ -2801,11 +2802,14 @@ 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 (((p == ZFS_PROP_USERUSED || p == ZFS_PROP_USERQUOTA || + p == ZFS_PROP_USERDNUSED || p == ZFS_PROP_USERDNQUOTA) && !(types & (USTYPE_PSX_USR | USTYPE_SMB_USR))) || - ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA) && + ((p == ZFS_PROP_GROUPUSED || p == ZFS_PROP_GROUPQUOTA || + p == ZFS_PROP_GROUPDNUSED || p == ZFS_PROP_GROUPDNQUOTA) && !(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP)))) continue; + cb.cb_prop = p; if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0) return (ret); @@ -3948,6 +3952,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_USERDNQUOTA "userdnquota" +#define ZFS_DELEG_PERM_GROUPDNQUOTA "groupdnquota" +#define ZFS_DELEG_PERM_USERDNUSED "userdnused" +#define ZFS_DELEG_PERM_GROUPDNUSED "groupdnused" + #define ZFS_DELEG_PERM_HOLD "hold" #define ZFS_DELEG_PERM_RELEASE "release" #define ZFS_DELEG_PERM_DIFF "diff" @@ -3978,6 +3987,10 @@ 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_USERDNQUOTA, ZFS_DELEG_NOTE_USERDNQUOTA }, + { ZFS_DELEG_PERM_USERDNUSED, ZFS_DELEG_NOTE_USERDNUSED }, + { ZFS_DELEG_PERM_GROUPDNQUOTA, ZFS_DELEG_NOTE_GROUPDNQUOTA }, + { ZFS_DELEG_PERM_GROUPDNUSED, ZFS_DELEG_NOTE_GROUPDNUSED }, { NULL, ZFS_DELEG_NOTE_NONE } }; @@ -4055,6 +4068,10 @@ 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_USERDNQUOTA: + case ZFS_DELEG_NOTE_USERDNUSED: + case ZFS_DELEG_NOTE_GROUPDNQUOTA: + case ZFS_DELEG_NOTE_GROUPDNUSED: /* other */ return (gettext("other")); default: @@ -4558,6 +4575,18 @@ 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_USERDNQUOTA: + str = gettext("Allows accessing any userdnquota@... property"); + break; + case ZFS_DELEG_NOTE_GROUPDNQUOTA: + str = gettext("Allows accessing any groupdnquota@... property"); + break; + case ZFS_DELEG_NOTE_GROUPDNUSED: + str = gettext("Allows reading any groupdnused@... property"); + break; + case ZFS_DELEG_NOTE_USERDNUSED: + str = gettext("Allows reading any userdnused@... property"); + break; /* other */ default: str = ""; diff --git a/include/sys/dmu_objset.h b/include/sys/dmu_objset.h index 837a0d5107b7..aacf98816755 100644 --- a/include/sys/dmu_objset.h +++ b/include/sys/dmu_objset.h @@ -56,6 +56,7 @@ struct dmu_tx; (arc_buf_size(buf) > OBJSET_OLD_PHYS_SIZE) #define OBJSET_FLAG_USERACCOUNTING_COMPLETE (1ULL<<0) +#define OBJSET_FLAG_USERDNACCOUNTING_COMPLETE (1ULL<<1) typedef struct objset_phys { dnode_phys_t os_meta_dnode; @@ -172,6 +173,9 @@ 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_userdnused_enabled(objset_t *os); +int dmu_objset_userdnspace_upgrade(objset_t *os); +boolean_t dmu_objset_userdnspace_present(objset_t *os); int dmu_fsname(const char *snapname, char *buf); void dmu_objset_evict_done(objset_t *os); diff --git a/include/sys/dnode.h b/include/sys/dnode.h index 50e01155903a..9a136d55caf8 100644 --- a/include/sys/dnode.h +++ b/include/sys/dnode.h @@ -120,6 +120,9 @@ enum dnode_dirtycontext { /* Does dnode have a SA spill blkptr in bonus? */ #define DNODE_FLAG_SPILL_BLKPTR (1<<2) +/* User/Group dnode accounting */ +#define DNODE_FLAG_USERDNUSED_ACCOUNTED (1<<3) + typedef struct dnode_phys { uint8_t dn_type; /* dmu_object_type_t */ uint8_t dn_indblkshift; /* ln2(indirect block size) */ diff --git a/include/sys/dsl_deleg.h b/include/sys/dsl_deleg.h index 59e8e055551a..321ecc433537 100644 --- a/include/sys/dsl_deleg.h +++ b/include/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_USERDNQUOTA "userdnquota" +#define ZFS_DELEG_PERM_GROUPDNQUOTA "groupdnquota" #define ZFS_DELEG_PERM_USERUSED "userused" #define ZFS_DELEG_PERM_GROUPUSED "groupused" +#define ZFS_DELEG_PERM_USERDNUSED "userdnused" +#define ZFS_DELEG_PERM_GROUPDNUSED "groupdnused" #define ZFS_DELEG_PERM_HOLD "hold" #define ZFS_DELEG_PERM_RELEASE "release" #define ZFS_DELEG_PERM_DIFF "diff" diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 4da144c724ab..9ff452a3dd5a 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -163,6 +163,10 @@ typedef enum { ZFS_PROP_USERQUOTA, ZFS_PROP_GROUPUSED, ZFS_PROP_GROUPQUOTA, + ZFS_PROP_USERDNUSED, + ZFS_PROP_USERDNQUOTA, + ZFS_PROP_GROUPDNUSED, + ZFS_PROP_GROUPDNQUOTA, ZFS_NUM_USERQUOTA_PROPS } zfs_userquota_prop_t; @@ -884,6 +888,7 @@ typedef enum zfs_ioc { ZFS_IOC_BOOKMARK, ZFS_IOC_GET_BOOKMARKS, ZFS_IOC_DESTROY_BOOKMARKS, + ZFS_IOC_USERDNSPACE_UPGRADE, /* * Linux - 3/64 numbers reserved. diff --git a/include/sys/zfs_vfsops.h b/include/sys/zfs_vfsops.h index 28407c6f76fe..a0b373da388f 100644 --- a/include/sys/zfs_vfsops.h +++ b/include/sys/zfs_vfsops.h @@ -110,6 +110,8 @@ typedef struct zfs_sb { kmutex_t z_lock; uint64_t z_userquota_obj; uint64_t z_groupquota_obj; + uint64_t z_userdnquota_obj; + uint64_t z_groupdnquota_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 256 diff --git a/include/zfeature_common.h b/include/zfeature_common.h index e383c4ff7887..4693ad89b493 100644 --- a/include/zfeature_common.h +++ b/include/zfeature_common.h @@ -50,6 +50,7 @@ typedef enum spa_feature { SPA_FEATURE_BOOKMARKS, SPA_FEATURE_FS_SS_LIMIT, SPA_FEATURE_LARGE_BLOCKS, + SPA_FEATURE_USERDN_ACCOUNTING, SPA_FEATURES } spa_feature_t; diff --git a/include/zfs_deleg.h b/include/zfs_deleg.h index 16133c59f33f..e100d93ffa31 100644 --- a/include/zfs_deleg.h +++ b/include/zfs_deleg.h @@ -63,6 +63,10 @@ typedef enum { ZFS_DELEG_NOTE_GROUPQUOTA, ZFS_DELEG_NOTE_USERUSED, ZFS_DELEG_NOTE_GROUPUSED, + ZFS_DELEG_NOTE_USERDNQUOTA, + ZFS_DELEG_NOTE_GROUPDNQUOTA, + ZFS_DELEG_NOTE_USERDNUSED, + ZFS_DELEG_NOTE_GROUPDNUSED, ZFS_DELEG_NOTE_HOLD, ZFS_DELEG_NOTE_RELEASE, ZFS_DELEG_NOTE_DIFF, diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c index 088ee35c4f6b..cbdeca3eaaad 100644 --- a/lib/libzfs/libzfs_dataset.c +++ b/lib/libzfs/libzfs_dataset.c @@ -946,7 +946,9 @@ 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_USERDNQUOTA && + uqtype != ZFS_PROP_GROUPDNQUOTA) { zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "'%s' is readonly"), propname); @@ -2649,8 +2651,12 @@ userquota_propname_decode(const char *propname, boolean_t zoned, return (EINVAL); *typep = type; - isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED); - isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED); + isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED || + type == ZFS_PROP_USERDNQUOTA || + type == ZFS_PROP_USERDNUSED); + isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED || + type == ZFS_PROP_GROUPDNQUOTA || + type == ZFS_PROP_GROUPDNUSED); cp = strchr(propname, '@') + 1; @@ -2783,7 +2789,8 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname, (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)propvalue); } else if (propvalue == 0 && - (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) { + (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA || + type == ZFS_PROP_USERDNQUOTA || type == ZFS_PROP_GROUPDNQUOTA)) { (void) strlcpy(propbuf, "none", proplen); } else { zfs_nicenum(propvalue, propbuf, proplen); diff --git a/module/zcommon/zfs_deleg.c b/module/zcommon/zfs_deleg.c index f6e41da9d7ea..7b8ae3baa306 100644 --- a/module/zcommon/zfs_deleg.c +++ b/module/zcommon/zfs_deleg.c @@ -62,6 +62,10 @@ zfs_deleg_perm_tab_t zfs_deleg_perm_tab[] = { {ZFS_DELEG_PERM_GROUPQUOTA}, {ZFS_DELEG_PERM_USERUSED}, {ZFS_DELEG_PERM_GROUPUSED}, + {ZFS_DELEG_PERM_USERDNQUOTA}, + {ZFS_DELEG_PERM_GROUPDNQUOTA}, + {ZFS_DELEG_PERM_USERDNUSED}, + {ZFS_DELEG_PERM_GROUPDNUSED}, {ZFS_DELEG_PERM_HOLD}, {ZFS_DELEG_PERM_RELEASE}, {NULL} diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index aaebab444cfa..f89a05de5160 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -51,7 +51,11 @@ const char *zfs_userquota_prop_prefixes[] = { "userused@", "userquota@", "groupused@", - "groupquota@" + "groupquota@", + "userdnused@", + "userdnquota@", + "groupdnused@", + "groupdnquota@" }; zprop_desc_t * @@ -165,6 +169,7 @@ zfs_prop_init(void) { "3", 3 }, { "4", 4 }, { "5", 5 }, + { "6", 6 }, { "current", ZPL_VERSION }, { NULL } }; @@ -301,7 +306,7 @@ zfs_prop_init(void) /* default index properties */ zprop_register_index(ZFS_PROP_VERSION, "version", 0, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, - "1 | 2 | 3 | 4 | 5 | current", "VERSION", version_table); + "1 | 2 | 3 | 4 | 5 | 6 | current", "VERSION", version_table); zprop_register_index(ZFS_PROP_CANMOUNT, "canmount", ZFS_CANMOUNT_ON, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "on | off | noauto", "CANMOUNT", canmount_table); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index 779b3bb789aa..78568d36c047 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -30,6 +30,7 @@ /* Portions Copyright 2010 Robert Milkowski */ +#include #include #include #include @@ -827,6 +828,9 @@ dmu_objset_create_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp, os->os_phys->os_type = type; if (dmu_objset_userused_enabled(os)) { os->os_phys->os_flags |= OBJSET_FLAG_USERACCOUNTING_COMPLETE; + if (dmu_objset_userdnused_enabled(os)) + os->os_phys->os_flags |= + OBJSET_FLAG_USERDNACCOUNTING_COMPLETE; os->os_flags = os->os_phys->os_flags; } @@ -1210,6 +1214,13 @@ dmu_objset_userused_enabled(objset_t *os) DMU_USERUSED_DNODE(os) != NULL); } +boolean_t +dmu_objset_userdnused_enabled(objset_t *os) +{ + return dmu_objset_userused_enabled(os) && + spa_feature_is_enabled(os->os_spa, SPA_FEATURE_USERDN_ACCOUNTING); +} + static void do_userquota_update(objset_t *os, uint64_t used, uint64_t flags, uint64_t user, uint64_t group, boolean_t subtract, dmu_tx_t *tx) @@ -1225,6 +1236,30 @@ do_userquota_update(objset_t *os, uint64_t used, uint64_t flags, } } +static void +do_userdnquota_update(objset_t *os, uint64_t flags, uint64_t user, + uint64_t group, boolean_t subtract, dmu_tx_t *tx) +{ + if (flags & DNODE_FLAG_USERDNUSED_ACCOUNTED) { + char name[23]; + + if (!spa_feature_is_active(os->os_spa, + SPA_FEATURE_USERDN_ACCOUNTING)) + spa_feature_incr(os->os_spa, + SPA_FEATURE_USERDN_ACCOUNTING, tx); + + (void) snprintf(name, sizeof (name), "dn-%llx", + (longlong_t)user); + VERIFY3U(0, ==, zap_increment(os, DMU_USERUSED_OBJECT, name, + subtract ? -1 : 1, tx)); + + (void) snprintf(name, sizeof (name), "dn-%llx", + (longlong_t)group); + VERIFY3U(0, ==, zap_increment(os, DMU_GROUPUSED_OBJECT, name, + subtract ? -1 : 1, tx)); + } +} + void dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx) { @@ -1263,11 +1298,15 @@ dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx) if (flags & DN_ID_OLD_EXIST) { do_userquota_update(os, dn->dn_oldused, dn->dn_oldflags, dn->dn_olduid, dn->dn_oldgid, B_TRUE, tx); + do_userdnquota_update(os, dn->dn_oldflags, + dn->dn_olduid, dn->dn_oldgid, B_TRUE, tx); } if (flags & DN_ID_NEW_EXIST) { do_userquota_update(os, DN_USED_BYTES(dn->dn_phys), dn->dn_phys->dn_flags, dn->dn_newuid, dn->dn_newgid, B_FALSE, tx); + do_userdnquota_update(os, dn->dn_phys->dn_flags, + dn->dn_newuid, dn->dn_newgid, B_FALSE, tx); } mutex_enter(&dn->dn_mtx); @@ -1439,19 +1478,19 @@ dmu_objset_userspace_present(objset_t *os) OBJSET_FLAG_USERACCOUNTING_COMPLETE); } -int -dmu_objset_userspace_upgrade(objset_t *os) +boolean_t +dmu_objset_userdnspace_present(objset_t *os) +{ + return (os->os_phys->os_flags & + OBJSET_FLAG_USERDNACCOUNTING_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 @@ -1482,12 +1521,51 @@ 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_userused_enabled(os)) + return (SET_ERROR(ENOTSUP)); + if (dmu_objset_is_snapshot(os)) + return (SET_ERROR(EINVAL)); + + 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); } +int +dmu_objset_userdnspace_upgrade(objset_t *os) +{ + int err = 0; + + if (dmu_objset_userdnspace_present(os)) + return (0); + if (dmu_objset_is_snapshot(os)) + return (0); + if (!dmu_objset_userdnused_enabled(os)) + return (SET_ERROR(ENOTSUP)); + + err = dmu_objset_space_upgrade(os); + if (err) + return (err); + + os->os_flags |= OBJSET_FLAG_USERDNACCOUNTING_COMPLETE; + txg_wait_synced(dmu_objset_pool(os), 0); + return (0); +} + void dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp, uint64_t *usedobjsp, uint64_t *availobjsp) @@ -2045,4 +2123,7 @@ EXPORT_SYMBOL(dmu_objset_userquota_get_ids); EXPORT_SYMBOL(dmu_objset_userused_enabled); EXPORT_SYMBOL(dmu_objset_userspace_upgrade); EXPORT_SYMBOL(dmu_objset_userspace_present); +EXPORT_SYMBOL(dmu_objset_userdnused_enabled); +EXPORT_SYMBOL(dmu_objset_userdnspace_upgrade); +EXPORT_SYMBOL(dmu_objset_userdnspace_present); #endif diff --git a/module/zfs/dnode_sync.c b/module/zfs/dnode_sync.c index df5c8e4ee6c4..27a67debd31e 100644 --- a/module/zfs/dnode_sync.c +++ b/module/zfs/dnode_sync.c @@ -576,12 +576,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_userdnused_enabled(dn->dn_objset)) + dn->dn_phys->dn_flags |= + DNODE_FLAG_USERDNUSED_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_USERDNUSED_ACCOUNTED)); } mutex_enter(&dn->dn_mtx); diff --git a/module/zfs/zfeature_common.c b/module/zfs/zfeature_common.c index 609a72ab301a..7523a1fdae0b 100644 --- a/module/zfs/zfeature_common.c +++ b/module/zfs/zfeature_common.c @@ -242,4 +242,7 @@ zpool_feature_init(void) "Support for blocks larger than 128KB.", B_FALSE, B_FALSE, B_FALSE, large_blocks_deps); } + zfeature_register(SPA_FEATURE_USERDN_ACCOUNTING, + "org.zfsonlinux:user_dnode_accounting", "userdn_accounting", + "User/Group dnode accounting.", B_TRUE, B_FALSE, B_FALSE, NULL); } diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 7ce19693e2d4..144a25c40cd9 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -235,9 +235,14 @@ static const char *userquota_perms[] = { ZFS_DELEG_PERM_USERQUOTA, ZFS_DELEG_PERM_GROUPUSED, ZFS_DELEG_PERM_GROUPQUOTA, + ZFS_DELEG_PERM_USERDNUSED, + ZFS_DELEG_PERM_USERDNQUOTA, + ZFS_DELEG_PERM_GROUPDNUSED, + ZFS_DELEG_PERM_GROUPDNQUOTA, }; static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc); +static int zfs_ioc_userdnspace_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, @@ -1155,7 +1160,9 @@ 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_USERDNUSED || + zc->zc_objset_type == ZFS_PROP_USERDNQUOTA) { if (zc->zc_guid == crgetuid(cr)) return (0); } else { @@ -3680,13 +3687,23 @@ 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_USERDNQUOTA]; + const char *giq_prefix = + zfs_userquota_prop_prefixes[ZFS_PROP_GROUPDNQUOTA]; 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_USERDNQUOTA; } 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_GROUPDNQUOTA; } else { /* USERUSED and GROUPUSED are read-only */ return (SET_ERROR(EINVAL)); @@ -4631,6 +4648,29 @@ zfs_ioc_userspace_upgrade(zfs_cmd_t *zc) return (error); } +/* + * inputs: + * zc_name name of filesystem + * + * outputs: + * none + */ +static int +zfs_ioc_userdnspace_upgrade(zfs_cmd_t *zc) +{ + int err; + zfs_sb_t *zsb; + + err = zfs_sb_hold(zc->zc_name, FTAG, &zsb, B_FALSE); + if (err != 0) + return (err); + + err = dmu_objset_userdnspace_upgrade(zsb->z_os); + zfs_sb_rele(zsb, FTAG); + + return (err); +} + static int zfs_ioc_share(zfs_cmd_t *zc) { @@ -5560,6 +5600,9 @@ zfs_ioctl_init(void) zfs_ioctl_register_dataset_nolog(ZFS_IOC_USERSPACE_UPGRADE, zfs_ioc_userspace_upgrade, zfs_secpolicy_userspace_upgrade, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); + zfs_ioctl_register_dataset_nolog(ZFS_IOC_USERDNSPACE_UPGRADE, + zfs_ioc_userdnspace_upgrade, zfs_secpolicy_userspace_upgrade, + POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); zfs_ioctl_register_dataset_nolog(ZFS_IOC_TMP_SNAPSHOT, zfs_ioc_tmp_snapshot, zfs_secpolicy_tmp_snapshot, POOL_CHECK_SUSPENDED | POOL_CHECK_READONLY); diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index f105d9aeda12..f7d24fb4057d 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -462,13 +462,19 @@ zfs_userquota_prop_to_obj(zfs_sb_t *zsb, zfs_userquota_prop_t type) { switch (type) { case ZFS_PROP_USERUSED: + case ZFS_PROP_USERDNUSED: return (DMU_USERUSED_OBJECT); + case ZFS_PROP_GROUPDNUSED: case ZFS_PROP_GROUPUSED: 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_USERDNQUOTA: + return (zsb->z_userdnquota_obj); + case ZFS_PROP_GROUPDNQUOTA: + return (zsb->z_groupdnquota_obj); default: return (SET_ERROR(ENOTSUP)); } @@ -484,16 +490,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_USERDNUSED || type == ZFS_PROP_USERDNUSED || + type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA) && + !dmu_objset_userdnspace_present(zsb->z_os)) + return (SET_ERROR(ENOTSUP)); + obj = zfs_userquota_prop_to_obj(zsb, type); if (obj == 0) { *bufsizep = 0; return (0); } + if (type == ZFS_PROP_USERDNUSED || type == ZFS_PROP_USERDNUSED) + offset = strlen("dn-"); + for (zap_cursor_init_serialized(&zc, zsb->z_os, obj, *cookiep); (error = zap_cursor_retrieve(&zc, &za)) == 0; zap_cursor_advance(&zc)) { @@ -501,7 +516,10 @@ zfs_userspace_many(zfs_sb_t *zsb, zfs_userquota_prop_t type, *bufsizep) break; - fuidstr_to_sid(zsb, za.za_name, + if (za.za_name[0] == 'd' && offset == 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; @@ -542,7 +560,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[35] = "dn-"; + int offset = 0; int err; uint64_t obj; @@ -551,11 +570,19 @@ 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_USERDNUSED || type == ZFS_PROP_USERDNUSED || + type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA) && + !dmu_objset_userdnspace_present(zsb->z_os)) + return (SET_ERROR(ENOTSUP)); + obj = zfs_userquota_prop_to_obj(zsb, type); if (obj == 0) return (0); - err = id_to_fuidstr(zsb, domain, rid, buf, B_FALSE); + if (type == ZFS_PROP_USERDNUSED || type == ZFS_PROP_USERDNUSED) + offset = strlen("dn-"); + + err = id_to_fuidstr(zsb, domain, rid, buf + offset, B_FALSE); if (err) return (err); @@ -582,8 +609,14 @@ zfs_set_userquota(zfs_sb_t *zsb, zfs_userquota_prop_t type, if (zsb->z_version < ZPL_VERSION_USERSPACE) return (SET_ERROR(ENOTSUP)); - objp = (type == ZFS_PROP_USERQUOTA) ? &zsb->z_userquota_obj : - &zsb->z_groupquota_obj; + if (type == ZFS_PROP_USERQUOTA) + objp = &zsb->z_userquota_obj; + else if (type == ZFS_PROP_USERDNQUOTA) + objp = &zsb->z_userdnquota_obj; + else if (type == ZFS_PROP_GROUPQUOTA) + objp = &zsb->z_groupquota_obj; + else + objp = &zsb->z_groupdnquota_obj; err = id_to_fuidstr(zsb, domain, rid, buf, B_TRUE); if (err)