git: d373650a5397 - stable/13 - Add support for mounting single files in nullfs

From: Doug Rabson <dfr_at_FreeBSD.org>
Date: Fri, 06 Jan 2023 09:46:33 UTC
The branch stable/13 has been updated by dfr:

URL: https://cgit.FreeBSD.org/src/commit/?id=d373650a53977de4aba1da737525bd37eeba72ca

commit d373650a53977de4aba1da737525bd37eeba72ca
Author:     Doug Rabson <dfr@FreeBSD.org>
AuthorDate: 2022-11-23 14:51:13 +0000
Commit:     Doug Rabson <dfr@FreeBSD.org>
CommitDate: 2023-01-06 07:57:57 +0000

    Add support for mounting single files in nullfs
    
    The main use-case for this is to support mounting config files and
    secrets into OCI containers. My current workaround copies the files into
    the container which is messy and risks secrets leaking into container
    images if the cleanup fails.
    
    This adds a VFCF flag to indicate whether the filesystem supports file
    mounts and allows fspath to be either a directory or a file if the flag
    is set.
    
    Test Plan:
    $ sudo mkdir -p /mnt
    $ sudo touch /mnt/foo
    $ sudo mount -t nullfs /COPYRIGHT /mnt/foo
    
    Reviewed by:    mjg, kib
    Tested by:      pho
    
    (cherry picked from commit 521fbb722c33663cf00a83bca70ad7cb790687b3)
---
 sys/fs/nullfs/null_vfsops.c | 13 ++++++++++++-
 sys/kern/vfs_lookup.c       |  4 ++--
 sys/kern/vfs_mount.c        | 32 ++++++++++++++++++++++++++------
 sys/sys/mount.h             |  1 +
 4 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/sys/fs/nullfs/null_vfsops.c b/sys/fs/nullfs/null_vfsops.c
index 0bb98072edf4..1a83973313b1 100644
--- a/sys/fs/nullfs/null_vfsops.c
+++ b/sys/fs/nullfs/null_vfsops.c
@@ -156,6 +156,17 @@ nullfs_mount(struct mount *mp)
 		}
 	}
 
+	/*
+	 * Lower vnode must be the same type as the covered vnode - we
+	 * don't allow mounting directories to files or vice versa.
+	 */
+	if ((lowerrootvp->v_type != VDIR && lowerrootvp->v_type != VREG) ||
+	    lowerrootvp->v_type != mp->mnt_vnodecovered->v_type) {
+		NULLFSDEBUG("nullfs_mount: target must be same type as fspath");
+		vput(lowerrootvp);
+		return (EINVAL);
+	}
+
 	xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
 	    M_NULLFSMNT, M_WAITOK | M_ZERO);
 
@@ -467,4 +478,4 @@ static struct vfsops null_vfsops = {
 	.vfs_unlink_lowervp =	nullfs_unlink_lowervp,
 };
 
-VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL);
+VFS_SET(null_vfsops, nullfs, VFCF_LOOPBACK | VFCF_JAIL | VFCF_FILEMOUNT);
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index bbceced68e50..3ded2180ac82 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -1154,8 +1154,8 @@ good:
 	 * Check to see if the vnode has been mounted on;
 	 * if so find the root of the mounted filesystem.
 	 */
-	while (dp->v_type == VDIR && (mp = dp->v_mountedhere) &&
-	       (cnp->cn_flags & NOCROSSMOUNT) == 0) {
+	while ((dp->v_type == VDIR || dp->v_type == VREG) &&
+	    (mp = dp->v_mountedhere) && (cnp->cn_flags & NOCROSSMOUNT) == 0) {
 		if (vfs_busy(mp, 0))
 			continue;
 		vput(dp);
diff --git a/sys/kern/vfs_mount.c b/sys/kern/vfs_mount.c
index d17e1c7fbbd7..038ace288729 100644
--- a/sys/kern/vfs_mount.c
+++ b/sys/kern/vfs_mount.c
@@ -962,8 +962,13 @@ vfs_domount_first(
 		error = priv_check_cred(td->td_ucred, PRIV_VFS_ADMIN);
 	if (error == 0)
 		error = vinvalbuf(vp, V_SAVE, 0, 0);
-	if (error == 0 && vp->v_type != VDIR)
-		error = ENOTDIR;
+	if (vfsp->vfc_flags & VFCF_FILEMOUNT) {
+		if (error == 0 && vp->v_type != VDIR && vp->v_type != VREG)
+			error = EINVAL;
+	} else {
+		if (error == 0 && vp->v_type != VDIR)
+			error = ENOTDIR;
+	}
 	if (error == 0 && (fsflags & MNT_EMPTYDIR) != 0)
 		error = vfs_emptydir(vp);
 	if (error == 0) {
@@ -1376,22 +1381,33 @@ vfs_domount(
 	/*
 	 * Get vnode to be covered or mount point's vnode in case of MNT_UPDATE.
 	 */
-	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1,
+	NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1 | WANTPARENT,
 	    UIO_SYSSPACE, fspath, td);
 	error = namei(&nd);
 	if (error != 0)
 		return (error);
-	NDFREE(&nd, NDF_ONLY_PNBUF);
 	vp = nd.ni_vp;
 	if ((fsflags & MNT_UPDATE) == 0) {
 		if ((vp->v_vflag & VV_ROOT) != 0 &&
 		    (fsflags & MNT_NOCOVER) != 0) {
 			vput(vp);
-			return (EBUSY);
+			error = EBUSY;
+			goto out;
 		}
 		pathbuf = malloc(MNAMELEN, M_TEMP, M_WAITOK);
 		strcpy(pathbuf, fspath);
-		error = vn_path_to_global_path(td, vp, pathbuf, MNAMELEN);
+		/*
+		 * Note: we allow any vnode type here. If the path sanity check
+		 * succeeds, the type will be validated in vfs_domount_first
+		 * above.
+		 */
+		if (vp->v_type == VDIR)
+			error = vn_path_to_global_path(td, vp, pathbuf,
+			    MNAMELEN);
+		else
+			error = vn_path_to_global_path_hardlink(td, vp,
+			    nd.ni_dvp, pathbuf, MNAMELEN,
+			    nd.ni_cnd.cn_nameptr, nd.ni_cnd.cn_namelen);
 		if (error == 0) {
 			error = vfs_domount_first(td, vfsp, pathbuf, vp,
 			    fsflags, optlist);
@@ -1400,6 +1416,10 @@ vfs_domount(
 	} else
 		error = vfs_domount_update(td, vp, fsflags, optlist);
 
+out:
+	NDFREE(&nd, NDF_ONLY_PNBUF);
+	vrele(nd.ni_dvp);
+
 	return (error);
 }
 
diff --git a/sys/sys/mount.h b/sys/sys/mount.h
index 0e7ecd646964..6e988a48922e 100644
--- a/sys/sys/mount.h
+++ b/sys/sys/mount.h
@@ -648,6 +648,7 @@ struct ovfsconf {
 #define	VFCF_DELEGADMIN	0x00800000	/* supports delegated administration */
 #define	VFCF_SBDRY	0x01000000	/* Stop at Boundary: defer stop requests
 					   to kernel->user (AST) transition */
+#define	VFCF_FILEMOUNT	0x02000000	/* allow mounting files */
 
 typedef uint32_t fsctlop_t;