Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement defaultuserquota/defaultgroupquota #16283

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/os/freebsd/zfs/sys/zfs_vfsops_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ struct zfsvfs {
uint64_t z_groupobjquota_obj;
uint64_t z_projectquota_obj;
uint64_t z_projectobjquota_obj;
uint64_t z_defaultuserquota_obj;
uint64_t z_defaultgroupquota_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
Expand Down
2 changes: 2 additions & 0 deletions include/os/linux/zfs/sys/zfs_vfsops_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ struct zfsvfs {
uint64_t z_groupobjquota_obj;
uint64_t z_projectquota_obj;
uint64_t z_projectobjquota_obj;
uint64_t z_defaultuserquota_obj;
uint64_t z_defaultgroupquota_obj;
uint64_t z_replay_eof; /* New end of file - replay only */
sa_attr_type_t *z_attr_table; /* SA attr mapping->id */
uint64_t z_hold_size; /* znode hold array size */
Expand Down
2 changes: 2 additions & 0 deletions include/sys/fs/zfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ typedef enum {
ZFS_PROP_VOLTHREADING,
ZFS_PROP_DIRECT,
ZFS_PROP_LONGNAME,
ZFS_PROP_DEFAULTUSERQUOTA,
ZFS_PROP_DEFAULTGROUPQUOTA,
ZFS_NUM_PROPS
} zfs_prop_t;

Expand Down
1 change: 1 addition & 0 deletions include/sys/zfs_quota.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ extern int zfs_userspace_many(struct zfsvfs *, zfs_userquota_prop_t,
uint64_t *, void *, uint64_t *);
extern int zfs_set_userquota(struct zfsvfs *, zfs_userquota_prop_t,
const char *, uint64_t, uint64_t);
extern int zfs_set_defaultquota(struct zfsvfs *, int, uint64_t);

extern boolean_t zfs_id_overobjquota(struct zfsvfs *, uint64_t, uint64_t);
extern boolean_t zfs_id_overblockquota(struct zfsvfs *, uint64_t, uint64_t);
Expand Down
4 changes: 3 additions & 1 deletion lib/libzfs/libzfs.abi
Original file line number Diff line number Diff line change
Expand Up @@ -2049,7 +2049,9 @@
<enumerator name='ZFS_PROP_VOLTHREADING' value='97'/>
<enumerator name='ZFS_PROP_DIRECT' value='98'/>
<enumerator name='ZFS_PROP_LONGNAME' value='99'/>
<enumerator name='ZFS_NUM_PROPS' value='100'/>
<enumerator name='ZFS_PROP_DEFAULTUSERQUOTA' value='100'/>
<enumerator name='ZFS_PROP_DEFAULTGROUPQUOTA' value='101'/>
<enumerator name='ZFS_NUM_PROPS' value='102'/>
</enum-decl>
<typedef-decl name='zfs_prop_t' type-id='4b000d60' id='58603c44'/>
<enum-decl name='zprop_source_t' naming-typedef-id='a2256d42' id='5903f80e'>
Expand Down
2 changes: 2 additions & 0 deletions lib/libzfs/libzfs_dataset.c
Original file line number Diff line number Diff line change
Expand Up @@ -2811,6 +2811,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
case ZFS_PROP_REFQUOTA:
case ZFS_PROP_RESERVATION:
case ZFS_PROP_REFRESERVATION:
case ZFS_PROP_DEFAULTUSERQUOTA:
case ZFS_PROP_DEFAULTGROUPQUOTA:

if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
return (-1);
Expand Down
6 changes: 6 additions & 0 deletions man/man7/zfsprops.7
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,9 @@ 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 defaultuserquota Ns = Ns Ar size Ns | Ns Sy none
Sets a default quota to be applied to each user for whom no user-specific
quota is set.
.It Sy groupquota@ Ns Ar group Ns = Ns Ar size Ns | Ns Sy none
Limits the amount of space consumed by the specified group.
Group space consumption is identified by the
Expand All @@ -1465,6 +1468,9 @@ but it limits number of objects a group can consume.
Please refer to
.Sy userobjused
for more information about how objects are counted.
.It Sy defaultgroupquota Ns = Ns Ar size Ns | Ns Sy none
Sets a default quota to be applied to each group for whom no group-specific
quota is set.
.It Sy projectquota@ Ns Ar project Ns = Ns Ar size Ns | Ns Sy none
Limits the amount of space consumed by the specified project.
Project space consumption is identified by the
Expand Down
16 changes: 16 additions & 0 deletions module/os/freebsd/zfs/zfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,22 @@ zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os)
else if (error != 0)
return (error);

error = zap_lookup(os, MASTER_NODE_OBJ,
zfs_prop_to_name(ZFS_PROP_DEFAULTUSERQUOTA),
8, 1, &zfsvfs->z_defaultuserquota_obj);
if (error == ENOENT)
zfsvfs->z_defaultuserquota_obj = 0;
else if (error != 0)
return (error);

error = zap_lookup(os, MASTER_NODE_OBJ,
zfs_prop_to_name(ZFS_PROP_DEFAULTGROUPQUOTA),
8, 1, &zfsvfs->z_defaultgroupquota_obj);
if (error == ENOENT)
zfsvfs->z_defaultgroupquota_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)
Expand Down
16 changes: 16 additions & 0 deletions module/os/linux/zfs/zfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,22 @@ zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os)
else if (error != 0)
return (error);

