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 <br...@clisp.org> 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 <br...@clisp.org> 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>