changeset 11935:d233b3d8efbf

fdopendir: optimize on mingw * lib/unistd.in.h (_gl_directory_name): New prototype. * lib/fchdir.c (_gl_directory_name): Implement it. (fchdir): Use it to simplify implementation. * lib/fdopendir.c (fdopendir) [REPLACE_FCHDIR]: Use metadata from fchdir, when available, to avoid calling [f]chdir(). Signed-off-by: Eric Blake <ebb9@byu.net>
author Eric Blake <ebb9@byu.net>
date Tue, 01 Sep 2009 10:05:44 -0600
parents 80567a8f98f8
children a126d5b22410
files ChangeLog lib/fchdir.c lib/fdopendir.c lib/unistd.in.h
diffstat 4 files changed, 39 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2009-09-02  Eric Blake  <ebb9@byu.net>
 
+	fdopendir: optimize on mingw
+	* lib/unistd.in.h (_gl_directory_name): New prototype.
+	* lib/fchdir.c (_gl_directory_name): Implement it.
+	(fchdir): Use it to simplify implementation.
+	* lib/fdopendir.c (fdopendir) [REPLACE_FCHDIR]: Use metadata from
+	fchdir, when available, to avoid calling [f]chdir().
+
 	fdopendir: split into its own module
 	* lib/openat.c (fdopendir): Move...
 	* lib/fdopendir.c: ...into new file.
--- a/lib/fchdir.c
+++ b/lib/fchdir.c
@@ -154,6 +154,25 @@
   return newfd;
 }
 
+/* If FD is currently visiting a directory, then return the name of
+   that directory.  Otherwise, return NULL and set errno.  */
+const char *
+_gl_directory_name (int fd)
+{
+  if (0 <= fd && fd < dirs_allocated && dirs[fd].name != NULL)
+    return dirs[fd].name;
+  /* At this point, fd is either invalid, or open but not a directory.
+     If dup2 fails, errno is correctly EBADF.  */
+  if (0 <= fd)
+    {
+      if (dup2 (fd, fd) == fd)
+        errno = ENOTDIR;
+    }
+  else
+    errno = EBADF;
+  return NULL;
+}
+
 /* Return stat information about FD in STATBUF.  Needed when
    rpl_open() used a dummy file to work around an open() that can't
    normally visit directories.  */
@@ -222,16 +241,6 @@
 int
 fchdir (int fd)
 {
-  if (fd >= 0 && fd < dirs_allocated && dirs[fd].name != NULL)
-    return chdir (dirs[fd].name);
-  /* At this point, fd is either invalid, or open but not a directory.
-     If dup2 fails, errno is correctly EBADF.  */
-  if (0 <= fd)
-    {
-      if (dup2 (fd, fd) == fd)
-        errno = ENOTDIR;
-    }
-  else
-    errno = EBADF;
-  return -1;
+  const char *name = _gl_directory_name (fd);
+  return name ? chdir (name) : -1;
 }
--- a/lib/fdopendir.c
+++ b/lib/fdopendir.c
@@ -30,7 +30,8 @@
 /* Replacement for Solaris' function by the same name.
    <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
    First, try to simulate it via opendir ("/proc/self/fd/FD").  Failing
-   that, simulate it by doing save_cwd/fchdir/opendir(".")/restore_cwd.
+   that, simulate it by using fchdir metadata, or by doing
+   save_cwd/fchdir/opendir(".")/restore_cwd.
    If either the save_cwd or the restore_cwd fails (relatively unlikely),
    then give a diagnostic and exit nonzero.
    Otherwise, this function works just like Solaris' fdopendir.
@@ -45,7 +46,6 @@
 DIR *
 fdopendir (int fd)
 {
-  struct saved_cwd saved_cwd;
   int saved_errno;
   DIR *dir;
 
@@ -66,6 +66,13 @@
      save_cwd/restore_cwd.  */
   if (! dir && EXPECTED_ERRNO (saved_errno))
     {
+#if REPLACE_FCHDIR
+      const char *name = _gl_directory_name (fd);
+      if (name)
+        dir = opendir (name);
+      saved_errno = errno;
+#else /* !REPLACE_FCHDIR */
+      struct saved_cwd saved_cwd;
       if (save_cwd (&saved_cwd) != 0)
 	openat_save_fail (errno);
 
@@ -84,6 +91,7 @@
 	}
 
       free_cwd (&saved_cwd);
+#endif /* !REPLACE_FCHDIR */
     }
 
   if (dir)
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -252,6 +252,7 @@
 extern int _gl_register_fd (int fd, const char *filename);
 extern void _gl_unregister_fd (int fd);
 extern int _gl_register_dup (int oldfd, int newfd);
+extern const char *_gl_directory_name (int fd);
 
 # endif
 #elif defined GNULIB_POSIXCHECK