On 03/07/19 21:24, Andreas Dilger wrote:
> When calling 'stat -c %N' to print the filename, don't explicitly
> request the size of the file via statx(), as it may add overhead on
> some filesystems.  The size is only needed to optimize an allocation
> for the relatively rare case of reading a symlink name, and the worst
> effect is a somewhat-too-large temporary buffer may be allocated for
> areadlink_with_size(), or internal retries if buffer is too small.
> 
> The file size will be returned by statx() on most filesystems, even
> if not requested, unless the filesystem considers this to be too
> expensive for that file, in which case the tradeoff is worthwhile.
> 
> * src/stat.c: Don't explicitly request STATX_SIZE for filenames.
> Start with a 1KB buffer for areadlink_with_size() if st_size unset.
> ---
>  src/stat.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/src/stat.c b/src/stat.c
> index ec0bb7d..c887013 100644
> --- a/src/stat.c
> +++ b/src/stat.c
> @@ -1282,7 +1282,7 @@ fmt_to_mask (char fmt)
>    switch (fmt)
>      {
>      case 'N':
> -      return STATX_MODE|STATX_SIZE;
> +      return STATX_MODE;
>      case 'd':
>      case 'D':
>        return STATX_MODE;
> @@ -1491,7 +1491,9 @@ print_stat (char *pformat, size_t prefix_len, unsigned 
> int m,
>        out_string (pformat, prefix_len, quoteN (filename));
>        if (S_ISLNK (statbuf->st_mode))
>          {
> -          char *linkname = areadlink_with_size (filename, statbuf->st_size);
> +          /* if statx() didn't set size, most symlinks are under 1KB */
> +          char *linkname = areadlink_with_size (filename, statbuf->st_size ?:
> +                                                1023);

It would be nice to have areadlink_with_size treat 0 as auto select some lower 
bound.
There is already logic there, and it would be generally helpful,
as st_size can often be 0, as shown with:

  $ 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

With the attached gnulib diff, we get:

  $ strace -e readlink git/coreutils/src/stat -c %N /proc/$$/cwd
  readlink("/proc/12512/cwd", "/home/padraig", 1024) = 13

I'll push both later.

thanks!
Pádraig
diff --git a/lib/areadlink-with-size.c b/lib/areadlink-with-size.c
index eacad3f..2fbe51c 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).  */
@@ -61,7 +62,7 @@ areadlink_with_size (char const *file, size_t size)
                           : INITIAL_LIMIT_BOUND);
 
   /* The initial buffer size for the link value.  */
-  size_t buf_size = size < initial_limit ? size + 1 : initial_limit;
+  size_t buf_size = size && size < initial_limit ? size + 1 : initial_limit;
 
   while (1)
     {

Reply via email to