2012-08-27 08:50:44

by Jan Kara

[permalink] [raw]
Subject: Re: [REVIEW][PATCH 13/15] userns: Add basic quota support

Hello,

On Sat 25-08-12 17:05:35, Eric W. Biederman wrote:
> Two helper are added dqgetusr and dqgetgrp to allow the quota
> infrastructure to be called with a kuid and a kgid respectively. This
> creates type safe variants of dqget and leads to shorter more
> comprehensible code.
It would look more comprehensible to me to have functions like:
kuid2qown() and kgid2qown() and then call dqget(sb, kuid2qown(attr->uid))
(see below for qown_t change proposal). The code then at the first look
explains what is going on... Hmm?

> Place the USRQUOTA and GRPQUOTA defines into enum quota_type. This
> brings with it the ability for the compiler to check that switch
> statements handle every quota type, and the ability to mark which
> values store the type of a quota entry.
OK, makes sense.

> Add the data type qown_t a union of kuid_t and kgid_t. qown_t is a
> replacement for the implicit union of uid and gid stored in an
> unsigned int that is was used in the quota data structures. Making
> the data type explicit allows the kuid_t and kgid_t type safety to
> propogate more thoroughly through the code, revealing more places
> where uid/gid conversions need be made.
Hum, when we already do this, wouldn't it make more sense to embed quota
type in qown_t? Because with the union thing you have no meaningful way of
accessing that type without having quota type anyway. So having that in a
single structure makes a lot of sense, plus it makes prototypes shorter...
And you have to call make_qown() anyway...

> Allong with the data type qown_t comes the helper functions
> qown_eq, from_qown, from_qown_munged, qown_valid, and make_qown.
>
> Update struct dquot dq_id to be a qown_t.
>
> Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
> and dquot_set_dqblk to use enum quota_type and qown_t.
>
> Make minimal changes to gfs2, ocfs2, and xfs to deal with the change
> in quota structures and signatures. The ocfs2 changes are larger than
> most because of the extensive tracing throughout the ocfs2 quota code
> that prints out dq_id.
Otherwise the changes look OK to me, although I didn't check them in
detail yet (as above suggestions will change the code anyway).

