hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame stdlib/canonicalize.c

Packit 6c4009
/* Return the canonical absolute name of a given file.
Packit 6c4009
   Copyright (C) 1996-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <sys/stat.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <stddef.h>
Packit 6c4009
Packit 6c4009
#include <eloop-threshold.h>
Packit 6c4009
#include <shlib-compat.h>
Packit 6c4009
Packit 6c4009
/* Return the canonical absolute name of file NAME.  A canonical name
Packit 6c4009
   does not contain any `.', `..' components nor any repeated path
Packit 6c4009
   separators ('/') or symlinks.  All path components must exist.  If
Packit 6c4009
   RESOLVED is null, the result is malloc'd; otherwise, if the
Packit 6c4009
   canonical name is PATH_MAX chars or more, returns null with `errno'
Packit 6c4009
   set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
Packit 6c4009
   returns the name in RESOLVED.  If the name cannot be resolved and
Packit 6c4009
   RESOLVED is non-NULL, it contains the path of the first component
Packit 6c4009
   that cannot be resolved.  If the path can be resolved, RESOLVED
Packit 6c4009
   holds the same value as the value returned.  */
Packit 6c4009
Packit 6c4009
char *
Packit 6c4009
__realpath (const char *name, char *resolved)
Packit 6c4009
{
Packit 6c4009
  char *rpath, *dest, *extra_buf = NULL;
Packit 6c4009
  const char *start, *end, *rpath_limit;
Packit 6c4009
  long int path_max;
Packit 6c4009
  int num_links = 0;
Packit 6c4009
Packit 6c4009
  if (name == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* As per Single Unix Specification V2 we must return an error if
Packit 6c4009
	 either parameter is a null pointer.  We extend this to allow
Packit 6c4009
	 the RESOLVED parameter to be NULL in case the we are expected to
Packit 6c4009
	 allocate the room for the return value.  */
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (name[0] == '\0')
Packit 6c4009
    {
Packit 6c4009
      /* As per Single Unix Specification V2 we must return an error if
Packit 6c4009
	 the name argument points to an empty string.  */
Packit 6c4009
      __set_errno (ENOENT);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
#ifdef PATH_MAX
Packit 6c4009
  path_max = PATH_MAX;
Packit 6c4009
#else
Packit 6c4009
  path_max = __pathconf (name, _PC_PATH_MAX);
Packit 6c4009
  if (path_max <= 0)
Packit 6c4009
    path_max = 1024;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  if (resolved == NULL)
Packit 6c4009
    {
Packit 6c4009
      rpath = malloc (path_max);
Packit 6c4009
      if (rpath == NULL)
Packit 6c4009
	return NULL;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    rpath = resolved;
Packit 6c4009
  rpath_limit = rpath + path_max;
Packit 6c4009
Packit 6c4009
  if (name[0] != '/')
Packit 6c4009
    {
Packit 6c4009
      if (!__getcwd (rpath, path_max))
Packit 6c4009
	{
Packit 6c4009
	  rpath[0] = '\0';
Packit 6c4009
	  goto error;
Packit 6c4009
	}
Packit 6c4009
      dest = __rawmemchr (rpath, '\0');
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      rpath[0] = '/';
Packit 6c4009
      dest = rpath + 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  for (start = end = name; *start; start = end)
Packit 6c4009
    {
Packit 6c4009
      struct stat64 st;
Packit 6c4009
      int n;
Packit 6c4009
Packit 6c4009
      /* Skip sequence of multiple path-separators.  */
Packit 6c4009
      while (*start == '/')
Packit 6c4009
	++start;
Packit 6c4009
Packit 6c4009
      /* Find end of path component.  */
Packit 6c4009
      for (end = start; *end && *end != '/'; ++end)
Packit 6c4009
	/* Nothing.  */;
Packit 6c4009
Packit 6c4009
      if (end - start == 0)
Packit 6c4009
	break;
Packit 6c4009
      else if (end - start == 1 && start[0] == '.')
Packit 6c4009
	/* nothing */;
Packit 6c4009
      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
Packit 6c4009
	{
Packit 6c4009
	  /* Back up to previous component, ignore if at root already.  */
Packit 6c4009
	  if (dest > rpath + 1)
Packit 6c4009
	    while ((--dest)[-1] != '/');
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  size_t new_size;
Packit 6c4009
Packit 6c4009
	  if (dest[-1] != '/')
Packit 6c4009
	    *dest++ = '/';
Packit 6c4009
Packit 6c4009
	  if (dest + (end - start) >= rpath_limit)
Packit 6c4009
	    {
Packit 6c4009
	      ptrdiff_t dest_offset = dest - rpath;
Packit 6c4009
	      char *new_rpath;
Packit 6c4009
Packit 6c4009
	      if (resolved)
Packit 6c4009
		{
Packit 6c4009
		  __set_errno (ENAMETOOLONG);
Packit 6c4009
		  if (dest > rpath + 1)
Packit 6c4009
		    dest--;
Packit 6c4009
		  *dest = '\0';
Packit 6c4009
		  goto error;
Packit 6c4009
		}
Packit 6c4009
	      new_size = rpath_limit - rpath;
Packit 6c4009
	      if (end - start + 1 > path_max)
Packit 6c4009
		new_size += end - start + 1;
Packit 6c4009
	      else
Packit 6c4009
		new_size += path_max;
Packit 6c4009
	      new_rpath = (char *) realloc (rpath, new_size);
Packit 6c4009
	      if (new_rpath == NULL)
Packit 6c4009
		goto error;
Packit 6c4009
	      rpath = new_rpath;
Packit 6c4009
	      rpath_limit = rpath + new_size;
Packit 6c4009
Packit 6c4009
	      dest = rpath + dest_offset;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  dest = __mempcpy (dest, start, end - start);
Packit 6c4009
	  *dest = '\0';
Packit 6c4009
Packit 6c4009
	  if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
Packit 6c4009
	    goto error;
Packit 6c4009
Packit 6c4009
	  if (S_ISLNK (st.st_mode))
Packit 6c4009
	    {
Packit 6c4009
	      char *buf = __alloca (path_max);
Packit 6c4009
	      size_t len;
Packit 6c4009
Packit 6c4009
	      if (++num_links > __eloop_threshold ())
Packit 6c4009
		{
Packit 6c4009
		  __set_errno (ELOOP);
Packit 6c4009
		  goto error;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      n = __readlink (rpath, buf, path_max - 1);
Packit 6c4009
	      if (n < 0)
Packit 6c4009
		goto error;
Packit 6c4009
	      buf[n] = '\0';
Packit 6c4009
Packit 6c4009
	      if (!extra_buf)
Packit 6c4009
		extra_buf = __alloca (path_max);
Packit 6c4009
Packit 6c4009
	      len = strlen (end);
Packit 6c4009
	      if (path_max - n <= len)
Packit 6c4009
		{
Packit 6c4009
		  __set_errno (ENAMETOOLONG);
Packit 6c4009
		  goto error;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* Careful here, end may be a pointer into extra_buf... */
Packit 6c4009
	      memmove (&extra_buf[n], end, len + 1);
Packit 6c4009
	      name = end = memcpy (extra_buf, buf, n);
Packit 6c4009
Packit 6c4009
	      if (buf[0] == '/')
Packit 6c4009
		dest = rpath + 1;	/* It's an absolute symlink */
Packit 6c4009
	      else
Packit 6c4009
		/* Back up to previous component, ignore if at root already: */
Packit 6c4009
		if (dest > rpath + 1)
Packit 6c4009
		  while ((--dest)[-1] != '/');
Packit 6c4009
	    }
Packit 6c4009
	  else if (!S_ISDIR (st.st_mode) && *end != '\0')
Packit 6c4009
	    {
Packit 6c4009
	      __set_errno (ENOTDIR);
Packit 6c4009
	      goto error;
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  if (dest > rpath + 1 && dest[-1] == '/')
Packit 6c4009
    --dest;
Packit 6c4009
  *dest = '\0';
Packit 6c4009
Packit 6c4009
  assert (resolved == NULL || resolved == rpath);
Packit 6c4009
  return rpath;
Packit 6c4009
Packit 6c4009
error:
Packit 6c4009
  assert (resolved == NULL || resolved == rpath);
Packit 6c4009
  if (resolved == NULL)
Packit 6c4009
    free (rpath);
Packit 6c4009
  return NULL;
Packit 6c4009
}
Packit 6c4009
libc_hidden_def (__realpath)
Packit 6c4009
versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
Packit 6c4009
Packit 6c4009
Packit 6c4009
#if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
Packit 6c4009
char *
Packit 6c4009
attribute_compat_text_section
Packit 6c4009
__old_realpath (const char *name, char *resolved)
Packit 6c4009
{
Packit 6c4009
  if (resolved == NULL)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return __realpath (name, resolved);
Packit 6c4009
}
Packit 6c4009
compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
char *
Packit 6c4009
__canonicalize_file_name (const char *name)
Packit 6c4009
{
Packit 6c4009
  return __realpath (name, NULL);
Packit 6c4009
}
Packit 6c4009
weak_alias (__canonicalize_file_name, canonicalize_file_name)