(root)/
tar-1.35/
gnu/
careadlinkat.c
       1  /* Read symbolic links into a buffer without size limitation, relative to fd.
       2  
       3     Copyright (C) 2001, 2003-2004, 2007, 2009-2023 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  #include "allocator.h"
      39  
      40  enum { STACK_BUF_SIZE = 1024 };
      41  
      42  /* Act like careadlinkat (see below), with an additional argument
      43     STACK_BUF that can be used as temporary storage.
      44  
      45     If GCC_LINT is defined, do not inline this function with GCC 10.1
      46     and later, to avoid creating a pointer to the stack that GCC
      47     -Wreturn-local-addr incorrectly complains about.  See:
      48     https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93644
      49     Although the noinline attribute can hurt performance a bit, no better way
      50     to pacify GCC is known; even an explicit #pragma does not pacify GCC.
      51     When the GCC bug is fixed this workaround should be limited to the
      52     broken GCC versions.  */
      53  #if _GL_GNUC_PREREQ (10, 1)
      54  # if _GL_GNUC_PREREQ (12, 1)
      55  #  pragma GCC diagnostic ignored "-Wreturn-local-addr"
      56  # elif defined GCC_LINT || defined lint
      57  __attribute__ ((__noinline__))
      58  # elif __OPTIMIZE__ && !__NO_INLINE__
      59  #  define GCC_BOGUS_WRETURN_LOCAL_ADDR
      60  # endif
      61  #endif
      62  static char *
      63  readlink_stk (int fd, char const *filename,
      64                char *buffer, size_t buffer_size,
      65                struct allocator const *alloc,
      66                ssize_t (*preadlinkat) (int, char const *, char *, size_t),
      67                char stack_buf[STACK_BUF_SIZE])
      68  {
      69    if (! alloc)
      70      alloc = &stdlib_allocator;
      71  
      72    if (!buffer)
      73      {
      74        buffer = stack_buf;
      75        buffer_size = STACK_BUF_SIZE;
      76      }
      77  
      78    char *buf = buffer;
      79    idx_t buf_size_max = MIN (IDX_MAX, MIN (SSIZE_MAX, SIZE_MAX));
      80    idx_t buf_size = MIN (buffer_size, buf_size_max);
      81  
      82    while (buf)
      83      {
      84        /* Attempt to read the link into the current buffer.  */
      85        idx_t link_length = preadlinkat (fd, filename, buf, buf_size);
      86        if (link_length < 0)
      87          {
      88            if (buf != buffer)
      89              {
      90                int readlinkat_errno = errno;
      91                alloc->free (buf);
      92                errno = readlinkat_errno;
      93              }
      94            return NULL;
      95          }
      96  
      97        idx_t link_size = link_length;
      98  
      99        if (link_size < buf_size)
     100          {
     101            buf[link_size++] = '\0';
     102  
     103            if (buf == stack_buf)
     104              {
     105                char *b = alloc->allocate (link_size);
     106                buf_size = link_size;
     107                if (! b)
     108                  break;
     109                return memcpy (b, buf, link_size);
     110              }
     111  
     112            if (link_size < buf_size && buf != buffer && alloc->reallocate)
     113              {
     114                /* Shrink BUF before returning it.  */
     115                char *b = alloc->reallocate (buf, link_size);
     116                if (b)
     117                  return b;
     118              }
     119  
     120            return buf;
     121          }
     122  
     123        if (buf != buffer)
     124          alloc->free (buf);
     125  
     126        if (buf_size_max / 2 <= buf_size)
     127          {
     128            errno = ENAMETOOLONG;
     129            return NULL;
     130          }
     131  
     132        buf_size = 2 * buf_size + 1;
     133        buf = alloc->allocate (buf_size);
     134      }
     135  
     136    if (alloc->die)
     137      alloc->die (buf_size);
     138    errno = ENOMEM;
     139    return NULL;
     140  }
     141  
     142  
     143  /* Assuming the current directory is FD, get the symbolic link value
     144     of FILENAME as a null-terminated string and put it into a buffer.
     145     If FD is AT_FDCWD, FILENAME is interpreted relative to the current
     146     working directory, as in openat.
     147  
     148     If the link is small enough to fit into BUFFER put it there.
     149     BUFFER's size is BUFFER_SIZE, and BUFFER can be null
     150     if BUFFER_SIZE is zero.
     151  
     152     If the link is not small, put it into a dynamically allocated
     153     buffer managed by ALLOC.  It is the caller's responsibility to free
     154     the returned value if it is nonnull and is not BUFFER.  A null
     155     ALLOC stands for the standard allocator.
     156  
     157     The PREADLINKAT function specifies how to read links.  It operates
     158     like POSIX readlinkat()
     159     <https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html>
     160     but can assume that its first argument is the same as FD.
     161  
     162     If successful, return the buffer address; otherwise return NULL and
     163     set errno.  */
     164  
     165  char *
     166  careadlinkat (int fd, char const *filename,
     167                char *buffer, size_t buffer_size,
     168                struct allocator const *alloc,
     169                ssize_t (*preadlinkat) (int, char const *, char *, size_t))
     170  {
     171    /* Allocate the initial buffer on the stack.  This way, in the
     172       common case of a symlink of small size, we get away with a
     173       single small malloc instead of a big malloc followed by a
     174       shrinking realloc.  */
     175    #ifdef GCC_BOGUS_WRETURN_LOCAL_ADDR
     176     #warning "GCC might issue a bogus -Wreturn-local-addr warning here."
     177     #warning "See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93644>."
     178    #endif
     179    char stack_buf[STACK_BUF_SIZE];
     180    return readlink_stk (fd, filename, buffer, buffer_size, alloc,
     181                         preadlinkat, stack_buf);
     182  }