File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libiconv / srclib / canonicalize-lgpl.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 09:29:43 2012 UTC (12 years, 1 month ago) by misho
Branches: libiconv, MAIN
CVS tags: v1_14p0, v1_14, HEAD
libiconv v1.14

    1: /* Return the canonical absolute name of a given file.
    2:    Copyright (C) 1996-2011 Free Software Foundation, Inc.
    3:    This file is part of the GNU C Library.
    4: 
    5:    This program is free software: you can redistribute it and/or modify
    6:    it under the terms of the GNU General Public License as published by
    7:    the Free Software Foundation; either version 3 of the License, or
    8:    (at your option) any later version.
    9: 
   10:    This program 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
   13:    GNU General Public License for more details.
   14: 
   15:    You should have received a copy of the GNU General Public License
   16:    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
   17: 
   18: #ifndef _LIBC
   19: # define _GL_USE_STDLIB_ALLOC 1
   20: # include <config.h>
   21: #endif
   22: 
   23: #if !HAVE_CANONICALIZE_FILE_NAME || !FUNC_REALPATH_WORKS || defined _LIBC
   24: 
   25: /* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
   26:    optimizes away the name == NULL test below.  */
   27: #define _GL_ARG_NONNULL(params)
   28: 
   29: /* Specification.  */
   30: #include <stdlib.h>
   31: 
   32: #include <alloca.h>
   33: #include <string.h>
   34: #include <unistd.h>
   35: #include <limits.h>
   36: #if HAVE_SYS_PARAM_H || defined _LIBC
   37: # include <sys/param.h>
   38: #endif
   39: #include <sys/stat.h>
   40: #include <errno.h>
   41: #include <stddef.h>
   42: 
   43: #ifdef _LIBC
   44: # include <shlib-compat.h>
   45: #else
   46: # define SHLIB_COMPAT(lib, introduced, obsoleted) 0
   47: # define versioned_symbol(lib, local, symbol, version) extern int dummy
   48: # define compat_symbol(lib, local, symbol, version)
   49: # define weak_alias(local, symbol)
   50: # define __canonicalize_file_name canonicalize_file_name
   51: # define __realpath realpath
   52: # include "pathmax.h"
   53: # include "malloca.h"
   54: # if HAVE_GETCWD
   55: #  if IN_RELOCWRAPPER
   56:     /* When building the relocatable program wrapper, use the system's getcwd
   57:        function, not the gnulib override, otherwise we would get a link error.
   58:      */
   59: #   undef getcwd
   60: #  endif
   61: #  ifdef VMS
   62:     /* We want the directory in Unix syntax, not in VMS syntax.  */
   63: #   define __getcwd(buf, max) getcwd (buf, max, 0)
   64: #  else
   65: #   define __getcwd getcwd
   66: #  endif
   67: # else
   68: #  define __getcwd(buf, max) getwd (buf)
   69: # endif
   70: # define __readlink readlink
   71: # define __set_errno(e) errno = (e)
   72: # ifndef MAXSYMLINKS
   73: #  ifdef SYMLOOP_MAX
   74: #   define MAXSYMLINKS SYMLOOP_MAX
   75: #  else
   76: #   define MAXSYMLINKS 20
   77: #  endif
   78: # endif
   79: #endif
   80: 
   81: #ifndef DOUBLE_SLASH_IS_DISTINCT_ROOT
   82: # define DOUBLE_SLASH_IS_DISTINCT_ROOT 0
   83: #endif
   84: 
   85: #if !FUNC_REALPATH_WORKS || defined _LIBC
   86: /* Return the canonical absolute name of file NAME.  A canonical name
   87:    does not contain any `.', `..' components nor any repeated path
   88:    separators ('/') or symlinks.  All path components must exist.  If
   89:    RESOLVED is null, the result is malloc'd; otherwise, if the
   90:    canonical name is PATH_MAX chars or more, returns null with `errno'
   91:    set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
   92:    returns the name in RESOLVED.  If the name cannot be resolved and
   93:    RESOLVED is non-NULL, it contains the path of the first component
   94:    that cannot be resolved.  If the path can be resolved, RESOLVED
   95:    holds the same value as the value returned.  */
   96: 
   97: char *
   98: __realpath (const char *name, char *resolved)
   99: {
  100:   char *rpath, *dest, *extra_buf = NULL;
  101:   const char *start, *end, *rpath_limit;
  102:   long int path_max;
  103:   int num_links = 0;
  104: 
  105:   if (name == NULL)
  106:     {
  107:       /* As per Single Unix Specification V2 we must return an error if
  108:          either parameter is a null pointer.  We extend this to allow
  109:          the RESOLVED parameter to be NULL in case the we are expected to
  110:          allocate the room for the return value.  */
  111:       __set_errno (EINVAL);
  112:       return NULL;
  113:     }
  114: 
  115:   if (name[0] == '\0')
  116:     {
  117:       /* As per Single Unix Specification V2 we must return an error if
  118:          the name argument points to an empty string.  */
  119:       __set_errno (ENOENT);
  120:       return NULL;
  121:     }
  122: 
  123: #ifdef PATH_MAX
  124:   path_max = PATH_MAX;
  125: #else
  126:   path_max = pathconf (name, _PC_PATH_MAX);
  127:   if (path_max <= 0)
  128:     path_max = 8192;
  129: #endif
  130: 
  131:   if (resolved == NULL)
  132:     {
  133:       rpath = malloc (path_max);
  134:       if (rpath == NULL)
  135:         {
  136:           /* It's easier to set errno to ENOMEM than to rely on the
  137:              'malloc-posix' gnulib module.  */
  138:           errno = ENOMEM;
  139:           return NULL;
  140:         }
  141:     }
  142:   else
  143:     rpath = resolved;
  144:   rpath_limit = rpath + path_max;
  145: 
  146:   if (name[0] != '/')
  147:     {
  148:       if (!__getcwd (rpath, path_max))
  149:         {
  150:           rpath[0] = '\0';
  151:           goto error;
  152:         }
  153:       dest = strchr (rpath, '\0');
  154:     }
  155:   else
  156:     {
  157:       rpath[0] = '/';
  158:       dest = rpath + 1;
  159:       if (DOUBLE_SLASH_IS_DISTINCT_ROOT && name[1] == '/')
  160:         *dest++ = '/';
  161:     }
  162: 
  163:   for (start = end = name; *start; start = end)
  164:     {
  165: #ifdef _LIBC
  166:       struct stat64 st;
  167: #else
  168:       struct stat st;
  169: #endif
  170:       int n;
  171: 
  172:       /* Skip sequence of multiple path-separators.  */
  173:       while (*start == '/')
  174:         ++start;
  175: 
  176:       /* Find end of path component.  */
  177:       for (end = start; *end && *end != '/'; ++end)
  178:         /* Nothing.  */;
  179: 
  180:       if (end - start == 0)
  181:         break;
  182:       else if (end - start == 1 && start[0] == '.')
  183:         /* nothing */;
  184:       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
  185:         {
  186:           /* Back up to previous component, ignore if at root already.  */
  187:           if (dest > rpath + 1)
  188:             while ((--dest)[-1] != '/');
  189:           if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
  190:               && *dest == '/')
  191:             dest++;
  192:         }
  193:       else
  194:         {
  195:           size_t new_size;
  196: 
  197:           if (dest[-1] != '/')
  198:             *dest++ = '/';
  199: 
  200:           if (dest + (end - start) >= rpath_limit)
  201:             {
  202:               ptrdiff_t dest_offset = dest - rpath;
  203:               char *new_rpath;
  204: 
  205:               if (resolved)
  206:                 {
  207:                   __set_errno (ENAMETOOLONG);
  208:                   if (dest > rpath + 1)
  209:                     dest--;
  210:                   *dest = '\0';
  211:                   goto error;
  212:                 }
  213:               new_size = rpath_limit - rpath;
  214:               if (end - start + 1 > path_max)
  215:                 new_size += end - start + 1;
  216:               else
  217:                 new_size += path_max;
  218:               new_rpath = (char *) realloc (rpath, new_size);
  219:               if (new_rpath == NULL)
  220:                 {
  221:                   /* It's easier to set errno to ENOMEM than to rely on the
  222:                      'realloc-posix' gnulib module.  */
  223:                   errno = ENOMEM;
  224:                   goto error;
  225:                 }
  226:               rpath = new_rpath;
  227:               rpath_limit = rpath + new_size;
  228: 
  229:               dest = rpath + dest_offset;
  230:             }
  231: 
  232: #ifdef _LIBC
  233:           dest = __mempcpy (dest, start, end - start);
  234: #else
  235:           memcpy (dest, start, end - start);
  236:           dest += end - start;
  237: #endif
  238:           *dest = '\0';
  239: 
  240: #ifdef _LIBC
  241:           if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
  242: #else
  243:           if (lstat (rpath, &st) < 0)
  244: #endif
  245:             goto error;
  246: 
  247:           if (S_ISLNK (st.st_mode))
  248:             {
  249:               char *buf;
  250:               size_t len;
  251: 
  252:               if (++num_links > MAXSYMLINKS)
  253:                 {
  254:                   __set_errno (ELOOP);
  255:                   goto error;
  256:                 }
  257: 
  258:               buf = malloca (path_max);
  259:               if (!buf)
  260:                 {
  261:                   errno = ENOMEM;
  262:                   goto error;
  263:                 }
  264: 
  265:               n = __readlink (rpath, buf, path_max - 1);
  266:               if (n < 0)
  267:                 {
  268:                   int saved_errno = errno;
  269:                   freea (buf);
  270:                   errno = saved_errno;
  271:                   goto error;
  272:                 }
  273:               buf[n] = '\0';
  274: 
  275:               if (!extra_buf)
  276:                 {
  277:                   extra_buf = malloca (path_max);
  278:                   if (!extra_buf)
  279:                     {
  280:                       freea (buf);
  281:                       errno = ENOMEM;
  282:                       goto error;
  283:                     }
  284:                 }
  285: 
  286:               len = strlen (end);
  287:               if ((long int) (n + len) >= path_max)
  288:                 {
  289:                   freea (buf);
  290:                   __set_errno (ENAMETOOLONG);
  291:                   goto error;
  292:                 }
  293: 
  294:               /* Careful here, end may be a pointer into extra_buf... */
  295:               memmove (&extra_buf[n], end, len + 1);
  296:               name = end = memcpy (extra_buf, buf, n);
  297: 
  298:               if (buf[0] == '/')
  299:                 {
  300:                   dest = rpath + 1;     /* It's an absolute symlink */
  301:                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && buf[1] == '/')
  302:                     *dest++ = '/';
  303:                 }
  304:               else
  305:                 {
  306:                   /* Back up to previous component, ignore if at root
  307:                      already: */
  308:                   if (dest > rpath + 1)
  309:                     while ((--dest)[-1] != '/');
  310:                   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1
  311:                       && *dest == '/')
  312:                     dest++;
  313:                 }
  314:             }
  315:           else if (!S_ISDIR (st.st_mode) && *end != '\0')
  316:             {
  317:               __set_errno (ENOTDIR);
  318:               goto error;
  319:             }
  320:         }
  321:     }
  322:   if (dest > rpath + 1 && dest[-1] == '/')
  323:     --dest;
  324:   if (DOUBLE_SLASH_IS_DISTINCT_ROOT && dest == rpath + 1 && *dest == '/')
  325:     dest++;
  326:   *dest = '\0';
  327: 
  328:   if (extra_buf)
  329:     freea (extra_buf);
  330: 
  331:   return rpath;
  332: 
  333: error:
  334:   {
  335:     int saved_errno = errno;
  336:     if (extra_buf)
  337:       freea (extra_buf);
  338:     if (resolved == NULL)
  339:       free (rpath);
  340:     errno = saved_errno;
  341:   }
  342:   return NULL;
  343: }
  344: versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
  345: #endif /* !FUNC_REALPATH_WORKS || defined _LIBC */
  346: 
  347: 
  348: #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
  349: char *
  350: attribute_compat_text_section
  351: __old_realpath (const char *name, char *resolved)
  352: {
  353:   if (resolved == NULL)
  354:     {
  355:       __set_errno (EINVAL);
  356:       return NULL;
  357:     }
  358: 
  359:   return __realpath (name, resolved);
  360: }
  361: compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
  362: #endif
  363: 
  364: 
  365: char *
  366: __canonicalize_file_name (const char *name)
  367: {
  368:   return __realpath (name, NULL);
  369: }
  370: weak_alias (__canonicalize_file_name, canonicalize_file_name)
  371: 
  372: #else
  373: 
  374: /* This declaration is solely to ensure that after preprocessing
  375:    this file is never empty.  */
  376: typedef int dummy;
  377: 
  378: #endif

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>