Honza
>
> Cc: Steven Whitehouse <[email protected]>
> Cc: [email protected]
> Cc: Mark Fasheh <[email protected]>
> Cc: Joel Becker <[email protected]>
> Cc: [email protected]
> Cc: Ben Myers <[email protected]>
> Cc: Alex Elder <[email protected]>
> Cc: [email protected]
> Cc: Jan Kara <[email protected]>
> Cc: Dmitry Monakhov <[email protected]>
> Signed-off-by: Eric W. Biederman <[email protected]>
> ---
> fs/gfs2/quota.c | 44 ++++++++++++++---------
> fs/ocfs2/file.c | 6 +--
> fs/ocfs2/quota_global.c | 34 +++++++++++++-----
> fs/ocfs2/quota_local.c | 12 +++++--
> fs/quota/dquot.c | 43 ++++++++++++----------
> fs/quota/netlink.c | 11 ++++--
> fs/quota/quota.c | 44 +++++++++++++++-------
> fs/quota/quota_tree.c | 20 +++++++---
> fs/quota/quota_v1.c | 8 +++--
> fs/quota/quota_v2.c | 14 +++++--
> fs/xfs/xfs_quotaops.c | 18 ++++++----
> fs/xfs/xfs_trans_dquot.c | 8 +++--
> include/linux/quota.h | 91 +++++++++++++++++++++++++++++++++++++++++++---
> include/linux/quotaops.h | 18 +++++++--
> init/Kconfig | 2 -
> 15 files changed, 270 insertions(+), 103 deletions(-)
>
> diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
> index a3bde91..858f052 100644
> --- a/fs/gfs2/quota.c
> +++ b/fs/gfs2/quota.c
> @@ -1057,6 +1057,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
> return 0;
>
> for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
> + enum quota_type qtype;
> + qown_t qown;
> qd = ip->i_res->rs_qa_qd[x];
>
> if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) ||
> @@ -1068,10 +1070,11 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
> value += qd->qd_change;
> spin_unlock(&qd_lru_lock);
>
> + qtype = test_bit(QDF_USER, &qd->qd_flags) ? USRQUOTA : GRPQUOTA;
> + qown = make_qown(&init_user_ns, qtype, qd->qd_id);
> if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
> print_message(qd, "exceeded");
> - quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
> - USRQUOTA : GRPQUOTA, qd->qd_id,
> + quota_send_warning(qtype, qown,
> sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
>
> error = -EDQUOT;
> @@ -1081,8 +1084,7 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
> time_after_eq(jiffies, qd->qd_last_warn +
> gfs2_tune_get(sdp,
> gt_quota_warn_period) * HZ)) {
> - quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
> - USRQUOTA : GRPQUOTA, qd->qd_id,
> + quota_send_warning(qtype, qown,
> sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN);
> error = print_message(qd, "warning");
> qd->qd_last_warn = jiffies;
> @@ -1469,28 +1471,32 @@ static int gfs2_quota_get_xstate(struct super_block *sb,
> return 0;
> }
>
> -static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
> - struct fs_disk_quota *fdq)
> +static int gfs2_get_dqblk(struct super_block *sb, enum quota_type type,
> + qown_t id, struct fs_disk_quota *fdq)
> {
> struct gfs2_sbd *sdp = sb->s_fs_info;
> struct gfs2_quota_lvb *qlvb;
> struct gfs2_quota_data *qd;
> struct gfs2_holder q_gh;
> int error;
> + int user;
> + u32 gfs_id;
>
> memset(fdq, 0, sizeof(struct fs_disk_quota));
>
> if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
> return -ESRCH; /* Crazy XFS error code */
>
> + gfs_id = from_qown(&init_user_ns, type, id);
> +
> if (type == USRQUOTA)
> - type = QUOTA_USER;
> + user = QUOTA_USER;
> else if (type == GRPQUOTA)
> - type = QUOTA_GROUP;
> + user = QUOTA_GROUP;
> else
> return -EINVAL;
>
> - error = qd_get(sdp, type, id, &qd);
> + error = qd_get(sdp, user, gfs_id, &qd);
> if (error)
> return error;
> error = do_glock(qd, FORCE, &q_gh);
> @@ -1499,8 +1505,8 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
>
> qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
> fdq->d_version = FS_DQUOT_VERSION;
> - fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
> - fdq->d_id = id;
> + fdq->d_flags = (user == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
> + fdq->d_id = gfs_id;
> fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift;
> fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift;
> fdq->d_bcount = be64_to_cpu(qlvb->qb_value) << sdp->sd_fsb2bb_shift;
> @@ -1514,8 +1520,8 @@ out:
> /* GFS2 only supports a subset of the XFS fields */
> #define GFS2_FIELDMASK (FS_DQ_BSOFT|FS_DQ_BHARD|FS_DQ_BCOUNT)
>
> -static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
> - struct fs_disk_quota *fdq)
> +static int gfs2_set_dqblk(struct super_block *sb, enum quota_type type,
> + qown_t id, struct fs_disk_quota *fdq)
> {
> struct gfs2_sbd *sdp = sb->s_fs_info;
> struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
> @@ -1526,18 +1532,22 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
> int alloc_required;
> loff_t offset;
> int error;
> + int user;
> + u32 gfs_id;
>
> if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
> return -ESRCH; /* Crazy XFS error code */
>
> + gfs_id = from_qown(&init_user_ns, type, id);
> +
> switch(type) {
> case USRQUOTA:
> - type = QUOTA_USER;
> + user = QUOTA_USER;
> if (fdq->d_flags != FS_USER_QUOTA)
> return -EINVAL;
> break;
> case GRPQUOTA:
> - type = QUOTA_GROUP;
> + user = QUOTA_GROUP;
> if (fdq->d_flags != FS_GROUP_QUOTA)
> return -EINVAL;
> break;
> @@ -1547,10 +1557,10 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
>
> if (fdq->d_fieldmask & ~GFS2_FIELDMASK)
> return -EINVAL;
> - if (fdq->d_id != id)
> + if (fdq->d_id != gfs_id)
> return -EINVAL;
>
> - error = qd_get(sdp, type, id, &qd);
> + error = qd_get(sdp, user, gfs_id, &qd);
> if (error)
> return error;
>
> diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
> index 46a1f6d..063e889 100644
> --- a/fs/ocfs2/file.c
> +++ b/fs/ocfs2/file.c
> @@ -1184,8 +1184,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
> if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
> && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
> OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
> - transfer_to[USRQUOTA] = dqget(sb, attr->ia_uid,
> - USRQUOTA);
> + transfer_to[USRQUOTA] = dqgetusr(sb, attr->ia_uid);
> if (!transfer_to[USRQUOTA]) {
> status = -ESRCH;
> goto bail_unlock;
> @@ -1194,8 +1193,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
> if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
> && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
> OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
> - transfer_to[GRPQUOTA] = dqget(sb, attr->ia_gid,
> - GRPQUOTA);
> + transfer_to[GRPQUOTA] = dqgetgrp(sb, attr->ia_gid);
> if (!transfer_to[GRPQUOTA]) {
> status = -ESRCH;
> goto bail_unlock;
> diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
> index 0a86e30..88360f1 100644
> --- a/fs/ocfs2/quota_global.c
> +++ b/fs/ocfs2/quota_global.c
> @@ -95,7 +95,8 @@ static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
> struct ocfs2_global_disk_dqblk *d = dp;
> struct mem_dqblk *m = &dquot->dq_dqb;
>
> - d->dqb_id = cpu_to_le32(dquot->dq_id);
> + d->dqb_id = cpu_to_le32(from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id));
> d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
> d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
> d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
> @@ -113,10 +114,13 @@ static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
> struct ocfs2_global_disk_dqblk *d = dp;
> struct ocfs2_mem_dqinfo *oinfo =
> sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> + qown_t qown;
>
> if (qtree_entry_unused(&oinfo->dqi_gi, dp))
> return 0;
> - return le32_to_cpu(d->dqb_id) == dquot->dq_id;
> +
> + qown = make_qown(&init_user_ns, dquot->dq_type, le32_to_cpu(d->dqb_id));
> + return qown_eq(qown, dquot->dq_id, dquot->dq_type);
> }
>
> struct qtree_fmt_operations ocfs2_global_ops = {
> @@ -504,7 +508,9 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
> olditime = dquot->dq_dqb.dqb_itime;
> oldbtime = dquot->dq_dqb.dqb_btime;
> ocfs2_global_disk2memdqb(dquot, &dqblk);
> - trace_ocfs2_sync_dquot(dquot->dq_id, dquot->dq_dqb.dqb_curspace,
> + trace_ocfs2_sync_dquot(from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id),
> + dquot->dq_dqb.dqb_curspace,
> (long long)spacechange,
> dquot->dq_dqb.dqb_curinodes,
> (long long)inodechange);
> @@ -556,7 +562,8 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
> if (err < 0) {
> mlog(ML_ERROR, "Failed to lock quota info, losing quota write"
> " (type=%d, id=%u)\n", dquot->dq_type,
> - (unsigned)dquot->dq_id);
> + (unsigned)from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id));
> goto out;
> }
> if (freeing)
> @@ -591,7 +598,9 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
> struct ocfs2_super *osb = OCFS2_SB(sb);
> int status = 0;
>
> - trace_ocfs2_sync_dquot_helper(dquot->dq_id, dquot->dq_type,
> + trace_ocfs2_sync_dquot_helper(from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id),
> + dquot->dq_type,
> type, sb->s_id);
> if (type != dquot->dq_type)
> goto out;
> @@ -643,7 +652,9 @@ static int ocfs2_write_dquot(struct dquot *dquot)
> struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> int status = 0;
>
> - trace_ocfs2_write_dquot(dquot->dq_id, dquot->dq_type);
> + trace_ocfs2_write_dquot(from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id),
> + dquot->dq_type);
>
> handle = ocfs2_start_trans(osb, OCFS2_QWRITE_CREDITS);
> if (IS_ERR(handle)) {
> @@ -681,7 +692,9 @@ static int ocfs2_release_dquot(struct dquot *dquot)
> struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
> int status = 0;
>
> - trace_ocfs2_release_dquot(dquot->dq_id, dquot->dq_type);
> + trace_ocfs2_release_dquot(from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id),
> + dquot->dq_type);
>
> mutex_lock(&dquot->dq_lock);
> /* Check whether we are not racing with some other dqget() */
> @@ -739,7 +752,8 @@ static int ocfs2_acquire_dquot(struct dquot *dquot)
> int need_alloc = ocfs2_global_qinit_alloc(sb, type);
> handle_t *handle;
>
> - trace_ocfs2_acquire_dquot(dquot->dq_id, type);
> + trace_ocfs2_acquire_dquot(from_qown(&init_user_ns, type, dquot->dq_id),
> + type);
> mutex_lock(&dquot->dq_lock);
> /*
> * We need an exclusive lock, because we're going to update use count
> @@ -826,7 +840,9 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
> handle_t *handle;
> struct ocfs2_super *osb = OCFS2_SB(sb);
>
> - trace_ocfs2_mark_dquot_dirty(dquot->dq_id, type);
> + trace_ocfs2_mark_dquot_dirty(from_qown(&init_user_ns, type,
> + dquot->dq_id),
> + type);
>
> /* In case user set some limits, sync dquot immediately to global
> * quota file so that information propagates quicker */
> diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
> index f100bf7..a80936e 100644
> --- a/fs/ocfs2/quota_local.c
> +++ b/fs/ocfs2/quota_local.c
> @@ -501,7 +501,10 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
> }
> dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
> ol_dqblk_block_off(sb, chunk, bit));
> - dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
> + dquot = dqget(sb,
> + make_qown(&init_user_ns, type,
> + le64_to_cpu(dqblk->dqb_id)),
> + type);
> if (!dquot) {
> status = -EIO;
> mlog(ML_ERROR, "Failed to get quota structure "
> @@ -881,7 +884,9 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
> dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data
> + ol_dqblk_block_offset(sb, od->dq_local_off));
>
> - dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id);
> + dqblk->dqb_id = cpu_to_le64(from_qown(&init_user_ns,
> + od->dq_dquot.dq_type,
> + od->dq_dquot.dq_id));
> spin_lock(&dq_data_lock);
> dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
> od->dq_origspace);
> @@ -891,7 +896,8 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
> trace_olq_set_dquot(
> (unsigned long long)le64_to_cpu(dqblk->dqb_spacemod),
> (unsigned long long)le64_to_cpu(dqblk->dqb_inodemod),
> - od->dq_dquot.dq_id);
> + from_qown(&init_user_ns, od->dq_dquot.dq_type,
> + od->dq_dquot.dq_id));
> }
>
> /* Write dquot to local quota file */
> diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
> index 36a29b7..5fbd1d9a 100644
> --- a/fs/quota/dquot.c
> +++ b/fs/quota/dquot.c
> @@ -253,10 +253,12 @@ static qsize_t inode_get_rsv_space(struct inode *inode);
> static void __dquot_initialize(struct inode *inode, int type);
>
> static inline unsigned int
> -hashfn(const struct super_block *sb, unsigned int id, int type)
> +hashfn(const struct super_block *sb, qown_t qown, enum quota_type type)
> {
> + unsigned int id;
> unsigned long tmp;
>
> + id = from_qown(&init_user_ns, type, qown);
> tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
> return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
> }
> @@ -277,15 +279,15 @@ static inline void remove_dquot_hash(struct dquot *dquot)
> }
>
> static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb,
> - unsigned int id, int type)
> + qown_t id, enum quota_type type)
> {
> struct hlist_node *node;
> struct dquot *dquot;
>
> hlist_for_each (node, dquot_hash+hashent) {
> dquot = hlist_entry(node, struct dquot, dq_hash);
> - if (dquot->dq_sb == sb && dquot->dq_id == id &&
> - dquot->dq_type == type)
> + if (dquot->dq_sb == sb && dquot->dq_type == type &&
> + qown_eq(dquot->dq_id, id, type))
> return dquot;
> }
> return NULL;
> @@ -741,7 +743,9 @@ void dqput(struct dquot *dquot)
> #ifdef CONFIG_QUOTA_DEBUG
> if (!atomic_read(&dquot->dq_count)) {
> quota_error(dquot->dq_sb, "trying to free free dquot of %s %d",
> - quotatypes[dquot->dq_type], dquot->dq_id);
> + quotatypes[dquot->dq_type],
> + from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id));
> BUG();
> }
> #endif
> @@ -829,7 +833,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
> * a) checking for quota flags under dq_list_lock and
> * b) getting a reference to dquot before we release dq_list_lock
> */
> -struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
> +struct dquot *dqget(struct super_block *sb, qown_t id, enum quota_type type)
> {
> unsigned int hashent = hashfn(sb, id, type);
> struct dquot *dquot = NULL, *empty = NULL;
> @@ -1129,7 +1133,7 @@ static void dquot_decr_space(struct dquot *dquot, qsize_t number)
>
> struct dquot_warn {
> struct super_block *w_sb;
> - qid_t w_dq_id;
> + qown_t w_dq_id;
> short w_dq_type;
> short w_type;
> };
> @@ -1156,9 +1160,9 @@ static int need_print_warning(struct dquot_warn *warn)
>
> switch (warn->w_dq_type) {
> case USRQUOTA:
> - return current_fsuid() == warn->w_dq_id;
> + return uid_eq(current_fsuid(), warn->w_dq_id.uid);
> case GRPQUOTA:
> - return in_group_p(warn->w_dq_id);
> + return in_group_p(warn->w_dq_id.gid);
> }
> return 0;
> }
> @@ -1390,7 +1394,6 @@ static int dquot_active(const struct inode *inode)
> */
> static void __dquot_initialize(struct inode *inode, int type)
> {
> - unsigned int id = 0;
> int cnt;
> struct dquot *got[MAXQUOTAS];
> struct super_block *sb = inode->i_sb;
> @@ -1403,15 +1406,16 @@ static void __dquot_initialize(struct inode *inode, int type)
>
> /* First get references to structures we might need. */
> for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
> + qown_t id;
> got[cnt] = NULL;
> if (type != -1 && cnt != type)
> continue;
> switch (cnt) {
> case USRQUOTA:
> - id = inode->i_uid;
> + id.uid = inode->i_uid;
> break;
> case GRPQUOTA:
> - id = inode->i_gid;
> + id.gid = inode->i_gid;
> break;
> }
> got[cnt] = dqget(sb, id, cnt);
> @@ -1897,10 +1901,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
> if (!dquot_active(inode))
> return 0;
>
> - if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid)
> - transfer_to[USRQUOTA] = dqget(sb, iattr->ia_uid, USRQUOTA);
> - if (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)
> - transfer_to[GRPQUOTA] = dqget(sb, iattr->ia_gid, GRPQUOTA);
> + if (iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid))
> + transfer_to[USRQUOTA] = dqgetusr(sb, iattr->ia_uid);
> + if (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))
> + transfer_to[GRPQUOTA] = dqgetgrp(sb, iattr->ia_gid);
>
> ret = __dquot_transfer(inode, transfer_to);
> dqput_all(transfer_to);
> @@ -2362,7 +2366,8 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
> di->d_version = FS_DQUOT_VERSION;
> di->d_flags = dquot->dq_type == USRQUOTA ?
> FS_USER_QUOTA : FS_GROUP_QUOTA;
> - di->d_id = dquot->dq_id;
> + di->d_id = from_qown_munged(current_user_ns(), dquot->dq_type,
> + dquot->dq_id);
>
> spin_lock(&dq_data_lock);
> di->d_blk_hardlimit = stoqb(dm->dqb_bhardlimit);
> @@ -2376,7 +2381,7 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
> spin_unlock(&dq_data_lock);
> }
>
> -int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
> +int dquot_get_dqblk(struct super_block *sb, enum quota_type type, qown_t id,
> struct fs_disk_quota *di)
> {
> struct dquot *dquot;
> @@ -2488,7 +2493,7 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
> return 0;
> }
>
> -int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
> +int dquot_set_dqblk(struct super_block *sb, enum quota_type type, qown_t id,
> struct fs_disk_quota *di)
> {
> struct dquot *dquot;
> diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
> index d67908b..b2678da 100644
> --- a/fs/quota/netlink.c
> +++ b/fs/quota/netlink.c
> @@ -30,7 +30,7 @@ static struct genl_family quota_genl_family = {
> *
> */
>
> -void quota_send_warning(short type, unsigned int id, dev_t dev,
> +void quota_send_warning(enum quota_type type, qown_t id, dev_t dev,
> const char warntype)
> {
> static atomic_t seq;
> @@ -59,7 +59,8 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
> ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type);
> if (ret)
> goto attr_err_out;
> - ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id);
> + ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID,
> + from_qown_munged(&init_user_ns, type, id));
> if (ret)
> goto attr_err_out;
> ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
> @@ -71,7 +72,11 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
> ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev));
> if (ret)
> goto attr_err_out;
> - ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
> + /* Report the current user as seen by the filesystem that issues
> + * quota warning.
> + */
> + ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID,
> + from_kuid_munged(&init_user_ns, current_uid()));
> if (ret)
> goto attr_err_out;
> genlmsg_end(skb, msg_head);
> diff --git a/fs/quota/quota.c b/fs/quota/quota.c
> index 6f15578..b9f44b7 100644
> --- a/fs/quota/quota.c
> +++ b/fs/quota/quota.c
> @@ -32,8 +32,8 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
> /* allow to query information for dquots we "own" */
> case Q_GETQUOTA:
> case Q_XGETQUOTA:
> - if ((type == USRQUOTA && current_euid() == id) ||
> - (type == GRPQUOTA && in_egroup_p(id)))
> + if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
> + (type == GRPQUOTA && in_egroup_p(make_kgid(current_user_ns(), id))))
> break;
> /*FALLTHROUGH*/
> default:
> @@ -62,7 +62,7 @@ static int quota_sync_all(int type)
> return ret;
> }
>
> -static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
> +static int quota_quotaon(struct super_block *sb, enum quota_type type, int cmd, qid_t id,
> struct path *path)
> {
> if (!sb->s_qcop->quota_on && !sb->s_qcop->quota_on_meta)
> @@ -127,16 +127,20 @@ static void copy_to_if_dqblk(struct if_dqblk *dst, struct fs_disk_quota *src)
> dst->dqb_valid = QIF_ALL;
> }
>
> -static int quota_getquota(struct super_block *sb, int type, qid_t id,
> - void __user *addr)
> +static int quota_getquota(struct super_block *sb, enum quota_type type,
> + qid_t id, void __user *addr)
> {
> + qown_t qown;
> struct fs_disk_quota fdq;
> struct if_dqblk idq;
> int ret;
>
> if (!sb->s_qcop->get_dqblk)
> return -ENOSYS;
> - ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
> + qown = make_qown(current_user_ns(), type, id);
> + if (qown_valid(type, qown))
> + return -EINVAL;
> + ret = sb->s_qcop->get_dqblk(sb, type, qown, &fdq);
> if (ret)
> return ret;
> copy_to_if_dqblk(&idq, &fdq);
> @@ -171,18 +175,22 @@ static void copy_from_if_dqblk(struct fs_disk_quota *dst, struct if_dqblk *src)
> dst->d_fieldmask |= FS_DQ_ITIMER;
> }
>
> -static int quota_setquota(struct super_block *sb, int type, qid_t id,
> - void __user *addr)
> +static int quota_setquota(struct super_block *sb, enum quota_type type,
> + qid_t id, void __user *addr)
> {
> struct fs_disk_quota fdq;
> struct if_dqblk idq;
> + qown_t qown;
>
> if (copy_from_user(&idq, addr, sizeof(idq)))
> return -EFAULT;
> if (!sb->s_qcop->set_dqblk)
> return -ENOSYS;
> + qown = make_qown(current_user_ns(), type, id);
> + if (!qown_valid(type, qown))
> + return -EINVAL;
> copy_from_if_dqblk(&fdq, &idq);
> - return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
> + return sb->s_qcop->set_dqblk(sb, type, qown, &fdq);
> }
>
> static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr)
> @@ -209,27 +217,35 @@ static int quota_getxstate(struct super_block *sb, void __user *addr)
> return ret;
> }
>
> -static int quota_setxquota(struct super_block *sb, int type, qid_t id,
> +static int quota_setxquota(struct super_block *sb, enum quota_type type, qid_t id,
> void __user *addr)
> {
> struct fs_disk_quota fdq;
> + qown_t qown;
>
> if (copy_from_user(&fdq, addr, sizeof(fdq)))
> return -EFAULT;
> if (!sb->s_qcop->set_dqblk)
> return -ENOSYS;
> - return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
> + qown = make_qown(current_user_ns(), type, id);
> + if (!qown_valid(type, qown))
> + return -EINVAL;
> + return sb->s_qcop->set_dqblk(sb, type, qown, &fdq);
> }
>
> -static int quota_getxquota(struct super_block *sb, int type, qid_t id,
> - void __user *addr)
> +static int quota_getxquota(struct super_block *sb, enum quota_type type,
> + qid_t id, void __user *addr)
> {
> struct fs_disk_quota fdq;
> + qown_t qown;
> int ret;
>
> if (!sb->s_qcop->get_dqblk)
> return -ENOSYS;
> - ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
> + qown = make_qown(current_user_ns(), type, id);
> + if (!qown_valid(type, qown))
> + return -EINVAL;
> + ret = sb->s_qcop->get_dqblk(sb, type, qown, &fdq);
> if (!ret && copy_to_user(addr, &fdq, sizeof(fdq)))
> return -EFAULT;
> return ret;
> diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
> index e41c1becf..4704619 100644
> --- a/fs/quota/quota_tree.c
> +++ b/fs/quota/quota_tree.c
> @@ -22,10 +22,12 @@ MODULE_LICENSE("GPL");
>
> #define __QUOTA_QT_PARANOIA
>
> -static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
> +static int get_index(struct qtree_mem_dqinfo *info, qown_t qown, int depth)
> {
> unsigned int epb = info->dqi_usable_bs >> 2;
> + qid_t id;
>
> + id = from_qown(&init_user_ns, info->dqi_type, qown);
> depth = info->dqi_qtree_depth - depth - 1;
> while (depth--)
> id /= epb;
> @@ -538,8 +540,10 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
> ddquot += info->dqi_entry_size;
> }
> if (i == qtree_dqstr_in_blk(info)) {
> - quota_error(dquot->dq_sb, "Quota for id %u referenced "
> - "but not present", dquot->dq_id);
> + quota_error(dquot->dq_sb,
> + "Quota for id %u referenced but not present",
> + from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id));
> ret = -EIO;
> goto out_buf;
> } else {
> @@ -607,8 +611,11 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
> offset = find_dqentry(info, dquot);
> if (offset <= 0) { /* Entry not present? */
> if (offset < 0)
> - quota_error(sb, "Can't read quota structure "
> - "for id %u", dquot->dq_id);
> + quota_error(sb,"Can't read quota structure "
> + "for id %u",
> + from_qown(&init_user_ns,
> + dquot->dq_type,
> + dquot->dq_id));
> dquot->dq_off = 0;
> set_bit(DQ_FAKE_B, &dquot->dq_flags);
> memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
> @@ -626,7 +633,8 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
> if (ret >= 0)
> ret = -EIO;
> quota_error(sb, "Error while reading quota structure for id %u",
> - dquot->dq_id);
> + from_qown(&init_user_ns, dquot->dq_type,
> + dquot->dq_id));
> set_bit(DQ_FAKE_B, &dquot->dq_flags);
> memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
> kfree(ddquot);
> diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c
> index 34b37a6..7c028f9 100644
> --- a/fs/quota/quota_v1.c
> +++ b/fs/quota/quota_v1.c
> @@ -63,7 +63,8 @@ static int v1_read_dqblk(struct dquot *dquot)
> /* Set structure to 0s in case read fails/is after end of file */
> memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
> dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
> - sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id));
> + sizeof(struct v1_disk_dqblk),
> + v1_dqoff(from_qown(&init_user_ns, type, dquot->dq_id)));
>
> v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
> if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
> @@ -83,7 +84,8 @@ static int v1_commit_dqblk(struct dquot *dquot)
> struct v1_disk_dqblk dqblk;
>
> v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
> - if (dquot->dq_id == 0) {
> + if (((type == USRQUOTA) && uid_eq(dquot->dq_id.uid, GLOBAL_ROOT_UID)) ||
> + ((type == GRPQUOTA) && gid_eq(dquot->dq_id.gid, GLOBAL_ROOT_GID))) {
> dqblk.dqb_btime =
> sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
> dqblk.dqb_itime =
> @@ -93,7 +95,7 @@ static int v1_commit_dqblk(struct dquot *dquot)
> if (sb_dqopt(dquot->dq_sb)->files[type])
> ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
> (char *)&dqblk, sizeof(struct v1_disk_dqblk),
> - v1_dqoff(dquot->dq_id));
> + v1_dqoff(from_qown(&init_user_ns, type, dquot->dq_id)));
> if (ret != sizeof(struct v1_disk_dqblk)) {
> quota_error(dquot->dq_sb, "dquota write failed");
> if (ret >= 0)
> diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
> index f1ab360..b9068b7 100644
> --- a/fs/quota/quota_v2.c
> +++ b/fs/quota/quota_v2.c
> @@ -206,7 +206,8 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
> d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
> d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
> d->dqb_btime = cpu_to_le64(m->dqb_btime);
> - d->dqb_id = cpu_to_le32(dquot->dq_id);
> + d->dqb_id = cpu_to_le32(from_qown(&init_user_ns,
> + dquot->dq_type, dquot->dq_id));
> if (qtree_entry_unused(info, dp))
> d->dqb_itime = cpu_to_le64(1);
> }
> @@ -216,10 +217,12 @@ static int v2r0_is_id(void *dp, struct dquot *dquot)
> struct v2r0_disk_dqblk *d = dp;
> struct qtree_mem_dqinfo *info =
> sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> + qown_t qown;
>
> if (qtree_entry_unused(info, dp))
> return 0;
> - return le32_to_cpu(d->dqb_id) == dquot->dq_id;
> + qown = make_qown(&init_user_ns, dquot->dq_type, le32_to_cpu(d->dqb_id));
> + return qown_eq(qown, dquot->dq_id, dquot->dq_type);
> }
>
> static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
> @@ -257,7 +260,8 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
> d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));
> d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
> d->dqb_btime = cpu_to_le64(m->dqb_btime);
> - d->dqb_id = cpu_to_le32(dquot->dq_id);
> + d->dqb_id = cpu_to_le32(from_qown(&init_user_ns,
> + dquot->dq_type, dquot->dq_id));
> if (qtree_entry_unused(info, dp))
> d->dqb_itime = cpu_to_le64(1);
> }
> @@ -267,10 +271,12 @@ static int v2r1_is_id(void *dp, struct dquot *dquot)
> struct v2r1_disk_dqblk *d = dp;
> struct qtree_mem_dqinfo *info =
> sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
> + qown_t qown;
>
> if (qtree_entry_unused(info, dp))
> return 0;
> - return le32_to_cpu(d->dqb_id) == dquot->dq_id;
> + qown = make_qown(&init_user_ns, dquot->dq_type, le32_to_cpu(d->dqb_id));
> + return qown_eq(qown, dquot->dq_id, dquot->dq_type);
> }
>
> static int v2_read_dquot(struct dquot *dquot)
> diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c
> index fed504f..9ee2d6d 100644
> --- a/fs/xfs/xfs_quotaops.c
> +++ b/fs/xfs/xfs_quotaops.c
> @@ -29,7 +29,7 @@
>
>
> STATIC int
> -xfs_quota_type(int type)
> +xfs_quota_type(enum quota_type type)
> {
> switch (type) {
> case USRQUOTA:
> @@ -97,28 +97,31 @@ xfs_fs_set_xstate(
> STATIC int
> xfs_fs_get_dqblk(
> struct super_block *sb,
> - int type,
> - qid_t id,
> + enum quota_type type,
> + qown_t id,
> struct fs_disk_quota *fdq)
> {
> struct xfs_mount *mp = XFS_M(sb);
> + xfs_dqid_t xfs_id;
>
> if (!XFS_IS_QUOTA_RUNNING(mp))
> return -ENOSYS;
> if (!XFS_IS_QUOTA_ON(mp))
> return -ESRCH;
>
> - return -xfs_qm_scall_getquota(mp, id, xfs_quota_type(type), fdq);
> + xfs_id = from_qown(&init_user_ns, type, id);
> + return -xfs_qm_scall_getquota(mp, xfs_id, xfs_quota_type(type), fdq);
> }
>
> STATIC int
> xfs_fs_set_dqblk(
> struct super_block *sb,
> - int type,
> - qid_t id,
> + enum quota_type type,
> + qown_t id,
> struct fs_disk_quota *fdq)
> {
> struct xfs_mount *mp = XFS_M(sb);
> + xfs_dqid_t xfs_id;
>
> if (sb->s_flags & MS_RDONLY)
> return -EROFS;
> @@ -127,7 +130,8 @@ xfs_fs_set_dqblk(
> if (!XFS_IS_QUOTA_ON(mp))
> return -ESRCH;
>
> - return -xfs_qm_scall_setqlim(mp, id, xfs_quota_type(type), fdq);
> + xfs_id = from_qown(&init_user_ns, type, id);
> + return -xfs_qm_scall_setqlim(mp, xfs_id, xfs_quota_type(type), fdq);
> }
>
> const struct quotactl_ops xfs_quotactl_operations = {
> diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
> index bcb6054..0c7ab32 100644
> --- a/fs/xfs/xfs_trans_dquot.c
> +++ b/fs/xfs/xfs_trans_dquot.c
> @@ -575,12 +575,14 @@ xfs_quota_warn(
> struct xfs_dquot *dqp,
> int type)
> {
> + enum quota_type qtype;
> + qown_t qown;
> /* no warnings for project quotas - we just return ENOSPC later */
> if (dqp->dq_flags & XFS_DQ_PROJ)
> return;
> - quota_send_warning((dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA,
> - be32_to_cpu(dqp->q_core.d_id), mp->m_super->s_dev,
> - type);
> + qtype = (dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA;
> + qown = make_qown(&init_user_ns, qtype, be32_to_cpu(dqp->q_core.d_id));
> + quota_send_warning(qtype, qown, mp->m_super->s_dev, type);
> }
>
> /*
> diff --git a/include/linux/quota.h b/include/linux/quota.h
> index 524ede8..67921d5 100644
> --- a/include/linux/quota.h
> +++ b/include/linux/quota.h
> @@ -181,10 +181,91 @@ enum {
> #include <linux/dqblk_v2.h>
>
> #include <linux/atomic.h>
> +#include <linux/uidgid.h>
>
> typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
> typedef long long qsize_t; /* Type in which we store sizes */
>
> +#undef USRQUOTA
> +#undef GRPQUOTA
> +enum quota_type {
> + USRQUOTA = 0,
> + GRPQUOTA = 1,
> +};
> +
> +typedef union quota_id {
> + kuid_t uid;
> + kgid_t gid;
> +} qown_t; /* Type in which we store the quota owner */
> +
> +static inline bool qown_eq(qown_t left, qown_t right, enum quota_type type)
> +{
> + switch(type) {
> + case USRQUOTA:
> + return uid_eq(left.uid, right.uid);
> + case GRPQUOTA:
> + return gid_eq(left.gid, right.gid);
> + default:
> + BUG();
> + }
> +}
> +
> +static inline u32 from_qown(struct user_namespace *user_ns,
> + enum quota_type type, qown_t qown)
> +{
> + switch (type) {
> + case USRQUOTA:
> + return from_kuid(user_ns, qown.uid);
> + case GRPQUOTA:
> + return from_kgid(user_ns, qown.gid);
> + default:
> + BUG();
> + }
> +}
> +
> +static inline u32 from_qown_munged(struct user_namespace *user_ns,
> + enum quota_type type, qown_t qown)
> +{
> + switch (type) {
> + case USRQUOTA:
> + return from_kuid_munged(user_ns, qown.uid);
> + case GRPQUOTA:
> + return from_kgid_munged(user_ns, qown.gid);
> + default:
> + BUG();
> + }
> +}
> +
> +static inline qown_t make_qown(struct user_namespace *user_ns,
> + enum quota_type type, qid_t id)
> +{
> + qown_t qown;
> +
> + switch (type) {
> + case USRQUOTA:
> + qown.uid = make_kuid(user_ns, id);
> + break;
> + case GRPQUOTA:
> + qown.gid = make_kgid(user_ns, id);
> + break;
> + default:
> + BUG();
> + }
> + return qown;
> +}
> +
> +static inline bool qown_valid(enum quota_type type, qown_t qown)
> +{
> + switch (type) {
> + case USRQUOTA:
> + return uid_valid(qown.uid);
> + case GRPQUOTA:
> + return gid_valid(qown.gid);
> + default:
> + BUG();
> + }
> +}
> +
> extern spinlock_t dq_data_lock;
>
> /* Maximal numbers of writes for quota operation (insert/delete/update)
> @@ -294,7 +375,7 @@ struct dquot {
> atomic_t dq_count; /* Use count */
> wait_queue_head_t dq_wait_unused; /* Wait queue for dquot to become unused */
> struct super_block *dq_sb; /* superblock this applies to */
> - unsigned int dq_id; /* ID this applies to (uid, gid) */
> + qown_t dq_id; /* ID this applies to (uid, gid) */
> loff_t dq_off; /* Offset of dquot on disk */
> unsigned long dq_flags; /* See DQ_* */
> short dq_type; /* Type of quota */
> @@ -336,8 +417,8 @@ struct quotactl_ops {
> int (*quota_sync)(struct super_block *, int);
> int (*get_info)(struct super_block *, int, struct if_dqinfo *);
> int (*set_info)(struct super_block *, int, struct if_dqinfo *);
> - int (*get_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
> - int (*set_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
> + int (*get_dqblk)(struct super_block *, enum quota_type, qown_t, struct fs_disk_quota *);
> + int (*set_dqblk)(struct super_block *, enum quota_type, qown_t, struct fs_disk_quota *);
> int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
> int (*set_xstate)(struct super_block *, unsigned int, int);
> };
> @@ -386,10 +467,10 @@ static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
> }
>
> #ifdef CONFIG_QUOTA_NETLINK_INTERFACE
> -extern void quota_send_warning(short type, unsigned int id, dev_t dev,
> +extern void quota_send_warning(enum quota_type type, qown_t id, dev_t dev,
> const char warntype);
> #else
> -static inline void quota_send_warning(short type, unsigned int id, dev_t dev,
> +static inline void quota_send_warning(enum quota_type type, qown_t id, dev_t dev,
> const char warntype)
> {
> return;
> diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
> index ec6b65f..d988b11 100644
> --- a/include/linux/quotaops.h
> +++ b/include/linux/quotaops.h
> @@ -44,13 +44,23 @@ void inode_sub_rsv_space(struct inode *inode, qsize_t number);
>
> void dquot_initialize(struct inode *inode);
> void dquot_drop(struct inode *inode);
> -struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
> +struct dquot *dqget(struct super_block *sb, qown_t id, enum quota_type type);
> void dqput(struct dquot *dquot);
> int dquot_scan_active(struct super_block *sb,
> int (*fn)(struct dquot *dquot, unsigned long priv),
> unsigned long priv);
> struct dquot *dquot_alloc(struct super_block *sb, int type);
> void dquot_destroy(struct dquot *dquot);
> +static inline struct dquot *dqgetusr(struct super_block *sb, kuid_t uid)
> +{
> + qown_t id = { .uid = uid };
> + return dqget(sb, id, USRQUOTA);
> +}
> +static inline struct dquot *dqgetgrp(struct super_block *sb, kgid_t gid)
> +{
> + qown_t id = { .gid = gid };
> + return dqget(sb, id, GRPQUOTA);
> +}
>
> int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags);
> void __dquot_free_space(struct inode *inode, qsize_t number, int flags);
> @@ -87,15 +97,15 @@ int dquot_writeback_dquots(struct super_block *sb, int type);
> int dquot_quota_sync(struct super_block *sb, int type);
> int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
> int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
> -int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
> +int dquot_get_dqblk(struct super_block *sb, enum quota_type type, qown_t id,
> struct fs_disk_quota *di);
> -int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
> +int dquot_set_dqblk(struct super_block *sb, enum quota_type type, qown_t id,
> struct fs_disk_quota *di);
>
> int __dquot_transfer(struct inode *inode, struct dquot **transfer_to);
> int dquot_transfer(struct inode *inode, struct iattr *iattr);
>
> -static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, int type)
> +static inline struct mem_dqinfo *sb_dqinfo(struct super_block *sb, enum quota_type type)
> {
> return sb_dqopt(sb)->info + type;
> }
> diff --git a/init/Kconfig b/init/Kconfig
> index 2a388e5..a0bccce 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -928,8 +928,6 @@ config UIDGID_CONVERTED
> depends on IMA = n
> depends on EVM = n
> depends on FS_POSIX_ACL = n
> - depends on QUOTA = n
> - depends on QUOTACTL = n
>
> # Networking
> depends on NET_9P = n
> --
> 1.7.5.4
>
--
Jan Kara <[email protected]>
SUSE Labs, CR


2012-08-27 15:54:44

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [REVIEW][PATCH 13/15] userns: Add basic quota support

Jan Kara <[email protected]> writes:

> Hello,
>
> On Sat 25-08-12 17:05:35, Eric W. Biederman wrote:
>> Two helper are added dqgetusr and dqgetgrp to allow the quota
>> infrastructure to be called with a kuid and a kgid respectively. This
>> creates type safe variants of dqget and leads to shorter more
>> comprehensible code.
> It would look more comprehensible to me to have functions like:
> kuid2qown() and kgid2qown() and then call dqget(sb, kuid2qown(attr->uid))
> (see below for qown_t change proposal). The code then at the first look
> explains what is going on... Hmm?
>
>> Place the USRQUOTA and GRPQUOTA defines into enum quota_type. This
>> brings with it the ability for the compiler to check that switch
>> statements handle every quota type, and the ability to mark which
>> values store the type of a quota entry.
> OK, makes sense.
>
>> Add the data type qown_t a union of kuid_t and kgid_t. qown_t is a
>> replacement for the implicit union of uid and gid stored in an
>> unsigned int that is was used in the quota data structures. Making
>> the data type explicit allows the kuid_t and kgid_t type safety to
>> propogate more thoroughly through the code, revealing more places
>> where uid/gid conversions need be made.
> Hum, when we already do this, wouldn't it make more sense to embed quota
> type in qown_t? Because with the union thing you have no meaningful way of
> accessing that type without having quota type anyway. So having that in a
> single structure makes a lot of sense, plus it makes prototypes shorter...
> And you have to call make_qown() anyway...

So I think there was a reason for having the type separate but I'm not
seeing that reason off the top of my head.

I think it was filesystems like gfs2 doing weird things. But I tell
you want I will play with this and if I can't reproduce find a reason
for putting them in one structure I will because there are definitely
good reasons for doing that.


>> Allong with the data type qown_t comes the helper functions
>> qown_eq, from_qown, from_qown_munged, qown_valid, and make_qown.
>>
>> Update struct dquot dq_id to be a qown_t.
>>
>> Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
>> and dquot_set_dqblk to use enum quota_type and qown_t.
>>
>> Make minimal changes to gfs2, ocfs2, and xfs to deal with the change
>> in quota structures and signatures. The ocfs2 changes are larger than
>> most because of the extensive tracing throughout the ocfs2 quota code
>> that prints out dq_id.
> Otherwise the changes look OK to me, although I didn't check them in
> detail yet (as above suggestions will change the code anyway).
>
> Honza

Thanks,

Eric

2012-08-28 00:12:57

by Eric W. Biederman

[permalink] [raw]
Subject: [PATCH] userns: Add basic quota support v2


Add the data type struct qown which holds the owning identifier of a
quota. struct qown is a replacement for the implicit union of uid,
gid and project stored in an unsigned int and the quota type field
that is was used in the quota data structures. Making the data type
explicit allows the kuid_t and kgid_t type safety to propogate more
thoroughly through the code, revealing more places where uid/gid
conversions need be made.

Allong with the data type struct qown comes the helper functions
qown_eq, qown_lt, from_qown, from_qown_munged, qown_valid, make_qown,
make_qown_invalid, make_qown_uid, make_qown_gid.

Replace struct dquot dq_id and dq_type with dq_own a struct qown.

Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
and dquot_set_dqblk to use struct qown.

Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
the change in quota structures and signatures. The ocfs2 changes are
larger than most because of the extensive tracing throughout the ocfs2
quota code that prints out dq_id.

v2:
- Renamed qown_t struct qown
- Added the quota type to struct qown.
- Removed enum quota_type (In this patch it was just noise)
- Added qown_lt, make_qown_invalid, make_qown_uid, make_qown_gid
- Taught qown to handle xfs project ids (but only in init_user_ns).
Q_XGETQUOTA calls .get_quotblk with project ids.

Cc: Steven Whitehouse <[email protected]>
Cc: Mark Fasheh <[email protected]>
Cc: Joel Becker <[email protected]>
Cc: Ben Myers <[email protected]>
Cc: Alex Elder <[email protected]>
Cc: Jan Kara <[email protected]>
Cc: Dmitry Monakhov <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
---

fs/ext3/super.c | 2 +-
fs/ext4/super.c | 2 +-
fs/gfs2/quota.c | 52 +++++++++------
fs/ocfs2/file.c | 6 +-
fs/ocfs2/quota_global.c | 43 +++++++-----
fs/ocfs2/quota_local.c | 15 +++--
fs/quota/dquot.c | 118 +++++++++++++++++-----------------
fs/quota/netlink.c | 10 ++-
fs/quota/quota.c | 28 ++++++--
fs/quota/quota_tree.c | 35 ++++++----
fs/quota/quota_v1.c | 12 ++--
fs/quota/quota_v2.c | 26 ++++---
fs/xfs/xfs_quotaops.c | 14 ++--
fs/xfs/xfs_trans_dquot.c | 8 ++-
include/linux/quota.h | 162 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/quotaops.h | 6 +-
init/Kconfig | 2 -
17 files changed, 371 insertions(+), 170 deletions(-)

diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index ff9bcdc..c5879f1 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2814,7 +2814,7 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf)

static inline struct inode *dquot_to_inode(struct dquot *dquot)
{
- return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ return sb_dqopt(dquot->dq_sb)->files[dquot->dq_own.type];
}

static int ext3_write_dquot(struct dquot *dquot)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d76ec82..f60b48f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4796,7 +4796,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)

static inline struct inode *dquot_to_inode(struct dquot *dquot)
{
- return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ return sb_dqopt(dquot->dq_sb)->files[dquot->dq_own.type];
}

static int ext4_write_dquot(struct dquot *dquot)
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index a3bde91..f0310f9 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -1057,6 +1057,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
return 0;

for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
+ int qtype;
+ struct qown qown;
qd = ip->i_res->rs_qa_qd[x];

if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) ||
@@ -1068,11 +1070,12 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
value += qd->qd_change;
spin_unlock(&qd_lru_lock);

+ qtype = test_bit(QDF_USER, &qd->qd_flags) ? USRQUOTA : GRPQUOTA;
+ qown = make_qown(&init_user_ns, qtype, qd->qd_id);
if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
print_message(qd, "exceeded");
- quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
- USRQUOTA : GRPQUOTA, qd->qd_id,
- sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
+ quota_send_warning(qown, sdp->sd_vfs->s_dev,
+ QUOTA_NL_BHARDWARN);

error = -EDQUOT;
break;
@@ -1081,9 +1084,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
time_after_eq(jiffies, qd->qd_last_warn +
gfs2_tune_get(sdp,
gt_quota_warn_period) * HZ)) {
- quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
- USRQUOTA : GRPQUOTA, qd->qd_id,
- sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN);
+ quota_send_warning(qown, sdp->sd_vfs->s_dev,
+ QUOTA_NL_BSOFTWARN);
error = print_message(qd, "warning");
qd->qd_last_warn = jiffies;
}
@@ -1469,7 +1471,7 @@ static int gfs2_quota_get_xstate(struct super_block *sb,
return 0;
}

-static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
+static int gfs2_get_dqblk(struct super_block *sb, struct qown qown,
struct fs_disk_quota *fdq)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
@@ -1477,20 +1479,24 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
struct gfs2_quota_data *qd;
struct gfs2_holder q_gh;
int error;
+ int user;
+ u32 gfs_id;

memset(fdq, 0, sizeof(struct fs_disk_quota));

if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return -ESRCH; /* Crazy XFS error code */

- if (type == USRQUOTA)
- type = QUOTA_USER;
- else if (type == GRPQUOTA)
- type = QUOTA_GROUP;
+ gfs_id = from_qown(&init_user_ns, qown);
+
+ if (qown.type == USRQUOTA)
+ user = QUOTA_USER;
+ else if (qown.type == GRPQUOTA)
+ user = QUOTA_GROUP;
else
return -EINVAL;

- error = qd_get(sdp, type, id, &qd);
+ error = qd_get(sdp, user, gfs_id, &qd);
if (error)
return error;
error = do_glock(qd, FORCE, &q_gh);
@@ -1499,8 +1505,8 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,

qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
fdq->d_version = FS_DQUOT_VERSION;
- fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
- fdq->d_id = id;
+ fdq->d_flags = (user == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
+ fdq->d_id = gfs_id;
fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift;
fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift;
fdq->d_bcount = be64_to_cpu(qlvb->qb_value) << sdp->sd_fsb2bb_shift;
@@ -1514,8 +1520,8 @@ out:
/* GFS2 only supports a subset of the XFS fields */
#define GFS2_FIELDMASK (FS_DQ_BSOFT|FS_DQ_BHARD|FS_DQ_BCOUNT)

-static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
- struct fs_disk_quota *fdq)
+static int gfs2_set_dqblk(struct super_block *sb,
+ struct qown qown, struct fs_disk_quota *fdq)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
@@ -1526,18 +1532,22 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
int alloc_required;
loff_t offset;
int error;
+ int user;
+ u32 gfs_id;

if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return -ESRCH; /* Crazy XFS error code */

- switch(type) {
+ gfs_id = from_qown(&init_user_ns, qown);
+
+ switch(qown.type) {
case USRQUOTA:
- type = QUOTA_USER;
+ user = QUOTA_USER;
if (fdq->d_flags != FS_USER_QUOTA)
return -EINVAL;
break;
case GRPQUOTA:
- type = QUOTA_GROUP;
+ user = QUOTA_GROUP;
if (fdq->d_flags != FS_GROUP_QUOTA)
return -EINVAL;
break;
@@ -1547,10 +1557,10 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,

if (fdq->d_fieldmask & ~GFS2_FIELDMASK)
return -EINVAL;
- if (fdq->d_id != id)
+ if (fdq->d_id != gfs_id)
return -EINVAL;

- error = qd_get(sdp, type, id, &qd);
+ error = qd_get(sdp, user, gfs_id, &qd);
if (error)
return error;

diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 46a1f6d..3879186 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1184,8 +1184,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
- transfer_to[USRQUOTA] = dqget(sb, attr->ia_uid,
- USRQUOTA);
+ transfer_to[USRQUOTA] = dqget(sb, make_qown_uid(attr->ia_uid));
if (!transfer_to[USRQUOTA]) {
status = -ESRCH;
goto bail_unlock;
@@ -1194,8 +1193,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
- transfer_to[GRPQUOTA] = dqget(sb, attr->ia_gid,
- GRPQUOTA);
+ transfer_to[GRPQUOTA] = dqget(sb, make_qown_gid(attr->ia_gid));
if (!transfer_to[GRPQUOTA]) {
status = -ESRCH;
goto bail_unlock;
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index 0a86e30..dcee469 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -95,7 +95,7 @@ static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
struct ocfs2_global_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;

- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_qown(&init_user_ns, dquot->dq_own));
d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
@@ -112,11 +112,14 @@ static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
{
struct ocfs2_global_disk_dqblk *d = dp;
struct ocfs2_mem_dqinfo *oinfo =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
+ struct qown qown;

if (qtree_entry_unused(&oinfo->dqi_gi, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+
+ qown = make_qown(&init_user_ns, dquot->dq_own.type, le32_to_cpu(d->dqb_id));
+ return qown_eq(qown, dquot->dq_own);
}

struct qtree_fmt_operations ocfs2_global_ops = {
@@ -475,7 +478,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
{
int err, err2;
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
struct ocfs2_global_disk_dqblk dqblk;
s64 spacechange, inodechange;
@@ -504,7 +507,8 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
olditime = dquot->dq_dqb.dqb_itime;
oldbtime = dquot->dq_dqb.dqb_btime;
ocfs2_global_disk2memdqb(dquot, &dqblk);
- trace_ocfs2_sync_dquot(dquot->dq_id, dquot->dq_dqb.dqb_curspace,
+ trace_ocfs2_sync_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_dqb.dqb_curspace,
(long long)spacechange,
dquot->dq_dqb.dqb_curinodes,
(long long)inodechange);
@@ -555,8 +559,8 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
err = ocfs2_qinfo_lock(info, freeing);
if (err < 0) {
mlog(ML_ERROR, "Failed to lock quota info, losing quota write"
- " (type=%d, id=%u)\n", dquot->dq_type,
- (unsigned)dquot->dq_id);
+ " (type=%d, id=%u)\n", dquot->dq_own.type,
+ (unsigned)from_qown(&init_user_ns, dquot->dq_own));
goto out;
}
if (freeing)
@@ -591,9 +595,10 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
struct ocfs2_super *osb = OCFS2_SB(sb);
int status = 0;

- trace_ocfs2_sync_dquot_helper(dquot->dq_id, dquot->dq_type,
+ trace_ocfs2_sync_dquot_helper(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_own.type,
type, sb->s_id);
- if (type != dquot->dq_type)
+ if (type != dquot->dq_own.type)
goto out;
status = ocfs2_lock_global_qf(oinfo, 1);
if (status < 0)
@@ -643,7 +648,8 @@ static int ocfs2_write_dquot(struct dquot *dquot)
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
int status = 0;

- trace_ocfs2_write_dquot(dquot->dq_id, dquot->dq_type);
+ trace_ocfs2_write_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_own.type);

handle = ocfs2_start_trans(osb, OCFS2_QWRITE_CREDITS);
if (IS_ERR(handle)) {
@@ -677,11 +683,12 @@ static int ocfs2_release_dquot(struct dquot *dquot)
{
handle_t *handle;
struct ocfs2_mem_dqinfo *oinfo =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
int status = 0;

- trace_ocfs2_release_dquot(dquot->dq_id, dquot->dq_type);
+ trace_ocfs2_release_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_own.type);

mutex_lock(&dquot->dq_lock);
/* Check whether we are not racing with some other dqget() */
@@ -691,7 +698,7 @@ static int ocfs2_release_dquot(struct dquot *dquot)
if (status < 0)
goto out;
handle = ocfs2_start_trans(osb,
- ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_type));
+ ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_own.type));
if (IS_ERR(handle)) {
status = PTR_ERR(handle);
mlog_errno(status);
@@ -733,13 +740,14 @@ static int ocfs2_acquire_dquot(struct dquot *dquot)
int ex = 0;
struct super_block *sb = dquot->dq_sb;
struct ocfs2_super *osb = OCFS2_SB(sb);
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
struct inode *gqinode = info->dqi_gqinode;
int need_alloc = ocfs2_global_qinit_alloc(sb, type);
handle_t *handle;

- trace_ocfs2_acquire_dquot(dquot->dq_id, type);
+ trace_ocfs2_acquire_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ type);
mutex_lock(&dquot->dq_lock);
/*
* We need an exclusive lock, because we're going to update use count
@@ -821,12 +829,13 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
int sync = 0;
int status;
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
handle_t *handle;
struct ocfs2_super *osb = OCFS2_SB(sb);

- trace_ocfs2_mark_dquot_dirty(dquot->dq_id, type);
+ trace_ocfs2_mark_dquot_dirty(from_qown(&init_user_ns, dquot->dq_own),
+ type);

/* In case user set some limits, sync dquot immediately to global
* quota file so that information propagates quicker */
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index f100bf7..3aec405 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -501,7 +501,9 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
}
dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
ol_dqblk_block_off(sb, chunk, bit));
- dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
+ dquot = dqget(sb,
+ make_qown(&init_user_ns, type,
+ le64_to_cpu(dqblk->dqb_id)));
if (!dquot) {
status = -EIO;
mlog(ML_ERROR, "Failed to get quota structure "
@@ -881,7 +883,8 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data
+ ol_dqblk_block_offset(sb, od->dq_local_off));

- dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id);
+ dqblk->dqb_id = cpu_to_le64(from_qown(&init_user_ns,
+ od->dq_dquot.dq_own));
spin_lock(&dq_data_lock);
dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
od->dq_origspace);
@@ -891,7 +894,7 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
trace_olq_set_dquot(
(unsigned long long)le64_to_cpu(dqblk->dqb_spacemod),
(unsigned long long)le64_to_cpu(dqblk->dqb_inodemod),
- od->dq_dquot.dq_id);
+ from_qown(&init_user_ns, od->dq_dquot.dq_own));
}

/* Write dquot to local quota file */
@@ -900,7 +903,7 @@ int ocfs2_local_write_dquot(struct dquot *dquot)
struct super_block *sb = dquot->dq_sb;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
struct buffer_head *bh;
- struct inode *lqinode = sb_dqopt(sb)->files[dquot->dq_type];
+ struct inode *lqinode = sb_dqopt(sb)->files[dquot->dq_own.type];
int status;

status = ocfs2_read_quota_phys_block(lqinode, od->dq_local_phys_blk,
@@ -1221,7 +1224,7 @@ static void olq_alloc_dquot(struct buffer_head *bh, void *private)
int ocfs2_create_local_dquot(struct dquot *dquot)
{
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct inode *lqinode = sb_dqopt(sb)->files[type];
struct ocfs2_quota_chunk *chunk;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
@@ -1275,7 +1278,7 @@ out:
int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot)
{
int status;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
struct super_block *sb = dquot->dq_sb;
struct ocfs2_local_disk_chunk *dchunk;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 36a29b7..766ab61 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -253,11 +253,13 @@ static qsize_t inode_get_rsv_space(struct inode *inode);
static void __dquot_initialize(struct inode *inode, int type);

static inline unsigned int
-hashfn(const struct super_block *sb, unsigned int id, int type)
+hashfn(const struct super_block *sb, struct qown qown)
{
+ unsigned int id;
unsigned long tmp;

- tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
+ id = from_qown(&init_user_ns, qown);
+ tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - qown.type);
return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
}

@@ -267,7 +269,7 @@ hashfn(const struct super_block *sb, unsigned int id, int type)
static inline void insert_dquot_hash(struct dquot *dquot)
{
struct hlist_head *head;
- head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
+ head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_own);
hlist_add_head(&dquot->dq_hash, head);
}

@@ -277,15 +279,14 @@ static inline void remove_dquot_hash(struct dquot *dquot)
}

static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb,
- unsigned int id, int type)
+ struct qown own)
{
struct hlist_node *node;
struct dquot *dquot;

hlist_for_each (node, dquot_hash+hashent) {
dquot = hlist_entry(node, struct dquot, dq_hash);
- if (dquot->dq_sb == sb && dquot->dq_id == id &&
- dquot->dq_type == type)
+ if (dquot->dq_sb == sb && qown_eq(dquot->dq_own, own))
return dquot;
}
return NULL;
@@ -351,7 +352,7 @@ int dquot_mark_dquot_dirty(struct dquot *dquot)
spin_lock(&dq_list_lock);
if (!test_and_set_bit(DQ_MOD_B, &dquot->dq_flags)) {
list_add(&dquot->dq_dirty, &sb_dqopt(dquot->dq_sb)->
- info[dquot->dq_type].dqi_dirty_list);
+ info[dquot->dq_own.type].dqi_dirty_list);
ret = 0;
}
spin_unlock(&dq_list_lock);
@@ -410,17 +411,17 @@ int dquot_acquire(struct dquot *dquot)
mutex_lock(&dquot->dq_lock);
mutex_lock(&dqopt->dqio_mutex);
if (!test_bit(DQ_READ_B, &dquot->dq_flags))
- ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_own.type]->read_dqblk(dquot);
if (ret < 0)
goto out_iolock;
set_bit(DQ_READ_B, &dquot->dq_flags);
/* Instantiate dquot if needed */
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) {
- ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_own.type]->commit_dqblk(dquot);
/* Write the info if needed */
- if (info_dirty(&dqopt->info[dquot->dq_type])) {
- ret2 = dqopt->ops[dquot->dq_type]->write_file_info(
- dquot->dq_sb, dquot->dq_type);
+ if (info_dirty(&dqopt->info[dquot->dq_own.type])) {
+ ret2 = dqopt->ops[dquot->dq_own.type]->write_file_info(
+ dquot->dq_sb, dquot->dq_own.type);
}
if (ret < 0)
goto out_iolock;
@@ -455,7 +456,7 @@ int dquot_commit(struct dquot *dquot)
/* Inactive dquot can be only if there was error during read/init
* => we have better not writing it */
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
- ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_own.type]->commit_dqblk(dquot);
else
ret = -EIO;
out_sem:
@@ -477,12 +478,12 @@ int dquot_release(struct dquot *dquot)
if (atomic_read(&dquot->dq_count) > 1)
goto out_dqlock;
mutex_lock(&dqopt->dqio_mutex);
- if (dqopt->ops[dquot->dq_type]->release_dqblk) {
- ret = dqopt->ops[dquot->dq_type]->release_dqblk(dquot);
+ if (dqopt->ops[dquot->dq_own.type]->release_dqblk) {
+ ret = dqopt->ops[dquot->dq_own.type]->release_dqblk(dquot);
/* Write the info */
- if (info_dirty(&dqopt->info[dquot->dq_type])) {
- ret2 = dqopt->ops[dquot->dq_type]->write_file_info(
- dquot->dq_sb, dquot->dq_type);
+ if (info_dirty(&dqopt->info[dquot->dq_own.type])) {
+ ret2 = dqopt->ops[dquot->dq_own.type]->write_file_info(
+ dquot->dq_sb, dquot->dq_own.type);
}
if (ret >= 0)
ret = ret2;
@@ -521,7 +522,7 @@ restart:
list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
if (dquot->dq_sb != sb)
continue;
- if (dquot->dq_type != type)
+ if (dquot->dq_own.type != type)
continue;
/* Wait for dquot users */
if (atomic_read(&dquot->dq_count)) {
@@ -741,7 +742,8 @@ void dqput(struct dquot *dquot)
#ifdef CONFIG_QUOTA_DEBUG
if (!atomic_read(&dquot->dq_count)) {
quota_error(dquot->dq_sb, "trying to free free dquot of %s %d",
- quotatypes[dquot->dq_type], dquot->dq_id);
+ quotatypes[dquot->dq_own.type],
+ from_qown(&init_user_ns, dquot->dq_own));
BUG();
}
#endif
@@ -752,7 +754,7 @@ we_slept:
/* We have more than one user... nothing to do */
atomic_dec(&dquot->dq_count);
/* Releasing dquot during quotaoff phase? */
- if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_type) &&
+ if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_own.type) &&
atomic_read(&dquot->dq_count) == 1)
wake_up(&dquot->dq_wait_unused);
spin_unlock(&dq_list_lock);
@@ -815,7 +817,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
INIT_LIST_HEAD(&dquot->dq_dirty);
init_waitqueue_head(&dquot->dq_wait_unused);
dquot->dq_sb = sb;
- dquot->dq_type = type;
+ dquot->dq_own = make_qown_invalid(type);
atomic_set(&dquot->dq_count, 1);

