diff options
author | Mateusz Guzik <mjg@FreeBSD.org> | 2021-05-11 03:48:08 +0000 |
---|---|---|
committer | Mateusz Guzik <mjg@FreeBSD.org> | 2021-05-11 04:30:12 +0000 |
commit | 12288bd999ca3a493b8dc4cba109e5a8fa838c45 (patch) | |
tree | edd7d732ed7609b40f8912fe1d1321b9a6ac9dfa | |
parent | a436e665314276c8ab4cb3264f5f7cb0f29d7506 (diff) | |
download | src-12288bd999ca3a493b8dc4cba109e5a8fa838c45.tar.gz src-12288bd999ca3a493b8dc4cba109e5a8fa838c45.zip |
cache: fix lockless absolute symlink traversal to non-fp mounts
Said lookups would incorrectly fail with EOPNOTSUP.
Reported by: kib
-rw-r--r-- | sys/kern/vfs_cache.c | 14 |
1 files changed, 14 insertions, 0 deletions
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 607b1e67a40a..5b978511db17 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -3904,6 +3904,7 @@ struct cache_fpl { #endif }; +static bool cache_fplookup_mp_supported(struct mount *mp); static bool cache_fplookup_is_mp(struct cache_fpl *fpl); static int cache_fplookup_cross_mount(struct cache_fpl *fpl); static int cache_fplookup_partial_setup(struct cache_fpl *fpl); @@ -5142,6 +5143,19 @@ cache_fplookup_symlink(struct cache_fpl *fpl) if (seqc_in_modify(fpl->dvp_seqc)) { return (cache_fpl_aborted(fpl)); } + /* + * The main loop assumes that ->dvp points to a vnode belonging + * to a filesystem which can do lockless lookup, but the absolute + * symlink can be wandering off to one which does not. + */ + mp = atomic_load_ptr(&fpl->dvp->v_mount); + if (__predict_false(mp == NULL)) { + return (cache_fpl_aborted(fpl)); + } + if (!cache_fplookup_mp_supported(mp)) { + cache_fpl_checkpoint(fpl); + return (cache_fpl_partial(fpl)); + } } return (0); } |