diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2012-03-05 15:48:11 +0100 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2012-03-05 15:48:11 +0100 |
commit | ac45d61357e86b9a0cf14e45e8e09dfb626970ef (patch) | |
tree | e560d3a8f26d1530780ed62f2a4bd3f16a20ac55 /fs | |
parent | 192cfd58774b4d17b2fe8bdc77d89c2ef4e0591d (diff) | |
download | linux-ac45d61357e86b9a0cf14e45e8e09dfb626970ef.tar.gz linux-ac45d61357e86b9a0cf14e45e8e09dfb626970ef.tar.bz2 linux-ac45d61357e86b9a0cf14e45e8e09dfb626970ef.zip |
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>
Diffstat (limited to 'fs')
-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) fuse_put_request(fc, req); if (!err) { struct inode *inode = entry->d_inode; + struct fuse_inode *fi = get_fuse_inode(inode); - /* - * Set nlink to zero so the inode can be cleared, if the inode - * does have more links this will be discovered at the next - * lookup/getattr. - */ - clear_nlink(inode); + spin_lock(&fc->lock); + fi->attr_version = ++fc->attr_version; + drop_nlink(inode); + spin_unlock(&fc->lock); fuse_invalidate_attr(inode); fuse_invalidate_attr(dir); fuse_invalidate_entry_cache(entry); @@ -762,8 +761,17 @@ static int fuse_link(struct dentry *entry, struct inode *newdir, will reflect changes in the backing inode (link count, etc.) */ - if (!err || err == -EINTR) + if (!err) { + struct fuse_inode *fi = get_fuse_inode(inode); + + spin_lock(&fc->lock); + fi->attr_version = ++fc->attr_version; + inc_nlink(inode); + spin_unlock(&fc->lock); fuse_invalidate_attr(inode); + } else if (err == -EINTR) { + fuse_invalidate_attr(inode); + } return err; } |