aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cddl/contrib/opensolaris/cmd/dtrace/dtrace.114
-rw-r--r--etc/mtree/BSD.tests.dist2
-rw-r--r--sbin/ipf/libipf/interror.c6
-rw-r--r--share/man/man4/Makefile6
-rw-r--r--share/man/man4/dtrace_callout_execute.468
-rw-r--r--share/man/man4/dtrace_vfs.497
-rw-r--r--share/man/man4/geom_zero.4174
-rw-r--r--share/man/man4/zero.43
-rw-r--r--share/man/man7/ports.76
-rw-r--r--share/man/man9/VFS.93
-rw-r--r--share/man/man9/callout.94
-rw-r--r--share/mk/src.opts.mk6
-rw-r--r--sys/fs/cd9660/cd9660_lookup.c2
-rw-r--r--sys/fs/cd9660/cd9660_vnops.c4
-rw-r--r--sys/fs/fuse/fuse_vnops.c11
-rw-r--r--sys/fs/nullfs/null_vnops.c30
-rw-r--r--sys/fs/smbfs/smbfs_vnops.c2
-rw-r--r--sys/fs/unionfs/union_vnops.c22
-rw-r--r--sys/kern/kern_descrip.c57
-rw-r--r--sys/kern/sys_procdesc.c5
-rw-r--r--sys/kern/uipc_mqueue.c8
-rw-r--r--sys/kern/uipc_sem.c6
-rw-r--r--sys/kern/uipc_syscalls.c4
-rw-r--r--sys/kern/vfs_cache.c19
-rw-r--r--sys/kern/vfs_lookup.c46
-rw-r--r--sys/kern/vfs_syscalls.c5
-rw-r--r--sys/kern/vfs_vnops.c22
-rw-r--r--sys/netpfil/ipfilter/netinet/ip_htable.c43
-rw-r--r--sys/netpfil/ipfilter/netinet/ip_htable.h2
-rw-r--r--sys/sys/file.h21
-rw-r--r--sys/sys/filedesc.h19
-rw-r--r--sys/sys/namei.h9
-rw-r--r--sys/sys/procdesc.h6
-rw-r--r--sys/sys/socketvar.h2
-rw-r--r--tests/sys/cddl/Makefile2
-rw-r--r--tests/sys/kern/Makefile5
-rw-r--r--tests/sys/kern/jail_lookup_root.c133
-rw-r--r--tools/build/options/WITHOUT_ZFS_TESTS1
-rw-r--r--usr.bin/truss/truss.15
-rw-r--r--usr.sbin/quot/Makefile5
-rw-r--r--usr.sbin/quot/quot.83
-rw-r--r--usr.sbin/quot/quot.c58
-rw-r--r--usr.sbin/quot/tests/Makefile4
-rw-r--r--usr.sbin/quot/tests/quot_test.sh120
44 files changed, 911 insertions, 159 deletions
diff --git a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1 b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1
index 34c36920ac97..7c9a4b1e7cf6 100644
--- a/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1
+++ b/cddl/contrib/opensolaris/cmd/dtrace/dtrace.1
@@ -20,7 +20,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd July 30, 2025
+.Dd November 8, 2025
.Dt DTRACE 1
.Os
.Sh NAME
@@ -528,7 +528,7 @@ to disallow the possibility of enabling destructive actions system-wide at any p
Any attempts to enable destructive actions will cause
.Nm
to exit with a runtime error.
-.It Fl x Ar arg Op Ns = Ns value
+.It Fl x Ar arg Ns Op = Ns Ar value
Enable or modify a DTrace runtime option or D compiler option.
Boolean options are enabled by specifying their name.
Options with values are set by separating the option name and value with an
@@ -836,6 +836,14 @@ command line to define a set of macro variables and so forth).
The additional arguments can be used in D programs specified using the
.Fl s
option or on the command line.
+.Sh ENVIRONMENT
+.Bl -tag -width 'DTRACE_DEBUG'
+.It Ev DTRACE_DEBUG
+When defined,
+.Nm
+will output debug log messages to
+.Xr stderr 4 .
+.El
.Sh FILES
.Bl -tag -width /boot/dtrace.dof -compact
.It Pa /boot/dtrace.dof
@@ -875,6 +883,7 @@ in
.Sh SEE ALSO
.Xr cpp 1 ,
.Xr dtrace_audit 4 ,
+.Xr dtrace_callout_execute 4 ,
.Xr dtrace_fbt 4 ,
.Xr dtrace_io 4 ,
.Xr dtrace_ip 4 ,
@@ -885,6 +894,7 @@ in
.Xr dtrace_tcp 4 ,
.Xr dtrace_udp 4 ,
.Xr dtrace_udplite 4 ,
+.Xr dtrace_vfs 4 ,
.Xr elf 5 ,
.Xr SDT 9
.Rs
diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index bbd0862c226b..5ae161ed5cd2 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -1177,6 +1177,8 @@
..
pw
..
+ quot
+ ..
rpcbind
..
sa
diff --git a/sbin/ipf/libipf/interror.c b/sbin/ipf/libipf/interror.c
index 994fb9d2b320..6d8c313ceb8b 100644
--- a/sbin/ipf/libipf/interror.c
+++ b/sbin/ipf/libipf/interror.c
@@ -17,7 +17,7 @@ typedef struct {
static ipf_error_entry_t *find_error(int);
-#define IPF_NUM_ERRORS 477
+#define IPF_NUM_ERRORS sizeof(ipf_errors) / sizeof(ipf_error_entry_t)
/*
* NO REUSE OF NUMBERS!
@@ -25,7 +25,7 @@ static ipf_error_entry_t *find_error(int);
* IF YOU WANT TO ADD AN ERROR TO THIS TABLE, _ADD_ A NEW NUMBER.
* DO _NOT_ USE AN EMPTY NUMBER OR FILL IN A GAP.
*/
-static ipf_error_entry_t ipf_errors[IPF_NUM_ERRORS] = {
+static ipf_error_entry_t ipf_errors[] = {
{ 1, "auth table locked/full" },
{ 2, "" },
{ 3, "copyinptr received bad address" },
@@ -228,6 +228,8 @@ static ipf_error_entry_t ipf_errors[IPF_NUM_ERRORS] = {
{ 30024, "object size incorrect for hash table" },
{ 30025, "hash table size must be at least 1"},
{ 30026, "cannot allocate memory for hash table context" },
+ { 30027, "hash table larger than maximum allowed" },
+ { 30028, "hash table multiplication overflow" },
/* -------------------------------------------------------------------------- */
{ 40001, "invalid minor device numebr for log read" },
{ 40002, "read size too small" },
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index 5f576c391086..5ea51eb02eb0 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -172,6 +172,7 @@ MAN= aac.4 \
geom_linux_lvm.4 \
geom_map.4 \
geom_uzip.4 \
+ geom_zero.4 \
gif.4 \
${_gve.4} \
gpio.4 \
@@ -960,6 +961,7 @@ _ccd.4= ccd.4
.if ${MK_CDDL} != "no"
_dtrace_provs= dtrace_audit.4 \
+ dtrace_callout_execute.4 \
dtrace_fbt.4 \
dtrace_io.4 \
dtrace_ip.4 \
@@ -969,7 +971,8 @@ _dtrace_provs= dtrace_audit.4 \
dtrace_sctp.4 \
dtrace_tcp.4 \
dtrace_udp.4 \
- dtrace_udplite.4
+ dtrace_udplite.4 \
+ dtrace_vfs.4
MLINKS+= dtrace_audit.4 dtaudit.4
.endif
@@ -1066,6 +1069,7 @@ MAN+= \
uvscom.4 \
zyd.4
+MLINKS+=geom_zero.4 gzero.4
MLINKS+=otus.4 if_otus.4
MLINKS+=rsu.4 if_rsu.4
MLINKS+=rtwn_usb.4 if_rtwn_usb.4
diff --git a/share/man/man4/dtrace_callout_execute.4 b/share/man/man4/dtrace_callout_execute.4
new file mode 100644
index 000000000000..1154ed066b97
--- /dev/null
+++ b/share/man/man4/dtrace_callout_execute.4
@@ -0,0 +1,68 @@
+.\"
+.\" Copyright (c) 2025 Mateusz Piotrowski <0mp@FreeBSD.org>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd November 4, 2025
+.Dt DTRACE_CALLOUT_EXECUTE 4
+.Os
+.Sh NAME
+.Nm dtrace_callout_execute
+.Nd a DTrace provider for the callout API
+.Sh SYNOPSIS
+.Nm callout_execute Ns Cm :kernel::callout_start
+.Nm callout_execute Ns Cm :kernel::callout_end
+.Sh DESCRIPTION
+The
+.Nm callout_execute
+provider allows for tracing the
+.Xr callout 9
+mechanism.
+.Pp
+The
+.Nm callout_execute Ns Cm :kernel::callout_start
+probe fires just before a callout.
+.Pp
+The
+.Nm callout_execute Ns Cm :kernel::callout_end
+probe fires right after a callout.
+.Pp
+The only argument to the
+.Nm callout_execute
+probes,
+.Fa args[0] ,
+is a callout handler
+.Ft struct callout *
+of the invoked callout.
+.Sh EXAMPLES
+.Ss Example 1: Graph of Callout Execution Time
+The following
+.Xr d 7
+script generates a distribution graph of
+.Xr callout 9
+execution times:
+.Bd -literal -offset 2n
+callout_execute:::callout_start
+{
+ self->cstart = timestamp;
+}
+
+callout_execute:::callout_end
+{
+ @length = quantize(timestamp - self->cstart);
+}
+.Ed
+.Sh SEE ALSO
+.Xr dtrace 1 ,
+.Xr tracing 7 ,
+.Xr callout 9 ,
+.Xr SDT 9
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm callout_execute
+provider was written by
+.An Robert N. M. Watson Aq Mt rwatson@FreeBSD.org .
+.Pp
+This manual page was written by
+.An Mateusz Piotrowski Aq Mt 0mp@FreeBSD.org .
diff --git a/share/man/man4/dtrace_vfs.4 b/share/man/man4/dtrace_vfs.4
new file mode 100644
index 000000000000..528d5da42f3d
--- /dev/null
+++ b/share/man/man4/dtrace_vfs.4
@@ -0,0 +1,97 @@
+.\"
+.\" Copyright (c) 2025 Mateusz Piotrowski <0mp@FreeBSD.org>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd November 3, 2025
+.Dt DTRACE_VFS 4
+.Os
+.Sh NAME
+.Nm dtrace_vfs
+.Nd a DTrace provider for Virtual File System
+.Sh SYNOPSIS
+.Sm off
+.Nm vfs Cm : fplookup : Ar function Cm : Ar name
+.Nm vfs Cm : namecache : Ar function Cm : Ar name
+.Nm vfs Cm : namei : Ar function Cm : Ar name
+.Nm vfs Cm : vop : Ar function Cm : Ar name
+.Sm on
+.Sh DESCRIPTION
+The DTrace
+.Nm vfs
+provider allows users to trace events in the
+.Xr VFS 9
+layer, the kernel interface for file systems on
+.Fx .
+.Pp
+Run
+.Ql dtrace -l -P vfs
+to list all
+.Nm vfs
+probes.
+Add
+.Fl v
+to generate program stability reports,
+which contain information about the number of probe arguments and their types.
+.Pp
+The
+.Cm fplookup
+module defines a single probe,
+.Fn vfs:fplookup:lookup:done "struct nameidata *ndp" "int line" "bool status_code" ,
+that instruments the fast path lookup code in
+.Xr VFS 9 .
+.Pp
+The
+.Cm namecache
+module provides probes related to the
+.Xr VFS 9
+cache.
+Consult the source code in
+.Pa src/sys/kern/vfs_cache.c
+for more details.
+.Pp
+The
+.Cm namei
+module manages probes related to pathname translation and lookup operations.
+Refer to
+.Xr namei 9
+to learn more.
+.Pp
+The
+.Cm vop
+module contains probes related to the functions responsible for
+.Xr vnode 9
+operations.
+.Sh COMPATIBILITY
+This provider is specific to
+.Fx .
+.Sh EXAMPLES
+Check what lookups failed to be handled in a lockless manner:
+.Bd -literal -offset 2n
+# dtrace -n 'vfs:fplookup:lookup:done { @[arg1, arg2] = count(); }'
+.Ed
+.Sh SEE ALSO
+.Xr dtrace 1 ,
+.Xr d 7 ,
+.Xr SDT 9 ,
+.Xr namei 9 ,
+.Xr VFS 9
+.Rs
+.%A Brendan Gregg
+.%A Jim Mauro
+.%B DTrace: Dynamic Tracing in Oracle Solaris, Mac OS X and FreeBSD
+.%I Prentice Hall
+.%P pp. 335\(en351
+.%D 2011
+.%U https://www.brendangregg.com/dtracebook/
+.Re
+.Sh AUTHORS
+.An -nosplit
+The
+.Fx
+.Nm vfs
+provider was written by
+.An Robert Watson Aq Mt rwatson@FreeBSD.org .
+.Pp
+This manual page was written by
+.An Mateusz Piotrowski Aq Mt 0mp@FreeBSD.org .
diff --git a/share/man/man4/geom_zero.4 b/share/man/man4/geom_zero.4
new file mode 100644
index 000000000000..8da09b1473c9
--- /dev/null
+++ b/share/man/man4/geom_zero.4
@@ -0,0 +1,174 @@
+.\"
+.\" Copyright (c) 2019 Greg White <gkwhite@gmail.com>. All rights reserved.
+.\" Copyright (c) 2025 Mateusz Piotrowski <0mp@FreeBSD.org>
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause
+.\"
+.Dd November 9, 2025
+.Dt GEOM_ZERO 4
+.Os
+.Sh NAME
+.Nm gzero ,
+.Nm geom_zero
+.Nd GEOM-based zero disk/block device
+.Sh SYNOPSIS
+.Cd "options GEOM_ZERO"
+.Pp
+In
+.Xr loader.conf 5
+or
+.Xr sysctl.conf 5 :
+.Cd kern.geom.zero.byte
+.Cd kern.geom.zero.clear
+.Sh DESCRIPTION
+.Nm
+is a
+.Xr GEOM 4
+device simulating a one-exabyte disk.
+It throws away any data written to it,
+and returns the value of
+.Va kern.geom.zero.byte
+for every byte read from it.
+.Pp
+.Nm
+differs from
+.Xr zero 4 ,
+which is a regular character device and has an infinite length,
+while
+.Pa /dev/gzero
+is a
+.Xr GEOM 4
+provider of large, but limited, size.
+.Pp
+Consult
+.Xr geom 8
+for instructions on how to use the supported commands of the
+.Xr GEOM 4
+.Nm ZERO
+class.
+.Pp
+.Nm
+is useful for benchmarking performance of GEOM and GEOM classes
+where compression of the data does not affect the results
+.Po blocks from
+.Pa /dev/gzero
+compress exceptionally well
+.Pc .
+Examples of such benchmarks include
+comparing the speed of two disk encryption algorithms and
+comparing a hardware versus software implementation
+of a single encryption algorithm.
+.Sh MIB VARIABLES
+The following variables are available as both
+.Xr sysctl 8
+variables and
+.Xr loader 8
+tunables:
+.Bl -tag -width "kern.geom.zero.clear"
+.It Va kern.geom.zero.byte
+This variable sets the fill byte of the
+.Nm
+device.
+Default:
+.Ql 0 .
+.It Va kern.geom.zero.clear
+This variable controls the clearing of the read data buffer.
+If set to
+.Ql 0 ,
+.Nm
+will not copy any data into the read data buffers
+and just return the read data buffers as they are without modifying them.
+In particular, it will not not fill the read buffer with the value of
+.Va kern.geom.zero.byte .
+This is useful for read benchmarking to reduce the measurement noise
+caused by extra memory initialization.
+Default:
+.Ql 1 .
+.El
+.Sh FILES
+.Bl -tag -width /dev/gzero
+.It Pa /dev/gzero
+The
+.Nm
+device.
+.El
+.Sh EXAMPLES
+Create the
+.Pa /dev/gzero
+device by loading the
+.Nm geom_zero
+kernel module:
+.Bd -literal -offset indent
+# geom zero load
+.Ed
+.Pp
+Show information about the
+.Nm
+device:
+.Bd -literal -offset indent
+# geom zero list
+Geom name: gzero
+Providers:
+1. Name: gzero
+ Mediasize: 1152921504606846976 (1.0E)
+ Sectorsize: 512
+ Mode: r0w0egzero0
+.Ed
+.Pp
+Set the fill byte of the
+.Nm
+device to 70
+.Po decimal for letter
+.Dq F
+in
+.Xr ascii 7
+.Pc :
+.Bd -literal -offset indent
+# sysctl kern.geom.zero.byte=70
+kern.geom.zero.byte: 0 -> 70
+# head -c 1 /dev/gzero
+F
+.Ed
+.Pp
+Benchmark read and write throughput of
+.Xr geli 8 Ap s
+default encryption algorithm with a 4-KiB sector size:
+.Bd -literal -offset indent
+# geom zero load
+# geli onetime -s 4096 gzero
+# sysctl kern.geom.zero.clear=0
+# dd if=/dev/gzero.eli of=/dev/zero bs=4k count=$((1024 * 256))
+262144+0 records in
+262144+0 records out
+1073741824 bytes transferred in 1.258195 secs (853398307 bytes/sec)
+# dd if=/dev/zero of=/dev/gzero.eli bs=4k count=$((1024 * 256))
+262144+0 records in
+262144+0 records out
+1073741824 bytes transferred in 1.663118 secs (645619658 bytes/sec)
+.Ed
+.Sh SEE ALSO
+.Xr GEOM 4 ,
+.Xr zero 4 ,
+.Xr geom 8 ,
+.Xr sysctl 8 ,
+.Xr bio 9
+.Sh HISTORY
+A
+.Nm
+device first appeared in
+.Fx 6 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+device was written by
+.An Paweł Jakub Dawidek Aq Mt pjd@FreeBSD.org .
+.Pp
+The
+.Nm
+manual page was originally written by
+.An Greg White Aq Mt gkwhite@gmail.com
+and rewritten by
+.An Mateusz Piotrowski Aq Mt 0mp@FreeBSD.org
+before landing in
+.Fx .
diff --git a/share/man/man4/zero.4 b/share/man/man4/zero.4
index f1cd52d455d1..85651d53d342 100644
--- a/share/man/man4/zero.4
+++ b/share/man/man4/zero.4
@@ -29,7 +29,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 7, 1996
+.Dd November 9, 2025
.Dt ZERO 4
.Os
.Sh NAME
@@ -48,6 +48,7 @@ supply of null bytes when read.
.El
.Sh SEE ALSO
.Xr full 4 ,
+.Xr gzero 4 ,
.Xr null 4
.Sh HISTORY
A
diff --git a/share/man/man7/ports.7 b/share/man/man7/ports.7
index 05ed6d29603b..648302668578 100644
--- a/share/man/man7/ports.7
+++ b/share/man/man7/ports.7
@@ -25,7 +25,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd September 10, 2025
+.Dd November 6, 2025
.Dt PORTS 7
.Os
.Sh NAME
@@ -581,6 +581,10 @@ data.
The default ports directory.
.It Pa /usr/ports/Mk/bsd.port.mk
The big Kahuna.
+.It Pa /var/db/ports
+The directory where the results of configuring
+.Va OPTIONS
+are stored.
.El
.Sh EXAMPLES
.Bl -tag -width 0n
diff --git a/share/man/man9/VFS.9 b/share/man/man9/VFS.9
index a269d8d070cf..fcb07afa478d 100644
--- a/share/man/man9/VFS.9
+++ b/share/man/man9/VFS.9
@@ -26,7 +26,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd February 9, 2010
+.Dd November 3, 2025
.Dt VFS 9
.Os
.Sh NAME
@@ -42,6 +42,7 @@ function from
rather than implementing empty functions or casting to
.Fa eopnotsupp .
.Sh SEE ALSO
+.Xr dtrace_vfs 4 ,
.Xr VFS_CHECKEXP 9 ,
.Xr VFS_FHTOVP 9 ,
.Xr VFS_INIT 9 ,
diff --git a/share/man/man9/callout.9 b/share/man/man9/callout.9
index 96b93283bfc2..97d9f4138742 100644
--- a/share/man/man9/callout.9
+++ b/share/man/man9/callout.9
@@ -27,7 +27,7 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd September 1, 2021
+.Dd November 4, 2025
.Dt CALLOUT 9
.Os
.Sh NAME
@@ -816,6 +816,8 @@ and
functions return a value of one if the callout was still pending when it was
called, a zero if the callout could not be stopped and a negative one is it
was either not running or has already completed.
+.Sh SEE ALSO
+.Xr dtrace_callout_execute 4
.Sh HISTORY
.Fx
initially used the long standing
diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk
index 811b0a493d50..d8872f93bfcd 100644
--- a/share/mk/src.opts.mk
+++ b/share/mk/src.opts.mk
@@ -192,6 +192,7 @@ __DEFAULT_YES_OPTIONS = \
WIRELESS \
WPA_SUPPLICANT_EAPOL \
ZFS \
+ ZFS_TESTS \
LOADER_ZFS \
ZONEINFO
@@ -459,6 +460,11 @@ MK_OFED_EXTRA:= no
.if ${MK_TESTS} == "no"
MK_DTRACE_TESTS:= no
+MK_ZFS_TESTS:= no
+.endif
+
+.if ${MK_ZFS} == "no"
+MK_ZFS_TESTS:= no
.endif
.if ${MK_TESTS_SUPPORT} == "no"
diff --git a/sys/fs/cd9660/cd9660_lookup.c b/sys/fs/cd9660/cd9660_lookup.c
index 961745f45afc..be594d46cf40 100644
--- a/sys/fs/cd9660/cd9660_lookup.c
+++ b/sys/fs/cd9660/cd9660_lookup.c
@@ -134,7 +134,7 @@ cd9660_lookup(ap)
char *name;
struct vnode **vpp = ap->a_vpp;
struct componentname *cnp = ap->a_cnp;
- int flags = cnp->cn_flags;
+ uint64_t flags = cnp->cn_flags;
int nameiop = cnp->cn_nameiop;
ep2 = ep = NULL;
diff --git a/sys/fs/cd9660/cd9660_vnops.c b/sys/fs/cd9660/cd9660_vnops.c
index 60b33fa9a493..a645c893524b 100644
--- a/sys/fs/cd9660/cd9660_vnops.c
+++ b/sys/fs/cd9660/cd9660_vnops.c
@@ -220,8 +220,8 @@ cd9660_getattr(ap)
vap->va_ctime = ip->inode.iso_ctime;
vap->va_rdev = ip->inode.iso_rdev;
- vap->va_size = (u_quad_t) ip->i_size;
- if (ip->i_size == 0 && (vap->va_mode & S_IFMT) == S_IFLNK) {
+ vap->va_size = ip->i_size;
+ if (ip->i_size == 0 && vp->v_type == VLNK) {
struct vop_readlink_args rdlnk;
struct iovec aiov;
struct uio auio;
diff --git a/sys/fs/fuse/fuse_vnops.c b/sys/fs/fuse/fuse_vnops.c
index 79317e4e4cd2..d5166559fbdc 100644
--- a/sys/fs/fuse/fuse_vnops.c
+++ b/sys/fs/fuse/fuse_vnops.c
@@ -1433,9 +1433,9 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
struct timespec now;
int nameiop = cnp->cn_nameiop;
- int flags = cnp->cn_flags;
- int wantparent = flags & (LOCKPARENT | WANTPARENT);
- int islastcn = flags & ISLASTCN;
+ bool wantparent = cnp->cn_flags & (LOCKPARENT | WANTPARENT);
+ bool isdotdot = cnp->cn_flags & ISDOTDOT;
+ bool islastcn = cnp->cn_flags & ISLASTCN;
struct mount *mp = vnode_mount(dvp);
struct fuse_data *data = fuse_get_mpdata(mp);
int default_permissions = data->dataflags & FSESS_DEFAULT_PERMISSIONS;
@@ -1468,8 +1468,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
return err;
is_dot = cnp->cn_namelen == 1 && *(cnp->cn_nameptr) == '.';
- if ((flags & ISDOTDOT) && !(data->dataflags & FSESS_EXPORT_SUPPORT))
- {
+ if (isdotdot && !(data->dataflags & FSESS_EXPORT_SUPPORT)) {
if (!(VTOFUD(dvp)->flag & FN_PARENT_NID)) {
/*
* Since the file system doesn't support ".." lookups,
@@ -1590,7 +1589,7 @@ fuse_vnop_lookup(struct vop_lookup_args *ap)
}
} else {
/* Entry was found */
- if (flags & ISDOTDOT) {
+ if (isdotdot) {
struct fuse_lookup_alloc_arg flaa;
flaa.nid = nid;
diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c
index 5daa302bedd1..90fe043f8b33 100644
--- a/sys/fs/nullfs/null_vnops.c
+++ b/sys/fs/nullfs/null_vnops.c
@@ -389,7 +389,7 @@ null_lookup(struct vop_lookup_args *ap)
{
struct componentname *cnp = ap->a_cnp;
struct vnode *dvp = ap->a_dvp;
- int flags = cnp->cn_flags;
+ uint64_t flags = cnp->cn_flags;
struct vnode *vp, *ldvp, *lvp;
struct mount *mp;
int error;
@@ -407,17 +407,25 @@ null_lookup(struct vop_lookup_args *ap)
/*
* Renames in the lower mounts might create an inconsistent
- * configuration where lower vnode is moved out of the
- * directory tree remounted by our null mount. Do not try to
- * handle it fancy, just avoid VOP_LOOKUP() with DOTDOT name
- * which cannot be handled by VOP, at least passing over lower
- * root.
+ * configuration where lower vnode is moved out of the directory tree
+ * remounted by our null mount.
+ *
+ * Do not try to handle it fancy, just avoid VOP_LOOKUP() with DOTDOT
+ * name which cannot be handled by the VOP.
*/
- if ((ldvp->v_vflag & VV_ROOT) != 0 && (flags & ISDOTDOT) != 0) {
- KASSERT((dvp->v_vflag & VV_ROOT) == 0,
- ("ldvp %p fl %#x dvp %p fl %#x flags %#x",
- ldvp, ldvp->v_vflag, dvp, dvp->v_vflag, flags));
- return (ENOENT);
+ if ((flags & ISDOTDOT) != 0) {
+ struct nameidata *ndp;
+
+ if ((ldvp->v_vflag & VV_ROOT) != 0) {
+ KASSERT((dvp->v_vflag & VV_ROOT) == 0,
+ ("ldvp %p fl %#x dvp %p fl %#x flags %#jx",
+ ldvp, ldvp->v_vflag, dvp, dvp->v_vflag,
+ (uintmax_t)flags));
+ return (ENOENT);
+ }
+ ndp = lookup_nameidata(cnp);
+ if (ndp != NULL && lookup_isroot(ndp, ldvp))
+ return (ENOENT);
}
/*
diff --git a/sys/fs/smbfs/smbfs_vnops.c b/sys/fs/smbfs/smbfs_vnops.c
index b03d39a65191..3c86b5a6e3f1 100644
--- a/sys/fs/smbfs/smbfs_vnops.c
+++ b/sys/fs/smbfs/smbfs_vnops.c
@@ -1044,7 +1044,7 @@ smbfs_lookup(struct vop_lookup_args *ap)
struct smbfattr fattr, *fap;
struct smb_cred *scred;
char *name = cnp->cn_nameptr;
- int flags = cnp->cn_flags;
+ uint64_t flags = cnp->cn_flags;
int nameiop = cnp->cn_nameiop;
int nmlen = cnp->cn_namelen;
int error, islastcn, isdot;
diff --git a/sys/fs/unionfs/union_vnops.c b/sys/fs/unionfs/union_vnops.c
index cd57a5cae459..aaff77dcb13d 100644
--- a/sys/fs/unionfs/union_vnops.c
+++ b/sys/fs/unionfs/union_vnops.c
@@ -76,14 +76,30 @@
KASSERT(((vp)->v_op == &unionfs_vnodeops), \
("unionfs: it is not unionfs-vnode"))
+static bool
+unionfs_lookup_isroot(struct componentname *cnp, struct vnode *dvp)
+{
+ struct nameidata *ndp;
+
+ if (dvp == NULL)
+ return (false);
+ if ((dvp->v_vflag & VV_ROOT) != 0)
+ return (true);
+ ndp = lookup_nameidata(cnp);
+ if (ndp == NULL)
+ return (false);
+ return (lookup_isroot(ndp, dvp));
+}
+
static int
unionfs_lookup(struct vop_cachedlookup_args *ap)
{
int iswhiteout;
int lockflag;
int error , uerror, lerror;
+ uint64_t cnflags;
u_long nameiop;
- u_long cnflags, cnflagsbk;
+ u_long cnflagsbk;
struct unionfs_node *dunp;
struct vnode *dvp, *udvp, *ldvp, *vp, *uvp, *lvp, *dtmpvp;
struct vattr va;
@@ -124,6 +140,10 @@ unionfs_lookup(struct vop_cachedlookup_args *ap)
if (LOOKUP != nameiop && udvp == NULLVP)
return (EROFS);
+ if (unionfs_lookup_isroot(cnp, udvp) ||
+ unionfs_lookup_isroot(cnp, ldvp))
+ return (ENOENT);
+
if (udvp != NULLVP) {
dtmpvp = udvp;
if (ldvp != NULLVP)
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index f51a1092114d..f073fc64e0bc 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -114,7 +114,8 @@ static void fdgrowtable_exp(struct filedesc *fdp, int nfd);
static void fdunused(struct filedesc *fdp, int fd);
static void fdused(struct filedesc *fdp, int fd);
static int fget_unlocked_seq(struct filedesc *fdp, int fd,
- cap_rights_t *needrightsp, struct file **fpp, seqc_t *seqp);
+ const cap_rights_t *needrightsp, struct file **fpp,
+ seqc_t *seqp);
static int getmaxfd(struct thread *td);
static u_long *filecaps_copy_prep(const struct filecaps *src);
static void filecaps_copy_finish(const struct filecaps *src,
@@ -2978,7 +2979,7 @@ finit_vnode(struct file *fp, u_int flag, void *data, struct fileops *ops)
}
int
-fget_cap_locked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
+fget_cap_locked(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp,
struct file **fpp, struct filecaps *havecapsp)
{
struct filedescent *fde;
@@ -3010,7 +3011,7 @@ out:
}
int
-fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp,
+fget_cap(struct thread *td, int fd, const cap_rights_t *needrightsp,
struct file **fpp, struct filecaps *havecapsp)
{
struct filedesc *fdp = td->td_proc->p_fd;
@@ -3256,7 +3257,7 @@ out_free:
}
static int
-fget_unlocked_seq(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
+fget_unlocked_seq(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp,
struct file **fpp, seqc_t *seqp)
{
#ifdef CAPABILITIES
@@ -3339,7 +3340,7 @@ fget_unlocked_seq(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
* racing with itself.
*/
int
-fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
+fget_unlocked(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp,
struct file **fpp)
{
#ifdef CAPABILITIES
@@ -3406,7 +3407,7 @@ out_fallback:
*/
#ifdef CAPABILITIES
int
-fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
+fget_only_user(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp,
struct file **fpp)
{
const struct filedescent *fde;
@@ -3436,7 +3437,7 @@ fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
}
#else
int
-fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
+fget_only_user(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp,
struct file **fpp)
{
struct file *fp;
@@ -3472,7 +3473,7 @@ fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
*/
static __inline int
_fget(struct thread *td, int fd, struct file **fpp, int flags,
- cap_rights_t *needrightsp)
+ const cap_rights_t *needrightsp)
{
struct filedesc *fdp;
struct file *fp;
@@ -3520,15 +3521,15 @@ _fget(struct thread *td, int fd, struct file **fpp, int flags,
}
int
-fget(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp)
+fget(struct thread *td, int fd, const cap_rights_t *rightsp, struct file **fpp)
{
return (_fget(td, fd, fpp, 0, rightsp));
}
int
-fget_mmap(struct thread *td, int fd, cap_rights_t *rightsp, vm_prot_t *maxprotp,
- struct file **fpp)
+fget_mmap(struct thread *td, int fd, const cap_rights_t *rightsp,
+ vm_prot_t *maxprotp, struct file **fpp)
{
int error;
#ifndef CAPABILITIES
@@ -3571,22 +3572,24 @@ fget_mmap(struct thread *td, int fd, cap_rights_t *rightsp, vm_prot_t *maxprotp,
}
int
-fget_read(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp)
+fget_read(struct thread *td, int fd, const cap_rights_t *rightsp,
+ struct file **fpp)
{
return (_fget(td, fd, fpp, FREAD, rightsp));
}
int
-fget_write(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp)
+fget_write(struct thread *td, int fd, const cap_rights_t *rightsp,
+ struct file **fpp)
{
return (_fget(td, fd, fpp, FWRITE, rightsp));
}
int
-fget_fcntl(struct thread *td, int fd, cap_rights_t *rightsp, int needfcntl,
- struct file **fpp)
+fget_fcntl(struct thread *td, int fd, const cap_rights_t *rightsp,
+ int needfcntl, struct file **fpp)
{
struct filedesc *fdp = td->td_proc->p_fd;
#ifndef CAPABILITIES
@@ -3624,7 +3627,7 @@ fget_fcntl(struct thread *td, int fd, cap_rights_t *rightsp, int needfcntl,
* XXX: what about the unused flags ?
*/
static __inline int
-_fgetvp(struct thread *td, int fd, int flags, cap_rights_t *needrightsp,
+_fgetvp(struct thread *td, int fd, int flags, const cap_rights_t *needrightsp,
struct vnode **vpp)
{
struct file *fp;
@@ -3646,14 +3649,15 @@ _fgetvp(struct thread *td, int fd, int flags, cap_rights_t *needrightsp,
}
int
-fgetvp(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp)
+fgetvp(struct thread *td, int fd, const cap_rights_t *rightsp,
+ struct vnode **vpp)
{
return (_fgetvp(td, fd, 0, rightsp, vpp));
}
int
-fgetvp_rights(struct thread *td, int fd, cap_rights_t *needrightsp,
+fgetvp_rights(struct thread *td, int fd, const cap_rights_t *needrightsp,
struct filecaps *havecaps, struct vnode **vpp)
{
struct filecaps caps;
@@ -3685,14 +3689,16 @@ out:
}
int
-fgetvp_read(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp)
+fgetvp_read(struct thread *td, int fd, const cap_rights_t *rightsp,
+ struct vnode **vpp)
{
return (_fgetvp(td, fd, FREAD, rightsp, vpp));
}
int
-fgetvp_exec(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp)
+fgetvp_exec(struct thread *td, int fd, const cap_rights_t *rightsp,
+ struct vnode **vpp)
{
return (_fgetvp(td, fd, FEXEC, rightsp, vpp));
@@ -3700,7 +3706,7 @@ fgetvp_exec(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp
#ifdef notyet
int
-fgetvp_write(struct thread *td, int fd, cap_rights_t *rightsp,
+fgetvp_write(struct thread *td, int fd, const cap_rights_t *rightsp,
struct vnode **vpp)
{
@@ -3718,13 +3724,10 @@ int __noinline
_fdrop(struct file *fp, struct thread *td)
{
int error;
-#ifdef INVARIANTS
- int count;
- count = refcount_load(&fp->f_count);
- if (count != 0)
- panic("fdrop: fp %p count %d", fp, count);
-#endif
+ KASSERT(refcount_load(&fp->f_count) == 0,
+ ("fdrop: fp %p count %d", fp, refcount_load(&fp->f_count)));
+
error = fo_close(fp, td);
atomic_subtract_int(&openfiles, 1);
crfree(fp->f_cred);
diff --git a/sys/kern/sys_procdesc.c b/sys/kern/sys_procdesc.c
index aab7b1616594..4665dc2c0421 100644
--- a/sys/kern/sys_procdesc.c
+++ b/sys/kern/sys_procdesc.c
@@ -121,7 +121,7 @@ static struct fileops procdesc_ops = {
* died.
*/
int
-procdesc_find(struct thread *td, int fd, cap_rights_t *rightsp,
+procdesc_find(struct thread *td, int fd, const cap_rights_t *rightsp,
struct proc **p)
{
struct procdesc *pd;
@@ -168,7 +168,8 @@ procdesc_pid(struct file *fp_procdesc)
* Retrieve the PID associated with a process descriptor.
*/
int
-kern_pdgetpid(struct thread *td, int fd, cap_rights_t *rightsp, pid_t *pidp)
+kern_pdgetpid(struct thread *td, int fd, const cap_rights_t *rightsp,
+ pid_t *pidp)
{
struct file *fp;
int error;
diff --git a/sys/kern/uipc_mqueue.c b/sys/kern/uipc_mqueue.c
index be3e3fefa749..926a9d311b67 100644
--- a/sys/kern/uipc_mqueue.c
+++ b/sys/kern/uipc_mqueue.c
@@ -846,7 +846,8 @@ mqfs_lookupx(struct vop_cachedlookup_args *ap)
struct mqfs_node *pd;
struct mqfs_node *pn;
struct mqfs_info *mqfs;
- int nameiop, flags, error, namelen;
+ uint64_t flags;
+ int nameiop, error, namelen;
char *pname;
struct thread *td;
@@ -2159,13 +2160,14 @@ sys_kmq_unlink(struct thread *td, struct kmq_unlink_args *uap)
return (error);
}
-typedef int (*_fgetf)(struct thread *, int, cap_rights_t *, struct file **);
+typedef int (*_fgetf)(struct thread *, int, const cap_rights_t *,
+ struct file **);
/*
* Get message queue by giving file slot
*/
static int
-_getmq(struct thread *td, int fd, cap_rights_t *rightsp, _fgetf func,
+_getmq(struct thread *td, int fd, const cap_rights_t *rightsp, _fgetf func,
struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq)
{
struct mqfs_node *pn;
diff --git a/sys/kern/uipc_sem.c b/sys/kern/uipc_sem.c
index 7cb25749ad9c..9871c0528338 100644
--- a/sys/kern/uipc_sem.c
+++ b/sys/kern/uipc_sem.c
@@ -123,8 +123,8 @@ static int ksem_create(struct thread *td, const char *path,
semid_t *semidp, mode_t mode, unsigned int value,
int flags, int compat32);
static void ksem_drop(struct ksem *ks);
-static int ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp,
- struct file **fpp);
+static int ksem_get(struct thread *td, semid_t id,
+ const cap_rights_t *rightsp, struct file **fpp);
static struct ksem *ksem_hold(struct ksem *ks);
static void ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks);
static struct ksem *ksem_lookup(char *path, Fnv32_t fnv);
@@ -588,7 +588,7 @@ ksem_create(struct thread *td, const char *name, semid_t *semidp, mode_t mode,
}
static int
-ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp,
+ksem_get(struct thread *td, semid_t id, const cap_rights_t *rightsp,
struct file **fpp)
{
struct ksem *ks;
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index ef3ebeb58176..336a0dd77d5c 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -91,7 +91,7 @@ static int sockargs(struct mbuf **, char *, socklen_t, int);
* A reference on the file entry is held upon returning.
*/
int
-getsock_cap(struct thread *td, int fd, cap_rights_t *rightsp,
+getsock_cap(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp, u_int *fflagp, struct filecaps *havecapsp)
{
struct file *fp;
@@ -727,7 +727,7 @@ kern_sendit(struct thread *td, int s, struct msghdr *mp, int flags,
struct uio auio;
struct iovec *iov;
struct socket *so;
- cap_rights_t *rights;
+ const cap_rights_t *rights;
#ifdef KTRACE
struct uio *ktruio = NULL;
#endif
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
index aacbd43403e1..e6cf39c09f19 100644
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -4006,7 +4006,7 @@ SYSCTL_PROC(_vfs_cache_param, OID_AUTO, fast_lookup, CTLTYPE_INT|CTLFLAG_RW|CTLF
*/
struct nameidata_outer {
size_t ni_pathlen;
- int cn_flags;
+ uint64_t cn_flags;
};
struct nameidata_saved {
@@ -4292,7 +4292,7 @@ cache_fpl_terminated(struct cache_fpl *fpl)
(NC_NOMAKEENTRY | NC_KEEPPOSENTRY | LOCKLEAF | LOCKPARENT | WANTPARENT | \
FAILIFEXISTS | FOLLOW | EMPTYPATH | LOCKSHARED | SAVENAME | SAVESTART | \
WILLBEDIR | ISOPEN | NOMACCHECK | AUDITVNODE1 | AUDITVNODE2 | NOCAPCHECK | \
- WANTIOCTLCAPS)
+ WANTIOCTLCAPS | NAMEILOOKUP)
#define CACHE_FPL_INTERNAL_CN_FLAGS \
(ISDOTDOT | MAKEENTRY | ISLASTCN)
@@ -5126,30 +5126,19 @@ static int __noinline
cache_fplookup_dotdot(struct cache_fpl *fpl)
{
struct nameidata *ndp;
- struct componentname *cnp;
struct namecache *ncp;
struct vnode *dvp;
- struct prison *pr;
u_char nc_flag;
ndp = fpl->ndp;
- cnp = fpl->cnp;
dvp = fpl->dvp;
- MPASS(cache_fpl_isdotdot(cnp));
+ MPASS(cache_fpl_isdotdot(fpl->cnp));
/*
* XXX this is racy the same way regular lookup is
*/
- for (pr = cnp->cn_cred->cr_prison; pr != NULL;
- pr = pr->pr_parent)
- if (dvp == pr->pr_root)
- break;
-
- if (dvp == ndp->ni_rootdir ||
- dvp == ndp->ni_topdir ||
- dvp == rootvnode ||
- pr != NULL) {
+ if (lookup_isroot(ndp, dvp)) {
fpl->tvp = dvp;
fpl->tvp_seqc = vn_seqc_read_any(dvp);
if (seqc_in_modify(fpl->tvp_seqc)) {
diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c
index cb013eb7ff83..dc8b7b92ccd4 100644
--- a/sys/kern/vfs_lookup.c
+++ b/sys/kern/vfs_lookup.c
@@ -530,12 +530,12 @@ namei(struct nameidata *ndp)
cnp->cn_origflags = cnp->cn_flags;
#endif
ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_thread->td_ucred;
- KASSERT(ndp->ni_resflags == 0, ("%s: garbage in ni_resflags: %x\n",
+ KASSERT(ndp->ni_resflags == 0, ("%s: garbage in ni_resflags: %x",
__func__, ndp->ni_resflags));
KASSERT(cnp->cn_cred && td->td_proc, ("namei: bad cred/proc"));
KASSERT((cnp->cn_flags & NAMEI_INTERNAL_FLAGS) == 0,
- ("namei: unexpected flags: %" PRIx64 "\n",
- cnp->cn_flags & NAMEI_INTERNAL_FLAGS));
+ ("namei: unexpected flags: %#jx",
+ (uintmax_t)(cnp->cn_flags & NAMEI_INTERNAL_FLAGS)));
if (cnp->cn_flags & NOCACHE)
KASSERT(cnp->cn_nameiop != LOOKUP,
("%s: NOCACHE passed with LOOKUP", __func__));
@@ -761,6 +761,31 @@ needs_exclusive_leaf(struct mount *mp, int flags)
_Static_assert(MAXNAMLEN == NAME_MAX,
"MAXNAMLEN and NAME_MAX have different values");
+
+struct nameidata *
+lookup_nameidata(struct componentname *cnp)
+{
+ if ((cnp->cn_flags & NAMEILOOKUP) == 0)
+ return (NULL);
+ return (__containerof(cnp, struct nameidata, ni_cnd));
+}
+
+/*
+ * Would a dotdot lookup relative to dvp cause this lookup to cross a jail or
+ * chroot boundary?
+ */
+bool
+lookup_isroot(struct nameidata *ndp, struct vnode *dvp)
+{
+ for (struct prison *pr = ndp->ni_cnd.cn_cred->cr_prison; pr != NULL;
+ pr = pr->pr_parent) {
+ if (dvp == pr->pr_root)
+ return (true);
+ }
+ return (dvp == ndp->ni_rootdir || dvp == ndp->ni_topdir ||
+ dvp == rootvnode);
+}
+
/*
* Search a pathname.
* This is a very central and rather complicated routine.
@@ -808,7 +833,6 @@ lookup(struct nameidata *ndp)
struct vnode *dp = NULL; /* the directory we are searching */
struct vnode *tdp; /* saved dp */
struct mount *mp; /* mount table entry */
- struct prison *pr;
size_t prev_ni_pathlen; /* saved ndp->ni_pathlen */
int docache; /* == 0 do not cache last component */
int wantparent; /* 1 => wantparent or lockparent flag */
@@ -1008,15 +1032,11 @@ dirloop:
goto bad;
}
for (;;) {
- for (pr = cnp->cn_cred->cr_prison; pr != NULL;
- pr = pr->pr_parent)
- if (dp == pr->pr_root)
- break;
- bool isroot = dp == ndp->ni_rootdir ||
- dp == ndp->ni_topdir || dp == rootvnode ||
- pr != NULL;
- if (isroot && (ndp->ni_lcf &
- NI_LCF_STRICTRELATIVE) != 0) {
+ bool isroot;
+
+ isroot = lookup_isroot(ndp, dp);
+ if (__predict_false(isroot && (ndp->ni_lcf &
+ NI_LCF_STRICTRELATIVE) != 0)) {
error = ENOTCAPABLE;
goto capdotdot;
}
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 7dcdaa66adb8..51f26b843c45 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -4315,7 +4315,7 @@ out:
* semantics.
*/
int
-getvnode_path(struct thread *td, int fd, cap_rights_t *rightsp,
+getvnode_path(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp)
{
struct file *fp;
@@ -4353,7 +4353,8 @@ getvnode_path(struct thread *td, int fd, cap_rights_t *rightsp,
* A reference on the file entry is held upon returning.
*/
int
-getvnode(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp)
+getvnode(struct thread *td, int fd, const cap_rights_t *rightsp,
+ struct file **fpp)
{
int error;
diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c
index 6591b543ddea..e685d581733b 100644
--- a/sys/kern/vfs_vnops.c
+++ b/sys/kern/vfs_vnops.c
@@ -195,21 +195,26 @@ vn_open(struct nameidata *ndp, int *flagp, int cmode, struct file *fp)
}
static uint64_t
-open2nameif(int fmode, u_int vn_open_flags)
+open2nameif(int fmode, u_int vn_open_flags, uint64_t cn_flags)
{
uint64_t res;
- res = ISOPEN | LOCKLEAF;
+ res = ISOPEN | LOCKLEAF | cn_flags;
if ((fmode & O_RESOLVE_BENEATH) != 0)
res |= RBENEATH;
if ((fmode & O_EMPTY_PATH) != 0)
res |= EMPTYPATH;
+ if ((fmode & O_NOFOLLOW) != 0)
+ res &= ~FOLLOW;
if ((vn_open_flags & VN_OPEN_NOAUDIT) == 0)
res |= AUDITVNODE1;
+ else
+ res &= ~AUDITVNODE1;
if ((vn_open_flags & VN_OPEN_NOCAPCHECK) != 0)
res |= NOCAPCHECK;
if ((vn_open_flags & VN_OPEN_WANTIOCTLCAPS) != 0)
res |= WANTIOCTLCAPS;
+
return (res);
}
@@ -242,7 +247,9 @@ restart:
return (EINVAL);
else if ((fmode & (O_CREAT | O_DIRECTORY)) == O_CREAT) {
ndp->ni_cnd.cn_nameiop = CREATE;
- ndp->ni_cnd.cn_flags = open2nameif(fmode, vn_open_flags);
+ ndp->ni_cnd.cn_flags = open2nameif(fmode, vn_open_flags,
+ ndp->ni_cnd.cn_flags);
+
/*
* Set NOCACHE to avoid flushing the cache when
* rolling in many files at once.
@@ -251,8 +258,8 @@ restart:
* exist despite NOCACHE.
*/
ndp->ni_cnd.cn_flags |= LOCKPARENT | NOCACHE | NC_KEEPPOSENTRY;
- if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0)
- ndp->ni_cnd.cn_flags |= FOLLOW;
+ if ((fmode & O_EXCL) != 0)
+ ndp->ni_cnd.cn_flags &= ~FOLLOW;
if ((vn_open_flags & VN_OPEN_INVFS) == 0)
bwillwrite();
if ((error = namei(ndp)) != 0)
@@ -320,9 +327,8 @@ restart:
}
} else {
ndp->ni_cnd.cn_nameiop = LOOKUP;
- ndp->ni_cnd.cn_flags = open2nameif(fmode, vn_open_flags);
- ndp->ni_cnd.cn_flags |= (fmode & O_NOFOLLOW) != 0 ? NOFOLLOW :
- FOLLOW;
+ ndp->ni_cnd.cn_flags = open2nameif(fmode, vn_open_flags,
+ ndp->ni_cnd.cn_flags);
if ((fmode & FWRITE) == 0)
ndp->ni_cnd.cn_flags |= LOCKSHARED;
if ((error = namei(ndp)) != 0)
diff --git a/sys/netpfil/ipfilter/netinet/ip_htable.c b/sys/netpfil/ipfilter/netinet/ip_htable.c
index b56909a02bc3..fc0044651c70 100644
--- a/sys/netpfil/ipfilter/netinet/ip_htable.c
+++ b/sys/netpfil/ipfilter/netinet/ip_htable.c
@@ -99,6 +99,8 @@ typedef struct ipf_htable_softc_s {
u_long ipf_nhtnodes[LOOKUP_POOL_SZ];
iphtable_t *ipf_htables[LOOKUP_POOL_SZ];
iphtent_t *ipf_node_explist;
+ ipftuneable_t *ipf_htable_tune;
+ u_int ipf_htable_size_max;
} ipf_htable_softc_t;
ipf_lookup_t ipf_htable_backend = {
@@ -125,6 +127,18 @@ ipf_lookup_t ipf_htable_backend = {
};
+static ipftuneable_t ipf_htable_tuneables[] = {
+ { { (void *)offsetof(ipf_htable_softc_t, ipf_htable_size_max) },
+ "htable_size_max", 1, 0x7fffffff,
+ stsizeof(ipf_htable_softc_t, ipf_htable_size_max),
+ 0, NULL, NULL },
+ { { NULL },
+ NULL, 0, 0,
+ 0,
+ 0, NULL, NULL }
+};
+
+
/* ------------------------------------------------------------------------ */
/* Function: ipf_htable_soft_create */
/* Returns: void * - NULL = failure, else pointer to local context */
@@ -145,6 +159,18 @@ ipf_htable_soft_create(ipf_main_softc_t *softc)
bzero((char *)softh, sizeof(*softh));
+ softh->ipf_htable_tune = ipf_tune_array_copy(softh,
+ sizeof(ipf_htable_tuneables),
+ ipf_htable_tuneables);
+ if (softh->ipf_htable_tune == NULL) {
+ ipf_htable_soft_destroy(softc, softh);
+ return (NULL);
+ }
+ if (ipf_tune_array_link(softc, softh->ipf_htable_tune) == -1) {
+ ipf_htable_soft_destroy(softc, softh);
+ return (NULL);
+ }
+
return (softh);
}
@@ -163,6 +189,12 @@ ipf_htable_soft_destroy(ipf_main_softc_t *softc, void *arg)
{
ipf_htable_softc_t *softh = arg;
+ if (softh->ipf_htable_tune != NULL) {
+ ipf_tune_array_unlink(softc, softh->ipf_htable_tune);
+ KFREES(softh->ipf_htable_tune, sizeof(ipf_htable_tuneables));
+ softh->ipf_htable_tune = NULL;
+ }
+
KFREE(softh);
}
@@ -184,6 +216,8 @@ ipf_htable_soft_init(softc, arg)
bzero((char *)softh, sizeof(*softh));
+ softh->ipf_htable_size_max = IPHTABLE_MAX_SIZE;
+
return (0);
}
@@ -332,6 +366,15 @@ ipf_htable_create(ipf_main_softc_t *softc, void *arg, iplookupop_t *op)
iph->iph_name[sizeof(iph->iph_name) - 1] = '\0';
}
+ if ((iph->iph_size == 0) ||
+ (iph->iph_size > softh->ipf_htable_size_max)) {
+ IPFERROR(30027);
+ return (EINVAL);
+ }
+ if (iph->iph_size > ( SIZE_MAX / sizeof(*iph->iph_table))) {
+ IPFERROR(30028);
+ return (EINVAL);
+ }
KMALLOCS(iph->iph_table, iphtent_t **,
iph->iph_size * sizeof(*iph->iph_table));
if (iph->iph_table == NULL) {
diff --git a/sys/netpfil/ipfilter/netinet/ip_htable.h b/sys/netpfil/ipfilter/netinet/ip_htable.h
index 55c289e57ff6..3a8782ccd4b2 100644
--- a/sys/netpfil/ipfilter/netinet/ip_htable.h
+++ b/sys/netpfil/ipfilter/netinet/ip_htable.h
@@ -55,6 +55,8 @@ typedef struct iphtable_s {
char iph_name[FR_GROUPLEN]; /* hash table number */
} iphtable_t;
+#define IPHTABLE_MAX_SIZE 1024
+
/* iph_type */
#define IPHASH_LOOKUP 0
#define IPHASH_GROUPMAP 1
diff --git a/sys/sys/file.h b/sys/sys/file.h
index 9ae31974745a..4c0ec276c700 100644
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -251,14 +251,15 @@ extern struct fileops socketops;
extern int maxfiles; /* kernel limit on number of open files */
extern int maxfilesperproc; /* per process limit on number of open files */
-int fget(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp);
-int fget_mmap(struct thread *td, int fd, cap_rights_t *rightsp,
+int fget(struct thread *td, int fd, const cap_rights_t *rightsp,
+ struct file **fpp);
+int fget_mmap(struct thread *td, int fd, const cap_rights_t *rightsp,
vm_prot_t *maxprotp, struct file **fpp);
-int fget_read(struct thread *td, int fd, cap_rights_t *rightsp,
+int fget_read(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp);
-int fget_write(struct thread *td, int fd, cap_rights_t *rightsp,
+int fget_write(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp);
-int fget_fcntl(struct thread *td, int fd, cap_rights_t *rightsp,
+int fget_fcntl(struct thread *td, int fd, const cap_rights_t *rightsp,
int needfcntl, struct file **fpp);
int _fdrop(struct file *fp, struct thread *td);
int fget_remote(struct thread *td, struct proc *p, int fd, struct file **fpp);
@@ -281,15 +282,15 @@ int file_kcmp_generic(struct file *fp1, struct file *fp2, struct thread *td);
void finit(struct file *, u_int, short, void *, struct fileops *);
void finit_vnode(struct file *, u_int, void *, struct fileops *);
-int fgetvp(struct thread *td, int fd, cap_rights_t *rightsp,
+int fgetvp(struct thread *td, int fd, const cap_rights_t *rightsp,
struct vnode **vpp);
-int fgetvp_exec(struct thread *td, int fd, cap_rights_t *rightsp,
+int fgetvp_exec(struct thread *td, int fd, const cap_rights_t *rightsp,
struct vnode **vpp);
-int fgetvp_rights(struct thread *td, int fd, cap_rights_t *needrightsp,
+int fgetvp_rights(struct thread *td, int fd, const cap_rights_t *needrightsp,
struct filecaps *havecaps, struct vnode **vpp);
-int fgetvp_read(struct thread *td, int fd, cap_rights_t *rightsp,
+int fgetvp_read(struct thread *td, int fd, const cap_rights_t *rightsp,
struct vnode **vpp);
-int fgetvp_write(struct thread *td, int fd, cap_rights_t *rightsp,
+int fgetvp_write(struct thread *td, int fd, const cap_rights_t *rightsp,
struct vnode **vpp);
int fgetvp_lookup_smr(int fd, struct nameidata *ndp, struct vnode **vpp, bool *fsearch);
int fgetvp_lookup(int fd, struct nameidata *ndp, struct vnode **vpp);
diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h
index 9055f0a785e7..bf7a27e37161 100644
--- a/sys/sys/filedesc.h
+++ b/sys/sys/filedesc.h
@@ -267,22 +267,23 @@ struct filedesc_to_leader *
struct filedesc_to_leader *
filedesc_to_leader_share(struct filedesc_to_leader *fdtol,
struct filedesc *fdp);
-int getvnode(struct thread *td, int fd, cap_rights_t *rightsp,
+int getvnode(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp);
-int getvnode_path(struct thread *td, int fd, cap_rights_t *rightsp,
+int getvnode_path(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp);
void mountcheckdirs(struct vnode *olddp, struct vnode *newdp);
-int fget_cap_locked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
- struct file **fpp, struct filecaps *havecapsp);
-int fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp,
+int fget_cap_locked(struct filedesc *fdp, int fd,
+ const cap_rights_t *needrightsp, struct file **fpp,
+ struct filecaps *havecapsp);
+int fget_cap(struct thread *td, int fd, const cap_rights_t *needrightsp,
struct file **fpp, struct filecaps *havecapsp);
/* Return a referenced file from an unlocked descriptor. */
-int fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
- struct file **fpp);
+int fget_unlocked(struct filedesc *fdp, int fd,
+ const cap_rights_t *needrightsp, struct file **fpp);
/* Return a file pointer without a ref. FILEDESC_IS_ONLY_USER must be true. */
-int fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp,
- struct file **fpp);
+int fget_only_user(struct filedesc *fdp, int fd,
+ const cap_rights_t *needrightsp, struct file **fpp);
#define fput_only_user(fdp, fp) ({ \
MPASS(FILEDESC_IS_ONLY_USER(fdp)); \
MPASS(refcount_load(&fp->f_count) > 0); \
diff --git a/sys/sys/namei.h b/sys/sys/namei.h
index e8369867c6b8..053a64367ff9 100644
--- a/sys/sys/namei.h
+++ b/sys/sys/namei.h
@@ -72,7 +72,7 @@ struct nameidata {
*/
const char *ni_dirp; /* pathname pointer */
enum uio_seg ni_segflg; /* location of pathname */
- cap_rights_t *ni_rightsneeded; /* rights required to look up vnode */
+ const cap_rights_t *ni_rightsneeded; /* rights needed to look up vnode */
/*
* Arguments to lookup.
*/
@@ -154,6 +154,7 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status,
#define LOCKSHARED 0x0100 /* Shared lock leaf */
#define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */
#define RBENEATH 0x100000000ULL /* No escape, even tmp, from start dir */
+#define NAMEILOOKUP 0x200000000ULL /* cnp is embedded in nameidata */
#define MODMASK 0xf000001ffULL /* mask of operational modifiers */
/*
@@ -249,12 +250,12 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status,
#define NDINIT_ALL(ndp, op, flags, segflg, namep, dirfd, startdir, rightsp, td) \
do { \
struct nameidata *_ndp = (ndp); \
- cap_rights_t *_rightsp = (rightsp); \
+ const cap_rights_t *_rightsp = (rightsp); \
MPASS(_rightsp != NULL); \
NDINIT_PREFILL(_ndp); \
NDINIT_DBG(_ndp); \
_ndp->ni_cnd.cn_nameiop = op; \
- _ndp->ni_cnd.cn_flags = flags; \
+ _ndp->ni_cnd.cn_flags = (flags) | NAMEILOOKUP; \
_ndp->ni_segflg = segflg; \
_ndp->ni_dirp = namep; \
_ndp->ni_dirfd = dirfd; \
@@ -312,6 +313,8 @@ void NDVALIDATE(struct nameidata *);
int namei(struct nameidata *ndp);
int lookup(struct nameidata *ndp);
+bool lookup_isroot(struct nameidata *ndp, struct vnode *dvp);
+struct nameidata *lookup_nameidata(struct componentname *cnp);
int relookup(struct vnode *dvp, struct vnode **vpp,
struct componentname *cnp);
#endif
diff --git a/sys/sys/procdesc.h b/sys/sys/procdesc.h
index ca26d65d5417..4e8b06fb7377 100644
--- a/sys/sys/procdesc.h
+++ b/sys/sys/procdesc.h
@@ -94,8 +94,10 @@ struct procdesc {
* In-kernel interfaces to process descriptors.
*/
int procdesc_exit(struct proc *);
-int procdesc_find(struct thread *, int fd, cap_rights_t *, struct proc **);
-int kern_pdgetpid(struct thread *, int fd, cap_rights_t *, pid_t *pidp);
+int procdesc_find(struct thread *, int fd, const cap_rights_t *,
+ struct proc **);
+int kern_pdgetpid(struct thread *, int fd, const cap_rights_t *,
+ pid_t *pidp);
void procdesc_new(struct proc *, int);
void procdesc_finit(struct procdesc *, struct file *);
pid_t procdesc_pid(struct file *);
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
index e7ad086818f4..d2b5ac867a46 100644
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -418,7 +418,7 @@ struct uio;
*/
int getsockaddr(struct sockaddr **namp, const struct sockaddr *uaddr,
size_t len);
-int getsock_cap(struct thread *td, int fd, cap_rights_t *rightsp,
+int getsock_cap(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp, u_int *fflagp, struct filecaps *havecaps);
void soabort(struct socket *so);
int soaccept(struct socket *so, struct sockaddr **nam);
diff --git a/tests/sys/cddl/Makefile b/tests/sys/cddl/Makefile
index 80c72ea5ec42..377faae73522 100644
--- a/tests/sys/cddl/Makefile
+++ b/tests/sys/cddl/Makefile
@@ -5,7 +5,7 @@ TESTSDIR= ${TESTSBASE}/sys/cddl
TESTS_SUBDIRS+= ${_zfs}
-.if ${MK_ZFS} != "no"
+.if ${MK_ZFS_TESTS} != "no"
_zfs= zfs
.endif
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
index 74c987e55734..f862e41d1d72 100644
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -13,6 +13,7 @@ ATF_TESTS_C+= basic_signal
ATF_TESTS_C+= kern_copyin
ATF_TESTS_C+= kern_descrip_test
ATF_TESTS_C+= fdgrowtable_test
+ATF_TESTS_C+= jail_lookup_root
ATF_TESTS_C+= kill_zombie
.if ${MK_OPENSSL} != "no"
ATF_TESTS_C+= ktls_test
@@ -58,6 +59,10 @@ PROGS+= coredump_phnum_helper
PROGS+= pdeathsig_helper
PROGS+= sendfile_helper
+.PATH: ${SRCTOP}/sbin/mount
+SRCS.jail_lookup_root+= jail_lookup_root.c getmntopts.c
+CFLAGS.jail_lookup_root+= -I${SRCTOP}/sbin/mount
+LIBADD.jail_lookup_root+= jail util
CFLAGS.sys_getrandom+= -I${SRCTOP}/sys/contrib/zstd/lib
LIBADD.sys_getrandom+= zstd
LIBADD.sys_getrandom+= c
diff --git a/tests/sys/kern/jail_lookup_root.c b/tests/sys/kern/jail_lookup_root.c
new file mode 100644
index 000000000000..34e89f4aea2b
--- /dev/null
+++ b/tests/sys/kern/jail_lookup_root.c
@@ -0,0 +1,133 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org>
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <jail.h>
+#include <mntopts.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+mkdir_checked(const char *dir, mode_t mode)
+{
+ int error;
+
+ error = mkdir(dir, mode);
+ ATF_REQUIRE_MSG(error == 0 || errno == EEXIST,
+ "mkdir %s: %s", dir, strerror(errno));
+}
+
+static void __unused
+mount_nullfs(const char *dir, const char *target)
+{
+ struct iovec *iov;
+ char errmsg[1024];
+ int error, iovlen;
+
+ iov = NULL;
+ iovlen = 0;
+
+ build_iovec(&iov, &iovlen, __DECONST(char *, "fstype"),
+ __DECONST(char *, "nullfs"), (size_t)-1);
+ build_iovec(&iov, &iovlen, __DECONST(char *, "fspath"),
+ __DECONST(char *, target), (size_t)-1);
+ build_iovec(&iov, &iovlen, __DECONST(char *, "from"),
+ __DECONST(char *, dir), (size_t)-1);
+ build_iovec(&iov, &iovlen, __DECONST(char *, "errmsg"),
+ errmsg, sizeof(errmsg));
+
+ errmsg[0] = '\0';
+ error = nmount(iov, iovlen, 0);
+ ATF_REQUIRE_MSG(error == 0, "nmount: %s",
+ errmsg[0] != '\0' ? errmsg : strerror(errno));
+
+ free_iovec(&iov, &iovlen);
+}
+
+ATF_TC_WITH_CLEANUP(jail_root);
+ATF_TC_HEAD(jail_root, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(jail_root, tc)
+{
+ int error, fd, jid;
+
+ mkdir_checked("./root", 0755);
+ mkdir_checked("./root/a", 0755);
+ mkdir_checked("./root/b", 0755);
+ mkdir_checked("./root/a/c", 0755);
+
+ jid = jail_setv(JAIL_CREATE | JAIL_ATTACH,
+ "name", "nullfs_jail_root_test",
+ "allow.mount", "true",
+ "allow.mount.nullfs", "true",
+ "enforce_statfs", "1",
+ "path", "./root",
+ "persist", NULL,
+ NULL);
+ ATF_REQUIRE_MSG(jid >= 0, "jail_setv: %s", jail_errmsg);
+
+ mount_nullfs("/a", "/b");
+
+ error = chdir("/b/c");
+ ATF_REQUIRE(error == 0);
+
+ error = rename("/a/c", "/c");
+ ATF_REQUIRE(error == 0);
+
+ /* Descending to the jail root should be ok. */
+ error = chdir("..");
+ ATF_REQUIRE(error == 0);
+
+ /* Going beyond the root will trigger an error. */
+ error = chdir("..");
+ ATF_REQUIRE_ERRNO(ENOENT, error != 0);
+ fd = open("..", O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE_ERRNO(ENOENT, fd < 0);
+}
+ATF_TC_CLEANUP(jail_root, tc)
+{
+ struct statfs fs;
+ fsid_t fsid;
+ int error, jid;
+
+ error = statfs("./root/b", &fs);
+ if (error != 0)
+ err(1, "statfs ./b");
+ fsid = fs.f_fsid;
+ error = statfs("./root", &fs);
+ if (error != 0)
+ err(1, "statfs ./root");
+ if (fsid.val[0] != fs.f_fsid.val[0] ||
+ fsid.val[1] != fs.f_fsid.val[1]) {
+ error = unmount("./root/b", 0);
+ if (error != 0)
+ err(1, "unmount ./root/b");
+ }
+
+ jid = jail_getid("nullfs_jail_root_test");
+ if (jid >= 0) {
+ error = jail_remove(jid);
+ if (error != 0)
+ err(1, "jail_remove");
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, jail_root);
+ return (atf_no_error());
+}
diff --git a/tools/build/options/WITHOUT_ZFS_TESTS b/tools/build/options/WITHOUT_ZFS_TESTS
new file mode 100644
index 000000000000..ae2ac45ca0f3
--- /dev/null
+++ b/tools/build/options/WITHOUT_ZFS_TESTS
@@ -0,0 +1 @@
+Do not build and install the legacy ZFS test suite.
diff --git a/usr.bin/truss/truss.1 b/usr.bin/truss/truss.1
index a0cd90b229aa..debb3af11b5d 100644
--- a/usr.bin/truss/truss.1
+++ b/usr.bin/truss/truss.1
@@ -1,5 +1,5 @@
.\"
-.Dd July 24, 2017
+.Dd June 18, 2025
.Dt TRUSS 1
.Os
.Sh NAME
@@ -104,7 +104,8 @@ Follow an already-running process:
.Xr kdump 1 ,
.Xr ktrace 1 ,
.Xr ptrace 2 ,
-.Xr utrace 2
+.Xr utrace 2 ,
+.Xr sysdecode 3
.Sh HISTORY
The
.Nm
diff --git a/usr.sbin/quot/Makefile b/usr.sbin/quot/Makefile
index b18c37126448..1f87278f1bfa 100644
--- a/usr.sbin/quot/Makefile
+++ b/usr.sbin/quot/Makefile
@@ -1,7 +1,10 @@
+.include <src.opts.mk>
PROG= quot
MAN= quot.8
-LIBADD= ufs
+LIBADD= ufs util
+HAS_TESTS=
+SUBDIR.${MK_TESTS}= tests
WARNS?= 2
diff --git a/usr.sbin/quot/quot.8 b/usr.sbin/quot/quot.8
index 0338457f6aeb..b777aef7288e 100644
--- a/usr.sbin/quot/quot.8
+++ b/usr.sbin/quot/quot.8
@@ -27,7 +27,7 @@
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd February 8, 1994
+.Dd November 13, 2025
.Dt QUOT 8
.Os
.Sh NAME
@@ -65,6 +65,7 @@ By default, all sizes are reported in 512-byte block counts.
Given a list of inodes (plus some optional data on each line)
in the standard input, for each file print out the owner (plus
the remainder of the input line).
+Lines that do not begin with a number are ignored.
This is traditionally used
in the pipe:
.Bd -literal -offset indent
diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c
index 7042acf07bc4..acde6e411091 100644
--- a/usr.sbin/quot/quot.c
+++ b/usr.sbin/quot/quot.c
@@ -40,9 +40,10 @@
#include <ufs/ffs/fs.h>
#include <err.h>
+#include <errno.h>
#include <fcntl.h>
#include <fstab.h>
-#include <errno.h>
+#include <inttypes.h>
#include <libufs.h>
#include <paths.h>
#include <pwd.h>
@@ -55,6 +56,7 @@
/* some flags of what to do: */
static char estimate;
static char count;
+static char noname;
static char unused;
static void (*func)(int, struct fs *, char *);
static long blocksize;
@@ -281,7 +283,7 @@ user(uid_t uid)
usr--) {
if (!usr->name) {
usr->uid = uid;
- if (!(pwd = getpwuid(uid))) {
+ if (noname || !(pwd = getpwuid(uid))) {
asprintf(&usr->name, "#%u", uid);
} else {
usr->name = strdup(pwd->pw_name);
@@ -308,7 +310,10 @@ cmpusers(const void *v1, const void *v2)
u1 = (const struct user *)v1;
u2 = (const struct user *)v2;
- return u2->space - u1->space;
+ return (u2->space > u1->space ? 1 :
+ u2->space < u1->space ? -1 :
+ u1->uid > u2->uid ? 1 :
+ u1->uid < u2->uid ? -1 : 0);
}
#define sortusers(users) (qsort((users),nusers,sizeof(struct user), \
@@ -476,43 +481,47 @@ douser(int fd, struct fs *super, char *name)
static void
donames(int fd, struct fs *super, char *name)
{
- int c;
- ino_t maxino;
- uintmax_t inode;
union dinode *dp;
+ char *end, *line;
+ size_t cap;
+ ssize_t len;
+ intmax_t inode, maxino;
maxino = super->fs_ncg * super->fs_ipg - 1;
- /* first skip the name of the filesystem */
- while ((c = getchar()) != EOF && (c < '0' || c > '9'))
- while ((c = getchar()) != EOF && c != '\n');
- ungetc(c,stdin);
- while (scanf("%ju", &inode) == 1) {
- if (inode > maxino) {
- warnx("illegal inode %ju", inode);
- return;
+ line = NULL;
+ cap = 0;
+ while ((len = getline(&line, &cap, stdin)) > 0) {
+ if (len > 0 && line[len - 1] == '\n')
+ line[--len] = '\0';
+ inode = strtoimax(line, &end, 10);
+ /*
+ * Silently ignore lines that do not begin with a number.
+ * For backward compatibility reasons, we do not require
+ * the optional comment to be preceded by whitespace.
+ */
+ if (end == line)
+ continue;
+ if (inode <= 0 || inode > maxino) {
+ warnx("invalid inode %jd", inode);
+ continue;
}
errno = 0;
if ((dp = get_inode(fd,super,inode))
&& !isfree(super, dp)) {
printf("%s\t",user(DIP(super, dp, di_uid))->name);
/* now skip whitespace */
- while ((c = getchar()) == ' ' || c == '\t');
+ while (*end == ' ' || *end == '\t')
+ end++;
/* and print out the remainder of the input line */
- while (c != EOF && c != '\n') {
- putchar(c);
- c = getchar();
- }
- putchar('\n');
+ printf("%s\n", end);
} else {
if (errno) {
err(1, "%s", name);
}
/* skip this line */
- while ((c = getchar()) != EOF && c != '\n');
}
- if (c == EOF)
- break;
}
+ free(line);
}
static void
@@ -578,6 +587,9 @@ main(int argc, char *argv[])
while (--argc > 0 && **++argv == '-') {
while (*++*argv) {
switch (**argv) {
+ case 'N':
+ noname = 1;
+ break;
case 'n':
func = donames;
break;
diff --git a/usr.sbin/quot/tests/Makefile b/usr.sbin/quot/tests/Makefile
new file mode 100644
index 000000000000..d4e64691f905
--- /dev/null
+++ b/usr.sbin/quot/tests/Makefile
@@ -0,0 +1,4 @@
+PACKAGE= tests
+ATF_TESTS_SH= quot_test
+
+.include <bsd.test.mk>
diff --git a/usr.sbin/quot/tests/quot_test.sh b/usr.sbin/quot/tests/quot_test.sh
new file mode 100644
index 000000000000..fd3d6df7b021
--- /dev/null
+++ b/usr.sbin/quot/tests/quot_test.sh
@@ -0,0 +1,120 @@
+#
+# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# Create and mount a UFS filesystem on a small memory disk
+quot_setup()
+{
+ atf_check -o save:dev mdconfig -t malloc -s 16M
+ local dev=$(cat dev)
+ atf_check -o ignore newfs "$@" /dev/$dev
+ atf_check mkdir mnt
+ local mnt=$(realpath mnt)
+ atf_check mount /dev/$dev "$mnt"
+ echo "/dev/$dev:" >expect
+ printf "%5d\t%5d\t%-8s\n" 8 2 "#0" >>expect
+ printf "%s\n" "/dev/$dev" >ninput
+ echo "/dev/$dev:" >nexpect
+}
+
+# Create a directory owned by a given UID
+quot_adduid()
+{
+ local uid=$1
+ atf_check install -d -o $uid -g 0 mnt/$uid
+ printf "%5d\t%5d\t%-8s\n" 4 1 "#$uid" >>expect
+ ls -di mnt/$uid >>ninput
+ printf "%s\t%s\n" "#$uid" mnt/$uid >>nexpect
+}
+
+# Perform the tests
+quot_test()
+{
+ local dev=$(cat dev)
+ # Deliberately add invalid lines to our -n input before the
+ # valid ones to verify that quot does not abort on first
+ # error. Note that quot deliberately ignores initial lines
+ # that don't start with a number, and that after encountering
+ # at least one line that does start with a number, quot would
+ # previously terminate on encountering one that doesn't (now
+ # it simply ignores them). This also tests that we don't
+ # require whitespace between the inode number and the comment.
+ echo "0zero" >>ninput
+ echo "invalid" >>ninput
+ echo "-1minusone" >>ninput
+ # Create inodes owned by a large number of users to exercise
+ # hash collisions and rehashing. The code uses an open hash
+ # table that starts out with only 8 entries and doubles every
+ # time it fills up.
+ local uid
+ for uid in $(seq 1 32); do
+ quot_adduid $uid
+ done
+ # Also create inodes owned by users with long UIDs, up to the
+ # highest possible value (2^32 - 2, because chown(2) and
+ # friends interpret 2^32 - 1 as “leave unchanged”).
+ local shift
+ for shift in $(seq 6 32); do
+ quot_adduid $(((1 << shift) - 2))
+ done
+ # Since quot operates directly on the underlying device, not
+ # on the mounted filesystem, we remount read-only to ensure
+ # that everything gets flushed to the memory disk.
+ atf_check mount -ur /dev/$dev
+ atf_check -o file:expect quot -fkN /dev/$dev
+ # Test -n option
+ atf_check -o file:nexpect \
+ -e inline:"quot: invalid inode 0\nquot: invalid inode -1\n" \
+ quot -Nn /dev/$dev <ninput
+}
+
+# Unmount and release the memory disk
+quot_cleanup()
+{
+ if [ -d mnt ]; then
+ umount mnt || true
+ fi
+ if [ -f dev ]; then
+ mdconfig -d -u $(cat dev) || true
+ fi
+}
+
+atf_test_case ufs1 cleanup
+ufs1_head()
+{
+ atf_set descr "Test quot on UFS1"
+ atf_set require.user root
+}
+ufs1_body()
+{
+ quot_setup -O1
+ quot_test
+}
+ufs1_cleanup()
+{
+ quot_cleanup
+}
+
+atf_test_case ufs2 cleanup
+ufs2_head()
+{
+ atf_set descr "Test quot on UFS2"
+ atf_set require.user root
+}
+ufs2_body()
+{
+ quot_setup -O2
+ quot_test
+}
+ufs2_cleanup()
+{
+ quot_cleanup
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case ufs1
+ atf_add_test_case ufs2
+}