aboutsummaryrefslogtreecommitdiff
path: root/sysdeps/unix/opendir.c
diff options
context:
space:
mode:
Diffstat (limited to 'sysdeps/unix/opendir.c')
-rw-r--r--sysdeps/unix/opendir.c65
1 files changed, 53 insertions, 12 deletions
diff --git a/sysdeps/unix/opendir.c b/sysdeps/unix/opendir.c
index 067dc2cb59..3cbd6628a3 100644
--- a/sysdeps/unix/opendir.c
+++ b/sysdeps/unix/opendir.c
@@ -29,9 +29,40 @@
#include <dirstream.h>
-
-#ifndef O_DIRECTORY
-# define O_DIRECTORY 0
+/* opendir() must not accidentally open something other than a directory.
+ Some OS's have kernel support for that, some don't. In the worst
+ case we have to stat() before the open() AND fstat() after.
+
+ We have to test at runtime for kernel support since libc may have
+ been compiled with different headers to the kernel it's running on.
+ This test can't be done reliably in the general case. We'll use
+ /dev/null, which if it's not a device lots of stuff will break, as
+ a guinea pig. It may be missing in chroot environments, so we
+ make sure to fail safe. */
+#ifdef O_DIRECTORY
+static int o_directory_works = -1;
+
+static void
+tryopen_o_directory (void)
+{
+ int serrno = errno;
+ int x = __open ("/dev/null", O_RDONLY|O_NDELAY|O_DIRECTORY);
+
+ if (x >= 0)
+ {
+ __close (x);
+ o_directory_works = 0;
+ }
+ else if (errno != ENOTDIR)
+ o_directory_works = 0;
+ else
+ o_directory_works = 1;
+
+ __set_errno (serrno);
+}
+# define EXTRA_FLAGS O_DIRECTORY
+#else
+# define EXTRA_FLAGS 0
#endif
@@ -53,18 +84,28 @@ __opendir (const char *name)
return NULL;
}
- /* We first have to check whether the name is for a directory. We
- cannot do this after the open() call since the open/close operation
- performed on, say, a tape device might have undesirable effects. */
- if (__stat (name, &statbuf) < 0)
- return NULL;
- if (! S_ISDIR (statbuf.st_mode))
+#ifdef O_DIRECTORY
+ /* Test whether O_DIRECTORY works. */
+ if (o_directory_works == -1)
+ tryopen_o_directory ();
+
+ /* We can skip the expensive `stat' call if O_DIRECTORY works. */
+ if (o_directory_works == 0)
+#endif
{
- __set_errno (ENOTDIR);
- return NULL;
+ /* We first have to check whether the name is for a directory. We
+ cannot do this after the open() call since the open/close operation
+ performed on, say, a tape device might have undesirable effects. */
+ if (__stat (name, &statbuf) < 0)
+ return NULL;
+ if (! S_ISDIR (statbuf.st_mode))
+ {
+ __set_errno (ENOTDIR);
+ return NULL;
+ }
}
- fd = __open (name, O_RDONLY|O_NDELAY|O_DIRECTORY);
+ fd = __open (name, O_RDONLY|O_NDELAY|EXTRA_FLAGS);
if (fd < 0)
return NULL;