transfer_to[cnt] = dqget() may returns NULL due to IO error. But NULL value in transfer_to[cnt] means a dquot transfer optimization. So after operation succeed inode will have new i_uid or i_gid but accounted to old dquot. This behaviour is differ from dquot_initialize(). Let's handle IO error from dqget() equally in all functions. In appliance to dquot_transfer() this means that we have to finish operation regardless to IO errors from dqget(). Signed-off-by: Dmitry Monakhov <dmonakhov@xxxxxxxxxx> --- fs/quota/dquot.c | 46 +++++++++++++++++++++++++++------------------- 1 files changed, 27 insertions(+), 19 deletions(-) diff --git a/fs/quota/dquot.c b/fs/quota/dquot.c index d6d535c..fbbaa4e 100644 --- a/fs/quota/dquot.c +++ b/fs/quota/dquot.c @@ -1733,27 +1733,29 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) struct dquot *transfer_from[MAXQUOTAS]; struct dquot *transfer_to[MAXQUOTAS]; int cnt, ret = QUOTA_OK; - int chuid = iattr->ia_valid & ATTR_UID && inode->i_uid != iattr->ia_uid, - chgid = iattr->ia_valid & ATTR_GID && inode->i_gid != iattr->ia_gid; char warntype_to[MAXQUOTAS]; char warntype_from_inodes[MAXQUOTAS], warntype_from_space[MAXQUOTAS]; + char valid[MAXQUOTAS]; /* First test before acquiring mutex - solves deadlocks when we * re-enter the quota code and are already holding the mutex */ if (IS_NOQUOTA(inode)) return QUOTA_OK; /* Initialize the arrays */ + memset(transfer_to, 0, sizeof(transfer_to)); + memset(transfer_from, 0, sizeof(transfer_from)); + memset(valid, 0, sizeof(valid)); + valid[USRQUOTA] = iattr->ia_valid & ATTR_UID && + inode->i_uid != iattr->ia_uid, + valid[GRPQUOTA] = iattr->ia_valid & ATTR_GID && + inode->i_gid != iattr->ia_gid; + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - transfer_from[cnt] = NULL; - transfer_to[cnt] = NULL; warntype_to[cnt] = QUOTA_NL_NOWARN; + if (!valid[cnt]) + continue; + transfer_to[cnt] = dqget(inode->i_sb, iattr->ia_uid, cnt); } - if (chuid) - transfer_to[USRQUOTA] = dqget(inode->i_sb, iattr->ia_uid, - USRQUOTA); - if (chgid) - transfer_to[GRPQUOTA] = dqget(inode->i_sb, iattr->ia_gid, - GRPQUOTA); down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ @@ -1767,9 +1769,11 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) space = cur_space + rsv_space; /* Build the transfer_from list and check the limits */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - if (!transfer_to[cnt]) + if (!valid[cnt]) continue; transfer_from[cnt] = inode->i_dquot[cnt]; + if (!transfer_to[cnt]) + continue; if (check_idq(transfer_to[cnt], 1, warntype_to + cnt) == NO_QUOTA || check_bdq(transfer_to[cnt], space, 0, warntype_to + cnt) == NO_QUOTA) @@ -1783,10 +1787,15 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) /* * Skip changes for same uid or gid or for turned off quota-type. */ - if (!transfer_to[cnt]) + if (!valid[cnt]) continue; + /* + * Due to IO error we might not have transfer_to[] or + * transfer_from[] structure. Nor than less the operation must + * being done regardless quota io errors. + */ + inode->i_dquot[cnt] = transfer_to[cnt]; - /* Due to IO error we might not have transfer_from[] structure */ if (transfer_from[cnt]) { warntype_from_inodes[cnt] = info_idq_free(transfer_from[cnt], 1); @@ -1797,12 +1806,11 @@ int dquot_transfer(struct inode *inode, struct iattr *iattr) dquot_free_reserved_space(transfer_from[cnt], rsv_space); } - - dquot_incr_inodes(transfer_to[cnt], 1); - dquot_incr_space(transfer_to[cnt], cur_space); - dquot_resv_space(transfer_to[cnt], rsv_space); - - inode->i_dquot[cnt] = transfer_to[cnt]; + if (transfer_to[cnt]) { + dquot_incr_inodes(transfer_to[cnt], 1); + dquot_incr_space(transfer_to[cnt], cur_space); + dquot_resv_space(transfer_to[cnt], rsv_space); + } } spin_unlock(&dq_data_lock); up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); -- 1.6.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html