Back to index

glibc  2.9
canonicalize.c
Go to the documentation of this file.
00001 /* Return the canonical absolute name of a given file.
00002    Copyright (C) 1996-2002,2004,2005,2006,2008 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <unistd.h>
00024 #include <limits.h>
00025 #include <sys/param.h>
00026 #include <sys/stat.h>
00027 #include <errno.h>
00028 #include <stddef.h>
00029 
00030 #include <shlib-compat.h>
00031 
00032 /* Return the canonical absolute name of file NAME.  A canonical name
00033    does not contain any `.', `..' components nor any repeated path
00034    separators ('/') or symlinks.  All path components must exist.  If
00035    RESOLVED is null, the result is malloc'd; otherwise, if the
00036    canonical name is PATH_MAX chars or more, returns null with `errno'
00037    set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
00038    returns the name in RESOLVED.  If the name cannot be resolved and
00039    RESOLVED is non-NULL, it contains the path of the first component
00040    that cannot be resolved.  If the path can be resolved, RESOLVED
00041    holds the same value as the value returned.  */
00042 
00043 char *
00044 __realpath (const char *name, char *resolved)
00045 {
00046   char *rpath, *dest, *extra_buf = NULL;
00047   const char *start, *end, *rpath_limit;
00048   long int path_max;
00049   int num_links = 0;
00050 
00051   if (name == NULL)
00052     {
00053       /* As per Single Unix Specification V2 we must return an error if
00054         either parameter is a null pointer.  We extend this to allow
00055         the RESOLVED parameter to be NULL in case the we are expected to
00056         allocate the room for the return value.  */
00057       __set_errno (EINVAL);
00058       return NULL;
00059     }
00060 
00061   if (name[0] == '\0')
00062     {
00063       /* As per Single Unix Specification V2 we must return an error if
00064         the name argument points to an empty string.  */
00065       __set_errno (ENOENT);
00066       return NULL;
00067     }
00068 
00069 #ifdef PATH_MAX
00070   path_max = PATH_MAX;
00071 #else
00072   path_max = pathconf (name, _PC_PATH_MAX);
00073   if (path_max <= 0)
00074     path_max = 1024;
00075 #endif
00076 
00077   if (resolved == NULL)
00078     {
00079       rpath = malloc (path_max);
00080       if (rpath == NULL)
00081        return NULL;
00082     }
00083   else
00084     rpath = resolved;
00085   rpath_limit = rpath + path_max;
00086 
00087   if (name[0] != '/')
00088     {
00089       if (!__getcwd (rpath, path_max))
00090        {
00091          rpath[0] = '\0';
00092          goto error;
00093        }
00094       dest = __rawmemchr (rpath, '\0');
00095     }
00096   else
00097     {
00098       rpath[0] = '/';
00099       dest = rpath + 1;
00100     }
00101 
00102   for (start = end = name; *start; start = end)
00103     {
00104       struct stat64 st;
00105       int n;
00106 
00107       /* Skip sequence of multiple path-separators.  */
00108       while (*start == '/')
00109        ++start;
00110 
00111       /* Find end of path component.  */
00112       for (end = start; *end && *end != '/'; ++end)
00113        /* Nothing.  */;
00114 
00115       if (end - start == 0)
00116        break;
00117       else if (end - start == 1 && start[0] == '.')
00118        /* nothing */;
00119       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
00120        {
00121          /* Back up to previous component, ignore if at root already.  */
00122          if (dest > rpath + 1)
00123            while ((--dest)[-1] != '/');
00124        }
00125       else
00126        {
00127          size_t new_size;
00128 
00129          if (dest[-1] != '/')
00130            *dest++ = '/';
00131 
00132          if (dest + (end - start) >= rpath_limit)
00133            {
00134              ptrdiff_t dest_offset = dest - rpath;
00135              char *new_rpath;
00136 
00137              if (resolved)
00138               {
00139                 __set_errno (ENAMETOOLONG);
00140                 if (dest > rpath + 1)
00141                   dest--;
00142                 *dest = '\0';
00143                 goto error;
00144               }
00145              new_size = rpath_limit - rpath;
00146              if (end - start + 1 > path_max)
00147               new_size += end - start + 1;
00148              else
00149               new_size += path_max;
00150              new_rpath = (char *) realloc (rpath, new_size);
00151              if (new_rpath == NULL)
00152               goto error;
00153              rpath = new_rpath;
00154              rpath_limit = rpath + new_size;
00155 
00156              dest = rpath + dest_offset;
00157            }
00158 
00159          dest = __mempcpy (dest, start, end - start);
00160          *dest = '\0';
00161 
00162          if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
00163            goto error;
00164 
00165          if (S_ISLNK (st.st_mode))
00166            {
00167              char *buf = __alloca (path_max);
00168              size_t len;
00169 
00170              if (++num_links > MAXSYMLINKS)
00171               {
00172                 __set_errno (ELOOP);
00173                 goto error;
00174               }
00175 
00176              n = __readlink (rpath, buf, path_max - 1);
00177              if (n < 0)
00178               goto error;
00179              buf[n] = '\0';
00180 
00181              if (!extra_buf)
00182               extra_buf = __alloca (path_max);
00183 
00184              len = strlen (end);
00185              if ((long int) (n + len) >= path_max)
00186               {
00187                 __set_errno (ENAMETOOLONG);
00188                 goto error;
00189               }
00190 
00191              /* Careful here, end may be a pointer into extra_buf... */
00192              memmove (&extra_buf[n], end, len + 1);
00193              name = end = memcpy (extra_buf, buf, n);
00194 
00195              if (buf[0] == '/')
00196               dest = rpath + 1;    /* It's an absolute symlink */
00197              else
00198               /* Back up to previous component, ignore if at root already: */
00199               if (dest > rpath + 1)
00200                 while ((--dest)[-1] != '/');
00201            }
00202          else if (!S_ISDIR (st.st_mode) && *end != '\0')
00203            {
00204              __set_errno (ENOTDIR);
00205              goto error;
00206            }
00207        }
00208     }
00209   if (dest > rpath + 1 && dest[-1] == '/')
00210     --dest;
00211   *dest = '\0';
00212 
00213   assert (resolved == NULL || resolved == rpath);
00214   return rpath;
00215 
00216 error:
00217   assert (resolved == NULL || resolved == rpath);
00218   if (resolved == NULL)
00219     free (rpath);
00220   return NULL;
00221 }
00222 versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
00223 
00224 
00225 #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
00226 char *
00227 attribute_compat_text_section
00228 __old_realpath (const char *name, char *resolved)
00229 {
00230   if (resolved == NULL)
00231     {
00232       __set_errno (EINVAL);
00233       return NULL;
00234     }
00235 
00236   return __realpath (name, resolved);
00237 }
00238 compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
00239 #endif
00240 
00241 
00242 char *
00243 __canonicalize_file_name (const char *name)
00244 {
00245   return __realpath (name, NULL);
00246 }
00247 weak_alias (__canonicalize_file_name, canonicalize_file_name)