Add O_SYMLINK emulation

for MacOSX partial compatibility, defined as O_PATH | O_SYNC | O_DIRECT.

libc openat() wrapper is modified to fstat() the descriptor and re-open
in the normal mode if the type is not symlink.

Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D56365
This commit is contained in:
Konstantin Belousov
2026-04-12 11:48:32 +03:00
parent ae6a13deb8
commit f9458655e7
6 changed files with 60 additions and 6 deletions
+3
View File
@@ -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_ */
+4
View File
@@ -75,3 +75,7 @@ FBSD_1.7 {
FBSD_1.9 {
pdwait;
};
FBSDprivate_1.0 {
__openat_symlink;
};
+3 -1
View File
@@ -29,9 +29,11 @@
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "namespace.h"
#include <sys/types.h>
#include <sys/fcntl.h>
#include <stdarg.h>
#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));
}
+43 -1
View File
@@ -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 <sys/types.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
#include <unistd.h>
#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));
}
+4 -1
View File
@@ -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);
+3 -3
View File
@@ -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