gfs2: perform quota checks against allocation parameters
Use struct gfs2_alloc_parms as an argument to gfs2_quota_check() and gfs2_quota_lock_check() to check for quota violations while accounting for the new blocks requested by the current operation in ap->target. Previously, the number of new blocks requested during an operation were not accounted for during quota_check and would allow these operations to exceed quota. This was not very apparent since most operations allocated only 1 block at a time and quotas would get violated in the next operation. i.e. quota excess would only be by 1 block or so. With fallocate, (where we allocate a bunch of blocks at once) the quota excess is non-trivial and is addressed by this patch. Signed-off-by: Abhi Das <adas@redhat.com> Signed-off-by: Bob Peterson <rpeterso@redhat.com> Acked-by: Steven Whitehouse <swhiteho@redhat.com>
This commit is contained in:
parent
f1ea6f4ec0
commit
b8fbf471ed
8 changed files with 32 additions and 27 deletions
|
@ -671,12 +671,12 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
|
||||||
|
|
||||||
if (alloc_required) {
|
if (alloc_required) {
|
||||||
struct gfs2_alloc_parms ap = { .aflags = 0, };
|
struct gfs2_alloc_parms ap = { .aflags = 0, };
|
||||||
error = gfs2_quota_lock_check(ip);
|
requested = data_blocks + ind_blocks;
|
||||||
|
ap.target = requested;
|
||||||
|
error = gfs2_quota_lock_check(ip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
requested = data_blocks + ind_blocks;
|
|
||||||
ap.target = requested;
|
|
||||||
error = gfs2_inplace_reserve(ip, &ap);
|
error = gfs2_inplace_reserve(ip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_qunlock;
|
goto out_qunlock;
|
||||||
|
|
|
@ -1224,7 +1224,7 @@ static int do_grow(struct inode *inode, u64 size)
|
||||||
|
|
||||||
if (gfs2_is_stuffed(ip) &&
|
if (gfs2_is_stuffed(ip) &&
|
||||||
(size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) {
|
(size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) {
|
||||||
error = gfs2_quota_lock_check(ip);
|
error = gfs2_quota_lock_check(ip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|
|
@ -429,11 +429,11 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
|
|
||||||
ret = gfs2_quota_lock_check(ip);
|
|
||||||
if (ret)
|
|
||||||
goto out_unlock;
|
|
||||||
gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks);
|
gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks);
|
||||||
ap.target = data_blocks + ind_blocks;
|
ap.target = data_blocks + ind_blocks;
|
||||||
|
ret = gfs2_quota_lock_check(ip, &ap);
|
||||||
|
if (ret)
|
||||||
|
goto out_unlock;
|
||||||
ret = gfs2_inplace_reserve(ip, &ap);
|
ret = gfs2_inplace_reserve(ip, &ap);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_quota_unlock;
|
goto out_quota_unlock;
|
||||||
|
@ -827,13 +827,13 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t
|
||||||
offset += bytes;
|
offset += bytes;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
error = gfs2_quota_lock_check(ip);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
retry:
|
retry:
|
||||||
gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks);
|
gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks);
|
||||||
|
|
||||||
ap.target = data_blocks + ind_blocks;
|
ap.target = data_blocks + ind_blocks;
|
||||||
|
|
||||||
|
error = gfs2_quota_lock_check(ip, &ap);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
error = gfs2_inplace_reserve(ip, &ap);
|
error = gfs2_inplace_reserve(ip, &ap);
|
||||||
if (error) {
|
if (error) {
|
||||||
if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) {
|
if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) {
|
||||||
|
@ -841,6 +841,7 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t
|
||||||
bytes &= bsize_mask;
|
bytes &= bsize_mask;
|
||||||
if (bytes == 0)
|
if (bytes == 0)
|
||||||
bytes = sdp->sd_sb.sb_bsize;
|
bytes = sdp->sd_sb.sb_bsize;
|
||||||
|
gfs2_quota_unlock(ip);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
goto out_qunlock;
|
goto out_qunlock;
|
||||||
|
|
|
@ -301,7 +301,7 @@ struct gfs2_blkreserv {
|
||||||
* to the allocation code.
|
* to the allocation code.
|
||||||
*/
|
*/
|
||||||
struct gfs2_alloc_parms {
|
struct gfs2_alloc_parms {
|
||||||
u32 target;
|
u64 target;
|
||||||
u32 aflags;
|
u32 aflags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -382,7 +382,7 @@ static int alloc_dinode(struct gfs2_inode *ip, u32 flags, unsigned *dblocks)
|
||||||
struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, };
|
struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, };
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = gfs2_quota_lock_check(ip);
|
error = gfs2_quota_lock_check(ip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (da->nr_blocks) {
|
if (da->nr_blocks) {
|
||||||
error = gfs2_quota_lock_check(dip);
|
error = gfs2_quota_lock_check(dip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
goto fail_quota_locks;
|
goto fail_quota_locks;
|
||||||
|
|
||||||
|
@ -953,7 +953,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
|
||||||
|
|
||||||
if (da.nr_blocks) {
|
if (da.nr_blocks) {
|
||||||
struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
|
struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
|
||||||
error = gfs2_quota_lock_check(dip);
|
error = gfs2_quota_lock_check(dip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_gunlock;
|
goto out_gunlock;
|
||||||
|
|
||||||
|
@ -1470,7 +1470,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
|
||||||
|
|
||||||
if (da.nr_blocks) {
|
if (da.nr_blocks) {
|
||||||
struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
|
struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
|
||||||
error = gfs2_quota_lock_check(ndip);
|
error = gfs2_quota_lock_check(ndip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_gunlock;
|
goto out_gunlock;
|
||||||
|
|
||||||
|
@ -1669,6 +1669,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)
|
||||||
kuid_t ouid, nuid;
|
kuid_t ouid, nuid;
|
||||||
kgid_t ogid, ngid;
|
kgid_t ogid, ngid;
|
||||||
int error;
|
int error;
|
||||||
|
struct gfs2_alloc_parms ap;
|
||||||
|
|
||||||
ouid = inode->i_uid;
|
ouid = inode->i_uid;
|
||||||
ogid = inode->i_gid;
|
ogid = inode->i_gid;
|
||||||
|
@ -1696,9 +1697,11 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
ap.target = gfs2_get_inode_blocks(&ip->i_inode);
|
||||||
|
|
||||||
if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
|
if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
|
||||||
!gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
|
!gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
|
||||||
error = gfs2_quota_check(ip, nuid, ngid);
|
error = gfs2_quota_check(ip, nuid, ngid, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_gunlock_q;
|
goto out_gunlock_q;
|
||||||
}
|
}
|
||||||
|
@ -1713,9 +1716,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)
|
||||||
|
|
||||||
if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
|
if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
|
||||||
!gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
|
!gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
|
||||||
u64 blocks = gfs2_get_inode_blocks(&ip->i_inode);
|
gfs2_quota_change(ip, -ap.target, ouid, ogid);
|
||||||
gfs2_quota_change(ip, -blocks, ouid, ogid);
|
gfs2_quota_change(ip, ap.target, nuid, ngid);
|
||||||
gfs2_quota_change(ip, blocks, nuid, ngid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out_end_trans:
|
out_end_trans:
|
||||||
|
|
|
@ -1094,7 +1094,8 @@ static int print_message(struct gfs2_quota_data *qd, char *type)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
|
int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid,
|
||||||
|
struct gfs2_alloc_parms *ap)
|
||||||
{
|
{
|
||||||
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||||
struct gfs2_quota_data *qd;
|
struct gfs2_quota_data *qd;
|
||||||
|
@ -1117,14 +1118,13 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid)
|
||||||
|
|
||||||
value = (s64)be64_to_cpu(qd->qd_qb.qb_value);
|
value = (s64)be64_to_cpu(qd->qd_qb.qb_value);
|
||||||
spin_lock(&qd_lock);
|
spin_lock(&qd_lock);
|
||||||
value += qd->qd_change;
|
value += qd->qd_change + ap->target;
|
||||||
spin_unlock(&qd_lock);
|
spin_unlock(&qd_lock);
|
||||||
|
|
||||||
if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
|
if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) {
|
||||||
print_message(qd, "exceeded");
|
print_message(qd, "exceeded");
|
||||||
quota_send_warning(qd->qd_id,
|
quota_send_warning(qd->qd_id,
|
||||||
sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
|
sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN);
|
||||||
|
|
||||||
error = -EDQUOT;
|
error = -EDQUOT;
|
||||||
break;
|
break;
|
||||||
} else if (be64_to_cpu(qd->qd_qb.qb_warn) &&
|
} else if (be64_to_cpu(qd->qd_qb.qb_warn) &&
|
||||||
|
|
|
@ -24,7 +24,8 @@ extern void gfs2_quota_unhold(struct gfs2_inode *ip);
|
||||||
extern int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid);
|
extern int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid);
|
||||||
extern void gfs2_quota_unlock(struct gfs2_inode *ip);
|
extern void gfs2_quota_unlock(struct gfs2_inode *ip);
|
||||||
|
|
||||||
extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid);
|
extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid,
|
||||||
|
struct gfs2_alloc_parms *ap);
|
||||||
extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change,
|
extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change,
|
||||||
kuid_t uid, kgid_t gid);
|
kuid_t uid, kgid_t gid);
|
||||||
|
|
||||||
|
@ -37,7 +38,8 @@ extern int gfs2_quotad(void *data);
|
||||||
|
|
||||||
extern void gfs2_wake_up_statfs(struct gfs2_sbd *sdp);
|
extern void gfs2_wake_up_statfs(struct gfs2_sbd *sdp);
|
||||||
|
|
||||||
static inline int gfs2_quota_lock_check(struct gfs2_inode *ip)
|
static inline int gfs2_quota_lock_check(struct gfs2_inode *ip,
|
||||||
|
struct gfs2_alloc_parms *ap)
|
||||||
{
|
{
|
||||||
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -48,7 +50,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip)
|
||||||
return ret;
|
return ret;
|
||||||
if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON)
|
if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON)
|
||||||
return 0;
|
return 0;
|
||||||
ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid);
|
ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid, ap);
|
||||||
if (ret)
|
if (ret)
|
||||||
gfs2_quota_unlock(ip);
|
gfs2_quota_unlock(ip);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -732,7 +732,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er,
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = gfs2_quota_lock_check(ip);
|
error = gfs2_quota_lock_check(ip, &ap);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue