about summary refs log tree commit diff
path: root/sysdeps/unix/opendir.c
diff options
context:
space:
mode:
authorUlrich Drepper <drepper@redhat.com>1998-10-20 23:03:20 +0000
committerUlrich Drepper <drepper@redhat.com>1998-10-20 23:03:20 +0000
commit413bb8ca872b5a7e8a241a9c55334afe400dad06 (patch)
tree6046fffd11cb35344f4f52ade585aefdf6217001 /sysdeps/unix/opendir.c
parent9cdc774d050c53e75fd55e69c9359da4c38ac151 (diff)
downloadglibc-413bb8ca872b5a7e8a241a9c55334afe400dad06.tar.gz
glibc-413bb8ca872b5a7e8a241a9c55334afe400dad06.tar.xz
glibc-413bb8ca872b5a7e8a241a9c55334afe400dad06.zip
Update.
1998-10-18  Zack Weinberg  <zack@rabi.phys.columbia.edu> 
 
        * sysdeps/unix/opendir.c: Check at runtime for kernel support for 
        O_DIRECTORY.
 
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;