FUSE: Respect userspace FS "do-not-cache" of path components

The FUSE protocol demands that kernel implementations cache user filesystem
path components (lookup/cnp data) for a maximum period of time in the range
of [0, ULONG_MAX] seconds.  In practice, typical requests are for 0, 1, or
10 seconds; or "a long time" to represent indefinite caching.

Historically, FreeBSD FUSE has ignored this client directive entirely.  This
works fine for local-only filesystems, but causes consistency issues with
multi-writer network filesystems.

For now, respect 0 second cache TTLs and do not cache such metadata.
Non-zero metadata caching TTLs in the range [0.000000001, ULONG_MAX] seconds
are still cached indefinitely, because it is unclear how a userspace
filesystem could do anything sensible with those semantics even if
implemented.

Pass fuse_entry_out to fuse_vnode_get when available and only cache lookup
if the user filesystem did not set a zero second TTL.

PR:		230258 (inspired by; does not fix)
This commit is contained in:
Conrad Meyer 2019-02-15 22:50:31 +00:00
parent 78a7722fbc
commit 09176f096b
5 changed files with 17 additions and 24 deletions

@ -471,7 +471,7 @@ fuse_internal_newentry_core(struct vnode *dvp,
if ((err = fuse_internal_checkentry(feo, vtyp))) {
return err;
}
err = fuse_vnode_get(mp, feo->nodeid, dvp, vpp, cnp, vtyp);
err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, vtyp);
if (err) {
fuse_internal_forget_send(mp, cnp->cn_thread, cnp->cn_cred,
feo->nodeid, 1);

@ -241,6 +241,7 @@ fuse_vnode_alloc(struct mount *mp,
int
fuse_vnode_get(struct mount *mp,
struct fuse_entry_out *feo,
uint64_t nodeid,
struct vnode *dvp,
struct vnode **vpp,
@ -261,7 +262,9 @@ fuse_vnode_get(struct mount *mp,
MPASS(!(cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.'));
fuse_vnode_setparent(*vpp, dvp);
}
if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0) {
if (dvp != NULL && cnp != NULL && (cnp->cn_flags & MAKEENTRY) != 0 &&
feo != NULL &&
(feo->entry_valid != 0 || feo->entry_valid_nsec != 0)) {
ASSERT_VOP_LOCKED(*vpp, "fuse_vnode_get");
ASSERT_VOP_LOCKED(dvp, "fuse_vnode_get");
cache_enter(dvp, *vpp, cnp);

@ -117,6 +117,7 @@ fuse_vnode_setparent(struct vnode *vp, struct vnode *dvp)
void fuse_vnode_destroy(struct vnode *vp);
int fuse_vnode_get(struct mount *mp,
struct fuse_entry_out *feo,
uint64_t nodeid,
struct vnode *dvp,
struct vnode **vpp,

@ -444,7 +444,8 @@ fuse_vfsop_root(struct mount *mp, int lkflags, struct vnode **vpp)
if (err == 0)
*vpp = data->vroot;
} else {
err = fuse_vnode_get(mp, FUSE_ROOT_ID, NULL, vpp, NULL, VDIR);
err = fuse_vnode_get(mp, NULL, FUSE_ROOT_ID, NULL, vpp, NULL,
VDIR);
if (err == 0) {
FUSE_LOCK();
MPASS(data->vroot == NULL || data->vroot == *vpp);

@ -384,7 +384,7 @@ fuse_vnop_create(struct vop_create_args *ap)
if ((err = fuse_internal_checkentry(feo, VREG))) {
goto out;
}
err = fuse_vnode_get(mp, feo->nodeid, dvp, vpp, cnp, VREG);
err = fuse_vnode_get(mp, feo, feo->nodeid, dvp, vpp, cnp, VREG);
if (err) {
struct fuse_release_in *fri;
uint64_t nodeid = feo->nodeid;
@ -857,8 +857,8 @@ calldaemon:
vref(dvp);
*vpp = dvp;
} else {
err = fuse_vnode_get(dvp->v_mount, nid, dvp,
&vp, cnp, IFTOVT(fattr->mode));
err = fuse_vnode_get(dvp->v_mount, feo, nid,
dvp, &vp, cnp, IFTOVT(fattr->mode));
if (err)
goto out;
*vpp = vp;
@ -893,12 +893,8 @@ calldaemon:
err = EISDIR;
goto out;
}
err = fuse_vnode_get(vnode_mount(dvp),
nid,
dvp,
&vp,
cnp,
IFTOVT(fattr->mode));
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
&vp, cnp, IFTOVT(fattr->mode));
if (err) {
goto out;
}
@ -936,12 +932,8 @@ calldaemon:
}
}
VOP_UNLOCK(dvp, 0);
err = fuse_vnode_get(vnode_mount(dvp),
nid,
NULL,
&vp,
cnp,
IFTOVT(fattr->mode));
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, NULL,
&vp, cnp, IFTOVT(fattr->mode));
vfs_unbusy(mp);
vn_lock(dvp, ltype | LK_RETRY);
if ((dvp->v_iflag & VI_DOOMED) != 0) {
@ -956,12 +948,8 @@ calldaemon:
vref(dvp);
*vpp = dvp;
} else {
err = fuse_vnode_get(vnode_mount(dvp),
nid,
dvp,
&vp,
cnp,
IFTOVT(fattr->mode));
err = fuse_vnode_get(vnode_mount(dvp), feo, nid, dvp,
&vp, cnp, IFTOVT(fattr->mode));
if (err) {
goto out;
}