spufs_mkdir(): don't d_add() on negative parent
authorAl Viro <viro@zeniv.linux.org.uk>
Tue, 29 Jan 2013 01:37:21 +0000 (20:37 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 23 Feb 2013 04:31:38 +0000 (23:31 -0500)
NOTE: this really needs testing - I could've easily fucked up
refcounting in there.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
arch/powerpc/platforms/cell/spufs/inode.c

index 99db6161e5c9e3b09fbed062fb08d69d790cd3d4..863184b182f47c09e0626d8147ebcd1e508af328 100644 (file)
@@ -199,37 +199,18 @@ static int spufs_fill_dir(struct dentry *dir,
                const struct spufs_tree_descr *files, umode_t mode,
                struct spu_context *ctx)
 {
-       struct dentry *dentry, *tmp;
-       int ret;
-
        while (files->name && files->name[0]) {
-               ret = -ENOMEM;
-               dentry = d_alloc_name(dir, files->name);
+               int ret;
+               struct dentry *dentry = d_alloc_name(dir, files->name);
                if (!dentry)
-                       goto out;
+                       return -ENOMEM;
                ret = spufs_new_file(dir->d_sb, dentry, files->ops,
                                        files->mode & mode, files->size, ctx);
                if (ret)
-                       goto out;
+                       return ret;
                files++;
        }
        return 0;
-out:
-       /*
-        * remove all children from dir. dir->inode is not set so don't
-        * just simply use spufs_prune_dir() and panic afterwards :)
-        * dput() looks like it will do the right thing:
-        * - dec parent's ref counter
-        * - remove child from parent's child list
-        * - free child's inode if possible
-        * - free child
-        */
-       list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
-               dput(dentry);
-       }
-
-       shrink_dcache_parent(dir);
-       return ret;
 }
 
 static int spufs_dir_close(struct inode *inode, struct file *file)
@@ -269,10 +250,9 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
        struct inode *inode;
        struct spu_context *ctx;
 
-       ret = -ENOSPC;
        inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
        if (!inode)
-               goto out;
+               return -ENOSPC;
 
        if (dir->i_mode & S_ISGID) {
                inode->i_gid = dir->i_gid;
@@ -280,40 +260,38 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
        }
        ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
        SPUFS_I(inode)->i_ctx = ctx;
-       if (!ctx)
-               goto out_iput;
+       if (!ctx) {
+               iput(inode);
+               return -ENOSPC;
+       }
 
        ctx->flags = flags;
        inode->i_op = &simple_dir_inode_operations;
        inode->i_fop = &simple_dir_operations;
+
+       mutex_lock(&inode->i_mutex);
+
+       dget(dentry);
+       inc_nlink(dir);
+       inc_nlink(inode);
+
+       d_instantiate(dentry, inode);
+
        if (flags & SPU_CREATE_NOSCHED)
                ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
                                         mode, ctx);
        else
                ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx);
 
-       if (ret)
-               goto out_free_ctx;
-
-       if (spufs_get_sb_info(dir->i_sb)->debug)
+       if (!ret && spufs_get_sb_info(dir->i_sb)->debug)
                ret = spufs_fill_dir(dentry, spufs_dir_debug_contents,
                                mode, ctx);
 
        if (ret)
-               goto out_free_ctx;
+               spufs_rmdir(dir, dentry);
 
-       d_instantiate(dentry, inode);
-       dget(dentry);
-       inc_nlink(dir);
-       inc_nlink(dentry->d_inode);
-       goto out;
+       mutex_unlock(&inode->i_mutex);
 
-out_free_ctx:
-       spu_forget(ctx);
-       put_spu_context(ctx);
-out_iput:
-       iput(inode);
-out:
        return ret;
 }