]> Pileus Git - ~andy/linux/blobdiff - fs/gfs2/inode.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[~andy/linux] / fs / gfs2 / inode.c
index d573125d1021c31072099a596bd3b33bbd2ac4e7..5c524180c98e85a50e8e4c63ce233f71ff82ea39 100644 (file)
@@ -149,7 +149,7 @@ struct inode *gfs2_inode_lookup(struct super_block *sb, unsigned int type,
        ip = GFS2_I(inode);
 
        if (!inode)
-               return ERR_PTR(-ENOBUFS);
+               return ERR_PTR(-ENOMEM);
 
        if (inode->i_state & I_NEW) {
                struct gfs2_sbd *sdp = GFS2_SB(inode);
@@ -469,14 +469,36 @@ static void init_dinode(struct gfs2_inode *dip, struct gfs2_inode *ip,
        brelse(dibh);
 }
 
+/**
+ * gfs2_trans_da_blocks - Calculate number of blocks to link inode
+ * @dip: The directory we are linking into
+ * @da: The dir add information
+ * @nr_inodes: The number of inodes involved
+ *
+ * This calculate the number of blocks we need to reserve in a
+ * transaction to link @nr_inodes into a directory. In most cases
+ * @nr_inodes will be 2 (the directory plus the inode being linked in)
+ * but in case of rename, 4 may be required.
+ *
+ * Returns: Number of blocks
+ */
+
+static unsigned gfs2_trans_da_blks(const struct gfs2_inode *dip,
+                                  const struct gfs2_diradd *da,
+                                  unsigned nr_inodes)
+{
+       return da->nr_blocks + gfs2_rg_blocks(dip, da->nr_blocks) +
+              (nr_inodes * RES_DINODE) + RES_QUOTA + RES_STATFS;
+}
+
 static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,
-                      struct gfs2_inode *ip, int arq)
+                      struct gfs2_inode *ip, struct gfs2_diradd *da)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&dip->i_inode);
-       struct gfs2_alloc_parms ap = { .target = sdp->sd_max_dirres, };
+       struct gfs2_alloc_parms ap = { .target = da->nr_blocks, };
        int error;
 
