gfs2: perform quota checks against allocation parameters
authorAbhi Das <adas@redhat.com>
Wed, 18 Mar 2015 17:03:41 +0000 (12:03 -0500)
committerBob Peterson <rpeterso@redhat.com>
Wed, 18 Mar 2015 17:46:54 +0000 (12:46 -0500)
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>
fs/gfs2/aops.c
fs/gfs2/bmap.c
fs/gfs2/file.c
fs/gfs2/incore.h
fs/gfs2/inode.c
fs/gfs2/quota.c
fs/gfs2/quota.h
fs/gfs2/xattr.c

index 4ad4f94edebe25cc8afa3fa7c4ec35913cb00642..7bc5c82423eaa7dfc35ae9538e7c11e7701f92d5 100644 (file)
@@ -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;
index f0b945ab853e488a852a1a8459e9aa873def54b9..61296ecbd0e20a59a8d5067169e871b1628d93a4 100644 (file)
@@ -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;
 
index 7353c0a01a1e2e51c6f37e6ba60a326e56dffe74..c569adbc1431f20bfba3abae34e3307a18dd6ed7 100644 (file)
@@ -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;
index 7a2dbbc0d6348e39872491b3e6a070c5729f93f5..3a4ea50c911338e826651ca2bd3da8490e1a6e8b 100644 (file)
@@ -301,7 +301,7 @@ struct gfs2_blkreserv {
  * to the allocation code.
  */
 struct gfs2_alloc_parms {
-       u32 target;
+       u64 target;
        u32 aflags;
 };
 
index 73c72253faac07f2918c08e5e20438d148160f7e..08bc84d7e768e7fb46ab7bae03c8f18597cb59fc 100644 (file)
@@ -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:
index 3aa17d4d1cfcbf341e6115a8029c0e82f3e1a2cd..964a769a7a8663e1654bf98ff626d8e2f2f33f7d 100644 (file)
@@ -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) &&
index 55d506eb3c4a31ab956ab23e85158d0ddc4947ea..ad04b3acae2b9d1449a8b2e1127dcc53e45bb74d 100644 (file)
@@ -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;
index 0b81f783f78724293b174cd997d7f7b7321468b9..fd260ce8869a86ca532f2138900bfc7eb7a70a06 100644 (file)
@@ -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;