diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2013-09-05 05:44:42 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-09-05 16:23:53 -0400 |
commit | 5835f3390e35ae3da9add646a2ca2cc30b47370e (patch) | |
tree | 01bf2c9ddb37a08b4db112281b77fc1a97f7c537 /fs/fuse/dir.c | |
parent | 6497d160f6abf8d1082ff1a4efd841118cb1fddd (diff) |
fuse: use d_materialise_unique()
Use d_materialise_unique() instead of d_splice_alias(). This allows dentry
subtrees to be moved to a new place if there moved, even if something is
referencing a dentry in the subtree (open fd, cwd, etc..).
This will also allow us to drop a subtree if it is found to be replaced by
something else. In this case the disconnected subtree can later be
reconnected to its new location.
d_materialise_unique() ensures that a directory entry only ever has one
alias. We keep fc->inst_mutex around the calls for d_materialise_unique()
on directories to prevent a race with mkdir "stealing" the inode.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 69 |
1 files changed, 26 insertions, 43 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 72a5d5b04494..131d14b604ef 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -267,26 +267,6 @@ int fuse_valid_type(int m) | |||
267 | S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); | 267 | S_ISBLK(m) || S_ISFIFO(m) || S_ISSOCK(m); |
268 | } | 268 | } |
269 | 269 | ||
270 | /* | ||
271 | * Add a directory inode to a dentry, ensuring that no other dentry | ||
272 | * refers to this inode. Called with fc->inst_mutex. | ||
273 | */ | ||
274 | static struct dentry *fuse_d_add_directory(struct dentry *entry, | ||
275 | struct inode *inode) | ||
276 | { | ||
277 | struct dentry *alias = d_find_alias(inode); | ||
278 | if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { | ||
279 | /* This tries to shrink the subtree below alias */ | ||
280 | fuse_invalidate_entry(alias); | ||
281 | dput(alias); | ||
282 | if (!hlist_empty(&inode->i_dentry)) | ||
283 | return ERR_PTR(-EBUSY); | ||
284 | } else { | ||
285 | dput(alias); | ||
286 | } | ||
287 | return d_splice_alias(inode, entry); | ||
288 | } | ||
289 | |||
290 | int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, | 270 | int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, |
291 | struct fuse_entry_out *outarg, struct inode **inode) | 271 | struct fuse_entry_out *outarg, struct inode **inode) |
292 | { | 272 | { |
@@ -345,6 +325,24 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, struct qstr *name, | |||
345 | return err; | 325 | return err; |
346 | } | 326 | } |
347 | 327 | ||
328 | static struct dentry *fuse_materialise_dentry(struct dentry *dentry, | ||
329 | struct inode *inode) | ||
330 | { | ||
331 | struct dentry *newent; | ||
332 | |||
333 | if (inode && S_ISDIR(inode->i_mode)) { | ||
334 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
335 | |||
336 | mutex_lock(&fc->inst_mutex); | ||
337 | newent = d_materialise_unique(dentry, inode); | ||
338 | mutex_unlock(&fc->inst_mutex); | ||
339 | } else { | ||
340 | newent = d_materialise_unique(dentry, inode); | ||
341 | } | ||
342 | |||
343 | return newent; | ||
344 | } | ||
345 | |||
348 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | 346 | static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, |
349 | unsigned int flags) | 347 | unsigned int flags) |
350 | { | 348 | { |
@@ -352,7 +350,6 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
352 | struct fuse_entry_out outarg; | 350 | struct fuse_entry_out outarg; |
353 | struct inode *inode; | 351 | struct inode *inode; |
354 | struct dentry *newent; | 352 | struct dentry *newent; |
355 | struct fuse_conn *fc = get_fuse_conn(dir); | ||
356 | bool outarg_valid = true; | 353 | bool outarg_valid = true; |
357 | 354 | ||
358 | err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, | 355 | err = fuse_lookup_name(dir->i_sb, get_node_id(dir), &entry->d_name, |
@@ -368,16 +365,10 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, | |||
368 | if (inode && get_node_id(inode) == FUSE_ROOT_ID) | 365 | if (inode && get_node_id(inode) == FUSE_ROOT_ID) |
369 | goto out_iput; | 366 | goto out_iput; |
370 | 367 | ||
371 | if (inode && S_ISDIR(inode->i_mode)) { | 368 | newent = fuse_materialise_dentry(entry, inode); |
372 | mutex_lock(&fc->inst_mutex); | 369 | err = PTR_ERR(newent); |
373 | newent = fuse_d_add_directory(entry, inode); | 370 | if (IS_ERR(newent)) |
374 | mutex_unlock(&fc->inst_mutex); | 371 | goto out_err; |
375 | err = PTR_ERR(newent); | ||
376 | if (IS_ERR(newent)) | ||
377 | goto out_iput; | ||
378 | } else { | ||
379 | newent = d_splice_alias(inode, entry); | ||
380 | } | ||
381 | 372 | ||
382 | entry = newent ? newent : entry; | 373 | entry = newent ? newent : entry; |
383 | if (outarg_valid) | 374 | if (outarg_valid) |
@@ -1275,18 +1266,10 @@ static int fuse_direntplus_link(struct file *file, | |||
1275 | if (!inode) | 1266 | if (!inode) |
1276 | goto out; | 1267 | goto out; |
1277 | 1268 | ||
1278 | if (S_ISDIR(inode->i_mode)) { | 1269 | alias = fuse_materialise_dentry(dentry, inode); |
1279 | mutex_lock(&fc->inst_mutex); | 1270 | err = PTR_ERR(alias); |
1280 | alias = fuse_d_add_directory(dentry, inode); | 1271 | if (IS_ERR(alias)) |
1281 | mutex_unlock(&fc->inst_mutex); | 1272 | goto out; |
1282 | err = PTR_ERR(alias); | ||
1283 | if (IS_ERR(alias)) { | ||
1284 | iput(inode); | ||
1285 | goto out; | ||
1286 | } | ||
1287 | } else { | ||
1288 | alias = d_splice_alias(inode, dentry); | ||
1289 | } | ||
1290 | 1273 | ||
1291 | if (alias) { | 1274 | if (alias) { |
1292 | dput(dentry); | 1275 | dput(dentry); |