return dquot;
@@ -829,35 +831,35 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
* a) checking for quota flags under dq_list_lock and
* b) getting a reference to dquot before we release dq_list_lock
*/
-struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
+struct dquot *dqget(struct super_block *sb, struct qown qown)
{
- unsigned int hashent = hashfn(sb, id, type);
+ unsigned int hashent = hashfn(sb, qown);
struct dquot *dquot = NULL, *empty = NULL;

- if (!sb_has_quota_active(sb, type))
+ if (!sb_has_quota_active(sb, qown.type))
return NULL;
we_slept:
spin_lock(&dq_list_lock);
spin_lock(&dq_state_lock);
- if (!sb_has_quota_active(sb, type)) {
+ if (!sb_has_quota_active(sb, qown.type)) {
spin_unlock(&dq_state_lock);
spin_unlock(&dq_list_lock);
goto out;
}
spin_unlock(&dq_state_lock);

- dquot = find_dquot(hashent, sb, id, type);
+ dquot = find_dquot(hashent, sb, qown);
if (!dquot) {
if (!empty) {
spin_unlock(&dq_list_lock);
- empty = get_empty_dquot(sb, type);
+ empty = get_empty_dquot(sb, qown.type);
if (!empty)
schedule(); /* Try to wait for a moment... */
goto we_slept;
}
dquot = empty;
empty = NULL;
- dquot->dq_id = id;
+ dquot->dq_own = qown;
/* all dquots go on the inuse_list */
put_inuse(dquot);
/* hash it first so it can be found */
@@ -1129,8 +1131,7 @@ static void dquot_decr_space(struct dquot *dquot, qsize_t number)

struct dquot_warn {
struct super_block *w_sb;
- qid_t w_dq_id;
- short w_dq_type;
+ struct qown w_dq_own;
short w_type;
};

@@ -1154,11 +1155,11 @@ static int need_print_warning(struct dquot_warn *warn)
if (!flag_print_warnings)
return 0;

- switch (warn->w_dq_type) {
+ switch (warn->w_dq_own.type) {
case USRQUOTA:
- return current_fsuid() == warn->w_dq_id;
+ return uid_eq(current_fsuid(), warn->w_dq_own.uid);
case GRPQUOTA:
- return in_group_p(warn->w_dq_id);
+ return in_group_p(warn->w_dq_own.gid);
}
return 0;
}
@@ -1184,7 +1185,7 @@ static void print_warning(struct dquot_warn *warn)
tty_write_message(tty, ": warning, ");
else
tty_write_message(tty, ": write failed, ");
- tty_write_message(tty, quotatypes[warn->w_dq_type]);
+ tty_write_message(tty, quotatypes[warn->w_dq_own.type]);
switch (warntype) {
case QUOTA_NL_IHARDWARN:
msg = " file limit reached.\r\n";
@@ -1217,8 +1218,7 @@ static void prepare_warning(struct dquot_warn *warn, struct dquot *dquot,
return;
warn->w_type = warntype;
warn->w_sb = dquot->dq_sb;
- warn->w_dq_id = dquot->dq_id;
- warn->w_dq_type = dquot->dq_type;
+ warn->w_dq_own = dquot->dq_own;
}

/*
@@ -1236,14 +1236,14 @@ static void flush_warnings(struct dquot_warn *warn)
#ifdef CONFIG_PRINT_QUOTA_WARNING
print_warning(&warn[i]);
#endif
- quota_send_warning(warn[i].w_dq_type, warn[i].w_dq_id,
+ quota_send_warning(warn[i].w_dq_own,
warn[i].w_sb->s_dev, warn[i].w_type);
}
}

static int ignore_hardlimit(struct dquot *dquot)
{
- struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+ struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_own.type];

return capable(CAP_SYS_RESOURCE) &&
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
@@ -1256,7 +1256,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes,
{
qsize_t newinodes = dquot->dq_dqb.dqb_curinodes + inodes;

- if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
+ if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_own.type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return 0;

@@ -1281,7 +1281,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes,
dquot->dq_dqb.dqb_itime == 0) {
prepare_warning(warn, dquot, QUOTA_NL_ISOFTWARN);
dquot->dq_dqb.dqb_itime = get_seconds() +
- sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+ sb_dqopt(dquot->dq_sb)->info[dquot->dq_own.type].dqi_igrace;
}

return 0;
@@ -1294,7 +1294,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc,
qsize_t tspace;
struct super_block *sb = dquot->dq_sb;

- if (!sb_has_quota_limits_enabled(sb, dquot->dq_type) ||
+ if (!sb_has_quota_limits_enabled(sb, dquot->dq_own.type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return 0;

@@ -1325,7 +1325,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc,
if (!prealloc) {
prepare_warning(warn, dquot, QUOTA_NL_BSOFTWARN);
dquot->dq_dqb.dqb_btime = get_seconds() +
- sb_dqopt(sb)->info[dquot->dq_type].dqi_bgrace;
+ sb_dqopt(sb)->info[dquot->dq_own.type].dqi_bgrace;
}
else
/*
@@ -1344,7 +1344,7 @@ static int info_idq_free(struct dquot *dquot, qsize_t inodes)

if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit ||
- !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type))
+ !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_own.type))
return QUOTA_NL_NOWARN;

newinodes = dquot->dq_dqb.dqb_curinodes - inodes;
@@ -1390,7 +1390,6 @@ static int dquot_active(const struct inode *inode)
*/
static void __dquot_initialize(struct inode *inode, int type)
{
- unsigned int id = 0;
int cnt;
struct dquot *got[MAXQUOTAS];
struct super_block *sb = inode->i_sb;
@@ -1403,18 +1402,19 @@ static void __dquot_initialize(struct inode *inode, int type)

/* First get references to structures we might need. */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ struct qown qown;
got[cnt] = NULL;
if (type != -1 && cnt != type)
continue;
switch (cnt) {
case USRQUOTA:
- id = inode->i_uid;
+ qown = make_qown_uid(inode->i_uid);
break;
case GRPQUOTA:
- id = inode->i_gid;
+ qown = make_qown_gid(inode->i_gid);
break;
}
- got[cnt] = dqget(sb, id, cnt);
+ got[cnt] = dqget(sb, qown);
}

down_write(&sb_dqopt(sb)->dqptr_sem);
@@ -1897,10 +1897,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
if (!dquot_active(inode))
return 0;

- if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid)
- transfer_to[USRQUOTA] = dqget(sb, iattr->ia_uid, USRQUOTA);
- if (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)
- transfer_to[GRPQUOTA] = dqget(sb, iattr->ia_gid, GRPQUOTA);
+ if (iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid))
+ transfer_to[USRQUOTA] = dqget(sb, make_qown_uid(iattr->ia_uid));
+ if (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))
+ transfer_to[GRPQUOTA] = dqget(sb, make_qown_gid(iattr->ia_gid));

ret = __dquot_transfer(inode, transfer_to);
dqput_all(transfer_to);
@@ -2360,9 +2360,9 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)

memset(di, 0, sizeof(*di));
di->d_version = FS_DQUOT_VERSION;
- di->d_flags = dquot->dq_type == USRQUOTA ?
+ di->d_flags = dquot->dq_own.type == USRQUOTA ?
FS_USER_QUOTA : FS_GROUP_QUOTA;
- di->d_id = dquot->dq_id;
+ di->d_id = from_qown_munged(current_user_ns(), dquot->dq_own);

spin_lock(&dq_data_lock);
di->d_blk_hardlimit = stoqb(dm->dqb_bhardlimit);
@@ -2376,12 +2376,12 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
spin_unlock(&dq_data_lock);
}

-int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_get_dqblk(struct super_block *sb, struct qown qown,
struct fs_disk_quota *di)
{
struct dquot *dquot;

- dquot = dqget(sb, id, type);
+ dquot = dqget(sb, qown);
if (!dquot)
return -ESRCH;
do_get_dqblk(dquot, di);
@@ -2401,7 +2401,7 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
{
struct mem_dqblk *dm = &dquot->dq_dqb;
int check_blim = 0, check_ilim = 0;
- struct mem_dqinfo *dqi = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+ struct mem_dqinfo *dqi = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_own.type];

if (di->d_fieldmask & ~VFS_FS_DQ_MASK)
return -EINVAL;
@@ -2488,13 +2488,13 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
return 0;
}

-int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, struct qown qown,
struct fs_disk_quota *di)
{
struct dquot *dquot;
int rc;

- dquot = dqget(sb, id, type);
+ dquot = dqget(sb, qown);
if (!dquot) {
rc = -ESRCH;
goto out;
diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
index d67908b..82e99d7 100644
--- a/fs/quota/netlink.c
+++ b/fs/quota/netlink.c
@@ -30,7 +30,7 @@ static struct genl_family quota_genl_family = {
*
*/

-void quota_send_warning(short type, unsigned int id, dev_t dev,
+void quota_send_warning(struct qown qown, dev_t dev,
const char warntype)
{
static atomic_t seq;
@@ -56,10 +56,11 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
"VFS: Cannot store netlink header in quota warning.\n");
goto err_out;
}
- ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type);
+ ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, qown.type);
if (ret)
goto attr_err_out;
- ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id);
+ ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID,
+ from_qown_munged(&init_user_ns, qown));
if (ret)
goto attr_err_out;
ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
@@ -71,7 +72,8 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev));
if (ret)
goto attr_err_out;
- ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
+ ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID,
+ from_kuid_munged(&init_user_ns, current_uid()));
if (ret)
goto attr_err_out;
genlmsg_end(skb, msg_head);
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 6f15578..6c0e376 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -32,8 +32,8 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
/* allow to query information for dquots we "own" */
case Q_GETQUOTA:
case Q_XGETQUOTA:
- if ((type == USRQUOTA && current_euid() == id) ||
- (type == GRPQUOTA && in_egroup_p(id)))
+ if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
+ (type == GRPQUOTA && in_egroup_p(make_kgid(current_user_ns(), id))))
break;
/*FALLTHROUGH*/
default:
@@ -130,13 +130,17 @@ static void copy_to_if_dqblk(struct if_dqblk *dst, struct fs_disk_quota *src)
static int quota_getquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
+ struct qown qown;
struct fs_disk_quota fdq;
struct if_dqblk idq;
int ret;

if (!sb->s_qcop->get_dqblk)
return -ENOSYS;
- ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
+ qown = make_qown(current_user_ns(), type, id);
+ if (qown_valid(qown))
+ return -EINVAL;
+ ret = sb->s_qcop->get_dqblk(sb, qown, &fdq);
if (ret)
return ret;
copy_to_if_dqblk(&idq, &fdq);
@@ -176,13 +180,17 @@ static int quota_setquota(struct super_block *sb, int type, qid_t id,
{
struct fs_disk_quota fdq;
struct if_dqblk idq;
+ struct qown qown;

if (copy_from_user(&idq, addr, sizeof(idq)))
return -EFAULT;
if (!sb->s_qcop->set_dqblk)
return -ENOSYS;
+ qown = make_qown(current_user_ns(), type, id);
+ if (!qown_valid(qown))
+ return -EINVAL;
copy_from_if_dqblk(&fdq, &idq);
- return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
+ return sb->s_qcop->set_dqblk(sb, qown, &fdq);
}

static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr)
@@ -213,23 +221,31 @@ static int quota_setxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
struct fs_disk_quota fdq;
+ struct qown qown;

if (copy_from_user(&fdq, addr, sizeof(fdq)))
return -EFAULT;
if (!sb->s_qcop->set_dqblk)
return -ENOSYS;
- return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
+ qown = make_qown(current_user_ns(), type, id);
+ if (!qown_valid(qown))
+ return -EINVAL;
+ return sb->s_qcop->set_dqblk(sb, qown, &fdq);
}

static int quota_getxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
struct fs_disk_quota fdq;
+ struct qown qown;
int ret;

if (!sb->s_qcop->get_dqblk)
return -ENOSYS;
- ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
+ qown = make_qown(current_user_ns(), type, id);
+ if (!qown_valid(qown))
+ return -EINVAL;
+ ret = sb->s_qcop->get_dqblk(sb, qown, &fdq);
if (!ret && copy_to_user(addr, &fdq, sizeof(fdq)))
return -EFAULT;
return ret;
diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
index e41c1becf..fa48156 100644
--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -22,10 +22,12 @@ MODULE_LICENSE("GPL");

#define __QUOTA_QT_PARANOIA

-static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
+static int get_index(struct qtree_mem_dqinfo *info, struct qown qown, int depth)
{
unsigned int epb = info->dqi_usable_bs >> 2;
+ qid_t id;

+ id = from_qown(&init_user_ns, qown);
depth = info->dqi_qtree_depth - depth - 1;
while (depth--)
id /= epb;
@@ -244,7 +246,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
/* This is enough as the block is already zeroed and the entry
* list is empty... */
info->dqi_free_entry = blk;
- mark_info_dirty(dquot->dq_sb, dquot->dq_type);
+ mark_info_dirty(dquot->dq_sb, dquot->dq_own.type);
}
/* Block will be full? */
if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
@@ -313,7 +315,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
}
}
ref = (__le32 *)buf;
- newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+ newblk = le32_to_cpu(ref[get_index(info, dquot->dq_own, depth)]);
if (!newblk)
newson = 1;
if (depth == info->dqi_qtree_depth - 1) {
@@ -322,7 +324,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
quota_error(dquot->dq_sb, "Inserting already present "
"quota entry (block %u)",
le32_to_cpu(ref[get_index(info,
- dquot->dq_id, depth)]));
+ dquot->dq_own, depth)]));
ret = -EIO;
goto out_buf;
}
@@ -332,7 +334,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
ret = do_insert_tree(info, dquot, &newblk, depth+1);
}
if (newson && ret >= 0) {
- ref[get_index(info, dquot->dq_id, depth)] =
+ ref[get_index(info, dquot->dq_own, depth)] =
cpu_to_le32(newblk);
ret = write_blk(info, *treeblk, buf);
} else if (newact && ret < 0) {
@@ -357,7 +359,7 @@ static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
*/
int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct super_block *sb = dquot->dq_sb;
ssize_t ret;
char *ddquot = getdqbuf(info->dqi_entry_size);
@@ -472,7 +474,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
*blk);
goto out_buf;
}
- newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+ newblk = le32_to_cpu(ref[get_index(info, dquot->dq_own, depth)]);
if (depth == info->dqi_qtree_depth - 1) {
ret = free_dqentry(info, dquot, newblk);
newblk = 0;
@@ -481,7 +483,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
}
if (ret >= 0 && !newblk) {
int i;
- ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
+ ref[get_index(info, dquot->dq_own, depth)] = cpu_to_le32(0);
/* Block got empty? */
for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++)
;
@@ -538,8 +540,9 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
ddquot += info->dqi_entry_size;
}
if (i == qtree_dqstr_in_blk(info)) {
- quota_error(dquot->dq_sb, "Quota for id %u referenced "
- "but not present", dquot->dq_id);
+ quota_error(dquot->dq_sb,
+ "Quota for id %u referenced but not present",
+ from_qown(&init_user_ns, dquot->dq_own));
ret = -EIO;
goto out_buf;
} else {
@@ -568,7 +571,7 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
goto out_buf;
}
ret = 0;
- blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+ blk = le32_to_cpu(ref[get_index(info, dquot->dq_own, depth)]);
if (!blk) /* No reference? */
goto out_buf;
if (depth < info->dqi_qtree_depth - 1)
@@ -589,7 +592,7 @@ static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,

int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct super_block *sb = dquot->dq_sb;
loff_t offset;
char *ddquot;
@@ -607,8 +610,10 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
offset = find_dqentry(info, dquot);
if (offset <= 0) { /* Entry not present? */
if (offset < 0)
- quota_error(sb, "Can't read quota structure "
- "for id %u", dquot->dq_id);
+ quota_error(sb,"Can't read quota structure "
+ "for id %u",
+ from_qown(&init_user_ns,
+ dquot->dq_own));
dquot->dq_off = 0;
set_bit(DQ_FAKE_B, &dquot->dq_flags);
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
@@ -626,7 +631,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
if (ret >= 0)
ret = -EIO;
quota_error(sb, "Error while reading quota structure for id %u",
- dquot->dq_id);
+ from_qown(&init_user_ns, dquot->dq_own));
set_bit(DQ_FAKE_B, &dquot->dq_flags);
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
kfree(ddquot);
diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c
index 34b37a6..ec37d70 100644
--- a/fs/quota/quota_v1.c
+++ b/fs/quota/quota_v1.c
@@ -54,7 +54,7 @@ static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)

static int v1_read_dqblk(struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct v1_disk_dqblk dqblk;

if (!sb_dqopt(dquot->dq_sb)->files[type])
@@ -63,7 +63,8 @@ static int v1_read_dqblk(struct dquot *dquot)
/* Set structure to 0s in case read fails/is after end of file */
memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
- sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id));
+ sizeof(struct v1_disk_dqblk),
+ v1_dqoff(from_qown(&init_user_ns, dquot->dq_own)));

v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
@@ -78,12 +79,13 @@ static int v1_read_dqblk(struct dquot *dquot)

static int v1_commit_dqblk(struct dquot *dquot)
{
- short type = dquot->dq_type;
+ short type = dquot->dq_own.type;
ssize_t ret;
struct v1_disk_dqblk dqblk;

v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
- if (dquot->dq_id == 0) {
+ if (((type == USRQUOTA) && uid_eq(dquot->dq_own.uid, GLOBAL_ROOT_UID)) ||
+ ((type == GRPQUOTA) && gid_eq(dquot->dq_own.gid, GLOBAL_ROOT_GID))) {
dqblk.dqb_btime =
sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
dqblk.dqb_itime =
@@ -93,7 +95,7 @@ static int v1_commit_dqblk(struct dquot *dquot)
if (sb_dqopt(dquot->dq_sb)->files[type])
ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
(char *)&dqblk, sizeof(struct v1_disk_dqblk),
- v1_dqoff(dquot->dq_id));
+ v1_dqoff(from_qown(&init_user_ns, dquot->dq_own)));
if (ret != sizeof(struct v1_disk_dqblk)) {
quota_error(dquot->dq_sb, "dquota write failed");
if (ret >= 0)
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index f1ab360..1c26279 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -196,7 +196,7 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
struct v2r0_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;

d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
@@ -206,7 +206,7 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_qown(&init_user_ns, dquot->dq_own));
if (qtree_entry_unused(info, dp))
d->dqb_itime = cpu_to_le64(1);
}
@@ -215,11 +215,13 @@ static int v2r0_is_id(void *dp, struct dquot *dquot)
{
struct v2r0_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
+ struct qown qown;

if (qtree_entry_unused(info, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+ qown = make_qown(&init_user_ns, dquot->dq_own.type, le32_to_cpu(d->dqb_id));
+ return qown_eq(qown, dquot->dq_own);
}

static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
@@ -247,7 +249,7 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
struct v2r1_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;

d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
@@ -257,7 +259,7 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_qown(&init_user_ns, dquot->dq_own));
if (qtree_entry_unused(info, dp))
d->dqb_itime = cpu_to_le64(1);
}
@@ -266,26 +268,28 @@ static int v2r1_is_id(void *dp, struct dquot *dquot)
{
struct v2r1_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
+ struct qown qown;

if (qtree_entry_unused(info, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+ qown = make_qown(&init_user_ns, dquot->dq_own.type, le32_to_cpu(d->dqb_id));
+ return qown_eq(qown, dquot->dq_own);
}

static int v2_read_dquot(struct dquot *dquot)
{
- return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv, dquot);
}

static int v2_write_dquot(struct dquot *dquot)
{
- return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv, dquot);
}

static int v2_release_dquot(struct dquot *dquot)
{
- return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv, dquot);
}

