bug-gnulib
[Top][All Lists]
Advanced

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

getcwd on AIX


From: Bruno Haible
Subject: getcwd on AIX
Date: Sun, 20 Nov 2011 20:27:06 +0100
User-agent: KMail/1.13.6 (Linux/2.6.37.6-0.5-desktop; KDE/4.6.0; x86_64; ; )

Hi,

In a testdir for module getcwd, I get this test failure on AIX 5.1, 5.2, 5.3,
6.1, 7.1:

  FAIL: test-getcwd.sh

On this platform, configure says:

  checking whether getcwd (NULL, 0) allocates memory for result... no
  checking for getcwd with POSIX signature... yes
  checking whether getcwd is declared... yes
  checking whether getcwd is declared without a macro... yes

The test program test-getcwd fails with exit code 4.

What's happening? After creating a sufficiently large number of subdirectories,
the statement

  c = getcwd (buf, PATH_MAX);

produces c = buf, strlen (c) = 1021 (whereas PATH_MAX = 1023), and
the result in buf is

/haible/multibuild-1511/aix51-cc/testdir1/gltests/confdir3/{...}/confdir3

which is wrong: The result should be

/home/haible/multibuild-1511/aix51-cc/testdir1/gltests/confdir3/{...}/confdir3

That is, the system's getcwd function and with it also the rpl_getcwd
function has returned a file name with a missing *first* component,
and errno is still 0, giving no indication to the failure.


Either of the two following patches fixes it. Which one do you prefer?


2011-11-20  Bruno Haible  <address@hidden>

        getcwd: Work around getcwd bug on AIX 5..7.
        * lib/getcwd.c (__getcwd): Don't use the system's getcwd on AIX.
        * doc/posix-functions/getcwd.texi: Mention list of platforms where
        getcwd does not handle long file names.

--- lib/getcwd.c.orig   Sun Nov 20 19:19:55 2011
+++ lib/getcwd.c        Sun Nov 20 19:03:32 2011
@@ -135,7 +135,7 @@
   size_t allocated = size;
   size_t used;
 
-#if HAVE_RAW_DECL_GETCWD
+#if HAVE_RAW_DECL_GETCWD && ! defined _AIX
   /* 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
@@ -143,7 +143,11 @@
 
      Use the system getcwd even if we have openat support, since the
      system getcwd works even when a parent is unreadable, while the
-     openat-based approach does not.  */
+     openat-based approach does not.
+
+     But on AIX 5.1..7.1, the system getcwd is not usable: If the current
+     directory name is slightly longer than PATH_MAX, it omits the first
+     directory component and returns this wrong result with errno = 0.  */
 
 # undef getcwd
   dir = getcwd (buf, size);
--- doc/posix-functions/getcwd.texi.orig        Sun Nov 20 19:19:55 2011
+++ doc/posix-functions/getcwd.texi     Sun Nov 20 19:12:51 2011
@@ -33,7 +33,8 @@
 This function is missing on some older platforms.
 @item
 This function does not handle long file names (greater than @code{PATH_MAX})
-correctly on some platforms.
+correctly on some platforms:
+glibc on Linux 2.4.20, MacOS X 10.5, FreeBSD 6.4, OpenBSD 4.9, AIX 7.1.
 @end itemize
 
 Portability problems not fixed by Gnulib:



2011-11-20  Bruno Haible  <address@hidden>

        getcwd: Work around getcwd bug on AIX 5..7.
        * lib/getcwd.c (__getcwd): On AIX, verify the system's getcwd result
        before returning it.
        * doc/posix-functions/getcwd.texi: Mention list of platforms where
        getcwd does not handle long file names.

*** lib/getcwd.c.orig   Sun Nov 20 19:40:14 2011
--- lib/getcwd.c        Sun Nov 20 19:40:09 2011
***************
*** 147,173 ****
  
  # undef getcwd
    dir = getcwd (buf, size);
!   if (dir || (size && errno == ERANGE))
!     return dir;
! 
!   /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
!      internal magic that lets it work even if an ancestor directory is
!      inaccessible, which is better in many cases.  So in this case try
!      again with a buffer that's almost always big enough.  */
!   if (errno == EINVAL && buf == NULL && size == 0)
      {
!       char big_buffer[BIG_FILE_NAME_LENGTH + 1];
!       dir = getcwd (big_buffer, sizeof big_buffer);
!       if (dir)
!         return strdup (dir);
      }
  
  # if HAVE_PARTLY_WORKING_GETCWD
!   /* The system getcwd works, except it sometimes fails when it
!      shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.    */
!   if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
!     return NULL;
  # endif
  #endif
  
    if (size == 0)
--- 147,194 ----
  
  # undef getcwd
    dir = getcwd (buf, size);