-       if (arq) {
+       if (da->nr_blocks) {
                error = gfs2_quota_lock_check(dip);
                if (error)
                        goto fail_quota_locks;
@@ -485,10 +507,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,
                if (error)
                        goto fail_quota_locks;
 
-               error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
-                                        dip->i_rgd->rd_length +
-                                        2 * RES_DINODE +
-                                        RES_STATFS + RES_QUOTA, 0);
+               error = gfs2_trans_begin(sdp, gfs2_trans_da_blks(dip, da, 2), 0);
                if (error)
                        goto fail_ipreserv;
        } else {
@@ -497,7 +516,7 @@ static int link_dinode(struct gfs2_inode *dip, const struct qstr *name,
                        goto fail_quota_locks;
        }
 
-       error = gfs2_dir_add(&dip->i_inode, name, ip);
+       error = gfs2_dir_add(&dip->i_inode, name, ip, da);
        if (error)
                goto fail_end_trans;
 
@@ -561,7 +580,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        struct dentry *d;
        int error;
        u32 aflags = 0;
-       int arq;
+       struct gfs2_diradd da = { .bh = NULL, };
 
        if (!name->len || name->len > GFS2_FNAMESIZE)
                return -ENAMETOOLONG;
@@ -586,6 +605,9 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        error = PTR_ERR(inode);
        if (!IS_ERR(inode)) {
                d = d_splice_alias(inode, dentry);
+               error = PTR_ERR(d);
+               if (IS_ERR(d))
+                       goto fail_gunlock;
                error = 0;
                if (file) {
                        if (S_ISREG(inode->i_mode)) {
@@ -603,7 +625,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
                goto fail_gunlock;
        }
 
-       arq = error = gfs2_diradd_alloc_required(dir, name);
+       error = gfs2_diradd_alloc_required(dir, name, &da);
        if (error < 0)
                goto fail_gunlock;
 
@@ -704,7 +726,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        if (error)
                goto fail_gunlock3;
 
-       error = link_dinode(dip, name, ip, arq);
+       error = link_dinode(dip, name, ip, &da);
        if (error)
                goto fail_gunlock3;
 
@@ -739,6 +761,7 @@ fail_free_vfs_inode:
        free_inode_nonrcu(inode);
        inode = NULL;
 fail_gunlock:
+       gfs2_dir_no_add(&da);
        gfs2_glock_dq_uninit(ghs);
        if (inode && !IS_ERR(inode)) {
                clear_nlink(inode);
@@ -799,6 +822,11 @@ static struct dentry *__gfs2_lookup(struct inode *dir, struct dentry *dentry,
        }
 
        d = d_splice_alias(inode, dentry);
+       if (IS_ERR(d)) {
+               iput(inode);
+               gfs2_glock_dq_uninit(&gh);
+               return d;
+       }
        if (file && S_ISREG(inode->i_mode))
                error = finish_open(file, dentry, gfs2_open_common, opened);
 
@@ -837,7 +865,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
        struct gfs2_inode *ip = GFS2_I(inode);
        struct gfs2_holder ghs[2];
        struct buffer_head *dibh;
-       int alloc_required;
+       struct gfs2_diradd da = { .bh = NULL, };
        int error;
 
        if (S_ISDIR(inode->i_mode))
@@ -892,13 +920,12 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
        if (ip->i_inode.i_nlink == (u32)-1)
                goto out_gunlock;
 
-       alloc_required = error = gfs2_diradd_alloc_required(dir, &dentry->d_name);
+       error = gfs2_diradd_alloc_required(dir, &dentry->d_name, &da);
        if (error < 0)
                goto out_gunlock;
-       error = 0;
 
-       if (alloc_required) {
-               struct gfs2_alloc_parms ap = { .target = sdp->sd_max_dirres, };
+       if (da.nr_blocks) {
+               struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
                error = gfs2_quota_lock_check(dip);
                if (error)
                        goto out_gunlock;
@@ -907,10 +934,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
                if (error)
                        goto out_gunlock_q;
 
-               error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
-                                        gfs2_rg_blocks(dip, sdp->sd_max_dirres) +
-                                        2 * RES_DINODE + RES_STATFS +
-                                        RES_QUOTA, 0);
+               error = gfs2_trans_begin(sdp, gfs2_trans_da_blks(dip, &da, 2), 0);
                if (error)
                        goto out_ipres;
        } else {
@@ -923,7 +947,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
        if (error)
                goto out_end_trans;
 
-       error = gfs2_dir_add(dir, &dentry->d_name, ip);
+       error = gfs2_dir_add(dir, &dentry->d_name, ip, &da);
        if (error)
                goto out_brelse;
 
@@ -939,12 +963,13 @@ out_brelse:
 out_end_trans:
        gfs2_trans_end(sdp);
 out_ipres:
-       if (alloc_required)
+       if (da.nr_blocks)
                gfs2_inplace_release(dip);
 out_gunlock_q:
-       if (alloc_required)
+       if (da.nr_blocks)
                gfs2_quota_unlock(dip);
 out_gunlock:
+       gfs2_dir_no_add(&da);
        gfs2_glock_dq(ghs + 1);
 out_child:
        gfs2_glock_dq(ghs);
@@ -1274,7 +1299,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
        struct gfs2_rgrpd *nrgd;
        unsigned int num_gh;
        int dir_rename = 0;
-       int alloc_required = 0;
+       struct gfs2_diradd da = { .nr_blocks = 0, };
        unsigned int x;
        int error;
 
@@ -1408,14 +1433,14 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
                        goto out_gunlock;
        }
 
-       if (nip == NULL)
-               alloc_required = gfs2_diradd_alloc_required(ndir, &ndentry->d_name);
-       error = alloc_required;
-       if (error < 0)
-               goto out_gunlock;
+       if (nip == NULL) {
+               error = gfs2_diradd_alloc_required(ndir, &ndentry->d_name, &da);
+               if (error)
+                       goto out_gunlock;
+       }
 
-       if (alloc_required) {
-               struct gfs2_alloc_parms ap = { .target = sdp->sd_max_dirres, };
+       if (da.nr_blocks) {
+               struct gfs2_alloc_parms ap = { .target = da.nr_blocks, };
                error = gfs2_quota_lock_check(ndip);
                if (error)
                        goto out_gunlock;
@@ -1424,10 +1449,8 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
                if (error)
                        goto out_gunlock_q;
 
-               error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
-                                        gfs2_rg_blocks(ndip, sdp->sd_max_dirres) +
-                                        4 * RES_DINODE + 4 * RES_LEAF +
-                                        RES_STATFS + RES_QUOTA + 4, 0);
+               error = gfs2_trans_begin(sdp, gfs2_trans_da_blks(ndip, &da, 4) +
+                                        4 * RES_LEAF + 4, 0);
                if (error)
                        goto out_ipreserv;
        } else {
@@ -1461,19 +1484,20 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
        if (error)
                goto out_end_trans;
 
-       error = gfs2_dir_add(ndir, &ndentry->d_name, ip);
+       error = gfs2_dir_add(ndir, &ndentry->d_name, ip, &da);
        if (error)
                goto out_end_trans;
 
 out_end_trans:
        gfs2_trans_end(sdp);
 out_ipreserv:
-       if (alloc_required)
+       if (da.nr_blocks)
                gfs2_inplace_release(ndip);
 out_gunlock_q:
-       if (alloc_required)
+       if (da.nr_blocks)
                gfs2_quota_unlock(ndip);
 out_gunlock:
+       gfs2_dir_no_add(&da);
        while (x--) {
                gfs2_glock_dq(ghs + x);
                gfs2_holder_uninit(ghs + x);
@@ -1627,10 +1651,22 @@ static int setattr_chown(struct inode *inode, struct iattr *attr)
        if (!(attr->ia_valid & ATTR_GID) || gid_eq(ogid, ngid))
                ogid = ngid = NO_GID_QUOTA_CHANGE;
 
-       error = gfs2_quota_lock(ip, nuid, ngid);
+       error = get_write_access(inode);
        if (error)
                return error;
 
+       error = gfs2_rs_alloc(ip);
+       if (error)
+               goto out;
+
+       error = gfs2_rindex_update(sdp);
+       if (error)
+               goto out;
+
+       error = gfs2_quota_lock(ip, nuid, ngid);
+       if (error)
+               goto out;
+
        if (!uid_eq(ouid, NO_UID_QUOTA_CHANGE) ||
            !gid_eq(ogid, NO_GID_QUOTA_CHANGE)) {
                error = gfs2_quota_check(ip, nuid, ngid);
@@ -1657,6 +1693,8 @@ out_end_trans:
        gfs2_trans_end(sdp);
 out_gunlock_q:
        gfs2_quota_unlock(ip);
+out:
+       put_write_access(inode);
        return error;
 }