bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-gnulib] Re: getcwd fixes for bugs in glibc, Solaris, OpenBSD, ...


From: Paul Eggert
Subject: [bug-gnulib] Re: getcwd fixes for bugs in glibc, Solaris, OpenBSD, ...
Date: Tue, 30 Nov 2004 23:24:54 -0800
User-agent: Gnus/5.1006 (Gnus v5.10.6) Emacs/21.3 (gnu/linux)

Jim Meyering <address@hidden> writes:

>> How about if we use the system getcwd if it works, and fall back on
>> our own code if the system getcwd has problems?  In the typical
>> case, if the system getcwd succeeds, or fails for any reason other
>> than ERANGE, ENAMETOOLONG or ENOENT, we can simply use its result.
>
> Yes, that's what I prefer, too.

OK, I installed this into coreutils and gnulib.  It also attempts
to revamp the code slightly to make it more similar to glibc getcwd.c.

I haven't done a performance analysis on the result, though.

2004-11-30  Paul Eggert  <address@hidden>

        * lib/getcwd.c (is_ENAMETOOLONG): New macro.
        (__getcwd.c): Don't restore errno; glibc doesn't.
        [HAVE_PARTLY_WORKING_GETCWD && !defined AT_FDCWD]: Try system getcwd
        first, falling back to our code only if its results look suspicious.
        Ensure that the resulting buffer is only as large as necessary.

        * m4/getcwd-path-max.m4 (gl_FUNC_GETCWD_PATH_MAX):
        Define HAVE_PARTLY_WORKING_GETCWD if getcwd is partly working.

Index: lib/getcwd.c
===================================================================
RCS file: /fetish/cu/lib/getcwd.c,v
retrieving revision 1.9
diff -p -u -r1.9 getcwd.c
--- lib/getcwd.c        26 Nov 2004 07:27:45 -0000      1.9
+++ lib/getcwd.c        1 Dec 2004 07:10:44 -0000
@@ -79,6 +79,12 @@
 
 #include <limits.h>
 
+#ifdef ENAMETOOLONG
+# define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
+#else
+# define is_ENAMETOOLONG(x) 0
+#endif
+
 #ifndef MAX
 # define MAX(a, b) ((a) < (b) ? (b) : (a))
 #endif
@@ -144,8 +150,21 @@ __getcwd (char *buf, size_t size)
   char *path;
   register char *pathp;
   struct stat st;
-  int prev_errno = errno;
   size_t allocated = size;
+  size_t used;
+
+#if HAVE_PARTLY_WORKING_GETCWD && !defined AT_FDCWD
+  /* The system getcwd works, except it sometimes fails when it
+     shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.  If
+     AT_FDCWD is not defined, the algorithm below is O(N**2) and this
+     is much slower than the system getcwd (at least on GNU/Linux).
+     So trust the system getcwd's results unless they look
+     suspicious.  */
+# undef getcwd
+  path = getcwd (buf, size);
+  if (path || (errno != ERANGE && !is_ENAMETOOLONG (errno) && errno != ENOENT))
+    return path;
+#endif
 
   if (size == 0)
     {
@@ -158,14 +177,14 @@ __getcwd (char *buf, size_t size)
       allocated = BIG_FILE_NAME_LENGTH + 1;
     }
 
-  if (buf != NULL)
-    path = buf;
-  else
+  if (buf == NULL)
     {
       path = malloc (allocated);
       if (path == NULL)
        return NULL;
     }
+  else
+    path = buf;
 
   pathp = path + allocated;
   *--pathp = '\0';
@@ -350,12 +369,19 @@ __getcwd (char *buf, size_t size)
     free (dotlist);
 #endif
 
-  memmove (path, pathp, path + allocated - pathp);
+  used = path + allocated - pathp;
+  memmove (path, pathp, used);
 
-  /* Restore errno on successful return.  */
-  __set_errno (prev_errno);
+  if (buf == NULL && size == 0)
+    /* Ensure that the buffer is only as large as necessary.  */
+    buf = realloc (path, used);
+
+  if (buf == NULL)
+    /* Either buf was NULL all along, or `realloc' failed but
+       we still have the original string.  */
+    buf = path;
 
-  return path;
+  return buf;
 
  memory_exhausted:
   __set_errno (ENOMEM);
