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>
This commit is contained in:
parent
192cfd5877
commit
ac45d61357
@ -644,13 +644,12 @@ static int fuse_unlink(struct inode *dir, struct dentry *entry)
|
|||||||
fuse_put_request(fc, req);
|
fuse_put_request(fc, req);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
struct inode *inode = entry->d_inode;
|
struct inode *inode = entry->d_inode;
|
||||||
|
struct fuse_inode *fi = get_fuse_inode(inode);
|
||||||
|
|
||||||
/*
|
spin_lock(&fc->lock);
|
||||||
* Set nlink to zero so the inode can be cleared, if the inode
|
fi->attr_version = ++fc->attr_version;
|
||||||
* does have more links this will be discovered at the next
|
drop_nlink(inode);
|
||||||
* lookup/getattr.
|
spin_unlock(&fc->lock);
|
||||||
*/
|
|
||||||
clear_nlink(inode);
|
|
||||||
fuse_invalidate_attr(inode);
|
fuse_invalidate_attr(inode);
|
||||||
fuse_invalidate_attr(dir);
|
fuse_invalidate_attr(dir);
|
||||||
fuse_invalidate_entry_cache(entry);
|
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,
|
will reflect changes in the backing inode (link count,
|
||||||
etc.)
|
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);
|
fuse_invalidate_attr(inode);
|
||||||
|
} else if (err == -EINTR) {
|
||||||
|
fuse_invalidate_attr(inode);
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user