error = zap_lookup(os, MASTER_NODE_OBJ,
zfs_prop_to_name(ZFS_PROP_DEFAULTUSERQUOTA),
8, 1, &zfsvfs->z_defaultuserquota_obj);
if (error == ENOENT)
zfsvfs->z_defaultuserquota_obj = 0;
else if (error != 0)
return (error);

error = zap_lookup(os, MASTER_NODE_OBJ,
zfs_prop_to_name(ZFS_PROP_DEFAULTGROUPQUOTA),
8, 1, &zfsvfs->z_defaultgroupquota_obj);
if (error == ENOENT)
zfsvfs->z_defaultgroupquota_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)
Expand Down
6 changes: 6 additions & 0 deletions module/zcommon/zfs_prop.c
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,12 @@ zfs_prop_init(void)
zprop_register_number(ZFS_PROP_SNAPSHOT_LIMIT, "snapshot_limit",
UINT64_MAX, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
"<count> | none", "SSLIMIT", B_FALSE, sfeatures);
zprop_register_number(ZFS_PROP_DEFAULTUSERQUOTA, "defaultuserquota", 0,
PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "<size> | none",
"DEFAULTUSERQUOTA", B_FALSE, sfeatures);
zprop_register_number(ZFS_PROP_DEFAULTGROUPQUOTA, "defaultgroupquota",
0, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "<size> | none",
"DEFAULTGROUPQUOTA", B_FALSE, sfeatures);

/* inherit number properties */
zprop_register_number(ZFS_PROP_RECORDSIZE, "recordsize",
Expand Down
19 changes: 19 additions & 0 deletions module/zfs/zfs_ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -2629,6 +2629,25 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source,
zfsvfs_rele(zfsvfs, FTAG);
break;
}
case ZFS_PROP_DEFAULTUSERQUOTA:
case ZFS_PROP_DEFAULTGROUPQUOTA:
{
zfsvfs_t *zfsvfs;

if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0)
break;

err = zfs_set_defaultquota(zfsvfs, prop, intval);
zfsvfs_rele(zfsvfs, FTAG);

/*
* Set err to -1 to force the zfs_set_prop_nvlist code down the
* default path to set the value in the nvlist.
*/
if (err == 0)
err = -1;
break;
}
default:
err = -1;
}
Expand Down
89 changes: 84 additions & 5 deletions module/zfs/zfs_quota.c
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,59 @@ zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
return (err);
}

int
zfs_set_defaultquota(zfsvfs_t *zfsvfs, int type, uint64_t quota)
{
int err;
dmu_tx_t *tx;
uint64_t *objp;
const char *name;

if (type == ZFS_PROP_DEFAULTUSERQUOTA) {
objp = &zfsvfs->z_defaultuserquota_obj;
name = zfs_prop_to_name(ZFS_PROP_DEFAULTUSERQUOTA);
} else if (type == ZFS_PROP_DEFAULTGROUPQUOTA) {
objp = &zfsvfs->z_defaultgroupquota_obj;
name = zfs_prop_to_name(ZFS_PROP_DEFAULTGROUPQUOTA);
} else {
/* defaultprojectquota NYI (does it make sense to do so?) */
return (SET_ERROR(EINVAL));
}

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, name);
}
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, name,
8, 1, objp, tx));
}
mutex_exit(&zfsvfs->z_lock);

if (quota == 0) {
err = zap_remove(zfsvfs->z_os, *objp, name, tx);
if (err == ENOENT)
err = 0;
} else {
err = zap_update(zfsvfs->z_os, *objp, name,
8, 1, &quota, tx);
}

ASSERT(err == 0);
dmu_tx_commit(tx);
return (err);
}

