root/maint/gnulib/lib/canonicalize-lgpl.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. file_accessible
  2. suffix_requires_dir_check
  3. dir_check
  4. get_path_max
  5. realpath_stk
  6. __realpath
  7. libc_hidden_def
  8. __canonicalize_file_name

   1 /* Return the canonical absolute name of a given file.
   2    Copyright (C) 1996-2021 Free Software Foundation, Inc.
   3    This file is part of the GNU C Library.
   4 
   5    The GNU C Library is free software; you can redistribute it and/or
   6    modify it under the terms of the GNU Lesser General Public
   7    License as published by the Free Software Foundation; either
   8    version 2.1 of the License, or (at your option) any later version.
   9 
  10    The GNU C Library is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13    Lesser General Public License for more details.
  14 
  15    You should have received a copy of the GNU Lesser General Public
  16    License along with the GNU C Library; if not, see
  17    <https://www.gnu.org/licenses/>.  */
  18 
  19 #ifndef _LIBC
  20 /* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
  21    optimizes away the name == NULL test below.  */
  22 # define _GL_ARG_NONNULL(params)
  23 
  24 # include <libc-config.h>
  25 #endif
  26 
  27 /* Specification.  */
  28 #include <stdlib.h>
  29 
  30 #include <errno.h>
  31 #include <fcntl.h>
  32 #include <limits.h>
  33 #include <stdbool.h>
  34 #include <string.h>
  35 #include <sys/stat.h>
  36 #include <unistd.h>
  37 
  38 #include <eloop-threshold.h>
  39 #include <filename.h>
  40 #include <idx.h>
  41 #include <intprops.h>
  42 #include <scratch_buffer.h>
  43 
  44 #ifdef _LIBC
  45 # include <shlib-compat.h>
  46 # define GCC_LINT 1
  47 # define _GL_ATTRIBUTE_PURE __attribute__ ((__pure__))
  48 #else
  49 # define __canonicalize_file_name canonicalize_file_name
  50 # define __realpath realpath
  51 # include "pathmax.h"
  52 # define __faccessat faccessat
  53 # if defined _WIN32 && !defined __CYGWIN__
  54 #  define __getcwd _getcwd
  55 # elif HAVE_GETCWD
  56 #  if IN_RELOCWRAPPER
  57     /* When building the relocatable program wrapper, use the system's getcwd
  58        function, not the gnulib override, otherwise we would get a link error.
  59      */
  60 #   undef getcwd
  61 #  endif
  62 #  if defined VMS && !defined getcwd
  63     /* We want the directory in Unix syntax, not in VMS syntax.
  64        The gnulib override of 'getcwd' takes 2 arguments; the original VMS
  65        'getcwd' takes 3 arguments.  */
  66 #   define __getcwd(buf, max) getcwd (buf, max, 0)
  67 #  else
  68 #   define __getcwd getcwd
  69 #  endif
  70 # else
  71 #  define __getcwd(buf, max) getwd (buf)
  72 # endif
  73 # define __mempcpy mempcpy
  74 # define __pathconf pathconf
  75 # define __rawmemchr rawmemchr
  76 # define __readlink readlink
  77 # if IN_RELOCWRAPPER
  78     /* When building the relocatable program wrapper, use the system's memmove
  79        function, not the gnulib override, otherwise we would get a link error.
  80      */
  81 #  undef memmove
  82 # endif
  83 #endif
  84 
  85 /* Suppress bogus GCC -Wmaybe-uninitialized warnings.  */
  86 #if defined GCC_LINT || defined lint
  87 # define IF_LINT(Code) Code
  88 #else
  89 # define IF_LINT(Code) /* empty */
  90 #endif
  91 
  92 #ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
  93 # define DOUBLE_SLASH_IS_DISTINCT_ROOT false
  94 #endif
  95 
  96 #if defined _LIBC || !FUNC_REALPATH_WORKS
  97 
  98 /* Return true if FILE's existence can be shown, false (setting errno)
  99    otherwise.  Follow symbolic links.  */
 100 static bool
 101 file_accessible (char const *file)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103 # if defined _LIBC || HAVE_FACCESSAT
 104   return __faccessat (AT_FDCWD, file, F_OK, AT_EACCESS) == 0;
 105 # else
 106   struct stat st;
 107   return stat (file, &st) == 0 || errno == EOVERFLOW;
 108 # endif
 109 }
 110 
 111 /* True if concatenating END as a suffix to a file name means that the
 112    code needs to check that the file name is that of a searchable
 113    directory, since the canonicalize_filename_mode_stk code won't
 114    check this later anyway when it checks an ordinary file name
 115    component within END.  END must either be empty, or start with a
 116    slash.  */
 117 
 118 static bool _GL_ATTRIBUTE_PURE
 119 suffix_requires_dir_check (char const *end)
     /* [previous][next][first][last][top][bottom][index][help] */
 120 {
 121   /* If END does not start with a slash, the suffix is OK.  */
 122   while (ISSLASH (*end))
 123     {
 124       /* Two or more slashes act like a single slash.  */
 125       do
 126         end++;
 127       while (ISSLASH (*end));
 128 
 129       switch (*end++)
 130         {
 131         default: return false;  /* An ordinary file name component is OK.  */
 132         case '\0': return true; /* Trailing "/" is trouble.  */
 133         case '.': break;        /* Possibly "." or "..".  */
 134         }
 135       /* Trailing "/.", or "/.." even if not trailing, is trouble.  */
 136       if (!*end || (*end == '.' && (!end[1] || ISSLASH (end[1]))))
 137         return true;
 138     }
 139 
 140   return false;
 141 }
 142 
 143 /* Append this to a file name to test whether it is a searchable directory.
 144    On POSIX platforms "/" suffices, but "/./" is sometimes needed on
 145    macOS 10.13 <https://bugs.gnu.org/30350>, and should also work on
 146    platforms like AIX 7.2 that need at least "/.".  */
 147 
 148 # if defined _LIBC || defined LSTAT_FOLLOWS_SLASHED_SYMLINK
 149 static char const dir_suffix[] = "/";
 150 # else
 151 static char const dir_suffix[] = "/./";
 152 # endif
 153 
 154 /* Return true if DIR is a searchable dir, false (setting errno) otherwise.
 155    DIREND points to the NUL byte at the end of the DIR string.
 156    Store garbage into DIREND[0 .. strlen (dir_suffix)].  */
 157 
 158 static bool
 159 dir_check (char *dir, char *dirend)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161   strcpy (dirend, dir_suffix);
 162   return file_accessible (dir);
 163 }
 164 
 165 static idx_t
 166 get_path_max (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 167 {
 168 # ifdef PATH_MAX
 169   long int path_max = PATH_MAX;
 170 # else
 171   /* The caller invoked realpath with a null RESOLVED, even though
 172      PATH_MAX is not defined as a constant.  The glibc manual says
 173      programs should not do this, and POSIX says the behavior is undefined.
 174      Historically, glibc here used the result of pathconf, or 1024 if that
 175      failed; stay consistent with this (dubious) historical practice.  */
 176   int err = errno;
 177   long int path_max = __pathconf ("/", _PC_PATH_MAX);
 178   __set_errno (err);
 179 # endif
 180   return path_max < 0 ? 1024 : path_max <= IDX_MAX ? path_max : IDX_MAX;
 181 }
 182 
 183 /* Act like __realpath (see below), with an additional argument
 184    rname_buf that can be used as temporary storage.
 185 
 186    If GCC_LINT is defined, do not inline this function with GCC 10.1
 187    and later, to avoid creating a pointer to the stack that GCC
 188    -Wreturn-local-addr incorrectly complains about.  See:
 189    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93644
 190    Although the noinline attribute can hurt performance a bit, no better way
 191    to pacify GCC is known; even an explicit #pragma does not pacify GCC.
 192    When the GCC bug is fixed this workaround should be limited to the
 193    broken GCC versions.  */
 194 # if __GNUC_PREREQ (10, 1)
 195 #  if defined GCC_LINT || defined lint
 196 __attribute__ ((__noinline__))
 197 #  elif __OPTIMIZE__ && !__NO_INLINE__
 198 #   define GCC_BOGUS_WRETURN_LOCAL_ADDR
 199 #  endif
 200 # endif
 201 static char *
 202 realpath_stk (const char *name, char *resolved,
     /* [previous][next][first][last][top][bottom][index][help] */
 203               struct scratch_buffer *rname_buf)
 204 {
 205   char *dest;
 206   char const *start;
 207   char const *end;
 208   int num_links = 0;
 209 
 210   if (name == NULL)
 211     {
 212       /* As per Single Unix Specification V2 we must return an error if
 213          either parameter is a null pointer.  We extend this to allow
 214          the RESOLVED parameter to be NULL in case the we are expected to
 215          allocate the room for the return value.  */
 216       __set_errno (EINVAL);
 217       return NULL;
 218     }
 219 
 220   if (name[0] == '\0')
 221     {
 222       /* As per Single Unix Specification V2 we must return an error if
 223          the name argument points to an empty string.  */
 224       __set_errno (ENOENT);
 225       return NULL;
 226     }
 227 
 228   struct scratch_buffer extra_buffer, link_buffer;
 229   scratch_buffer_init (&extra_buffer);
 230   scratch_buffer_init (&link_buffer);
 231   scratch_buffer_init (rname_buf);
 232   char *rname_on_stack = rname_buf->data;
 233   char *rname = rname_on_stack;
 234   bool end_in_extra_buffer = false;
 235   bool failed = true;
 236 
 237   /* This is always zero for Posix hosts, but can be 2 for MS-Windows
 238      and MS-DOS X:/foo/bar file names.  */
 239   idx_t prefix_len = FILE_SYSTEM_PREFIX_LEN (name);
 240 
 241   if (!IS_ABSOLUTE_FILE_NAME (name))
 242     {
 243       while (!__getcwd (rname, rname_buf->length))
 244         {
 245           if (errno != ERANGE)
 246             {
 247               dest = rname;
 248               goto error;
 249             }
 250           if (!scratch_buffer_grow (rname_buf))
 251             goto error_nomem;
 252           rname = rname_buf->data;
 253         }
 254       dest = __rawmemchr (rname, '\0');
 255       start = name;
 256       prefix_len = FILE_SYSTEM_PREFIX_LEN (rname);
 257     }
 258   else
 259     {
 260       dest = __mempcpy (rname, name, prefix_len);
 261       *dest++ = '/';
 262       if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
 263         {
 264           if (prefix_len == 0 /* implies ISSLASH (name[0]) */
 265               && ISSLASH (name[1]) && !ISSLASH (name[2]))
 266             *dest++ = '/';
 267           *dest = '\0';
 268         }
 269       start = name + prefix_len;
 270     }
 271 
 272   for ( ; *start; start = end)
 273     {
 274       /* Skip sequence of multiple file name separators.  */
 275       while (ISSLASH (*start))
 276         ++start;
 277 
 278       /* Find end of component.  */
 279       for (end = start; *end && !ISSLASH (*end); ++end)
 280         /* Nothing.  */;
 281 
 282       /* Length of this file name component; it can be zero if a file
 283          name ends in '/'.  */
 284       idx_t startlen = end - start;
 285 
 286       if (startlen == 0)
 287         break;
 288       else if (startlen == 1 && start[0] == '.')
 289         /* nothing */;
 290       else if (startlen == 2 && start[0] == '.' && start[1] == '.')
 291         {
 292           /* Back up to previous component, ignore if at root already.  */
 293           if (dest > rname + prefix_len + 1)
 294             for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest)
 295               continue;
 296           if (DOUBLE_SLASH_IS_DISTINCT_ROOT
 297               && dest == rname + 1 && !prefix_len
 298               && ISSLASH (*dest) && !ISSLASH (dest[1]))
 299             dest++;
 300         }
 301       else
 302         {
 303           if (!ISSLASH (dest[-1]))
 304             *dest++ = '/';
 305 
 306           while (rname + rname_buf->length - dest
 307                  < startlen + sizeof dir_suffix)
 308             {
 309               idx_t dest_offset = dest - rname;
 310               if (!scratch_buffer_grow_preserve (rname_buf))
 311                 goto error_nomem;
 312               rname = rname_buf->data;
 313               dest = rname + dest_offset;
 314             }
 315 
 316           dest = __mempcpy (dest, start, startlen);
 317           *dest = '\0';
 318 
 319           char *buf;
 320           ssize_t n;
 321           while (true)
 322             {
 323               buf = link_buffer.data;
 324               idx_t bufsize = link_buffer.length;
 325               n = __readlink (rname, buf, bufsize - 1);
 326               if (n < bufsize - 1)
 327                 break;
 328               if (!scratch_buffer_grow (&link_buffer))
 329                 goto error_nomem;
 330             }
 331           if (0 <= n)
 332             {
 333               if (++num_links > __eloop_threshold ())
 334                 {
 335                   __set_errno (ELOOP);
 336                   goto error;
 337                 }
 338 
 339               buf[n] = '\0';
 340 
 341               char *extra_buf = extra_buffer.data;
 342               idx_t end_idx IF_LINT (= 0);
 343               if (end_in_extra_buffer)
 344                 end_idx = end - extra_buf;
 345               size_t len = strlen (end);
 346               if (INT_ADD_OVERFLOW (len, n))
 347                 {
 348                   __set_errno (ENOMEM);
 349                   goto error_nomem;
 350                 }
 351               while (extra_buffer.length <= len + n)
 352                 {
 353                   if (!scratch_buffer_grow_preserve (&extra_buffer))
 354                     goto error_nomem;
 355                   extra_buf = extra_buffer.data;
 356                 }
 357               if (end_in_extra_buffer)
 358                 end = extra_buf + end_idx;
 359 
 360               /* Careful here, end may be a pointer into extra_buf... */
 361               memmove (&extra_buf[n], end, len + 1);
 362               name = end = memcpy (extra_buf, buf, n);
 363               end_in_extra_buffer = true;
 364 
 365               if (IS_ABSOLUTE_FILE_NAME (buf))
 366                 {
 367                   idx_t pfxlen = FILE_SYSTEM_PREFIX_LEN (buf);
 368 
 369                   dest = __mempcpy (rname, buf, pfxlen);
 370                   *dest++ = '/'; /* It's an absolute symlink */
 371                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT)
 372                     {
 373                       if (ISSLASH (buf[1]) && !ISSLASH (buf[2]) && !pfxlen)
 374                         *dest++ = '/';
 375                       *dest = '\0';
 376                     }
 377                   /* Install the new prefix to be in effect hereafter.  */
 378                   prefix_len = pfxlen;
 379                 }
 380               else
 381                 {
 382                   /* Back up to previous component, ignore if at root
 383                      already: */
 384                   if (dest > rname + prefix_len + 1)
 385                     for (--dest; dest > rname && !ISSLASH (dest[-1]); --dest)
 386                       continue;
 387                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1
 388                       && ISSLASH (*dest) && !ISSLASH (dest[1]) && !prefix_len)
 389                     dest++;
 390                 }
 391             }
 392           else if (! (suffix_requires_dir_check (end)
 393                       ? dir_check (rname, dest)
 394                       : errno == EINVAL))
 395             goto error;
 396         }
 397     }
 398   if (dest > rname + prefix_len + 1 && ISSLASH (dest[-1]))
 399     --dest;
 400   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rname + 1 && !prefix_len
 401       && ISSLASH (*dest) && !ISSLASH (dest[1]))
 402     dest++;
 403   failed = false;
 404 
 405 error:
 406   *dest++ = '\0';
 407   if (resolved != NULL && dest - rname <= get_path_max ())
 408     rname = strcpy (resolved, rname);
 409 
 410 error_nomem:
 411   scratch_buffer_free (&extra_buffer);
 412   scratch_buffer_free (&link_buffer);
 413 
 414   if (failed || rname == resolved)
 415     {
 416       scratch_buffer_free (rname_buf);
 417       return failed ? NULL : resolved;
 418     }
 419 
 420   return scratch_buffer_dupfree (rname_buf, dest - rname);
 421 }
 422 
 423 /* Return the canonical absolute name of file NAME.  A canonical name
 424    does not contain any ".", ".." components nor any repeated file name
 425    separators ('/') or symlinks.  All file name components must exist.  If
 426    RESOLVED is null, the result is malloc'd; otherwise, if the
 427    canonical name is PATH_MAX chars or more, returns null with 'errno'
 428    set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
 429    returns the name in RESOLVED.  If the name cannot be resolved and
 430    RESOLVED is non-NULL, it contains the name of the first component
 431    that cannot be resolved.  If the name can be resolved, RESOLVED
 432    holds the same value as the value returned.  */
 433 
 434 char *
 435 __realpath (const char *name, char *resolved)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437   #ifdef GCC_BOGUS_WRETURN_LOCAL_ADDR
 438    #warning "GCC might issue a bogus -Wreturn-local-addr warning here."
 439    #warning "See <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93644>."
 440   #endif
 441   struct scratch_buffer rname_buffer;
 442   return realpath_stk (name, resolved, &rname_buffer);
 443 }
 444 libc_hidden_def (__realpath)
     /* [previous][next][first][last][top][bottom][index][help] */
 445 versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
 446 
 447 #endif /* defined _LIBC || !FUNC_REALPATH_WORKS */
 448 
 449 
 450 #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
 451 char *
 452 attribute_compat_text_section
 453 __old_realpath (const char *name, char *resolved)
 454 {
 455   if (resolved == NULL)
 456     {
 457       __set_errno (EINVAL);
 458       return NULL;
 459     }
 460 
 461   return __realpath (name, resolved);
 462 }
 463 compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
 464 #endif
 465 
 466 
 467 char *
 468 __canonicalize_file_name (const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 469 {
 470   return __realpath (name, NULL);
 471 }
 472 weak_alias (__canonicalize_file_name, canonicalize_file_name)

/* [previous][next][first][last][top][bottom][index][help] */