Add support for user/group dnode accounting & quota

This patch tracks dnode usage for each user/group in the
DMU_USER/GROUPUSED_OBJECT ZAPs. ZAP entries dedicated to dnode
accounting have the key prefixed with "obj-" followed by the UID/GID
in string format (as done for the block accounting).
A new SPA feature has been added for dnode accounting as well as
a new ZPL version. The SPA feature must be enabled in the pool
before upgrading the zfs filesystem. During the zfs version upgrade,
a "quotacheck" will be executed by marking all dnode as dirty.

ZoL-bug-id: https://github.com/zfsonlinux/zfs/issues/3500

Signed-off-by: Jinshan Xiong <jinshan.xiong@intel.com>
Signed-off-by: Johann Lombardi <johann.lombardi@intel.com>
This commit is contained in:
Jinshan Xiong 2016-10-04 11:46:10 -07:00 committed by Brian Behlendorf
parent af322debaa
commit 1de321e626
43 changed files with 1119 additions and 98 deletions

View File

@ -1946,11 +1946,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",

View File

@ -2223,10 +2223,14 @@ 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)
@ -2374,6 +2378,20 @@ compare_nums:
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 inline const char *
us_type2str(unsigned field_type)
{
@ -2463,7 +2481,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;
@ -2538,10 +2556,22 @@ userspace_cb(void *arg, const char *domain, uid_t rid, uint64_t space)
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) {
propname = "quota";
if (!nvlist_exists(props, "used"))
(void) nvlist_add_uint64(props, "used", 0);
} else if (prop == ZFS_PROP_USEROBJUSED ||
prop == ZFS_PROP_GROUPOBJUSED) {
propname = "objused";
if (!nvlist_exists(props, "objquota"))
(void) nvlist_add_uint64(props, "objquota", 0);
} else if (prop == ZFS_PROP_USEROBJQUOTA ||
prop == ZFS_PROP_GROUPOBJQUOTA) {
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])
@ -2574,7 +2604,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),
@ -2582,7 +2612,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);
@ -2593,12 +2623,15 @@ 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:
if (type == DATA_TYPE_UINT32)
strval = (char *)us_type2str(val32);
break;
case USFIELD_NAME:
@ -2610,6 +2643,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",
@ -2618,7 +2653,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
@ -2690,7 +2726,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;
@ -2839,11 +2875,12 @@ 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) &&
(zfs_prop_is_group(p) &&
!(types & (USTYPE_PSX_GRP | USTYPE_SMB_GRP))))
continue;
cb.cb_prop = p;
if ((ret = zfs_userspace(zhp, p, userspace_cb, &cb)) != 0)
return (ret);
@ -4099,6 +4136,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"
@ -4129,6 +4171,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_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 },
{ NULL, ZFS_DELEG_NOTE_NONE }
};
@ -4206,6 +4252,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_USEROBJQUOTA:
case ZFS_DELEG_NOTE_USEROBJUSED:
case ZFS_DELEG_NOTE_GROUPOBJQUOTA:
case ZFS_DELEG_NOTE_GROUPOBJUSED:
/* other */
return (gettext("other"));
default:
@ -4709,6 +4759,19 @@ 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;
/* other */
default:
str = "";

View File