boolean_t
zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
{
Expand Down Expand Up @@ -425,8 +478,9 @@ boolean_t
zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
{
char buf[20];
uint64_t used, quota, quotaobj;
int err;
uint64_t used, quota, quotaobj, defquota, defquotaobj;
int err, uerr, derr;
const char *name;

if (usedobj == DMU_PROJECTUSED_OBJECT) {
if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
Expand All @@ -442,22 +496,46 @@ zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
quotaobj = zfsvfs->z_projectquota_obj;
} else if (usedobj == DMU_USERUSED_OBJECT) {
quotaobj = zfsvfs->z_userquota_obj;
defquotaobj = zfsvfs->z_defaultuserquota_obj;
name = zfs_prop_to_name(ZFS_PROP_DEFAULTUSERQUOTA);
} else if (usedobj == DMU_GROUPUSED_OBJECT) {
quotaobj = zfsvfs->z_groupquota_obj;
defquotaobj = zfsvfs->z_defaultgroupquota_obj;
name = zfs_prop_to_name(ZFS_PROP_DEFAULTGROUPQUOTA);
} else {
return (B_FALSE);
}
if (quotaobj == 0 || zfsvfs->z_replay)

/* no quota assigned */
if (quotaobj == 0 && defquotaobj == 0) {
return (B_FALSE);
}
if (zfsvfs->z_replay) {
return (B_FALSE);
}

(void) snprintf(buf, sizeof (buf), "%llx", (longlong_t)id);
err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
if (err != 0)
derr = zap_lookup(zfsvfs->z_os, defquotaobj, name,
8, 1, &defquota);
uerr = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);

/* bail if both id-specific && default lookups failed */
if (uerr != 0 && derr != 0)
return (B_FALSE);

err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
if (err != 0)
return (B_FALSE);

/*
* if a user/group has a specific quota assigned, use that.
* if neither quota...we've already returned false (hopefully).
* if a default quota is set, but no user quota is,
* use the default.
*/
if (uerr != 0 && derr == 0 && defquota)
quota = defquota;

return (used >= quota);
}

Expand All @@ -472,6 +550,7 @@ EXPORT_SYMBOL(zpl_get_file_info);
EXPORT_SYMBOL(zfs_userspace_one);
EXPORT_SYMBOL(zfs_userspace_many);
EXPORT_SYMBOL(zfs_set_userquota);
EXPORT_SYMBOL(zfs_set_defaultquota);
EXPORT_SYMBOL(zfs_id_overblockquota);
EXPORT_SYMBOL(zfs_id_overobjquota);
EXPORT_SYMBOL(zfs_id_overquota);
5 changes: 5 additions & 0 deletions tests/runfiles/common.run
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,11 @@ tags = ['functional', 'upgrade']

[tests/functional/userquota]
tests = [
'defaultuserquota_001_pos', 'defaultuserquota_002_pos',
'defaultuserquota_003_pos', 'defaultuserquota_004_neg',
'defaultuserquota_005_pos', 'defaultuserquota_006_pos',
'defaultuserquota_007_pos', 'defaultuserquota_008_pos',
'defaultuserquota_009_pos', 'defaultuserquota_010_neg',
'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',
Expand Down
10 changes: 10 additions & 0 deletions tests/zfs-tests/tests/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,16 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
functional/userquota/groupspace_002_pos.ksh \
functional/userquota/groupspace_003_pos.ksh \
functional/userquota/setup.ksh \
functional/userquota/defaultuserquota_001_pos.ksh \
functional/userquota/defaultuserquota_002_pos.ksh \
functional/userquota/defaultuserquota_003_pos.ksh \
functional/userquota/defaultuserquota_004_neg.ksh \
functional/userquota/defaultuserquota_005_pos.ksh \
functional/userquota/defaultuserquota_006_pos.ksh \
functional/userquota/defaultuserquota_007_pos.ksh \
functional/userquota/defaultuserquota_008_pos.ksh \
functional/userquota/defaultuserquota_009_pos.ksh \
functional/userquota/defaultuserquota_010_neg.ksh \
functional/userquota/userquota_001_pos.ksh \
functional/userquota/userquota_002_pos.ksh \
functional/userquota/userquota_003_pos.ksh \
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/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 https://opensource.org/licenses/CDDL-1.0.
# 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, 2016 by Delphix. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib

#
#
# DESCRIPTION:
# Check the basic function of defaultuserquota and defaultgroupquota
#
#
# STRATEGY:
# 1. Set defaultuserquota and exceed the quota size
# 2. The write operation should fail with "Disk quota exceeded"
# 3. Set defaultgroupquota and exceed the quota size
# 4. The write operation should fail with "Disk quota exceeded"
#
#

function cleanup
{
cleanup_quota
}

log_onexit cleanup

log_assert "If write operation exceeds default{user|group}quota size, it will fail"

mkmount_writable $QFS
log_note "Check the defaultuserquota"
log_must zfs set defaultuserquota=$UQUOTA_SIZE $QFS
log_must user_run $QUSER1 mkfile $UQUOTA_SIZE $QFILE
sync_pool
log_mustnot user_run $QUSER1 mkfile 1 $OFILE
cleanup_quota

log_note "Check the defaultgroupquota"
log_must zfs set defaultgroupquota=$GQUOTA_SIZE $QFS
mkmount_writable $QFS
log_must user_run $QUSER1 mkfile $GQUOTA_SIZE $QFILE
sync_pool
log_mustnot user_run $QUSER1 mkfile 1 $OFILE
log_mustnot user_run $QUSER2 mkfile 1 $OFILE
cleanup_quota

log_pass "Write operation exceeded default{user|group}quota size, failed as expected"
Loading
Loading