static int v2_free_file_info(struct super_block *sb, int type)
diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c
index fed504f..589e9c7 100644
--- a/fs/xfs/xfs_quotaops.c
+++ b/fs/xfs/xfs_quotaops.c
@@ -97,28 +97,29 @@ xfs_fs_set_xstate(
STATIC int
xfs_fs_get_dqblk(
struct super_block *sb,
- int type,
- qid_t id,
+ struct qown qown,
struct fs_disk_quota *fdq)
{
struct xfs_mount *mp = XFS_M(sb);
+ xfs_dqid_t xfs_id;

if (!XFS_IS_QUOTA_RUNNING(mp))
return -ENOSYS;
if (!XFS_IS_QUOTA_ON(mp))
return -ESRCH;

- return -xfs_qm_scall_getquota(mp, id, xfs_quota_type(type), fdq);
+ xfs_id = from_qown(&init_user_ns, qown);
+ return -xfs_qm_scall_getquota(mp, xfs_id, xfs_quota_type(qown.type), fdq);
}

STATIC int
xfs_fs_set_dqblk(
struct super_block *sb,
- int type,
- qid_t id,
+ struct qown qown,
struct fs_disk_quota *fdq)
{
struct xfs_mount *mp = XFS_M(sb);
+ xfs_dqid_t xfs_id;

if (sb->s_flags & MS_RDONLY)
return -EROFS;
@@ -127,7 +128,8 @@ xfs_fs_set_dqblk(
if (!XFS_IS_QUOTA_ON(mp))
return -ESRCH;

- return -xfs_qm_scall_setqlim(mp, id, xfs_quota_type(type), fdq);
+ xfs_id = from_qown(&init_user_ns, qown);
+ return -xfs_qm_scall_setqlim(mp, xfs_id, xfs_quota_type(qown.type), fdq);
}

const struct quotactl_ops xfs_quotactl_operations = {
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index bcb6054..3e9fbb8 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -575,12 +575,14 @@ xfs_quota_warn(
struct xfs_dquot *dqp,
int type)
{
+ int qtype;
+ struct qown qown;
/* no warnings for project quotas - we just return ENOSPC later */
if (dqp->dq_flags & XFS_DQ_PROJ)
return;
- quota_send_warning((dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA,
- be32_to_cpu(dqp->q_core.d_id), mp->m_super->s_dev,
- type);
+ qtype = (dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA;
+ qown = make_qown(&init_user_ns, qtype, be32_to_cpu(dqp->q_core.d_id));
+ quota_send_warning(qown, mp->m_super->s_dev, type);
}

/*
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 524ede8..6ebb782 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -181,10 +181,161 @@ enum {
#include <linux/dqblk_v2.h>

#include <linux/atomic.h>
+#include <linux/uidgid.h>

typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
typedef long long qsize_t; /* Type in which we store sizes */

+struct qown { /* Type in which we store the quota owner */
+ union {
+ kuid_t uid;
+ kgid_t gid;
+ unsigned int prj;
+ };
+ int type; /* USRQUOTA (uid) or GRPQUOTA (gid) or XQM_PRJQUOTA (prj) */
+};
+
+static inline bool qown_eq(struct qown left, struct qown right)
+{
+ if (left.type != right.type)
+ return false;
+ switch(left.type) {
+ case USRQUOTA:
+ return uid_eq(left.uid, right.uid);
+ case GRPQUOTA:
+ return gid_eq(left.gid, right.gid);
+ case XQM_PRJQUOTA:
+ return left.prj == right.prj;
+ default:
+ BUG();
+ }
+}
+
+static inline bool qown_lt(struct qown left, struct qown right)
+{
+ if (left.type < right.type)
+ return true;
+ if (left.type > right.type)
+ return false;
+ switch (left.type) {
+ case USRQUOTA:
+ return uid_lt(left.uid, right.uid);
+ case GRPQUOTA:
+ return gid_lt(left.gid, right.gid);
+ case XQM_PRJQUOTA:
+ return left.prj < right.prj;
+ default:
+ BUG();
+ }
+}
+
+static inline u32 from_qown(struct user_namespace *user_ns, struct qown qown)
+{
+ switch (qown.type) {
+ case USRQUOTA:
+ return from_kuid(user_ns, qown.uid);
+ case GRPQUOTA:
+ return from_kgid(user_ns, qown.gid);
+ case XQM_PRJQUOTA:
+ return (user_ns == &init_user_ns) ? qown.prj : -1;
+ default:
+ BUG();
+ }
+}
+
+static inline u32 from_qown_munged(struct user_namespace *user_ns,
+ struct qown qown)
+{
+ switch (qown.type) {
+ case USRQUOTA:
+ return from_kuid_munged(user_ns, qown.uid);
+ case GRPQUOTA:
+ return from_kgid_munged(user_ns, qown.gid);
+ case XQM_PRJQUOTA:
+ return (user_ns == &init_user_ns) ? qown.prj : -1;
+ default:
+ BUG();
+ }
+}
+
+static inline struct qown make_qown(struct user_namespace *user_ns,
+ int type, qid_t id)
+{
+ struct qown qown;
+
+ qown.type = type;
+ switch (type) {
+ case USRQUOTA:
+ qown.uid = make_kuid(user_ns, id);
+ break;
+ case GRPQUOTA:
+ qown.gid = make_kgid(user_ns, id);
+ break;
+ case XQM_PRJQUOTA:
+ if (user_ns == &init_user_ns)
+ qown.prj = id;
+ else
+ qown.prj = -1;
+ break;
+ default:
+ BUG();
+ }
+ return qown;
+}
+
+static inline struct qown make_qown_invalid(int type)
+{
+ struct qown qown;
+
+ qown.type = type;
+ switch (type) {
+ case USRQUOTA:
+ qown.uid = INVALID_UID;
+ break;
+ case GRPQUOTA:
+ qown.gid = INVALID_GID;
+ break;
+ case XQM_PRJQUOTA:
+ qown.prj = -1;
+ break;
+ default:
+ BUG();
+ }
+ return qown;
+}
+
+static inline struct qown make_qown_uid(kuid_t uid)
+{
+ struct qown qown = {
+ .type = USRQUOTA,
+ .uid = uid,
+ };
+ return qown;
+}
+
+static inline struct qown make_qown_gid(kgid_t gid)
+{
+ struct qown qown = {
+ .type = GRPQUOTA,
+ .gid = gid,
+ };
+ return qown;
+}
+
+static inline bool qown_valid(struct qown qown)
+{
+ switch (qown.type) {
+ case USRQUOTA:
+ return uid_valid(qown.uid);
+ case GRPQUOTA:
+ return gid_valid(qown.gid);
+ case XQM_PRJQUOTA:
+ return qown.prj != (unsigned int)-1;
+ default:
+ BUG();
+ }
+}
+
extern spinlock_t dq_data_lock;

/* Maximal numbers of writes for quota operation (insert/delete/update)
@@ -294,10 +445,9 @@ struct dquot {
atomic_t dq_count; /* Use count */
wait_queue_head_t dq_wait_unused; /* Wait queue for dquot to become unused */
struct super_block *dq_sb; /* superblock this applies to */
- unsigned int dq_id; /* ID this applies to (uid, gid) */
+ struct qown dq_own; /* ID this applies to (uid, gid) */
loff_t dq_off; /* Offset of dquot on disk */
unsigned long dq_flags; /* See DQ_* */
- short dq_type; /* Type of quota */
struct mem_dqblk dq_dqb; /* Diskquota usage */
};

@@ -336,8 +486,8 @@ struct quotactl_ops {
int (*quota_sync)(struct super_block *, int);
int (*get_info)(struct super_block *, int, struct if_dqinfo *);
int (*set_info)(struct super_block *, int, struct if_dqinfo *);
- int (*get_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
- int (*set_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
+ int (*get_dqblk)(struct super_block *, struct qown, struct fs_disk_quota *);
+ int (*set_dqblk)(struct super_block *, struct qown, struct fs_disk_quota *);
int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
int (*set_xstate)(struct super_block *, unsigned int, int);
};
@@ -386,10 +536,10 @@ static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
}

#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
-extern void quota_send_warning(short type, unsigned int id, dev_t dev,
+extern void quota_send_warning(struct qown qown, dev_t dev,
const char warntype);
#else
-static inline void quota_send_warning(short type, unsigned int id, dev_t dev,
+static inline void quota_send_warning(struct qown qown, dev_t dev,
const char warntype)
{
return;
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index ec6b65f..ed9a5e4 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -44,7 +44,7 @@ void inode_sub_rsv_space(struct inode *inode, qsize_t number);

void dquot_initialize(struct inode *inode);
void dquot_drop(struct inode *inode);
-struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
+struct dquot *dqget(struct super_block *sb, struct qown qown);
void dqput(struct dquot *dquot);
int dquot_scan_active(struct super_block *sb,
int (*fn)(struct dquot *dquot, unsigned long priv),
@@ -87,9 +87,9 @@ int dquot_writeback_dquots(struct super_block *sb, int type);
int dquot_quota_sync(struct super_block *sb, int type);
int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
-int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_get_dqblk(struct super_block *sb, struct qown id,
struct fs_disk_quota *di);
-int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, struct qown id,
struct fs_disk_quota *di);

int __dquot_transfer(struct inode *inode, struct dquot **transfer_to);
diff --git a/init/Kconfig b/init/Kconfig
index 2a388e5..a0bccce 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -928,8 +928,6 @@ config UIDGID_CONVERTED
depends on IMA = n
depends on EVM = n
depends on FS_POSIX_ACL = n
- depends on QUOTA = n
- depends on QUOTACTL = n

# Networking
depends on NET_9P = n
--
1.7.5.4

2012-08-28 09:05:50

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH] userns: Add basic quota support v2

On Mon 27-08-12 17:12:16, Eric W. Biederman wrote:
> Add the data type struct qown which holds the owning identifier of a
> quota. struct qown is a replacement for the implicit union of uid,
> gid and project stored in an unsigned int and the quota type field
> that is was used in the quota data structures. Making the data type
> explicit allows the kuid_t and kgid_t type safety to propogate more
> thoroughly through the code, revealing more places where uid/gid
> conversions need be made.
>
> Allong with the data type struct qown comes the helper functions
^^^^ Along

> qown_eq, qown_lt, from_qown, from_qown_munged, qown_valid, make_qown,
> make_qown_invalid, make_qown_uid, make_qown_gid.
>
> Replace struct dquot dq_id and dq_type with dq_own a struct qown.
>
> Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
> and dquot_set_dqblk to use struct qown.
>
> Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
> the change in quota structures and signatures. The ocfs2 changes are
> larger than most because of the extensive tracing throughout the ocfs2
> quota code that prints out dq_id.
>
> v2:
> - Renamed qown_t struct qown
> - Added the quota type to struct qown.
> - Removed enum quota_type (In this patch it was just noise)
> - Added qown_lt, make_qown_invalid, make_qown_uid, make_qown_gid
> - Taught qown to handle xfs project ids (but only in init_user_ns).
> Q_XGETQUOTA calls .get_quotblk with project ids.
Just a couple one minor comments below...

> @@ -130,13 +130,17 @@ static void copy_to_if_dqblk(struct if_dqblk *dst, struct fs_disk_quota *src)
> static int quota_getquota(struct super_block *sb, int type, qid_t id,
> void __user *addr)
> {
> + struct qown qown;
> struct fs_disk_quota fdq;
> struct if_dqblk idq;
> int ret;
>
> if (!sb->s_qcop->get_dqblk)
> return -ENOSYS;
> - ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
> + qown = make_qown(current_user_ns(), type, id);
> + if (qown_valid(qown))
^ missing '!'

> + return -EINVAL;
> + ret = sb->s_qcop->get_dqblk(sb, qown, &fdq);
> if (ret)
> return ret;
> copy_to_if_dqblk(&idq, &fdq);
...
> +static inline u32 from_qown(struct user_namespace *user_ns, struct qown qown)
> +{
> + switch (qown.type) {
> + case USRQUOTA:
> + return from_kuid(user_ns, qown.uid);
> + case GRPQUOTA:
> + return from_kgid(user_ns, qown.gid);
> + case XQM_PRJQUOTA:
> + return (user_ns == &init_user_ns) ? qown.prj : -1;
> + default:
> + BUG();
> + }
> +}
I would like a bit more if the function somehow expressed in its name
that it returns id. id_from_qown() might be a bit too long given how often
it is used. qown2id() would be OK but it would be inconsistent with how
names of other functions you've added are formed. So I'm somewhat
undecided...

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2012-08-28 10:02:17

by Boaz Harrosh

[permalink] [raw]
Subject: Re: [PATCH] userns: Add basic quota support v2

On 08/28/2012 12:05 PM, Jan Kara wrote:

>> +static inline u32 from_qown(struct user_namespace *user_ns, struct qown qown)
>> +{
>> + switch (qown.type) {
>> + case USRQUOTA:
>> + return from_kuid(user_ns, qown.uid);
>> + case GRPQUOTA:
>> + return from_kgid(user_ns, qown.gid);
>> + case XQM_PRJQUOTA:
>> + return (user_ns == &init_user_ns) ? qown.prj : -1;
>> + default:
>> + BUG();
>> + }
>> +}
> I would like a bit more if the function somehow expressed in its name
> that it returns id. id_from_qown() might be a bit too long given how often
> it is used. qown2id() would be OK but it would be inconsistent with how
> names of other functions you've added are formed. So I'm somewhat
> undecided...
>


qown_id()

> Honza

2012-08-28 17:34:24

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH] userns: Add basic quota support v2

Jan Kara <[email protected]> writes:

> On Mon 27-08-12 17:12:16, Eric W. Biederman wrote:
>> Add the data type struct qown which holds the owning identifier of a
>> quota. struct qown is a replacement for the implicit union of uid,
>> gid and project stored in an unsigned int and the quota type field
>> that is was used in the quota data structures. Making the data type
>> explicit allows the kuid_t and kgid_t type safety to propogate more
>> thoroughly through the code, revealing more places where uid/gid
>> conversions need be made.
>>
>> Allong with the data type struct qown comes the helper functions
> ^^^^ Along
>
>> qown_eq, qown_lt, from_qown, from_qown_munged, qown_valid, make_qown,
>> make_qown_invalid, make_qown_uid, make_qown_gid.
>>
>> Replace struct dquot dq_id and dq_type with dq_own a struct qown.
>>
>> Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
>> and dquot_set_dqblk to use struct qown.
>>
>> Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
>> the change in quota structures and signatures. The ocfs2 changes are
>> larger than most because of the extensive tracing throughout the ocfs2
>> quota code that prints out dq_id.
>>
>> v2:
>> - Renamed qown_t struct qown
>> - Added the quota type to struct qown.
>> - Removed enum quota_type (In this patch it was just noise)
>> - Added qown_lt, make_qown_invalid, make_qown_uid, make_qown_gid
>> - Taught qown to handle xfs project ids (but only in init_user_ns).
>> Q_XGETQUOTA calls .get_quotblk with project ids.
> Just a couple one minor comments below...
>
>> @@ -130,13 +130,17 @@ static void copy_to_if_dqblk(struct if_dqblk *dst, struct fs_disk_quota *src)
>> static int quota_getquota(struct super_block *sb, int type, qid_t id,
>> void __user *addr)
>> {
>> + struct qown qown;
>> struct fs_disk_quota fdq;
>> struct if_dqblk idq;
>> int ret;
>>
>> if (!sb->s_qcop->get_dqblk)
>> return -ENOSYS;
>> - ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
>> + qown = make_qown(current_user_ns(), type, id);
>> + if (qown_valid(qown))
> ^ missing '!'

Good catch thank you.

>> + return -EINVAL;
>> + ret = sb->s_qcop->get_dqblk(sb, qown, &fdq);
>> if (ret)
>> return ret;
>> copy_to_if_dqblk(&idq, &fdq);
> ...
>> +static inline u32 from_qown(struct user_namespace *user_ns, struct qown qown)
>> +{
>> + switch (qown.type) {
>> + case USRQUOTA:
>> + return from_kuid(user_ns, qown.uid);
>> + case GRPQUOTA:
>> + return from_kgid(user_ns, qown.gid);
>> + case XQM_PRJQUOTA:
>> + return (user_ns == &init_user_ns) ? qown.prj : -1;
>> + default:
>> + BUG();
>> + }
>> +}
> I would like a bit more if the function somehow expressed in its name
> that it returns id. id_from_qown() might be a bit too long given how often
> it is used. qown2id() would be OK but it would be inconsistent with how
> names of other functions you've added are formed. So I'm somewhat
> undecided...

The qown vs id distinction bothers me a little bit.

I almost want to name it struct kid, and the functions make_kid,
from_kid etc. Where the emphasis is that we are transforming in and out
of the kernel internal form. I don't really like make_kid because id as
a base name seems to generic and it barely tells you it is. Perhaps
make_kqid. Where we call the quota ids and qid for short?

I am a little uncomfortable calling them kqids because the userspace
code also places format_ids in a plain qid_t. But make_kqid and
from_kqid seems the best alternate set of names I can come up with.

Eric

2012-08-28 17:51:10

by Jan Kara

[permalink] [raw]
Subject: Re: [PATCH] userns: Add basic quota support v2

On Tue 28-08-12 10:34:04, Eric W. Biederman wrote:
> Jan Kara <[email protected]> writes:
> >> + return -EINVAL;
> >> + ret = sb->s_qcop->get_dqblk(sb, qown, &fdq);
> >> if (ret)
> >> return ret;
> >> copy_to_if_dqblk(&idq, &fdq);
> > ...
> >> +static inline u32 from_qown(struct user_namespace *user_ns, struct qown qown)
> >> +{
> >> + switch (qown.type) {
> >> + case USRQUOTA:
> >> + return from_kuid(user_ns, qown.uid);
> >> + case GRPQUOTA:
> >> + return from_kgid(user_ns, qown.gid);
> >> + case XQM_PRJQUOTA:
> >> + return (user_ns == &init_user_ns) ? qown.prj : -1;
> >> + default:
> >> + BUG();
> >> + }
> >> +}
> > I would like a bit more if the function somehow expressed in its name
> > that it returns id. id_from_qown() might be a bit too long given how often
> > it is used. qown2id() would be OK but it would be inconsistent with how
> > names of other functions you've added are formed. So I'm somewhat
> > undecided...
>
> The qown vs id distinction bothers me a little bit.
>
> I almost want to name it struct kid, and the functions make_kid,
> from_kid etc. Where the emphasis is that we are transforming in and out
> of the kernel internal form. I don't really like make_kid because id as
> a base name seems to generic and it barely tells you it is. Perhaps
> make_kqid. Where we call the quota ids and qid for short?
>
> I am a little uncomfortable calling them kqids because the userspace
> code also places format_ids in a plain qid_t. But make_kqid and
> from_kqid seems the best alternate set of names I can come up with.
OK, these would seem reasonable to me.

Honza
--
Jan Kara <[email protected]>
SUSE Labs, CR

2012-08-28 18:06:07

by Eric W. Biederman

[permalink] [raw]
Subject: [PATCH] userns: Add basic quota support v3


Add the data type struct qown which holds the owning identifier of a
quota. struct qown is a replacement for the implicit union of uid,
gid and project stored in an unsigned int and the quota type field
that is was used in the quota data structures. Making the data type
explicit allows the kuid_t and kgid_t type safety to propogate more
thoroughly through the code, revealing more places where uid/gid
conversions need be made.

Along with the data type struct qown comes the helper functions
qown_eq, qown_lt, from_qown, from_qown_munged, qown_valid, make_qown,
make_qown_invalid, make_qown_uid, make_qown_gid.

Replace struct dquot dq_id and dq_type with dq_own a struct qown.

Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
and dquot_set_dqblk to use struct qown.

Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
the change in quota structures and signatures. The ocfs2 changes are
larger than most because of the extensive tracing throughout the ocfs2
quota code that prints out dq_id.

v3:
- Add missing negation on qown_valid
v2:
- Renamed qown_t struct qown
- Added the quota type to struct qown.
- Removed enum quota_type (In this patch it was just noise)
- Added qown_lt, make_qown_invalid, make_qown_uid, make_qown_gid
- Taught qown to handle xfs project ids (but only in init_user_ns).
Q_XGETQUOTA calls .get_quotblk with project ids.

Cc: Steven Whitehouse <[email protected]>
Cc: Mark Fasheh <[email protected]>
Cc: Joel Becker <[email protected]>
Cc: Ben Myers <[email protected]>
Cc: Alex Elder <[email protected]>
Cc: Jan Kara <[email protected]>
Cc: Dmitry Monakhov <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
---
fs/ext3/super.c | 2 +-
fs/ext4/super.c | 2 +-
fs/gfs2/quota.c | 52 +++++++++------
fs/ocfs2/file.c | 6 +-
fs/ocfs2/quota_global.c | 43 +++++++-----
fs/ocfs2/quota_local.c | 15 +++--
fs/quota/dquot.c | 118 +++++++++++++++++-----------------
fs/quota/netlink.c | 10 ++-
fs/quota/quota.c | 28 ++++++--
fs/quota/quota_tree.c | 35 ++++++----
fs/quota/quota_v1.c | 12 ++--
fs/quota/quota_v2.c | 26 ++++---
fs/xfs/xfs_quotaops.c | 14 ++--
fs/xfs/xfs_trans_dquot.c | 8 ++-
include/linux/quota.h | 162 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/quotaops.h | 6 +-
init/Kconfig | 2 -
17 files changed, 371 insertions(+), 170 deletions(-)

diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index ff9bcdc..c5879f1 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2814,7 +2814,7 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf)

static inline struct inode *dquot_to_inode(struct dquot *dquot)
{
- return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ return sb_dqopt(dquot->dq_sb)->files[dquot->dq_own.type];
}

static int ext3_write_dquot(struct dquot *dquot)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d76ec82..f60b48f 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4796,7 +4796,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)

static inline struct inode *dquot_to_inode(struct dquot *dquot)
{
- return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ return sb_dqopt(dquot->dq_sb)->files[dquot->dq_own.type];
}

static int ext4_write_dquot(struct dquot *dquot)
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index a3bde91..f0310f9 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -1057,6 +1057,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
return 0;

for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
+ int qtype;
+ struct qown qown;
qd = ip->i_res->rs_qa_qd[x];

if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) ||
@@ -1068,11 +1070,12 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
value += qd->qd_change;
spin_unlock(&qd_lru_lock);

+ qtype = test_bit(QDF_USER, &qd->qd_flags) ? USRQUOTA : GRPQUOTA;
+ qown = make_qown(&init_user_ns, qtype, qd->qd_id);
if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
print_message(qd, "exceeded");
- quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
- USRQUOTA : GRPQUOTA, qd->qd_id,
- sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
+ quota_send_warning(qown, sdp->sd_vfs->s_dev,
+ QUOTA_NL_BHARDWARN);

error = -EDQUOT;
break;
@@ -1081,9 +1084,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
time_after_eq(jiffies, qd->qd_last_warn +
gfs2_tune_get(sdp,
gt_quota_warn_period) * HZ)) {
- quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
- USRQUOTA : GRPQUOTA, qd->qd_id,
- sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN);
+ quota_send_warning(qown, sdp->sd_vfs->s_dev,
+ QUOTA_NL_BSOFTWARN);
error = print_message(qd, "warning");
qd->qd_last_warn = jiffies;
}
@@ -1469,7 +1471,7 @@ static int gfs2_quota_get_xstate(struct super_block *sb,
return 0;
}

-static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
+static int gfs2_get_dqblk(struct super_block *sb, struct qown qown,
struct fs_disk_quota *fdq)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
@@ -1477,20 +1479,24 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
struct gfs2_quota_data *qd;
struct gfs2_holder q_gh;
int error;
+ int user;
+ u32 gfs_id;

memset(fdq, 0, sizeof(struct fs_disk_quota));

if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return -ESRCH; /* Crazy XFS error code */

- if (type == USRQUOTA)
- type = QUOTA_USER;
- else if (type == GRPQUOTA)
- type = QUOTA_GROUP;
+ gfs_id = from_qown(&init_user_ns, qown);
+
+ if (qown.type == USRQUOTA)
+ user = QUOTA_USER;
+ else if (qown.type == GRPQUOTA)
+ user = QUOTA_GROUP;
else
return -EINVAL;

- error = qd_get(sdp, type, id, &qd);
+ error = qd_get(sdp, user, gfs_id, &qd);
if (error)
return error;
error = do_glock(qd, FORCE, &q_gh);
@@ -1499,8 +1505,8 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,

qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
fdq->d_version = FS_DQUOT_VERSION;
- fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
- fdq->d_id = id;
+ fdq->d_flags = (user == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
+ fdq->d_id = gfs_id;
fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift;
fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift;
fdq->d_bcount = be64_to_cpu(qlvb->qb_value) << sdp->sd_fsb2bb_shift;
@@ -1514,8 +1520,8 @@ out:
/* GFS2 only supports a subset of the XFS fields */
#define GFS2_FIELDMASK (FS_DQ_BSOFT|FS_DQ_BHARD|FS_DQ_BCOUNT)

-static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
- struct fs_disk_quota *fdq)
+static int gfs2_set_dqblk(struct super_block *sb,
+ struct qown qown, struct fs_disk_quota *fdq)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
@@ -1526,18 +1532,22 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
int alloc_required;
loff_t offset;
int error;
+ int user;
+ u32 gfs_id;

if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return -ESRCH; /* Crazy XFS error code */

- switch(type) {
+ gfs_id = from_qown(&init_user_ns, qown);
+
+ switch(qown.type) {
case USRQUOTA:
- type = QUOTA_USER;
+ user = QUOTA_USER;
if (fdq->d_flags != FS_USER_QUOTA)
return -EINVAL;
break;
case GRPQUOTA:
- type = QUOTA_GROUP;
+ user = QUOTA_GROUP;
if (fdq->d_flags != FS_GROUP_QUOTA)
return -EINVAL;
break;
@@ -1547,10 +1557,10 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,

if (fdq->d_fieldmask & ~GFS2_FIELDMASK)
return -EINVAL;
- if (fdq->d_id != id)
+ if (fdq->d_id != gfs_id)
return -EINVAL;

- error = qd_get(sdp, type, id, &qd);
+ error = qd_get(sdp, user, gfs_id, &qd);
if (error)
return error;

diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 46a1f6d..3879186 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1184,8 +1184,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
- transfer_to[USRQUOTA] = dqget(sb, attr->ia_uid,
- USRQUOTA);
+ transfer_to[USRQUOTA] = dqget(sb, make_qown_uid(attr->ia_uid));
if (!transfer_to[USRQUOTA]) {
status = -ESRCH;
goto bail_unlock;
@@ -1194,8 +1193,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
- transfer_to[GRPQUOTA] = dqget(sb, attr->ia_gid,
- GRPQUOTA);
+ transfer_to[GRPQUOTA] = dqget(sb, make_qown_gid(attr->ia_gid));
if (!transfer_to[GRPQUOTA]) {
status = -ESRCH;
goto bail_unlock;
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index 0a86e30..dcee469 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -95,7 +95,7 @@ static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
struct ocfs2_global_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;

- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_qown(&init_user_ns, dquot->dq_own));
d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
@@ -112,11 +112,14 @@ static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
{
struct ocfs2_global_disk_dqblk *d = dp;
struct ocfs2_mem_dqinfo *oinfo =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
+ struct qown qown;

if (qtree_entry_unused(&oinfo->dqi_gi, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+
+ qown = make_qown(&init_user_ns, dquot->dq_own.type, le32_to_cpu(d->dqb_id));
+ return qown_eq(qown, dquot->dq_own);
}

struct qtree_fmt_operations ocfs2_global_ops = {
@@ -475,7 +478,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
{
int err, err2;
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
struct ocfs2_global_disk_dqblk dqblk;
s64 spacechange, inodechange;
@@ -504,7 +507,8 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
olditime = dquot->dq_dqb.dqb_itime;
oldbtime = dquot->dq_dqb.dqb_btime;
ocfs2_global_disk2memdqb(dquot, &dqblk);
- trace_ocfs2_sync_dquot(dquot->dq_id, dquot->dq_dqb.dqb_curspace,
+ trace_ocfs2_sync_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_dqb.dqb_curspace,
(long long)spacechange,
dquot->dq_dqb.dqb_curinodes,
(long long)inodechange);
@@ -555,8 +559,8 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
err = ocfs2_qinfo_lock(info, freeing);
if (err < 0) {
mlog(ML_ERROR, "Failed to lock quota info, losing quota write"
- " (type=%d, id=%u)\n", dquot->dq_type,
- (unsigned)dquot->dq_id);
+ " (type=%d, id=%u)\n", dquot->dq_own.type,
+ (unsigned)from_qown(&init_user_ns, dquot->dq_own));
goto out;
}
if (freeing)
@@ -591,9 +595,10 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
struct ocfs2_super *osb = OCFS2_SB(sb);
int status = 0;

- trace_ocfs2_sync_dquot_helper(dquot->dq_id, dquot->dq_type,
+ trace_ocfs2_sync_dquot_helper(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_own.type,
type, sb->s_id);
- if (type != dquot->dq_type)
+ if (type != dquot->dq_own.type)
goto out;
status = ocfs2_lock_global_qf(oinfo, 1);
if (status < 0)
@@ -643,7 +648,8 @@ static int ocfs2_write_dquot(struct dquot *dquot)
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
int status = 0;

- trace_ocfs2_write_dquot(dquot->dq_id, dquot->dq_type);
+ trace_ocfs2_write_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_own.type);

handle = ocfs2_start_trans(osb, OCFS2_QWRITE_CREDITS);
if (IS_ERR(handle)) {
@@ -677,11 +683,12 @@ static int ocfs2_release_dquot(struct dquot *dquot)
{
handle_t *handle;
struct ocfs2_mem_dqinfo *oinfo =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
int status = 0;

- trace_ocfs2_release_dquot(dquot->dq_id, dquot->dq_type);
+ trace_ocfs2_release_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ dquot->dq_own.type);

mutex_lock(&dquot->dq_lock);
/* Check whether we are not racing with some other dqget() */
@@ -691,7 +698,7 @@ static int ocfs2_release_dquot(struct dquot *dquot)
if (status < 0)
goto out;
handle = ocfs2_start_trans(osb,
- ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_type));
+ ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_own.type));
if (IS_ERR(handle)) {
status = PTR_ERR(handle);
mlog_errno(status);
@@ -733,13 +740,14 @@ static int ocfs2_acquire_dquot(struct dquot *dquot)
int ex = 0;
struct super_block *sb = dquot->dq_sb;
struct ocfs2_super *osb = OCFS2_SB(sb);
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
struct inode *gqinode = info->dqi_gqinode;
int need_alloc = ocfs2_global_qinit_alloc(sb, type);
handle_t *handle;

- trace_ocfs2_acquire_dquot(dquot->dq_id, type);
+ trace_ocfs2_acquire_dquot(from_qown(&init_user_ns, dquot->dq_own),
+ type);
mutex_lock(&dquot->dq_lock);
/*
* We need an exclusive lock, because we're going to update use count
@@ -821,12 +829,13 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
int sync = 0;
int status;
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
handle_t *handle;
struct ocfs2_super *osb = OCFS2_SB(sb);

- trace_ocfs2_mark_dquot_dirty(dquot->dq_id, type);
+ trace_ocfs2_mark_dquot_dirty(from_qown(&init_user_ns, dquot->dq_own),
+ type);

/* In case user set some limits, sync dquot immediately to global
* quota file so that information propagates quicker */
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index f100bf7..3aec405 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -501,7 +501,9 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
}
dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
ol_dqblk_block_off(sb, chunk, bit));
- dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
+ dquot = dqget(sb,
+ make_qown(&init_user_ns, type,
+ le64_to_cpu(dqblk->dqb_id)));
if (!dquot) {
status = -EIO;
mlog(ML_ERROR, "Failed to get quota structure "
@@ -881,7 +883,8 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data
+ ol_dqblk_block_offset(sb, od->dq_local_off));

- dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id);
+ dqblk->dqb_id = cpu_to_le64(from_qown(&init_user_ns,
+ od->dq_dquot.dq_own));
spin_lock(&dq_data_lock);
dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
od->dq_origspace);
@@ -891,7 +894,7 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
trace_olq_set_dquot(
(unsigned long long)le64_to_cpu(dqblk->dqb_spacemod),
(unsigned long long)le64_to_cpu(dqblk->dqb_inodemod),
- od->dq_dquot.dq_id);
+ from_qown(&init_user_ns, od->dq_dquot.dq_own));
}

/* Write dquot to local quota file */
@@ -900,7 +903,7 @@ int ocfs2_local_write_dquot(struct dquot *dquot)
struct super_block *sb = dquot->dq_sb;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
struct buffer_head *bh;
- struct inode *lqinode = sb_dqopt(sb)->files[dquot->dq_type];
+ struct inode *lqinode = sb_dqopt(sb)->files[dquot->dq_own.type];
int status;

status = ocfs2_read_quota_phys_block(lqinode, od->dq_local_phys_blk,
@@ -1221,7 +1224,7 @@ static void olq_alloc_dquot(struct buffer_head *bh, void *private)
int ocfs2_create_local_dquot(struct dquot *dquot)
{
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct inode *lqinode = sb_dqopt(sb)->files[type];
struct ocfs2_quota_chunk *chunk;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
@@ -1275,7 +1278,7 @@ out:
int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot)
{
int status;
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
struct super_block *sb = dquot->dq_sb;
struct ocfs2_local_disk_chunk *dchunk;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 36a29b7..766ab61 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -253,11 +253,13 @@ static qsize_t inode_get_rsv_space(struct inode *inode);
static void __dquot_initialize(struct inode *inode, int type);

static inline unsigned int
-hashfn(const struct super_block *sb, unsigned int id, int type)
+hashfn(const struct super_block *sb, struct qown qown)
{
+ unsigned int id;
unsigned long tmp;

- tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
+ id = from_qown(&init_user_ns, qown);
+ tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - qown.type);
return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
}

@@ -267,7 +269,7 @@ hashfn(const struct super_block *sb, unsigned int id, int type)
static inline void insert_dquot_hash(struct dquot *dquot)
{
struct hlist_head *head;
- head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
+ head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_own);
hlist_add_head(&dquot->dq_hash, head);
}

@@ -277,15 +279,14 @@ static inline void remove_dquot_hash(struct dquot *dquot)
}

static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb,
- unsigned int id, int type)
+ struct qown own)
{
struct hlist_node *node;
struct dquot *dquot;

hlist_for_each (node, dquot_hash+hashent) {
dquot = hlist_entry(node, struct dquot, dq_hash);
- if (dquot->dq_sb == sb && dquot->dq_id == id &&
- dquot->dq_type == type)
+ if (dquot->dq_sb == sb && qown_eq(dquot->dq_own, own))
return dquot;
}
return NULL;
@@ -351,7 +352,7 @@ int dquot_mark_dquot_dirty(struct dquot *dquot)
spin_lock(&dq_list_lock);
if (!test_and_set_bit(DQ_MOD_B, &dquot->dq_flags)) {
list_add(&dquot->dq_dirty, &sb_dqopt(dquot->dq_sb)->
- info[dquot->dq_type].dqi_dirty_list);
+ info[dquot->dq_own.type].dqi_dirty_list);
ret = 0;
}
spin_unlock(&dq_list_lock);
@@ -410,17 +411,17 @@ int dquot_acquire(struct dquot *dquot)
mutex_lock(&dquot->dq_lock);
mutex_lock(&dqopt->dqio_mutex);
if (!test_bit(DQ_READ_B, &dquot->dq_flags))
- ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_own.type]->read_dqblk(dquot);
if (ret < 0)
goto out_iolock;
set_bit(DQ_READ_B, &dquot->dq_flags);
/* Instantiate dquot if needed */
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) {
- ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_own.type]->commit_dqblk(dquot);
/* Write the info if needed */
- if (info_dirty(&dqopt->info[dquot->dq_type])) {
- ret2 = dqopt->ops[dquot->dq_type]->write_file_info(
- dquot->dq_sb, dquot->dq_type);
+ if (info_dirty(&dqopt->info[dquot->dq_own.type])) {
+ ret2 = dqopt->ops[dquot->dq_own.type]->write_file_info(
+ dquot->dq_sb, dquot->dq_own.type);
}
if (ret < 0)
goto out_iolock;
@@ -455,7 +456,7 @@ int dquot_commit(struct dquot *dquot)
/* Inactive dquot can be only if there was error during read/init
* => we have better not writing it */
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
- ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_own.type]->commit_dqblk(dquot);
else
ret = -EIO;
out_sem:
@@ -477,12 +478,12 @@ int dquot_release(struct dquot *dquot)
if (atomic_read(&dquot->dq_count) > 1)
goto out_dqlock;
mutex_lock(&dqopt->dqio_mutex);
- if (dqopt->ops[dquot->dq_type]->release_dqblk) {
- ret = dqopt->ops[dquot->dq_type]->release_dqblk(dquot);
+ if (dqopt->ops[dquot->dq_own.type]->release_dqblk) {
+ ret = dqopt->ops[dquot->dq_own.type]->release_dqblk(dquot);
/* Write the info */
- if (info_dirty(&dqopt->info[dquot->dq_type])) {
- ret2 = dqopt->ops[dquot->dq_type]->write_file_info(
- dquot->dq_sb, dquot->dq_type);
+ if (info_dirty(&dqopt->info[dquot->dq_own.type])) {
+ ret2 = dqopt->ops[dquot->dq_own.type]->write_file_info(
+ dquot->dq_sb, dquot->dq_own.type);
}
if (ret >= 0)
ret = ret2;
@@ -521,7 +522,7 @@ restart:
list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
if (dquot->dq_sb != sb)
continue;
- if (dquot->dq_type != type)
+ if (dquot->dq_own.type != type)
continue;
/* Wait for dquot users */
if (atomic_read(&dquot->dq_count)) {
@@ -741,7 +742,8 @@ void dqput(struct dquot *dquot)
#ifdef CONFIG_QUOTA_DEBUG
if (!atomic_read(&dquot->dq_count)) {
quota_error(dquot->dq_sb, "trying to free free dquot of %s %d",
- quotatypes[dquot->dq_type], dquot->dq_id);
+ quotatypes[dquot->dq_own.type],
+ from_qown(&init_user_ns, dquot->dq_own));
BUG();
}
#endif
@@ -752,7 +754,7 @@ we_slept:
/* We have more than one user... nothing to do */
atomic_dec(&dquot->dq_count);
/* Releasing dquot during quotaoff phase? */
- if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_type) &&
+ if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_own.type) &&
atomic_read(&dquot->dq_count) == 1)
wake_up(&dquot->dq_wait_unused);
spin_unlock(&dq_list_lock);
@@ -815,7 +817,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
INIT_LIST_HEAD(&dquot->dq_dirty);
init_waitqueue_head(&dquot->dq_wait_unused);
dquot->dq_sb = sb;
- dquot->dq_type = type;
+ dquot->dq_own = make_qown_invalid(type);
atomic_set(&dquot->dq_count, 1);

