(root)/
findutils-4.9.0/
gl/
lib/
careadlinkat.c
       1  /* Read symbolic links into a buffer without size limitation, relative to fd.
       2  
       3     Copyright (C) 2001, 2003-2004, 2007, 2009-2022 Free Software Foundation,
       4     Inc.
       5  
       6     This file is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU Lesser General Public License as
       8     published by the Free Software Foundation; either version 2.1 of the
       9     License, or (at your option) any later version.
      10  
      11     This file is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU Lesser General Public License for more details.
      15  
      16     You should have received a copy of the GNU Lesser General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  /* Written by Paul Eggert, Bruno Haible, and Jim Meyering.  */
      20  
      21  #include <config.h>
      22  
      23  #include "careadlinkat.h"
      24  
      25  #include "idx.h"
      26  #include "minmax.h"
      27  
      28  #include <errno.h>
      29  #include <limits.h>
      30  #include <string.h>
      31  #include <unistd.h>
      32  
      33  /* Define this independently so that stdint.h is not a prerequisite.  */
      34  #ifndef SIZE_MAX
      35  # define SIZE_MAX ((size_t) -1)
      36  #endif
      37  
      38  #ifndef SSIZE_MAX
      39  # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
      40  #endif
      41  
      42  #include "allocator.h"
      43  
      44  enum { STACK_BUF_SIZE = 1024 };
      45  
      46  /* Act like careadlinkat (see below), with an additional argument
      47     STACK_BUF that can be used as temporary storage.
      48  
      49     If GCC_LINT is defined, do not inline this function with GCC 10.1
      50     and later, to avoid creating a pointer to the stack that GCC
      51     -Wreturn-local-addr incorrectly complains about.  See:
      52     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93644
      53     Although the noinline attribute can hurt performance a bit, no better way
      54     to pacify GCC is known; even an explicit #pragma does not pacify GCC.
      55     When the GCC bug is fixed this workaround should be limited to the
      56     broken GCC versions.  */
      57  #if _GL_GNUC_PREREQ (10, 1)
      58  # if defined GCC_LINT || defined lint
      59  __attribute__ ((__noinline__))
      60  # elif __OPTIMIZE__ && !__NO_INLINE__
      61  #  define GCC_BOGUS_WRETURN_LOCAL_ADDR
      62  # endif
      63  #endif
      64  static char *
      65  readlink_stk (int fd, char const *filename,
      66                char *buffer, size_t buffer_size,
      67                struct allocator const *alloc,
      68                ssize_t (*preadlinkat) (int, char const *, char *, size_t),
      69                char stack_buf[STACK_BUF_SIZE])
      70  {
      71    if (! alloc)
      72      alloc = &stdlib_allocator;
      73  
      74    if (!buffer)
      75      {
      76        buffer = stack_buf;
      77        buffer_size = STACK_BUF_SIZE;
      78      }
      79  
      80    char *buf = buffer;
      81    idx_t buf_size_max = MIN (IDX_MAX, MIN (SSIZE_MAX, SIZE_MAX));
      82    idx_t buf_size = MIN (buffer_size, buf_size_max);
      83  
      84    while (buf)
      85      {
      86        /* Attempt to read the link into the current buffer.  */
      87        idx_t link_length = preadlinkat (fd, filename, buf, buf_size);
      88        if (link_length < 0)
      89          {
      90            if (buf != buffer)
      91              {
      92                int readlinkat_errno = errno;
      93                alloc->free (buf);
      94                errno = readlinkat_errno;
      95              }
      96            return NULL;
      97          }
      98  
      99        idx_t link_size = link_length;
     100  
     101        if (link_size < buf_size)
     102          {
     103            buf[link_size++] = '\0';
     104  
     105            if (buf == stack_buf)
     106              {
     107                char *b = alloc->allocate (link_size);
     108                buf_size = link_size;
     109                if (! b)
     110                  break;
     111                return memcpy (b, buf, link_size);
     112              }
     113  
     114            if (link_size < buf_size && buf != buffer && alloc->reallocate)
     115              {
     116                /* Shrink BUF before returning it.  */
     117                char *b = alloc->reallocate (buf, link_size);
     118                if (b)
     119                  return b;
     120              }
     121  
     122            return buf;
     123          }
     124  
     125        if (buf != buffer)
     126          alloc->free (buf);
     127  
     128        if (buf_size_max / 2 <= buf_size)
     129          {
     130            errno = ENAMETOOLONG;
     131            return NULL;
     132          }
     133  
     134        buf_size = 2 * buf_size + 1;
     135        buf = alloc->allocate (buf_size);
     136      }
     137  
     138    if (alloc->die)
     139      alloc->die (buf_size);
     140    errno = ENOMEM;
     141    return NULL;
     142  }
     143  
     144  
     145  /* Assuming the current directory is FD, get the symbolic link value
     146     of FILENAME as a null-terminated string and put it into a buffer.
     147     If FD is AT_FDCWD, FILENAME is interpreted relative to the current
     148     working directory, as in openat.
     149  
     150     If the link is small enough to fit into BUFFER put it there.
     151     BUFFER's size is BUFFER_SIZE, and BUFFER can be null
     152     if BUFFER_SIZE is zero.
     153  
     154     If the link is not small, put it into a dynamically allocated
     155     buffer managed by ALLOC.  It is the caller's responsibility to free
     156     the returned value if it is nonnull and is not BUFFER.  A null
     157     ALLOC stands for the standard allocator.
     158  
     159     The PREADLINKAT function specifies how to read links.  It operates
     160     like POSIX readlinkat()
     161     <https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html>
     162     but can assume that its first argument is the same as FD.
     163  
     164     If successful, return the buffer address; otherwise return NULL and
     165     set errno.  */
     166  
     167  char *
     168  careadlinkat (int fd, char const *filename,
     169                char *buffer, size_t buffer_size,
     170                struct allocator const *alloc,
     171                ssize_t (*preadlinkat) (int, char const *, char *, size_t))
     172  {
     173    /* Allocate the initial buffer on the stack.  This way, in the
     174       common case of a symlink of small size, we get away with a
     175       single small malloc instead of a big malloc followed by a
     176       shrinking realloc.  */
     177    #ifdef GCC_BOGUS_WRETURN_LOCAL_ADDR
     178     #warning "GCC might issue a bogus -Wreturn-local-addr warning here."
     179     #warning "See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93644>."
     180    #endif
     181    char stack_buf[STACK_BUF_SIZE];
     182    return readlink_stk (fd, filename, buffer, buffer_size, alloc,
     183                         preadlinkat, stack_buf);
     184  }