git: 9d9a2ccb9761 - stable/13 - Allow realpath to work for file mounts
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 06 Jan 2023 09:46:34 UTC
The branch stable/13 has been updated by dfr:
URL: https://cgit.FreeBSD.org/src/commit/?id=9d9a2ccb976157dfbf1bc9d18ec3c522e0fb65f5
commit 9d9a2ccb976157dfbf1bc9d18ec3c522e0fb65f5
Author: Doug Rabson <dfr@FreeBSD.org>
AuthorDate: 2022-12-04 15:53:07 +0000
Commit: Doug Rabson <dfr@FreeBSD.org>
CommitDate: 2023-01-06 07:57:57 +0000
Allow realpath to work for file mounts
For file mounts, the directory vnode is not available from namei and this
prevents the use of vn_fullpath_hardlink. In this case, we can use the
vnode which was covered by the file mount with vn_fullpath.
This also disallows file mounts over files with link counts greater than
one to ensure a deterministic path to the mount point.
Reviewed by: mjg, kib
Tested by: pho
(cherry picked from commit a1d74b2dab78d56582126b4944b435d00747f601)
---
sys/kern/vfs_cache.c | 28 ++++++++++++++++++++++++++--
sys/kern/vfs_mount.c | 5 +++++
2 files changed, 31 insertions(+), 2 deletions(-)
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index b422b30da3e2..e87f4a6cda1a 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -3133,12 +3133,36 @@ kern___realpathat(struct thread *td, int fd, const char *path, char *buf,
pathseg, path, fd, &cap_fstat_rights, td);
if ((error = namei(&nd)) != 0)
return (error);
- error = vn_fullpath_hardlink(nd.ni_vp, nd.ni_dvp, nd.ni_cnd.cn_nameptr,
- nd.ni_cnd.cn_namelen, &retbuf, &freebuf, &size);
+
+ if (nd.ni_vp->v_type == VREG && nd.ni_dvp->v_type != VDIR &&
+ (nd.ni_vp->v_vflag & VV_ROOT) != 0) {
+ /*
+ * This happens if vp is a file mount. The call to
+ * vn_fullpath_hardlink can panic if path resolution can't be
+ * handled without the directory.
+ *
+ * To resolve this, we find the vnode which was mounted on -
+ * this should have a unique global path since we disallow
+ * mounting on linked files.
+ */
+ struct vnode *covered_vp;
+ error = vn_lock(nd.ni_vp, LK_SHARED);
+ if (error != 0)
+ goto out;
+ covered_vp = nd.ni_vp->v_mount->mnt_vnodecovered;
+ vref(covered_vp);
+ VOP_UNLOCK(nd.ni_vp);
+ error = vn_fullpath(covered_vp, &retbuf, &freebuf);
+ vrele(covered_vp);
+ } else {
+ error = vn_fullpath_hardlink(nd.ni_vp, nd.ni_dvp, nd.ni_cnd.cn_nameptr,
+ nd.ni_cnd.cn_namelen, &retbuf, &freebuf, &size);
+ }
if (error == 0) {
error = copyout(retbuf, buf, size);
free(freebuf, M_TEMP);
}
+out:
NDFREE(&nd, 0);
return (error);
}
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index 038ace288729..e6bb6036a5a0 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -965,6 +965,11 @@ vfs_domount_first(
if (vfsp->vfc_flags & VFCF_FILEMOUNT) {
if (error == 0 && vp->v_type != VDIR && vp->v_type != VREG)
error = EINVAL;
+ /*
+ * For file mounts, ensure that there is only one hardlink to the file.
+ */
+ if (error == 0 && vp->v_type == VREG && va.va_nlink != 1)
+ error = EINVAL;
} else {
if (error == 0 && vp->v_type != VDIR)
error = ENOTDIR;