diff options
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 24 |
1 files changed, 14 insertions, 10 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 2060bf06b906..e5217b213b44 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -239,18 +239,20 @@ int fuse_valid_type(int m) | |||
239 | * Add a directory inode to a dentry, ensuring that no other dentry | 239 | * Add a directory inode to a dentry, ensuring that no other dentry |
240 | * refers to this inode. Called with fc->inst_mutex. | 240 | * refers to this inode. Called with fc->inst_mutex. |
241 | */ | 241 | */ |
242 | static int fuse_d_add_directory(struct dentry *entry, struct inode *inode) | 242 | static struct dentry *fuse_d_add_directory(struct dentry *entry, |
243 | struct inode *inode) | ||
243 | { | 244 | { |
244 | struct dentry *alias = d_find_alias(inode); | 245 | struct dentry *alias = d_find_alias(inode); |
245 | if (alias) { | 246 | if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { |
246 | /* This tries to shrink the subtree below alias */ | 247 | /* This tries to shrink the subtree below alias */ |
247 | fuse_invalidate_entry(alias); | 248 | fuse_invalidate_entry(alias); |
248 | dput(alias); | 249 | dput(alias); |
249 | if (!list_empty(&inode->i_dentry)) | 250 | if (!list_empty(&inode->i_dentry)) |
250 | return -EBUSY; | 251 | return ERR_PTR(-EBUSY); |
252 | } else { | ||
253 | dput(alias); | ||
251 | } | 254 | } |
252 | d_add(entry, inode); | 255 | return d_splice_alias(inode, entry); |
253 | return 0; | ||
254 | } | 256 | } |
255 | 257 | ||
256 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | 258 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, |
@@ -259,6 +261,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
259 | int err; | 261 | int err; |
260 | struct fuse_entry_out outarg; | 262 | struct fuse_entry_out outarg; |
261 | struct inode *inode = NULL; | 263 | struct inode *inode = NULL; |
264 | struct dentry *newent; | ||
262 | struct fuse_conn *fc = get_fuse_conn(dir); | 265 | struct fuse_conn *fc = get_fuse_conn(dir); |
263 | struct fuse_req *req; | 266 | struct fuse_req *req; |
264 | struct fuse_req *forget_req; | 267 | struct fuse_req *forget_req; |
@@ -303,21 +306,22 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
303 | 306 | ||
304 | if (inode && S_ISDIR(inode->i_mode)) { | 307 | if (inode && S_ISDIR(inode->i_mode)) { |
305 | mutex_lock(&fc->inst_mutex); | 308 | mutex_lock(&fc->inst_mutex); |
306 | err = fuse_d_add_directory(entry, inode); | 309 | newent = fuse_d_add_directory(entry, inode); |
307 | mutex_unlock(&fc->inst_mutex); | 310 | mutex_unlock(&fc->inst_mutex); |
308 | if (err) { | 311 | if (IS_ERR(newent)) { |
309 | iput(inode); | 312 | iput(inode); |
310 | return ERR_PTR(err); | 313 | return newent; |
311 | } | 314 | } |
312 | } else | 315 | } else |
313 | d_add(entry, inode); | 316 | newent = d_splice_alias(inode, entry); |
314 | 317 | ||
318 | entry = newent ? newent : entry; | ||
315 | entry->d_op = &fuse_dentry_operations; | 319 | entry->d_op = &fuse_dentry_operations; |
316 | if (!err) | 320 | if (!err) |
317 | fuse_change_entry_timeout(entry, &outarg); | 321 | fuse_change_entry_timeout(entry, &outarg); |
318 | else | 322 | else |
319 | fuse_invalidate_entry_cache(entry); | 323 | fuse_invalidate_entry_cache(entry); |
320 | return NULL; | 324 | return newent; |
321 | } | 325 | } |
322 | 326 | ||
323 | /* | 327 | /* |