@ -274,6 +274,7 @@ AC_CONFIG_FILES([
tests/zfs-tests/tests/functional/threadsappend/Makefile
tests/zfs-tests/tests/functional/truncate/Makefile
tests/zfs-tests/tests/functional/userquota/Makefile
tests/zfs-tests/tests/functional/upgrade/Makefile
tests/zfs-tests/tests/functional/vdev_zaps/Makefile
tests/zfs-tests/tests/functional/write_dirs/Makefile
tests/zfs-tests/tests/functional/xattr/Makefile

View File

@ -256,6 +256,12 @@ void zfs_znode_byteswap(void *buf, size_t size);
#define DMU_USERUSED_OBJECT (-1ULL)
#define DMU_GROUPUSED_OBJECT (-2ULL)
/*
* Zap prefix for object accounting in DMU_{USER,GROUP}USED_OBJECT.
*/
#define DMU_OBJACCT_PREFIX "obj-"
#define DMU_OBJACCT_PREFIX_LEN 4
/*
* artificial blkids for bonus buffer and spill blocks
*/

View File

@ -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_USEROBJACCOUNTING_COMPLETE (1ULL<<1)
typedef struct objset_phys {
dnode_phys_t os_meta_dnode;
@ -68,6 +69,8 @@ typedef struct objset_phys {
dnode_phys_t os_groupused_dnode;
} objset_phys_t;
typedef int (*dmu_objset_upgrade_cb_t)(objset_t *);
struct objset {
/* Immutable: */
struct dsl_dataset *os_dsl_dataset;
@ -125,6 +128,13 @@ struct objset {
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
@ -173,6 +183,17 @@ 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_userobjused_enabled(objset_t *os);
void dmu_objset_userobjspace_upgrade(objset_t *os);
boolean_t dmu_objset_userobjspace_present(objset_t *os);
static inline boolean_t dmu_objset_userobjspace_upgradable(objset_t *os)
{
return (dmu_objset_type(os) == DMU_OST_ZFS &&
dmu_objset_userobjused_enabled(os) &&
!dmu_objset_userobjspace_present(os));
}
int dmu_fsname(const char *snapname, char *buf);
void dmu_objset_evict_done(objset_t *os);

View File

@ -132,6 +132,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_USEROBJUSED_ACCOUNTED (1 << 3)
typedef struct dnode_phys {
uint8_t dn_type; /* dmu_object_type_t */
uint8_t dn_indblkshift; /* ln2(indirect block size) */

View File

@ -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"

View File

@ -171,6 +171,10 @@ typedef enum {
ZFS_PROP_USERQUOTA,
ZFS_PROP_GROUPUSED,
ZFS_PROP_GROUPQUOTA,
ZFS_PROP_USEROBJUSED,
ZFS_PROP_USEROBJQUOTA,
ZFS_PROP_GROUPOBJUSED,
ZFS_PROP_GROUPOBJQUOTA,
ZFS_NUM_USERQUOTA_PROPS
} zfs_userquota_prop_t;

View File

@ -277,6 +277,8 @@ struct spa {
*/
spa_config_lock_t spa_config_lock[SCL_LOCKS]; /* config changes */
refcount_t spa_refcount; /* number of opens */
taskq_t *spa_upgrade_taskq; /* taskq for upgrade jobs */
};
extern char *spa_config_path;

View File

@ -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_userobjquota_obj;
uint64_t z_groupobjquota_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 */
@ -190,6 +192,8 @@ extern boolean_t zfs_owner_overquota(zfs_sb_t *zsb, struct znode *,
boolean_t isgroup);
extern boolean_t zfs_fuid_overquota(zfs_sb_t *zsb, boolean_t isgroup,
uint64_t fuid);
extern boolean_t zfs_fuid_overobjquota(zfs_sb_t *zsb, boolean_t isgroup,
uint64_t fuid);
extern int zfs_set_version(zfs_sb_t *zsb, uint64_t newvers);
extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop,
uint64_t *value);

View File

@ -54,6 +54,7 @@ typedef enum spa_feature {
SPA_FEATURE_SHA512,
SPA_FEATURE_SKEIN,
SPA_FEATURE_EDONR,
SPA_FEATURE_USEROBJ_ACCOUNTING,
SPA_FEATURES
} spa_feature_t;

View File

@ -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,

View File

@ -944,7 +944,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_USEROBJQUOTA &&
uqtype != ZFS_PROP_GROUPOBJQUOTA) {
zfs_error_aux(hdl,
dgettext(TEXT_DOMAIN, "'%s' is readonly"),
propname);
@ -2741,8 +2743,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_USEROBJQUOTA ||
type == ZFS_PROP_USEROBJUSED);
isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED ||
type == ZFS_PROP_GROUPOBJQUOTA ||
type == ZFS_PROP_GROUPOBJUSED);
cp = strchr(propname, '@') + 1;
@ -2875,7 +2881,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_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA)) {
(void) strlcpy(propbuf, "none", proplen);
} else {
zfs_nicenum(propvalue, propbuf, proplen);
@ -4333,6 +4340,13 @@ 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)))
break;
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN,
"cannot get used/quota for %s"), zc.zc_name);