return dquot;
@@ -829,35 +831,35 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
* a) checking for quota flags under dq_list_lock and
* b) getting a reference to dquot before we release dq_list_lock
*/
-struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
+struct dquot *dqget(struct super_block *sb, struct qown qown)
{
- unsigned int hashent = hashfn(sb, id, type);
+ unsigned int hashent = hashfn(sb, qown);
struct dquot *dquot = NULL, *empty = NULL;

- if (!sb_has_quota_active(sb, type))
+ if (!sb_has_quota_active(sb, qown.type))
return NULL;
we_slept:
spin_lock(&dq_list_lock);
spin_lock(&dq_state_lock);
- if (!sb_has_quota_active(sb, type)) {
+ if (!sb_has_quota_active(sb, qown.type)) {
spin_unlock(&dq_state_lock);
spin_unlock(&dq_list_lock);
goto out;
}
spin_unlock(&dq_state_lock);

- dquot = find_dquot(hashent, sb, id, type);
+ dquot = find_dquot(hashent, sb, qown);
if (!dquot) {
if (!empty) {
spin_unlock(&dq_list_lock);
- empty = get_empty_dquot(sb, type);
+ empty = get_empty_dquot(sb, qown.type);
if (!empty)
schedule(); /* Try to wait for a moment... */
goto we_slept;
}
dquot = empty;
empty = NULL;
- dquot->dq_id = id;
+ dquot->dq_own = qown;
/* all dquots go on the inuse_list */
put_inuse(dquot);
/* hash it first so it can be found */
@@ -1129,8 +1131,7 @@ static void dquot_decr_space(struct dquot *dquot, qsize_t number)

struct dquot_warn {
struct super_block *w_sb;
- qid_t w_dq_id;
- short w_dq_type;
+ struct qown w_dq_own;
short w_type;
};

@@ -1154,11 +1155,11 @@ static int need_print_warning(struct dquot_warn *warn)
if (!flag_print_warnings)
return 0;

- switch (warn->w_dq_type) {
+ switch (warn->w_dq_own.type) {
case USRQUOTA:
- return current_fsuid() == warn->w_dq_id;
+ return uid_eq(current_fsuid(), warn->w_dq_own.uid);
case GRPQUOTA:
- return in_group_p(warn->w_dq_id);
+ return in_group_p(warn->w_dq_own.gid);
}
return 0;
}
@@ -1184,7 +1185,7 @@ static void print_warning(struct dquot_warn *warn)
tty_write_message(tty, ": warning, ");
else
tty_write_message(tty, ": write failed, ");
- tty_write_message(tty, quotatypes[warn->w_dq_type]);
+ tty_write_message(tty, quotatypes[warn->w_dq_own.type]);
switch (warntype) {
case QUOTA_NL_IHARDWARN:
msg = " file limit reached.\r\n";
@@ -1217,8 +1218,7 @@ static void prepare_warning(struct dquot_warn *warn, struct dquot *dquot,
return;
warn->w_type = warntype;
warn->w_sb = dquot->dq_sb;
- warn->w_dq_id = dquot->dq_id;
- warn->w_dq_type = dquot->dq_type;
+ warn->w_dq_own = dquot->dq_own;
}

/*
@@ -1236,14 +1236,14 @@ static void flush_warnings(struct dquot_warn *warn)
#ifdef CONFIG_PRINT_QUOTA_WARNING
print_warning(&warn[i]);
#endif
- quota_send_warning(warn[i].w_dq_type, warn[i].w_dq_id,
+ quota_send_warning(warn[i].w_dq_own,
warn[i].w_sb->s_dev, warn[i].w_type);
}
}

static int ignore_hardlimit(struct dquot *dquot)
{
- struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+ struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_own.type];

return capable(CAP_SYS_RESOURCE) &&
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
@@ -1256,7 +1256,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes,
{
qsize_t newinodes = dquot->dq_dqb.dqb_curinodes + inodes;

- if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
+ if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_own.type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return 0;

@@ -1281,7 +1281,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes,
dquot->dq_dqb.dqb_itime == 0) {
prepare_warning(warn, dquot, QUOTA_NL_ISOFTWARN);
dquot->dq_dqb.dqb_itime = get_seconds() +
- sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+ sb_dqopt(dquot->dq_sb)->info[dquot->dq_own.type].dqi_igrace;
}

return 0;
@@ -1294,7 +1294,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc,
qsize_t tspace;
struct super_block *sb = dquot->dq_sb;

- if (!sb_has_quota_limits_enabled(sb, dquot->dq_type) ||
+ if (!sb_has_quota_limits_enabled(sb, dquot->dq_own.type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return 0;

@@ -1325,7 +1325,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc,
if (!prealloc) {
prepare_warning(warn, dquot, QUOTA_NL_BSOFTWARN);
dquot->dq_dqb.dqb_btime = get_seconds() +
- sb_dqopt(sb)->info[dquot->dq_type].dqi_bgrace;
+ sb_dqopt(sb)->info[dquot->dq_own.type].dqi_bgrace;
}
else
/*
@@ -1344,7 +1344,7 @@ static int info_idq_free(struct dquot *dquot, qsize_t inodes)

if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit ||
- !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type))
+ !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_own.type))
return QUOTA_NL_NOWARN;

newinodes = dquot->dq_dqb.dqb_curinodes - inodes;
@@ -1390,7 +1390,6 @@ static int dquot_active(const struct inode *inode)
*/
static void __dquot_initialize(struct inode *inode, int type)
{
- unsigned int id = 0;
int cnt;
struct dquot *got[MAXQUOTAS];
struct super_block *sb = inode->i_sb;
@@ -1403,18 +1402,19 @@ static void __dquot_initialize(struct inode *inode, int type)

/* First get references to structures we might need. */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ struct qown qown;
got[cnt] = NULL;
if (type != -1 && cnt != type)
continue;
switch (cnt) {
case USRQUOTA:
- id = inode->i_uid;
+ qown = make_qown_uid(inode->i_uid);
break;
case GRPQUOTA:
- id = inode->i_gid;
+ qown = make_qown_gid(inode->i_gid);
break;
}
- got[cnt] = dqget(sb, id, cnt);
+ got[cnt] = dqget(sb, qown);
}

down_write(&sb_dqopt(sb)->dqptr_sem);
@@ -1897,10 +1897,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
if (!dquot_active(inode))
return 0;

- if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid)
- transfer_to[USRQUOTA] = dqget(sb, iattr->ia_uid, USRQUOTA);
- if (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)
- transfer_to[GRPQUOTA] = dqget(sb, iattr->ia_gid, GRPQUOTA);
+ if (iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid))
+ transfer_to[USRQUOTA] = dqget(sb, make_qown_uid(iattr->ia_uid));
+ if (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))
+ transfer_to[GRPQUOTA] = dqget(sb, make_qown_gid(iattr->ia_gid));

ret = __dquot_transfer(inode, transfer_to);
dqput_all(transfer_to);
@@ -2360,9 +2360,9 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)

memset(di, 0, sizeof(*di));
di->d_version = FS_DQUOT_VERSION;
- di->d_flags = dquot->dq_type == USRQUOTA ?
+ di->d_flags = dquot->dq_own.type == USRQUOTA ?
FS_USER_QUOTA : FS_GROUP_QUOTA;
- di->d_id = dquot->dq_id;
+ di->d_id = from_qown_munged(current_user_ns(), dquot->dq_own);

spin_lock(&dq_data_lock);
di->d_blk_hardlimit = stoqb(dm->dqb_bhardlimit);
@@ -2376,12 +2376,12 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
spin_unlock(&dq_data_lock);
}

-int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_get_dqblk(struct super_block *sb, struct qown qown,
struct fs_disk_quota *di)
{
struct dquot *dquot;

- dquot = dqget(sb, id, type);
+ dquot = dqget(sb, qown);
if (!dquot)
return -ESRCH;
do_get_dqblk(dquot, di);
@@ -2401,7 +2401,7 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
{
struct mem_dqblk *dm = &dquot->dq_dqb;
int check_blim = 0, check_ilim = 0;
- struct mem_dqinfo *dqi = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+ struct mem_dqinfo *dqi = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_own.type];

if (di->d_fieldmask & ~VFS_FS_DQ_MASK)
return -EINVAL;
@@ -2488,13 +2488,13 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
return 0;
}

-int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, struct qown qown,
struct fs_disk_quota *di)
{
struct dquot *dquot;
int rc;

- dquot = dqget(sb, id, type);
+ dquot = dqget(sb, qown);
if (!dquot) {
rc = -ESRCH;
goto out;
diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
index d67908b..82e99d7 100644
--- a/fs/quota/netlink.c
+++ b/fs/quota/netlink.c
@@ -30,7 +30,7 @@ static struct genl_family quota_genl_family = {
*
*/

-void quota_send_warning(short type, unsigned int id, dev_t dev,
+void quota_send_warning(struct qown qown, dev_t dev,
const char warntype)
{
static atomic_t seq;
@@ -56,10 +56,11 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
"VFS: Cannot store netlink header in quota warning.\n");
goto err_out;
}
- ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type);
+ ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, qown.type);
if (ret)
goto attr_err_out;
- ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id);
+ ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID,
+ from_qown_munged(&init_user_ns, qown));
if (ret)
goto attr_err_out;
ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
@@ -71,7 +72,8 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev));
if (ret)
goto attr_err_out;
- ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
+ ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID,
+ from_kuid_munged(&init_user_ns, current_uid()));
if (ret)
goto attr_err_out;
genlmsg_end(skb, msg_head);
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 6f15578..a31dc42 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -32,8 +32,8 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
/* allow to query information for dquots we "own" */
case Q_GETQUOTA:
case Q_XGETQUOTA:
- if ((type == USRQUOTA && current_euid() == id) ||
- (type == GRPQUOTA && in_egroup_p(id)))
+ if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
+ (type == GRPQUOTA && in_egroup_p(make_kgid(current_user_ns(), id))))
break;
/*FALLTHROUGH*/
default:
@@ -130,13 +130,17 @@ static void copy_to_if_dqblk(struct if_dqblk *dst, struct fs_disk_quota *src)
static int quota_getquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
+ struct qown qown;
struct fs_disk_quota fdq;
struct if_dqblk idq;
int ret;

if (!sb->s_qcop->get_dqblk)
return -ENOSYS;
- ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
+ qown = make_qown(current_user_ns(), type, id);
+ if (!qown_valid(qown))
+ return -EINVAL;
+ ret = sb->s_qcop->get_dqblk(sb, qown, &fdq);
if (ret)
return ret;
copy_to_if_dqblk(&idq, &fdq);
@@ -176,13 +180,17 @@ static int quota_setquota(struct super_block *sb, int type, qid_t id,
{
struct fs_disk_quota fdq;
struct if_dqblk idq;
+ struct qown qown;

if (copy_from_user(&idq, addr, sizeof(idq)))
return -EFAULT;
if (!sb->s_qcop->set_dqblk)
return -ENOSYS;
+ qown = make_qown(current_user_ns(), type, id);
+ if (!qown_valid(qown))
+ return -EINVAL;
copy_from_if_dqblk(&fdq, &idq);
- return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
+ return sb->s_qcop->set_dqblk(sb, qown, &fdq);
}

static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr)
@@ -213,23 +221,31 @@ static int quota_setxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
struct fs_disk_quota fdq;
+ struct qown qown;

if (copy_from_user(&fdq, addr, sizeof(fdq)))
return -EFAULT;
if (!sb->s_qcop->set_dqblk)
return -ENOSYS;
- return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
+ qown = make_qown(current_user_ns(), type, id);
+ if (!qown_valid(qown))
+ return -EINVAL;
+ return sb->s_qcop->set_dqblk(sb, qown, &fdq);
}

static int quota_getxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
struct fs_disk_quota fdq;
+ struct qown qown;
int ret;

if (!sb->s_qcop->get_dqblk)
return -ENOSYS;
- ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
+ qown = make_qown(current_user_ns(), type, id);
+ if (!qown_valid(qown))
+ return -EINVAL;
+ ret = sb->s_qcop->get_dqblk(sb, qown, &fdq);
if (!ret && copy_to_user(addr, &fdq, sizeof(fdq)))
return -EFAULT;
return ret;
diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
index e41c1becf..fa48156 100644
--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -22,10 +22,12 @@ MODULE_LICENSE("GPL");

#define __QUOTA_QT_PARANOIA

-static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
+static int get_index(struct qtree_mem_dqinfo *info, struct qown qown, int depth)
{
unsigned int epb = info->dqi_usable_bs >> 2;
+ qid_t id;

+ id = from_qown(&init_user_ns, qown);
depth = info->dqi_qtree_depth - depth - 1;
while (depth--)
id /= epb;
@@ -244,7 +246,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
/* This is enough as the block is already zeroed and the entry
* list is empty... */
info->dqi_free_entry = blk;
- mark_info_dirty(dquot->dq_sb, dquot->dq_type);
+ mark_info_dirty(dquot->dq_sb, dquot->dq_own.type);
}
/* Block will be full? */
if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
@@ -313,7 +315,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
}
}
ref = (__le32 *)buf;
- newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+ newblk = le32_to_cpu(ref[get_index(info, dquot->dq_own, depth)]);
if (!newblk)
newson = 1;
if (depth == info->dqi_qtree_depth - 1) {
@@ -322,7 +324,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
quota_error(dquot->dq_sb, "Inserting already present "
"quota entry (block %u)",
le32_to_cpu(ref[get_index(info,
- dquot->dq_id, depth)]));
+ dquot->dq_own, depth)]));
ret = -EIO;
goto out_buf;
}
@@ -332,7 +334,7 @@ static int do_insert_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
ret = do_insert_tree(info, dquot, &newblk, depth+1);
}
if (newson && ret >= 0) {
- ref[get_index(info, dquot->dq_id, depth)] =
+ ref[get_index(info, dquot->dq_own, depth)] =
cpu_to_le32(newblk);
ret = write_blk(info, *treeblk, buf);
} else if (newact && ret < 0) {
@@ -357,7 +359,7 @@ static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
*/
int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct super_block *sb = dquot->dq_sb;
ssize_t ret;
char *ddquot = getdqbuf(info->dqi_entry_size);
@@ -472,7 +474,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
*blk);
goto out_buf;
}
- newblk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+ newblk = le32_to_cpu(ref[get_index(info, dquot->dq_own, depth)]);
if (depth == info->dqi_qtree_depth - 1) {
ret = free_dqentry(info, dquot, newblk);
newblk = 0;
@@ -481,7 +483,7 @@ static int remove_tree(struct qtree_mem_dqinfo *info, struct dquot *dquot,
}
if (ret >= 0 && !newblk) {
int i;
- ref[get_index(info, dquot->dq_id, depth)] = cpu_to_le32(0);
+ ref[get_index(info, dquot->dq_own, depth)] = cpu_to_le32(0);
/* Block got empty? */
for (i = 0; i < (info->dqi_usable_bs >> 2) && !ref[i]; i++)
;
@@ -538,8 +540,9 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
ddquot += info->dqi_entry_size;
}
if (i == qtree_dqstr_in_blk(info)) {
- quota_error(dquot->dq_sb, "Quota for id %u referenced "
- "but not present", dquot->dq_id);
+ quota_error(dquot->dq_sb,
+ "Quota for id %u referenced but not present",
+ from_qown(&init_user_ns, dquot->dq_own));
ret = -EIO;
goto out_buf;
} else {
@@ -568,7 +571,7 @@ static loff_t find_tree_dqentry(struct qtree_mem_dqinfo *info,
goto out_buf;
}
ret = 0;
- blk = le32_to_cpu(ref[get_index(info, dquot->dq_id, depth)]);
+ blk = le32_to_cpu(ref[get_index(info, dquot->dq_own, depth)]);
if (!blk) /* No reference? */
goto out_buf;
if (depth < info->dqi_qtree_depth - 1)
@@ -589,7 +592,7 @@ static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,

int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct super_block *sb = dquot->dq_sb;
loff_t offset;
char *ddquot;
@@ -607,8 +610,10 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
offset = find_dqentry(info, dquot);
if (offset <= 0) { /* Entry not present? */
if (offset < 0)
- quota_error(sb, "Can't read quota structure "
- "for id %u", dquot->dq_id);
+ quota_error(sb,"Can't read quota structure "
+ "for id %u",
+ from_qown(&init_user_ns,
+ dquot->dq_own));
dquot->dq_off = 0;
set_bit(DQ_FAKE_B, &dquot->dq_flags);
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
@@ -626,7 +631,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
if (ret >= 0)
ret = -EIO;
quota_error(sb, "Error while reading quota structure for id %u",
- dquot->dq_id);
+ from_qown(&init_user_ns, dquot->dq_own));
set_bit(DQ_FAKE_B, &dquot->dq_flags);
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
kfree(ddquot);
diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c
index 34b37a6..ec37d70 100644
--- a/fs/quota/quota_v1.c
+++ b/fs/quota/quota_v1.c
@@ -54,7 +54,7 @@ static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)

static int v1_read_dqblk(struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_own.type;
struct v1_disk_dqblk dqblk;

if (!sb_dqopt(dquot->dq_sb)->files[type])
@@ -63,7 +63,8 @@ static int v1_read_dqblk(struct dquot *dquot)
/* Set structure to 0s in case read fails/is after end of file */
memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
- sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id));
+ sizeof(struct v1_disk_dqblk),
+ v1_dqoff(from_qown(&init_user_ns, dquot->dq_own)));

v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
@@ -78,12 +79,13 @@ static int v1_read_dqblk(struct dquot *dquot)

static int v1_commit_dqblk(struct dquot *dquot)
{
- short type = dquot->dq_type;
+ short type = dquot->dq_own.type;
ssize_t ret;
struct v1_disk_dqblk dqblk;

v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
- if (dquot->dq_id == 0) {
+ if (((type == USRQUOTA) && uid_eq(dquot->dq_own.uid, GLOBAL_ROOT_UID)) ||
+ ((type == GRPQUOTA) && gid_eq(dquot->dq_own.gid, GLOBAL_ROOT_GID))) {
dqblk.dqb_btime =
sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
dqblk.dqb_itime =
@@ -93,7 +95,7 @@ static int v1_commit_dqblk(struct dquot *dquot)
if (sb_dqopt(dquot->dq_sb)->files[type])
ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
(char *)&dqblk, sizeof(struct v1_disk_dqblk),
- v1_dqoff(dquot->dq_id));
+ v1_dqoff(from_qown(&init_user_ns, dquot->dq_own)));
if (ret != sizeof(struct v1_disk_dqblk)) {
quota_error(dquot->dq_sb, "dquota write failed");
if (ret >= 0)
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index f1ab360..1c26279 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -196,7 +196,7 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
struct v2r0_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;

d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
@@ -206,7 +206,7 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_qown(&init_user_ns, dquot->dq_own));
if (qtree_entry_unused(info, dp))
d->dqb_itime = cpu_to_le64(1);
}
@@ -215,11 +215,13 @@ static int v2r0_is_id(void *dp, struct dquot *dquot)
{
struct v2r0_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
+ struct qown qown;

if (qtree_entry_unused(info, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+ qown = make_qown(&init_user_ns, dquot->dq_own.type, le32_to_cpu(d->dqb_id));
+ return qown_eq(qown, dquot->dq_own);
}

static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
@@ -247,7 +249,7 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
struct v2r1_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;

d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
@@ -257,7 +259,7 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_qown(&init_user_ns, dquot->dq_own));
if (qtree_entry_unused(info, dp))
d->dqb_itime = cpu_to_le64(1);
}
@@ -266,26 +268,28 @@ static int v2r1_is_id(void *dp, struct dquot *dquot)
{
struct v2r1_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv;
+ struct qown qown;

if (qtree_entry_unused(info, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+ qown = make_qown(&init_user_ns, dquot->dq_own.type, le32_to_cpu(d->dqb_id));
+ return qown_eq(qown, dquot->dq_own);
}

static int v2_read_dquot(struct dquot *dquot)
{
- return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv, dquot);
}

static int v2_write_dquot(struct dquot *dquot)
{
- return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv, dquot);
}

static int v2_release_dquot(struct dquot *dquot)
{
- return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_own.type)->dqi_priv, dquot);
}

