git: 2b5cc1e0e095 - stable/14 - vfs cache: Add vn_fullpath_jail(), factor out common code
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 10 Oct 2025 17:16:32 UTC
The branch stable/14 has been updated by olce:
URL: https://cgit.FreeBSD.org/src/commit/?id=2b5cc1e0e09592f36302d2cb5c6de1e159ea61ac
commit 2b5cc1e0e09592f36302d2cb5c6de1e159ea61ac
Author: Olivier Certner <olce@FreeBSD.org>
AuthorDate: 2025-09-26 10:18:12 +0000
Commit: Olivier Certner <olce@FreeBSD.org>
CommitDate: 2025-10-10 17:16:00 +0000
vfs cache: Add vn_fullpath_jail(), factor out common code
Introduce vn_fullpath_jail(), which returns a path to the passed vnode
relative to the current jail's root. It will be used by mac_do(4) in
a subsequent commit.
Factor out common code between the new variant and vn_fullpath(). While
here, rework the comments a bit.
Add vn_fullpath_jail() to the vn_fullpath.9 manual page. While here,
document all the existing public vn_fullpath*() functions.
Reviewed by: kib (except latest manual page changes)
MFC after: 3 days
Event: EuroBSDCon 2025
Sponsored by: The FreeBSD Foundation
Differential Revision: https://reviews.freebsd.org/D52757
(cherry picked from commit c5a813c9f486da49551c3be2e7700ca0cb0a489a)
---
share/man/man9/Makefile | 1 +
share/man/man9/vn_fullpath.9 | 120 +++++++++++++++++++++++++++++++++++--------
sys/kern/vfs_cache.c | 52 ++++++++++++++++---
sys/sys/vnode.h | 1 +
4 files changed, 144 insertions(+), 30 deletions(-)
diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index e424692fbdee..70e7f4f2259b 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -2444,6 +2444,7 @@ MLINKS+=vm_page_insert.9 vm_page_remove.9
MLINKS+=vm_page_wire.9 vm_page_unwire.9 \
vm_page_wire.9 vm_page_unwire_noq.9 \
vm_page_wire.9 vm_page_wire_mapped.9
+MLINKS+=vn_fullpath.9 vn_fullpath_jail.9
MLINKS+=VOP_ACCESS.9 VOP_ACCESSX.9
MLINKS+=VOP_ATTRIB.9 VOP_GETATTR.9 \
VOP_ATTRIB.9 VOP_SETATTR.9 \
diff --git a/share/man/man9/vn_fullpath.9 b/share/man/man9/vn_fullpath.9
index 116f519ec8aa..9815abc3c86c 100644
--- a/share/man/man9/vn_fullpath.9
+++ b/share/man/man9/vn_fullpath.9
@@ -1,6 +1,13 @@
+.\"-
+.\" SPDX-License-Identifier: BSD-2-Clause
.\"
.\" Copyright (c) 2003 Robert N. M. Watson.
.\" All rights reserved.
+.\" Copyright (c) 2025 The FreeBSD Foundation
+.\"
+.\" Portions of this documentation were written by Olivier Certner
+.\" <olce@FreeBSD.org> at Kumacom SARL under sponsorship from the FreeBSD
+.\" Foundation.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
@@ -25,7 +32,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
.\" DAMAGE.
.\"
-.Dd June 15, 2021
+.Dd September 29, 2025
.Dt VN_FULLPATH 9
.Os
.Sh NAME
@@ -35,23 +42,64 @@
.In sys/param.h
.In sys/vnode.h
.Ft int
-.Fo vn_fullpath
-.Fa "struct vnode *vp" "char **retbuf" "char **freebuf"
+.Fn vn_fullpath "struct vnode *vp" "char **retbuf" "char **freebuf"
+.Ft int
+.Fn vn_fullpath_jail "struct vnode *vp" "char **retbuf" "char **freebuf"
+.Ft int
+.Fn vn_fullpath_global "struct vnode *vp" "char **retbuf" "char **freebuf"
+.Ft int
+.Fo vn_fullpath_hardlink
+.Fa "struct vnode *vp" "struct vnode *dvp"
+.Fa "const char *hrdl_name" "size_t hrdl_name_length"
+.Fa "char **retbuf" "char **freebuf" "size_t *buflen"
.Fc
.Sh DESCRIPTION
The
+.Fn vn_fullpath ,
+.Fn vn_fullpath_jail ,
+.Fn vn_fullpath_global
+and
+.Fn vn_fullpath_hardlink
+functions make a
+.Dq best effort
+attempt at generating a string pathname for the passed vnode.
+They differ in which directory the returned path is relative to, except for
+.Fn vn_fullpath_hardlink
+which behaves like
.Fn vn_fullpath
-function makes a
-.Dq "best effort"
-attempt to generate a string pathname for
-the passed vnode; the resulting path, if any, will be relative to
-the root directory of the process associated with the passed thread pointer.
+in this respect and is described at the end.
+.Pp
The
.Fn vn_fullpath
-function
-is implemented by inspecting the VFS name cache, and attempting to
-reconstruct a path from the process root to the object.
+function returns a path relative to the root directory of the process associated
+with the passed thread pointer.
+That root directory is either the system's or the thread's process' containing
+jail's root directory, or some descendant directory of such established by some
+.Xr chroot 2
+call.
+The
+.Fn vn_fullpath_jail
+function returns a path relative to the passed thread's process' current jail's
+root, ignoring intervening
+.Xr chroot 2
+calls possibly made inside that jail.
+The
+.Fn vn_fullpath_global
+function returns the full path from the system root, ignoring all jail roots and
+.Xr chroot 2
+calls.
+.Pp
+Paths that the kernel intends to communicate to the passed user thread should
+exclusively be obtained via
+.Fn vn_fullpath .
+Paths obtained via
+.Fn vn_fullpath_jail
+or
+.Fn vn_fullpath_global
+are only useful for specific kernel checks or auditing purposes.
.Pp
+All these functions are implemented by inspecting the VFS name cache, and
+attempting to reconstruct a path from the process root to the object.
This process is necessarily unreliable for several reasons: intermediate
entries in the path may not be found in the cache; files may have more
than one name (hard links), not all file systems use the name cache
@@ -64,7 +112,7 @@ a vnode pointer value, or a device number and inode number.
Code consuming the results of this function should anticipate (and
properly handle) failure.
.Pp
-Its arguments are:
+These functions take the following arguments:
.Bl -tag -width ".Fa freebuf"
.It Fa vp
The vnode to search for.
@@ -72,17 +120,13 @@ No need to be locked by the caller.
.It Fa retbuf
Pointer to a
.Vt "char *"
-that
-.Fn vn_fullpath
-may (on success) point at a newly
-allocated buffer containing the resulting pathname.
+that may be set (on success) to point at a newly allocated buffer containing the
+resulting pathname.
.It Fa freebuf
Pointer to a
.Vt "char *"
-that
-.Fn vn_fullpath
-may (on success) point at a buffer
-to be freed, when the caller is done with
+that may be set (on success) to point at a buffer to be freed, when the caller
+is done with
.Fa retbuf .
.El
.Pp
@@ -110,11 +154,43 @@ and if so, invoke
.Xr free 9
with a pool type of
.Dv M_TEMP .
+.Pp
+The
+.Fn vn_fullpath_hardlink
+function is a convenience wrapper which automatically appends the hardlink name
+passed via arguments
+.Fa hrdl_name
+and
+.Fa hrdl_name_length
+to the result of calling
+.Fn vn_fullpath
+on the vnode's parent directory.
+It requires the results of a prior call to
+.Xr namei 9
+with flag
+.Dv WANTPARENT
+to be passed in the
+.Fa vp
+and
+.Fa dvp
+arguments.
+Argument
+.Fa buflen
+must point to a valid storage containing the size of the desired buffer, which
+will be reduced to
+.Dv MAXPATHLEN
+if in excess of it.
.Sh RETURN VALUES
If the vnode is successfully converted to a pathname, 0 is returned;
otherwise, an error number is returned.
.Sh SEE ALSO
.Xr free 9
.Sh AUTHORS
-This manual page was written by
-.An Robert Watson Aq Mt rwatson@FreeBSD.org .
+.An -nosplit
+This manual page was initally written by
+.An Robert Watson Aq Mt rwatson@FreeBSD.org
+to describe the
+.Fn vn_fullpath
+function.
+The descriptions of the other related functions were added by
+.An Olivier Certner Aq Mt olce@FreeBSD.org .
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index bb9c68e05afc..21eb122c1136 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -3224,12 +3224,10 @@ sys___realpathat(struct thread *td, struct __realpathat_args *uap)
uap->flags, UIO_USERSPACE));
}
-/*
- * Retrieve the full filesystem path that correspond to a vnode from the name
- * cache (if available)
- */
-int
-vn_fullpath(struct vnode *vp, char **retbuf, char **freebuf)
+static int
+vn_fullpath_up_to_pwd_vnode(struct vnode *vp,
+ struct vnode *(*const get_pwd_vnode)(const struct pwd *),
+ char **retbuf, char **freebuf)
{
struct pwd *pwd;
char *buf;
@@ -3243,11 +3241,13 @@ vn_fullpath(struct vnode *vp, char **retbuf, char **freebuf)
buf = malloc(buflen, M_TEMP, M_WAITOK);
vfs_smr_enter();
pwd = pwd_get_smr();
- error = vn_fullpath_any_smr(vp, pwd->pwd_rdir, buf, retbuf, &buflen, 0);
+ error = vn_fullpath_any_smr(vp, get_pwd_vnode(pwd), buf, retbuf,
+ &buflen, 0);
VFS_SMR_ASSERT_NOT_ENTERED();
if (error < 0) {
pwd = pwd_hold(curthread);
- error = vn_fullpath_any(vp, pwd->pwd_rdir, buf, retbuf, &buflen);
+ error = vn_fullpath_any(vp, get_pwd_vnode(pwd), buf, retbuf,
+ &buflen);
pwd_drop(pwd);
}
if (error == 0)
@@ -3257,6 +3257,42 @@ vn_fullpath(struct vnode *vp, char **retbuf, char **freebuf)
return (error);
}
+static inline struct vnode *
+get_rdir(const struct pwd *pwd)
+{
+ return (pwd->pwd_rdir);
+}
+
+/*
+ * Produce a filesystem path that starts from the current chroot directory and
+ * corresponds to the passed vnode, using the name cache (if available).
+ */
+int
+vn_fullpath(struct vnode *vp, char **retbuf, char **freebuf)
+{
+ return (vn_fullpath_up_to_pwd_vnode(vp, get_rdir, retbuf, freebuf));
+}
+
+static inline struct vnode *
+get_jdir(const struct pwd *pwd)
+{
+ return (pwd->pwd_jdir);
+}
+
+/*
+ * Produce a filesystem path that starts from the current jail's root directory
+ * and corresponds to the passed vnode, using the name cache (if available).
+ *
+ * This function allows to ignore chroots done inside a jail (or the host),
+ * allowing path checks to remain unaffected by privileged or unprivileged
+ * chroot calls.
+ */
+int
+vn_fullpath_jail(struct vnode *vp, char **retbuf, char **freebuf)
+{
+ return (vn_fullpath_up_to_pwd_vnode(vp, get_jdir, retbuf, freebuf));
+}
+
/*
* This function is similar to vn_fullpath, but it attempts to lookup the
* pathname relative to the global root mount point. This is required for the
diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h
index bc98c2155cb7..28eb49dd3eac 100644
--- a/sys/sys/vnode.h
+++ b/sys/sys/vnode.h
@@ -717,6 +717,7 @@ int speedup_syncer(void);
int vn_vptocnp(struct vnode **vp, char *buf, size_t *buflen);
int vn_getcwd(char *buf, char **retbuf, size_t *buflen);
int vn_fullpath(struct vnode *vp, char **retbuf, char **freebuf);
+int vn_fullpath_jail(struct vnode *vp, char **retbuf, char **freebuf);
int vn_fullpath_global(struct vnode *vp, char **retbuf, char **freebuf);
int vn_fullpath_hardlink(struct vnode *vp, struct vnode *dvp,
const char *hdrl_name, size_t hrdl_name_length, char **retbuf,