[Top][All Lists]
[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>
- getcwd on AIX,
Bruno Haible <=