quota: Propagate error from ->acquire_dquot()

Currently when some error happened in ->acquire_dquot(), dqget() just
returned NULL. That was indistinguishable from a case when e.g. someone
run quotaoff and so was generally silently ignored. However
->acquire_dquot() can fail because of ENOSPC or EIO in which case user
should better know. So propagate error up from ->acquire_dquot properly.

Signed-off-by: Jan Kara <jack@suse.cz>
This commit is contained in:
Jan Kara 2015-06-24 18:07:02 +02:00 committed by Jan Kara
parent d725e66c06
commit 6184fc0b8d
4 changed files with 72 additions and 33 deletions

View File

@ -1209,8 +1209,8 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb, && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) { OCFS2_FEATURE_RO_COMPAT_USRQUOTA)) {
transfer_to[USRQUOTA] = dqget(sb, make_kqid_uid(attr->ia_uid)); transfer_to[USRQUOTA] = dqget(sb, make_kqid_uid(attr->ia_uid));
if (!transfer_to[USRQUOTA]) { if (IS_ERR(transfer_to[USRQUOTA])) {
status = -ESRCH; status = PTR_ERR(transfer_to[USRQUOTA]);
goto bail_unlock; goto bail_unlock;
} }
} }
@ -1218,8 +1218,8 @@ int ocfs2_setattr(struct dentry *dentry, struct iattr *attr)
&& OCFS2_HAS_RO_COMPAT_FEATURE(sb, && OCFS2_HAS_RO_COMPAT_FEATURE(sb,
OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) { OCFS2_FEATURE_RO_COMPAT_GRPQUOTA)) {
transfer_to[GRPQUOTA] = dqget(sb, make_kqid_gid(attr->ia_gid)); transfer_to[GRPQUOTA] = dqget(sb, make_kqid_gid(attr->ia_gid));
if (!transfer_to[GRPQUOTA]) { if (IS_ERR(transfer_to[GRPQUOTA])) {
status = -ESRCH; status = PTR_ERR(transfer_to[GRPQUOTA]);
goto bail_unlock; goto bail_unlock;
} }
} }

View File

