diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 40 |
1 files changed, 35 insertions, 5 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c3eb2c4..b7989f2 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -342,6 +342,24 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, return err; } +static struct dentry *fuse_materialise_dentry(struct dentry *dentry, + struct inode *inode) +{ + struct dentry *newent; + + if (inode && S_ISDIR(inode->i_mode)) { + struct fuse_conn *fc = get_fuse_conn(inode); + + mutex_lock(&fc->inst_mutex); + newent = d_materialise_unique(dentry, inode); + mutex_unlock(&fc->inst_mutex); + } else { + newent = d_materialise_unique(dentry, inode); + } + + return newent; +} + static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, unsigned int flags) { @@ -364,7 +382,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, if (inode && get_node_id(inode) == FUSE_ROOT_ID) goto out_iput; - newent = d_materialise_unique(entry, inode); + newent = fuse_materialise_dentry(entry, inode); err = PTR_ERR(newent); if (IS_ERR(newent)) goto out_err; @@ -583,9 +601,21 @@ static int create_new_entry(struct fuse_conn *fc, struct fuse_req *req, } kfree(forget); - err = d_instantiate_no_diralias(entry, inode); - if (err) - return err; + if (S_ISDIR(inode->i_mode)) { + struct dentry *alias; + mutex_lock(&fc->inst_mutex); + alias = d_find_alias(inode); + if (alias) { + /* New directory must have moved since mkdir */ + mutex_unlock(&fc->inst_mutex); + dput(alias); + iput(inode); + return -EBUSY; + } + d_instantiate(entry, inode); + mutex_unlock(&fc->inst_mutex); + } else + d_instantiate(entry, inode); fuse_change_entry_timeout(entry, &outarg); fuse_invalidate_attr(dir); @@ -1254,7 +1284,7 @@ static int fuse_direntplus_link(struct file *file, if (!inode) goto out; - alias = d_materialise_unique(dentry, inode); + alias = fuse_materialise_dentry(dentry, inode); err = PTR_ERR(alias); if (IS_ERR(alias)) goto out; |