diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index 299629fce2a..ef490e1a66e 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -384,4 +384,7 @@ struct uexterror; int __uexterr_format(const struct uexterror *ue, char *buf, size_t bufsz); int __libc_uexterr_gettext(char *buf, size_t bufsz); +int __impl_openat(int fd, const char *path, int flags, ...); +int __openat_symlink(int fd, const char *path, int flags, int interposed); + #endif /* _LIBC_PRIVATE_H_ */ diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map index 8acffcfd714..1bae1fb7853 100644 --- a/lib/libc/sys/Symbol.map +++ b/lib/libc/sys/Symbol.map @@ -75,3 +75,7 @@ FBSD_1.7 { FBSD_1.9 { pdwait; }; + +FBSDprivate_1.0 { + __openat_symlink; +}; diff --git a/lib/libc/sys/open.c b/lib/libc/sys/open.c index dd7bedebf14..d58a81f60b1 100644 --- a/lib/libc/sys/open.c +++ b/lib/libc/sys/open.c @@ -29,9 +29,11 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "namespace.h" #include #include #include +#include "un-namespace.h" #include "libc_private.h" #pragma weak open @@ -48,5 +50,5 @@ open(const char *path, int flags, ...) } else { mode = 0; } - return (INTERPOS_SYS(openat, AT_FDCWD, path, flags, mode)); + return (__impl_openat(AT_FDCWD, path, flags, mode)); } diff --git a/lib/libc/sys/openat.c b/lib/libc/sys/openat.c index ba937cae3a3..f4223be8aad 100644 --- a/lib/libc/sys/openat.c +++ b/lib/libc/sys/openat.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 The FreeBSD Foundation. + * Copyright 2014, 2026 The FreeBSD Foundation. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. @@ -29,11 +29,46 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "namespace.h" #include #include +#include +#include #include +#include +#include "un-namespace.h" #include "libc_private.h" +static int +do_openat(int fd, const char *path, int flags, int interposed) +{ + if (interposed) + return (__sys_openat(fd, path, flags | O_PATH, 0)); + return (INTERPOS_SYS(openat, fd, path, flags | O_PATH, 0)); +} + +int +__openat_symlink(int fd, const char *path, int flags, int interposed) +{ + struct stat st; + int rfd, xfd, saved_errno; + + flags &= ~O_SYMLINK; + rfd = do_openat(fd, path, flags | O_PATH | O_NOFOLLOW, interposed); + if (rfd != -1 && _fstat(rfd, &st) != -1 && !S_ISLNK(st.st_mode)) { + xfd = do_openat(rfd, "", flags | O_EMPTY_PATH, interposed); + saved_errno = errno; + /* dup to rfd to guarantee lowest fd number value */ + if (_dup2(xfd, rfd) == -1) { + _close(rfd); + rfd = -1; + } + _close(xfd); + errno = saved_errno; + } + return (rfd); +} + __sym_compat(openat, __impl_openat, FBSD_1.1); __weak_reference(openat, __impl_openat); __sym_default(openat, openat, FBSD_1.2); @@ -45,12 +80,19 @@ openat(int fd, const char *path, int flags, ...) va_list ap; int mode; + if (__predict_false((flags & (O_SYMLINK | O_CREAT)) == + (O_SYMLINK | O_CREAT))) { + errno = EINVAL; + return (-1); + } if ((flags & O_CREAT) != 0) { va_start(ap, flags); mode = va_arg(ap, int); va_end(ap); } else { mode = 0; + if (__predict_false((flags & O_SYMLINK) == O_SYMLINK)) + return (__openat_symlink(fd, path, flags, 0)); } return (INTERPOS_SYS(openat, fd, path, flags, mode)); } diff --git a/lib/libthr/thread/thr_syscalls.c b/lib/libthr/thread/thr_syscalls.c index bff2d0624ae..8168185188e 100644 --- a/lib/libthr/thread/thr_syscalls.c +++ b/lib/libthr/thread/thr_syscalls.c @@ -298,7 +298,10 @@ __thr_openat(int fd, const char *path, int flags, int mode) curthread = _get_curthread(); _thr_cancel_enter(curthread); - ret = __sys_openat(fd, path, flags, mode); + if (__predict_false((flags & O_SYMLINK) == O_SYMLINK)) + ret = __openat_symlink(fd, path, flags, 1); + else + ret = __sys_openat(fd, path, flags, mode); _thr_cancel_leave(curthread, ret == -1); return (ret); diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h index 80cbca4ea75..bf64d06f2a4 100644 --- a/sys/sys/fcntl.h +++ b/sys/sys/fcntl.h @@ -145,10 +145,10 @@ typedef __pid_t pid_t; /* * Emulate MacOSX compatibility flag without consuming a flags bit. - * It is not fully correct since reads over regular files opened with - * this definition fail. + * Selected bits set does not define a useful open request and is + * unlikely to be specified by reasonable code. */ -#define O_SYMLINK (O_PATH | O_NOFOLLOW) +#define O_SYMLINK (O_PATH | O_DSYNC | O_DIRECT) #endif #if __POSIX_VISIBLE >= 202405