diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2012-03-05 09:48:11 -0500 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2012-03-05 09:48:11 -0500 |
commit | ac45d61357e86b9a0cf14e45e8e09dfb626970ef (patch) | |
tree | e560d3a8f26d1530780ed62f2a4bd3f16a20ac55 | |
parent | 192cfd58774b4d17b2fe8bdc77d89c2ef4e0591d (diff) |
fuse: fix nlink after unlink
Anand Avati reports that the following sequence of system calls fail on a fuse
filesystem:
create("filename") => 0
link("filename", "linkname") => 0
unlink("filename") => 0
link("linkname", "filename") => -ENOENT ### BUG ###
vfs_link() fails with ENOENT if i_nlink is zero, this is done to prevent
resurrecting already deleted files.
Fuse clears i_nlink on unlink even if there are other links pointing to the
file.
Reported-by: Anand Avati <avati@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
-rw-r--r-- | fs/fuse/dir.c | 22 |
1 files changed, 15 insertions, 7 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 206632887bb4..da379070fab5 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -644,13 +644,12 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry) | |||
644 | fuse_put_request(fc, req); | 644 | fuse_put_request(fc, req); |
645 | if (!err) { | 645 | if (!err) { |
646 | struct inode *inode = entry->d_inode; | 646 | struct inode *inode = entry->d_inode; |
647 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
647 | 648 | ||
648 | /* | 649 | spin_lock(&fc->lock); |
649 | * Set nlink to zero so the inode can be cleared, if the inode | 650 | fi->attr_version = ++fc->attr_version; |
650 | * does have more links this will be discovered at the next | 651 | drop_nlink(inode); |
651 | * lookup/getattr. | 652 | spin_unlock(&fc->lock); |
652 | */ | ||
653 | clear_nlink(inode); | ||
654 | fuse_invalidate_attr(inode); | 653 | fuse_invalidate_attr(inode); |
655 | fuse_invalidate_attr(dir); | 654 | fuse_invalidate_attr(dir); |
656 | fuse_invalidate_entry_cache(entry); | 655 | fuse_invalidate_entry_cache(entry); |
@@ -762,8 +761,17 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, | |||
762 | will reflect changes in the backing inode (link count, | 761 | will reflect changes in the backing inode (link count, |
763 | etc.) | 762 | etc.) |
764 | */ | 763 | */ |
765 | if (!err || err == -EINTR) | 764 | if (!err) { |
765 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
766 | |||
767 | spin_lock(&fc->lock); | ||
768 | fi->attr_version = ++fc->attr_version; | ||
769 | inc_nlink(inode); | ||
770 | spin_unlock(&fc->lock); | ||
766 | fuse_invalidate_attr(inode); | 771 | fuse_invalidate_attr(inode); |
772 | } else if (err == -EINTR) { | ||
773 | fuse_invalidate_attr(inode); | ||
774 | } | ||
767 | return err; | 775 | return err; |
768 | } | 776 | } |
769 | 777 | ||