View File

@ -563,5 +563,31 @@ Booting off of pools using \fBedonr\fR is \fBNOT\fR supported
-- any attempt to enable \fBedonr\fR on a root pool will fail with an
error.
.sp
.ne 2
.na
\fB\fBuserobj_accounting\fR\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
.SH "SEE ALSO"
\fBzpool\fR(8)

View File

@ -663,6 +663,8 @@ The amount of space consumed by snapshots of this dataset. In particular, it is
.sp
.ne 2
.na
\fB\fBuserobjused@\fR\fIuser\fR\fR
.br
\fB\fBuserused@\fR\fIuser\fR\fR
.ad
.sp .6
@ -699,6 +701,11 @@ The \fBuserused@\fR... properties are not displayed by \fBzfs get all\fR. The us
.RE
Files created on Linux always have POSIX owners.
.RS 4n
The \fBuserobjused\fR is similar to \fBuserused\fR but instead it counts the number of objects consumed by \fIuser\fR. This feature doesn't count the internal objects used by ZFS, therefore it may under count a few objects comparing with the results of third-party tool such as \fBdfs -i\fR.
When the property \fBxattr=on\fR is set on a fileset, ZFS will create additional objects per-file to store extended attributes. These additional objects are reflected in the \fBuserobjused\fR value and are counted against the user's \fBuserobjquota\fR. When a filesystem is configured to use \fBxattr=sa\fR no additional internal objects are required.
.RE
.sp
.ne 2
.na
@ -713,6 +720,8 @@ This property is set to the number of user holds on this snapshot. User holds ar
.ne 2
.na
\fB\fBgroupused@\fR\fIgroup\fR\fR
.br
\fB\fBgroupobjused@\fR\fIgroup\fR\fR
.ad
.sp .6
.RS 4n
@ -721,6 +730,11 @@ The amount of space consumed by the specified group in this dataset. Space is ch
Unprivileged users can only access their own groups' space usage. The root user, or a user who has been granted the \fBgroupused\fR privilege with \fBzfs allow\fR, can access all groups' usage.
.RE
.RS 4n
The \fBgroupobjused\fR is similar to \fBgroupused\fR but instead it counts the number of objects consumed by \fIgroup\fR.
When the property \fBxattr=on\fR is set on a fileset, ZFS will create additional objects per-file to store extended attributes. These additional objects are reflected in the \fBgroupobjused\fR value and are counted against the group's \fBgroupobjquota.\fR. When a filesystem is configured to use \fBxattr=sa\fR no additional internal objects are required.
.RE
.sp
.ne 2
.na
@ -1081,6 +1095,8 @@ a zone. This feature must be enabled to be used (see \fBzpool-features\fR(5)).
.ne 2
.na
\fB\fBuserquota@\fR\fIuser\fR=\fBnone\fR | \fIsize\fR\fR
.br
\fB\fBuserobjquota@\fR\fIuser\fR=\fBnone\fR | \fIcount\fR\fR
.ad
.sp .6
.RS 4n
@ -1118,16 +1134,26 @@ This property is not available on volumes, on file systems before version 4, or
.RE
Files created on Linux always have POSIX owners.
.RS 4
The \fBuserobjquota\fR is similar to \fBuserquota\fR but it limits the number of objects a \fIuser\fR can create.
Please refer to \fBuserobjused\fR for more information about how ZFS counts object usage.
.RE
.sp
.ne 2
.na
\fB\fBgroupquota@\fR\fIgroup\fR=\fBnone\fR\fR | \fIsize\fR
.br
\fB\fBgroupobjquota@\fR\fIgroup\fR=\fBnone\fR\fR | \fIcount\fR
.ad
.sp .6
.RS 4n
Limits the amount of space consumed by the specified group. Group space consumption is identified by the \fBuserquota@\fR\fIuser\fR property.
.sp
Unprivileged users can access only their own groups' space usage. The root user, or a user who has been granted the \fBgroupquota\fR privilege with \fBzfs allow\fR, can get and set all groups' quotas.
The \fBgroupobjquota\fR is similar to \fBgroupquota\fR but it limits that the \fIgroup\fR can consume \fIcount\fR number of objects at most.
Please refer to \fBuserobjused\fR for more information about how zfs counts object usage.
.RE
.sp
@ -2386,8 +2412,8 @@ Upgrades to the specified \fIversion\fR. If the \fB-V\fR flag is not specified,
.sp .6
.RS 4n
Displays space consumed by, and quotas on, each user in the specified
filesystem or snapshot. This corresponds to the \fBuserused@\fR\fIuser\fR and
\fBuserquota@\fR\fIuser\fR properties.
filesystem or snapshot. This corresponds to the \fBuserused@\fR\fIuser\fR, \fBuserobjused@\fR\fIuser\fR,
\fBuserquota@\fR\fIuser\fR, and \fBuserobjquota@\fR\fIuser\fR properties.
.sp
.ne 2
.na
@ -3141,10 +3167,14 @@ send subcommand
share subcommand Allows sharing file systems over NFS or SMB
protocols
snapshot subcommand Must also have the 'mount' ability
groupobjquota other Allows accessing any groupobjquota@... property
groupquota other Allows accessing any groupquota@... property
groupobjused other Allows reading any groupobjused@... property
groupused other Allows reading any groupused@... property
userprop other Allows changing any user property
userobjquota other Allows accessing any userobjquota@... property
userquota other Allows accessing any userquota@... property
userobjused other Allows reading any userobjused@... property
userused other Allows reading any userused@... property
acltype property