!   if (dir != NULL)
      {
! # ifdef _AIX
!       /* On AIX 5.1..7.1, the system getcwd can succeed and produce a
!          wrong result: If the current directory name is slightly longer
!          than PATH_MAX, it omits the first directory component and
!          returns this wrong result with errno = 0.  */
!       struct stat st2;
! 
!       if (__lstat (".", &st) >= 0
!           && __lstat (dir, &st2) >= 0
!           && st.st_dev == st2.st_dev && st.st_ino == st2.st_ino)
!         return dir;
! # else
!       return dir;
! # endif
      }
+   else
+     {
+       /* dir is NULL.  Look at errno.  */
+       if (size && errno == ERANGE)
+         return NULL;
+ 
+       /* Solaris getcwd (NULL, 0) fails with errno == EINVAL, but it has
+          internal magic that lets it work even if an ancestor directory is
+          inaccessible, which is better in many cases.  So in this case try
+          again with a buffer that's almost always big enough.  */
+       if (errno == EINVAL && buf == NULL && size == 0)
+         {
+           char big_buffer[BIG_FILE_NAME_LENGTH + 1];
+           dir = getcwd (big_buffer, sizeof big_buffer);
+           if (dir)
+             return strdup (dir);
+         }
  
  # if HAVE_PARTLY_WORKING_GETCWD
!       /* The system getcwd works, except it sometimes fails when it
!          shouldn't, setting errno to ERANGE, ENAMETOOLONG, or ENOENT.    */
!       if (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)
!         return NULL;
  # endif
+     }
  #endif
  
    if (size == 0)
--- doc/posix-functions/getcwd.texi.orig        Sun Nov 20 19:38:08 2011
+++ doc/posix-functions/getcwd.texi     Sun Nov 20 19:12:51 2011
@@ -33,7 +33,8 @@
 This function is missing on some older platforms.
 @item
 This function does not handle long file names (greater than @code{PATH_MAX})
-correctly on some platforms.
+correctly on some platforms:
+glibc on Linux 2.4.20, MacOS X 10.5, FreeBSD 6.4, OpenBSD 4.9, AIX 7.1.
 @end itemize
 
 Portability problems not fixed by Gnulib:


Additionally, here's a possible test program, for use in m4/getcwd-path-max.m4.

===============================================================================
#include <stddef.h>
#include <string.h>
/* Get declarations of getcwd(), mkdir().  */
#if HAVE_UNISTD_H
# include <unistd.h>
#else
# include <io.h>
# include <direct.h>
# define mkdir(name,mode) _mkdir (name)
#endif
#include <sys/stat.h>
/* Arrange to define PATH_MAX, like "pathmax.h" does. */
#if HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <limits.h>
#if defined HAVE_SYS_PARAM_H && !defined PATH_MAX && !defined MAXPATHLEN
# include <sys/param.h>
#endif
#if !defined PATH_MAX && defined MAXPATHLEN
# define PATH_MAX MAXPATHLEN
#endif
#ifdef __hpux
# undef PATH_MAX
# define PATH_MAX 1024
#endif
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
# undef PATH_MAX
# define PATH_MAX 260
#endif
int main ()
{
#ifdef PATH_MAX
  char origcwd[PATH_MAX];
  size_t subdir_len;
  char subdir[PATH_MAX];
  char testcwd[PATH_MAX];
  int result;
  /* Bail out if we cannot determine the current directory.  */
  if (getcwd (origcwd, PATH_MAX) == NULL)
    return 1;
  /* Bail out if the current directory is the root directory.  */
  if (strlen (origcwd) <= 1)
    return 2;
  subdir_len = PATH_MAX + 1 - strlen (origcwd); /* > 1, < PATH_MAX */
  /* Create a nested subdir whose relative file name has length
     subdir_len.  */
  if (subdir_len < 6)
    memcpy (subdir, "_XyZZy", subdir_len);
  else
    {
      size_t i;
      memcpy (subdir, "subdir", 6);
      for (i = 6; i < subdir_len; i++)
        subdir[i] = '0' + ((i - 6) % 10);
      for (i = 79; i < subdir_len; i += 80)
        subdir[i] = '\0';
    }
  subdir[subdir_len] = '\0';
  {
    const char *s;
    for (s = subdir; s <= subdir + subdir_len; s += strlen (s) + 1)
      {
        if (mkdir (s, 0700) < 0)
          return 3;
        if (chdir (s) < 0)
          return 4;
      }
  }
  /* The current directory is now the concatenation
       origcwd "/" subdir
     which NUL bytes in subdir replaced with '/'.
     Its length is strlen (origcwd) + 1 + subdir_len = PATH_MAX + 2.
     It can not fit in a buffer of size PATH_MAX.  */
  result = (getcwd (testcwd, PATH_MAX) != NULL ? 5 : 0);
  {
    const char *s;
    for (s = subdir + subdir_len; s >= subdir; )
      {
        while (s > subdir && s[-1] != '\0')
          s--;
        chdir ("..");
        rmdir (s);
        s--;
      }
  }
  return result;
#else
  return 0;
#endif
}
===============================================================================

Bruno
-- 
In memoriam Kerem Yılmazer <http://en.wikipedia.org/wiki/Kerem_Yılmazer>



reply via email to

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