diff options
author | Philipp Stephani <phst@google.com> | 2020-12-24 16:48:40 +0100 |
---|---|---|
committer | Philipp Stephani <phst@google.com> | 2020-12-24 16:48:40 +0100 |
commit | 29064d02c31b08ae41d41a93fd1439718373b196 (patch) | |
tree | 6c976729d7c6f296b36965f235d2a58e8142393e /lib/canonicalize-lgpl.c | |
parent | 26b8b30ff42568ff3e3f8599a20328a1efe93d2a (diff) | |
download | emacs-29064d02c31b08ae41d41a93fd1439718373b196.tar.gz |
Update Gnulib.
All changes in this commit are autogenerated by running
admin/merge-gnulib.
Diffstat (limited to 'lib/canonicalize-lgpl.c')
-rw-r--r-- | lib/canonicalize-lgpl.c | 381 |
1 files changed, 168 insertions, 213 deletions
diff --git a/lib/canonicalize-lgpl.c b/lib/canonicalize-lgpl.c index 0b89d2a1842..584fce1cfa4 100644 --- a/lib/canonicalize-lgpl.c +++ b/lib/canonicalize-lgpl.c @@ -2,18 +2,19 @@ Copyright (C) 1996-2020 Free Software Foundation, Inc. This file is part of the GNU C Library. - 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 - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. - This program is distributed in the hope that it will be useful, + The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. */ + You should have received a copy of the GNU General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ #ifndef _LIBC /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc @@ -21,36 +22,36 @@ # define _GL_ARG_NONNULL(params) # define _GL_USE_STDLIB_ALLOC 1 -# include <config.h> +# include <libc-config.h> #endif -#if !HAVE_CANONICALIZE_FILE_NAME || !FUNC_REALPATH_WORKS || defined _LIBC - /* Specification. */ #include <stdlib.h> -#include <alloca.h> -#include <string.h> -#include <unistd.h> -#include <limits.h> -#if HAVE_SYS_PARAM_H || defined _LIBC -# include <sys/param.h> -#endif -#include <sys/stat.h> #include <errno.h> +#include <limits.h> +#include <stdbool.h> #include <stddef.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <scratch_buffer.h> #ifdef _LIBC +# include <eloop-threshold.h> # include <shlib-compat.h> +typedef ptrdiff_t idx_t; +# define IDX_MAX PTRDIFF_MAX +# define FILE_SYSTEM_PREFIX_LEN(name) 0 +# define IS_ABSOLUTE_FILE_NAME(name) ISSLASH(*(name)) +# define ISSLASH(c) ((c) == '/') +# define freea(p) ((void) (p)) #else -# define SHLIB_COMPAT(lib, introduced, obsoleted) 0 -# define versioned_symbol(lib, local, symbol, version) extern int dummy -# define compat_symbol(lib, local, symbol, version) -# define weak_alias(local, symbol) # define __canonicalize_file_name canonicalize_file_name # define __realpath realpath +# include "idx.h" # include "pathmax.h" -# include "malloca.h" # include "filename.h" # if defined _WIN32 && !defined __CYGWIN__ # define __getcwd _getcwd @@ -72,8 +73,10 @@ # else # define __getcwd(buf, max) getwd (buf) # endif +# define __mempcpy mempcpy +# define __pathconf pathconf +# define __rawmemchr rawmemchr # define __readlink readlink -# define __set_errno(e) errno = (e) # ifndef MAXSYMLINKS # ifdef SYMLOOP_MAX # define MAXSYMLINKS SYMLOOP_MAX @@ -81,48 +84,51 @@ # define MAXSYMLINKS 20 # endif # endif +# define __eloop_threshold() MAXSYMLINKS #endif #ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT # define DOUBLE_SLASH_IS_DISTINCT_ROOT 0 #endif -/* Define this independently so that stdint.h is not a prerequisite. */ -#ifndef SIZE_MAX -# define SIZE_MAX ((size_t) -1) -#endif - #if !FUNC_REALPATH_WORKS || defined _LIBC -static void -alloc_failed (void) +static idx_t +get_path_max (void) { -#if defined _WIN32 && ! defined __CYGWIN__ - /* Avoid errno problem without using the malloc or realloc modules; see: - https://lists.gnu.org/r/bug-gnulib/2016-08/msg00025.html */ - errno = ENOMEM; -#endif +# ifdef PATH_MAX + long int path_max = PATH_MAX; +# else + /* The caller invoked realpath with a null RESOLVED, even though + PATH_MAX is not defined as a constant. The glibc manual says + programs should not do this, and POSIX says the behavior is undefined. + Historically, glibc here used the result of pathconf, or 1024 if that + failed; stay consistent with this (dubious) historical practice. */ + int err = errno; + long int path_max = __pathconf ("/", _PC_PATH_MAX); + __set_errno (err); +# endif + return path_max < 0 ? 1024 : path_max <= IDX_MAX ? path_max : IDX_MAX; } /* Return the canonical absolute name of file NAME. A canonical name - does not contain any ".", ".." components nor any repeated path - separators ('/') or symlinks. All path components must exist. If + does not contain any ".", ".." components nor any repeated file name + separators ('/') or symlinks. All file name components must exist. If RESOLVED is null, the result is malloc'd; otherwise, if the canonical name is PATH_MAX chars or more, returns null with 'errno' set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars, returns the name in RESOLVED. If the name cannot be resolved and - RESOLVED is non-NULL, it contains the path of the first component - that cannot be resolved. If the path can be resolved, RESOLVED + RESOLVED is non-NULL, it contains the name of the first component + that cannot be resolved. If the name can be resolved, RESOLVED holds the same value as the value returned. */ char * __realpath (const char *name, char *resolved) { - char *rpath, *dest, *extra_buf = NULL; - const char *start, *end, *rpath_limit; - long int path_max; + char *dest; + char const *start; + char const *end; int num_links = 0; - size_t prefix_len; if (name == NULL) { @@ -142,205 +148,151 @@ __realpath (const char *name, char *resolved) return NULL; } -#ifdef PATH_MAX - path_max = PATH_MAX; -#else - path_max = pathconf (name, _PC_PATH_MAX); - if (path_max <= 0) - path_max = 8192; -#endif - - if (resolved == NULL) - { - rpath = malloc (path_max); - if (rpath == NULL) - { - alloc_failed (); - return NULL; - } - } - else - rpath = resolved; - rpath_limit = rpath + path_max; + struct scratch_buffer extra_buffer, link_buffer; + struct scratch_buffer rname_buffer; + struct scratch_buffer *rname_buf = &rname_buffer; + scratch_buffer_init (&extra_buffer); + scratch_buffer_init (&link_buffer); + scratch_buffer_init (rname_buf); + char *rname_on_stack = rname_buf->data; + char *rname = rname_on_stack; + bool end_in_extra_buffer = false; + bool failed = true; /* This is always zero for Posix hosts, but can be 2 for MS-Windows and MS-DOS X:/foo/bar file names. */ - prefix_len = FILE_SYSTEM_PREFIX_LEN (name); + idx_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name); if (!IS_ABSOLUTE_FILE_NAME (name)) { - if (!__getcwd (rpath, path_max)) + while (!__getcwd (rname, rname_buf->length)) { - rpath[0] = '\0'; - goto error; + if (errno != ERANGE) + { + dest = rname; + goto error; + } + if (!scratch_buffer_grow (rname_buf)) + goto error_nomem; + rname = rname_buf->data; } - dest = strchr (rpath, '\0'); + dest = __rawmemchr (rname, '\0'); start = name; - prefix_len = FILE_SYSTEM_PREFIX_LEN (rpath); + prefix_len = FILE_SYSTEM_PREFIX_LEN (rname); } else { - dest = rpath; - if (prefix_len) - { - memcpy (rpath, name, prefix_len); - dest += prefix_len; - } + dest = __mempcpy (rname, name, prefix_len); *dest++ = '/'; if (DOUBLE_SLASH_IS_DISTINCT_ROOT) { - if (ISSLASH (name[1]) && !ISSLASH (name[2]) && !prefix_len) + if (prefix_len == 0 /* implies ISSLASH (name[0]) */ + && ISSLASH (name[1]) && !ISSLASH (name[2])) *dest++ = '/'; *dest = '\0'; } start = name + prefix_len; } - for (end = start; *start; start = end) + for ( ; *start; start = end) { -#ifdef _LIBC - struct stat64 st; -#else - struct stat st; -#endif - - /* Skip sequence of multiple path-separators. */ + /* Skip sequence of multiple file name separators. */ while (ISSLASH (*start)) ++start; - /* Find end of path component. */ + /* Find end of component. */ for (end = start; *end && !ISSLASH (*end); ++end) /* Nothing. */; - if (end - start == 0) - break; - else if (end - start == 1 && start[0] == '.') + /* Length of this file name component; it can be zero if a file + name ends in '/'. */ + idx_t startlen = end - start; + + if (startlen == 1 && start[0] == '.') /* nothing */; - else if (end - start == 2 && start[0] == '.' && start[1] == '.') + else if (startlen == 2 && start[0] == '.' && start[1] == '.') { /* Back up to previous component, ignore if at root already. */ - if (dest > rpath + prefix_len + 1) - for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest) + if (dest > rname + prefix_len + 1) + for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; if (DOUBLE_SLASH_IS_DISTINCT_ROOT - && dest == rpath + 1 && !prefix_len + && dest == rname + 1 && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1])) dest++; } else { - size_t new_size; - if (!ISSLASH (dest[-1])) *dest++ = '/'; - if (dest + (end - start) >= rpath_limit) + while (rname + rname_buf->length - dest <= startlen) { - ptrdiff_t dest_offset = dest - rpath; - char *new_rpath; - - if (resolved) - { - __set_errno (ENAMETOOLONG); - if (dest > rpath + prefix_len + 1) - dest--; - *dest = '\0'; - goto error; - } - new_size = rpath_limit - rpath; - if (end - start + 1 > path_max) - new_size += end - start + 1; - else - new_size += path_max; - new_rpath = (char *) realloc (rpath, new_size); - if (new_rpath == NULL) - { - alloc_failed (); - goto error; - } - rpath = new_rpath; - rpath_limit = rpath + new_size; - - dest = rpath + dest_offset; + idx_t dest_offset = dest - rname; + if (!scratch_buffer_grow_preserve (rname_buf)) + goto error_nomem; + rname = rname_buf->data; + dest = rname + dest_offset; } -#ifdef _LIBC - dest = __mempcpy (dest, start, end - start); -#else - memcpy (dest, start, end - start); - dest += end - start; -#endif + dest = __mempcpy (dest, start, startlen); *dest = '\0'; - /* FIXME: if lstat fails with errno == EOVERFLOW, - the entry exists. */ -#ifdef _LIBC - if (__lxstat64 (_STAT_VER, rpath, &st) < 0) -#else - if (lstat (rpath, &st) < 0) -#endif - goto error; - - if (S_ISLNK (st.st_mode)) + /* If STARTLEN == 0, RNAME ends in '/'; use stat rather than + readlink, because readlink might fail with EINVAL without + checking whether RNAME sans '/' is valid. */ + struct stat st; + char *buf = NULL; + ssize_t n; + if (startlen != 0) { - char *buf; - size_t len; - ssize_t n; - - if (++num_links > MAXSYMLINKS) - { - __set_errno (ELOOP); - goto error; - } - - buf = malloca (path_max); - if (!buf) + while (true) { - __set_errno (ENOMEM); - goto error; + buf = link_buffer.data; + idx_t bufsize = link_buffer.length; + n = __readlink (rname, buf, bufsize - 1); + if (n < bufsize - 1) + break; + if (!scratch_buffer_grow (&link_buffer)) + goto error_nomem; } - - n = __readlink (rpath, buf, path_max - 1); if (n < 0) + buf = NULL; + } + if (buf) + { + if (++num_links > __eloop_threshold ()) { - int saved_errno = errno; - freea (buf); - __set_errno (saved_errno); + __set_errno (ELOOP); goto error; } - buf[n] = '\0'; - if (!extra_buf) - { - extra_buf = malloca (path_max); - if (!extra_buf) - { - freea (buf); - __set_errno (ENOMEM); - goto error; - } - } + buf[n] = '\0'; - len = strlen (end); - /* Check that n + len + 1 doesn't overflow and is <= path_max. */ - if (n >= SIZE_MAX - len || n + len >= path_max) + char *extra_buf = extra_buffer.data; + idx_t end_idx; + if (end_in_extra_buffer) + end_idx = end - extra_buf; + idx_t len = strlen (end); + while (extra_buffer.length <= len + n) { - freea (buf); - __set_errno (ENAMETOOLONG); - goto error; + if (!scratch_buffer_grow_preserve (&extra_buffer)) + goto error_nomem; + extra_buf = extra_buffer.data; } + if (end_in_extra_buffer) + end = extra_buf + end_idx; /* Careful here, end may be a pointer into extra_buf... */ memmove (&extra_buf[n], end, len + 1); name = end = memcpy (extra_buf, buf, n); + end_in_extra_buffer = true; if (IS_ABSOLUTE_FILE_NAME (buf)) { - size_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf); + idx_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf); - if (pfxlen) - memcpy (rpath, buf, pfxlen); - dest = rpath + pfxlen; + dest = __mempcpy (rname, buf, pfxlen); *dest++ = '/'; /* It's an absolute symlink */ if (DOUBLE_SLASH_IS_DISTINCT_ROOT) { @@ -355,44 +307,55 @@ __realpath (const char *name, char *resolved) { /* Back up to previous component, ignore if at root already: */ - if (dest > rpath + prefix_len + 1) - for (--dest; dest > rpath && !ISSLASH (dest[-1]); --dest) + if (dest > rname + prefix_len + 1) + for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest) continue; - if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 + if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len) dest++; } } - else if (!S_ISDIR (st.st_mode) && *end != '\0') - { - __set_errno (ENOTDIR); - goto error; - } + else if (! (startlen == 0 + ? stat (rname, &st) == 0 || errno == EOVERFLOW + : errno == EINVAL)) + goto error; } } - if (dest > rpath + prefix_len + 1 && ISSLASH (dest[-1])) + if (dest > rname + prefix_len + 1 && ISSLASH (dest[-1])) --dest; - if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && !prefix_len + if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && !prefix_len && ISSLASH (*dest) && !ISSLASH (dest[1])) dest++; - *dest = '\0'; - - if (extra_buf) - freea (extra_buf); - - return rpath; + failed = false; error: - { - int saved_errno = errno; - if (extra_buf) - freea (extra_buf); - if (resolved == NULL) - free (rpath); - __set_errno (saved_errno); - } - return NULL; + *dest++ = '\0'; + if (resolved != NULL && dest - rname <= get_path_max ()) + rname = strcpy (resolved, rname); + +error_nomem: + scratch_buffer_free (&extra_buffer); + scratch_buffer_free (&link_buffer); + if (failed || rname == resolved) + scratch_buffer_free (rname_buf); + + if (failed) + return NULL; + + if (rname == resolved) + return rname; + idx_t rname_size = dest - rname; + if (rname == rname_on_stack) + { + rname = malloc (rname_size); + if (rname == NULL) + return NULL; + return memcpy (rname, rname_on_stack, rname_size); + } + char *result = realloc (rname, rname_size); + return result != NULL ? result : rname; } +libc_hidden_def (__realpath) versioned_symbol (libc, __realpath, realpath, GLIBC_2_3); #endif /* !FUNC_REALPATH_WORKS || defined _LIBC */ @@ -420,11 +383,3 @@ __canonicalize_file_name (const char *name) return __realpath (name, NULL); } weak_alias (__canonicalize_file_name, canonicalize_file_name) - -#else - -/* This declaration is solely to ensure that after preprocessing - this file is never empty. */ -typedef int dummy; - -#endif |