View File

@ -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_USEROBJQUOTA},
{ZFS_DELEG_PERM_GROUPOBJQUOTA},
{ZFS_DELEG_PERM_USEROBJUSED},
{ZFS_DELEG_PERM_GROUPOBJUSED},
{ZFS_DELEG_PERM_HOLD},
{ZFS_DELEG_PERM_RELEASE},
{NULL}

View File

@ -52,7 +52,11 @@ const char *zfs_userquota_prop_prefixes[] = {
"userused@",
"userquota@",
"groupused@",
"groupquota@"
"groupquota@",
"userobjused@",
"userobjquota@",
"groupobjused@",
"groupobjquota@"
};
zprop_desc_t *

View File

@ -31,6 +31,7 @@
/* Portions Copyright 2010 Robert Milkowski */
#include <sys/zfeature.h>
#include <sys/cred.h>
#include <sys/zfs_context.h>
#include <sys/dmu_objset.h>
@ -53,6 +54,7 @@
#include <sys/dsl_destroy.h>
#include <sys/vdev.h>
#include <sys/policy.h>
#include <sys/spa_impl.h>
/*
* Needed to close a window in dnode_move() that allows the objset to be freed
@ -77,6 +79,9 @@ int dmu_rescan_dnode_threshold = 1 << DN_MAX_INDBLKSHIFT;
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)
{
@ -519,6 +524,8 @@ dmu_objset_open_impl(spa_t *spa, dsl_dataset_t *ds, blkptr_t *bp,
DMU_GROUPUSED_OBJECT, &os->os_groupused_dnode);
}
mutex_init(&os->os_upgrade_lock, NULL, MUTEX_DEFAULT, NULL);
*osp = os;
return (0);
}
@ -625,6 +632,9 @@ dmu_objset_own(const char *name, dmu_objset_type_t type,
err = dmu_objset_own_impl(ds, type, readonly, tag, osp);
dsl_pool_rele(dp, FTAG);
if (err == 0 && dmu_objset_userobjspace_upgradable(*osp))
dmu_objset_userobjspace_upgrade(*osp);
return (err);
}
@ -685,6 +695,10 @@ dmu_objset_refresh_ownership(objset_t *os, void *tag)
void
dmu_objset_disown(objset_t *os, void *tag)
{
/*
* Stop upgrading thread
*/
dmu_objset_upgrade_stop(os);
dsl_dataset_disown(os->os_dsl_dataset, tag);
}
@ -859,6 +873,12 @@ 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_userobjused_enabled(os)) {
ds->ds_feature_activation_needed[
SPA_FEATURE_USEROBJ_ACCOUNTING] = B_TRUE;
os->os_phys->os_flags |=
OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE;
}
os->os_flags = os->os_phys->os_flags;
}
@ -1067,6 +1087,60 @@ dmu_objset_snapshot_one(const char *fsname, const char *snapname)
return (err);
}
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) {
taskqid_t id = os->os_upgrade_id;
os->os_upgrade_id = 0;
mutex_exit(&os->os_upgrade_lock);
taskq_cancel_id(os->os_spa->spa_upgrade_taskq, id);
} else {
mutex_exit(&os->os_upgrade_lock);
}
}
static void
dmu_objset_sync_dnodes(list_t *list, list_t *newlist, dmu_tx_t *tx)
{
@ -1257,6 +1331,13 @@ 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));
}
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)
@ -1272,6 +1353,25 @@ do_userquota_update(objset_t *os, uint64_t used, uint64_t flags,
}
}
static void
do_userobjquota_update(objset_t *os, uint64_t flags, uint64_t user,
uint64_t group, boolean_t subtract, dmu_tx_t *tx)
{
if (flags & DNODE_FLAG_USEROBJUSED_ACCOUNTED) {
char name[20 + DMU_OBJACCT_PREFIX_LEN];
(void) snprintf(name, sizeof (name), DMU_OBJACCT_PREFIX "%llx",
(longlong_t)user);
VERIFY0(zap_increment(os, DMU_USERUSED_OBJECT, name,
subtract ? -1 : 1, tx));
(void) snprintf(name, sizeof (name), DMU_OBJACCT_PREFIX "%llx",
(longlong_t)group);
VERIFY0(zap_increment(os, DMU_GROUPUSED_OBJECT, name,
subtract ? -1 : 1, tx));
}
}
void
dmu_objset_do_userquota_updates(objset_t *os, dmu_tx_t *tx)
{
@ -1310,11 +1410,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_userobjquota_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_userobjquota_update(os, dn->dn_phys->dn_flags,
dn->dn_newuid, dn->dn_newgid, B_FALSE, tx);
}
mutex_enter(&dn->dn_mtx);
@ -1486,19 +1590,19 @@ 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);
}
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
@ -1512,6 +1616,13 @@ dmu_objset_userspace_upgrade(objset_t *os)
dmu_buf_t *db;
int objerr;
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);
if (issig(JUSTLOOKING) && issig(FORREAL))
return (SET_ERROR(EINTR));
@ -1529,12 +1640,60 @@ 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_userobjspace_upgrade_cb(objset_t *os)
{
int err = 0;
if (dmu_objset_userobjspace_present(os))
return (0);
if (dmu_objset_is_snapshot(os))
return (SET_ERROR(EINVAL));
if (!dmu_objset_userobjused_enabled(os))
return (SET_ERROR(ENOTSUP));
dmu_objset_ds(os)->ds_feature_activation_needed[
SPA_FEATURE_USEROBJ_ACCOUNTING] = B_TRUE;
err = dmu_objset_space_upgrade(os);
if (err)
return (err);
os->os_flags |= OBJSET_FLAG_USEROBJACCOUNTING_COMPLETE;
txg_wait_synced(dmu_objset_pool(os), 0);
return (0);
}
void
dmu_objset_userobjspace_upgrade(objset_t *os)
{
dmu_objset_upgrade(os, dmu_objset_userobjspace_upgrade_cb);
}
void
dmu_objset_space(objset_t *os, uint64_t *refdbytesp, uint64_t *availbytesp,
uint64_t *usedobjsp, uint64_t *availobjsp)
@ -2096,4 +2255,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_userobjused_enabled);
EXPORT_SYMBOL(dmu_objset_userobjspace_upgrade);
EXPORT_SYMBOL(dmu_objset_userobjspace_present);
#endif

