Skip to content

Add hierarchical project quota support #17341

New issue

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

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

Already on GitHub? Sign in to your account

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 33 additions & 1 deletion cmd/zfs/zfs_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8863,13 +8863,14 @@ zfs_do_project(int argc, char **argv)
.zpc_newline = B_TRUE,
.zpc_recursive = B_FALSE,
.zpc_set_flag = B_FALSE,
.zpc_add_hierarchy = B_FALSE,
};
int ret = 0, c;

if (argc < 2)
usage(B_FALSE);

while ((c = getopt(argc, argv, "0Ccdkp:rs")) != -1) {
while ((c = getopt(argc, argv, "0Ccdkp:rsY")) != -1) {
switch (c) {
case '0':
zpc.zpc_newline = B_FALSE;
Expand Down Expand Up @@ -8933,6 +8934,12 @@ zfs_do_project(int argc, char **argv)
zpc.zpc_set_flag = B_TRUE;
zpc.zpc_op = ZFS_PROJECT_OP_SET;
break;
case 'Y':
zpc.zpc_add_hierarchy = B_TRUE;
zpc.zpc_set_flag = B_TRUE;
zpc.zpc_recursive = B_TRUE;
zpc.zpc_dironly = B_FALSE;
break;
default:
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
optopt);
Expand All @@ -8959,13 +8966,25 @@ zfs_do_project(int argc, char **argv)
gettext("'-0' is only valid together with '-c'\n"));
usage(B_FALSE);
}
if (zpc.zpc_add_hierarchy) {
(void) fprintf(stderr,
gettext("'-Y' is only valid with set project "
"id\n"));
usage(B_FALSE);
}
break;
case ZFS_PROJECT_OP_CHECK:
if (zpc.zpc_keep_projid) {
(void) fprintf(stderr,
gettext("'-k' is only valid together with '-C'\n"));
usage(B_FALSE);
}
if (zpc.zpc_add_hierarchy) {
(void) fprintf(stderr,
gettext("'-Y' is only valid with set project "
"id\n"));
usage(B_FALSE);
}
break;
case ZFS_PROJECT_OP_CLEAR:
if (zpc.zpc_dironly) {
Expand All @@ -8983,6 +9002,12 @@ zfs_do_project(int argc, char **argv)
gettext("'-p' is useless together with '-C'\n"));
usage(B_FALSE);
}
if (zpc.zpc_add_hierarchy) {
(void) fprintf(stderr,
gettext("'-Y' is only valid with set project "
"id\n"));
usage(B_FALSE);
}
break;
case ZFS_PROJECT_OP_SET:
if (zpc.zpc_dironly) {
Expand All @@ -9001,6 +9026,13 @@ zfs_do_project(int argc, char **argv)
gettext("'-0' is only valid together with '-c'\n"));
usage(B_FALSE);
}
if (zpc.zpc_add_hierarchy && zpc.zpc_expected_projid ==
ZFS_INVALID_PROJID) {
(void) fprintf(stderr,
gettext("'-p' is needed for add hierarchy "
"option '-Y'\n"));
usage(B_FALSE);
}
break;
default:
ASSERT(0);
Expand Down
49 changes: 46 additions & 3 deletions cmd/zfs/zfs_project.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ zfs_project_sanity_check(const char *name, zfs_project_control_t *zpc,
"'-r' option on non-dir target %s\n"), name);
return (-1);
}
if (zpc->zpc_add_hierarchy) {
(void) fprintf(stderr, gettext(
"'-Y' option on non-dir target %s\n"), name);
return (-1);
}
}

return (0);
Expand Down Expand Up @@ -118,6 +123,30 @@ zfs_project_load_projid(const char *name, zfs_project_control_t *zpc)
return (ret);
}

static int
zfs_project_add_hierarchy(const char *name, zfs_project_control_t *zpc)
{
project_hierarchy_arg_t pharg;
int ret, fd;

fd = open(name, O_RDONLY | O_NOCTTY);
if (fd < 0) {
(void) fprintf(stderr, gettext("failed to open %s: %s\n"),
name, strerror(errno));
return (fd);
}
pharg.pha_projid = zpc->zpc_expected_projid;

ret = ioctl(fd, FS_IOC_ADD_PROJECT_HIERARCHY, &pharg);
if (ret)
(void) fprintf(stderr,
gettext("failed to add project hierarchy for %s: %s\n"),
name, strerror(errno));

close(fd);
return (ret);
}

static int
zfs_project_handle_one(const char *name, zfs_project_control_t *zpc)
{
Expand Down Expand Up @@ -194,10 +223,12 @@ zfs_project_handle_one(const char *name, zfs_project_control_t *zpc)
}

ret = ioctl(fd, ZFS_IOC_FSSETXATTR, &fsx);
if (ret)
if (ret && errno != EXDEV) {
(void) fprintf(stderr,
gettext("failed to set xattr for %s: %s\n"),
name, strerror(errno));
}


