summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Meyering <jim@meyering.net>2006-03-19 18:27:51 +0000
committerJim Meyering <jim@meyering.net>2006-03-19 18:27:51 +0000
commit164d936ac297d08ba012b584ef7d9ea8f5335a9f (patch)
treee4dfdf65c1224af2d3120d3ebaf298d56062bea2
parente233777dbe95d8c8e4135a098503660c6dbaa451 (diff)
downloadcoreutils-164d936ac297d08ba012b584ef7d9ea8f5335a9f.tar.xz
Work even in a chroot where d_ino values for entries in "/"
don't match the stat.st_ino values for the same names. (__getcwd): When no d_ino value matches the target inode number, iterate through all entries again, using lstat instead. Reported by Kenshi Muto in http://bugs.debian.org/355810.
-rw-r--r--lib/getcwd.c119
1 files changed, 71 insertions, 48 deletions
diff --git a/lib/getcwd.c b/lib/getcwd.c
index 739ef44c7..ba226799d 100644
--- a/lib/getcwd.c
+++ b/lib/getcwd.c
@@ -211,6 +211,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,6 +258,21 @@ __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)
@@ -269,58 +285,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);
- }
-
- memcpy (dotlist + dotlen, d->d_name, _D_ALLOC_NAMLEN (d));
- 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;