Index: lib/libc/include/libc_private.h =================================================================== --- lib/libc/include/libc_private.h +++ lib/libc/include/libc_private.h @@ -320,6 +320,7 @@ struct iovec; struct kevent; struct msghdr; +struct open_how; struct pollfd; struct rusage; struct sigaction; @@ -362,6 +363,7 @@ int __sys_nanosleep(const struct timespec *, struct timespec *); int __sys_open(const char *, int, ...); int __sys_openat(int, const char *, int, ...); +long __sys_openat2(int, const char *, struct open_how *, __size_t); int __sys_pdfork(int *, int); int __sys_pselect(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timespec *, Index: lib/libc/sys/Symbol.map =================================================================== --- lib/libc/sys/Symbol.map +++ lib/libc/sys/Symbol.map @@ -422,6 +422,7 @@ timerfd_create; timerfd_gettime; timerfd_settime; + openat2; }; FBSDprivate_1.0 { @@ -813,6 +814,8 @@ __sys_open; _openat; __sys_openat; + _openat2; + __sys_openat2; _pathconf; __sys_pathconf; __sys_pdfork; Index: lib/libc/sys/openat2.c =================================================================== --- /dev/null +++ lib/libc/sys/openat2.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 iXsystems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice(s), this list of conditions and the following disclaimer as + * the first lines of this file unmodified other than the possible + * addition of one or more copyright notices. + * 2. Redistributions in binary form must reproduce the above copyright + * notice(s), this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include "libc_private.h" + +long +openat2(int fd, const char *path, struct open_how *how, size_t size) +{ + return (sys_openat2(fd, path, how, size)); +} Index: sys/bsm/audit_kevents.h =================================================================== --- sys/bsm/audit_kevents.h +++ sys/bsm/audit_kevents.h @@ -662,6 +662,7 @@ #define AUE_AIO_READV 43268 /* FreeBSD-specific. */ #define AUE_FSPACECTL 43269 /* FreeBSD-specific. */ #define AUE_TIMERFD 43270 /* FreeBSD/Linux. */ +#define AUE_OPENAT2 43271 /* FreeBSD/Linux. */ /* * Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the Index: sys/kern/syscalls.master =================================================================== --- sys/kern/syscalls.master +++ sys/kern/syscalls.master @@ -3329,6 +3329,13 @@ _Out_opt_ _Contains_long_timet_ struct itimerspec *old_value ); } - +588 AUE_OPENAT2 STD { + long openat2( + int fd, + _In_z_ const char *path, + _In_ struct open_how *how, + size_t size + ); + } ; vim: syntax=off Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -1087,9 +1087,12 @@ int sys_open(struct thread *td, struct open_args *uap) { - - return (kern_openat(td, AT_FDCWD, uap->path, UIO_USERSPACE, - uap->flags, uap->mode)); + struct open_how how = (struct open_how) { + .flags = uap->flags, + .mode = uap->mode, + }; + return (kern_openat2(td, AT_FDCWD, uap->path, UIO_USERSPACE, + &how, sizeof(struct open_how))); } #ifndef _SYS_SYSPROTO_H_ @@ -1103,15 +1106,73 @@ int sys_openat(struct thread *td, struct openat_args *uap) { + struct open_how how = (struct open_how) { + .flags = uap->flag, + .mode = uap->mode, + }; + AUDIT_ARG_FD(uap->fd); + return (kern_openat2(td, uap->fd, uap->path, UIO_USERSPACE, + &how, sizeof(struct open_how))); +} + +#ifndef _SYS_SYSPROTO_H_ +struct openat2_args { + int fd; + char *path; + struct open_how *how; + size_t size; +}; +#endif +static int +parse_open_how(struct open_how *how, size_t size, int *flag_out, int *mode_out, + uint64_t *resolve_out) +{ + if (size > sizeof(struct open_how)) + return (E2BIG); + + if ((how == NULL) || (size != sizeof(struct open_how))) + return (EINVAL); + + if (how->resolve & ~VALID_RESOLVE_FLAGS) + return (EINVAL); + + if (how->flags >= INT32_MAX) + return (EINVAL); + + if (how->mode >= INT32_MAX) + return (EINVAL); + + *flag_out = how->flags; + *mode_out = how->mode; + *resolve_out = 0; /* Not implemented yet */ + return (0); +} + +long +sys_openat2(struct thread *td, struct openat2_args *uap) +{ AUDIT_ARG_FD(uap->fd); - return (kern_openat(td, uap->fd, uap->path, UIO_USERSPACE, uap->flag, - uap->mode)); + int error; + struct open_how how; + + if (uap->size > sizeof(struct open_how)) + return (E2BIG); + + if ((uap->how == NULL) || (uap->size != sizeof(struct open_how))) + return (EINVAL); + + error = copyin(uap->how, &how, sizeof(struct open_how)); + if (error != 0) + return (error); + + return (kern_openat2(td, uap->fd, uap->path, UIO_USERSPACE, + &how, sizeof(struct open_how))); } int -kern_openat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, - int flags, int mode) +kern_openat2(struct thread *td, int fd, const char *path, enum uio_seg pathseg, + struct open_how *how, size_t size) { struct proc *p = td->td_proc; struct filedesc *fdp; @@ -1120,12 +1181,17 @@ struct vnode *vp; struct nameidata nd; cap_rights_t rights; - int cmode, error, indx; + int cmode, error, indx, flags, mode; + uint64_t resolve; indx = -1; fdp = p->p_fd; pdp = p->p_pd; + error = parse_open_how(how, size, &flags, &mode, &resolve); + if (error) + return (error); + AUDIT_ARG_FFLAGS(flags); AUDIT_ARG_MODE(mode); cap_rights_init_one(&rights, CAP_LOOKUP); @@ -1160,7 +1226,7 @@ NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | AUDITVNODE1 | WANTIOCTLCAPS, pathseg, path, fd, &rights); td->td_dupfd = -1; /* XXX check for fdopen */ - error = vn_open_cred(&nd, &flags, cmode, VN_OPEN_WANTIOCTLCAPS, + error = vn_open_cred(&nd, &flags, cmode, VN_OPEN_WANTIOCTLCAPS | resolve, td->td_ucred, fp); if (error != 0) { /* @@ -1255,6 +1321,17 @@ return (error); } +int +kern_openat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, + int flags, int mode) +{ + struct open_how how = (struct open_how) { + .flags = flags, + .mode = mode + }; + return (kern_openat2(td, fd, path, pathseg, &how, sizeof(struct open_how))); +} + #ifdef COMPAT_43 /* * Create a file. Index: sys/sys/fcntl.h =================================================================== --- sys/sys/fcntl.h +++ sys/sys/fcntl.h @@ -45,6 +45,7 @@ #include #include +#include #ifndef _MODE_T_DECLARED typedef __mode_t mode_t; @@ -379,6 +380,7 @@ int flock(int, int); int fspacectl(int, int, const struct spacectl_range *, int, struct spacectl_range *); +long openat2(int, const char *, struct open_how *, __size_t); #endif #if __POSIX_VISIBLE >= 200809 int openat(int, const char *, int, ...); Index: sys/sys/openat2.h =================================================================== --- /dev/null +++ sys/sys/openat2.h @@ -0,0 +1,66 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 2024 iXsystems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * @(#)fcntl.h 8.5 (Berkeley) 5/4/95 + * $FreeBSD$ + */ + +#ifndef _SYS__OPENAT2_H_ +#define _SYS__OPENAT2_H_ + +#include + +struct open_how { + uint64_t flags; + uint64_t mode; + uint64_t resolve; +}; + +/* + * resolve flags for openat2(2). + */ +#define RESOLVE_NO_XDEV 0x01 /* Block mount-point crossings. */ +#define RESOLVE_NO_MAGICLINKS 0x02 /* In linux, this blocks traversal through + procfs-style "magic-links" */ +#define RESOLVE_NO_SYMLINKS 0x04 /* Block traverse through all symlinks */ +#define RESOLVE_BENEATH 0x08 /* Block lexical "..", symlinks, and absolute + paths which escape the dirfd */ +#define RESOLVE_IN_ROOT 0x10 /* Make all jumps to "/" and ".." be + scoped inside the dirfd (similar to + chroot(2)). */ +#define RESOLVE_CACHED 0x20 /* Only complete if resolution can be completed + via cached lookup. */ + +#ifdef _KERNEL +/* + * This is used for validation in kern_openat2 to prevent setting + * resolve flags that have not been implemented yet. + */ +#define VALID_RESOLVE_FLAGS 0 +#endif /* _KERNEL */ +#endif /* !_SYS__SYS_OPENAT2_H_ */ Index: sys/sys/syscallsubr.h =================================================================== --- sys/sys/syscallsubr.h +++ sys/sys/syscallsubr.h @@ -53,6 +53,7 @@ struct msqid_ds; struct pollfd; struct ogetdirentries_args; +struct open_how; struct rlimit; struct rusage; struct sched_param; @@ -251,6 +252,8 @@ int oflags, int fd, long pos); int kern_openat(struct thread *td, int fd, const char *path, enum uio_seg pathseg, int flags, int mode); +int kern_openat2(struct thread *td, int fd, const char *path, + enum uio_seg pathseg, struct open_how *, size_t); int kern_pathconf(struct thread *td, const char *path, enum uio_seg pathseg, int name, u_long flags, long *valuep); int kern_pipe(struct thread *td, int fildes[2], int flags,