@ -499,8 +499,8 @@ static int ocfs2_recover_local_quota_file(struct inode *lqinode,
dquot = dqget(sb, dquot = dqget(sb,
make_kqid(&init_user_ns, type, make_kqid(&init_user_ns, type,
le64_to_cpu(dqblk->dqb_id))); le64_to_cpu(dqblk->dqb_id)));
if (!dquot) { if (IS_ERR(dquot)) {
status = -EIO; status = PTR_ERR(dquot);
mlog(ML_ERROR, "Failed to get quota structure " mlog(ML_ERROR, "Failed to get quota structure "
"for id %u, type %d. Cannot finish quota " "for id %u, type %d. Cannot finish quota "
"file recovery.\n", "file recovery.\n",

View File

@ -247,7 +247,7 @@ struct dqstats dqstats;
EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dqstats);
static qsize_t inode_get_rsv_space(struct inode *inode); static qsize_t inode_get_rsv_space(struct inode *inode);
static void __dquot_initialize(struct inode *inode, int type); static int __dquot_initialize(struct inode *inode, int type);
static inline unsigned int static inline unsigned int
hashfn(const struct super_block *sb, struct kqid qid) hashfn(const struct super_block *sb, struct kqid qid)
@ -832,16 +832,17 @@ static struct dquot *get_empty_dquot(struct super_block *sb, int type)
struct dquot *dqget(struct super_block *sb, struct kqid qid) struct dquot *dqget(struct super_block *sb, struct kqid qid)
{ {
unsigned int hashent = hashfn(sb, qid); unsigned int hashent = hashfn(sb, qid);
struct dquot *dquot = NULL, *empty = NULL; struct dquot *dquot, *empty = NULL;
if (!sb_has_quota_active(sb, qid.type)) if (!sb_has_quota_active(sb, qid.type))
return NULL; return ERR_PTR(-ESRCH);
we_slept: we_slept:
spin_lock(&dq_list_lock); spin_lock(&dq_list_lock);
spin_lock(&dq_state_lock); spin_lock(&dq_state_lock);
if (!sb_has_quota_active(sb, qid.type)) { if (!sb_has_quota_active(sb, qid.type)) {
spin_unlock(&dq_state_lock); spin_unlock(&dq_state_lock);
spin_unlock(&dq_list_lock); spin_unlock(&dq_list_lock);
dquot = ERR_PTR(-ESRCH);
goto out; goto out;
} }
spin_unlock(&dq_state_lock); spin_unlock(&dq_state_lock);
@ -876,12 +877,16 @@ we_slept:
* already finished or it will be canceled due to dq_count > 1 test */ * already finished or it will be canceled due to dq_count > 1 test */
wait_on_dquot(dquot); wait_on_dquot(dquot);
/* Read the dquot / allocate space in quota file */ /* Read the dquot / allocate space in quota file */
if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) {
sb->dq_op->acquire_dquot(dquot) < 0) { int err;
err = sb->dq_op->acquire_dquot(dquot);
if (err < 0) {
dqput(dquot); dqput(dquot);
dquot = NULL; dquot = ERR_PTR(err);
goto out; goto out;
} }
}
#ifdef CONFIG_QUOTA_DEBUG #ifdef CONFIG_QUOTA_DEBUG
BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */ BUG_ON(!dquot->dq_sb); /* Has somebody invalidated entry under us? */
#endif #endif
@ -1390,15 +1395,16 @@ static int dquot_active(const struct inode *inode)
* It is better to call this function outside of any transaction as it * It is better to call this function outside of any transaction as it
* might need a lot of space in journal for dquot structure allocation. * might need a lot of space in journal for dquot structure allocation.
*/ */
static void __dquot_initialize(struct inode *inode, int type) static int __dquot_initialize(struct inode *inode, int type)
{ {
int cnt, init_needed = 0; int cnt, init_needed = 0;
struct dquot **dquots, *got[MAXQUOTAS]; struct dquot **dquots, *got[MAXQUOTAS];
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
qsize_t rsv; qsize_t rsv;
int ret = 0;
if (!dquot_active(inode)) if (!dquot_active(inode))
return; return 0;
dquots = i_dquot(inode); dquots = i_dquot(inode);
@ -1407,6 +1413,7 @@ static void __dquot_initialize(struct inode *inode, int type)
struct kqid qid; struct kqid qid;
kprojid_t projid; kprojid_t projid;
int rc; int rc;
struct dquot *dquot;
got[cnt] = NULL; got[cnt] = NULL;
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
@ -1438,16 +1445,25 @@ static void __dquot_initialize(struct inode *inode, int type)
qid = make_kqid_projid(projid); qid = make_kqid_projid(projid);
break; break;
} }
got[cnt] = dqget(sb, qid); dquot = dqget(sb, qid);
if (IS_ERR(dquot)) {
/* We raced with somebody turning quotas off... */
if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot);
goto out_put;
}
dquot = NULL;
}
got[cnt] = dquot;
} }
/* All required i_dquot has been initialized */ /* All required i_dquot has been initialized */
if (!init_needed) if (!init_needed)
return; return 0;
spin_lock(&dq_data_lock); spin_lock(&dq_data_lock);
if (IS_NOQUOTA(inode)) if (IS_NOQUOTA(inode))
goto out_err; goto out_lock;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
if (type != -1 && cnt != type) if (type != -1 && cnt != type)
continue; continue;
@ -1469,15 +1485,18 @@ static void __dquot_initialize(struct inode *inode, int type)
dquot_resv_space(dquots[cnt], rsv); dquot_resv_space(dquots[cnt], rsv);
} }
} }
out_err: out_lock:
spin_unlock(&dq_data_lock); spin_unlock(&dq_data_lock);
out_put:
/* Drop unused references */ /* Drop unused references */
dqput_all(got); dqput_all(got);
return ret;
} }
void dquot_initialize(struct inode *inode) int dquot_initialize(struct inode *inode)
{ {
__dquot_initialize(inode, -1); return __dquot_initialize(inode, -1);
} }
EXPORT_SYMBOL(dquot_initialize); EXPORT_SYMBOL(dquot_initialize);
@ -1961,18 +1980,37 @@ EXPORT_SYMBOL(__dquot_transfer);
int dquot_transfer(struct inode *inode, struct iattr *iattr) int dquot_transfer(struct inode *inode, struct iattr *iattr)
{ {
struct dquot *transfer_to[MAXQUOTAS] = {}; struct dquot *transfer_to[MAXQUOTAS] = {};
struct dquot *dquot;
struct super_block *sb = inode->i_sb; struct super_block *sb = inode->i_sb;
int ret; int ret;
if (!dquot_active(inode)) if (!dquot_active(inode))
return 0; return 0;
if (iattr->ia_valid & ATTR_UID && !uid_eq(iattr->ia_uid, inode->i_uid)) 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)); dquot = dqget(sb, make_kqid_uid(iattr->ia_uid));
if (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid)) if (IS_ERR(dquot)) {
transfer_to[GRPQUOTA] = dqget(sb, make_kqid_gid(iattr->ia_gid)); if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot);
goto out_put;
}
dquot = NULL;
}
transfer_to[USRQUOTA] = dquot;
}
if (iattr->ia_valid & ATTR_GID && !gid_eq(iattr->ia_gid, inode->i_gid)){
dquot = dqget(sb, make_kqid_gid(iattr->ia_gid));
if (IS_ERR(dquot)) {
if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot);
goto out_put;
}
dquot = NULL;
}
transfer_to[GRPQUOTA] = dquot;
}
ret = __dquot_transfer(inode, transfer_to); ret = __dquot_transfer(inode, transfer_to);
out_put:
dqput_all(transfer_to); dqput_all(transfer_to);
return ret; return ret;
} }
@ -2518,8 +2556,8 @@ int dquot_get_dqblk(struct super_block *sb, struct kqid qid,
struct dquot *dquot; struct dquot *dquot;
dquot = dqget(sb, qid); dquot = dqget(sb, qid);
if (!dquot) if (IS_ERR(dquot))
return -ESRCH; return PTR_ERR(dquot);
do_get_dqblk(dquot, di); do_get_dqblk(dquot, di);
dqput(dquot); dqput(dquot);
@ -2631,8 +2669,8 @@ int dquot_set_dqblk(struct super_block *sb, struct kqid qid,
int rc; int rc;
dquot = dqget(sb, qid); dquot = dqget(sb, qid);
if (!dquot) { if (IS_ERR(dquot)) {
rc = -ESRCH; rc = PTR_ERR(dquot);
goto out; goto out;
} }
rc = do_set_dqblk(dquot, di); rc = do_set_dqblk(dquot, di);

View File

@ -43,7 +43,7 @@ void inode_claim_rsv_space(struct inode *inode, qsize_t number);
void inode_sub_rsv_space(struct inode *inode, qsize_t number); void inode_sub_rsv_space(struct inode *inode, qsize_t number);
void inode_reclaim_rsv_space(struct inode *inode, qsize_t number); void inode_reclaim_rsv_space(struct inode *inode, qsize_t number);
void dquot_initialize(struct inode *inode); int dquot_initialize(struct inode *inode);
void dquot_drop(struct inode *inode); void dquot_drop(struct inode *inode);
struct dquot *dqget(struct super_block *sb, struct kqid qid); struct dquot *dqget(struct super_block *sb, struct kqid qid);
static inline struct dquot *dqgrab(struct dquot *dquot) static inline struct dquot *dqgrab(struct dquot *dquot)
@ -200,8 +200,9 @@ static inline int sb_has_quota_active(struct super_block *sb, int type)
return 0; return 0;
} }
static inline void dquot_initialize(struct inode *inode) static inline int dquot_initialize(struct inode *inode)
{ {
return 0;
} }
static inline void dquot_drop(struct inode *inode) static inline void dquot_drop(struct inode *inode)