From: NeilBrown <[email protected]>

As afs supports the fhandle interfaces there is a theoretical possibility
that the inode created for mkdir could be found by open_by_handle_at()
and given a dentry before d_instantiate() is called.  This would result
in two dentries for the one directory inode, which is not permitted.

So this patch changes afs_mkdir() to use d_splice_alias() and to
return the alternate dentry from ->mkdir() if appropriate.

Signed-off-by: NeilBrown <[email protected]>
---
 fs/afs/dir.c      | 14 ++++++++++----
 fs/afs/internal.h |  1 +
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/fs/afs/dir.c b/fs/afs/dir.c
index 9c57614feccf..1e472768e1f1 100644
--- a/fs/afs/dir.c
+++ b/fs/afs/dir.c
@@ -1248,7 +1248,7 @@ void afs_check_for_remote_deletion(struct afs_operation 
*op)
 /*
  * Create a new inode for create/mkdir/symlink
  */
-static void afs_vnode_new_inode(struct afs_operation *op)
+static struct dentry *afs_vnode_new_inode(struct afs_operation *op)
 {
        struct afs_vnode_param *dvp = &op->file[0];
        struct afs_vnode_param *vp = &op->file[1];
@@ -1265,7 +1265,7 @@ static void afs_vnode_new_inode(struct afs_operation *op)
                 * the new directory on the server.
                 */
                afs_op_accumulate_error(op, PTR_ERR(inode), 0);
-               return;
+               return NULL;
        }
 
        vnode = AFS_FS_I(inode);
@@ -1276,7 +1276,7 @@ static void afs_vnode_new_inode(struct afs_operation *op)
                afs_init_new_symlink(vnode, op);
        if (!afs_op_error(op))
                afs_cache_permit(vnode, op->key, vnode->cb_break, &vp->scb);
-       d_instantiate(op->dentry, inode);
+       return d_splice_alias(inode, op->dentry);
 }
 
 static void afs_create_success(struct afs_operation *op)
@@ -1285,7 +1285,7 @@ static void afs_create_success(struct afs_operation *op)
        op->ctime = op->file[0].scb.status.mtime_client;
        afs_vnode_commit_status(op, &op->file[0]);
        afs_update_dentry_version(op, &op->file[0], op->dentry);
-       afs_vnode_new_inode(op);
+       op->create.ret = afs_vnode_new_inode(op);
 }
 
 static void afs_create_edit_dir(struct afs_operation *op)
@@ -1356,6 +1356,12 @@ static struct dentry *afs_mkdir(struct mnt_idmap *idmap, 
struct inode *dir,
        op->ops         = &afs_mkdir_operation;
        ret = afs_do_sync_operation(op);
        afs_dir_unuse_cookie(dvnode, ret);
+       if (op->create.ret) {
+               /* Alternate dentry */
+               if (ret == 0)
+                       return op->create.ret;
+               dput(op->create.ret);
+       }
        return ERR_PTR(ret);
 }
 
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
index f2898ce9c0e6..ce94f10a14c0 100644
--- a/fs/afs/internal.h
+++ b/fs/afs/internal.h
@@ -889,6 +889,7 @@ struct afs_operation {
                        int     reason;         /* enum afs_edit_dir_reason */
                        mode_t  mode;
                        const char *symlink;
+                       struct dentry *ret;
                } create;
                struct {
                        struct dentry   *unblock;
-- 
2.50.0.107.gf914562f5916.dirty


Reply via email to