From 9955c9456df52505b657b09b9caca1588b36f4ba Mon Sep 17 00:00:00 2001
From: Andreas Dilger <adilger@whamcloud.com>
Date: Thu, 4 Jul 2019 15:28:15 -0600
Subject: [PATCH] areadlink_with_size: handle size == 0 better

If size is unset/zero when passed to areadlink_with_size(),
the buffer allocation and while loop start out with buf_size = 1
and in most cases symlink length is considerably larger so there
are several pointless loops while the buffer size is grown:

    $ strace -e readlink stat -c %N /proc/$$/cwd
     readlink("/proc/9036/cwd", "/", 1)      = 1
     readlink("/proc/9036/cwd", "/h", 2)     = 2
     readlink("/proc/9036/cwd", "/hom", 4)   = 4
     readlink("/proc/9036/cwd", "/home/pa", 8) = 8
     readlink("/proc/9036/cwd", "/home/padraig", 16) = 13

Also, there is no point in allocating a buffer under 32 bytes.

* src/areadlink-with-size.c: initialize buf_size=32 if size=0
---
 lib/areadlink-with-size.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/lib/areadlink-with-size.c b/lib/areadlink-with-size.c
index eacad3f..b49e05c 100644
--- a/lib/areadlink-with-size.c
+++ b/lib/areadlink-with-size.c
@@ -36,14 +36,15 @@
    check, so it's OK to guess too small on hosts where there is no
    arbitrary limit to symbolic link length.  */
 #ifndef SYMLINK_MAX
-# define SYMLINK_MAX 1024
+# define SYMLINK_MAX 1023
 #endif
 
 #define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX)
 
 /* Call readlink to get the symbolic link value of FILE.
    SIZE is a hint as to how long the link is expected to be;
-   typically it is taken from st_size.  It need not be correct.
+   typically it is taken from st_size.  It need not be correct, and a
+   value of 0 (or more than 8Ki) will select an appropriate lower bound.
    Return a pointer to that NUL-terminated string in malloc'd storage.
    If readlink fails, malloc fails, or if the link value is longer
    than SSIZE_MAX, return NULL (caller may use errno to diagnose).  */
@@ -60,8 +61,10 @@ areadlink_with_size (char const *file, size_t size)
                           ? symlink_max + 1
                           : INITIAL_LIMIT_BOUND);
 
-  /* The initial buffer size for the link value.  */
-  size_t buf_size = size < initial_limit ? size + 1 : initial_limit;
+  /* The initial buffer size for the link value. If size is unset, there is
+     no benefit to malloc() a buffer under 32 bytes, and it avoids several
+     iterations of the while loop compared to starting with size == 1.  */
+  size_t buf_size = size < initial_limit ? (size ?: 31) + 1 : initial_limit;
 
   while (1)
     {
-- 
2.4.5