out:
close(fd);
Expand Down Expand Up @@ -247,11 +278,13 @@ zfs_project_handle_dir(const char *name, zfs_project_control_t *zpc,
ret = zfs_project_handle_one(fullname, zpc);
if (!ret && zpc->zpc_recursive && ent->d_type == DT_DIR)
zfs_project_item_alloc(head, fullname);
if (ret && errno == EXDEV)
ret = 0;

free(fullname);
}

if (errno && !ret) {
if (errno && !ret && errno != EXDEV) {
ret = -errno;
(void) fprintf(stderr, gettext("failed to readdir %s: %s\n"),
name, strerror(errno));
Expand All @@ -273,6 +306,11 @@ zfs_project_handle(const char *name, zfs_project_control_t *zpc)
if (ret)
return (ret);

if (zpc->zpc_op == ZFS_PROJECT_OP_SET && zpc->zpc_add_hierarchy) {
ret = zfs_project_add_hierarchy(name, zpc);
if (ret)
return (ret);
}
if ((zpc->zpc_op == ZFS_PROJECT_OP_SET ||
zpc->zpc_op == ZFS_PROJECT_OP_CHECK) &&
zpc->zpc_expected_projid == ZFS_INVALID_PROJID) {
Expand All @@ -286,15 +324,20 @@ zfs_project_handle(const char *name, zfs_project_control_t *zpc)
if (ret || !S_ISDIR(st.st_mode) || zpc->zpc_dironly ||
(!zpc->zpc_recursive &&
zpc->zpc_op != ZFS_PROJECT_OP_LIST &&
zpc->zpc_op != ZFS_PROJECT_OP_CHECK))
zpc->zpc_op != ZFS_PROJECT_OP_CHECK)) {
if (ret && errno == EXDEV)
ret = 0;
return (ret);
}

list_create(&head, sizeof (zfs_project_item_t),
offsetof(zfs_project_item_t, zpi_list));
zfs_project_item_alloc(&head, name);
while ((zpi = list_remove_head(&head)) != NULL) {
if (!ret)
ret = zfs_project_handle_dir(zpi->zpi_name, zpc, &head);
if (ret && errno == EXDEV)
ret = 0;
free(zpi);
}

Expand Down
1 change: 1 addition & 0 deletions cmd/zfs/zfs_projectutil.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ typedef struct zfs_project_control {
boolean_t zpc_newline;
boolean_t zpc_recursive;
boolean_t zpc_set_flag;
boolean_t zpc_add_hierarchy;
} zfs_project_control_t;

int zfs_project_handle(const char *name, zfs_project_control_t *zpc);
Expand Down
1 change: 1 addition & 0 deletions include/os/freebsd/zfs/sys/zfs_vfsops_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ struct zfsvfs {
uint64_t z_userobjquota_obj;
uint64_t z_groupobjquota_obj;
uint64_t z_projectquota_obj;
uint64_t z_projecthierarchy_obj;
uint64_t z_projectobjquota_obj;
uint64_t z_defaultuserquota;
uint64_t z_defaultgroupquota;
Expand Down
1 change: 1 addition & 0 deletions include/os/linux/zfs/sys/zfs_vfsops_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ struct zfsvfs {
uint64_t z_userobjquota_obj;
uint64_t z_groupobjquota_obj;
uint64_t z_projectquota_obj;
uint64_t z_projecthierarchy_obj;
uint64_t z_projectobjquota_obj;
uint64_t z_defaultuserquota;
uint64_t z_defaultgroupquota;
Expand Down
1 change: 1 addition & 0 deletions include/os/linux/zfs/sys/zfs_vnops_os.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ extern int zfs_getattr_fast(zidmap_t *, struct inode *ip, struct kstat *sp);
#endif
extern int zfs_setattr(znode_t *zp, vattr_t *vap, int flag, cred_t *cr,
zidmap_t *mnt_ns);
extern int zfs_setattr_xattr_dir(znode_t *zp);
extern int zfs_rename(znode_t *sdzp, char *snm, znode_t *tdzp,
char *tnm, cred_t *cr, int flags, uint64_t rflags, vattr_t *wo_vap,
zidmap_t *mnt_ns);
Expand Down
8 changes: 8 additions & 0 deletions include/sys/dmu_objset.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,12 @@ struct objset {
dmu_objset_upgrade_cb_t os_upgrade_cb;
boolean_t os_upgrade_exit;
int os_upgrade_status;

kmutex_t os_projecthierarchyused_lock;
kmutex_t os_projecthierarchyop_lock;

avl_tree_t os_project_deltas[TXG_SIZE];
uint64_t os_projecthierarchy_obj;
};

#define DMU_META_OBJSET 0
Expand Down Expand Up @@ -268,6 +274,8 @@ int dmu_fsname(const char *snapname, char *buf);

void dmu_objset_evict_done(objset_t *os);
void dmu_objset_willuse_space(objset_t *os, int64_t space, dmu_tx_t *tx);
void do_projectusage_update(objset_t *os, uint64_t projid, int64_t used,
dmu_tx_t *tx);

void dmu_objset_init(void);
void dmu_objset_fini(void);
Expand Down
5 changes: 5 additions & 0 deletions include/sys/fs/zfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1993,6 +1993,11 @@ enum zio_encrypt {
ZFS_XA_NS_PREFIX_MATCH(LINUX_TRUSTED, name) || \
ZFS_XA_NS_PREFIX_MATCH(LINUX_USER, name))

typedef struct project_hierarchy_arg {
uint64_t pha_projid;
} project_hierarchy_arg_t;
#define FS_IOC_ADD_PROJECT_HIERARCHY _IOW('t', 9, project_hierarchy_arg_t)

#ifdef __cplusplus
}
#endif
Expand Down
5 changes: 5 additions & 0 deletions include/sys/zfs_quota.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ extern int zfs_userspace_many(struct zfsvfs *, zfs_userquota_prop_t,
uint64_t *, void *, uint64_t *, uint64_t *);
extern int zfs_set_userquota(struct zfsvfs *, zfs_userquota_prop_t,
const char *, uint64_t, uint64_t);
extern int zfs_project_hierarchy_add(struct zfsvfs *, uint64_t, uint64_t);
extern int zfs_project_hierarchy_remove(struct zfsvfs *, uint64_t, uint64_t,
boolean_t);
extern int zfs_projects_are_hierarchical(struct zfsvfs *, uint64_t, uint64_t,
boolean_t *);

extern boolean_t zfs_id_overobjquota(struct zfsvfs *, uint64_t, uint64_t);
extern boolean_t zfs_id_overblockquota(struct zfsvfs *, uint64_t, uint64_t);
Expand Down
1 change: 1 addition & 0 deletions include/sys/zfs_znode.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ extern "C" {
#define ZFS_FUID_TABLES "FUID"
#define ZFS_SHARES_DIR "SHARES"
#define ZFS_SA_ATTRS "SA_ATTRS"
#define ZFS_PROJECT_HIERARCHY "PROJECT_HIERARCHY"

/*
* Convert mode bits (zp_mode) to BSD-style DT_* values for storing in
Expand Down
7 changes: 7 additions & 0 deletions module/os/freebsd/zfs/zfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,13 @@ zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os)
else if (error != 0)
return (error);

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

error = zap_lookup(os, MASTER_NODE_OBJ,
zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA],
8, 1, &zfsvfs->z_userobjquota_obj);
Expand Down
8 changes: 8 additions & 0 deletions module/os/linux/zfs/zfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,14 @@ zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os)
else if (error != 0)
return (error);

error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_PROJECT_HIERARCHY,
8, 1, &zfsvfs->z_projecthierarchy_obj);
if (error == ENOENT)
zfsvfs->z_projecthierarchy_obj = 0;
else if (error != 0)
return (error);
zfsvfs->z_os->os_projecthierarchy_obj = zfsvfs->z_projecthierarchy_obj;

error = zap_lookup(os, MASTER_NODE_OBJ,
zfs_userquota_prop_prefixes[ZFS_PROP_USEROBJQUOTA],
8, 1, &zfsvfs->z_userobjquota_obj);
Expand Down
44 changes: 44 additions & 0 deletions module/os/linux/zfs/zfs_vnops_os.c
Original file line number Diff line number Diff line change
Expand Up @@ -2640,6 +2640,50 @@ zfs_setattr(znode_t *zp, vattr_t *vap, int flags, cred_t *cr, zidmap_t *mnt_ns)
return (err);
}

/*
* This function sets projid of zp on its xattr dir and each xattr file
*/
int
zfs_setattr_xattr_dir(znode_t *zp)
{
zfsvfs_t *zfsvfs = ZTOZSB(zp);
znode_t *xzp = NULL;
uint64_t xattr_obj = 0;
xoptattr_t *xoap;
xvattr_t xva;
int err;

err = sa_lookup(zp->z_sa_hdl, SA_ZPL_XATTR(zfsvfs),
&xattr_obj, sizeof (xattr_obj));
if (err || !xattr_obj)
return (err);

err = zfs_zget(zfsvfs, xattr_obj, &xzp);
ASSERT(err == 0);
xva_init(&xva);
xoap = xva_getxoptattr(&xva);
XVA_SET_REQ(&xva, XAT_PROJID);
xoap->xoa_projid = zp->z_projid;
XVA_SET_REQ(&xva, XAT_PROJINHERIT);
xoap->xoa_projinherit = 1;

cred_t *cr = CRED();
crhold(cr);
err = zfs_setattr(xzp, (vattr_t *)&xva, 0, cr, zfs_init_idmap);
crfree(cr);
if (err) {
zrele(xzp);
return (err);
}
err = zfs_setattr_dir(xzp);
if (err) {
zrele(xzp);
return (err);
}
zrele(xzp);
return (0);
}

typedef struct zfs_zlock {
krwlock_t *zl_rwlock; /* lock we acquired */
znode_t *zl_znode; /* znode we held */
Expand Down
Loading
Loading