From: Bruno Haible Date: Wed, 18 Jan 2023 12:16:42 +0000 (+0100) Subject: getcwd: Speed up on Linux. Add support for Android. X-Git-Tag: v1.0~1815 X-Git-Url: https://gitweb.git.savannah.gnu.org/gitweb/?a=commitdiff_plain;h=356a414e8c15ef3f8cc7b7157427c8ce9a9f7c1b;p=gnulib.git getcwd: Speed up on Linux. Add support for Android. * lib/getcwd.c (__getcwd_generic): On Linux, use a specific readlink call to speed up the operation. --- diff --git a/ChangeLog b/ChangeLog index 79e9da5d22..854820e5cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-01-18 Bruno Haible + + getcwd: Speed up on Linux. Add support for Android. + * lib/getcwd.c (__getcwd_generic): On Linux, use a specific readlink + call to speed up the operation. + 2023-01-17 Paul Eggert qcopy-acl: port to Fedora 9 diff --git a/lib/getcwd.c b/lib/getcwd.c index a4f5c5eae3..5201cd06b5 100644 --- a/lib/getcwd.c +++ b/lib/getcwd.c @@ -172,6 +172,9 @@ __getcwd_generic (char *buf, size_t size) #if HAVE_OPENAT_SUPPORT int fd = AT_FDCWD; bool fd_needs_closing = false; +# if defined __linux__ + bool proc_fs_not_mounted = false; +# endif #else char dots[DEEP_NESTING * sizeof ".." + BIG_FILE_NAME_COMPONENT_LENGTH + 1]; char *dotlist = dots; @@ -437,6 +440,67 @@ __getcwd_generic (char *buf, size_t size) thisdev = dotdev; thisino = dotino; + +#if HAVE_OPENAT_SUPPORT + /* On some platforms, a system call returns the directory that FD points + to. This is useful if some of the ancestor directories of the + directory are unreadable, because in this situation the loop that + climbs up the ancestor hierarchy runs into an EACCES error. + For example, in some Android app, /data/data/com.termux is readable, + but /data/data and /data are not. */ +# if defined __linux__ + /* On Linux, in particular, if /proc is mounted, + readlink ("/proc/self/fd/") + returns the directory, if its length is < 4096. (If the length is + >= 4096, it fails with error ENAMETOOLONG, even if the buffer that we + pass to the readlink function would be large enough.) */ + if (!proc_fs_not_mounted) + { + char namebuf[14 + 10 + 1]; + sprintf (namebuf, "/proc/self/fd/%u", (unsigned int) fd); + char linkbuf[4096]; + ssize_t linklen = readlink (namebuf, linkbuf, sizeof linkbuf); + if (linklen < 0) + { + if (errno != ENAMETOOLONG) + /* If this call was not successful, the next one will likely be + not successful either. */ + proc_fs_not_mounted = true; + } + else + { + dirroom = dirp - dir; + if (dirroom < linklen) + { + if (size != 0) + { + __set_errno (ERANGE); + goto lose; + } + else + { + char *tmp; + size_t oldsize = allocated; + + allocated += linklen - dirroom; + if (allocated < oldsize + || ! (tmp = realloc (dir, allocated))) + goto memory_exhausted; + + /* Move current contents up to the end of the buffer. */ + dirp = memmove (tmp + dirroom + (allocated - oldsize), + tmp + dirroom, + oldsize - dirroom); + dir = tmp; + } + } + dirp -= linklen; + memcpy (dirp, linkbuf, linklen); + break; + } + } +# endif +#endif } if (dirstream && __closedir (dirstream) != 0)