View File

@ -570,12 +570,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);

View File

@ -1167,6 +1167,13 @@ spa_activate(spa_t *spa, int mode)
*/
spa->spa_zvol_taskq = taskq_create("z_zvol", 1, defclsyspri,
1, INT_MAX, 0);
/*
* The taskq to upgrade datasets in this pool. Currently used by
* feature SPA_FEATURE_USEROBJ_ACCOUNTING.
*/
spa->spa_upgrade_taskq = taskq_create("z_upgrade", boot_ncpus,
defclsyspri, 1, INT_MAX, TASKQ_DYNAMIC);
}
/*
@ -1190,6 +1197,11 @@ spa_deactivate(spa_t *spa)
spa->spa_zvol_taskq = NULL;
}
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);

View File

@ -285,4 +285,15 @@ zpool_feature_init(void)
"Edon-R hash algorithm.",
ZFEATURE_FLAG_PER_DATASET, edonr_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);
}
}

View File

@ -1886,7 +1886,9 @@ boolean_t
zfs_acl_ids_overquota(zfs_sb_t *zsb, zfs_acl_ids_t *acl_ids)
{
return (zfs_fuid_overquota(zsb, B_FALSE, acl_ids->z_fuid) ||
zfs_fuid_overquota(zsb, B_TRUE, acl_ids->z_fgid));
zfs_fuid_overquota(zsb, B_TRUE, acl_ids->z_fgid) ||
zfs_fuid_overobjquota(zsb, B_FALSE, acl_ids->z_fuid) ||
zfs_fuid_overobjquota(zsb, B_TRUE, acl_ids->z_fgid));
}
/*

View File

@ -244,9 +244,14 @@ 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,
};
static int zfs_ioc_userspace_upgrade(zfs_cmd_t *zc);
static int zfs_ioc_userobjspace_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,
@ -1171,7 +1176,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_USEROBJUSED ||
zc->zc_objset_type == ZFS_PROP_USEROBJQUOTA) {
if (zc->zc_guid == crgetuid(cr))
return (0);
} else {
@ -2426,6 +2433,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_userobjspace_upgrade(zc);
kmem_free(zc, sizeof (zfs_cmd_t));
}
break;
@ -3720,13 +3728,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_USEROBJQUOTA];
const char *giq_prefix =
zfs_userquota_prop_prefixes[ZFS_PROP_GROUPOBJQUOTA];
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 {
/* USERUSED and GROUPUSED are read-only */
return (SET_ERROR(EINVAL));
@ -4927,6 +4945,48 @@ zfs_ioc_userspace_upgrade(zfs_cmd_t *zc)
return (error);
}
/*
* inputs:
* zc_name name of filesystem
*
* outputs:
* none
*/
static int
zfs_ioc_userobjspace_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)) {
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_userobjspace_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);
}
static int
zfs_ioc_share(zfs_cmd_t *zc)
{

View 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)

