SCNX* macros don't exist.
[gnulib.git] / lib / getcwd.c
index 94e5ba3..614ded1 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005 Free Software
+/* Copyright (C) 1991,92,93,94,95,96,97,98,99,2004,2005,2006 Free Software
    Foundation, Inc.
    This file is part of the GNU C Library.
 
@@ -16,8 +16,8 @@
    with this program; if not, write to the Free Software Foundation,
    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
 
-#ifdef HAVE_CONFIG_H
-# include "config.h"
+#ifdef HAVE_CONFIG_H
+# include <config.h>
 #endif
 
 #if !_LIBC
 #include <stdbool.h>
 #include <stddef.h>
 
-#if HAVE_FCNTL_H
-# include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
-#endif
+#include <fcntl.h> /* For AT_FDCWD on Solaris 9.  */
 
 #ifndef __set_errno
 # define __set_errno(val) (errno = (val))
 #endif
 
-#if HAVE_DIRENT_H || _LIBC
-# include <dirent.h>
-# ifndef _D_EXACT_NAMLEN
-#  define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
-# endif
-#else
-# define dirent direct
-# if HAVE_SYS_NDIR_H
-#  include <sys/ndir.h>
-# endif
-# if HAVE_SYS_DIR_H
-#  include <sys/dir.h>
-# endif
-# if HAVE_NDIR_H
-#  include <ndir.h>
-# endif
-#endif
+#include <dirent.h>
 #ifndef _D_EXACT_NAMLEN
-# define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
+# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name)
 #endif
 #ifndef _D_ALLOC_NAMLEN
 # define _D_ALLOC_NAMLEN(d) (_D_EXACT_NAMLEN (d) + 1)
 #endif
 
-#if HAVE_UNISTD_H || _LIBC
-# include <unistd.h>
-#endif
-
+#include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
 
 
 #include <limits.h>
 
+/* Work around a bug in Solaris 9 and 10: AT_FDCWD is positive.  Its
+   value exceeds INT_MAX, so its use as an int doesn't conform to the
+   C standard, and GCC and Sun C complain in some cases.  */
+#if 0 < AT_FDCWD && AT_FDCWD == 0xffd19553
+# undef AT_FDCWD
+# define AT_FDCWD (-3041965)
+#endif
+
 #ifdef ENAMETOOLONG
 # define is_ENAMETOOLONG(x) ((x) == ENAMETOOLONG)
 #else
@@ -206,6 +193,9 @@ __getcwd (char *buf, size_t size)
       ino_t dotino;
       bool mount_point;
       int parent_status;
+      size_t dirroom;
+      size_t namlen;
+      bool use_d_ino = true;
 
       /* Look at the parent directory.  */
 #ifdef AT_FDCWD
@@ -246,110 +236,131 @@ __getcwd (char *buf, size_t size)
        goto lose;
       dotlist[dotlen++] = '/';
 #endif
