diff options
author | Abhi Das <adas@redhat.com> | 2015-03-18 13:03:41 -0400 |
---|---|---|
committer | Bob Peterson <rpeterso@redhat.com> | 2015-03-18 13:46:54 -0400 |
commit | b8fbf471edb3dbf441716fd2a52a7ca76c381381 (patch) | |
tree | a5a2132cc29397dd1f1d8892497dbf18b2fb125f | |
parent | f1ea6f4ec0a48d7b6bbf4d380a0ac14d69fadb44 (diff) |
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>
-rw-r--r-- | fs/gfs2/aops.c | 6 | ||||
-rw-r--r-- | fs/gfs2/bmap.c | 2 | ||||
-rw-r--r-- | fs/gfs2/file.c | 15 | ||||
-rw-r--r-- | fs/gfs2/incore.h | 2 | ||||
-rw-r--r-- | fs/gfs2/inode.c | 18 | ||||
-rw-r--r-- | fs/gfs2/quota.c | 6 | ||||
-rw-r--r-- | fs/gfs2/quota.h | 8 | ||||
-rw-r--r-- | fs/gfs2/xattr.c | 2 |
8 files changed, 32 insertions, 27 deletions
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 4ad4f94edebe..7bc5c82423ea 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c | |||
@@ -671,12 +671,12 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping, | |||
671 | 671 | ||
672 | if (alloc_required) { | 672 | if (alloc_required) { |
673 | struct gfs2_alloc_parms ap = { .aflags = 0, }; | 673 | struct gfs2_alloc_parms ap = { .aflags = 0, }; |
674 | error = gfs2_quota_lock_check(ip); | 674 | requested = data_blocks + ind_blocks; |
675 | ap.target = requested; | ||
676 | error = gfs2_quota_lock_check(ip, &ap); | ||
675 | if (error) | 677 | if (error) |
676 | goto out_unlock; | 678 | goto out_unlock; |
677 | 679 | ||
678 | requested = data_blocks + ind_blocks; | ||
679 | ap.target = requested; | ||
680 | error = gfs2_inplace_reserve(ip, &ap); | 680 | error = gfs2_inplace_reserve(ip, &ap); |
681 | if (error) | 681 | if (error) |
682 | goto out_qunlock; | 682 | goto out_qunlock; |
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c index f0b945ab853e..61296ecbd0e2 100644 --- a/fs/gfs2/bmap.c +++ b/fs/gfs2/bmap.c | |||
@@ -1224,7 +1224,7 @@ static int do_grow(struct inode *inode, u64 size) | |||
1224 | 1224 | ||
1225 | if (gfs2_is_stuffed(ip) && | 1225 | if (gfs2_is_stuffed(ip) && |
1226 | (size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) { | 1226 | (size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) { |
1227 | error = gfs2_quota_lock_check(ip); | 1227 | error = gfs2_quota_lock_check(ip, &ap); |
1228 | if (error) | 1228 | if (error) |
1229 | return error; | 1229 | return error; |
1230 | 1230 | ||
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c index 7353c0a01a1e..c569adbc1431 100644 --- a/fs/gfs2/file.c +++ b/fs/gfs2/file.c | |||
@@ -429,11 +429,11 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) | |||
429 | if (ret) | 429 | if (ret) |
430 | goto out_unlock; | 430 | goto out_unlock; |
431 | 431 | ||
432 | ret = gfs2_quota_lock_check(ip); | ||
433 | if (ret) | ||
434 | goto out_unlock; | ||
435 | gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks); | 432 | gfs2_write_calc_reserv(ip, PAGE_CACHE_SIZE, &data_blocks, &ind_blocks); |
436 | ap.target = data_blocks + ind_blocks; | 433 | ap.target = data_blocks + ind_blocks; |
434 | ret = gfs2_quota_lock_check(ip, &ap); | ||
435 | if (ret) | ||
436 | goto out_unlock; | ||
437 | ret = gfs2_inplace_reserve(ip, &ap); | 437 | ret = gfs2_inplace_reserve(ip, &ap); |
438 | if (ret) | 438 | if (ret) |
439 | goto out_quota_unlock; | 439 | goto out_quota_unlock; |
@@ -827,13 +827,13 @@ static long __gfs2_fallocate(struct file *file, int mode, loff_t offset, loff_t | |||
827 | offset += bytes; | 827 | offset += bytes; |
828 | continue; | 828 | continue; |
829 | } | 829 | } |
830 | error = gfs2_quota_lock_check(ip); | ||
831 | if (error) | ||
832 | return error; | ||
833 | retry: | 830 | retry: |
834 | gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks); | 831 | gfs2_write_calc_reserv(ip, bytes, &data_blocks, &ind_blocks); |
835 | |||
836 | ap.target = data_blocks + ind_blocks; | 832 | ap.target = data_blocks + ind_blocks; |
833 | |||
834 | error = gfs2_quota_lock_check(ip, &ap); | ||
835 | if (error) | ||
836 | return error; | ||
837 | error = gfs2_inplace_reserve(ip, &ap); | 837 | error = gfs2_inplace_reserve(ip, &ap); |
838 | if (error) { | 838 | if (error) { |
839 | if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) { | 839 | if (error == -ENOSPC && bytes > sdp->sd_sb.sb_bsize) { |
@@ -841,6 +841,7 @@ retry: | |||
841 | bytes &= bsize_mask; | 841 | bytes &= bsize_mask; |
842 | if (bytes == 0) | 842 | if (bytes == 0) |
843 | bytes = sdp->sd_sb.sb_bsize; | 843 | bytes = sdp->sd_sb.sb_bsize; |
844 | gfs2_quota_unlock(ip); | ||
844 | goto retry; | 845 | goto retry; |
845 | } | 846 | } |
846 | goto out_qunlock; | 847 | goto out_qunlock; |
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 7a2dbbc0d634..3a4ea50c9113 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h | |||
@@ -301,7 +301,7 @@ struct gfs2_blkreserv { | |||
301 | * to the allocation code. | 301 | * to the allocation code. |
302 | */ | 302 | */ |
303 | struct gfs2_alloc_parms { | 303 | struct gfs2_alloc_parms { |
304 | u32 target; | 304 | u64 target; |
305 | u32 aflags; | 305 | u32 aflags; |
306 | }; | 306 | }; |
307 | 307 | ||
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c index 73c72253faac..08bc84d7e768 100644 --- a/fs/gfs2/inode.c +++ b/fs/gfs2/inode.c | |||
@@ -382,7 +382,7 @@ static int alloc_dinode(struct gfs2_inode *ip, u32 flags, unsigned *dblocks) | |||
382 | struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, }; | 382 | struct gfs2_alloc_parms ap = { .target = *dblocks, .aflags = flags, }; |
383 | int error; | 383 | int error; |
384 | 384 | ||
385 | error = gfs2_quota_lock_check(ip); | 385 | error = gfs2_quota_lock_check(ip, &ap); |
386 | if (error) | 386 | if (error) |
387 | goto out; | 387 | goto out; |
388 | 388 | ||
@@ -525,7 +525,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name, | |||
525 | int error; | 525 | int error; |
526 | 526 | ||
527 | if (da->nr_blocks) { | 527 | if (da->nr_blocks) { |
528 | error = gfs2_quota_lock_check(dip); | 528 | error = gfs2_quota_lock_check(dip, &ap); |
529 | if (error) | 529 | if (error) |
530 | goto fail_quota_locks; | 530 | goto fail_quota_locks; |
531 | 531 | ||
@@ -953,7 +953,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir, | |||
953 | 953 | ||
954 | if (da.nr_blocks) { | 954 | if (da.nr_blocks) { |
955 | struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; | 955 | struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; |
956 | error = gfs2_quota_lock_check(dip); | 956 | error = gfs2_quota_lock_check(dip, &ap); |
957 | if (error) | 957 | if (error) |
958 | goto out_gunlock; | 958 | goto out_gunlock; |
959 | 959 | ||
@@ -1470,7 +1470,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry, | |||
1470 | 1470 | ||
1471 | if (da.nr_blocks) { | 1471 | if (da.nr_blocks) { |
1472 | struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; | 1472 | struct gfs2_alloc_parms ap = { .target = da.nr_blocks, }; |
1473 | error = gfs2_quota_lock_check(ndip); | 1473 | error = gfs2_quota_lock_check(ndip, &ap); |
1474 | if (error) | 1474 | if (error) |
1475 | goto out_gunlock; | 1475 | goto out_gunlock; |
1476 | 1476 | ||
@@ -1669,6 +1669,7 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) | |||
1669 | kuid_t ouid, nuid; | 1669 | kuid_t ouid, nuid; |
1670 | kgid_t ogid, ngid; | 1670 | kgid_t ogid, ngid; |
1671 | int error; | 1671 | int error; |
1672 | struct gfs2_alloc_parms ap; | ||
1672 | 1673 | ||
1673 | ouid = inode->i_uid; | 1674 | ouid = inode->i_uid; |
1674 | ogid = inode->i_gid; | 1675 | ogid = inode->i_gid; |
@@ -1696,9 +1697,11 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) | |||
1696 | if (error) | 1697 | if (error) |
1697 | goto out; | 1698 | goto out; |
1698 | 1699 | ||
1700 | ap.target = gfs2_get_inode_blocks(&ip->i_inode); | ||
1701 | |||
1699 | if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || | 1702 | if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || |
1700 | !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { | 1703 | !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { |
1701 | error = gfs2_quota_check(ip, nuid, ngid); | 1704 | error = gfs2_quota_check(ip, nuid, ngid, &ap); |
1702 | if (error) | 1705 | if (error) |
1703 | goto out_gunlock_q; | 1706 | goto out_gunlock_q; |
1704 | } | 1707 | } |
@@ -1713,9 +1716,8 @@ static int setattr_chown(struct inode *inode, struct iattr *attr) | |||
1713 | 1716 | ||
1714 | if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || | 1717 | if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) || |
1715 | !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { | 1718 | !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) { |
1716 | u64 blocks = gfs2_get_inode_blocks(&ip->i_inode); | 1719 | gfs2_quota_change(ip, -ap.target, ouid, ogid); |
1717 | gfs2_quota_change(ip, -blocks, ouid, ogid); | 1720 | gfs2_quota_change(ip, ap.target, nuid, ngid); |
1718 | gfs2_quota_change(ip, blocks, nuid, ngid); | ||
1719 | } | 1721 | } |
1720 | 1722 | ||
1721 | out_end_trans: | 1723 | out_end_trans: |
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 3aa17d4d1cfc..964a769a7a86 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c | |||
@@ -1094,7 +1094,8 @@ static int print_message(struct gfs2_quota_data *qd, char *type) | |||
1094 | return 0; | 1094 | return 0; |
1095 | } | 1095 | } |
1096 | 1096 | ||
1097 | int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) | 1097 | int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid, |
1098 | struct gfs2_alloc_parms *ap) | ||
1098 | { | 1099 | { |
1099 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); | 1100 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); |
1100 | struct gfs2_quota_data *qd; | 1101 | struct gfs2_quota_data *qd; |
@@ -1117,14 +1118,13 @@ int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid) | |||
1117 | 1118 | ||
1118 | value = (s64)be64_to_cpu(qd->qd_qb.qb_value); | 1119 | value = (s64)be64_to_cpu(qd->qd_qb.qb_value); |
1119 | spin_lock(&qd_lock); | 1120 | spin_lock(&qd_lock); |
1120 | value += qd->qd_change; | 1121 | value += qd->qd_change + ap->target; |
1121 | spin_unlock(&qd_lock); | 1122 | spin_unlock(&qd_lock); |
1122 | 1123 | ||
1123 | if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) { | 1124 | if (be64_to_cpu(qd->qd_qb.qb_limit) && (s64)be64_to_cpu(qd->qd_qb.qb_limit) < value) { |
1124 | print_message(qd, "exceeded"); | 1125 | print_message(qd, "exceeded"); |
1125 | quota_send_warning(qd->qd_id, | 1126 | quota_send_warning(qd->qd_id, |
1126 | sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN); | 1127 | sdp->sd_vfs->s_dev, QUOTA_NL_BHARDWARN); |
1127 | |||
1128 | error = -EDQUOT; | 1128 | error = -EDQUOT; |
1129 | break; | 1129 | break; |
1130 | } else if (be64_to_cpu(qd->qd_qb.qb_warn) && | 1130 | } else if (be64_to_cpu(qd->qd_qb.qb_warn) && |
diff --git a/fs/gfs2/quota.h b/fs/gfs2/quota.h index 55d506eb3c4a..ad04b3acae2b 100644 --- a/fs/gfs2/quota.h +++ b/fs/gfs2/quota.h | |||
@@ -24,7 +24,8 @@ extern void gfs2_quota_unhold(struct gfs2_inode *ip); | |||
24 | extern int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); | 24 | extern int gfs2_quota_lock(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); |
25 | extern void gfs2_quota_unlock(struct gfs2_inode *ip); | 25 | extern void gfs2_quota_unlock(struct gfs2_inode *ip); |
26 | 26 | ||
27 | extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid); | 27 | extern int gfs2_quota_check(struct gfs2_inode *ip, kuid_t uid, kgid_t gid, |
28 | struct gfs2_alloc_parms *ap); | ||
28 | extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change, | 29 | extern void gfs2_quota_change(struct gfs2_inode *ip, s64 change, |
29 | kuid_t uid, kgid_t gid); | 30 | kuid_t uid, kgid_t gid); |
30 | 31 | ||
@@ -37,7 +38,8 @@ extern int gfs2_quotad(void *data); | |||
37 | 38 | ||
38 | extern void gfs2_wake_up_statfs(struct gfs2_sbd *sdp); | 39 | extern void gfs2_wake_up_statfs(struct gfs2_sbd *sdp); |
39 | 40 | ||
40 | static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) | 41 | static inline int gfs2_quota_lock_check(struct gfs2_inode *ip, |
42 | struct gfs2_alloc_parms *ap) | ||
41 | { | 43 | { |
42 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); | 44 | struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode); |
43 | int ret; | 45 | int ret; |
@@ -48,7 +50,7 @@ static inline int gfs2_quota_lock_check(struct gfs2_inode *ip) | |||
48 | return ret; | 50 | return ret; |
49 | if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) | 51 | if (sdp->sd_args.ar_quota != GFS2_QUOTA_ON) |
50 | return 0; | 52 | return 0; |
51 | ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid); | 53 | ret = gfs2_quota_check(ip, ip->i_inode.i_uid, ip->i_inode.i_gid, ap); |
52 | if (ret) | 54 | if (ret) |
53 | gfs2_quota_unlock(ip); | 55 | gfs2_quota_unlock(ip); |
54 | return ret; | 56 | return ret; |
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c index 0b81f783f787..fd260ce8869a 100644 --- a/fs/gfs2/xattr.c +++ b/fs/gfs2/xattr.c | |||
@@ -732,7 +732,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er, | |||
732 | if (error) | 732 | if (error) |
733 | return error; | 733 | return error; |
734 | 734 | ||
735 | error = gfs2_quota_lock_check(ip); | 735 | error = gfs2_quota_lock_check(ip, &ap); |
736 | if (error) | 736 | if (error) |
737 | return error; | 737 | return error; |
738 | 738 | ||