14536c563SJohn Marino /* Read symbolic links into a buffer without size limitation, relative to fd.
24536c563SJohn Marino 
3*6ea1f93eSDaniel Fojt    Copyright (C) 2001, 2003-2004, 2007, 2009-2018 Free Software Foundation,
44536c563SJohn Marino    Inc.
54536c563SJohn Marino 
64536c563SJohn Marino    This program is free software: you can redistribute it and/or modify
74536c563SJohn Marino    it under the terms of the GNU General Public License as published by
84536c563SJohn Marino    the Free Software Foundation; either version 3 of the License, or
94536c563SJohn Marino    (at your option) any later version.
104536c563SJohn Marino 
114536c563SJohn Marino    This program is distributed in the hope that it will be useful,
124536c563SJohn Marino    but WITHOUT ANY WARRANTY; without even the implied warranty of
134536c563SJohn Marino    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
144536c563SJohn Marino    GNU General Public License for more details.
154536c563SJohn Marino 
164536c563SJohn Marino    You should have received a copy of the GNU General Public License
17*6ea1f93eSDaniel Fojt    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
184536c563SJohn Marino 
194536c563SJohn Marino /* Written by Paul Eggert, Bruno Haible, and Jim Meyering.  */
204536c563SJohn Marino 
214536c563SJohn Marino #include <config.h>
224536c563SJohn Marino 
234536c563SJohn Marino #include "careadlinkat.h"
244536c563SJohn Marino 
254536c563SJohn Marino #include <errno.h>
264536c563SJohn Marino #include <limits.h>
274536c563SJohn Marino #include <string.h>
284536c563SJohn Marino #include <unistd.h>
294536c563SJohn Marino 
304536c563SJohn Marino /* Define this independently so that stdint.h is not a prerequisite.  */
314536c563SJohn Marino #ifndef SIZE_MAX
324536c563SJohn Marino # define SIZE_MAX ((size_t) -1)
334536c563SJohn Marino #endif
344536c563SJohn Marino 
354536c563SJohn Marino #ifndef SSIZE_MAX
364536c563SJohn Marino # define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
374536c563SJohn Marino #endif
384536c563SJohn Marino 
394536c563SJohn Marino #include "allocator.h"
404536c563SJohn Marino 
414536c563SJohn Marino /* Assuming the current directory is FD, get the symbolic link value
424536c563SJohn Marino    of FILENAME as a null-terminated string and put it into a buffer.
434536c563SJohn Marino    If FD is AT_FDCWD, FILENAME is interpreted relative to the current
444536c563SJohn Marino    working directory, as in openat.
454536c563SJohn Marino 
464536c563SJohn Marino    If the link is small enough to fit into BUFFER put it there.
474536c563SJohn Marino    BUFFER's size is BUFFER_SIZE, and BUFFER can be null
484536c563SJohn Marino    if BUFFER_SIZE is zero.
494536c563SJohn Marino 
504536c563SJohn Marino    If the link is not small, put it into a dynamically allocated
514536c563SJohn Marino    buffer managed by ALLOC.  It is the caller's responsibility to free
524536c563SJohn Marino    the returned value if it is nonnull and is not BUFFER.  A null
534536c563SJohn Marino    ALLOC stands for the standard allocator.
544536c563SJohn Marino 
554536c563SJohn Marino    The PREADLINKAT function specifies how to read links.  It operates
564536c563SJohn Marino    like POSIX readlinkat()
574536c563SJohn Marino    <http://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html>
584536c563SJohn Marino    but can assume that its first argument is the same as FD.
594536c563SJohn Marino 
604536c563SJohn Marino    If successful, return the buffer address; otherwise return NULL and
614536c563SJohn Marino    set errno.  */
624536c563SJohn Marino 
634536c563SJohn Marino char *
careadlinkat(int fd,char const * filename,char * buffer,size_t buffer_size,struct allocator const * alloc,ssize_t (* preadlinkat)(int,char const *,char *,size_t))644536c563SJohn Marino careadlinkat (int fd, char const *filename,
654536c563SJohn Marino               char *buffer, size_t buffer_size,
664536c563SJohn Marino               struct allocator const *alloc,
674536c563SJohn Marino               ssize_t (*preadlinkat) (int, char const *, char *, size_t))
684536c563SJohn Marino {
694536c563SJohn Marino   char *buf;
704536c563SJohn Marino   size_t buf_size;
714536c563SJohn Marino   size_t buf_size_max =
724536c563SJohn Marino     SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
734536c563SJohn Marino   char stack_buf[1024];
744536c563SJohn Marino 
754536c563SJohn Marino   if (! alloc)
764536c563SJohn Marino     alloc = &stdlib_allocator;
774536c563SJohn Marino 
784536c563SJohn Marino   if (! buffer_size)
794536c563SJohn Marino     {
804536c563SJohn Marino       /* Allocate the initial buffer on the stack.  This way, in the
814536c563SJohn Marino          common case of a symlink of small size, we get away with a
824536c563SJohn Marino          single small malloc() instead of a big malloc() followed by a
834536c563SJohn Marino          shrinking realloc().  */
844536c563SJohn Marino       buffer = stack_buf;
854536c563SJohn Marino       buffer_size = sizeof stack_buf;
864536c563SJohn Marino     }
874536c563SJohn Marino 
884536c563SJohn Marino   buf = buffer;
894536c563SJohn Marino   buf_size = buffer_size;
904536c563SJohn Marino 
914536c563SJohn Marino   do
924536c563SJohn Marino     {
934536c563SJohn Marino       /* Attempt to read the link into the current buffer.  */
944536c563SJohn Marino       ssize_t link_length = preadlinkat (fd, filename, buf, buf_size);
954536c563SJohn Marino       size_t link_size;
964536c563SJohn Marino       if (link_length < 0)
974536c563SJohn Marino         {
984536c563SJohn Marino           /* On AIX 5L v5.3 and HP-UX 11i v2 04/09, readlink returns -1
994536c563SJohn Marino              with errno == ERANGE if the buffer is too small.  */
1004536c563SJohn Marino           int readlinkat_errno = errno;
1014536c563SJohn Marino           if (readlinkat_errno != ERANGE)
1024536c563SJohn Marino             {
1034536c563SJohn Marino               if (buf != buffer)
1044536c563SJohn Marino                 {
1054536c563SJohn Marino                   alloc->free (buf);
1064536c563SJohn Marino                   errno = readlinkat_errno;
1074536c563SJohn Marino                 }
1084536c563SJohn Marino               return NULL;
1094536c563SJohn Marino             }
1104536c563SJohn Marino         }
1114536c563SJohn Marino 
1124536c563SJohn Marino       link_size = link_length;
1134536c563SJohn Marino 
1144536c563SJohn Marino       if (link_size < buf_size)
1154536c563SJohn Marino         {
1164536c563SJohn Marino           buf[link_size++] = '\0';
1174536c563SJohn Marino 
1184536c563SJohn Marino           if (buf == stack_buf)
1194536c563SJohn Marino             {
1204536c563SJohn Marino               char *b = (char *) alloc->allocate (link_size);
1214536c563SJohn Marino               buf_size = link_size;
1224536c563SJohn Marino               if (! b)
1234536c563SJohn Marino                 break;
1244536c563SJohn Marino               memcpy (b, buf, link_size);
1254536c563SJohn Marino               buf = b;
1264536c563SJohn Marino             }
1274536c563SJohn Marino           else if (link_size < buf_size && buf != buffer && alloc->reallocate)
1284536c563SJohn Marino             {
1294536c563SJohn Marino               /* Shrink BUF before returning it.  */
1304536c563SJohn Marino               char *b = (char *) alloc->reallocate (buf, link_size);
1314536c563SJohn Marino               if (b)
1324536c563SJohn Marino                 buf = b;
1334536c563SJohn Marino             }
1344536c563SJohn Marino 
1354536c563SJohn Marino           return buf;
1364536c563SJohn Marino         }
1374536c563SJohn Marino 
1384536c563SJohn Marino       if (buf != buffer)
1394536c563SJohn Marino         alloc->free (buf);
1404536c563SJohn Marino 
1414536c563SJohn Marino       if (buf_size <= buf_size_max / 2)
1424536c563SJohn Marino         buf_size *= 2;
1434536c563SJohn Marino       else if (buf_size < buf_size_max)
1444536c563SJohn Marino         buf_size = buf_size_max;
1454536c563SJohn Marino       else if (buf_size_max < SIZE_MAX)
1464536c563SJohn Marino         {
1474536c563SJohn Marino           errno = ENAMETOOLONG;
1484536c563SJohn Marino           return NULL;
1494536c563SJohn Marino         }
1504536c563SJohn Marino       else
1514536c563SJohn Marino         break;
1524536c563SJohn Marino       buf = (char *) alloc->allocate (buf_size);
1534536c563SJohn Marino     }
1544536c563SJohn Marino   while (buf);
1554536c563SJohn Marino 
1564536c563SJohn Marino   if (alloc->die)
1574536c563SJohn Marino     alloc->die (buf_size);
1584536c563SJohn Marino   errno = ENOMEM;
1594536c563SJohn Marino   return NULL;
1604536c563SJohn Marino }
161