(linenum→info "unix/slp.c:2238")

coreutils/6.9/lib/canonicalize.c

    1: /* Return the canonical absolute name of a given file.
    2:    Copyright (C) 1996-2007 Free Software Foundation, Inc.
    3: 
    4:    This program is free software; you can redistribute it and/or modify
    5:    it under the terms of the GNU General Public License as published by
    6:    the Free Software Foundation; either version 2, or (at your option)
    7:    any later version.
    8: 
    9:    This program is distributed in the hope that it will be useful,
   10:    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11:    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12:    GNU General Public License for more details.
   13: 
   14:    You should have received a copy of the GNU General Public License
   15:    along with this program; see the file COPYING.
   16:    If not, write to the Free Software Foundation,
   17:    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
   18: 
   19: #include <config.h>
   20: 
   21: #include "canonicalize.h"
   22: 
   23: #include <stdlib.h>
   24: #include <string.h>
   25: 
   26: #if HAVE_SYS_PARAM_H
   27: # include <sys/param.h>
   28: #endif
   29: 
   30: #include <sys/stat.h>
   31: 
   32: #include <unistd.h>
   33: 
   34: #include <errno.h>
   35: #include <stddef.h>
   36: 
   37: #include "cycle-check.h"
   38: #include "filenamecat.h"
   39: #include "xalloc.h"
   40: #include "xgetcwd.h"
   41: 
   42: #ifndef ELOOP
   43: # define ELOOP 0
   44: #endif
   45: #ifndef __set_errno
   46: # define __set_errno(Val) errno = (Val)
   47: #endif
   48: 
   49: #include "pathmax.h"
   50: #include "xreadlink.h"
   51: 
   52: #if !HAVE_CANONICALIZE_FILE_NAME
   53: /* Return the canonical absolute name of file NAME.  A canonical name
   54:    does not contain any `.', `..' components nor any repeated file name
   55:    separators ('/') or symlinks.  All components must exist.
   56:    The result is malloc'd.  */
   57: 
   58: char *
   59: canonicalize_file_name (const char *name)
   60: {
   61: # if HAVE_RESOLVEPATH
   62: 
   63:   char *resolved, *extra_buf = NULL;
   64:   size_t resolved_size;
   65:   ssize_t resolved_len;
   66: 
   67:   if (name == NULL)
   68:     {
   69:       __set_errno (EINVAL);
   70:       return NULL;
   71:     }
   72: 
   73:   if (name[0] == '\0')
   74:     {
   75:       __set_errno (ENOENT);
   76:       return NULL;
   77:     }
   78: 
   79:   /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
   80:      relative names into absolute ones, so prepend the working
   81:      directory if the file name is not absolute.  */
   82:   if (name[0] != '/')
   83:     {
   84:       char *wd;
   85: 
   86:       if (!(wd = xgetcwd ()))
   87:         return NULL;
   88: 
   89:       extra_buf = file_name_concat (wd, name, NULL);
   90:       name = extra_buf;
   91:       free (wd);
   92:     }
   93: 
   94:   resolved_size = strlen (name);
   95:   while (1)
   96:     {
   97:       resolved_size = 2 * resolved_size + 1;
   98:       resolved = xmalloc (resolved_size);
   99:       resolved_len = resolvepath (name, resolved, resolved_size);
  100:       if (resolved_len < 0)
  101:         {
  102:           free (resolved);
  103:           free (extra_buf);
  104:           return NULL;
  105:         }
  106:       if (resolved_len < resolved_size)
  107:         break;
  108:       free (resolved);
  109:     }
  110: 
  111:   free (extra_buf);
  112: 
  113:   /* NUL-terminate the resulting name.  */
  114:   resolved[resolved_len] = '\0';
  115: 
  116:   return resolved;
  117: 
  118: # else
  119: 
  120:   return canonicalize_filename_mode (name, CAN_EXISTING);
  121: 
  122: # endif /* !HAVE_RESOLVEPATH */
  123: }
  124: #endif /* !HAVE_CANONICALIZE_FILE_NAME */
  125: 
  126: /* Return the canonical absolute name of file NAME.  A canonical name
  127:    does not contain any `.', `..' components nor any repeated file name
  128:    separators ('/') or symlinks.  Whether components must exist
  129:    or not depends on canonicalize mode.  The result is malloc'd.  */
  130: 
  131: char *
  132: canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
  133: {
  134:   char *rname, *dest, *extra_buf = NULL;
  135:   char const *start;
  136:   char const *end;
  137:   char const *rname_limit;
  138:   size_t extra_len = 0;
  139:   struct cycle_check_state cycle_state;
  140: 
  141:   if (name == NULL)
  142:     {
  143:       __set_errno (EINVAL);
  144:       return NULL;
  145:     }
  146: 
  147:   if (name[0] == '\0')
  148:     {
  149:       __set_errno (ENOENT);
  150:       return NULL;
  151:     }
  152: 
  153:   if (name[0] != '/')
  154:     {
  155:       rname = xgetcwd ();
  156:       if (!rname)
  157:         return NULL;
  158:       dest = strchr (rname, '\0');
  159:       if (dest - rname < PATH_MAX)
  160:         {
  161:           char *p = xrealloc (rname, PATH_MAX);
  162:           dest = p + (dest - rname);
  163:           rname = p;
  164:           rname_limit = rname + PATH_MAX;
  165:         }
  166:       else
  167:         {
  168:           rname_limit = dest;
  169:         }
  170:     }
  171:   else
  172:     {
  173:       rname = xmalloc (PATH_MAX);
  174:       rname_limit = rname + PATH_MAX;
  175:       rname[0] = '/';
  176:       dest = rname + 1;
  177:     }
  178: 
  179:   cycle_check_init (&cycle_state);
  180:   for (start = end = name; *start; start = end)
  181:     {
  182:       /* Skip sequence of multiple file name separators.  */
  183:       while (*start == '/')
  184:         ++start;
  185: 
  186:       /* Find end of component.  */
  187:       for (end = start; *end && *end != '/'; ++end)
  188:         /* Nothing.  */;
  189: 
  190:       if (end - start == 0)
  191:         break;
  192:       else if (end - start == 1 && start[0] == '.')
  193:         /* nothing */;
  194:       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
  195:         {
  196:           /* Back up to previous component, ignore if at root already.  */
  197:           if (dest > rname + 1)
  198:             while ((--dest)[-1] != '/');
  199:         }
  200:       else
  201:         {
  202:           struct stat st;
  203: 
  204:           if (dest[-1] != '/')
  205:             *dest++ = '/';
  206: 
  207:           if (dest + (end - start) >= rname_limit)
  208:             {
  209:               ptrdiff_t dest_offset = dest - rname;
  210:               size_t new_size = rname_limit - rname;
  211: 
  212:               if (end - start + 1 > PATH_MAX)
  213:                 new_size += end - start + 1;
  214:               else
  215:                 new_size += PATH_MAX;
  216:               rname = xrealloc (rname, new_size);
  217:               rname_limit = rname + new_size;
  218: 
  219:               dest = rname + dest_offset;
  220:             }
  221: 
  222:           dest = memcpy (dest, start, end - start);
  223:           dest += end - start;
  224:           *dest = '\0';
  225: 
  226:           if (lstat (rname, &st) != 0)
  227:             {
  228:               if (can_mode == CAN_EXISTING)
  229:                 goto error;
  230:               if (can_mode == CAN_ALL_BUT_LAST && *end)
  231:                 goto error;
  232:               st.st_mode = 0;
  233:             }
  234: 
  235:           if (S_ISLNK (st.st_mode))
  236:             {
  237:               char *buf;
  238:               size_t n, len;
  239: 
  240:               if (cycle_check (&cycle_state, &st))
  241:                 {
  242:                   __set_errno (ELOOP);
  243:                   if (can_mode == CAN_MISSING)
  244:                     continue;
  245:                   else
  246:                     goto error;
  247:                 }
  248: 
  249:               buf = xreadlink_with_size (rname, st.st_size);
  250:               if (!buf)
  251:                 {
  252:                   if (can_mode == CAN_MISSING)
  253:                     continue;
  254:                   else
  255:                     goto error;
  256:                 }
  257: 
  258:               n = strlen (buf);
  259:               len = strlen (end);
  260: 
  261:               if (!extra_len)
  262:                 {
  263:                   extra_len =
  264:                     ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX;
  265:                   extra_buf = xmalloc (extra_len);
  266:                 }
  267:               else if ((n + len + 1) > extra_len)
  268:                 {
  269:                   extra_len = n + len + 1;
  270:                   extra_buf = xrealloc (extra_buf, extra_len);
  271:                 }
  272: 
  273:               /* Careful here, end may be a pointer into extra_buf... */
  274:               memmove (&extra_buf[n], end, len + 1);
  275:               name = end = memcpy (extra_buf, buf, n);
  276: 
  277:               if (buf[0] == '/')
  278:                 dest = rname + 1;     /* It's an absolute symlink */
  279:               else
  280:                 /* Back up to previous component, ignore if at root already: */
  281:                 if (dest > rname + 1)
  282:                   while ((--dest)[-1] != '/');
  283: 
  284:               free (buf);
  285:             }
  286:           else
  287:             {
  288:               if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING))
  289:                 {
  290:                   errno = ENOTDIR;
  291:                   goto error;
  292:                 }
  293:             }
  294:         }
  295:     }
  296:   if (dest > rname + 1 && dest[-1] == '/')
  297:     --dest;
  298:   *dest = '\0';
  299: 
  300:   free (extra_buf);
  301:   return rname;
  302: 
  303: error:
  304:   free (extra_buf);
  305:   free (rname);
  306:   return NULL;
  307: }
Syntax (Markdown)