aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/fuse
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2021-12-06 05:43:17 +0000
committerAlan Somers <asomers@FreeBSD.org>2022-01-03 02:36:38 +0000
commit139764c4613cde14c97ff8dd8007eb0f27f5fb9f (patch)
tree1f4f5b55b35af2770d02894678060b89fee1c30f /sys/fs/fuse
parent2472f847281a8827acfdab88141b63228381ab6e (diff)
downloadsrc-139764c4613cde14c97ff8dd8007eb0f27f5fb9f.tar.gz
src-139764c4613cde14c97ff8dd8007eb0f27f5fb9f.zip
fusefs: correctly handle an inode that changes file types
Correctly handle the situation where a FUSE server unlinks a file, then creates a new file of a different type but with the same inode number. Previously fuse_vnop_lookup in this situation would return EAGAIN. But since it didn't call vgone(), the vnode couldn't be reused right away. Fix this by immediately calling vgone() and reallocating a new vnode. This problem can occur in three code paths, during VOP_LOOKUP, VOP_SETATTR, or following FUSE_GETATTR, which usually happens during VOP_GETATTR but can occur during other vops, too. Note that the correct response actually doesn't depend on whether the entry cache has expired. In fact, during VOP_LOOKUP, we can't even tell. Either it has expired already, or else the vnode got reclaimed by vnlru. Also, correct the error code during the VOP_SETATTR path. PR: 258022 Reported by: chogata@moosefs.pro Reviewed by: pfg Differential Revision: https://reviews.freebsd.org/D33283 (cherry picked from commit 25927e068fcbcac0a5111a881de723bd984b04b3)
Diffstat (limited to 'sys/fs/fuse')
-rw-r--r--sys/fs/fuse/fuse_internal.c9
-rw-r--r--sys/fs/fuse/fuse_node.c25
2 files changed, 20 insertions, 14 deletions
diff --git a/sys/fs/fuse/fuse_internal.c b/sys/fs/fuse/fuse_internal.c
index 403116593bd9..b9df64921fc8 100644
--- a/sys/fs/fuse/fuse_internal.c
+++ b/sys/fs/fuse/fuse_internal.c
@@ -1256,12 +1256,15 @@ int fuse_internal_setattr(struct vnode *vp, struct vattr *vap,
* STALE vnode, ditch
*
* The vnode has changed its type "behind our back".
+ * This probably means that the file got deleted and
+ * recreated on the server, with the same inode.
* There's nothing really we can do, so let us just
- * force an internal revocation and tell the caller to
- * try again, if interested.
+ * return ENOENT. After all, the entry must not have
+ * existed in the recent past. If the user tries
+ * again, it will work.
*/
fuse_internal_vnode_disappear(vp);
- err = EAGAIN;
+ err = ENOENT;
}
}
if (err == 0) {
diff --git a/sys/fs/fuse/fuse_node.c b/sys/fs/fuse/fuse_node.c
index f74de2faa702..b5f46daf025d 100644
--- a/sys/fs/fuse/fuse_node.c
+++ b/sys/fs/fuse/fuse_node.c
@@ -212,24 +212,27 @@ fuse_vnode_alloc(struct mount *mp,
return (err);
if (*vpp) {
- if ((*vpp)->v_type != vtyp) {
+ if ((*vpp)->v_type == vtyp) {
+ /* Reuse a vnode that hasn't yet been reclaimed */
+ MPASS((*vpp)->v_data != NULL);
+ MPASS(VTOFUD(*vpp)->nid == nodeid);
+ SDT_PROBE2(fusefs, , node, trace, 1,
+ "vnode taken from hash");
+ return (0);
+ } else {
/*
- * STALE vnode! This probably indicates a buggy
- * server, but it could also be the result of a race
- * between FUSE_LOOKUP and another client's
- * FUSE_UNLINK/FUSE_CREATE
+ * The inode changed types! If we get here, we can't
+ * tell whether the inode's entry cache had expired
+ * yet. So this could be the result of a buggy server,
+ * but more likely the server just reused an inode
+ * number following an entry cache expiration.
*/
SDT_PROBE3(fusefs, , node, stale_vnode, *vpp, vtyp,
nodeid);
fuse_internal_vnode_disappear(*vpp);
+ vgone(*vpp);
lockmgr((*vpp)->v_vnlock, LK_RELEASE, NULL);
- *vpp = NULL;
- return (EAGAIN);
}
- MPASS((*vpp)->v_data != NULL);
- MPASS(VTOFUD(*vpp)->nid == nodeid);
- SDT_PROBE2(fusefs, , node, trace, 1, "vnode taken from hash");
- return (0);
}
fvdat = malloc(sizeof(*fvdat), M_FUSEVN, M_WAITOK | M_ZERO);
switch (vtyp) {