It is relatively easy to support O_DIRECTORY on platforms that lack it, so let’s do that instead of having to work around bugs like <https://bugs.gnu.org/78509#95>. * lib/fcntl.in.h (O_DIRECTORY): Default to 0x20000000 not 0, since Gnulib now supports it. * lib/open.c, lib/openat.c (OPEN_TRAILING_SLASH_BUG): Default to false, so that this can be used outside #if. (open, openat): Add support for O_DIRECTORY on platforms that lack it. If fstat fails, fail instead of assuming the file is a directory, since failure can occur due to EOVERFLOW, etc. Rearrange code to minimize differences between open.c and openat.c. * m4/fcntl-o.m4 (gl_FCNTL_O_FLAGS): Also test O_DIRECTORY, and define HAVE_WORKING_O_DIRECTORY if needed. Prefer AS_CASE for Emacs’s benefit. * m4/open.m4 (gl_FUNC_OPEN): * m4/openat.m4 (gl_FUNC_OPENAT): Require gl_FCNTL_O_FLAGS and replace the function if O_DIRECTORY does not work. * tests/test-open.h: Test O_DIRECTORY. --- ChangeLog | 23 ++++++++++ doc/posix-functions/open.texi | 4 ++ doc/posix-functions/openat.texi | 4 ++ doc/posix-headers/fcntl.texi | 7 ++- lib/fcntl.in.h | 2 +- lib/open.c | 60 +++++++++++++------------- lib/openat.c | 44 ++++++++++--------- m4/fcntl-o.m4 | 75 ++++++++++++++++++++++----------- m4/open.m4 | 5 ++- m4/openat.m4 | 21 +++------ tests/test-open.h | 5 +++ 11 files changed, 159 insertions(+), 91 deletions(-)
diff --git a/ChangeLog b/ChangeLog index 9351692a28..e67f901f54 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2025-05-26 Paul Eggert <egg...@cs.ucla.edu> + + fcntl-h: support O_DIRECTORY + It is relatively easy to support O_DIRECTORY on platforms that + lack it, so let’s do that instead of having to work around bugs + like <https://bugs.gnu.org/78509#95>. + * lib/fcntl.in.h (O_DIRECTORY): Default to 0x20000000 not 0, + since Gnulib now supports it. + * lib/open.c, lib/openat.c (OPEN_TRAILING_SLASH_BUG): + Default to false, so that this can be used outside #if. + (open, openat): Add support for O_DIRECTORY on platforms that lack it. + If fstat fails, fail instead of assuming the file is a directory, + since failure can occur due to EOVERFLOW, etc. + Rearrange code to minimize differences between open.c and openat.c. + * m4/fcntl-o.m4 (gl_FCNTL_O_FLAGS): Also test O_DIRECTORY, + and define HAVE_WORKING_O_DIRECTORY if needed. + Prefer AS_CASE for Emacs’s benefit. + * m4/open.m4 (gl_FUNC_OPEN): + * m4/openat.m4 (gl_FUNC_OPENAT): + Require gl_FCNTL_O_FLAGS and replace the function + if O_DIRECTORY does not work. + * tests/test-open.h: Test O_DIRECTORY. + 2025-05-26 Collin Funk <collin.fu...@gmail.com> crypto/gc: Simplify the previous change. diff --git a/doc/posix-functions/open.texi b/doc/posix-functions/open.texi index a7e83e4f49..1f10f30ca9 100644 --- a/doc/posix-functions/open.texi +++ b/doc/posix-functions/open.texi @@ -15,6 +15,10 @@ Some platforms do not support @code{O_CLOEXEC}: @c See posix-headers/fcntl.texi for the list. Mac OS X 10.6, FreeBSD 8.4, NetBSD 5.1, OpenBSD 4.9, Minix 3.1.8, AIX 7.1, HP-UX 11, Solaris 10, Cygwin 1.7.x, mingw, MSVC 14. @item +Some platforms do not support @code{O_DIRECTORY}: +mingw, MSVC 14, glibc 2.0, OS X 10.9, FreeBSD 7.4, NetBSD 4.0.1, OpenBSD 4.9, +AIX 7.1, HP-UX 11, Solaris 10. +@item On platforms where @code{off_t} is a 32-bit type, @code{open} may not work correctly with files 2 GiB and larger. @xref{Large File Support}. @item diff --git a/doc/posix-functions/openat.texi b/doc/posix-functions/openat.texi index c5ac2ca784..5c114dfe6d 100644 --- a/doc/posix-functions/openat.texi +++ b/doc/posix-functions/openat.texi @@ -18,6 +18,10 @@ But the replacement function is not safe to be used in libraries and is not mult Some platforms do not support @code{O_CLOEXEC}: AIX 7.1, Solaris 10. @item +Some platforms do not support @code{O_DIRECTORY}: +mingw, MSVC 14, glibc 2.0, OS X 10.9, FreeBSD 7.4, NetBSD 4.0.1, OpenBSD 4.9, +AIX 7.1, HP-UX 11, Solaris 10. +@item On platforms where @code{off_t} is a 32-bit type, @code{open} may not work correctly with files 2 GiB and larger. @xref{Large File Support}. @item diff --git a/doc/posix-headers/fcntl.texi b/doc/posix-headers/fcntl.texi index 211ed49a0c..9adaecc3ae 100644 --- a/doc/posix-headers/fcntl.texi +++ b/doc/posix-headers/fcntl.texi @@ -25,7 +25,7 @@ macOS 14, FreeBSD 10.4, NetBSD 10.0, OpenBSD 7.5, MSVC 14, Cygwin, Haiku, Minix Mac OS X 10.6, FreeBSD 8.4, NetBSD 5.1, OpenBSD 4.9, Minix 3.1.8, AIX 7.1, HP-UX 11.31, Solaris 10, Cygwin 1.7.1, mingw, MSVC 14. @item -@samp{O_DIRECTORY}, @samp{O_DSYNC}, @samp{O_NOCTTY}, +@samp{O_DSYNC}, @samp{O_NOCTTY}, @samp{O_NOFOLLOW}, @samp{O_RSYNC}, @samp{O_SYNC}, and @samp{O_TTY_INIT} are not defined on some platforms. When not otherwise defined, Gnulib defines these macros to 0, @@ -91,6 +91,11 @@ on some platforms but not on others. When not otherwise defined, Gnulib defines these macros to 0, which is generally safe. +@item +@samp{O_DIRECTORY} is not defined on some platforms: +mingw, MSVC 14, glibc 2.0, OS X 10.9, FreeBSD 7.4, NetBSD 4.0.1, OpenBSD 4.9, +AIX 7.1, HP-UX 11, Solaris 10. + @item @samp{FD_CLOEXEC}, @samp{F_DUPFD}, and @samp{F_GETFD} are not defined on some platforms: diff --git a/lib/fcntl.in.h b/lib/fcntl.in.h index 0e4c04ea9b..c5068ed48a 100644 --- a/lib/fcntl.in.h +++ b/lib/fcntl.in.h @@ -315,7 +315,7 @@ _GL_WARN_ON_USE (openat, "openat is not portable - " #endif #ifndef O_DIRECTORY -# define O_DIRECTORY 0 +# define O_DIRECTORY 0x20000000 /* Try to not collide with system O_* flags. */ #endif #ifndef O_DSYNC diff --git a/lib/open.c b/lib/open.c index 7415b48f81..0d30df961a 100644 --- a/lib/open.c +++ b/lib/open.c @@ -55,24 +55,19 @@ orig_open (const char *filename, int flags, mode_t mode) #include <sys/stat.h> #include <unistd.h> +#ifndef OPEN_TRAILING_SLASH_BUG +# define OPEN_TRAILING_SLASH_BUG false +#endif + #ifndef REPLACE_OPEN_DIRECTORY -# define REPLACE_OPEN_DIRECTORY 0 +# define REPLACE_OPEN_DIRECTORY false #endif int open (const char *filename, int flags, ...) { - /* 0 = unknown, 1 = yes, -1 = no. */ -#if GNULIB_defined_O_CLOEXEC - int have_cloexec = -1; -#else - static int have_cloexec; -#endif - - mode_t mode; - int fd; + mode_t mode = 0; - mode = 0; if (flags & O_CREAT) { va_list arg; @@ -99,7 +94,6 @@ open (const char *filename, int flags, ...) filename = "NUL"; #endif -#if OPEN_TRAILING_SLASH_BUG /* Fail if one of O_CREAT, O_WRONLY, O_RDWR is specified and the filename ends in a slash, as POSIX says such a filename must name a directory <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: @@ -118,9 +112,10 @@ open (const char *filename, int flags, ...) directories, - if O_WRONLY or O_RDWR is specified, open() must fail because the file does not contain a '.' directory. */ - if ((flags & O_CREAT) - || (flags & O_ACCMODE) == O_RDWR - || (flags & O_ACCMODE) == O_WRONLY) + if (OPEN_TRAILING_SLASH_BUG + && (flags & O_CREAT + || (flags & O_ACCMODE) == O_RDWR + || (flags & O_ACCMODE) == O_WRONLY)) { size_t len = strlen (filename); if (len > 0 && filename[len - 1] == '/') @@ -129,10 +124,16 @@ open (const char *filename, int flags, ...) return -1; } } + + /* 0 = unknown, 1 = yes, -1 = no. */ +#if GNULIB_defined_O_CLOEXEC + int have_cloexec = -1; +#else + static int have_cloexec; #endif - fd = orig_open (filename, - flags & ~(have_cloexec < 0 ? O_CLOEXEC : 0), mode); + int fd = orig_open (filename, + flags & ~(have_cloexec < 0 ? O_CLOEXEC : 0), mode); if (flags & O_CLOEXEC) { @@ -175,10 +176,9 @@ open (const char *filename, int flags, ...) } #endif -#if OPEN_TRAILING_SLASH_BUG - /* If the filename ends in a slash and fd does not refer to a directory, - then fail. - Rationale: POSIX says such a filename must name a directory + /* If the filename ends in a slash or O_DIRECTORY is given, + then fail if fd does not refer to a directory. + Rationale: A filename ending in slash cannot name a non-directory <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: "A pathname that contains at least one non-<slash> character and that ends with one or more trailing <slash> characters shall not be resolved @@ -186,23 +186,25 @@ open (const char *filename, int flags, ...) <slash> characters names an existing directory" If the named file without the slash is not a directory, open() must fail with ENOTDIR. */ - if (fd >= 0) + if (((!HAVE_WORKING_O_DIRECTORY && flags & O_DIRECTORY) + || OPEN_TRAILING_SLASH_BUG) + && 0 <= fd) { - /* We know len is positive, since open did not fail with ENOENT. */ - size_t len = strlen (filename); - if (filename[len - 1] == '/') + /* FILENAME must be nonempty, as open did not fail with ENOENT. */ + if ((!HAVE_WORKING_O_DIRECTORY && flags & O_DIRECTORY) + || filename[strlen (filename) - 1] == '/') { struct stat statbuf; - - if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode)) + int r = fstat (fd, &statbuf); + if (r < 0 || !S_ISDIR (statbuf.st_mode)) { + int err = r < 0 ? errno : ENOTDIR; close (fd); - errno = ENOTDIR; + errno = err; return -1; } } } -#endif #if REPLACE_FCHDIR if (!REPLACE_OPEN_DIRECTORY && 0 <= fd) diff --git a/lib/openat.c b/lib/openat.c index f6dce43ba7..66b53264a2 100644 --- a/lib/openat.c +++ b/lib/openat.c @@ -54,6 +54,10 @@ orig_openat (int fd, char const *filename, int flags, mode_t mode) #include <sys/stat.h> #include <errno.h> +#ifndef OPEN_TRAILING_SLASH_BUG +# define OPEN_TRAILING_SLASH_BUG false +#endif + #if HAVE_OPENAT /* Like openat, but support O_CLOEXEC and work around Solaris 9 bugs @@ -75,7 +79,6 @@ rpl_openat (int dfd, char const *filename, int flags, ...) va_end (arg); } -# if OPEN_TRAILING_SLASH_BUG /* Fail if one of O_CREAT, O_WRONLY, O_RDWR is specified and the filename ends in a slash, as POSIX says such a filename must name a directory <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: @@ -94,9 +97,10 @@ rpl_openat (int dfd, char const *filename, int flags, ...) directories, - if O_WRONLY or O_RDWR is specified, open() must fail because the file does not contain a '.' directory. */ - if ((flags & O_CREAT) - || (flags & O_ACCMODE) == O_RDWR - || (flags & O_ACCMODE) == O_WRONLY) + if (OPEN_TRAILING_SLASH_BUG + && (flags & O_CREAT + || (flags & O_ACCMODE) == O_RDWR + || (flags & O_ACCMODE) == O_WRONLY)) { size_t len = strlen (filename); if (len > 0 && filename[len - 1] == '/') @@ -105,14 +109,13 @@ rpl_openat (int dfd, char const *filename, int flags, ...) return -1; } } -# endif /* 0 = unknown, 1 = yes, -1 = no. */ -#if GNULIB_defined_O_CLOEXEC +# if GNULIB_defined_O_CLOEXEC int have_cloexec = -1; -#else +# else static int have_cloexec; -#endif +# endif int fd = orig_openat (dfd, filename, flags & ~(have_cloexec < 0 ? O_CLOEXEC : 0), mode); @@ -134,10 +137,9 @@ rpl_openat (int dfd, char const *filename, int flags, ...) } -# if OPEN_TRAILING_SLASH_BUG - /* If the filename ends in a slash and fd does not refer to a directory, - then fail. - Rationale: POSIX says such a filename must name a directory + /* If the filename ends in a slash or O_DIRECTORY is given, + then fail if fd does not refer to a directory. + Rationale: A filename ending in slash cannot name a non-directory <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13>: "A pathname that contains at least one non-<slash> character and that ends with one or more trailing <slash> characters shall not be resolved @@ -145,23 +147,25 @@ rpl_openat (int dfd, char const *filename, int flags, ...) <slash> characters names an existing directory" If the named file without the slash is not a directory, open() must fail with ENOTDIR. */ - if (fd >= 0) + if (((!HAVE_WORKING_O_DIRECTORY && flags & O_DIRECTORY) + || OPEN_TRAILING_SLASH_BUG) + && 0 <= fd) { - /* We know len is positive, since open did not fail with ENOENT. */ - size_t len = strlen (filename); - if (filename[len - 1] == '/') + /* FILENAME must be nonempty, as open did not fail with ENOENT. */ + if ((!HAVE_WORKING_O_DIRECTORY && flags & O_DIRECTORY) + || filename[strlen (filename) - 1] == '/') { struct stat statbuf; - - if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode)) + int r = fstat (fd, &statbuf); + if (r < 0 || !S_ISDIR (statbuf.st_mode)) { + int err = r < 0 ? errno : ENOTDIR; close (fd); - errno = ENOTDIR; + errno = err; return -1; } } } -# endif return fd; } diff --git a/m4/fcntl-o.m4 b/m4/fcntl-o.m4 index 7147679654..2f0faebdd1 100644 --- a/m4/fcntl-o.m4 +++ b/m4/fcntl-o.m4 @@ -1,5 +1,5 @@ # fcntl-o.m4 -# serial 8 +# serial 9 dnl Copyright (C) 2006, 2009-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -10,7 +10,8 @@ dnl Written by Paul Eggert. AC_PREREQ([2.60]) -# Test whether the flags O_NOATIME and O_NOFOLLOW actually work. +# Test whether the flags O_DIRECTORY, O_NOATIME and O_NOFOLLOW actually work. +# Define HAVE_WORKING_O_DIRECTORY to 1 if O_DIRECTORY works, or to 0 otherwise. # Define HAVE_WORKING_O_NOATIME to 1 if O_NOATIME works, or to 0 otherwise. # Define HAVE_WORKING_O_NOFOLLOW to 1 if O_NOFOLLOW works, or to 0 otherwise. AC_DEFUN([gl_FCNTL_O_FLAGS], @@ -33,14 +34,21 @@ AC_DEFUN([gl_FCNTL_O_FLAGS], # include <stdlib.h> # defined sleep(n) _sleep ((n) * 1000) #endif + #include <errno.h> #include <fcntl.h> ]GL_MDA_DEFINES[ + #ifndef O_DIRECTORY + #define O_DIRECTORY 0 + #endif #ifndef O_NOATIME #define O_NOATIME 0 #endif #ifndef O_NOFOLLOW #define O_NOFOLLOW 0 #endif + #ifndef O_SEARCH + #define O_SEARCH O_RDONLY + #endif static int const constants[] = { O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC, O_APPEND, @@ -49,6 +57,14 @@ AC_DEFUN([gl_FCNTL_O_FLAGS], ]], [[ int result = !constants; + + { + int fd = open ("confdefs.h", O_SEARCH | O_DIRECTORY); + result |= ! (fd < 0 && errno == ENOTDIR); + if (0 <= fd) + close (fd); + } + #if HAVE_SYMLINK { static char const sym[] = "conftest.sym"; @@ -112,31 +128,40 @@ AC_DEFUN([gl_FCNTL_O_FLAGS], } return result;]])], [gl_cv_header_working_fcntl_h=yes], - [case $? in #( - 4) gl_cv_header_working_fcntl_h='no (bad O_NOFOLLOW)';; #( - 64) gl_cv_header_working_fcntl_h='no (bad O_NOATIME)';; #( - 68) gl_cv_header_working_fcntl_h='no (bad O_NOATIME, O_NOFOLLOW)';; #( - *) gl_cv_header_working_fcntl_h='no';; - esac], - [case "$host_os" in - # Guess 'no' on native Windows. - mingw* | windows*) gl_cv_header_working_fcntl_h='no' ;; - *) gl_cv_header_working_fcntl_h=cross-compiling ;; - esac - ]) - ]) + [AS_CASE([$?], + [ 1], [gl_cv_header_working_fcntl_h="no (bad O_DIRECTORY)"], + [ 4], [gl_cv_header_working_fcntl_h="no (bad O_NOFOLLOW)"], + [ 5], [gl_cv_header_working_fcntl_h="no (bad O_DIRECTORY, O_NOFOLLOW)"], + [64], [gl_cv_header_working_fcntl_h="no (bad O_NOATIME)"], + [65], [gl_cv_header_working_fcntl_h="no (bad O_DIRECTORY, O_NOATIME)"], + [68], [gl_cv_header_working_fcntl_h="no (bad O_NOATIME, O_NOFOLLOW)"], + [69], [gl_cv_header_working_fcntl_h="no (bad O_DIRECTORY, O_NOATIME, O_NOFOLLOW)"], + [gl_cv_header_working_fcntl_h="no"])], + [AS_CASE([$host_os,$gl_cross_guess_normal], + # The O_DIRECTORY test is known to fail on Mac OS X 10.4.11 (2007) + # (see <https://bugs.gnu.org/78509#95>) + # and to succeed on macOS 12.6 [darwin21.6.0] (2021). + # For now, guess it fails on macOS 12.5 and earlier. + [darwin[[0-9]].*yes | darwin1[[0-9]]*.*yes | darwin20.*yes | \ + darwin21.[[0-5]].*yes], + [gl_cv_header_working_fcntl_h="guessing no (bad O_DIRECTORY)"], + # Known to be "no" on native MS-Windows. + [mingw* | windows*], + [gl_cv_header_working_fcntl_h=no], + [gl_cv_header_working_fcntl_h=$gl_cross_guess_normal])])]) + + AS_CASE([$gl_cv_header_working_fcntl_h], + [*O_DIRECTORY* | *no], [ac_val=0], [ac_val=1]) + AC_DEFINE_UNQUOTED([HAVE_WORKING_O_DIRECTORY], [$ac_val], + [Define to 1 if O_DIRECTORY works, 0 otherwise.]) - case $gl_cv_header_working_fcntl_h in #( - *O_NOATIME* | no | cross-compiling) ac_val=0;; #( - *) ac_val=1;; - esac + AS_CASE([$gl_cv_header_working_fcntl_h], + [*O_NOATIME* | *no], [ac_val=0], [ac_val=1]) AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOATIME], [$ac_val], - [Define to 1 if O_NOATIME works.]) + [Define to 1 if O_NOATIME works, 0 otherwise.]) - case $gl_cv_header_working_fcntl_h in #( - *O_NOFOLLOW* | no | cross-compiling) ac_val=0;; #( - *) ac_val=1;; - esac + AS_CASE([$gl_cv_header_working_fcntl_h], + [*O_NOFOLLOW* | *no], [ac_val=0], [ac_val=1]) AC_DEFINE_UNQUOTED([HAVE_WORKING_O_NOFOLLOW], [$ac_val], - [Define to 1 if O_NOFOLLOW works.]) + [Define to 1 if O_NOFOLLOW works, 0 otherwise.]) ]) diff --git a/m4/open.m4 b/m4/open.m4 index 2bceddbdbe..dd3a805f5e 100644 --- a/m4/open.m4 +++ b/m4/open.m4 @@ -1,5 +1,5 @@ # open.m4 -# serial 16 +# serial 17 dnl Copyright (C) 2007-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -10,6 +10,9 @@ AC_DEFUN([gl_FUNC_OPEN], [ AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([gl_PREPROC_O_CLOEXEC]) + AC_REQUIRE([gl_FCNTL_O_FLAGS]) + AS_CASE([$gl_cv_header_working_fcntl_h], + [*O_DIRECTORY* | *no], [REPLACE_OPEN=1]) case "$host_os" in mingw* | windows* | pw*) REPLACE_OPEN=1 diff --git a/m4/openat.m4 b/m4/openat.m4 index eacc70f299..7557451cf1 100644 --- a/m4/openat.m4 +++ b/m4/openat.m4 @@ -1,5 +1,5 @@ # openat.m4 -# serial 46 +# serial 47 dnl Copyright (C) 2004-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -15,21 +15,14 @@ AC_DEFUN([gl_FUNC_OPENAT], AC_REQUIRE([gl_FCNTL_H_DEFAULTS]) AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) AC_CHECK_FUNCS_ONCE([openat]) + AC_REQUIRE([gl_FCNTL_O_FLAGS]) AC_REQUIRE([gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK]) AC_REQUIRE([gl_PREPROC_O_CLOEXEC]) - case $ac_cv_func_openat+$gl_cv_func_lstat_dereferences_slashed_symlink+$gl_cv_macro_O_CLOEXEC in - yes+*yes+yes) - ;; - yes+*) - # Solaris 10 lacks O_CLOEXEC. - # Solaris 9 has *at functions, but uniformly mishandles trailing - # slash in all of them. - REPLACE_OPENAT=1 - ;; - *) - HAVE_OPENAT=0 - ;; - esac + AS_CASE([$ac_cv_func_openat+$gl_cv_header_working_fcntl_h+$gl_cv_func_lstat_dereferences_slashed_symlink+$gl_cv_macro_O_CLOEXEC], + [yes+*O_DIRECTORY*+*+* | yes+*no+*+*], [REPLACE_OPENAT=1], + [yes+*+*yes+yes], [], + [yes+*], [REPLACE_OPENAT=1], + [HAVE_OPENAT=0]) ]) # Prerequisites of lib/openat.c. diff --git a/tests/test-open.h b/tests/test-open.h index d253ef65b5..36823a4cbe 100644 --- a/tests/test-open.h +++ b/tests/test-open.h @@ -69,6 +69,11 @@ test_open (int (*func) (char const *, int, ...), bool print) ASSERT (func (BASE "file/", O_RDONLY) == -1); ASSERT (errno == ENOTDIR || errno == EISDIR || errno == EINVAL); + /* Cannot open non-directory with O_DIRECTORY. */ + errno = 0; + ASSERT (func (BASE "file", O_RDONLY | O_DIRECTORY) == -1); + ASSERT (errno == ENOTDIR); + /* Directories cannot be opened for writing. */ errno = 0; ASSERT (func (".", O_WRONLY) == -1); -- 2.48.1