]> Savannah Git Hosting - gnulib.git/commitdiff
getcwd: Speed up on Linux. Add support for Android.
authorBruno Haible <bruno@clisp.org>
Wed, 18 Jan 2023 12:16:42 +0000 (13:16 +0100)
committerBruno Haible <bruno@clisp.org>
Wed, 18 Jan 2023 12:16:42 +0000 (13:16 +0100)
* lib/getcwd.c (__getcwd_generic): On Linux, use a specific readlink
call to speed up the operation.

ChangeLog
lib/getcwd.c

index 79e9da5d222be5ad7ea9cb902c04ab191451d719..854820e5cfbde80a0e31699fa6929967716dc679 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2023-01-18  Bruno Haible  <bruno@clisp.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.
+
 2023-01-17  Paul Eggert  <eggert@cs.ucla.edu>
 
        qcopy-acl: port to Fedora 9
index a4f5c5eae3922bace8597d30c9d8363a28e6375c..5201cd06b595cead0304423c9d652586befacaa9 100644 (file)
@@ -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/<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)