Add user/group/project space files to .zfs
Users who access OpenZFS across a network may not have access to most administrative tools in OpenZFS. This commit exposes one of those tools, user/group/project space & quota utilization, to the .zfs directory within each file system dataset. These files can be accessed over NFS. A module parameter is added to enable or disable these files. Signed-off-by: Sam Atkinson <samatk@amazon.com>
This commit is contained in:
parent
8f2f6cd2ac
commit
f5215349f8
|
@ -0,0 +1,27 @@
|
|||
dnl #
|
||||
dnl # Linux 5.10 added the the seq_read_iter helper
|
||||
dnl #
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SRC_SEQ_READ_ITER], [
|
||||
ZFS_LINUX_TEST_SRC([seq_file_has_seq_read_iter], [
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/fs.h>
|
||||
static const struct file_operations
|
||||
fops __attribute__ ((unused)) = {
|
||||
.read_iter = seq_read_iter
|
||||
};
|
||||
],[])
|
||||
])
|
||||
|
||||
AC_DEFUN([ZFS_AC_KERNEL_SEQ_READ_ITER], [
|
||||
dnl #
|
||||
dnl # Linux 5.10 added the the seq_read_iter helper
|
||||
dnl #
|
||||
AC_MSG_CHECKING([whether seq_file's seq_read_iter() exists])
|
||||
ZFS_LINUX_TEST_RESULT([seq_file_has_seq_read_iter], [
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE(HAVE_SEQ_READ_ITER, 1,
|
||||
[seq_file has seq_read_iter()])
|
||||
],[
|
||||
AC_MSG_RESULT([no])
|
||||
])
|
||||
])
|
|
@ -166,6 +166,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_SRC], [
|
|||
ZFS_AC_KERNEL_SRC_REGISTER_SYSCTL_TABLE
|
||||
ZFS_AC_KERNEL_SRC_COPY_SPLICE_READ
|
||||
ZFS_AC_KERNEL_SRC_SYNC_BDEV
|
||||
ZFS_AC_KERNEL_SRC_SEQ_READ_ITER
|
||||
case "$host_cpu" in
|
||||
powerpc*)
|
||||
ZFS_AC_KERNEL_SRC_CPU_HAS_FEATURE
|
||||
|
@ -314,6 +315,7 @@ AC_DEFUN([ZFS_AC_KERNEL_TEST_RESULT], [
|
|||
ZFS_AC_KERNEL_REGISTER_SYSCTL_TABLE
|
||||
ZFS_AC_KERNEL_COPY_SPLICE_READ
|
||||
ZFS_AC_KERNEL_SYNC_BDEV
|
||||
ZFS_AC_KERNEL_SEQ_READ_ITER
|
||||
case "$host_cpu" in
|
||||
powerpc*)
|
||||
ZFS_AC_KERNEL_CPU_HAS_FEATURE
|
||||
|
|
|
@ -39,6 +39,11 @@
|
|||
#define ZFS_CTLDIR_NAME ".zfs"
|
||||
#define ZFS_SNAPDIR_NAME "snapshot"
|
||||
#define ZFS_SHAREDIR_NAME "shares"
|
||||
#define ZFS_SPACEDIR_NAME "space"
|
||||
#define ZFS_QUOTADIR_NAME "quota"
|
||||
#define ZFS_USERFILE_NAME "user"
|
||||
#define ZFS_GROUPFILE_NAME "group"
|
||||
#define ZFS_PROJECTFILE_NAME "project"
|
||||
|
||||
#define zfs_has_ctldir(zdp) \
|
||||
((zdp)->z_id == ZTOZSB(zdp)->z_root && \
|
||||
|
@ -48,6 +53,7 @@
|
|||
(ZTOZSB(zdp)->z_show_ctldir))
|
||||
|
||||
extern int zfs_expire_snapshot;
|
||||
extern int zfs_ctldir_spacefiles;
|
||||
|
||||
/* zfsctl generic functions */
|
||||
extern int zfsctl_create(zfsvfs_t *);
|
||||
|
@ -86,6 +92,16 @@ extern int zfsctl_shares_lookup(struct inode *dip, char *name,
|
|||
struct inode **ipp, int flags, cred_t *cr, int *direntflags,
|
||||
pathname_t *realpnp);
|
||||
|
||||
/* zfsctl '.zfs/space' functions */
|
||||
extern int zfsctl_spacedir_lookup(struct inode *dip, char *name,
|
||||
struct inode **ipp, int flags, cred_t *cr, int *direntflags,
|
||||
pathname_t *realpnp);
|
||||
|
||||
/* zfsctl '.zfs/quota' functions */
|
||||
extern int zfsctl_quotadir_lookup(struct inode *dip, char *name,
|
||||
struct inode **ipp, int flags, cred_t *cr, int *direntflags,
|
||||
pathname_t *realpnp);
|
||||
|
||||
/*
|
||||
* These inodes numbers are reserved for the .zfs control directory.
|
||||
* It is important that they be no larger that 48-bits because only
|
||||
|
@ -95,8 +111,22 @@ extern int zfsctl_shares_lookup(struct inode *dip, char *name,
|
|||
*/
|
||||
#define ZFSCTL_INO_ROOT 0x0000FFFFFFFFFFFFULL
|
||||
#define ZFSCTL_INO_SHARES 0x0000FFFFFFFFFFFEULL
|
||||
#define ZFSCTL_INO_SNAPDIR 0x0000FFFFFFFFFFFDULL
|
||||
#define ZFSCTL_INO_SNAPDIRS 0x0000FFFFFFFFFFFCULL
|
||||
#define ZFSCTL_INO_SPACEDIR 0x0000FFFFFFFFFFFDULL
|
||||
#define ZFSCTL_INO_SPACE_USER 0x0000FFFFFFFFFFFCULL
|
||||
#define ZFSCTL_INO_SPACE_GROUP 0x0000FFFFFFFFFFFBULL
|
||||
#define ZFSCTL_INO_SPACE_PROJ 0x0000FFFFFFFFFFFAULL
|
||||
#define ZFSCTL_INO_QUOTADIR 0x0000FFFFFFFFFFF9ULL
|
||||
#define ZFSCTL_INO_QUOTA_USER 0x0000FFFFFFFFFFF8ULL
|
||||
#define ZFSCTL_INO_QUOTA_GROUP 0x0000FFFFFFFFFFF7ULL
|
||||
#define ZFSCTL_INO_QUOTA_PROJ 0x0000FFFFFFFFFFF6ULL
|
||||
/*
|
||||
* snapdir inode numbers must be lowest; each snapshot gets its own
|
||||
* directory and inode number, assigned by decrementing from
|
||||
* ZFSCTL_INO_SNAPDIRS.
|
||||
*/
|
||||
#define ZFSCTL_INO_SNAPDIR 0x0000FFFFFFFFFFF5ULL
|
||||
#define ZFSCTL_INO_SNAPDIRS 0x0000FFFFFFFFFFF4ULL
|
||||
|
||||
|
||||
#define ZFSCTL_EXPIRE_SNAPSHOT 300
|
||||
|
||||
|
|
|
@ -118,6 +118,30 @@ extern const struct inode_operations zpl_ops_snapdir;
|
|||
extern const struct file_operations zpl_fops_shares;
|
||||
extern const struct inode_operations zpl_ops_shares;
|
||||
|
||||
extern const struct file_operations zpl_fops_spacedir;
|
||||
extern const struct inode_operations zpl_ops_spacedir;
|
||||
|
||||
extern const struct file_operations zpl_fops_userspace_file;
|
||||
extern const struct inode_operations zpl_ops_userspace_file;
|
||||
|
||||
extern const struct file_operations zpl_fops_groupspace_file;
|
||||
extern const struct inode_operations zpl_ops_groupspace_file;
|
||||
|
||||
extern const struct file_operations zpl_fops_projectspace_file;
|
||||
extern const struct inode_operations zpl_ops_projectspace_file;
|
||||
|
||||
extern const struct file_operations zpl_fops_quotadir;
|
||||
extern const struct inode_operations zpl_ops_quotadir;
|
||||
|
||||
extern const struct file_operations zpl_fops_userquota_file;
|
||||
extern const struct inode_operations zpl_ops_userquota_file;
|
||||
|
||||
extern const struct file_operations zpl_fops_groupquota_file;
|
||||
extern const struct inode_operations zpl_ops_groupquota_file;
|
||||
|
||||
extern const struct file_operations zpl_fops_projectquota_file;
|
||||
extern const struct inode_operations zpl_ops_projectquota_file;
|
||||
|
||||
#if defined(HAVE_VFS_ITERATE) || defined(HAVE_VFS_ITERATE_SHARED)
|
||||
|
||||
#define ZPL_DIR_CONTEXT_INIT(_dirent, _actor, _pos) { \
|
||||
|
|
|
@ -1375,6 +1375,20 @@ which have the
|
|||
.Em no_root_squash
|
||||
option set.
|
||||
.
|
||||
.It Sy zfs_ctldir_spacefiles Ns = Ns Sy 0 Ns | Ns 1 Pq int
|
||||
Enable the existence of (quota|space)/(user|group|project) files in the
|
||||
.Sy .zfs
|
||||
directory.
|
||||
These files can be used in lieu of the
|
||||
.Sy zfs-userspace(8) ,
|
||||
.Sy zfs-groupspace(8) ,
|
||||
and
|
||||
.Sy zfs-projectspace(8)
|
||||
tools to determine space and quota utilization for a given dataset.
|
||||
These would normally be used when the dataset is being accessed over
|
||||
a network, e.g. using NFS, where ZFS command line tools, libzfs,
|
||||
etc. are not available.
|
||||
.
|
||||
.It Sy zfs_flags Ns = Ns Sy 0 Pq int
|
||||
Set additional debugging flags.
|
||||
The following flags may be bitwise-ored together:
|
||||
|
|
|
@ -111,6 +111,7 @@ static krwlock_t zfs_snapshot_lock;
|
|||
*/
|
||||
int zfs_expire_snapshot = ZFSCTL_EXPIRE_SNAPSHOT;
|
||||
static int zfs_admin_snapshot = 0;
|
||||
int zfs_ctldir_spacefiles = 0;
|
||||
|
||||
typedef struct {
|
||||
char *se_name; /* full snapshot name */
|
||||
|
@ -818,6 +819,14 @@ zfsctl_root_lookup(struct inode *dip, const char *name, struct inode **ipp,
|
|||
} else if (strcmp(name, ZFS_SHAREDIR_NAME) == 0) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_SHARES,
|
||||
&zpl_fops_shares, &zpl_ops_shares);
|
||||
} else if (strcmp(name, ZFS_SPACEDIR_NAME) == 0 &&
|
||||
zfs_ctldir_spacefiles) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_SPACEDIR,
|
||||
&zpl_fops_spacedir, &zpl_ops_spacedir);
|
||||
} else if (strcmp(name, ZFS_QUOTADIR_NAME) == 0 &&
|
||||
zfs_ctldir_spacefiles) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_QUOTADIR,
|
||||
&zpl_fops_quotadir, &zpl_ops_quotadir);
|
||||
} else {
|
||||
*ipp = NULL;
|
||||
}
|
||||
|
@ -1282,6 +1291,74 @@ zfsctl_shares_lookup(struct inode *dip, char *name, struct inode **ipp,
|
|||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
zfsctl_spacedir_lookup(struct inode *dip, char *name, struct inode **ipp,
|
||||
int flags, cred_t *cr, int *direntflags, pathname_t *realpnp)
|
||||
{
|
||||
zfsvfs_t *zfsvfs = ITOZSB(dip);
|
||||
int error;
|
||||
|
||||
if ((error = zfs_enter(zfsvfs, FTAG)) != 0)
|
||||
return (error);
|
||||
|
||||
if (strcmp(name, ZFS_USERFILE_NAME) == 0) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_SPACE_USER,
|
||||
&zpl_fops_userspace_file, &zpl_ops_userspace_file);
|
||||
(*ipp)->i_mode = (S_IFREG|S_IRUGO);
|
||||
} else if (strcmp(name, ZFS_GROUPFILE_NAME) == 0) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_SPACE_GROUP,
|
||||
&zpl_fops_groupspace_file, &zpl_ops_groupspace_file);
|
||||
(*ipp)->i_mode = (S_IFREG|S_IRUGO);
|
||||
} else if (strcmp(name, ZFS_PROJECTFILE_NAME) == 0) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_SPACE_PROJ,
|
||||
&zpl_fops_projectspace_file, &zpl_ops_projectspace_file);
|
||||
(*ipp)->i_mode = (S_IFREG|S_IRUGO);
|
||||
} else {
|
||||
*ipp = NULL;
|
||||
}
|
||||
|
||||
if (*ipp == NULL)
|
||||
error = SET_ERROR(ENOENT);
|
||||
|
||||
zfs_exit(zfsvfs, FTAG);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
zfsctl_quotadir_lookup(struct inode *dip, char *name, struct inode **ipp,
|
||||
int flags, cred_t *cr, int *direntflags, pathname_t *realpnp)
|
||||
{
|
||||
zfsvfs_t *zfsvfs = ITOZSB(dip);
|
||||
int error;
|
||||
|
||||
if ((error = zfs_enter(zfsvfs, FTAG)) != 0)
|
||||
return (error);
|
||||
|
||||
if (strcmp(name, ZFS_USERFILE_NAME) == 0) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_QUOTA_USER,
|
||||
&zpl_fops_userquota_file, &zpl_ops_userquota_file);
|
||||
(*ipp)->i_mode = (S_IFREG|S_IRUGO);
|
||||
} else if (strcmp(name, ZFS_GROUPFILE_NAME) == 0) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_QUOTA_GROUP,
|
||||
&zpl_fops_groupquota_file, &zpl_ops_groupquota_file);
|
||||
(*ipp)->i_mode = (S_IFREG|S_IRUGO);
|
||||
} else if (strcmp(name, ZFS_PROJECTFILE_NAME) == 0) {
|
||||
*ipp = zfsctl_inode_lookup(zfsvfs, ZFSCTL_INO_QUOTA_PROJ,
|
||||
&zpl_fops_projectquota_file, &zpl_ops_projectquota_file);
|
||||
(*ipp)->i_mode = (S_IFREG|S_IRUGO);
|
||||
} else {
|
||||
*ipp = NULL;
|
||||
}
|
||||
|
||||
if (*ipp == NULL)
|
||||
error = SET_ERROR(ENOENT);
|
||||
|
||||
zfs_exit(zfsvfs, FTAG);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the various pieces we'll need to create and manipulate .zfs
|
||||
* directories. Currently this is unused but available.
|
||||
|
@ -1315,3 +1392,7 @@ MODULE_PARM_DESC(zfs_admin_snapshot, "Enable mkdir/rmdir/mv in .zfs/snapshot");
|
|||
|
||||
module_param(zfs_expire_snapshot, int, 0644);
|
||||
MODULE_PARM_DESC(zfs_expire_snapshot, "Seconds to expire .zfs/snapshot");
|
||||
|
||||
module_param(zfs_ctldir_spacefiles, int, 0644);
|
||||
MODULE_PARM_DESC(zfs_ctldir_spacefiles,
|
||||
"Enable user/group/project space/quota files in .zfs/");
|
||||
|
|
|
@ -1760,13 +1760,37 @@ zfs_vget(struct super_block *sb, struct inode **ipp, fid_t *fidp)
|
|||
if ((err = zfs_enter(zfsvfs, FTAG)) != 0)
|
||||
return (err);
|
||||
/* A zero fid_gen means we are in the .zfs control directories */
|
||||
if (fid_gen == 0 &&
|
||||
(object == ZFSCTL_INO_ROOT || object == ZFSCTL_INO_SNAPDIR)) {
|
||||
if (fid_gen == 0 && object >= ZFSCTL_INO_SNAPDIR &&
|
||||
object <= ZFSCTL_INO_ROOT && object != ZFSCTL_INO_SHARES) {
|
||||
*ipp = zfsvfs->z_ctldir;
|
||||
ASSERT(*ipp != NULL);
|
||||
if (object == ZFSCTL_INO_SNAPDIR) {
|
||||
VERIFY(zfsctl_root_lookup(*ipp, "snapshot", ipp,
|
||||
0, kcred, NULL, NULL) == 0);
|
||||
VERIFY0(zfsctl_root_lookup(*ipp,
|
||||
ZFS_SNAPDIR_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_SPACEDIR) {
|
||||
VERIFY0(zfsctl_root_lookup(*ipp,
|
||||
ZFS_SPACEDIR_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_QUOTADIR) {
|
||||
VERIFY0(zfsctl_root_lookup(*ipp,
|
||||
ZFS_QUOTADIR_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_SPACE_USER) {
|
||||
VERIFY0(zfsctl_spacedir_lookup(*ipp,
|
||||
ZFS_USERFILE_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_SPACE_GROUP) {
|
||||
VERIFY0(zfsctl_spacedir_lookup(*ipp,
|
||||
ZFS_GROUPFILE_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_SPACE_PROJ) {
|
||||
VERIFY0(zfsctl_spacedir_lookup(*ipp,
|
||||
ZFS_PROJECTFILE_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_QUOTA_USER) {
|
||||
VERIFY0(zfsctl_quotadir_lookup(*ipp,
|
||||
ZFS_USERFILE_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_QUOTA_GROUP) {
|
||||
VERIFY0(zfsctl_quotadir_lookup(*ipp,
|
||||
ZFS_GROUPFILE_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else if (object == ZFSCTL_INO_QUOTA_PROJ) {
|
||||
VERIFY0(zfsctl_quotadir_lookup(*ipp,
|
||||
ZFS_PROJECTFILE_NAME, ipp, 0, kcred, NULL, NULL));
|
||||
} else {
|
||||
/*
|
||||
* Must have an existing ref, so igrab()
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <sys/zfs_vfsops.h>
|
||||
#include <sys/zfs_vnops.h>
|
||||
#include <sys/zfs_ctldir.h>
|
||||
#include <sys/zfs_quota.h>
|
||||
#include <sys/zpl.h>
|
||||
#include <sys/dmu.h>
|
||||
#include <sys/dsl_dataset.h>
|
||||
|
@ -60,27 +61,55 @@ zpl_root_iterate(struct file *filp, zpl_dir_context_t *ctx)
|
|||
if ((error = zpl_enter(zfsvfs, FTAG)) != 0)
|
||||
return (error);
|
||||
|
||||
if (!zpl_dir_emit_dots(filp, ctx))
|
||||
if (!zpl_dir_emit_dots(filp, ctx)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->pos == 2) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_SNAPDIR_NAME,
|
||||
strlen(ZFS_SNAPDIR_NAME), ZFSCTL_INO_SNAPDIR, DT_DIR))
|
||||
strlen(ZFS_SNAPDIR_NAME), ZFSCTL_INO_SNAPDIR, DT_DIR)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
if (ctx->pos == 3) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_SHAREDIR_NAME,
|
||||
strlen(ZFS_SHAREDIR_NAME), ZFSCTL_INO_SHARES, DT_DIR))
|
||||
strlen(ZFS_SHAREDIR_NAME), ZFSCTL_INO_SHARES, DT_DIR)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
if (ctx->pos == 4 && zfs_ctldir_spacefiles) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_SPACEDIR_NAME,
|
||||
strlen(ZFS_SPACEDIR_NAME),
|
||||
ZFSCTL_INO_SPACEDIR, DT_DIR)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
if (ctx->pos == 5 && zfs_ctldir_spacefiles) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_QUOTADIR_NAME,
|
||||
strlen(ZFS_QUOTADIR_NAME),
|
||||
ZFSCTL_INO_QUOTADIR, DT_DIR)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
out:
|
||||
zpl_exit(zfsvfs, FTAG);
|
||||
|
||||
return (error);
|
||||
}
|
||||
|
||||
|
@ -671,3 +700,551 @@ const struct inode_operations zpl_ops_shares = {
|
|||
.lookup = zpl_shares_lookup,
|
||||
.getattr = zpl_shares_getattr,
|
||||
};
|
||||
|
||||
static int
|
||||
zpl_spacedir_iterate(struct file *filp, zpl_dir_context_t *ctx)
|
||||
{
|
||||
zfsvfs_t *zfsvfs = ITOZSB(file_inode(filp));
|
||||
int error = 0;
|
||||
|
||||
if ((error = zpl_enter(zfsvfs, FTAG)) != 0)
|
||||
return (error);
|
||||
|
||||
if (!zpl_dir_emit_dots(filp, ctx)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->pos == 2) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_USERFILE_NAME,
|
||||
strlen(ZFS_USERFILE_NAME), ZFSCTL_INO_SPACE_USER,
|
||||
DT_REG)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
if (ctx->pos == 3) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_GROUPFILE_NAME,
|
||||
strlen(ZFS_GROUPFILE_NAME), ZFSCTL_INO_SPACE_GROUP,
|
||||
DT_REG)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
if (ctx->pos == 4) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_PROJECTFILE_NAME,
|
||||
strlen(ZFS_PROJECTFILE_NAME), ZFSCTL_INO_SPACE_PROJ,
|
||||
DT_REG)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
out:
|
||||
zpl_exit(zfsvfs, FTAG);
|
||||
return (error);
|
||||
}
|
||||
|
||||
#if !defined(HAVE_VFS_ITERATE) && !defined(HAVE_VFS_ITERATE_SHARED)
|
||||
static int
|
||||
zpl_spacedir_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
{
|
||||
zpl_dir_context_t ctx =
|
||||
ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos);
|
||||
int error;
|
||||
|
||||
error = zpl_spacedir_iterate(filp, &ctx);
|
||||
filp->f_pos = ctx.pos;
|
||||
|
||||
return (error);
|
||||
}
|
||||
#endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */
|
||||
|
||||
static struct dentry *
|
||||
zpl_spacedir_lookup(struct inode *dip, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
cred_t *cr = CRED();
|
||||
struct inode *ip;
|
||||
int error;
|
||||
|
||||
crhold(cr);
|
||||
error = -zfsctl_spacedir_lookup(dip, dname(dentry), &ip, 0, cr,
|
||||
NULL, NULL);
|
||||
ASSERT3S(error, <=, 0);
|
||||
crfree(cr);
|
||||
|
||||
if (error) {
|
||||
if (error == -ENOENT)
|
||||
return (d_splice_alias(NULL, dentry));
|
||||
else
|
||||
return (ERR_PTR(error));
|
||||
}
|
||||
|
||||
return (d_splice_alias(ip, dentry));
|
||||
}
|
||||
|
||||
static int
|
||||
#ifdef HAVE_USERNS_IOPS_GETATTR
|
||||
zpl_spacedir_getattr_impl(struct user_namespace *user_ns,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags)
|
||||
#elif defined(HAVE_IDMAP_IOPS_GETATTR)
|
||||
zpl_spacedir_getattr_impl(struct mnt_idmap *user_ns,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags)
|
||||
#else
|
||||
zpl_spacedir_getattr_impl(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags)
|
||||
#endif
|
||||
{
|
||||
(void) request_mask, (void) query_flags;
|
||||
struct inode *ip = path->dentry->d_inode;
|
||||
|
||||
#if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR))
|
||||
#ifdef HAVE_GENERIC_FILLATTR_USERNS
|
||||
generic_fillattr(user_ns, ip, stat);
|
||||
#elif defined(HAVE_GENERIC_FILLATTR_IDMAP)
|
||||
generic_fillattr(user_ns, ip, stat);
|
||||
#elif defined(HAVE_GENERIC_FILLATTR_IDMAP_REQMASK)
|
||||
generic_fillattr(user_ns, request_mask, ip, stat);
|
||||
#else
|
||||
(void) user_ns;
|
||||
#endif
|
||||
#else
|
||||
generic_fillattr(ip, stat);
|
||||
#endif
|
||||
stat->atime = current_time(ip);
|
||||
|
||||
return (0);
|
||||
}
|
||||
ZPL_GETATTR_WRAPPER(zpl_spacedir_getattr);
|
||||
|
||||
/*
|
||||
* The '.zfs/space' directory file operations.
|
||||
*/
|
||||
const struct file_operations zpl_fops_spacedir = {
|
||||
.open = zpl_common_open,
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
#ifdef HAVE_VFS_ITERATE_SHARED
|
||||
.iterate_shared = zpl_spacedir_iterate,
|
||||
#elif defined(HAVE_VFS_ITERATE)
|
||||
.iterate = zpl_spacedir_iterate,
|
||||
#else
|
||||
.readdir = zpl_spacedir_readdir,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* The '.zfs/space' directory inode operations.
|
||||
*/
|
||||
const struct inode_operations zpl_ops_spacedir = {
|
||||
.lookup = zpl_spacedir_lookup,
|
||||
.getattr = zpl_spacedir_getattr,
|
||||
};
|
||||
|
||||
static int
|
||||
zpl_quotadir_iterate(struct file *filp, zpl_dir_context_t *ctx)
|
||||
{
|
||||
zfsvfs_t *zfsvfs = ITOZSB(file_inode(filp));
|
||||
int error = 0;
|
||||
|
||||
if ((error = zpl_enter(zfsvfs, FTAG)) != 0)
|
||||
return (error);
|
||||
|
||||
if (!zpl_dir_emit_dots(filp, ctx)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->pos == 2) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_USERFILE_NAME,
|
||||
strlen(ZFS_USERFILE_NAME), ZFSCTL_INO_QUOTA_USER,
|
||||
DT_REG)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
if (ctx->pos == 3) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_GROUPFILE_NAME,
|
||||
strlen(ZFS_GROUPFILE_NAME), ZFSCTL_INO_QUOTA_GROUP,
|
||||
DT_REG)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
if (ctx->pos == 4) {
|
||||
if (!zpl_dir_emit(ctx, ZFS_PROJECTFILE_NAME,
|
||||
strlen(ZFS_PROJECTFILE_NAME), ZFSCTL_INO_QUOTA_PROJ,
|
||||
DT_REG)) {
|
||||
error = SET_ERROR(-EIO);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx->pos++;
|
||||
}
|
||||
|
||||
out:
|
||||
zpl_exit(zfsvfs, FTAG);
|
||||
return (error);
|
||||
}
|
||||
|
||||
#if !defined(HAVE_VFS_ITERATE) && !defined(HAVE_VFS_ITERATE_SHARED)
|
||||
static int
|
||||
zpl_quotadir_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
||||
{
|
||||
zpl_dir_context_t ctx =
|
||||
ZPL_DIR_CONTEXT_INIT(dirent, filldir, filp->f_pos);
|
||||
int error;
|
||||
|
||||
error = zpl_quotadir_iterate(filp, &ctx);
|
||||
filp->f_pos = ctx.pos;
|
||||
|
||||
return (error);
|
||||
}
|
||||
#endif /* !HAVE_VFS_ITERATE && !HAVE_VFS_ITERATE_SHARED */
|
||||
|
||||
static struct dentry *
|
||||
zpl_quotadir_lookup(struct inode *dip, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
cred_t *cr = CRED();
|
||||
struct inode *ip;
|
||||
int error;
|
||||
|
||||
crhold(cr);
|
||||
error = -zfsctl_quotadir_lookup(dip, dname(dentry), &ip, 0, cr,
|
||||
NULL, NULL);
|
||||
ASSERT3S(error, <=, 0);
|
||||
crfree(cr);
|
||||
|
||||
if (error) {
|
||||
if (error == -ENOENT)
|
||||
return (d_splice_alias(NULL, dentry));
|
||||
else
|
||||
return (ERR_PTR(error));
|
||||
}
|
||||
|
||||
return (d_splice_alias(ip, dentry));
|
||||
}
|
||||
|
||||
static int
|
||||
#ifdef HAVE_USERNS_IOPS_GETATTR
|
||||
zpl_quotadir_getattr_impl(struct user_namespace *user_ns,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags)
|
||||
#elif defined(HAVE_IDMAP_IOPS_GETATTR)
|
||||
zpl_quotadir_getattr_impl(struct mnt_idmap *user_ns,
|
||||
const struct path *path, struct kstat *stat, u32 request_mask,
|
||||
unsigned int query_flags)
|
||||
#else
|
||||
zpl_quotadir_getattr_impl(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags)
|
||||
#endif
|
||||
{
|
||||
(void) request_mask, (void) query_flags;
|
||||
struct inode *ip = path->dentry->d_inode;
|
||||
|
||||
#if (defined(HAVE_USERNS_IOPS_GETATTR) || defined(HAVE_IDMAP_IOPS_GETATTR))
|
||||
#ifdef HAVE_GENERIC_FILLATTR_USERNS
|
||||
generic_fillattr(user_ns, ip, stat);
|
||||
#elif defined(HAVE_GENERIC_FILLATTR_IDMAP)
|
||||
generic_fillattr(user_ns, ip, stat);
|
||||
#elif defined(HAVE_GENERIC_FILLATTR_IDMAP_REQMASK)
|
||||
generic_fillattr(user_ns, request_mask, ip, stat);
|
||||
#else
|
||||
(void) user_ns;
|
||||
#endif
|
||||
#else
|
||||
generic_fillattr(ip, stat);
|
||||
#endif
|
||||
stat->atime = current_time(ip);
|
||||
|
||||
return (0);
|
||||
}
|
||||
ZPL_GETATTR_WRAPPER(zpl_quotadir_getattr);
|
||||
|
||||
/*
|
||||
* The '.zfs/quota' directory file operations.
|
||||
*/
|
||||
const struct file_operations zpl_fops_quotadir = {
|
||||
.open = zpl_common_open,
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
#ifdef HAVE_VFS_ITERATE_SHARED
|
||||
.iterate_shared = zpl_quotadir_iterate,
|
||||
#elif defined(HAVE_VFS_ITERATE)
|
||||
.iterate = zpl_quotadir_iterate,
|
||||
#else
|
||||
.readdir = zpl_quotadir_readdir,
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
* The '.zfs/quota' directory inode operations.
|
||||
*/
|
||||
const struct inode_operations zpl_ops_quotadir = {
|
||||
.lookup = zpl_quotadir_lookup,
|
||||
.getattr = zpl_quotadir_getattr,
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers for:
|
||||
* .zfs/(space|quota)user
|
||||
* .zfs/(space|quota)group
|
||||
* .zfs/(space|quota)project
|
||||
*/
|
||||
static int foreach_zfs_useracct(zfsvfs_t *zfsvfs,
|
||||
zfs_userquota_prop_t type, uint64_t cookie,
|
||||
int (*fn)(zfs_useracct_t *zua, zfs_userquota_prop_t type, void *v),
|
||||
void *fn_arg)
|
||||
{
|
||||
uint64_t cbufsize, bufsize = 16 * sizeof (zfs_useracct_t);
|
||||
zfs_useracct_t *buf = (zfs_useracct_t *)kmem_alloc(bufsize, KM_SLEEP);
|
||||
int err = 0;
|
||||
for (;;) {
|
||||
cbufsize = bufsize;
|
||||
if (zfs_userspace_many(zfsvfs, type, &cookie,
|
||||
buf, &cbufsize)) {
|
||||
err = 1;
|
||||
break;
|
||||
};
|
||||
if (cbufsize == 0) {
|
||||
break;
|
||||
}
|
||||
zfs_useracct_t *zua = buf;
|
||||
while (cbufsize > 0) {
|
||||
if (fn(zua, type, fn_arg)) {
|
||||
err = 1;
|
||||
}
|
||||
zua++;
|
||||
cbufsize -= sizeof (zfs_useracct_t);
|
||||
}
|
||||
}
|
||||
kmem_free(buf, bufsize);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static int zua_nvlist_add(zfs_useracct_t *zua,
|
||||
zfs_userquota_prop_t type, void *v)
|
||||
{
|
||||
nvlist_t *ids = (nvlist_t *)v;
|
||||
char name[MAXNAMELEN];
|
||||
(void) snprintf(name, sizeof (name), "%u", zua->zu_rid);
|
||||
nvlist_t *spacelist;
|
||||
if (nvlist_lookup_nvlist(ids, name, &spacelist) ||
|
||||
spacelist == NULL) {
|
||||
VERIFY0(nvlist_alloc(&spacelist, NV_UNIQUE_NAME, KM_NOSLEEP));
|
||||
VERIFY0(nvlist_add_nvlist(ids, name, spacelist));
|
||||
/* lookup again because nvlist_add_nvlist does a deep-copy */
|
||||
VERIFY0(nvlist_lookup_nvlist(ids, name, &spacelist));
|
||||
}
|
||||
VERIFY0(nvlist_add_uint64(spacelist,
|
||||
zfs_userquota_prop_prefixes[type], zua->zu_space));
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void seq_print_spaceval(struct seq_file *seq, nvlist_t *spacelist,
|
||||
zfs_userquota_prop_t type)
|
||||
{
|
||||
uint64_t spaceval;
|
||||
seq_printf(seq, ",");
|
||||
if (!nvlist_lookup_uint64(spacelist,
|
||||
zfs_userquota_prop_prefixes[type],
|
||||
&spaceval)) {
|
||||
seq_printf(seq, "%llu", spaceval);
|
||||
}
|
||||
}
|
||||
|
||||
static int zpl_quotaspace_show(struct seq_file *seq)
|
||||
{
|
||||
zfs_userquota_prop_t *props = (zfs_userquota_prop_t *)seq->private;
|
||||
zfsvfs_t *zfsvfs = ITOZSB(file_inode(seq->file));
|
||||
nvlist_t *ids;
|
||||
unsigned int i;
|
||||
VERIFY0(nvlist_alloc(&ids, NV_UNIQUE_NAME, 0));
|
||||
for (i = 0; i < 2; ++i)
|
||||
VERIFY0(foreach_zfs_useracct(zfsvfs, props[i], 0,
|
||||
zua_nvlist_add, ids));
|
||||
for (nvpair_t *idpair = nvlist_next_nvpair(ids, NULL);
|
||||
idpair != NULL; idpair = nvlist_next_nvpair(ids, idpair)) {
|
||||
const char *id = nvpair_name(idpair);
|
||||
seq_printf(seq, "%s", id);
|
||||
nvlist_t *spacelist;
|
||||
VERIFY0(nvpair_value_nvlist(idpair, &spacelist));
|
||||
for (i = 0; i < 2; ++i)
|
||||
seq_print_spaceval(seq, spacelist, props[i]);
|
||||
seq_putc(seq, '\n');
|
||||
}
|
||||
nvlist_free(ids);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int zpl_quota_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
seq_printf(seq, "id,quota,objquota\n");
|
||||
return (zpl_quotaspace_show(seq));
|
||||
}
|
||||
|
||||
static int zpl_space_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
seq_printf(seq, "id,used,objused\n");
|
||||
return (zpl_quotaspace_show(seq));
|
||||
}
|
||||
|
||||
static zfs_userquota_prop_t userspace_props[2] = {
|
||||
ZFS_PROP_USERUSED,
|
||||
ZFS_PROP_USEROBJUSED
|
||||
};
|
||||
|
||||
static zfs_userquota_prop_t groupspace_props[2] = {
|
||||
ZFS_PROP_GROUPUSED,
|
||||
ZFS_PROP_GROUPOBJUSED
|
||||
};
|
||||
|
||||
static zfs_userquota_prop_t projectspace_props[2] = {
|
||||
ZFS_PROP_PROJECTUSED,
|
||||
ZFS_PROP_PROJECTOBJUSED
|
||||
};
|
||||
|
||||
static zfs_userquota_prop_t userquota_props[2] = {
|
||||
ZFS_PROP_USERQUOTA,
|
||||
ZFS_PROP_USEROBJQUOTA
|
||||
};
|
||||
|
||||
static zfs_userquota_prop_t groupquota_props[2] = {
|
||||
ZFS_PROP_GROUPQUOTA,
|
||||
ZFS_PROP_GROUPOBJQUOTA
|
||||
};
|
||||
|
||||
static zfs_userquota_prop_t projectquota_props[2] = {
|
||||
ZFS_PROP_PROJECTQUOTA,
|
||||
ZFS_PROP_PROJECTOBJQUOTA
|
||||
};
|
||||
|
||||
static int zpl_fops_userspace_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, zpl_space_show,
|
||||
(void *)userspace_props);
|
||||
}
|
||||
|
||||
static int zpl_fops_groupspace_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, zpl_space_show,
|
||||
(void *)groupspace_props);
|
||||
}
|
||||
|
||||
static int zpl_fops_projectspace_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, zpl_space_show,
|
||||
(void *)projectspace_props);
|
||||
}
|
||||
|
||||
static int zpl_fops_userquota_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, zpl_quota_show,
|
||||
(void *)userquota_props);
|
||||
}
|
||||
|
||||
static int zpl_fops_groupquota_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, zpl_quota_show,
|
||||
(void *)groupquota_props);
|
||||
}
|
||||
|
||||
static int zpl_fops_projectquota_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, zpl_quota_show,
|
||||
(void *)projectquota_props);
|
||||
}
|
||||
|
||||
/* .zfs/space/user */
|
||||
const struct file_operations zpl_fops_userspace_file = {
|
||||
.open = zpl_fops_userspace_open,
|
||||
#ifdef HAVE_SEQ_READ_ITER
|
||||
.read_iter = seq_read_iter,
|
||||
#endif
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/* .zfs/space/group */
|
||||
const struct file_operations zpl_fops_groupspace_file = {
|
||||
.open = zpl_fops_groupspace_open,
|
||||
.read = seq_read,
|
||||
#ifdef HAVE_SEQ_READ_ITER
|
||||
.read_iter = seq_read_iter,
|
||||
#endif
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/* .zfs/space/project */
|
||||
const struct file_operations zpl_fops_projectspace_file = {
|
||||
.open = zpl_fops_projectspace_open,
|
||||
.read = seq_read,
|
||||
#ifdef HAVE_SEQ_READ_ITER
|
||||
.read_iter = seq_read_iter,
|
||||
#endif
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/* .zfs/quota/user */
|
||||
const struct file_operations zpl_fops_userquota_file = {
|
||||
.open = zpl_fops_userquota_open,
|
||||
#ifdef HAVE_SEQ_READ_ITER
|
||||
.read_iter = seq_read_iter,
|
||||
#endif
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/* .zfs/quota/group */
|
||||
const struct file_operations zpl_fops_groupquota_file = {
|
||||
.open = zpl_fops_groupquota_open,
|
||||
.read = seq_read,
|
||||
#ifdef HAVE_SEQ_READ_ITER
|
||||
.read_iter = seq_read_iter,
|
||||
#endif
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/* .zfs/quota/project */
|
||||
const struct file_operations zpl_fops_projectquota_file = {
|
||||
.open = zpl_fops_projectquota_open,
|
||||
.read = seq_read,
|
||||
#ifdef HAVE_SEQ_READ_ITER
|
||||
.read_iter = seq_read_iter,
|
||||
#endif
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
|
||||
const struct inode_operations zpl_ops_userspace_file = {};
|
||||
const struct inode_operations zpl_ops_groupspace_file = {};
|
||||
const struct inode_operations zpl_ops_projectspace_file = {};
|
||||
const struct inode_operations zpl_ops_userquota_file = {};
|
||||
const struct inode_operations zpl_ops_groupquota_file = {};
|
||||
const struct inode_operations zpl_ops_projectquota_file = {};
|
||||
|
|
|
@ -211,7 +211,7 @@ tags = ['functional', 'user_namespace']
|
|||
|
||||
[tests/functional/userquota:Linux]
|
||||
tests = ['groupspace_001_pos', 'groupspace_002_pos', 'groupspace_003_pos',
|
||||
'userquota_013_pos', 'userspace_003_pos']
|
||||
'userquota_013_pos', 'userspace_003_pos', 'ctldir_files_001']
|
||||
tags = ['functional', 'userquota']
|
||||
|
||||
[tests/functional/zvol/zvol_misc:Linux]
|
||||
|
|
|
@ -2039,6 +2039,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
|||
functional/userquota/userspace_encrypted.ksh \
|
||||
functional/userquota/userspace_send_encrypted.ksh \
|
||||
functional/userquota/userspace_encrypted_13709.ksh \
|
||||
functional/userquota/ctldir_files_001.ksh \
|
||||
functional/vdev_zaps/cleanup.ksh \
|
||||
functional/vdev_zaps/setup.ksh \
|
||||
functional/vdev_zaps/vdev_zaps_001_pos.ksh \
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
#!/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
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
. $STF_SUITE/tests/functional/userquota/userquota_common.kshlib
|
||||
|
||||
#
|
||||
# DESCRIPTION:
|
||||
# Check the zfs space files in .zfs directory
|
||||
#
|
||||
#
|
||||
# STRATEGY:
|
||||
# 1. set zfs userquota to a fs
|
||||
# 2. write some data to the fs with specified user and group
|
||||
# 3. use zfs space files in .zfs to check the result
|
||||
#
|
||||
|
||||
function cleanup
|
||||
{
|
||||
log_must cleanup_quota
|
||||
echo 0 >$SPACEFILES_ENABLED_PARAM || log_fail
|
||||
}
|
||||
|
||||
log_onexit cleanup
|
||||
|
||||
log_assert "Check the .zfs space files"
|
||||
|
||||
typeset userquota=104857600
|
||||
typeset groupquota=524288000
|
||||
|
||||
log_must zfs set userquota@$QUSER1=$userquota $QFS
|
||||
log_must zfs set groupquota@$QGROUP=$groupquota $QFS
|
||||
mkmount_writable $QFS
|
||||
log_must user_run $QUSER1 mkfile 50m $QFILE
|
||||
|
||||
typeset SPACEFILES_ENABLED_PARAM=/sys/module/zfs/parameters/zfs_ctldir_spacefiles
|
||||
echo 1 >$SPACEFILES_ENABLED_PARAM || log_fail
|
||||
typeset mntp=$(get_prop mountpoint $QFS)
|
||||
typeset user_id=$(id -u $QUSER1) || log_fail
|
||||
typeset group_id=$(id -g $QUSER1) || log_fail
|
||||
|
||||
sync_all_pools
|
||||
|
||||
log_must eval "grep \"^$user_id,[[:digit:]]*\" $mntp/.zfs/space/user"
|
||||
log_must eval "grep \"^$user_id,$userquota\" $mntp/.zfs/quota/user"
|
||||
log_must eval "grep \"^$group_id,[[:digit:]]*\" $mntp/.zfs/space/group"
|
||||
log_must eval "grep \"^$group_id,$groupquota\" $mntp/.zfs/quota/group"
|
||||
|
||||
log_pass "Check the .zfs space files"
|
||||
|
Loading…
Reference in New Issue