static int v2_free_file_info(struct super_block *sb, int type)
diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c
index fed504f..589e9c7 100644
--- a/fs/xfs/xfs_quotaops.c
+++ b/fs/xfs/xfs_quotaops.c
@@ -97,28 +97,29 @@ xfs_fs_set_xstate(
STATIC int
xfs_fs_get_dqblk(
struct super_block *sb,
- int type,
- qid_t id,
+ struct qown qown,
struct fs_disk_quota *fdq)
{
struct xfs_mount *mp = XFS_M(sb);
+ xfs_dqid_t xfs_id;

if (!XFS_IS_QUOTA_RUNNING(mp))
return -ENOSYS;
if (!XFS_IS_QUOTA_ON(mp))
return -ESRCH;

- return -xfs_qm_scall_getquota(mp, id, xfs_quota_type(type), fdq);
+ xfs_id = from_qown(&init_user_ns, qown);
+ return -xfs_qm_scall_getquota(mp, xfs_id, xfs_quota_type(qown.type), fdq);
}

STATIC int
xfs_fs_set_dqblk(
struct super_block *sb,
- int type,
- qid_t id,
+ struct qown qown,
struct fs_disk_quota *fdq)
{
struct xfs_mount *mp = XFS_M(sb);
+ xfs_dqid_t xfs_id;

if (sb->s_flags & MS_RDONLY)
return -EROFS;
@@ -127,7 +128,8 @@ xfs_fs_set_dqblk(
if (!XFS_IS_QUOTA_ON(mp))
return -ESRCH;

- return -xfs_qm_scall_setqlim(mp, id, xfs_quota_type(type), fdq);
+ xfs_id = from_qown(&init_user_ns, qown);
+ return -xfs_qm_scall_setqlim(mp, xfs_id, xfs_quota_type(qown.type), fdq);
}

const struct quotactl_ops xfs_quotactl_operations = {
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index bcb6054..3e9fbb8 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -575,12 +575,14 @@ xfs_quota_warn(
struct xfs_dquot *dqp,
int type)
{
+ int qtype;
+ struct qown qown;
/* no warnings for project quotas - we just return ENOSPC later */
if (dqp->dq_flags & XFS_DQ_PROJ)
return;
- quota_send_warning((dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA,
- be32_to_cpu(dqp->q_core.d_id), mp->m_super->s_dev,
- type);
+ qtype = (dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA;
+ qown = make_qown(&init_user_ns, qtype, be32_to_cpu(dqp->q_core.d_id));
+ quota_send_warning(qown, mp->m_super->s_dev, type);
}

/*
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 524ede8..6ebb782 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -181,10 +181,161 @@ enum {
#include <linux/dqblk_v2.h>

#include <linux/atomic.h>
+#include <linux/uidgid.h>

typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
typedef long long qsize_t; /* Type in which we store sizes */

+struct qown { /* Type in which we store the quota owner */
+ union {
+ kuid_t uid;
+ kgid_t gid;
+ unsigned int prj;
+ };
+ int type; /* USRQUOTA (uid) or GRPQUOTA (gid) or XQM_PRJQUOTA (prj) */
+};
+
+static inline bool qown_eq(struct qown left, struct qown right)
+{
+ if (left.type != right.type)
+ return false;
+ switch(left.type) {
+ case USRQUOTA:
+ return uid_eq(left.uid, right.uid);
+ case GRPQUOTA:
+ return gid_eq(left.gid, right.gid);
+ case XQM_PRJQUOTA:
+ return left.prj == right.prj;
+ default:
+ BUG();
+ }
+}
+
+static inline bool qown_lt(struct qown left, struct qown right)
+{
+ if (left.type < right.type)
+ return true;
+ if (left.type > right.type)
+ return false;
+ switch (left.type) {
+ case USRQUOTA:
+ return uid_lt(left.uid, right.uid);
+ case GRPQUOTA:
+ return gid_lt(left.gid, right.gid);
+ case XQM_PRJQUOTA:
+ return left.prj < right.prj;
+ default:
+ BUG();
+ }
+}
+
+static inline u32 from_qown(struct user_namespace *user_ns, struct qown qown)
+{
+ switch (qown.type) {
+ case USRQUOTA:
+ return from_kuid(user_ns, qown.uid);
+ case GRPQUOTA:
+ return from_kgid(user_ns, qown.gid);
+ case XQM_PRJQUOTA:
+ return (user_ns == &init_user_ns) ? qown.prj : -1;
+ default:
+ BUG();
+ }
+}
+
+static inline u32 from_qown_munged(struct user_namespace *user_ns,
+ struct qown qown)
+{
+ switch (qown.type) {
+ case USRQUOTA:
+ return from_kuid_munged(user_ns, qown.uid);
+ case GRPQUOTA:
+ return from_kgid_munged(user_ns, qown.gid);
+ case XQM_PRJQUOTA:
+ return (user_ns == &init_user_ns) ? qown.prj : -1;
+ default:
+ BUG();
+ }
+}
+
+static inline struct qown make_qown(struct user_namespace *user_ns,
+ int type, qid_t id)
+{
+ struct qown qown;
+
+ qown.type = type;
+ switch (type) {
+ case USRQUOTA:
+ qown.uid = make_kuid(user_ns, id);
+ break;
+ case GRPQUOTA:
+ qown.gid = make_kgid(user_ns, id);
+ break;
+ case XQM_PRJQUOTA:
+ if (user_ns == &init_user_ns)
+ qown.prj = id;
+ else
+ qown.prj = -1;
+ break;
+ default:
+ BUG();
+ }
+ return qown;
+}
+
+static inline struct qown make_qown_invalid(int type)
+{
+ struct qown qown;
+
+ qown.type = type;
+ switch (type) {
+ case USRQUOTA:
+ qown.uid = INVALID_UID;
+ break;
+ case GRPQUOTA:
+ qown.gid = INVALID_GID;
+ break;
+ case XQM_PRJQUOTA:
+ qown.prj = -1;
+ break;
+ default:
+ BUG();
+ }
+ return qown;
+}
+
+static inline struct qown make_qown_uid(kuid_t uid)
+{
+ struct qown qown = {
+ .type = USRQUOTA,
+ .uid = uid,
+ };
+ return qown;
+}
+
+static inline struct qown make_qown_gid(kgid_t gid)
+{
+ struct qown qown = {
+ .type = GRPQUOTA,
+ .gid = gid,
+ };
+ return qown;
+}
+
+static inline bool qown_valid(struct qown qown)
+{
+ switch (qown.type) {
+ case USRQUOTA:
+ return uid_valid(qown.uid);
+ case GRPQUOTA:
+ return gid_valid(qown.gid);
+ case XQM_PRJQUOTA:
+ return qown.prj != (unsigned int)-1;
+ default:
+ BUG();
+ }
+}
+
extern spinlock_t dq_data_lock;

/* Maximal numbers of writes for quota operation (insert/delete/update)
@@ -294,10 +445,9 @@ struct dquot {
atomic_t dq_count; /* Use count */
wait_queue_head_t dq_wait_unused; /* Wait queue for dquot to become unused */
struct super_block *dq_sb; /* superblock this applies to */
- unsigned int dq_id; /* ID this applies to (uid, gid) */
+ struct qown dq_own; /* ID this applies to (uid, gid) */
loff_t dq_off; /* Offset of dquot on disk */
unsigned long dq_flags; /* See DQ_* */
- short dq_type; /* Type of quota */
struct mem_dqblk dq_dqb; /* Diskquota usage */
};

@@ -336,8 +486,8 @@ struct quotactl_ops {
int (*quota_sync)(struct super_block *, int);
int (*get_info)(struct super_block *, int, struct if_dqinfo *);
int (*set_info)(struct super_block *, int, struct if_dqinfo *);
- int (*get_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
- int (*set_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
+ int (*get_dqblk)(struct super_block *, struct qown, struct fs_disk_quota *);
+ int (*set_dqblk)(struct super_block *, struct qown, struct fs_disk_quota *);
int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
int (*set_xstate)(struct super_block *, unsigned int, int);
};
@@ -386,10 +536,10 @@ static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
}

#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
-extern void quota_send_warning(short type, unsigned int id, dev_t dev,
+extern void quota_send_warning(struct qown qown, dev_t dev,
const char warntype);
#else
-static inline void quota_send_warning(short type, unsigned int id, dev_t dev,
+static inline void quota_send_warning(struct qown qown, dev_t dev,
const char warntype)
{
return;
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index ec6b65f..ed9a5e4 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -44,7 +44,7 @@ void inode_sub_rsv_space(struct inode *inode, qsize_t number);

void dquot_initialize(struct inode *inode);
void dquot_drop(struct inode *inode);
-struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
+struct dquot *dqget(struct super_block *sb, struct qown qown);
void dqput(struct dquot *dquot);
int dquot_scan_active(struct super_block *sb,
int (*fn)(struct dquot *dquot, unsigned long priv),
@@ -87,9 +87,9 @@ int dquot_writeback_dquots(struct super_block *sb, int type);
int dquot_quota_sync(struct super_block *sb, int type);
int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
-int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_get_dqblk(struct super_block *sb, struct qown id,
struct fs_disk_quota *di);
-int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, struct qown id,
struct fs_disk_quota *di);

int __dquot_transfer(struct inode *inode, struct dquot **transfer_to);
diff --git a/init/Kconfig b/init/Kconfig
index 2a388e5..a0bccce 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -928,8 +928,6 @@ config UIDGID_CONVERTED
depends on IMA = n
depends on EVM = n
depends on FS_POSIX_ACL = n
- depends on QUOTA = n
- depends on QUOTACTL = n

# Networking
depends on NET_9P = n
--
1.7.5.4

2012-08-28 19:10:22

by Eric W. Biederman

[permalink] [raw]
Subject: [PATCH] userns: Add basic quota support v4


Add the data type struct kqid which holds the kernel internal form of
the owning identifier of a quota. struct kqid is a replacement for
the implicit union of uid, gid and project stored in an unsigned int
and the quota type field that is was used in the quota data
structures. Making the data type explicit allows the kuid_t and
kgid_t type safety to propogate more thoroughly through the code,
revealing more places where uid/gid conversions need be made.

Along with the data type struct kqid comes the helper functions
qid_eq, qid_lt, from_kqid, from_kqid_munged, qid_valid, make_kqid,
make_kqid_invalid, make_kqid_uid, make_kqid_gid.

Change struct dquot dq_id to a struct kqid and remove the now
unecessary dq_type.

Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
and dquot_set_dqblk to use struct kqid.

Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
the change in quota structures and signatures. The ocfs2 changes are
larger than most because of the extensive tracing throughout the ocfs2
quota code that prints out dq_id.

v4:
- Rename struct qown struct kqid and associated changes
to the naming of the helper functions.
- Use qid_t to hold the userspace identifier representation
of quota identifiers in all new code.
v3:
- Add missing negation on qown_valid
v2:
- Renamed qown_t struct qown
- Added the quota type to struct qown.
- Removed enum quota_type (In this patch it was just noise)
- Added qown_lt, make_qown_invalid, make_qown_uid, make_qown_gid
- Taught qown to handle xfs project ids (but only in init_user_ns).
Q_XGETQUOTA calls .get_quotblk with project ids.

Cc: Steven Whitehouse <[email protected]>
Cc: Mark Fasheh <[email protected]>
Cc: Joel Becker <[email protected]>
Cc: Ben Myers <[email protected]>
Cc: Alex Elder <[email protected]>
Cc: Jan Kara <[email protected]>
Cc: Dmitry Monakhov <[email protected]>
Signed-off-by: Eric W. Biederman <[email protected]>
---
fs/ext3/super.c | 2 +-
fs/ext4/super.c | 2 +-
fs/gfs2/quota.c | 52 +++++++++------
fs/ocfs2/file.c | 6 +-
fs/ocfs2/quota_global.c | 43 +++++++-----
fs/ocfs2/quota_local.c | 15 +++--
fs/quota/dquot.c | 116 ++++++++++++++++----------------
fs/quota/netlink.c | 10 ++-
fs/quota/quota.c | 28 ++++++--
fs/quota/quota_tree.c | 23 ++++---
fs/quota/quota_v1.c | 12 ++--
fs/quota/quota_v2.c | 26 ++++---
fs/xfs/xfs_quotaops.c | 14 ++--
fs/xfs/xfs_trans_dquot.c | 8 ++-
include/linux/quota.h | 162 ++++++++++++++++++++++++++++++++++++++++++++--
include/linux/quotaops.h | 6 +-
init/Kconfig | 2 -
17 files changed, 364 insertions(+), 163 deletions(-)

diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index ff9bcdc..73e42f5 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -2814,7 +2814,7 @@ static int ext3_statfs (struct dentry * dentry, struct kstatfs * buf)

static inline struct inode *dquot_to_inode(struct dquot *dquot)
{
- return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ return sb_dqopt(dquot->dq_sb)->files[dquot->dq_id.type];
}

static int ext3_write_dquot(struct dquot *dquot)
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d76ec82..78e6036 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4796,7 +4796,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)

static inline struct inode *dquot_to_inode(struct dquot *dquot)
{
- return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+ return sb_dqopt(dquot->dq_sb)->files[dquot->dq_id.type];
}

static int ext4_write_dquot(struct dquot *dquot)
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index a3bde91..e27f8d6 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -1057,6 +1057,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
return 0;

for (x = 0; x < ip->i_res->rs_qa_qd_num; x++) {
+ int qtype;
+ struct kqid qid;
qd = ip->i_res->rs_qa_qd[x];

if (!((qd->qd_id == uid && test_bit(QDF_USER, &qd->qd_flags)) ||
@@ -1068,11 +1070,12 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
value += qd->qd_change;
spin_unlock(&qd_lru_lock);

+ qtype = test_bit(QDF_USER, &qd->qd_flags) ? USRQUOTA : GRPQUOTA;
+ qid = make_kqid(&init_user_ns, qtype, qd->qd_id);
if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
print_message(qd, "exceeded");
- quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
- USRQUOTA : GRPQUOTA, qd->qd_id,
- sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
+ quota_send_warning(qid, sdp->sd_vfs->s_dev,
+ QUOTA_NL_BHARDWARN);

error = -EDQUOT;
break;
@@ -1081,9 +1084,8 @@ int gfs2_quota_check(struct gfs2_inode *ip, u32 uid, u32 gid)
time_after_eq(jiffies, qd->qd_last_warn +
gfs2_tune_get(sdp,
gt_quota_warn_period) * HZ)) {
- quota_send_warning(test_bit(QDF_USER, &qd->qd_flags) ?
- USRQUOTA : GRPQUOTA, qd->qd_id,
- sdp->sd_vfs->s_dev, QUOTA_NL_BSOFTWARN);
+ quota_send_warning(qid, sdp->sd_vfs->s_dev,
+ QUOTA_NL_BSOFTWARN);
error = print_message(qd, "warning");
qd->qd_last_warn = jiffies;
}
@@ -1469,7 +1471,7 @@ static int gfs2_quota_get_xstate(struct super_block *sb,
return 0;
}

-static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
+static int gfs2_get_dqblk(struct super_block *sb, struct kqid qid,
struct fs_disk_quota *fdq)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
@@ -1477,20 +1479,24 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,
struct gfs2_quota_data *qd;
struct gfs2_holder q_gh;
int error;
+ int user;
+ u32 gfs_id;

memset(fdq, 0, sizeof(struct fs_disk_quota));

if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return -ESRCH; /* Crazy XFS error code */

- if (type == USRQUOTA)
- type = QUOTA_USER;
- else if (type == GRPQUOTA)
- type = QUOTA_GROUP;
+ gfs_id = from_kqid(&init_user_ns, qid);
+
+ if (qid.type == USRQUOTA)
+ user = QUOTA_USER;
+ else if (qid.type == GRPQUOTA)
+ user = QUOTA_GROUP;
else
return -EINVAL;

- error = qd_get(sdp, type, id, &qd);
+ error = qd_get(sdp, user, gfs_id, &qd);
if (error)
return error;
error = do_glock(qd, FORCE, &q_gh);
@@ -1499,8 +1505,8 @@ static int gfs2_get_dqblk(struct super_block *sb, int type, qid_t id,

qlvb = (struct gfs2_quota_lvb *)qd->qd_gl->gl_lvb;
fdq->d_version = FS_DQUOT_VERSION;
- fdq->d_flags = (type == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
- fdq->d_id = id;
+ fdq->d_flags = (user == QUOTA_USER) ? FS_USER_QUOTA : FS_GROUP_QUOTA;
+ fdq->d_id = gfs_id;
fdq->d_blk_hardlimit = be64_to_cpu(qlvb->qb_limit) << sdp->sd_fsb2bb_shift;
fdq->d_blk_softlimit = be64_to_cpu(qlvb->qb_warn) << sdp->sd_fsb2bb_shift;
fdq->d_bcount = be64_to_cpu(qlvb->qb_value) << sdp->sd_fsb2bb_shift;
@@ -1514,8 +1520,8 @@ out:
/* GFS2 only supports a subset of the XFS fields */
#define GFS2_FIELDMASK (FS_DQ_BSOFT|FS_DQ_BHARD|FS_DQ_BCOUNT)

-static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
- struct fs_disk_quota *fdq)
+static int gfs2_set_dqblk(struct super_block *sb,
+ struct kqid qid, struct fs_disk_quota *fdq)
{
struct gfs2_sbd *sdp = sb->s_fs_info;
struct gfs2_inode *ip = GFS2_I(sdp->sd_quota_inode);
@@ -1526,18 +1532,22 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
int alloc_required;
loff_t offset;
int error;
+ int user;
+ u32 gfs_id;

if (sdp->sd_args.ar_quota == GFS2_QUOTA_OFF)
return -ESRCH; /* Crazy XFS error code */

- switch(type) {
+ gfs_id = from_kqid(&init_user_ns, qid);
+
+ switch(qid.type) {
case USRQUOTA:
- type = QUOTA_USER;
+ user = QUOTA_USER;
if (fdq->d_flags != FS_USER_QUOTA)
return -EINVAL;
break;
case GRPQUOTA:
- type = QUOTA_GROUP;
+ user = QUOTA_GROUP;
if (fdq->d_flags != FS_GROUP_QUOTA)
return -EINVAL;
break;
@@ -1547,10 +1557,10 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,

if (fdq->d_fieldmask & ~GFS2_FIELDMASK)
return -EINVAL;
- if (fdq->d_id != id)
+ if (fdq->d_id != gfs_id)
return -EINVAL;

- error = qd_get(sdp, type, id, &qd);
+ error = qd_get(sdp, user, gfs_id, &qd);
if (error)
return error;

diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 46a1f6d..5a4ee77 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -1184,8 +1184,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & ATTR_UID && attr->ia_uid != inode->i_uid
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
- transfer_to[USRQUOTA] = dqget(sb, attr->ia_uid,
- USRQUOTA);
+ transfer_to[USRQUOTA] = dqget(sb, make_kqid_uid(attr->ia_uid));
if (!transfer_to[USRQUOTA]) {
status = -ESRCH;
goto bail_unlock;
@@ -1194,8 +1193,7 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
if (attr->ia_valid & ATTR_GID && attr->ia_gid != inode->i_gid
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
- transfer_to[GRPQUOTA] = dqget(sb, attr->ia_gid,
- GRPQUOTA);
+ transfer_to[GRPQUOTA] = dqget(sb, make_kqid_gid(attr->ia_gid));
if (!transfer_to[GRPQUOTA]) {
status = -ESRCH;
goto bail_unlock;
diff --git a/fs/ocfs2/quota_global.c b/fs/ocfs2/quota_global.c
index 0a86e30..dd4deaa 100644
--- a/fs/ocfs2/quota_global.c
+++ b/fs/ocfs2/quota_global.c
@@ -95,7 +95,7 @@ static void ocfs2_global_mem2diskdqb(void *dp, struct dquot *dquot)
struct ocfs2_global_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;

- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id));
d->dqb_use_count = cpu_to_le32(OCFS2_DQUOT(dquot)->dq_use_count);
d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
@@ -112,11 +112,14 @@ static int ocfs2_global_is_id(void *dp, struct dquot *dquot)
{
struct ocfs2_global_disk_dqblk *d = dp;
struct ocfs2_mem_dqinfo *oinfo =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
+ struct kqid qid;

if (qtree_entry_unused(&oinfo->dqi_gi, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+
+ qid = make_kqid(&init_user_ns, dquot->dq_id.type, le32_to_cpu(d->dqb_id));
+ return qid_eq(qid, dquot->dq_id);
}

struct qtree_fmt_operations ocfs2_global_ops = {
@@ -475,7 +478,7 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
{
int err, err2;
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
struct ocfs2_global_disk_dqblk dqblk;
s64 spacechange, inodechange;
@@ -504,7 +507,8 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
olditime = dquot->dq_dqb.dqb_itime;
oldbtime = dquot->dq_dqb.dqb_btime;
ocfs2_global_disk2memdqb(dquot, &dqblk);
- trace_ocfs2_sync_dquot(dquot->dq_id, dquot->dq_dqb.dqb_curspace,
+ trace_ocfs2_sync_dquot(from_kqid(&init_user_ns, dquot->dq_id),
+ dquot->dq_dqb.dqb_curspace,
(long long)spacechange,
dquot->dq_dqb.dqb_curinodes,
(long long)inodechange);
@@ -555,8 +559,8 @@ int __ocfs2_sync_dquot(struct dquot *dquot, int freeing)
err = ocfs2_qinfo_lock(info, freeing);
if (err < 0) {
mlog(ML_ERROR, "Failed to lock quota info, losing quota write"
- " (type=%d, id=%u)\n", dquot->dq_type,
- (unsigned)dquot->dq_id);
+ " (type=%d, id=%u)\n", dquot->dq_id.type,
+ (unsigned)from_kqid(&init_user_ns, dquot->dq_id));
goto out;
}
if (freeing)
@@ -591,9 +595,10 @@ static int ocfs2_sync_dquot_helper(struct dquot *dquot, unsigned long type)
struct ocfs2_super *osb = OCFS2_SB(sb);
int status = 0;

- trace_ocfs2_sync_dquot_helper(dquot->dq_id, dquot->dq_type,
+ trace_ocfs2_sync_dquot_helper(from_kqid(&init_user_ns, dquot->dq_id),
+ dquot->dq_id.type,
type, sb->s_id);
- if (type != dquot->dq_type)
+ if (type != dquot->dq_id.type)
goto out;
status = ocfs2_lock_global_qf(oinfo, 1);
if (status < 0)
@@ -643,7 +648,8 @@ static int ocfs2_write_dquot(struct dquot *dquot)
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
int status = 0;

- trace_ocfs2_write_dquot(dquot->dq_id, dquot->dq_type);
+ trace_ocfs2_write_dquot(from_kqid(&init_user_ns, dquot->dq_id),
+ dquot->dq_id.type);

handle = ocfs2_start_trans(osb, OCFS2_QWRITE_CREDITS);
if (IS_ERR(handle)) {
@@ -677,11 +683,12 @@ static int ocfs2_release_dquot(struct dquot *dquot)
{
handle_t *handle;
struct ocfs2_mem_dqinfo *oinfo =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
struct ocfs2_super *osb = OCFS2_SB(dquot->dq_sb);
int status = 0;

- trace_ocfs2_release_dquot(dquot->dq_id, dquot->dq_type);
+ trace_ocfs2_release_dquot(from_kqid(&init_user_ns, dquot->dq_id),
+ dquot->dq_id.type);

mutex_lock(&dquot->dq_lock);
/* Check whether we are not racing with some other dqget() */
@@ -691,7 +698,7 @@ static int ocfs2_release_dquot(struct dquot *dquot)
if (status < 0)
goto out;
handle = ocfs2_start_trans(osb,
- ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_type));
+ ocfs2_calc_qdel_credits(dquot->dq_sb, dquot->dq_id.type));
if (IS_ERR(handle)) {
status = PTR_ERR(handle);
mlog_errno(status);
@@ -733,13 +740,14 @@ static int ocfs2_acquire_dquot(struct dquot *dquot)
int ex = 0;
struct super_block *sb = dquot->dq_sb;
struct ocfs2_super *osb = OCFS2_SB(sb);
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct ocfs2_mem_dqinfo *info = sb_dqinfo(sb, type)->dqi_priv;
struct inode *gqinode = info->dqi_gqinode;
int need_alloc = ocfs2_global_qinit_alloc(sb, type);
handle_t *handle;

- trace_ocfs2_acquire_dquot(dquot->dq_id, type);
+ trace_ocfs2_acquire_dquot(from_kqid(&init_user_ns, dquot->dq_id),
+ type);
mutex_lock(&dquot->dq_lock);
/*
* We need an exclusive lock, because we're going to update use count
@@ -821,12 +829,13 @@ static int ocfs2_mark_dquot_dirty(struct dquot *dquot)
int sync = 0;
int status;
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct ocfs2_mem_dqinfo *oinfo = sb_dqinfo(sb, type)->dqi_priv;
handle_t *handle;
struct ocfs2_super *osb = OCFS2_SB(sb);

- trace_ocfs2_mark_dquot_dirty(dquot->dq_id, type);
+ trace_ocfs2_mark_dquot_dirty(from_kqid(&init_user_ns, dquot->dq_id),
+ type);

/* In case user set some limits, sync dquot immediately to global
* quota file so that information propagates quicker */
diff --git a/fs/ocfs2/quota_local.c b/fs/ocfs2/quota_local.c
index f100bf7..27fe7ee 100644
--- a/fs/ocfs2/quota_local.c
+++ b/fs/ocfs2/quota_local.c
@@ -501,7 +501,9 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
}
dqblk = (struct ocfs2_local_disk_dqblk *)(qbh->b_data +
ol_dqblk_block_off(sb, chunk, bit));
- dquot = dqget(sb, le64_to_cpu(dqblk->dqb_id), type);
+ dquot = dqget(sb,
+ make_kqid(&init_user_ns, type,
+ le64_to_cpu(dqblk->dqb_id)));
if (!dquot) {
status = -EIO;
mlog(ML_ERROR, "Failed to get quota structure "
@@ -881,7 +883,8 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
dqblk = (struct ocfs2_local_disk_dqblk *)(bh->b_data
+ ol_dqblk_block_offset(sb, od->dq_local_off));

- dqblk->dqb_id = cpu_to_le64(od->dq_dquot.dq_id);
+ dqblk->dqb_id = cpu_to_le64(from_kqid(&init_user_ns,
+ od->dq_dquot.dq_id));
spin_lock(&dq_data_lock);
dqblk->dqb_spacemod = cpu_to_le64(od->dq_dquot.dq_dqb.dqb_curspace -
od->dq_origspace);
@@ -891,7 +894,7 @@ static void olq_set_dquot(struct buffer_head *bh, void *private)
trace_olq_set_dquot(
(unsigned long long)le64_to_cpu(dqblk->dqb_spacemod),
(unsigned long long)le64_to_cpu(dqblk->dqb_inodemod),
- od->dq_dquot.dq_id);
+ from_kqid(&init_user_ns, od->dq_dquot.dq_id));
}

/* Write dquot to local quota file */
@@ -900,7 +903,7 @@ int ocfs2_local_write_dquot(struct dquot *dquot)
struct super_block *sb = dquot->dq_sb;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
struct buffer_head *bh;
- struct inode *lqinode = sb_dqopt(sb)->files[dquot->dq_type];
+ struct inode *lqinode = sb_dqopt(sb)->files[dquot->dq_id.type];
int status;

status = ocfs2_read_quota_phys_block(lqinode, od->dq_local_phys_blk,
@@ -1221,7 +1224,7 @@ static void olq_alloc_dquot(struct buffer_head *bh, void *private)
int ocfs2_create_local_dquot(struct dquot *dquot)
{
struct super_block *sb = dquot->dq_sb;
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct inode *lqinode = sb_dqopt(sb)->files[type];
struct ocfs2_quota_chunk *chunk;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
@@ -1275,7 +1278,7 @@ out:
int ocfs2_local_release_dquot(handle_t *handle, struct dquot *dquot)
{
int status;
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct ocfs2_dquot *od = OCFS2_DQUOT(dquot);
struct super_block *sb = dquot->dq_sb;
struct ocfs2_local_disk_chunk *dchunk;
diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c
index 36a29b7..f52625c 100644
--- a/fs/quota/dquot.c
+++ b/fs/quota/dquot.c
@@ -253,11 +253,13 @@ static qsize_t inode_get_rsv_space(struct inode *inode);
static void __dquot_initialize(struct inode *inode, int type);

static inline unsigned int
-hashfn(const struct super_block *sb, unsigned int id, int type)
+hashfn(const struct super_block *sb, struct kqid qid)
{
+ unsigned int id;
unsigned long tmp;

- tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - type);
+ id = from_kqid(&init_user_ns, qid);
+ tmp = (((unsigned long)sb>>L1_CACHE_SHIFT) ^ id) * (MAXQUOTAS - qid.type);
return (tmp + (tmp >> dq_hash_bits)) & dq_hash_mask;
}

@@ -267,7 +269,7 @@ hashfn(const struct super_block *sb, unsigned int id, int type)
static inline void insert_dquot_hash(struct dquot *dquot)
{
struct hlist_head *head;
- head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id, dquot->dq_type);
+ head = dquot_hash + hashfn(dquot->dq_sb, dquot->dq_id);
hlist_add_head(&dquot->dq_hash, head);
}

@@ -277,15 +279,14 @@ static inline void remove_dquot_hash(struct dquot *dquot)
}

static struct dquot *find_dquot(unsigned int hashent, struct super_block *sb,
- unsigned int id, int type)
+ struct kqid qid)
{
struct hlist_node *node;
struct dquot *dquot;

hlist_for_each (node, dquot_hash+hashent) {
dquot = hlist_entry(node, struct dquot, dq_hash);
- if (dquot->dq_sb == sb && dquot->dq_id == id &&
- dquot->dq_type == type)
+ if (dquot->dq_sb == sb && qid_eq(dquot->dq_id, qid))
return dquot;
}
return NULL;
@@ -351,7 +352,7 @@ int dquot_mark_dquot_dirty(struct dquot *dquot)
spin_lock(&dq_list_lock);
if (!test_and_set_bit(DQ_MOD_B, &dquot->dq_flags)) {
list_add(&dquot->dq_dirty, &sb_dqopt(dquot->dq_sb)->
- info[dquot->dq_type].dqi_dirty_list);
+ info[dquot->dq_id.type].dqi_dirty_list);
ret = 0;
}
spin_unlock(&dq_list_lock);
@@ -410,17 +411,17 @@ int dquot_acquire(struct dquot *dquot)
mutex_lock(&dquot->dq_lock);
mutex_lock(&dqopt->dqio_mutex);
if (!test_bit(DQ_READ_B, &dquot->dq_flags))
- ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_id.type]->read_dqblk(dquot);
if (ret < 0)
goto out_iolock;
set_bit(DQ_READ_B, &dquot->dq_flags);
/* Instantiate dquot if needed */
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) {
- ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot);
/* Write the info if needed */
- if (info_dirty(&dqopt->info[dquot->dq_type])) {
- ret2 = dqopt->ops[dquot->dq_type]->write_file_info(
- dquot->dq_sb, dquot->dq_type);
+ if (info_dirty(&dqopt->info[dquot->dq_id.type])) {
+ ret2 = dqopt->ops[dquot->dq_id.type]->write_file_info(
+ dquot->dq_sb, dquot->dq_id.type);
}
if (ret < 0)
goto out_iolock;
@@ -455,7 +456,7 @@ int dquot_commit(struct dquot *dquot)
/* Inactive dquot can be only if there was error during read/init
* => we have better not writing it */
if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags))
- ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot);
+ ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot);
else
ret = -EIO;
out_sem:
@@ -477,12 +478,12 @@ int dquot_release(struct dquot *dquot)
if (atomic_read(&dquot->dq_count) > 1)
goto out_dqlock;
mutex_lock(&dqopt->dqio_mutex);
- if (dqopt->ops[dquot->dq_type]->release_dqblk) {
- ret = dqopt->ops[dquot->dq_type]->release_dqblk(dquot);
+ if (dqopt->ops[dquot->dq_id.type]->release_dqblk) {
+ ret = dqopt->ops[dquot->dq_id.type]->release_dqblk(dquot);
/* Write the info */
- if (info_dirty(&dqopt->info[dquot->dq_type])) {
- ret2 = dqopt->ops[dquot->dq_type]->write_file_info(
- dquot->dq_sb, dquot->dq_type);
+ if (info_dirty(&dqopt->info[dquot->dq_id.type])) {
+ ret2 = dqopt->ops[dquot->dq_id.type]->write_file_info(
+ dquot->dq_sb, dquot->dq_id.type);
}
if (ret >= 0)
ret = ret2;
@@ -521,7 +522,7 @@ restart:
list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) {
if (dquot->dq_sb != sb)
continue;
- if (dquot->dq_type != type)
+ if (dquot->dq_id.type != type)
continue;
/* Wait for dquot users */
if (atomic_read(&dquot->dq_count)) {
@@ -741,7 +742,8 @@ void dqput(struct dquot *dquot)
#ifdef CONFIG_QUOTA_DEBUG
if (!atomic_read(&dquot->dq_count)) {
quota_error(dquot->dq_sb, "trying to free free dquot of %s %d",
- quotatypes[dquot->dq_type], dquot->dq_id);
+ quotatypes[dquot->dq_id.type],
+ from_kqid(&init_user_ns, dquot->dq_id));
BUG();
}
#endif
@@ -752,7 +754,7 @@ we_slept:
/* We have more than one user... nothing to do */
atomic_dec(&dquot->dq_count);
/* Releasing dquot during quotaoff phase? */
- if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_type) &&
+ if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_id.type) &&
atomic_read(&dquot->dq_count) == 1)
wake_up(&dquot->dq_wait_unused);
spin_unlock(&dq_list_lock);
@@ -815,7 +817,7 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
INIT_LIST_HEAD(&dquot->dq_dirty);
init_waitqueue_head(&dquot->dq_wait_unused);
dquot->dq_sb = sb;
- dquot->dq_type = type;
+ dquot->dq_id = make_kqid_invalid(type);
atomic_set(&dquot->dq_count, 1);

return dquot;
@@ -829,35 +831,35 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
* a) checking for quota flags under dq_list_lock and
* b) getting a reference to dquot before we release dq_list_lock
*/
-struct dquot *dqget(struct super_block *sb, unsigned int id, int type)
+struct dquot *dqget(struct super_block *sb, struct kqid qid)
{
- unsigned int hashent = hashfn(sb, id, type);
+ unsigned int hashent = hashfn(sb, qid);
struct dquot *dquot = NULL, *empty = NULL;

- if (!sb_has_quota_active(sb, type))
+ if (!sb_has_quota_active(sb, qid.type))
return NULL;
we_slept:
spin_lock(&dq_list_lock);
spin_lock(&dq_state_lock);
- if (!sb_has_quota_active(sb, type)) {
+ if (!sb_has_quota_active(sb, qid.type)) {
spin_unlock(&dq_state_lock);
spin_unlock(&dq_list_lock);
goto out;
}
spin_unlock(&dq_state_lock);

- dquot = find_dquot(hashent, sb, id, type);
+ dquot = find_dquot(hashent, sb, qid);
if (!dquot) {
if (!empty) {
spin_unlock(&dq_list_lock);
- empty = get_empty_dquot(sb, type);
+ empty = get_empty_dquot(sb, qid.type);
if (!empty)
schedule(); /* Try to wait for a moment... */
goto we_slept;
}
dquot = empty;
empty = NULL;
- dquot->dq_id = id;
+ dquot->dq_id = qid;
/* all dquots go on the inuse_list */
put_inuse(dquot);
/* hash it first so it can be found */
@@ -1129,8 +1131,7 @@ static void dquot_decr_space(struct dquot *dquot, qsize_t number)

struct dquot_warn {
struct super_block *w_sb;
- qid_t w_dq_id;
- short w_dq_type;
+ struct kqid w_dq_id;
short w_type;
};

@@ -1154,11 +1155,11 @@ static int need_print_warning(struct dquot_warn *warn)
if (!flag_print_warnings)
return 0;

- switch (warn->w_dq_type) {
+ switch (warn->w_dq_id.type) {
case USRQUOTA:
- return current_fsuid() == warn->w_dq_id;
+ return uid_eq(current_fsuid(), warn->w_dq_id.uid);
case GRPQUOTA:
- return in_group_p(warn->w_dq_id);
+ return in_group_p(warn->w_dq_id.gid);
}
return 0;
}
@@ -1184,7 +1185,7 @@ static void print_warning(struct dquot_warn *warn)
tty_write_message(tty, ": warning, ");
else
tty_write_message(tty, ": write failed, ");
- tty_write_message(tty, quotatypes[warn->w_dq_type]);
+ tty_write_message(tty, quotatypes[warn->w_dq_id.type]);
switch (warntype) {
case QUOTA_NL_IHARDWARN:
msg = " file limit reached.\r\n";
@@ -1218,7 +1219,6 @@ static void prepare_warning(struct dquot_warn *warn, struct dquot *dquot,
warn->w_type = warntype;
warn->w_sb = dquot->dq_sb;
warn->w_dq_id = dquot->dq_id;
- warn->w_dq_type = dquot->dq_type;
}

/*
@@ -1236,14 +1236,14 @@ static void flush_warnings(struct dquot_warn *warn)
#ifdef CONFIG_PRINT_QUOTA_WARNING
print_warning(&warn[i]);
#endif
- quota_send_warning(warn[i].w_dq_type, warn[i].w_dq_id,
+ quota_send_warning(warn[i].w_dq_id,
warn[i].w_sb->s_dev, warn[i].w_type);
}
}

static int ignore_hardlimit(struct dquot *dquot)
{
- struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+ struct mem_dqinfo *info = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type];

return capable(CAP_SYS_RESOURCE) &&
(info->dqi_format->qf_fmt_id != QFMT_VFS_OLD ||
@@ -1256,7 +1256,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes,
{
qsize_t newinodes = dquot->dq_dqb.dqb_curinodes + inodes;

- if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type) ||
+ if (!sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_id.type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return 0;

@@ -1281,7 +1281,7 @@ static int check_idq(struct dquot *dquot, qsize_t inodes,
dquot->dq_dqb.dqb_itime == 0) {
prepare_warning(warn, dquot, QUOTA_NL_ISOFTWARN);
dquot->dq_dqb.dqb_itime = get_seconds() +
- sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace;
+ sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type].dqi_igrace;
}

return 0;
@@ -1294,7 +1294,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc,
qsize_t tspace;
struct super_block *sb = dquot->dq_sb;

- if (!sb_has_quota_limits_enabled(sb, dquot->dq_type) ||
+ if (!sb_has_quota_limits_enabled(sb, dquot->dq_id.type) ||
test_bit(DQ_FAKE_B, &dquot->dq_flags))
return 0;

@@ -1325,7 +1325,7 @@ static int check_bdq(struct dquot *dquot, qsize_t space, int prealloc,
if (!prealloc) {
prepare_warning(warn, dquot, QUOTA_NL_BSOFTWARN);
dquot->dq_dqb.dqb_btime = get_seconds() +
- sb_dqopt(sb)->info[dquot->dq_type].dqi_bgrace;
+ sb_dqopt(sb)->info[dquot->dq_id.type].dqi_bgrace;
}
else
/*
@@ -1344,7 +1344,7 @@ static int info_idq_free(struct dquot *dquot, qsize_t inodes)

if (test_bit(DQ_FAKE_B, &dquot->dq_flags) ||
dquot->dq_dqb.dqb_curinodes <= dquot->dq_dqb.dqb_isoftlimit ||
- !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_type))
+ !sb_has_quota_limits_enabled(dquot->dq_sb, dquot->dq_id.type))
return QUOTA_NL_NOWARN;

newinodes = dquot->dq_dqb.dqb_curinodes - inodes;
@@ -1390,7 +1390,6 @@ static int dquot_active(const struct inode *inode)
*/
static void __dquot_initialize(struct inode *inode, int type)
{
- unsigned int id = 0;
int cnt;
struct dquot *got[MAXQUOTAS];
struct super_block *sb = inode->i_sb;
@@ -1403,18 +1402,19 @@ static void __dquot_initialize(struct inode *inode, int type)

/* First get references to structures we might need. */
for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
+ struct kqid qid;
got[cnt] = NULL;
if (type != -1 && cnt != type)
continue;
switch (cnt) {
case USRQUOTA:
- id = inode->i_uid;
+ qid = make_kqid_uid(inode->i_uid);
break;
case GRPQUOTA:
- id = inode->i_gid;
+ qid = make_kqid_gid(inode->i_gid);
break;
}
- got[cnt] = dqget(sb, id, cnt);
+ got[cnt] = dqget(sb, qid);
}

down_write(&sb_dqopt(sb)->dqptr_sem);
@@ -1897,10 +1897,10 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr)
if (!dquot_active(inode))
return 0;

- if (iattr->ia_valid & ATTR_UID && iattr->ia_uid != inode->i_uid)
- transfer_to[USRQUOTA] = dqget(sb, iattr->ia_uid, USRQUOTA);
- if (iattr->ia_valid & ATTR_GID && iattr->ia_gid != inode->i_gid)
- transfer_to[GRPQUOTA] = dqget(sb, iattr->ia_gid, GRPQUOTA);
+ if (iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid))
+ transfer_to[USRQUOTA] = dqget(sb, make_kqid_uid(iattr->ia_uid));
+ if (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid))
+ transfer_to[GRPQUOTA] = dqget(sb, make_kqid_gid(iattr->ia_gid));

ret = __dquot_transfer(inode, transfer_to);
dqput_all(transfer_to);
@@ -2360,9 +2360,9 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)

memset(di, 0, sizeof(*di));
di->d_version = FS_DQUOT_VERSION;
- di->d_flags = dquot->dq_type == USRQUOTA ?
+ di->d_flags = dquot->dq_id.type == USRQUOTA ?
FS_USER_QUOTA : FS_GROUP_QUOTA;
- di->d_id = dquot->dq_id;
+ di->d_id = from_kqid_munged(current_user_ns(), dquot->dq_id);

spin_lock(&dq_data_lock);
di->d_blk_hardlimit = stoqb(dm->dqb_bhardlimit);
@@ -2376,12 +2376,12 @@ static void do_get_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
spin_unlock(&dq_data_lock);
}

-int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_get_dqblk(struct super_block *sb, struct kqid qid,
struct fs_disk_quota *di)
{
struct dquot *dquot;

- dquot = dqget(sb, id, type);
+ dquot = dqget(sb, qid);
if (!dquot)
return -ESRCH;
do_get_dqblk(dquot, di);
@@ -2401,7 +2401,7 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
{
struct mem_dqblk *dm = &dquot->dq_dqb;
int check_blim = 0, check_ilim = 0;
- struct mem_dqinfo *dqi = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_type];
+ struct mem_dqinfo *dqi = &sb_dqopt(dquot->dq_sb)->info[dquot->dq_id.type];

if (di->d_fieldmask & ~VFS_FS_DQ_MASK)
return -EINVAL;
@@ -2488,13 +2488,13 @@ static int do_set_dqblk(struct dquot *dquot, struct fs_disk_quota *di)
return 0;
}

-int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, struct kqid qid,
struct fs_disk_quota *di)
{
struct dquot *dquot;
int rc;

- dquot = dqget(sb, id, type);
+ dquot = dqget(sb, qid);
if (!dquot) {
rc = -ESRCH;
goto out;
diff --git a/fs/quota/netlink.c b/fs/quota/netlink.c
index d67908b..16e8abb 100644
--- a/fs/quota/netlink.c
+++ b/fs/quota/netlink.c
@@ -30,7 +30,7 @@ static struct genl_family quota_genl_family = {
*
*/

-void quota_send_warning(short type, unsigned int id, dev_t dev,
+void quota_send_warning(struct kqid qid, dev_t dev,
const char warntype)
{
static atomic_t seq;
@@ -56,10 +56,11 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
"VFS: Cannot store netlink header in quota warning.\n");
goto err_out;
}
- ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, type);
+ ret = nla_put_u32(skb, QUOTA_NL_A_QTYPE, qid.type);
if (ret)
goto attr_err_out;
- ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID, id);
+ ret = nla_put_u64(skb, QUOTA_NL_A_EXCESS_ID,
+ from_kqid_munged(&init_user_ns, qid));
if (ret)
goto attr_err_out;
ret = nla_put_u32(skb, QUOTA_NL_A_WARNING, warntype);
@@ -71,7 +72,8 @@ void quota_send_warning(short type, unsigned int id, dev_t dev,
ret = nla_put_u32(skb, QUOTA_NL_A_DEV_MINOR, MINOR(dev));
if (ret)
goto attr_err_out;
- ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID, current_uid());
+ ret = nla_put_u64(skb, QUOTA_NL_A_CAUSED_ID,
+ from_kuid_munged(&init_user_ns, current_uid()));
if (ret)
goto attr_err_out;
genlmsg_end(skb, msg_head);
diff --git a/fs/quota/quota.c b/fs/quota/quota.c
index 6f15578..ff0135d 100644
--- a/fs/quota/quota.c
+++ b/fs/quota/quota.c
@@ -32,8 +32,8 @@ static int check_quotactl_permission(struct super_block *sb, int type, int cmd,
/* allow to query information for dquots we "own" */
case Q_GETQUOTA:
case Q_XGETQUOTA:
- if ((type == USRQUOTA && current_euid() == id) ||
- (type == GRPQUOTA && in_egroup_p(id)))
+ if ((type == USRQUOTA && uid_eq(current_euid(), make_kuid(current_user_ns(), id))) ||
+ (type == GRPQUOTA && in_egroup_p(make_kgid(current_user_ns(), id))))
break;
/*FALLTHROUGH*/
default:
@@ -130,13 +130,17 @@ static void copy_to_if_dqblk(struct if_dqblk *dst, struct fs_disk_quota *src)
static int quota_getquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
+ struct kqid qid;
struct fs_disk_quota fdq;
struct if_dqblk idq;
int ret;

if (!sb->s_qcop->get_dqblk)
return -ENOSYS;
- ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
+ qid = make_kqid(current_user_ns(), type, id);
+ if (!qid_valid(qid))
+ return -EINVAL;
+ ret = sb->s_qcop->get_dqblk(sb, qid, &fdq);
if (ret)
return ret;
copy_to_if_dqblk(&idq, &fdq);
@@ -176,13 +180,17 @@ static int quota_setquota(struct super_block *sb, int type, qid_t id,
{
struct fs_disk_quota fdq;
struct if_dqblk idq;
+ struct kqid qid;

if (copy_from_user(&idq, addr, sizeof(idq)))
return -EFAULT;
if (!sb->s_qcop->set_dqblk)
return -ENOSYS;
+ qid = make_kqid(current_user_ns(), type, id);
+ if (!qid_valid(qid))
+ return -EINVAL;
copy_from_if_dqblk(&fdq, &idq);
- return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
+ return sb->s_qcop->set_dqblk(sb, qid, &fdq);
}

static int quota_setxstate(struct super_block *sb, int cmd, void __user *addr)
@@ -213,23 +221,31 @@ static int quota_setxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
struct fs_disk_quota fdq;
+ struct kqid qid;

if (copy_from_user(&fdq, addr, sizeof(fdq)))
return -EFAULT;
if (!sb->s_qcop->set_dqblk)
return -ENOSYS;
- return sb->s_qcop->set_dqblk(sb, type, id, &fdq);
+ qid = make_kqid(current_user_ns(), type, id);
+ if (!qid_valid(qid))
+ return -EINVAL;
+ return sb->s_qcop->set_dqblk(sb, qid, &fdq);
}

static int quota_getxquota(struct super_block *sb, int type, qid_t id,
void __user *addr)
{
struct fs_disk_quota fdq;
+ struct kqid qid;
int ret;

if (!sb->s_qcop->get_dqblk)
return -ENOSYS;
- ret = sb->s_qcop->get_dqblk(sb, type, id, &fdq);
+ qid = make_kqid(current_user_ns(), type, id);
+ if (!qid_valid(qid))
+ return -EINVAL;
+ ret = sb->s_qcop->get_dqblk(sb, qid, &fdq);
if (!ret && copy_to_user(addr, &fdq, sizeof(fdq)))
return -EFAULT;
return ret;
diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c
index e41c1becf..3534c22 100644
--- a/fs/quota/quota_tree.c
+++ b/fs/quota/quota_tree.c
@@ -22,10 +22,12 @@ MODULE_LICENSE("GPL");

#define __QUOTA_QT_PARANOIA

-static int get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth)
+static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth)
{
unsigned int epb = info->dqi_usable_bs >> 2;
+ qid_t id;

+ id = from_kqid(&init_user_ns, qid);
depth = info->dqi_qtree_depth - depth - 1;
while (depth--)
id /= epb;
@@ -244,7 +246,7 @@ static uint find_free_dqentry(struct qtree_mem_dqinfo *info,
/* This is enough as the block is already zeroed and the entry
* list is empty... */
info->dqi_free_entry = blk;
- mark_info_dirty(dquot->dq_sb, dquot->dq_type);
+ mark_info_dirty(dquot->dq_sb, dquot->dq_id.type);
}
/* Block will be full? */
if (le16_to_cpu(dh->dqdh_entries) + 1 >= qtree_dqstr_in_blk(info)) {
@@ -357,7 +359,7 @@ static inline int dq_insert_tree(struct qtree_mem_dqinfo *info,
*/
int qtree_write_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct super_block *sb = dquot->dq_sb;
ssize_t ret;
char *ddquot = getdqbuf(info->dqi_entry_size);
@@ -538,8 +540,9 @@ static loff_t find_block_dqentry(struct qtree_mem_dqinfo *info,
ddquot += info->dqi_entry_size;
}
if (i == qtree_dqstr_in_blk(info)) {
- quota_error(dquot->dq_sb, "Quota for id %u referenced "
- "but not present", dquot->dq_id);
+ quota_error(dquot->dq_sb,
+ "Quota for id %u referenced but not present",
+ from_kqid(&init_user_ns, dquot->dq_id));
ret = -EIO;
goto out_buf;
} else {
@@ -589,7 +592,7 @@ static inline loff_t find_dqentry(struct qtree_mem_dqinfo *info,

int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct super_block *sb = dquot->dq_sb;
loff_t offset;
char *ddquot;
@@ -607,8 +610,10 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
offset = find_dqentry(info, dquot);
if (offset <= 0) { /* Entry not present? */
if (offset < 0)
- quota_error(sb, "Can't read quota structure "
- "for id %u", dquot->dq_id);
+ quota_error(sb,"Can't read quota structure "
+ "for id %u",
+ from_kqid(&init_user_ns,
+ dquot->dq_id));
dquot->dq_off = 0;
set_bit(DQ_FAKE_B, &dquot->dq_flags);
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
@@ -626,7 +631,7 @@ int qtree_read_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot)
if (ret >= 0)
ret = -EIO;
quota_error(sb, "Error while reading quota structure for id %u",
- dquot->dq_id);
+ from_kqid(&init_user_ns, dquot->dq_id));
set_bit(DQ_FAKE_B, &dquot->dq_flags);
memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
kfree(ddquot);
diff --git a/fs/quota/quota_v1.c b/fs/quota/quota_v1.c
index 34b37a6..469c684 100644
--- a/fs/quota/quota_v1.c
+++ b/fs/quota/quota_v1.c
@@ -54,7 +54,7 @@ static void v1_mem2disk_dqblk(struct v1_disk_dqblk *d, struct mem_dqblk *m)

static int v1_read_dqblk(struct dquot *dquot)
{
- int type = dquot->dq_type;
+ int type = dquot->dq_id.type;
struct v1_disk_dqblk dqblk;

if (!sb_dqopt(dquot->dq_sb)->files[type])
@@ -63,7 +63,8 @@ static int v1_read_dqblk(struct dquot *dquot)
/* Set structure to 0s in case read fails/is after end of file */
memset(&dqblk, 0, sizeof(struct v1_disk_dqblk));
dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk,
- sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id));
+ sizeof(struct v1_disk_dqblk),
+ v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));

v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk);
if (dquot->dq_dqb.dqb_bhardlimit == 0 &&
@@ -78,12 +79,13 @@ static int v1_read_dqblk(struct dquot *dquot)

static int v1_commit_dqblk(struct dquot *dquot)
{
- short type = dquot->dq_type;
+ short type = dquot->dq_id.type;
ssize_t ret;
struct v1_disk_dqblk dqblk;

v1_mem2disk_dqblk(&dqblk, &dquot->dq_dqb);
- if (dquot->dq_id == 0) {
+ if (((type == USRQUOTA) && uid_eq(dquot->dq_id.uid, GLOBAL_ROOT_UID)) ||
+ ((type == GRPQUOTA) && gid_eq(dquot->dq_id.gid, GLOBAL_ROOT_GID))) {
dqblk.dqb_btime =
sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace;
dqblk.dqb_itime =
@@ -93,7 +95,7 @@ static int v1_commit_dqblk(struct dquot *dquot)
if (sb_dqopt(dquot->dq_sb)->files[type])
ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type,
(char *)&dqblk, sizeof(struct v1_disk_dqblk),
- v1_dqoff(dquot->dq_id));
+ v1_dqoff(from_kqid(&init_user_ns, dquot->dq_id)));
if (ret != sizeof(struct v1_disk_dqblk)) {
quota_error(dquot->dq_sb, "dquota write failed");
if (ret >= 0)
diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c
index f1ab360..928cb22 100644
--- a/fs/quota/quota_v2.c
+++ b/fs/quota/quota_v2.c
@@ -196,7 +196,7 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
struct v2r0_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;

d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
@@ -206,7 +206,7 @@ static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
d->dqb_bsoftlimit = cpu_to_le32(v2_stoqb(m->dqb_bsoftlimit));
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id));
if (qtree_entry_unused(info, dp))
d->dqb_itime = cpu_to_le64(1);
}
@@ -215,11 +215,13 @@ static int v2r0_is_id(void *dp, struct dquot *dquot)
{
struct v2r0_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
+ struct kqid qid;

if (qtree_entry_unused(info, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+ qid = make_kqid(&init_user_ns, dquot->dq_id.type, le32_to_cpu(d->dqb_id));
+ return qid_eq(qid, dquot->dq_id);
}

static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
@@ -247,7 +249,7 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
struct v2r1_disk_dqblk *d = dp;
struct mem_dqblk *m = &dquot->dq_dqb;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;

d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
@@ -257,7 +259,7 @@ static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(m->dqb_bsoftlimit));
d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
d->dqb_btime = cpu_to_le64(m->dqb_btime);
- d->dqb_id = cpu_to_le32(dquot->dq_id);
+ d->dqb_id = cpu_to_le32(from_kqid(&init_user_ns, dquot->dq_id));
if (qtree_entry_unused(info, dp))
d->dqb_itime = cpu_to_le64(1);
}
@@ -266,26 +268,28 @@ static int v2r1_is_id(void *dp, struct dquot *dquot)
{
struct v2r1_disk_dqblk *d = dp;
struct qtree_mem_dqinfo *info =
- sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+ sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv;
+ struct kqid qid;

if (qtree_entry_unused(info, dp))
return 0;
- return le32_to_cpu(d->dqb_id) == dquot->dq_id;
+ qid = make_kqid(&init_user_ns, dquot->dq_id.type, le32_to_cpu(d->dqb_id));
+ return qid_eq(qid, dquot->dq_id);
}

static int v2_read_dquot(struct dquot *dquot)
{
- return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_read_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
}

static int v2_write_dquot(struct dquot *dquot)
{
- return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_write_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
}

static int v2_release_dquot(struct dquot *dquot)
{
- return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv, dquot);
+ return qtree_release_dquot(sb_dqinfo(dquot->dq_sb, dquot->dq_id.type)->dqi_priv, dquot);
}

static int v2_free_file_info(struct super_block *sb, int type)
diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c
index fed504f..96944c0 100644
--- a/fs/xfs/xfs_quotaops.c
+++ b/fs/xfs/xfs_quotaops.c
@@ -97,28 +97,29 @@ xfs_fs_set_xstate(
STATIC int
xfs_fs_get_dqblk(
struct super_block *sb,
- int type,
- qid_t id,
+ struct kqid qid,
struct fs_disk_quota *fdq)
{
struct xfs_mount *mp = XFS_M(sb);
+ xfs_dqid_t xfs_id;

if (!XFS_IS_QUOTA_RUNNING(mp))
return -ENOSYS;
if (!XFS_IS_QUOTA_ON(mp))
return -ESRCH;

- return -xfs_qm_scall_getquota(mp, id, xfs_quota_type(type), fdq);
+ xfs_id = from_kqid(&init_user_ns, qid);
+ return -xfs_qm_scall_getquota(mp, xfs_id, xfs_quota_type(qid.type), fdq);
}

STATIC int
xfs_fs_set_dqblk(
struct super_block *sb,
- int type,
- qid_t id,
+ struct kqid qid,
struct fs_disk_quota *fdq)
{
struct xfs_mount *mp = XFS_M(sb);
+ xfs_dqid_t xfs_id;

if (sb->s_flags & MS_RDONLY)
return -EROFS;
@@ -127,7 +128,8 @@ xfs_fs_set_dqblk(
if (!XFS_IS_QUOTA_ON(mp))
return -ESRCH;

- return -xfs_qm_scall_setqlim(mp, id, xfs_quota_type(type), fdq);
+ xfs_id = from_kqid(&init_user_ns, qid);
+ return -xfs_qm_scall_setqlim(mp, xfs_id, xfs_quota_type(qid.type), fdq);
}

const struct quotactl_ops xfs_quotactl_operations = {
diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
index bcb6054..46de393 100644
--- a/fs/xfs/xfs_trans_dquot.c
+++ b/fs/xfs/xfs_trans_dquot.c
@@ -575,12 +575,14 @@ xfs_quota_warn(
struct xfs_dquot *dqp,
int type)
{
+ int qtype;
+ struct kqid qid;
/* no warnings for project quotas - we just return ENOSPC later */
if (dqp->dq_flags & XFS_DQ_PROJ)
return;
- quota_send_warning((dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA,
- be32_to_cpu(dqp->q_core.d_id), mp->m_super->s_dev,
- type);
+ qtype = (dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA;
+ qid = make_kqid(&init_user_ns, qtype, be32_to_cpu(dqp->q_core.d_id));
+ quota_send_warning(qid, mp->m_super->s_dev, type);
}

/*
diff --git a/include/linux/quota.h b/include/linux/quota.h
index 524ede8..0e73250 100644
--- a/include/linux/quota.h
+++ b/include/linux/quota.h
@@ -181,10 +181,161 @@ enum {
#include <linux/dqblk_v2.h>

#include <linux/atomic.h>
+#include <linux/uidgid.h>

typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
typedef long long qsize_t; /* Type in which we store sizes */

+struct kqid { /* Type in which we store the quota identifier */
+ union {
+ kuid_t uid;
+ kgid_t gid;
+ qid_t prj;
+ };
+ int type; /* USRQUOTA (uid) or GRPQUOTA (gid) or XQM_PRJQUOTA (prj) */
+};
+
+static inline bool qid_eq(struct kqid left, struct kqid right)
+{
+ if (left.type != right.type)
+ return false;
+ switch(left.type) {
+ case USRQUOTA:
+ return uid_eq(left.uid, right.uid);
+ case GRPQUOTA:
+ return gid_eq(left.gid, right.gid);
+ case XQM_PRJQUOTA:
+ return left.prj == right.prj;
+ default:
+ BUG();
+ }
+}
+
+static inline bool qid_lt(struct kqid left, struct kqid right)
+{
+ if (left.type < right.type)
+ return true;
+ if (left.type > right.type)
+ return false;
+ switch (left.type) {
+ case USRQUOTA:
+ return uid_lt(left.uid, right.uid);
+ case GRPQUOTA:
+ return gid_lt(left.gid, right.gid);
+ case XQM_PRJQUOTA:
+ return left.prj < right.prj;
+ default:
+ BUG();
+ }
+}
+
+static inline qid_t from_kqid(struct user_namespace *user_ns, struct kqid qid)
+{
+ switch (qid.type) {
+ case USRQUOTA:
+ return from_kuid(user_ns, qid.uid);
+ case GRPQUOTA:
+ return from_kgid(user_ns, qid.gid);
+ case XQM_PRJQUOTA:
+ return (user_ns == &init_user_ns) ? qid.prj : -1;
+ default:
+ BUG();
+ }
+}
+
+static inline qid_t from_kqid_munged(struct user_namespace *user_ns,
+ struct kqid qid)
+{
+ switch (qid.type) {
+ case USRQUOTA:
+ return from_kuid_munged(user_ns, qid.uid);
+ case GRPQUOTA:
+ return from_kgid_munged(user_ns, qid.gid);
+ case XQM_PRJQUOTA:
+ return (user_ns == &init_user_ns) ? qid.prj : -1;
+ default:
+ BUG();
+ }
+}
+
+static inline struct kqid make_kqid(struct user_namespace *user_ns,
+ int type, qid_t qid)
+{
+ struct kqid kqid;
+
+ kqid.type = type;
+ switch (type) {
+ case USRQUOTA:
+ kqid.uid = make_kuid(user_ns, qid);
+ break;
+ case GRPQUOTA:
+ kqid.gid = make_kgid(user_ns, qid);
+ break;
+ case XQM_PRJQUOTA:
+ if (user_ns == &init_user_ns)
+ kqid.prj = qid;
+ else
+ kqid.prj = -1;
+ break;
+ default:
+ BUG();
+ }
+ return kqid;
+}
+
+static inline struct kqid make_kqid_invalid(int type)
+{
+ struct kqid kqid;
+
+ kqid.type = type;
+ switch (type) {
+ case USRQUOTA:
+ kqid.uid = INVALID_UID;
+ break;
+ case GRPQUOTA:
+ kqid.gid = INVALID_GID;
+ break;
+ case XQM_PRJQUOTA:
+ kqid.prj = -1;
+ break;
+ default:
+ BUG();
+ }
+ return kqid;
+}
+
+static inline struct kqid make_kqid_uid(kuid_t uid)
+{
+ struct kqid kqid = {
+ .type = USRQUOTA,
+ .uid = uid,
+ };
+ return kqid;
+}
+
+static inline struct kqid make_kqid_gid(kgid_t gid)
+{
+ struct kqid kqid = {
+ .type = GRPQUOTA,
+ .gid = gid,
+ };
+ return kqid;
+}
+
+static inline bool qid_valid(struct kqid qid)
+{
+ switch (qid.type) {
+ case USRQUOTA:
+ return uid_valid(qid.uid);
+ case GRPQUOTA:
+ return gid_valid(qid.gid);
+ case XQM_PRJQUOTA:
+ return qid.prj != (qid_t)-1;
+ default:
+ BUG();
+ }
+}
+
extern spinlock_t dq_data_lock;

/* Maximal numbers of writes for quota operation (insert/delete/update)
@@ -294,10 +445,9 @@ struct dquot {
atomic_t dq_count; /* Use count */
wait_queue_head_t dq_wait_unused; /* Wait queue for dquot to become unused */
struct super_block *dq_sb; /* superblock this applies to */
- unsigned int dq_id; /* ID this applies to (uid, gid) */
+ struct kqid dq_id; /* ID this applies to (uid, gid, prj) */
loff_t dq_off; /* Offset of dquot on disk */
unsigned long dq_flags; /* See DQ_* */
- short dq_type; /* Type of quota */
struct mem_dqblk dq_dqb; /* Diskquota usage */
};

@@ -336,8 +486,8 @@ struct quotactl_ops {
int (*quota_sync)(struct super_block *, int);
int (*get_info)(struct super_block *, int, struct if_dqinfo *);
int (*set_info)(struct super_block *, int, struct if_dqinfo *);
- int (*get_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
- int (*set_dqblk)(struct super_block *, int, qid_t, struct fs_disk_quota *);
+ int (*get_dqblk)(struct super_block *, struct kqid, struct fs_disk_quota *);
+ int (*set_dqblk)(struct super_block *, struct kqid, struct fs_disk_quota *);
int (*get_xstate)(struct super_block *, struct fs_quota_stat *);
int (*set_xstate)(struct super_block *, unsigned int, int);
};
@@ -386,10 +536,10 @@ static inline unsigned int dquot_generic_flag(unsigned int flags, int type)
}

#ifdef CONFIG_QUOTA_NETLINK_INTERFACE
-extern void quota_send_warning(short type, unsigned int id, dev_t dev,
+extern void quota_send_warning(struct kqid qid, dev_t dev,
const char warntype);
#else
-static inline void quota_send_warning(short type, unsigned int id, dev_t dev,
+static inline void quota_send_warning(struct kqid qid, dev_t dev,
const char warntype)
{
return;
diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h
index ec6b65f..1c50093 100644
--- a/include/linux/quotaops.h
+++ b/include/linux/quotaops.h
@@ -44,7 +44,7 @@ void inode_sub_rsv_space(struct inode *inode, qsize_t number);

void dquot_initialize(struct inode *inode);
void dquot_drop(struct inode *inode);
-struct dquot *dqget(struct super_block *sb, unsigned int id, int type);
+struct dquot *dqget(struct super_block *sb, struct kqid qid);
void dqput(struct dquot *dquot);
int dquot_scan_active(struct super_block *sb,
int (*fn)(struct dquot *dquot, unsigned long priv),
@@ -87,9 +87,9 @@ int dquot_writeback_dquots(struct super_block *sb, int type);
int dquot_quota_sync(struct super_block *sb, int type);
int dquot_get_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
int dquot_set_dqinfo(struct super_block *sb, int type, struct if_dqinfo *ii);
-int dquot_get_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_get_dqblk(struct super_block *sb, struct kqid id,
struct fs_disk_quota *di);
-int dquot_set_dqblk(struct super_block *sb, int type, qid_t id,
+int dquot_set_dqblk(struct super_block *sb, struct kqid id,
struct fs_disk_quota *di);

int __dquot_transfer(struct inode *inode, struct dquot **transfer_to);
diff --git a/init/Kconfig b/init/Kconfig
index 2a388e5..a0bccce 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -928,8 +928,6 @@ config UIDGID_CONVERTED
depends on IMA = n
depends on EVM = n
depends on FS_POSIX_ACL = n
- depends on QUOTA = n
- depends on QUOTACTL = n

# Networking
depends on NET_9P = n
--
1.7.5.4

2012-08-29 02:10:37

by Dave Chinner

[permalink] [raw]
Subject: Re: [PATCH] userns: Add basic quota support v4

On Tue, Aug 28, 2012 at 12:09:56PM -0700, Eric W. Biederman wrote:
>
> Add the data type struct kqid which holds the kernel internal form of
> the owning identifier of a quota. struct kqid is a replacement for
> the implicit union of uid, gid and project stored in an unsigned int
> and the quota type field that is was used in the quota data
> structures. Making the data type explicit allows the kuid_t and
> kgid_t type safety to propogate more thoroughly through the code,
> revealing more places where uid/gid conversions need be made.
>
> Along with the data type struct kqid comes the helper functions
> qid_eq, qid_lt, from_kqid, from_kqid_munged, qid_valid, make_kqid,

I think Jan's comment about from_kqid being named id_from_kgid is
better, though I also think it would read better as kqid_to_id().
ie:

id = kqid_to_id(ns, qid);

> make_kqid_invalid, make_kqid_uid, make_kqid_gid.

and these named something like uid_to_kqid()

> Change struct dquot dq_id to a struct kqid and remove the now
> unecessary dq_type.
>
> Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
> and dquot_set_dqblk to use struct kqid.
>
> Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
> the change in quota structures and signatures. The ocfs2 changes are
> larger than most because of the extensive tracing throughout the ocfs2
> quota code that prints out dq_id.

How did you test that this all works? e.g. run xfstests -g quota on
each of those filesystems and check for no regressions? And if you
wrote any tests, can you convert them to be part of xfstests so that
namespace aware quotas get tested regularly?

>
> v4:
> - Rename struct qown struct kqid and associated changes
> to the naming of the helper functions.
> - Use qid_t to hold the userspace identifier representation
> of quota identifiers in all new code.
> v3:
> - Add missing negation on qown_valid
> v2:
> - Renamed qown_t struct qown
> - Added the quota type to struct qown.
> - Removed enum quota_type (In this patch it was just noise)
> - Added qown_lt, make_qown_invalid, make_qown_uid, make_qown_gid
> - Taught qown to handle xfs project ids (but only in init_user_ns).
> Q_XGETQUOTA calls .get_quotblk with project ids.

Q_XSETQLIM was modified to handle project quotas as well, I assume?

> index fed504f..96944c0 100644
> --- a/fs/xfs/xfs_quotaops.c
> +++ b/fs/xfs/xfs_quotaops.c
> @@ -97,28 +97,29 @@ xfs_fs_set_xstate(
> STATIC int
> xfs_fs_get_dqblk(
> struct super_block *sb,
> - int type,
> - qid_t id,
> + struct kqid qid,
> struct fs_disk_quota *fdq)
> {
> struct xfs_mount *mp = XFS_M(sb);
> + xfs_dqid_t xfs_id;
>
> if (!XFS_IS_QUOTA_RUNNING(mp))
> return -ENOSYS;
> if (!XFS_IS_QUOTA_ON(mp))
> return -ESRCH;
>
> - return -xfs_qm_scall_getquota(mp, id, xfs_quota_type(type), fdq);
> + xfs_id = from_kqid(&init_user_ns, qid);
> + return -xfs_qm_scall_getquota(mp, xfs_id, xfs_quota_type(qid.type), fdq);
> }

Why a temporary variable? Why not just:

return -xfs_qm_scall_getquota(mp, from_kqid(&init_user_ns, qid),
xfs_quota_type(qid.type), fdq);

Indeed, why not drive the struct kqid down another level into
xfs_qm_scall_getquota() where all they are used for is parameters to
the xfs_qm_dqget() function?

>
> STATIC int
> xfs_fs_set_dqblk(
> struct super_block *sb,
> - int type,
> - qid_t id,
> + struct kqid qid,
> struct fs_disk_quota *fdq)
> {
> struct xfs_mount *mp = XFS_M(sb);
> + xfs_dqid_t xfs_id;
>
> if (sb->s_flags & MS_RDONLY)
> return -EROFS;
> @@ -127,7 +128,8 @@ xfs_fs_set_dqblk(
> if (!XFS_IS_QUOTA_ON(mp))
> return -ESRCH;
>
> - return -xfs_qm_scall_setqlim(mp, id, xfs_quota_type(type), fdq);
> + xfs_id = from_kqid(&init_user_ns, qid);
> + return -xfs_qm_scall_setqlim(mp, xfs_id, xfs_quota_type(qid.type), fdq);
> }

Same is true here....

>
> const struct quotactl_ops xfs_quotactl_operations = {
> diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
> index bcb6054..46de393 100644
> --- a/fs/xfs/xfs_trans_dquot.c
> +++ b/fs/xfs/xfs_trans_dquot.c
> @@ -575,12 +575,14 @@ xfs_quota_warn(
> struct xfs_dquot *dqp,
> int type)
> {
> + int qtype;
> + struct kqid qid;
> /* no warnings for project quotas - we just return ENOSPC later */
> if (dqp->dq_flags & XFS_DQ_PROJ)
> return;
> - quota_send_warning((dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA,
> - be32_to_cpu(dqp->q_core.d_id), mp->m_super->s_dev,
> - type);
> + qtype = (dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA;
> + qid = make_kqid(&init_user_ns, qtype, be32_to_cpu(dqp->q_core.d_id));
> + quota_send_warning(qid, mp->m_super->s_dev, type);
> }
>
> /*
> diff --git a/include/linux/quota.h b/include/linux/quota.h
> index 524ede8..0e73250 100644
> --- a/include/linux/quota.h
> +++ b/include/linux/quota.h
> @@ -181,10 +181,161 @@ enum {
> #include <linux/dqblk_v2.h>
>
> #include <linux/atomic.h>
> +#include <linux/uidgid.h>
>
> typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
> typedef long long qsize_t; /* Type in which we store sizes */

>From fs/xfs/xfs_types.h:

typedef __uint32_t prid_t; /* project ID */

Perhaps it would be better to have an official kprid_t definition
here, i.e:

>
> +struct kqid { /* Type in which we store the quota identifier */
> + union {
> + kuid_t uid;
> + kgid_t gid;
> + qid_t prj;

kprid_t prid;

> + };
> + int type; /* USRQUOTA (uid) or GRPQUOTA (gid) or XQM_PRJQUOTA (prj) */
> +};
> +
> +static inline bool qid_eq(struct kqid left, struct kqid right)
> +{
> + if (left.type != right.type)
> + return false;
> + switch(left.type) {
> + case USRQUOTA:
> + return uid_eq(left.uid, right.uid);
> + case GRPQUOTA:
> + return gid_eq(left.gid, right.gid);
> + case XQM_PRJQUOTA:
> + return left.prj == right.prj;
> + default:
> + BUG();

BUG()? Seriously? The most this justifies is a WARN_ON_ONCE() to
indicate a potential programming error, not bringing down the entire
machine.

> + }
> +}
> +
> +static inline bool qid_lt(struct kqid left, struct kqid right)
> +{
> + if (left.type < right.type)
> + return true;
> + if (left.type > right.type)
> + return false;
> + switch (left.type) {
> + case USRQUOTA:
> + return uid_lt(left.uid, right.uid);
> + case GRPQUOTA:
> + return gid_lt(left.gid, right.gid);
> + case XQM_PRJQUOTA:
> + return left.prj < right.prj;
> + default:
> + BUG();
> + }
> +}

What is this function used for? it's not referenced at all by the
patch, and there's no documentation/comments explaining why it
exists or how it is intended to be used....

> +static inline qid_t from_kqid(struct user_namespace *user_ns, struct kqid qid)
> +{
> + switch (qid.type) {
> + case USRQUOTA:
> + return from_kuid(user_ns, qid.uid);
> + case GRPQUOTA:
> + return from_kgid(user_ns, qid.gid);
> + case XQM_PRJQUOTA:
> + return (user_ns == &init_user_ns) ? qid.prj : -1;
> + default:
> + BUG();
> + }
> +}

Oh, this can return an error. That's only checked in a coupl eof
places this function is called. it needs tobe checked everywhere,
otherwise we now have the possibility of quota usage being accounted
to uid/gid/prid 0xffffffff when namespace matches are not found.

> +static inline qid_t from_kqid_munged(struct user_namespace *user_ns,
> + struct kqid qid)

What does munging do to the return value? how is it different to
from_kqid()? Document your API....


> +static inline struct kqid make_kqid(struct user_namespace *user_ns,
> + int type, qid_t qid)
> +{
> + struct kqid kqid;
> +
> + kqid.type = type;
> + switch (type) {
> + case USRQUOTA:
> + kqid.uid = make_kuid(user_ns, qid);
> + break;
> + case GRPQUOTA:
> + kqid.gid = make_kgid(user_ns, qid);
> + break;
> + case XQM_PRJQUOTA:
> + if (user_ns == &init_user_ns)
> + kqid.prj = qid;
> + else
> + kqid.prj = -1;
> + break;

kqid.prj = (user_ns == &init_user_ns) ? qid : -1;

> + default:
> + BUG();
> + }
> + return kqid;
> +}
> +
> +static inline struct kqid make_kqid_invalid(int type)
> +{
> + struct kqid kqid;
> +
> + kqid.type = type;
> + switch (type) {
> + case USRQUOTA:
> + kqid.uid = INVALID_UID;
> + break;
> + case GRPQUOTA:
> + kqid.gid = INVALID_GID;
> + break;
> + case XQM_PRJQUOTA:
> + kqid.prj = -1;
> + break;
> + default:
> + BUG();
> + }
> + return kqid;
> +}
> +
> +static inline struct kqid make_kqid_uid(kuid_t uid)
> +{
> + struct kqid kqid = {
> + .type = USRQUOTA,
> + .uid = uid,
> + };
> + return kqid;
> +}

Isn't this sort of construct frowned upon? i.e. returning a
structure out of scope? It may be inline code and hence work, but
this strikes me as a landmine waiting for someone to tread on....

Cheers,

Dave.
--
Dave Chinner
[email protected]

2012-08-29 09:31:45

by Eric W. Biederman

[permalink] [raw]
Subject: Re: [PATCH] userns: Add basic quota support v4


Dave thanks for taking the time to take a detailed look at this code.

Dave Chinner <[email protected]> writes:

> On Tue, Aug 28, 2012 at 12:09:56PM -0700, Eric W. Biederman wrote:
>>
>> Add the data type struct kqid which holds the kernel internal form of
>> the owning identifier of a quota. struct kqid is a replacement for
>> the implicit union of uid, gid and project stored in an unsigned int
>> and the quota type field that is was used in the quota data
>> structures. Making the data type explicit allows the kuid_t and
>> kgid_t type safety to propogate more thoroughly through the code,
>> revealing more places where uid/gid conversions need be made.
>>
>> Along with the data type struct kqid comes the helper functions
>> qid_eq, qid_lt, from_kqid, from_kqid_munged, qid_valid, make_kqid,
>
> I think Jan's comment about from_kqid being named id_from_kgid is
> better, though I also think it would read better as kqid_to_id().
> ie:
>
> id = kqid_to_id(ns, qid);

kqid and qid are the same thing just in a different encoding.
Emphasizing the quota identifier instead of the kernel vs user encoding
change is paying attention to the wrong thing.

Using make_kqid and from_kqid follows the exact same conventions as I have
established for kuids and kgids. So if you learn one you have learned
them all.

>> make_kqid_invalid, make_kqid_uid, make_kqid_gid.
>
> and these named something like uid_to_kqid()

The last two are indeed weird, and definitely not the common case,
since there is no precedent I can almost see doing something different
but I don't see a good case for a different name.

>> Change struct dquot dq_id to a struct kqid and remove the now
>> unecessary dq_type.
>>
>> Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
>> and dquot_set_dqblk to use struct kqid.
>>
>> Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
>> the change in quota structures and signatures. The ocfs2 changes are
>> larger than most because of the extensive tracing throughout the ocfs2
>> quota code that prints out dq_id.
>
> How did you test that this all works?

By making it a compile error if you get a conversion wrong and making it
a rule not to make any logic changes. That combined with code review
and running the code a bit to make certain I did not horribly mess up.

> e.g. run xfstests -g quota on
> each of those filesystems and check for no regressions? And if you
> wrote any tests, can you convert them to be part of xfstests so that
> namespace aware quotas get tested regularly?

I have not written any tests, and running the xfstests in a namespace
should roughly be a matter of "unshare -U xfstest -g quota" It isn't
quite that easy because /proc/self/uid_map and /proc/self/gid_map need
to be written first.

Right now only ext2 ext3 and ext4 compile with user namespace support
enabled. xfs and gfs2 and ocfs2 still only compile with user namespace
support disabled because my patches to convert them are waiting in the
wings until I get the core subsystems primarily quota and posix acls
reviewed.

>> v4:
>> - Rename struct qown struct kqid and associated changes
>> to the naming of the helper functions.
>> - Use qid_t to hold the userspace identifier representation
>> of quota identifiers in all new code.
>> v3:
>> - Add missing negation on qown_valid
>> v2:
>> - Renamed qown_t struct qown
>> - Added the quota type to struct qown.
>> - Removed enum quota_type (In this patch it was just noise)
>> - Added qown_lt, make_qown_invalid, make_qown_uid, make_qown_gid
>> - Taught qown to handle xfs project ids (but only in init_user_ns).
>> Q_XGETQUOTA calls .get_quotblk with project ids.
>
> Q_XSETQLIM was modified to handle project quotas as well, I assume?

I didn't break the project quota support for Q_XSETQLIM.


>> index fed504f..96944c0 100644
>> --- a/fs/xfs/xfs_quotaops.c
>> +++ b/fs/xfs/xfs_quotaops.c
>> @@ -97,28 +97,29 @@ xfs_fs_set_xstate(
>> STATIC int
>> xfs_fs_get_dqblk(
>> struct super_block *sb,
>> - int type,
>> - qid_t id,
>> + struct kqid qid,
>> struct fs_disk_quota *fdq)
>> {
>> struct xfs_mount *mp = XFS_M(sb);
>> + xfs_dqid_t xfs_id;
>>
>> if (!XFS_IS_QUOTA_RUNNING(mp))
>> return -ENOSYS;
>> if (!XFS_IS_QUOTA_ON(mp))
>> return -ESRCH;
>>
>> - return -xfs_qm_scall_getquota(mp, id, xfs_quota_type(type), fdq);
>> + xfs_id = from_kqid(&init_user_ns, qid);
>> + return -xfs_qm_scall_getquota(mp, xfs_id, xfs_quota_type(qid.type), fdq);
>> }
>
> Why a temporary variable? Why not just:
>
> return -xfs_qm_scall_getquota(mp, from_kqid(&init_user_ns, qid),
> xfs_quota_type(qid.type), fdq);
>

Because I am not fond of very long lines and because I did the basic
conversion of gfs2 first where using a separate variable a larger
difference.

>From a code review perspective I am still more comfortable with
introducing a temporary variable as the fundamental change is
easier to see.

> Indeed, why not drive the struct kqid down another level into
> xfs_qm_scall_getquota() where all they are used for is parameters to
> the xfs_qm_dqget() function?

I have not driven struct kqid down another level because change
needs to wait until I add user namespace support to xfs.

I only touch xfs in this case because making the implicit union explicit
is needed for type safety and sanity, and unfortunately that requires
the prototype of the quota operations to change.

>> STATIC int
>> xfs_fs_set_dqblk(
>> struct super_block *sb,
>> - int type,
>> - qid_t id,
>> + struct kqid qid,
>> struct fs_disk_quota *fdq)
>> {
>> struct xfs_mount *mp = XFS_M(sb);
>> + xfs_dqid_t xfs_id;
>>
>> if (sb->s_flags & MS_RDONLY)
>> return -EROFS;
>> @@ -127,7 +128,8 @@ xfs_fs_set_dqblk(
>> if (!XFS_IS_QUOTA_ON(mp))
>> return -ESRCH;
>>
>> - return -xfs_qm_scall_setqlim(mp, id, xfs_quota_type(type), fdq);
>> + xfs_id = from_kqid(&init_user_ns, qid);
>> + return -xfs_qm_scall_setqlim(mp, xfs_id, xfs_quota_type(qid.type), fdq);
>> }
>
> Same is true here....
>
>>
>> const struct quotactl_ops xfs_quotactl_operations = {
>> diff --git a/fs/xfs/xfs_trans_dquot.c b/fs/xfs/xfs_trans_dquot.c
>> index bcb6054..46de393 100644
>> --- a/fs/xfs/xfs_trans_dquot.c
>> +++ b/fs/xfs/xfs_trans_dquot.c
>> @@ -575,12 +575,14 @@ xfs_quota_warn(
>> struct xfs_dquot *dqp,
>> int type)
>> {
>> + int qtype;
>> + struct kqid qid;
>> /* no warnings for project quotas - we just return ENOSPC later */
>> if (dqp->dq_flags & XFS_DQ_PROJ)
>> return;
>> - quota_send_warning((dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA,
>> - be32_to_cpu(dqp->q_core.d_id), mp->m_super->s_dev,
>> - type);
>> + qtype = (dqp->dq_flags & XFS_DQ_USER) ? USRQUOTA : GRPQUOTA;
>> + qid = make_kqid(&init_user_ns, qtype, be32_to_cpu(dqp->q_core.d_id));
>> + quota_send_warning(qid, mp->m_super->s_dev, type);
>> }
>>
>> /*
>> diff --git a/include/linux/quota.h b/include/linux/quota.h
>> index 524ede8..0e73250 100644
>> --- a/include/linux/quota.h
>> +++ b/include/linux/quota.h
>> @@ -181,10 +181,161 @@ enum {
>> #include <linux/dqblk_v2.h>
>>
>> #include <linux/atomic.h>
>> +#include <linux/uidgid.h>
>>
>> typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
>> typedef long long qsize_t; /* Type in which we store sizes */
>
> From fs/xfs/xfs_types.h:
>
> typedef __uint32_t prid_t; /* project ID */
>
> Perhaps it would be better to have an official kprid_t definition
> here, i.e:

We can always improve. For this patch I don't see it making a
useful difference.

prid_t isn't exported to non xfs code. The project id is already
stored in an unsigned int or a qid_t.

>> +struct kqid { /* Type in which we store the quota identifier */
>> + union {
>> + kuid_t uid;
>> + kgid_t gid;
>> + qid_t prj;
>
> kprid_t prid;
>

Hmm. The naming here is interesting. No one calls the project id prj.
So I have an avoidable inconsistency here. xfs most commonly uses
projid and occassionally calls it prid.

So I am inclined to rename this field projid, but I don't know if it is
worth respinning the patch for something so trivial.

>> + };
>> + int type; /* USRQUOTA (uid) or GRPQUOTA (gid) or XQM_PRJQUOTA (prj) */
>> +};
>> +
>> +static inline bool qid_eq(struct kqid left, struct kqid right)
>> +{
>> + if (left.type != right.type)
>> + return false;
>> + switch(left.type) {
>> + case USRQUOTA:
>> + return uid_eq(left.uid, right.uid);
>> + case GRPQUOTA:
>> + return gid_eq(left.gid, right.gid);
>> + case XQM_PRJQUOTA:
>> + return left.prj == right.prj;
>> + default:
>> + BUG();
>
> BUG()? Seriously? The most this justifies is a WARN_ON_ONCE() to
> indicate a potential programming error, not bringing down the entire
> machine.

Dead serious. Any other type value is impossible and unsupported.

BUG is not panic. BUG is an oops. BUG won't bring down the machine
unless the sysadmin wants it too.

Beyond that I am not interesting in supporting exploitable abuse
of this type.

>> +static inline bool qid_lt(struct kqid left, struct kqid right)
>> +{
>> + if (left.type < right.type)
>> + return true;
>> + if (left.type > right.type)
>> + return false;
>> + switch (left.type) {
>> + case USRQUOTA:
>> + return uid_lt(left.uid, right.uid);
>> + case GRPQUOTA:
>> + return gid_lt(left.gid, right.gid);
>> + case XQM_PRJQUOTA:
>> + return left.prj < right.prj;
>> + default:
>> + BUG();
>> + }
>> +}
>
> What is this function used for? it's not referenced at all by the
> patch, and there's no documentation/comments explaining why it
> exists or how it is intended to be used....

This function is introduced early, but it is needed for gfs2 as
performs a sort of quota identifiers.

>> +static inline qid_t from_kqid(struct user_namespace *user_ns, struct kqid qid)
>> +{
>> + switch (qid.type) {
>> + case USRQUOTA:
>> + return from_kuid(user_ns, qid.uid);
>> + case GRPQUOTA:
>> + return from_kgid(user_ns, qid.gid);
>> + case XQM_PRJQUOTA:
>> + return (user_ns == &init_user_ns) ? qid.prj : -1;
>> + default:
>> + BUG();
>> + }
>> +}
>
> Oh, this can return an error. That's only checked in a coupl eof
> places this function is called. it needs tobe checked everywhere,
> otherwise we now have the possibility of quota usage being accounted
> to uid/gid/prid 0xffffffff when namespace matches are not found.

No this is not an error condition. Returning -1 is the mapping that is
used when there is not a mapping entry.

Depending on the circumstances not having a mapping can be an error,
but it can also be a don't care condition.

All in kernel values are defined as having a mapping into the initial
user namespace.

Looking at my tree the only calls of from_kqid not mapping the id
into the initial user namespace are from_kqid_munged and they report
to userspace. from_kqid_munged returns fs_overflowuid or fs_overflowgid
in case of a mapping failure.

projects ids can only be accessed if capable(CAP_SYS_ADMIN) which
you can only have in the initial user namespace so for now there will
alwasy be a mapping for project ids.

>> +static inline qid_t from_kqid_munged(struct user_namespace *user_ns,
>> + struct kqid qid)
>
> What does munging do to the return value? how is it different to
> from_kqid()? Document your API....

This bit certainly certainly could use a bit more explanation.

Mapping of uids and gids from one domain to another is not new in linux.
It originates with the transition from 16bit identifiers to 32bit
identifiers. In most places when there is a 32bit identifier that can
not be represented we return a fs_overflowid aka (u16)-2 aka nobody.

So in general when we want to pass a value to userspace from_kqid_munged
is called, and if there is a mapping failure we report the value that
userspace set fs_overflowuid or fs_overflowgid to. For project ids it
which are restricted to the initial user namespace no mapping failures
can occur.

>> +static inline struct kqid make_kqid(struct user_namespace *user_ns,
>> + int type, qid_t qid)
>> +{
>> + struct kqid kqid;
>> +
>> + kqid.type = type;
>> + switch (type) {
>> + case USRQUOTA:
>> + kqid.uid = make_kuid(user_ns, qid);
>> + break;
>> + case GRPQUOTA:
>> + kqid.gid = make_kgid(user_ns, qid);
>> + break;
>> + case XQM_PRJQUOTA:
>> + if (user_ns == &init_user_ns)
>> + kqid.prj = qid;
>> + else
>> + kqid.prj = -1;
>> + break;
>
> kqid.prj = (user_ns == &init_user_ns) ? qid : -1;

That is an interesting inconsitency. Given that implemented the others
on one line.

>> + default:
>> + BUG();
>> + }
>> + return kqid;
>> +}
>> +
>> +static inline struct kqid make_kqid_invalid(int type)
>> +{
>> + struct kqid kqid;
>> +
>> + kqid.type = type;
>> + switch (type) {
>> + case USRQUOTA:
>> + kqid.uid = INVALID_UID;
>> + break;
>> + case GRPQUOTA:
>> + kqid.gid = INVALID_GID;
>> + break;
>> + case XQM_PRJQUOTA:
>> + kqid.prj = -1;
>> + break;
>> + default:
>> + BUG();
>> + }
>> + return kqid;
>> +}
>> +
>> +static inline struct kqid make_kqid_uid(kuid_t uid)
>> +{
>> + struct kqid kqid = {
>> + .type = USRQUOTA,
>> + .uid = uid,
>> + };
>> + return kqid;
>> +}
>
> Isn't this sort of construct frowned upon? i.e. returning a
> structure out of scope? It may be inline code and hence work, but
> this strikes me as a landmine waiting for someone to tread on....

Not at all because I am returning the structure by value.

Return a structure by reference is the case that doesn't work.

Eric

2012-08-31 01:18:08

by Dave Chinner

[permalink] [raw]
Subject: Re: [PATCH] userns: Add basic quota support v4

On Wed, Aug 29, 2012 at 02:31:26AM -0700, Eric W. Biederman wrote:
>
> Dave thanks for taking the time to take a detailed look at this code.
>
> Dave Chinner <[email protected]> writes:
>
> > On Tue, Aug 28, 2012 at 12:09:56PM -0700, Eric W. Biederman wrote:
> >>
> >> Add the data type struct kqid which holds the kernel internal form of
> >> the owning identifier of a quota. struct kqid is a replacement for
> >> the implicit union of uid, gid and project stored in an unsigned int
> >> and the quota type field that is was used in the quota data
> >> structures. Making the data type explicit allows the kuid_t and
> >> kgid_t type safety to propogate more thoroughly through the code,
> >> revealing more places where uid/gid conversions need be made.
> >>
> >> Along with the data type struct kqid comes the helper functions
> >> qid_eq, qid_lt, from_kqid, from_kqid_munged, qid_valid, make_kqid,
> >
> > I think Jan's comment about from_kqid being named id_from_kgid is
> > better, though I also think it would read better as kqid_to_id().
> > ie:
> >
> > id = kqid_to_id(ns, qid);
>
> kqid and qid are the same thing just in a different encoding.
> Emphasizing the quota identifier instead of the kernel vs user encoding
> change is paying attention to the wrong thing.

Not from a quota perspective. The only thing the quota code really
cares about is the quota identifier, not the encoding.

Fundamentally, from_kqid() doen't tell me anything about what I'm
getting from the kqid. There's code all over the place that used the
"<format>_to_<other format>" convention because it's obvious what is
being converted from/to. e.g. cpu_to_beXX, compat_to_ptr,
dma_to_phys, pfn_to_page, etc. Best practises say "follow existing
conventions".

> Using make_kqid and from_kqid follows the exact same conventions as I have
> established for kuids and kgids. So if you learn one you have learned
> them all.

For those of us that have to look at it once every few months,
following the same conventions as all the other code in the kernel
(i.e. kqid_to_id()) tells me everything I need to know without
having to go through the process of looking up the unusual
from_kqid() function and then from_kuid() to find out what it is
actually doing....

> >> make_kqid_invalid, make_kqid_uid, make_kqid_gid.
> >
> > and these named something like uid_to_kqid()
>
> The last two are indeed weird, and definitely not the common case,
> since there is no precedent I can almost see doing something different
> but I don't see a good case for a different name.

There's plenty of precendence in other code that converts format.
A very common convention that is used everywhere is DEFINE_...().
That would be make the code easier to grasp than "make...".

> >> Change struct dquot dq_id to a struct kqid and remove the now
> >> unecessary dq_type.
> >>
> >> Update the signature of dqget, quota_send_warning, dquot_get_dqblk,
> >> and dquot_set_dqblk to use struct kqid.
> >>
> >> Make minimal changes to ext3, ext4, gfs2, ocfs2, and xfs to deal with
> >> the change in quota structures and signatures. The ocfs2 changes are
> >> larger than most because of the extensive tracing throughout the ocfs2
> >> quota code that prints out dq_id.
> >
> > How did you test that this all works?
>
> By making it a compile error if you get a conversion wrong and making it
> a rule not to make any logic changes.
>
> That combined with code review
> and running the code a bit to make certain I did not horribly mess up.

But no actual regression testing. You're messing with code that I
will have to triage when it goes wrong for a user, so IMO your code
has to pass the same bar as the code I write has to pass for review
- please regression test your code and write new regression tests
for new functionality.

> > e.g. run xfstests -g quota on
> > each of those filesystems and check for no regressions? And if you
> > wrote any tests, can you convert them to be part of xfstests so that
> > namespace aware quotas get tested regularly?
>
> I have not written any tests, and running the xfstests in a namespace
> should roughly be a matter of "unshare -U xfstest -g quota" It isn't
> quite that easy because /proc/self/uid_map and /proc/self/gid_map need

Asking people to run the entire regression test suite differently
and with special setup magic won't get the code tested regularly.
Writing a new, self contained test that exercises quota in multiple
namespaces simultaneously is what is needed - that way people who
don't even know that namespaces exist will be regression testing
it...

> >> --- a/include/linux/quota.h
> >> +++ b/include/linux/quota.h
> >> @@ -181,10 +181,161 @@ enum {
> >> #include <linux/dqblk_v2.h>
> >>
> >> #include <linux/atomic.h>
> >> +#include <linux/uidgid.h>
> >>
> >> typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */
> >> typedef long long qsize_t; /* Type in which we store sizes */
> >
> > From fs/xfs/xfs_types.h:
> >
> > typedef __uint32_t prid_t; /* project ID */
> >
> > Perhaps it would be better to have an official kprid_t definition
> > here, i.e:
>
> We can always improve. For this patch I don't see it making a
> useful difference.

If you are touching the code and introducing new types and concepts
at a global level, then this is precisely when you should be getting
the definitions of those new types correct.

> prid_t isn't exported to non xfs code. The project id is already
> stored in an unsigned int or a qid_t.

You missed my point - that you are now making a distinction
outside XFS about project quotas. i.e. effectively making it
a first-class quota citizen. That means it needs an equivalent
global type definition and not rely implictly on some subsystem
getting the type conversion correct.

Indeed, isn't that the entire point of your patch set - using the
correct global types throughout the stack to avoid type conversion
errors?

> >> +struct kqid { /* Type in which we store the quota identifier */
> >> + union {
> >> + kuid_t uid;
> >> + kgid_t gid;
> >> + qid_t prj;
> >
> > kprid_t prid;
> >
>
> Hmm. The naming here is interesting. No one calls the project id prj.
> So I have an avoidable inconsistency here. xfs most commonly uses
> projid and occassionally calls it prid.

Wrong way around. XFS uses projid in one place - xfs_set_projid() -
and prid in 30 others. The type is xfs_prid_t as well. prid is much
prefered to projid...

> So I am inclined to rename this field projid, but I don't know if it is
> worth respinning the patch for something so trivial.

I'd like to think you are joking, given that the patch series is all
about using consistent, verifiable identifiers... :/

> >> + };
> >> + int type; /* USRQUOTA (uid) or GRPQUOTA (gid) or XQM_PRJQUOTA (prj) */
> >> +};
> >> +
> >> +static inline bool qid_eq(struct kqid left, struct kqid right)
> >> +{
> >> + if (left.type != right.type)
> >> + return false;
> >> + switch(left.type) {
> >> + case USRQUOTA:
> >> + return uid_eq(left.uid, right.uid);
> >> + case GRPQUOTA:
> >> + return gid_eq(left.gid, right.gid);
> >> + case XQM_PRJQUOTA:
> >> + return left.prj == right.prj;
> >> + default:
> >> + BUG();
> >
> > BUG()? Seriously? The most this justifies is a WARN_ON_ONCE() to
> > indicate a potential programming error, not bringing down the entire
> > machine.
>
> Dead serious. Any other type value is impossible and unsupported.

So make the compiler check it (use an enum, or perhaps
BUILD_BUG_ON()). Don't bring the machine down because you haven't
thought about error handling properly.

> BUG is not panic. BUG is an oops. BUG won't bring down the machine
> unless the sysadmin wants it too.

panic, oops, it's the same thing to most enterprise distros as they
are configured to dump the kernel or reboot when an oops occurs.

Even if they are not configured this way, any oops that occurs with
a filesystem lock held (like these will) will eventually result in a
filesystem deadlock and subsequent fatal application and/or system
hang. This most definitely is not a condition that should be
triggering a fatal error.

> Beyond that I am not interesting in supporting exploitable abuse
> of this type.

/me jumps up and down on his seat excitedly

Oh, Oh, it's Security Theatre time! Can I have a go!? Please?

If someone can cause an unsupported quota type to appear
in this field, using BUG() creates a wonderful DOS exploit...

:)

> >> +static inline bool qid_lt(struct kqid left, struct kqid right)
> >> +{
> >> + if (left.type < right.type)
> >> + return true;
> >> + if (left.type > right.type)
> >> + return false;
> >> + switch (left.type) {
> >> + case USRQUOTA:
> >> + return uid_lt(left.uid, right.uid);
> >> + case GRPQUOTA:
> >> + return gid_lt(left.gid, right.gid);
> >> + case XQM_PRJQUOTA:
> >> + return left.prj < right.prj;
> >> + default:
> >> + BUG();
> >> + }
> >> +}
> >
> > What is this function used for? it's not referenced at all by the
> > patch, and there's no documentation/comments explaining why it
> > exists or how it is intended to be used....
>
> This function is introduced early, but it is needed for gfs2 as
> performs a sort of quota identifiers.

Introduce it when it is used so the correctness can be determined in
the context that uses it.

> >> +static inline qid_t from_kqid(struct user_namespace *user_ns, struct kqid qid)
> >> +{
> >> + switch (qid.type) {
> >> + case USRQUOTA:
> >> + return from_kuid(user_ns, qid.uid);
> >> + case GRPQUOTA:
> >> + return from_kgid(user_ns, qid.gid);
> >> + case XQM_PRJQUOTA:
> >> + return (user_ns == &init_user_ns) ? qid.prj : -1;
> >> + default:
> >> + BUG();
> >> + }
> >> +}
> >
> > Oh, this can return an error. That's only checked in a coupl eof
> > places this function is called. it needs tobe checked everywhere,
> > otherwise we now have the possibility of quota usage being accounted
> > to uid/gid/prid 0xffffffff when namespace matches are not found.
>
> No this is not an error condition. Returning -1 is the mapping that is
> used when there is not a mapping entry.
>
> Depending on the circumstances not having a mapping can be an error,
> but it can also be a don't care condition.

Which the XFS code would then use as the quota ID for accounting.
That's wrong. So, not having a mapping in this case is an error than
needs to be handled. You didn't add any error handling.

> All in kernel values are defined as having a mapping into the initial
> user namespace.
>
> Looking at my tree the only calls of from_kqid not mapping the id
> into the initial user namespace are from_kqid_munged and they report
> to userspace. from_kqid_munged returns fs_overflowuid or fs_overflowgid
> in case of a mapping failure.
>
> projects ids can only be accessed if capable(CAP_SYS_ADMIN) which
> you can only have in the initial user namespace so for now there will
> alwasy be a mapping for project ids.

from_kuid and from_kgid can return -1 when a mapping fails, so it is
not just project quotas that are at issue here. Returning -1 as a
quota id is almost always going to be an invalid ID. If you can't
account quota correctly, then the operation should not be allowed to
proceed. IOWs, no mapping is an error from a quota perspective.

And FWIW, explanations like this need to be in the code....

> >> +static inline qid_t from_kqid_munged(struct user_namespace *user_ns,
> >> + struct kqid qid)
> >
> > What does munging do to the return value? how is it different to
> > from_kqid()? Document your API....
>
> This bit certainly certainly could use a bit more explanation.

Yes, the lack of comments in your code is a bit disturbing.

> Mapping of uids and gids from one domain to another is not new in linux.
> It originates with the transition from 16bit identifiers to 32bit
> identifiers. In most places when there is a 32bit identifier that can
> not be represented we return a fs_overflowid aka (u16)-2 aka nobody.
>
> So in general when we want to pass a value to userspace from_kqid_munged
> is called, and if there is a mapping failure we report the value that
> userspace set fs_overflowuid or fs_overflowgid to. For project ids it
> which are restricted to the initial user namespace no mapping failures
> can occur.

Please add this as a comment to the code.

> >> +static inline struct kqid make_kqid_uid(kuid_t uid)
> >> +{
> >> + struct kqid kqid = {
> >> + .type = USRQUOTA,
> >> + .uid = uid,
> >> + };
> >> + return kqid;
> >> +}
> >
> > Isn't this sort of construct frowned upon? i.e. returning a
> > structure out of scope? It may be inline code and hence work, but
> > this strikes me as a landmine waiting for someone to tread on....
>
> Not at all because I am returning the structure by value.
>
> Return a structure by reference is the case that doesn't work.

Right, but you completely missed my point. It's unconventional,
tricky code - returning a structure by value is not something that
is commonly used. If I have to stop and think hard about why 5 lines
of apparently simple code is really OK, then IMO you are doing
something wrong. I might only look at this once every 6 months - I
don't want to have to spend time working this tricky stuff out
every time I look at it...

Cheers,

Dave.
--
Dave Chinner
[email protected]