-      /* Clear errno to distinguish EOF from error if readdir returns
-        NULL.  */
-      __set_errno (0);
-      while ((d = __readdir (dirstream)) != NULL)
+      for (;;)
        {
+         /* Clear errno to distinguish EOF from error if readdir returns
+            NULL.  */
+         __set_errno (0);
+         d = __readdir (dirstream);
+
+         /* When we've iterated through all directory entries without finding
+            one with a matching d_ino, rewind the stream and consider each
+            name again, but this time, using lstat.  This is necessary in a
+            chroot on at least one system (glibc-2.3.6 + linux 2.6.12), where
+            .., ../.., ../../.., etc. all had the same device number, yet the
+            d_ino values for entries in / did not match those obtained
+            via lstat.  */
+         if (d == NULL && errno == 0 && use_d_ino)
+           {
+             use_d_ino = false;
+             rewinddir (dirstream);
+             d = __readdir (dirstream);
+           }
+
+         if (d == NULL)
+           {
+             if (errno == 0)
+               /* EOF on dirstream, which can mean e.g., that the current
+                  directory has been removed.  */
+               __set_errno (ENOENT);
+             goto lose;
+           }
          if (d->d_name[0] == '.' &&
              (d->d_name[1] == '\0' ||
               (d->d_name[1] == '.' && d->d_name[2] == '\0')))
            continue;
-         if (MATCHING_INO (d, thisino) || mount_point)
+
+         if (use_d_ino)
            {
-             int entry_status;
+             bool match = (MATCHING_INO (d, thisino) || mount_point);
+             if (! match)
+               continue;
+           }
+
+         {
+           int entry_status;
 #ifdef AT_FDCWD
-             entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
+           entry_status = fstatat (fd, d->d_name, &st, AT_SYMLINK_NOFOLLOW);
 #else
-             /* Compute size needed for this file name, or for the file
-                name ".." in the same directory, whichever is larger.
-                Room for ".." might be needed the next time through
-                the outer loop.  */
-             size_t name_alloc = _D_ALLOC_NAMLEN (d);
-             size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
-
-             if (filesize < dotlen)
-               goto memory_exhausted;
-
-             if (dotsize < filesize)
-               {
-                 /* My, what a deep directory tree you have, Grandma.  */
-                 size_t newsize = MAX (filesize, dotsize * 2);
-                 size_t i;
-                 if (newsize < dotsize)
-                   goto memory_exhausted;
-                 if (dotlist != dots)
-                   free (dotlist);
-                 dotlist = malloc (newsize);
-                 if (dotlist == NULL)
-                   goto lose;
-                 dotsize = newsize;
-
-                 i = 0;
-                 do
-                   {
-                     dotlist[i++] = '.';
-                     dotlist[i++] = '.';
-                     dotlist[i++] = '/';
-                   }
-                 while (i < dotlen);
-               }
-
-             strcpy (dotlist + dotlen, d->d_name);
-             entry_status = __lstat (dotlist, &st);
+           /* Compute size needed for this file name, or for the file
+              name ".." in the same directory, whichever is larger.
+              Room for ".." might be needed the next time through
+              the outer loop.  */
+           size_t name_alloc = _D_ALLOC_NAMLEN (d);
+           size_t filesize = dotlen + MAX (sizeof "..", name_alloc);
+
+           if (filesize < dotlen)
+             goto memory_exhausted;
+
+           if (dotsize < filesize)
+             {
+               /* My, what a deep directory tree you have, Grandma.  */
+               size_t newsize = MAX (filesize, dotsize * 2);
+               size_t i;
+               if (newsize < dotsize)
+                 goto memory_exhausted;
+               if (dotlist != dots)
+                 free (dotlist);
+               dotlist = malloc (newsize);
+               if (dotlist == NULL)
+                 goto lose;
+               dotsize = newsize;
+
+               i = 0;
+               do
+                 {
+                   dotlist[i++] = '.';
+                   dotlist[i++] = '.';
+                   dotlist[i++] = '/';
+                 }
+               while (i < dotlen);
+             }
+
+           memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
+           entry_status = __lstat (dotlist, &st);
 #endif
-             /* We don't fail here if we cannot stat() a directory entry.
-                This can happen when (network) file systems fail.  If this
-                entry is in fact the one we are looking for we will find
-                out soon as we reach the end of the directory without
-                having found anything.  */
-             if (entry_status == 0 && S_ISDIR (st.st_mode)
-                 && st.st_dev == thisdev && st.st_ino == thisino)
-               break;
-           }
+           /* We don't fail here if we cannot stat() a directory entry.
+              This can happen when (network) file systems fail.  If this
+              entry is in fact the one we are looking for we will find
+              out soon as we reach the end of the directory without
+              having found anything.  */
+           if (entry_status == 0 && S_ISDIR (st.st_mode)
+               && st.st_dev == thisdev && st.st_ino == thisino)
+             break;
+         }
        }
-      if (d == NULL)
-       {
-         if (errno == 0)
-           /* EOF on dirstream, which means that the current directory
-              has been removed.  */
-           __set_errno (ENOENT);
-         goto lose;
-       }
-      else
-       {
-         size_t dirroom = dirp - dir;
-         size_t namlen = _D_EXACT_NAMLEN (d);
 
-         if (dirroom <= namlen)
+      dirroom = dirp - dir;
+      namlen = _D_EXACT_NAMLEN (d);
+
+      if (dirroom <= namlen)
+       {
+         if (size != 0)
            {
-             if (size != 0)
-               {
-                 __set_errno (ERANGE);
-                 goto lose;
-               }
-             else
-               {
-                 char *tmp;
-                 size_t oldsize = allocated;
-
-                 allocated += MAX (allocated, namlen);
-                 if (allocated < oldsize
-                     || ! (tmp = realloc (dir, allocated)))
-                   goto memory_exhausted;
-
-                 /* Move current contents up to the end of the buffer.
-                    This is guaranteed to be non-overlapping.  */
-                 dirp = memcpy (tmp + allocated - (oldsize - dirroom),
-                                tmp + dirroom,
-                                oldsize - dirroom);
-                 dir = tmp;
-               }
+             __set_errno (ERANGE);
+             goto lose;
+           }
+         else
+           {
+             char *tmp;
+             size_t oldsize = allocated;
+
+             allocated += MAX (allocated, namlen);
+             if (allocated < oldsize
+                 || ! (tmp = realloc (dir, allocated)))
+               goto memory_exhausted;
+
+             /* Move current contents up to the end of the buffer.
+                This is guaranteed to be non-overlapping.  */
+             dirp = memcpy (tmp + allocated - (oldsize - dirroom),
+                            tmp + dirroom,
+                            oldsize - dirroom);
+             dir = tmp;
            }
-         dirp -= namlen;
-         memcpy (dirp, d->d_name, namlen);
-         *--dirp = '/';
        }
+      dirp -= namlen;
+      memcpy (dirp, d->d_name, namlen);
+      *--dirp = '/';
 
       thisdev = dotdev;
       thisino = dotino;