View File

@ -602,21 +602,18 @@ tests = ['sparse_001_pos']
[tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos']
# DISABLED:
# groupspace_001_pos
# groupspace_002_pos
# userquota_001_pos
# userquota_004_pos
# userquota_007_pos
# userquota_010_pos
# userspace_001_pos
# userspace_002_pos
[tests/functional/upgrade]
tests = [ 'upgrade_userobj_001_pos' ]
[tests/functional/userquota]
tests = [
'userquota_002_pos', 'userquota_003_pos',
'userquota_005_neg', 'userquota_006_pos',
'userquota_008_pos', 'userquota_009_pos',
'userquota_011_pos', 'userquota_012_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',
'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' ]
# DISABLED:
# vdev_zaps_007_pos -- fails due to a pre-existing issue with zpool split

View File

@ -48,10 +48,7 @@ main(int argc, char **argv)
if (argc == 4 && sscanf(argv[3], "%u", &first_file) != 1)
usage("Invalid first file", -3);
if (numfiles < first_file)
usage("First file larger than last file", -3);
for (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) {

View File

@ -2857,3 +2857,29 @@ function block_device_wait
$UDEVADM settle
fi
}
#
# Synchronize all the data in pool
#
# $1 pool name
#
function sync_pool #pool
{
typeset pool=${1:-$TESTPOOL}
log_must $SYNC
log_must $SLEEP 2
# Flush all the pool data.
typeset -i ret
$ZPOOL scrub $pool >/dev/null 2>&1
ret=$?
(( $ret != 0 )) && \
log_fail "$ZPOOL scrub $pool failed."
while ! is_pool_scrubbed $pool; do
if is_pool_resilvered $pool ; then
log_fail "$pool should not be resilver completed."
fi
log_must $SLEEP 2
done
}

View File

@ -51,6 +51,7 @@ SUBDIRS = \
sparse \
threadsappend \
truncate \
upgrade \
userquota \
vdev_zaps \
write_dirs \

View File

@ -38,7 +38,8 @@ typeset -a properties=("size" "capacity" "altroot" "health" "guid" "version"
"feature@large_blocks" "feature@large_dnode" "feature@filesystem_limits"
"feature@spacemap_histogram" "feature@enabled_txg" "feature@hole_birth"
"feature@extensible_dataset" "feature@bookmarks" "feature@embedded_data"
"feature@sha512" "feature@skein" "feature@edonr")
"feature@sha512" "feature@skein" "feature@edonr"
"feature@userobj_accounting")
else
typeset -a properties=("size" "capacity" "altroot" "health" "guid" "version"
"bootfs" ""leaked" delegation" "autoreplace" "cachefile" "dedupditto" "dedupratio"

View File

@ -213,32 +213,6 @@ function get_vdevs #pool cnt
$ECHO "$vdevs"
}
#
# Synchronize all the data in pool
#
# $1 pool name
#
function sync_pool #pool
{
typeset pool=$1
log_must $SYNC
log_must $SLEEP 2
# Flush all the pool data.
typeset -i ret
$ZPOOL scrub $pool >/dev/null 2>&1
ret=$?
(( $ret != 0 )) && \
log_fail "$ZPOOL scrub $pool failed."
while ! is_pool_scrubbed $pool; do
if is_pool_resilvered $pool ; then
log_fail "$pool should not be resilver completed."
fi
log_must $SLEEP 2
done
}
#
# Create and replace the same name virtual device files
#

