* getcwd: Speed up on Linux. Add support for Android.
@ 2023-01-18 12:36 Bruno Haible
0 siblings, 0 replies; only message in thread
From: Bruno Haible @ 2023-01-18 12:36 UTC (permalink / raw)
On Android, in the Termux app, I see a test failure:
FAIL test-getcwd.sh (exit status: 5)
What happens, in the test_long_name() function of this test:
- The directory in which the test is run is
The peculiar circumstance is that the ancestor directories
are not readable (they produce an error EACCES).
- The test creates a hierarchy by doing 449 times chdir("confdir3"),
then call rpl_getcwd.
- rpl_getcwd first calls getcwd_system, which fails with error ENAMETOOLONG.
- Then rpl_getcwd goes to the parent directory 455 times. 454 times this
succeeds (up to /data/data/com.termux); then it fails (since /data/data
is not readable).
- At this point rpl_getcwd gives up and fails with errno EACCES.
But we can do better: Android uses the Linux kernel. Therefore it has getcwd
available as a system call, and this system call does not care about unreadable
ancestor directories. More precisely, we cannot use the getcwd system call
directly, because it would require chdir("..") calls and thus make our
rpl_getcwd function not multi-thread safe. But the /proc file system supports
a way to translate an fd to a file name, via readlink . That's what we need
So, the fix is to use this /proc file system trick repeatedly; it works as
soon as the directory name is at most 4095 bytes long.
This code makes the ".."-climbing loop a bit slower: after reading all
directory entries it now checks whether the readlink() check works. But
the advantage is that it terminates this loop much earlier than before
and thus saves dozens or hundreds of loop rounds. And in particular if
some of the ancestors are not readable, this won't make the loop fail.
2023-01-18 Bruno Haible <firstname.lastname@example.org>
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/lib/getcwd.c b/lib/getcwd.c
index a4f5c5eae3..5201cd06b5 100644
@@ -172,6 +172,9 @@ __getcwd_generic (char *buf, size_t size)
int fd = AT_FDCWD;
bool fd_needs_closing = false;
+# if defined __linux__
+ bool proc_fs_not_mounted = false;
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;
+ /* 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/<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;
+ 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;
+ dirroom = dirp - dir;
+ if (dirroom < linklen)
+ if (size != 0)
+ __set_errno (ERANGE);
+ goto lose;
+ 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);
if (dirstream && __closedir (dirstream) != 0)
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2023-01-18 12:37 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-18 12:36 getcwd: Speed up on Linux. Add support for Android Bruno Haible
Code repositories for project(s) associated with this public inbox
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).