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
					
				| @ -671,12 +671,12 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, | ||||
| 
 | ||||
| 	if (alloc_required) { | ||||
| 		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) | ||||
| 			goto out_unlock; | ||||
| 
 | ||||
| 		requested = data_blocks + ind_blocks; | ||||
| 		ap.target = requested; | ||||
| 		error = gfs2_inplace_reserve(ip, &ap); | ||||
| 		if (error) | ||||
| 			goto out_qunlock; | ||||
|  | ||||
| @ -1224,7 +1224,7 @@ static int do_grow(struct inode *inode, u64 size) | ||||
| 
 | ||||
| 	if (gfs2_is_stuffed(ip) && | ||||
| 	    (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) | ||||
| 			return error; | ||||
| 
 | ||||
|  | ||||
| @ -429,11 +429,11 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | ||||
| 	if (ret) | ||||
| 		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); | ||||
| 	ap.target = data_blocks + ind_blocks; | ||||
| 	ret = gfs2_quota_lock_check(ip, &ap); | ||||
| 	if (ret) | ||||
| 		goto out_unlock; | ||||
| 	ret = gfs2_inplace_reserve(ip, &ap); | ||||
| 	if (ret) | ||||
| 		goto out_quota_unlock; | ||||
| @ -827,13 +827,13 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t | ||||
| 			offset += bytes; | ||||
| 			continue; | ||||
| 		} | ||||
| 		error = gfs2_quota_lock_check(ip); | ||||
| 		if (error) | ||||
| 			return error; | ||||
| retry: | ||||
| 		gfs2_write_calc_reserv(ip, bytes, &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); | ||||
| 		if (error) { | ||||
| 			if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) { | ||||
| @ -841,6 +841,7 @@ retry: | ||||
| 				bytes &= bsize_mask; | ||||
| 				if (bytes == 0) | ||||
| 					bytes = sdp->sd_sb.sb_bsize; | ||||
| 				gfs2_quota_unlock(ip); | ||||
| 				goto retry; | ||||
| 			} | ||||
| 			goto out_qunlock; | ||||
|  | ||||
| @ -301,7 +301,7 @@ struct gfs2_blkreserv { | ||||
|  * to the allocation code. | ||||
|  */ | ||||
| struct gfs2_alloc_parms { | ||||
| 	u32 target; | ||||
| 	u64 target; | ||||
| 	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, }; | ||||
| 	int error; | ||||
| 
 | ||||
| 	error = gfs2_quota_lock_check(ip); | ||||
| 	error = gfs2_quota_lock_check(ip, &ap); | ||||
| 	if (error) | ||||
| 		goto out; | ||||
| 
 | ||||
| @ -525,7 +525,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, | ||||
| 	int error; | ||||
| 
 | ||||
| 	if (da->nr_blocks) { | ||||
| 		error = gfs2_quota_lock_check(dip); | ||||
| 		error = gfs2_quota_lock_check(dip, &ap); | ||||
| 		if (error) | ||||
| 			goto fail_quota_locks; | ||||
| 
 | ||||
| @ -953,7 +953,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, | ||||
| 
 | ||||
| 	if (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) | ||||
| 			goto out_gunlock; | ||||
| 
 | ||||
| @ -1470,7 +1470,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | ||||
| 
 | ||||
| 	if (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) | ||||
| 			goto out_gunlock; | ||||
| 
 | ||||
| @ -1669,6 +1669,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) | ||||
| 	kuid_t ouid, nuid; | ||||
| 	kgid_t ogid, ngid; | ||||
| 	int error; | ||||
| 	struct gfs2_alloc_parms ap; | ||||
| 
 | ||||
| 	ouid = inode->i_uid; | ||||
| 	ogid = inode->i_gid; | ||||
| @ -1696,9 +1697,11 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) | ||||
| 	if (error) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	ap.target = gfs2_get_inode_blocks(&ip->i_inode); | ||||
| 
 | ||||
| 	if (!uid_eq(ouid, NO_UID_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) | ||||
| 			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) || | ||||
| 	    !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { | ||||
| 		u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); | ||||
| 		gfs2_quota_change(ip, -blocks, ouid, ogid); | ||||
| 		gfs2_quota_change(ip, blocks, nuid, ngid); | ||||
| 		gfs2_quota_change(ip, -ap.target, ouid, ogid); | ||||
| 		gfs2_quota_change(ip, ap.target, nuid, ngid); | ||||
| 	} | ||||
| 
 | ||||
| out_end_trans: | ||||
|  | ||||
| @ -1094,7 +1094,8 @@ static int print_message(struct gfs2_quota_data *qd, char *type) | ||||
| 	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_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); | ||||
| 		spin_lock(&qd_lock); | ||||
| 		value += qd->qd_change; | ||||
| 		value += qd->qd_change + ap->target; | ||||
| 		spin_unlock(&qd_lock); | ||||
| 
 | ||||
| 		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(qd->qd_id, | ||||
| 					   sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN); | ||||
| 
 | ||||
| 			error = -EDQUOT; | ||||
| 			break; | ||||
| 		} 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 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, | ||||
| 			      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); | ||||
| 
 | ||||
| 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); | ||||
| 	int ret; | ||||
| @ -48,7 +50,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) | ||||
| 		return ret; | ||||
| 	if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) | ||||
| 		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) | ||||
| 		gfs2_quota_unlock(ip); | ||||
| 	return ret; | ||||
|  | ||||
| @ -732,7 +732,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er, | ||||
| 	if (error) | ||||
| 		return error; | ||||
| 
 | ||||
| 	error = gfs2_quota_lock_check(ip); | ||||
| 	error = gfs2_quota_lock_check(ip, &ap); | ||||
| 	if (error) | ||||
| 		return error; | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user