View File

@ -0,0 +1,5 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/upgrade
dist_pkgdata_SCRIPTS = \
setup.ksh \
cleanup.ksh \
upgrade_userobj_001_pos.ksh

View File

@ -0,0 +1,44 @@
#!/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/include/libtest.shlib
verify_runnable "global"
log_must $ZPOOL destroy $TESTPOOL
log_must $RM /tmp/zpool_upgrade_test.dat
default_cleanup

View File

@ -0,0 +1,44 @@
#!/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/include/libtest.shlib
verify_runnable "global"
# create a pool without any features
log_must $MKFILE 128m /tmp/zpool_upgrade_test.dat
log_must $ZPOOL create -d -m $TESTDIR $TESTPOOL /tmp/zpool_upgrade_test.dat
log_pass

View File

@ -0,0 +1,98 @@
#!/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.
#
. $STF_SUITE/include/libtest.shlib
#
# 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
#
function cleanup
{
datasetexists $TESTPOOL/fs1 && log_must $ZFS destroy $TESTPOOL/fs1
datasetexists $TESTPOOL/fs2 && log_must $ZFS destroy $TESTPOOL/fs2
}
verify_runnable "global"
log_assert "pool upgrade for userobj accounting should work"
log_onexit cleanup
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!"

View File

@ -6,6 +6,7 @@ dist_pkgdata_SCRIPTS = \
cleanup.ksh \
groupspace_001_pos.ksh \
groupspace_002_pos.ksh \
groupspace_003_pos.ksh \
userquota_001_pos.ksh \
userquota_002_pos.ksh \
userquota_003_pos.ksh \
@ -18,5 +19,7 @@ dist_pkgdata_SCRIPTS = \
userquota_010_pos.ksh \
userquota_011_pos.ksh \
userquota_012_neg.ksh \
userquota_013_pos.ksh \
userspace_001_pos.ksh \
userspace_002_pos.ksh
userspace_002_pos.ksh \
userspace_003_pos.ksh

View File

@ -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) 2013 by Delphix. All rights reserved.
#
. $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=sa $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 expect"

View File

@ -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

View File

@ -50,6 +50,7 @@ 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)
@ -62,7 +63,7 @@ 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)

View File

@ -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"

View File

@ -0,0 +1,77 @@
#!/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.
#
. $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=sa $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, it as expect"

View File

@ -38,8 +38,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

View File

@ -75,7 +75,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"

View File

@ -0,0 +1,116 @@
#!/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.
#
. $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=sa $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"