X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fgetcwd.c;h=614ded18d59d173f0246643a9f18a3fa814261df;hb=1c01f30b6b6a4b087e613f7122095058bfa36c54;hp=5bb9e4be14eb61de5be91734fb4cacd065624d56;hpb=7e69cc2d1b5885537c8bdf028c2d5407a1f210fd;p=gnulib.git diff --git a/lib/getcwd.c b/lib/getcwd.c index 5bb9e4be1..614ded18d 100644 --- a/lib/getcwd.c +++ b/lib/getcwd.c @@ -36,25 +36,9 @@ # define __set_errno(val) (errno = (val)) #endif -#if HAVE_DIRENT_H || _LIBC -# include -# ifndef _D_EXACT_NAMLEN -# define _D_EXACT_NAMLEN(d) strlen ((d)->d_name) -# endif -#else -# define dirent direct -# if HAVE_SYS_NDIR_H -# include -# endif -# if HAVE_SYS_DIR_H -# include -# endif -# if HAVE_NDIR_H -# include -# endif -#endif +#include #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) @@ -211,6 +195,7 @@ __getcwd (char *buf, size_t size) int parent_status; size_t dirroom; size_t namlen; + bool use_d_ino = true; /* Look at the parent directory. */ #ifdef AT_FDCWD @@ -257,11 +242,26 @@ __getcwd (char *buf, size_t size) 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 means that the current directory - has been removed. */ + /* EOF on dirstream, which can mean e.g., that the current + directory has been removed. */ __set_errno (ENOENT); goto lose; } @@ -269,58 +269,65 @@ __getcwd (char *buf, size_t size) (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; + } } dirroom = dirp - dir;