X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fgetcwd.c;h=7d47072101e7fdc4d339c8cd77a92b115c89afe6;hb=9a09e8291d1bd692b26684c2bb7d9379593e8846;hp=6658ed58c0c4982bd073090956bec8c30ea15a56;hpb=bd24844d02c2add6a689c2963d35b9f9bb216147;p=gnulib.git diff --git a/lib/getcwd.c b/lib/getcwd.c index 6658ed58c..7d4707210 100644 --- a/lib/getcwd.c +++ b/lib/getcwd.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991-1999, 2004-2009 Free Software Foundation, Inc. +/* Copyright (C) 1991-1999, 2004-2010 Free Software Foundation, Inc. This file is part of the GNU C Library. This program is free software: you can redistribute it and/or modify @@ -67,7 +67,7 @@ #endif #ifndef PATH_MAX -# ifdef MAXPATHLEN +# ifdef MAXPATHLEN # define PATH_MAX MAXPATHLEN # else # define PATH_MAX 1024 @@ -136,13 +136,11 @@ __getcwd (char *buf, size_t size) size_t allocated = size; size_t used; -#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 - 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 look - suspicious. +#if HAVE_RAW_DECL_GETCWD + /* 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 + look suspicious. Use the system getcwd even if we have openat support, since the system getcwd works even when a parent is unreadable, while the @@ -150,17 +148,36 @@ __getcwd (char *buf, size_t size) # undef getcwd dir = getcwd (buf, size); - if (dir || (errno != ERANGE && errno != ENAMETOOLONG && errno != ENOENT)) + if (dir) 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) { if (buf != NULL) - { - __set_errno (EINVAL); - return NULL; - } + { + __set_errno (EINVAL); + return NULL; + } allocated = BIG_FILE_NAME_LENGTH + 1; } @@ -169,7 +186,7 @@ __getcwd (char *buf, size_t size) { dir = malloc (allocated); if (dir == NULL) - return NULL; + return NULL; } else dir = buf; @@ -202,7 +219,7 @@ __getcwd (char *buf, size_t size) #if HAVE_OPENAT_SUPPORT fd = openat (fd, "..", O_RDONLY); if (fd < 0) - goto lose; + goto lose; fd_needs_closing = true; parent_status = fstat (fd, &st); #else @@ -212,13 +229,13 @@ __getcwd (char *buf, size_t size) parent_status = __lstat (dotlist, &st); #endif if (parent_status != 0) - goto lose; + goto lose; if (dirstream && __closedir (dirstream) != 0) - { - dirstream = NULL; - goto lose; - } + { + dirstream = NULL; + goto lose; + } /* Figure out if this directory is a mount point. */ dotdev = st.st_dev; @@ -229,138 +246,138 @@ __getcwd (char *buf, size_t size) #if HAVE_OPENAT_SUPPORT dirstream = fdopendir (fd); if (dirstream == NULL) - goto lose; + goto lose; /* Reset fd. It may have been closed by fdopendir. */ fd = dirfd (dirstream); fd_needs_closing = false; #else dirstream = __opendir (dotlist); if (dirstream == NULL) - goto lose; + goto lose; dotlist[dotlen++] = '/'; #endif 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 (use_d_ino) - { - bool match = (MATCHING_INO (d, thisino) || mount_point); - if (! match) - continue; - } - - { - int entry_status; + { + /* 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 (use_d_ino) + { + bool match = (MATCHING_INO (d, thisino) || mount_point); + if (! match) + continue; + } + + { + int entry_status; #if HAVE_OPENAT_SUPPORT - 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; namlen = _D_EXACT_NAMLEN (d); if (dirroom <= namlen) - { - 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; - } - } + { + 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; + } + } dirp -= namlen; memcpy (dirp, d->d_name, namlen); *--dirp = '/';