diff options
Diffstat (limited to 'lib/careadlinkat.c')
-rw-r--r-- | lib/careadlinkat.c | 42 |
1 files changed, 28 insertions, 14 deletions
diff --git a/lib/careadlinkat.c b/lib/careadlinkat.c index 197ce8de77f..1aa04363dac 100644 --- a/lib/careadlinkat.c +++ b/lib/careadlinkat.c @@ -1,7 +1,7 @@ /* Read symbolic links into a buffer without size limitation, relative to fd. - Copyright (C) 2001, 2003-2004, 2007, 2009-2020 Free Software - Foundation, Inc. + Copyright (C) 2001, 2003-2004, 2007, 2009-2020 Free Software Foundation, + Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -72,23 +72,38 @@ careadlinkat (int fd, char const *filename, SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX; char stack_buf[1024]; +#if (defined GCC_LINT || defined lint) && _GL_GNUC_PREREQ (10, 1) + /* Pacify preadlinkat without creating a pointer to the stack + that a broken gcc -Wreturn-local-addr would cry wolf about. See: + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95044 + This workaround differs from the mainline code, but + no other way to pacify GCC 10.1.0 is known; even an explicit + #pragma does not pacify GCC. When the GCC bug is fixed this + workaround should be limited to the broken GCC versions. */ +# define WORK_AROUND_GCC_BUG_95044 +#endif + if (! alloc) alloc = &stdlib_allocator; - if (! buffer_size) + if (!buffer) { +#ifdef WORK_AROUND_GCC_BUG_95044 + buffer = alloc->allocate (sizeof stack_buf); +#else /* Allocate the initial buffer on the stack. This way, in the common case of a symlink of small size, we get away with a single small malloc() instead of a big malloc() followed by a shrinking realloc(). */ buffer = stack_buf; +#endif buffer_size = sizeof stack_buf; } buf = buffer; buf_size = buffer_size; - do + while (buf) { /* Attempt to read the link into the current buffer. */ ssize_t link_length = preadlinkat (fd, filename, buf, buf_size); @@ -117,19 +132,19 @@ careadlinkat (int fd, char const *filename, if (buf == stack_buf) { - char *b = (char *) alloc->allocate (link_size); + char *b = alloc->allocate (link_size); buf_size = link_size; if (! b) break; - memcpy (b, buf, link_size); - buf = b; + return memcpy (b, buf, link_size); } - else if (link_size < buf_size && buf != buffer && alloc->reallocate) + + if (link_size < buf_size && buf != buffer && alloc->reallocate) { /* Shrink BUF before returning it. */ - char *b = (char *) alloc->reallocate (buf, link_size); + char *b = alloc->reallocate (buf, link_size); if (b) - buf = b; + return b; } return buf; @@ -138,8 +153,8 @@ careadlinkat (int fd, char const *filename, if (buf != buffer) alloc->free (buf); - if (buf_size <= buf_size_max / 2) - buf_size *= 2; + if (buf_size < buf_size_max / 2) + buf_size = 2 * buf_size + 1; else if (buf_size < buf_size_max) buf_size = buf_size_max; else if (buf_size_max < SIZE_MAX) @@ -149,9 +164,8 @@ careadlinkat (int fd, char const *filename, } else break; - buf = (char *) alloc->allocate (buf_size); + buf = alloc->allocate (buf_size); } - while (buf); if (alloc->die) alloc->die (buf_size); |