This is the mail archive of the
libc-hacker@sourceware.cygnus.com
mailing list for the glibc project.
Re: several changes today
- To: drepper@cygnus.com, libc-hacker@cygnus.com
- Subject: Re: several changes today
- From: Zack Weinberg <zack@rabi.columbia.edu>
- Date: Mon, 19 Oct 1998 08:22:30 -0400
On Sun, 18 Oct 1998 23:30:51 -0400, Zack Weinberg wrote:
>The patch is not tested yet - I'm recompiling now.
And of course it had a silly bug in it. Here is a corrected version.
zw
>1998-10-18 23:25 -0400 Zack Weinberg <zack@rabi.phys.columbia.edu>
>
> * sysdeps/unix/opendir.c: Check at runtime for kernel support for
> O_DIRECTORY or the appended-slash hack. Make sure not to open
> anything other than a directory, by stat()ing before and after
> the open if necessary. Clean up.
============================================================
Index: sysdeps/unix/opendir.c
--- sysdeps/unix/opendir.c 1998/10/16 16:00:50 1.23
+++ sysdeps/unix/opendir.c 1998/10/19 12:20:04
@@ -16,52 +16,146 @@
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
-#include <stddef.h>
#include <stdlib.h>
+#include <string.h>
#include <dirent.h>
#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <dirstream.h>
-
-/* We want to be really safe the file we opened is a directory. Some systems
- have support for this, others don't. */
+/* 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
-# define OPENDIR(NAME) \
- do { \
- fd = __open (NAME, O_RDONLY|O_NDELAY|O_DIRECTORY); \
- if (fd < 0) \
- return NULL; \
- } while (0)
-#else
-# define OPENDIR(NAME) \
- do { \
- fd = __open (NAME, O_RDONLY|O_NDELAY); \
- if (fd < 0 || __fstat (fd, &statbuf) < 0 || ! S_ISDIR (statbuf.st_mode)) \
- { \
- if (fd >= 0) \
- __close (fd); \
- return NULL; \
- } \
- } while (0)
+static int
+tryopen_o_directory (const char *name)
+{
+ static int o_directory_works = -1;
+
+ if (o_directory_works == -1)
+ {
+ 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);
+ }
+
+ if (o_directory_works)
+ return __open (name, O_RDONLY|O_NDELAY|O_DIRECTORY);
+ else
+ return -2;
+}
#endif
+static int
+tryopen_append_slash (const char *name)
+{
+ static int append_slash_works = -1;
+
+ if (append_slash_works == -1)
+ {
+ int serrno = errno;
+ int x = __open ("/dev/null/", O_RDONLY|O_NDELAY);
+
+ if (x >= 0)
+ {
+ __close (x);
+ append_slash_works = 0;
+ }
+ else if (errno != ENOTDIR)
+ append_slash_works = 0;
+ else
+ append_slash_works = 1;
+
+ __set_errno (serrno);
+ }
+
+ if (append_slash_works)
+ {
+ size_t namelen = strlen (name);
+ char *namex = alloca (namelen + 2);
+
+ strcpy (namex, name);
+ namex[namelen+1] = '/';
+ namex[namelen+2] = '\0';
+
+ return __open (namex, O_RDONLY|O_NDELAY);
+ }
+ else
+ return -2;
+}
+
+static int
+tryopen_check_by_hand (const char *name)
+{
+ struct stat st;
+ int fd;
+ int serrno = errno;
+ /* 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, &st) < 0)
+ return -1;
+ if (! S_ISDIR (st.st_mode))
+ {
+ __set_errno (ENOTDIR);
+ return -1;
+ }
+
+ fd = __open (name, O_RDONLY|O_NDELAY);
+ /* We have to check again after the open, because the target might've
+ changed between the stat and the open. */
+ if (fd < 0)
+ return -1;
+ if (fstat (fd, &st) < 0)
+ {
+ __close (fd);
+ return -1;
+ }
+ if (! S_ISDIR (st.st_mode))
+ {
+ __close (fd);
+ __set_errno (ENOTDIR);
+ return -1;
+ }
+
+ __set_errno (serrno);
+ return fd;
+}
+
+
/* Open a directory stream on NAME. */
DIR *
__opendir (const char *name)
{
DIR *dirp;
- struct stat statbuf;
int fd;
size_t allocation;
- int save_errno;
if (name[0] == '\0')
{
@@ -71,41 +165,33 @@
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)
+ allocation = (BUFSIZ < sizeof (struct dirent)
+ ? sizeof (struct dirent) : BUFSIZ);
+
+ dirp = (DIR *) calloc (1, sizeof (DIR) + allocation);
+ if (dirp == NULL)
return NULL;
- if (! S_ISDIR (statbuf.st_mode))
+
+ fd = -2;
+#ifdef O_DIRECTORY
+ fd = tryopen_o_directory (name);
+#endif
+ if (fd == -2) fd = tryopen_append_slash (name);
+ if (fd == -2) fd = tryopen_check_by_hand (name);
+
+ if (fd < 0)
{
- __set_errno (ENOTDIR);
+ free (dirp);
return NULL;
}
-
- OPENDIR (name);
-
+
if (__fcntl (fd, F_SETFD, FD_CLOEXEC) < 0)
- goto lose;
-
-#ifdef _STATBUF_ST_BLKSIZE
- if (statbuf.st_blksize < sizeof (struct dirent))
- allocation = sizeof (struct dirent);
- else
- allocation = statbuf.st_blksize;
-#else
- allocation = (BUFSIZ < sizeof (struct dirent)
- ? sizeof (struct dirent) : BUFSIZ);
-#endif
-
- dirp = (DIR *) calloc (1, sizeof (DIR) + allocation); /* Zero-fill. */
- if (dirp == NULL)
- lose:
{
- save_errno = errno;
- (void) __close (fd);
- __set_errno (save_errno);
+ __close (fd);
+ free (dirp);
return NULL;
}
+
dirp->data = (char *) (dirp + 1);
dirp->allocation = allocation;
dirp->fd = fd;