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