aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMateusz Guzik <mjg@FreeBSD.org>2020-10-09 19:10:00 +0000
committerMateusz Guzik <mjg@FreeBSD.org>2020-10-09 19:10:00 +0000
commiteb88fed44616747f930b74c42259511823811fe1 (patch)
tree8a7ed88be7eb7cf909917906d204130cc48b95ef
parentb56d7f9663c0d7efc331b3014a0b6a8f293e867c (diff)
downloadsrc-eb88fed44616747f930b74c42259511823811fe1.tar.gz
src-eb88fed44616747f930b74c42259511823811fe1.zip
cache: fix vexec panic when racing against vgone
Use of dead_vnodeops would result in a panic instead of returning the intended EOPNOTSUPP error. While here make sure to abort, not just try to return a partial result. The former allows the regular lookup to restart from scratch, while the latter makes it stuck with an unusable vnode. Reported by: kevans
Notes
Notes: svn path=/head/; revision=366582
-rw-r--r--sys/fs/deadfs/dead_vnops.c1
-rw-r--r--sys/kern/vfs_cache.c34
2 files changed, 25 insertions, 10 deletions
diff --git a/sys/fs/deadfs/dead_vnops.c b/sys/fs/deadfs/dead_vnops.c
index 91a75dd59451..49f852791387 100644
--- a/sys/fs/deadfs/dead_vnops.c
+++ b/sys/fs/deadfs/dead_vnops.c
@@ -79,6 +79,7 @@ struct vop_vector dead_vnodeops = {
.vop_vptocnp = VOP_EBADF,
.vop_unset_text = dead_unset_text,
.vop_write = dead_write,
+ .vop_fplookup_vexec = VOP_EOPNOTSUPP,
};
VFS_VOP_VECTOR_REGISTER(dead_vnodeops);
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index 08a09c4512d3..ef1b057a23c0 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -4040,32 +4040,46 @@ cache_fplookup_parse_advance(struct cache_fpl *fpl)
}
}
+/*
+ * See the API contract for VOP_FPLOOKUP_VEXEC.
+ */
static int __noinline
cache_fplookup_failed_vexec(struct cache_fpl *fpl, int error)
{
+ struct vnode *dvp;
+ seqc_t dvp_seqc;
+
+ dvp = fpl->dvp;
+ dvp_seqc = fpl->dvp_seqc;
/*
* Hack: they may be looking up foo/bar, where foo is a
* regular file. In such a case we need to turn ENOTDIR,
* but we may happen to get here with a different error.
*/
- if (fpl->dvp->v_type != VDIR) {
+ if (dvp->v_type != VDIR) {
+ /*
+ * The check here is predominantly to catch
+ * EOPNOTSUPP from dead_vnodeops. If the vnode
+ * gets doomed past this point it is going to
+ * fail seqc verification.
+ */
+ if (VN_IS_DOOMED(dvp)) {
+ return (cache_fpl_aborted(fpl));
+ }
error = ENOTDIR;
}
switch (error) {
case EAGAIN:
- /*
- * Can happen when racing against vgone.
- * */
- case EOPNOTSUPP:
- cache_fpl_partial(fpl);
+ if (!vn_seqc_consistent(dvp, dvp_seqc)) {
+ error = cache_fpl_aborted(fpl);
+ } else {
+ cache_fpl_partial(fpl);
+ }
break;
default:
- /*
- * See the API contract for VOP_FPLOOKUP_VEXEC.
- */
- if (!vn_seqc_consistent(fpl->dvp, fpl->dvp_seqc)) {
+ if (!vn_seqc_consistent(dvp, dvp_seqc)) {
error = cache_fpl_aborted(fpl);
} else {
cache_fpl_smr_exit(fpl);