From ba58148b6f0408b791e097ab2967c8f6da959fd9 Mon Sep 17 00:00:00 2001 From: Eric Sandeen Date: Fri, 22 Jan 2016 12:25:32 -0600 Subject: quota: Fixup comments about return value of Q_[X]GETNEXTQUOTA We actually return ENOENT, not ESRCH, when there is no structure with higher ID from ->get_nextdqblk. Fixup comments. Signed-off-by: Jan Kara --- fs/quota/quota.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs/quota') diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 0ebc90496525..a925f629a00a 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -224,7 +224,7 @@ static int quota_getquota(struct super_block *sb, int type, qid_t id, /* * Return quota for next active quota >= this id, if any exists, - * otherwise return -ESRCH via ->get_nextdqblk + * otherwise return -ENOENT via ->get_nextdqblk */ static int quota_getnextquota(struct super_block *sb, int type, qid_t id, void __user *addr) @@ -655,7 +655,7 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id, /* * Return quota for next active quota >= this id, if any exists, - * otherwise return -ESRCH via ->get_nextdqblk. + * otherwise return -ENOENT via ->get_nextdqblk. */ static int quota_getnextxquota(struct super_block *sb, int type, qid_t id, void __user *addr) -- cgit v1.2.3 From 7955118eafc4a2621fd88e92b505919af344583f Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 25 Jan 2016 16:13:53 +0100 Subject: quota: Allow Q_GETQUOTA for frozen filesystem quota_cmd_write() forgot to list Q_GETQUOTA among commands allowed for frozen filesystem. Thus Q_GETQUOTA quotactl would unnecessarily block on frozen filesystems. Fix the issue by properly listing Q_GETQUOTA. Signed-off-by: Jan Kara --- fs/quota/quota.c | 1 + 1 file changed, 1 insertion(+) (limited to 'fs/quota') diff --git a/fs/quota/quota.c b/fs/quota/quota.c index a925f629a00a..8e297c92f7d4 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -768,6 +768,7 @@ static int quotactl_cmd_write(int cmd) switch (cmd) { case Q_GETFMT: case Q_GETINFO: + case Q_GETQUOTA: case Q_GETNEXTQUOTA: case Q_SYNC: case Q_XGETQSTAT: -- cgit v1.2.3 From be6257b251cebd2deb8c76d43e387e28e3f7412d Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 25 Jan 2016 19:24:50 +0100 Subject: quota: Add support for ->get_nextdqblk() for VFS quota Add infrastructure for supporting get_nextdqblk() callback for VFS quotas. Translate the operation into a callback to appropriate filesystem and consequently to quota format callback. Signed-off-by: Jan Kara --- fs/ext4/super.c | 1 + fs/quota/dquot.c | 39 +++++++++++++++++++++++++++++++++++++++ fs/reiserfs/super.c | 1 + include/linux/quota.h | 3 +++ include/linux/quotaops.h | 3 +++ 5 files changed, 47 insertions(+) (limited to 'fs/quota') diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 3ed01ec011d7..b5bcbddceb91 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -1132,6 +1132,7 @@ static const struct dquot_operations ext4_quota_operations = { .alloc_dquot = dquot_alloc, .destroy_dquot = dquot_destroy, .get_projid = ext4_get_projid, + .get_next_id = dquot_get_next_id, }; static const struct quotactl_ops ext4_qctl_operations = { diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 3c3b81bb6dfe..7e0137bde6d6 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -2031,6 +2031,21 @@ int dquot_commit_info(struct super_block *sb, int type) } EXPORT_SYMBOL(dquot_commit_info); +int dquot_get_next_id(struct super_block *sb, struct kqid *qid) +{ + struct quota_info *dqopt = sb_dqopt(sb); + int err; + + if (!dqopt->ops[qid->type]->get_next_id) + return -ENOSYS; + mutex_lock(&dqopt->dqio_mutex); + err = dqopt->ops[qid->type]->get_next_id(sb, qid); + mutex_unlock(&dqopt->dqio_mutex); + + return err; +} +EXPORT_SYMBOL(dquot_get_next_id); + /* * Definitions of diskquota operations. */ @@ -2042,6 +2057,7 @@ const struct dquot_operations dquot_operations = { .write_info = dquot_commit_info, .alloc_dquot = dquot_alloc, .destroy_dquot = dquot_destroy, + .get_next_id = dquot_get_next_id, }; EXPORT_SYMBOL(dquot_operations); @@ -2565,6 +2581,27 @@ int dquot_get_dqblk(struct super_block *sb, struct kqid qid, } EXPORT_SYMBOL(dquot_get_dqblk); +int dquot_get_next_dqblk(struct super_block *sb, struct kqid *qid, + struct qc_dqblk *di) +{ + struct dquot *dquot; + int err; + + if (!sb->dq_op->get_next_id) + return -ENOSYS; + err = sb->dq_op->get_next_id(sb, qid); + if (err < 0) + return err; + dquot = dqget(sb, *qid); + if (IS_ERR(dquot)) + return PTR_ERR(dquot); + do_get_dqblk(dquot, di); + dqput(dquot); + + return 0; +} +EXPORT_SYMBOL(dquot_get_next_dqblk); + #define VFS_QC_MASK \ (QC_SPACE | QC_SPC_SOFT | QC_SPC_HARD | \ QC_INO_COUNT | QC_INO_SOFT | QC_INO_HARD | \ @@ -2765,6 +2802,7 @@ const struct quotactl_ops dquot_quotactl_ops = { .get_state = dquot_get_state, .set_info = dquot_set_dqinfo, .get_dqblk = dquot_get_dqblk, + .get_nextdqblk = dquot_get_next_dqblk, .set_dqblk = dquot_set_dqblk }; EXPORT_SYMBOL(dquot_quotactl_ops); @@ -2776,6 +2814,7 @@ const struct quotactl_ops dquot_quotactl_sysfile_ops = { .get_state = dquot_get_state, .set_info = dquot_set_dqinfo, .get_dqblk = dquot_get_dqblk, + .get_nextdqblk = dquot_get_next_dqblk, .set_dqblk = dquot_set_dqblk }; EXPORT_SYMBOL(dquot_quotactl_sysfile_ops); diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index c0306ec8ed7b..b8f2d1e8c645 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -802,6 +802,7 @@ static const struct dquot_operations reiserfs_quota_operations = { .write_info = reiserfs_write_info, .alloc_dquot = dquot_alloc, .destroy_dquot = dquot_destroy, + .get_next_id = dquot_get_next_id, }; static const struct quotactl_ops reiserfs_qctl_operations = { diff --git a/include/linux/quota.h b/include/linux/quota.h index fba92f5c1a63..9dfb6bce8c9e 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h @@ -306,6 +306,7 @@ struct quota_format_ops { int (*read_dqblk)(struct dquot *dquot); /* Read structure for one user */ int (*commit_dqblk)(struct dquot *dquot); /* Write structure for one user */ int (*release_dqblk)(struct dquot *dquot); /* Called when last reference to dquot is being dropped */ + int (*get_next_id)(struct super_block *sb, struct kqid *qid); /* Get next ID with existing structure in the quota file */ }; /* Operations working with dquots */ @@ -321,6 +322,8 @@ struct dquot_operations { * quota code only */ qsize_t *(*get_reserved_space) (struct inode *); int (*get_projid) (struct inode *, kprojid_t *);/* Get project ID */ + /* Get next ID with active quota structure */ + int (*get_next_id) (struct super_block *sb, struct kqid *qid); }; struct path; diff --git a/include/linux/quotaops.h b/include/linux/quotaops.h index 7a57c28eb5e7..f00fa86ac966 100644 --- a/include/linux/quotaops.h +++ b/include/linux/quotaops.h @@ -82,6 +82,7 @@ int dquot_commit(struct dquot *dquot); int dquot_acquire(struct dquot *dquot); int dquot_release(struct dquot *dquot); int dquot_commit_info(struct super_block *sb, int type); +int dquot_get_next_id(struct super_block *sb, struct kqid *qid); int dquot_mark_dquot_dirty(struct dquot *dquot); int dquot_file_open(struct inode *inode, struct file *file); @@ -99,6 +100,8 @@ int dquot_get_state(struct super_block *sb, struct qc_state *state); int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii); int dquot_get_dqblk(struct super_block *sb, struct kqid id, struct qc_dqblk *di); +int dquot_get_next_dqblk(struct super_block *sb, struct kqid *id, + struct qc_dqblk *di); int dquot_set_dqblk(struct super_block *sb, struct kqid id, struct qc_dqblk *di); -- cgit v1.2.3 From 0066373d9f9320f69f24b080ee00d10f14397355 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Mon, 25 Jan 2016 20:39:27 +0100 Subject: quota_v2: Implement get_next_id() for V2 quota format Implement functions to get id of next existing quota structure in quota file for quota tree based formats and thus for V2 quota format. Signed-off-by: Jan Kara --- fs/quota/quota_tree.c | 67 +++++++++++++++++++++++++++++++++++++++++++-- fs/quota/quota_v2.c | 6 ++++ include/linux/dqblk_qtree.h | 2 ++ 3 files changed, 73 insertions(+), 2 deletions(-) (limited to 'fs/quota') diff --git a/fs/quota/quota_tree.c b/fs/quota/quota_tree.c index 58efb83dec1c..0738972e8d3f 100644 --- a/fs/quota/quota_tree.c +++ b/fs/quota/quota_tree.c @@ -22,10 +22,9 @@ MODULE_LICENSE("GPL"); #define __QUOTA_QT_PARANOIA -static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth) +static int __get_index(struct qtree_mem_dqinfo *info, qid_t id, int depth) { unsigned int epb = info->dqi_usable_bs >> 2; - qid_t id = from_kqid(&init_user_ns, qid); depth = info->dqi_qtree_depth - depth - 1; while (depth--) @@ -33,6 +32,13 @@ static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth) return id % epb; } +static int get_index(struct qtree_mem_dqinfo *info, struct kqid qid, int depth) +{ + qid_t id = from_kqid(&init_user_ns, qid); + + return __get_index(info, id, depth); +} + /* Number of entries in one blocks */ static int qtree_dqstr_in_blk(struct qtree_mem_dqinfo *info) { @@ -668,3 +674,60 @@ int qtree_release_dquot(struct qtree_mem_dqinfo *info, struct dquot *dquot) return 0; } EXPORT_SYMBOL(qtree_release_dquot); + +static int find_next_id(struct qtree_mem_dqinfo *info, qid_t *id, + unsigned int blk, int depth) +{ + char *buf = getdqbuf(info->dqi_usable_bs); + __le32 *ref = (__le32 *)buf; + ssize_t ret; + unsigned int epb = info->dqi_usable_bs >> 2; + unsigned int level_inc = 1; + int i; + + if (!buf) + return -ENOMEM; + + for (i = depth; i < info->dqi_qtree_depth - 1; i++) + level_inc *= epb; + + ret = read_blk(info, blk, buf); + if (ret < 0) { + quota_error(info->dqi_sb, + "Can't read quota tree block %u", blk); + goto out_buf; + } + for (i = __get_index(info, *id, depth); i < epb; i++) { + if (ref[i] == cpu_to_le32(0)) { + *id += level_inc; + continue; + } + if (depth == info->dqi_qtree_depth - 1) { + ret = 0; + goto out_buf; + } + ret = find_next_id(info, id, le32_to_cpu(ref[i]), depth + 1); + if (ret != -ENOENT) + break; + } + if (i == epb) { + ret = -ENOENT; + goto out_buf; + } +out_buf: + kfree(buf); + return ret; +} + +int qtree_get_next_id(struct qtree_mem_dqinfo *info, struct kqid *qid) +{ + qid_t id = from_kqid(&init_user_ns, *qid); + int ret; + + ret = find_next_id(info, &id, QT_TREEOFF, 0); + if (ret < 0) + return ret; + *qid = make_kqid(&init_user_ns, qid->type, id); + return 0; +} +EXPORT_SYMBOL(qtree_get_next_id); diff --git a/fs/quota/quota_v2.c b/fs/quota/quota_v2.c index ed85d4f35c04..ca71bf881ad1 100644 --- a/fs/quota/quota_v2.c +++ b/fs/quota/quota_v2.c @@ -304,6 +304,11 @@ static int v2_free_file_info(struct super_block *sb, int type) return 0; } +static int v2_get_next_id(struct super_block *sb, struct kqid *qid) +{ + return qtree_get_next_id(sb_dqinfo(sb, qid->type)->dqi_priv, qid); +} + static const struct quota_format_ops v2_format_ops = { .check_quota_file = v2_check_quota_file, .read_file_info = v2_read_file_info, @@ -312,6 +317,7 @@ static const struct quota_format_ops v2_format_ops = { .read_dqblk = v2_read_dquot, .commit_dqblk = v2_write_dquot, .release_dqblk = v2_release_dquot, + .get_next_id = v2_get_next_id, }; static struct quota_format_type v2r0_quota_format = { diff --git a/include/linux/dqblk_qtree.h b/include/linux/dqblk_qtree.h index ff8b55359648..0de21e935976 100644 --- a/include/linux/dqblk_qtree.h +++ b/include/linux/dqblk_qtree.h @@ -15,6 +15,7 @@ #define QTREE_DEL_REWRITE 6 struct dquot; +struct kqid; /* Operations */ struct qtree_fmt_operations { @@ -52,5 +53,6 @@ static inline int qtree_depth(struct qtree_mem_dqinfo *info) entries *= epb; return i; } +int qtree_get_next_id(struct qtree_mem_dqinfo *info, struct kqid *qid); #endif /* _LINUX_DQBLK_QTREE_H */ -- cgit v1.2.3 From 044c9b6753a6b6cf486e16b53296b4707b35dbe3 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 18 Feb 2016 13:20:20 +0100 Subject: quota: Fix possible races during quota loading When loading new quota structure from disk, there is a possibility caller of dqget() will see uninitialized data due to CPU reordering loads or stores - loads from dquot can be reordered before test of DQ_ACTIVE_B bit or setting of this bit could be reordered before filling of the structure. Fix the issue by adding proper memory barriers. Signed-off-by: Jan Kara --- fs/quota/dquot.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'fs/quota') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index 7e0137bde6d6..e8467dc90c7e 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -411,6 +411,8 @@ int dquot_acquire(struct dquot *dquot) ret = dqopt->ops[dquot->dq_id.type]->read_dqblk(dquot); if (ret < 0) goto out_iolock; + /* Make sure flags update is visible after dquot has been filled */ + smp_mb__before_atomic(); set_bit(DQ_READ_B, &dquot->dq_flags); /* Instantiate dquot if needed */ if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) { @@ -427,6 +429,11 @@ int dquot_acquire(struct dquot *dquot) goto out_iolock; } } + /* + * Make sure flags update is visible after on-disk struct has been + * allocated. Paired with smp_rmb() in dqget(). + */ + smp_mb__before_atomic(); set_bit(DQ_ACTIVE_B, &dquot->dq_flags); out_iolock: mutex_unlock(&dqopt->dqio_mutex); @@ -887,6 +894,11 @@ we_slept: goto out; } } + /* + * Make sure following reads see filled structure - paired with + * smp_mb__before_atomic() in dquot_acquire(). + */ + smp_rmb(); #ifdef CONFIG_QUOTA_DEBUG BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */ #endif -- cgit v1.2.3 From ccf370e43e08cab4f8f13464c02c9aeffd8f0cd6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Thu, 18 Feb 2016 14:03:03 +0100 Subject: quota: Forbid Q_GETQUOTA and Q_GETNEXTQUOTA for frozen filesystem Commit 7955118eafc4 (quota: Allow Q_GETQUOTA for frozen filesystem) allowed Q_GETQUOTA call for frozen filesystem. It makes sense on the first look but zero-day testing has shown that with this change ext4 warns about starting a transaction for frozen filesystem. This happens because ext4_acquire_dquot() prepares for allocating space for new quota structure. Although it would be possible to implement Q_GETQUOTA for ext4 without allocating space for non-existent structures, the matter further complicates because OCFS2 needs to update on-disk structure use count when a new cluster node loads quota information from disk. So just revert the change and forbid Q_GETQUOTA together with Q_GETNEXTQUOTA for frozen filesystem. Add comment to quotactl_cmd_write() to save us from repeating this excercise in a few years when I forget again. Signed-off-by: Jan Kara --- fs/quota/quota.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/quota') diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 8e297c92f7d4..0f10ee9892ce 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c @@ -765,11 +765,14 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, /* Return 1 if 'cmd' will block on frozen filesystem */ static int quotactl_cmd_write(int cmd) { + /* + * We cannot allow Q_GETQUOTA and Q_GETNEXTQUOTA without write access + * as dquot_acquire() may allocate space for new structure and OCFS2 + * needs to increment on-disk use count. + */ switch (cmd) { case Q_GETFMT: case Q_GETINFO: - case Q_GETQUOTA: - case Q_GETNEXTQUOTA: case Q_SYNC: case Q_XGETQSTAT: case Q_XGETQSTATV: -- cgit v1.2.3 From ab73ef46398e2c0159f3a71de834586422d2a44a Mon Sep 17 00:00:00 2001 From: Nikolay Borisov Date: Thu, 3 Mar 2016 10:54:57 +0100 Subject: quota: Fix possible GPF due to uninitialised pointers When dqget() in __dquot_initialize() fails e.g. due to IO error, __dquot_initialize() will pass an array of uninitialized pointers to dqput_all() and thus can lead to deference of random data. Fix the problem by properly initializing the array. CC: stable@vger.kernel.org Signed-off-by: Nikolay Borisov Signed-off-by: Jan Kara --- fs/quota/dquot.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'fs/quota') diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index e8467dc90c7e..dcec1edf579f 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1410,7 +1410,7 @@ static int dquot_active(const struct inode *inode) static int __dquot_initialize(struct inode *inode, int type) { int cnt, init_needed = 0; - struct dquot **dquots, *got[MAXQUOTAS]; + struct dquot **dquots, *got[MAXQUOTAS] = {}; struct super_block *sb = inode->i_sb; qsize_t rsv; int ret = 0; @@ -1427,7 +1427,6 @@ static int __dquot_initialize(struct inode *inode, int type) int rc; struct dquot *dquot; - got[cnt] = NULL; if (type != -1 && cnt != type) continue; /* -- cgit v1.2.3