Index: m4/getcwd-path-max.m4
===================================================================
RCS file: /fetish/cu/m4/getcwd-path-max.m4,v
retrieving revision 1.13
diff -p -u -r1.13 getcwd-path-max.m4
--- m4/getcwd-path-max.m4       25 Nov 2004 19:26:50 -0000      1.13
+++ m4/getcwd-path-max.m4       1 Dec 2004 07:10:44 -0000
@@ -1,4 +1,4 @@
-#serial 5
+#serial 6
 # Check for several getcwd bugs with long paths.
 # If so, arrange to compile the wrapper function.
 
@@ -29,12 +29,13 @@ AC_DEFUN([gl_FUNC_GETCWD_PATH_MAX],
   AC_CHECK_DECLS_ONCE(getcwd)
   AC_CHECK_HEADERS_ONCE(fcntl.h)
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
-  AC_CACHE_CHECK([whether getcwd handles long paths properly],
-                 gl_cv_func_getcwd_path_max,
-  [
-  # Arrange for deletion of the temporary directory this test creates.
-  ac_clean_files="$ac_clean_files confdir3"
-  AC_RUN_IFELSE([AC_LANG_SOURCE([[
+  AC_CACHE_CHECK([whether getcwd handles long file names properly],
+    gl_cv_func_getcwd_path_max,
+    [# Arrange for deletion of the temporary directory this test creates.
+     ac_clean_files="$ac_clean_files confdir3"
+     AC_RUN_IFELSE(
+       [AC_LANG_SOURCE(
+         [[
 #include <errno.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -112,18 +113,23 @@ main (void)
         as a failure, too.  */
       if (mkdir (DIR_NAME, S_IRWXU) < 0 || chdir (DIR_NAME) < 0)
        {
-         fail = 1;
+         fail = 2;
          break;
        }
 
       if (PATH_MAX <= cwd_len && cwd_len < PATH_MAX + DIR_NAME_SIZE)
        {
          c = getcwd (buf, PATH_MAX);
-         if (c || errno != ERANGE)
+         if (!c && errno == ENOENT)
            {
              fail = 1;
              break;
            }
+         if (c || ! (errno == ERANGE || is_ENAMETOOLONG (errno)))
+           {
+             fail = 2;
+             break;
+           }
        }
 
       if (dotdot_max <= cwd_len - initial_cwd_len)
@@ -131,16 +137,25 @@ main (void)
          if (dotdot_max + DIR_NAME_SIZE < cwd_len - initial_cwd_len)
            break;
          c = getcwd (buf, cwd_len + 1);
-         if (!c && (AT_FDCWD || !is_ENAMETOOLONG (errno)))
+         if (!c)
            {
-             fail = 1;
-             break;
+             if (! (errno == ERANGE || errno == ENOENT
+                    || is_ENAMETOOLONG (errno)))
+               {
+                 fail = 2;
+                 break;
+               }
+             if (AT_FDCWD || errno == ERANGE || errno == ENOENT)
+               {
+                 fail = 1;
+                 break;
+               }
            }
        }
 
       if (c && strlen (c) != cwd_len)
        {
-         fail = 1;
+         fail = 2;
          break;
        }
       ++n_chdirs;
@@ -165,8 +180,20 @@ main (void)
   exit (fail);
 #endif
 }
-  ]])],
-       [gl_cv_func_getcwd_path_max=yes],
-       [gl_cv_func_getcwd_path_max=no],
-       [gl_cv_func_getcwd_path_max=no])])
+          ]])],
+    [gl_cv_func_getcwd_path_max=yes],
+    [case $? in
+     1) gl_cv_func_getcwd_path_max='no, but it is partly working';;
+     *) gl_cv_func_getcwd_path_max=no;;
+     esac],
+    [gl_cv_func_getcwd_path_max=no])
+  ])
+  case $gl_cv_func_getcwd_path_max in
+  no,*)
+    AC_DEFINE([HAVE_PARTLY_WORKING_GETCWD], 1,
+      [Define to 1 if getcwd works, except it sometimes fails when it 
shouldn't,
+       setting errno to ERANGE, ENAMETOOLONG, or ENOENT.  If __GETCWD_PREFIX
+       is not defined, it doesn't matter whether HAVE_PARTLY_WORKING_GETCWD
+       is defined.]);;
+  esac
 ])




reply via email to

[Prev in Thread] Current Thread [Next in Thread]