LCOV - code coverage report
Current view: top level - lib - canonicalize.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 80 107 74.8 %
Date: 2018-01-30 Functions: 2 2 100.0 %

          Line data    Source code
       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 3 of the License, or
       7             :    (at your option) 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.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : #include <config.h>
      18             : 
      19             : #include "canonicalize.h"
      20             : 
      21             : #include <stdlib.h>
      22             : #include <string.h>
      23             : 
      24             : #if HAVE_SYS_PARAM_H
      25             : # include <sys/param.h>
      26             : #endif
      27             : 
      28             : #include <sys/stat.h>
      29             : 
      30             : #include <unistd.h>
      31             : 
      32             : #include <errno.h>
      33             : #include <stddef.h>
      34             : 
      35             : #include "file-set.h"
      36             : #include "filenamecat.h"
      37             : #include "hash-triple.h"
      38             : #include "xalloc.h"
      39             : #include "xgetcwd.h"
      40             : 
      41             : #ifndef ELOOP
      42             : # define ELOOP 0
      43             : #endif
      44             : #ifndef __set_errno
      45             : # define __set_errno(Val) errno = (Val)
      46             : #endif
      47             : 
      48             : #include "pathmax.h"
      49             : #include "areadlink.h"
      50             : 
      51             : #if !HAVE_CANONICALIZE_FILE_NAME
      52             : /* Return the canonical absolute name of file NAME.  A canonical name
      53             :    does not contain any `.', `..' components nor any repeated file name
      54             :    separators ('/') or symlinks.  All components must exist.
      55             :    The result is malloc'd.  */
      56             : 
      57             : char *
      58             : canonicalize_file_name (const char *name)
      59             : {
      60             : # if HAVE_RESOLVEPATH
      61             : 
      62             :   char *resolved, *extra_buf = NULL;
      63             :   size_t resolved_size;
      64             :   ssize_t resolved_len;
      65             : 
      66             :   if (name == NULL)
      67             :     {
      68             :       __set_errno (EINVAL);
      69             :       return NULL;
      70             :     }
      71             : 
      72             :   if (name[0] == '\0')
      73             :     {
      74             :       __set_errno (ENOENT);
      75             :       return NULL;
      76             :     }
      77             : 
      78             :   /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
      79             :      relative names into absolute ones, so prepend the working
      80             :      directory if the file name is not absolute.  */
      81             :   if (name[0] != '/')
      82             :     {
      83             :       char *wd;
      84             : 
      85             :       if (!(wd = xgetcwd ()))
      86             :         return NULL;
      87             : 
      88             :       extra_buf = file_name_concat (wd, name, NULL);
      89             :       name = extra_buf;
      90             :       free (wd);
      91             :     }
      92             : 
      93             :   resolved_size = strlen (name);
      94             :   while (1)
      95             :     {
      96             :       resolved_size = 2 * resolved_size + 1;
      97             :       resolved = xmalloc (resolved_size);
      98             :       resolved_len = resolvepath (name, resolved, resolved_size);
      99             :       if (resolved_len < 0)
     100             :         {
     101             :           free (resolved);
     102             :           free (extra_buf);
     103             :           return NULL;
     104             :         }
     105             :       if (resolved_len < resolved_size)
     106             :         break;
     107             :       free (resolved);
     108             :     }
     109             : 
     110             :   free (extra_buf);
     111             : 
     112             :   /* NUL-terminate the resulting name.  */
     113             :   resolved[resolved_len] = '\0';
     114             : 
     115             :   return resolved;
     116             : 
     117             : # else
     118             : 
     119             :   return canonicalize_filename_mode (name, CAN_EXISTING);
     120             : 
     121             : # endif /* !HAVE_RESOLVEPATH */
     122             : }
     123             : #endif /* !HAVE_CANONICALIZE_FILE_NAME */
     124             : 
     125             : /* Return true if we've already seen the triple, <FILENAME, dev, ino>.
     126             :    If *HT is not initialized, initialize it.  */
     127             : static bool
     128           2 : seen_triple (Hash_table **ht, char const *filename, struct stat const *st)
     129             : {
     130           2 :   if (*ht == NULL)
     131             :     {
     132           2 :       size_t initial_capacity = 7;
     133           2 :       *ht = hash_initialize (initial_capacity,
     134             :                             NULL,
     135             :                             triple_hash,
     136             :                             triple_compare_ino_str,
     137             :                             triple_free);
     138           2 :       if (*ht == NULL)
     139           0 :         xalloc_die ();
     140             :     }
     141             : 
     142           2 :   if (seen_file (*ht, filename, st))
     143           0 :     return true;
     144             : 
     145           2 :   record_file (*ht, filename, st);
     146           2 :   return false;
     147             : }
     148             : 
     149             : /* Return the canonical absolute name of file NAME.  A canonical name
     150             :    does not contain any `.', `..' components nor any repeated file name
     151             :    separators ('/') or symlinks.  Whether components must exist
     152             :    or not depends on canonicalize mode.  The result is malloc'd.  */
     153             : 
     154             : char *
     155          15 : canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
     156             : {
     157          15 :   char *rname, *dest, *extra_buf = NULL;
     158             :   char const *start;
     159             :   char const *end;
     160             :   char const *rname_limit;
     161          15 :   size_t extra_len = 0;
     162          15 :   Hash_table *ht = NULL;
     163             : 
     164          15 :   if (name == NULL)
     165             :     {
     166           0 :       __set_errno (EINVAL);
     167           0 :       return NULL;
     168             :     }
     169             : 
     170          15 :   if (name[0] == '\0')
     171             :     {
     172           1 :       __set_errno (ENOENT);
     173           1 :       return NULL;
     174             :     }
     175             : 
     176          14 :   if (name[0] != '/')
     177             :     {
     178           9 :       rname = xgetcwd ();
     179           9 :       if (!rname)
     180           0 :         return NULL;
     181           9 :       dest = strchr (rname, '\0');
     182           9 :       if (dest - rname < PATH_MAX)
     183             :         {
     184           9 :           char *p = xrealloc (rname, PATH_MAX);
     185           9 :           dest = p + (dest - rname);
     186           9 :           rname = p;
     187           9 :           rname_limit = rname + PATH_MAX;
     188             :         }
     189             :       else
     190             :         {
     191           0 :           rname_limit = dest;
     192             :         }
     193             :     }
     194             :   else
     195             :     {
     196           5 :       rname = xmalloc (PATH_MAX);
     197           5 :       rname_limit = rname + PATH_MAX;
     198           5 :       rname[0] = '/';
     199           5 :       dest = rname + 1;
     200             :     }
     201             : 
     202          26 :   for (start = end = name; *start; start = end)
     203             :     {
     204             :       /* Skip sequence of multiple file name separators.  */
     205          55 :       while (*start == '/')
     206          17 :         ++start;
     207             : 
     208             :       /* Find end of component.  */
     209          19 :       for (end = start; *end && *end != '/'; ++end)
     210             :         /* Nothing.  */;
     211             : 
     212          19 :       if (end - start == 0)
     213           5 :         break;
     214          14 :       else if (end - start == 1 && start[0] == '.')
     215             :         /* nothing */;
     216          11 :       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
     217             :         {
     218             :           /* Back up to previous component, ignore if at root already.  */
     219           4 :           if (dest > rname + 1)
     220           1 :             while ((--dest)[-1] != '/');
     221             :         }
     222             :       else
     223             :         {
     224             :           struct stat st;
     225             : 
     226           9 :           if (dest[-1] != '/')
     227           5 :             *dest++ = '/';
     228             : 
     229           9 :           if (dest + (end - start) >= rname_limit)
     230             :             {
     231           0 :               ptrdiff_t dest_offset = dest - rname;
     232           0 :               size_t new_size = rname_limit - rname;
     233             : 
     234           0 :               if (end - start + 1 > PATH_MAX)
     235           0 :                 new_size += end - start + 1;
     236             :               else
     237           0 :                 new_size += PATH_MAX;
     238           0 :               rname = xrealloc (rname, new_size);
     239           0 :               rname_limit = rname + new_size;
     240             : 
     241           0 :               dest = rname + dest_offset;
     242             :             }
     243             : 
     244           9 :           dest = memcpy (dest, start, end - start);
     245           9 :           dest += end - start;
     246           9 :           *dest = '\0';
     247             : 
     248           9 :           if (lstat (rname, &st) != 0)
     249             :             {
     250           3 :               if (can_mode == CAN_EXISTING)
     251           3 :                 goto error;
     252           2 :               if (can_mode == CAN_ALL_BUT_LAST && *end)
     253           1 :                 goto error;
     254           1 :               st.st_mode = 0;
     255             :             }
     256             : 
     257           7 :           if (S_ISLNK (st.st_mode))
     258             :             {
     259             :               char *buf;
     260             :               size_t n, len;
     261             : 
     262             :               /* Detect loops.  We cannot use the cycle-check module here,
     263             :                  since it's actually possible to encounter the same symlink
     264             :                  more than once in a given traversal.  However, encountering
     265             :                  the same symlink,NAME pair twice does indicate a loop.  */
     266           2 :               if (seen_triple (&ht, name, &st))
     267             :                 {
     268           0 :                   __set_errno (ELOOP);
     269           0 :                   if (can_mode == CAN_MISSING)
     270           0 :                     continue;
     271             :                   else
     272           0 :                     goto error;
     273             :                 }
     274             : 
     275           2 :               buf = areadlink_with_size (rname, st.st_size);
     276           2 :               if (!buf)
     277             :                 {
     278           0 :                   if (can_mode == CAN_MISSING && errno != ENOMEM)
     279           0 :                     continue;
     280             :                   else
     281             :                     goto error;
     282             :                 }
     283             : 
     284           2 :               n = strlen (buf);
     285           2 :               len = strlen (end);
     286             : 
     287           2 :               if (!extra_len)
     288             :                 {
     289           2 :                   extra_len =
     290           2 :                     ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX;
     291           2 :                   extra_buf = xmalloc (extra_len);
     292             :                 }
     293           0 :               else if ((n + len + 1) > extra_len)
     294             :                 {
     295           0 :                   extra_len = n + len + 1;
     296           0 :                   extra_buf = xrealloc (extra_buf, extra_len);
     297             :                 }
     298             : 
     299             :               /* Careful here, end may be a pointer into extra_buf... */
     300           2 :               memmove (&extra_buf[n], end, len + 1);
     301           2 :               name = end = memcpy (extra_buf, buf, n);
     302             : 
     303           2 :               if (buf[0] == '/')
     304           0 :                 dest = rname + 1;       /* It's an absolute symlink */
     305             :               else
     306             :                 /* Back up to previous component, ignore if at root already: */
     307           2 :                 if (dest > rname + 1)
     308           2 :                   while ((--dest)[-1] != '/');
     309             : 
     310           2 :               free (buf);
     311             :             }
     312             :           else
     313             :             {
     314           5 :               if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING))
     315             :                 {
     316           0 :                   errno = ENOTDIR;
     317           0 :                   goto error;
     318             :                 }
     319             :             }
     320             :         }
     321             :     }
     322          12 :   if (dest > rname + 1 && dest[-1] == '/')
     323           1 :     --dest;
     324          12 :   *dest = '\0';
     325             : 
     326          12 :   free (extra_buf);
     327          12 :   if (ht)
     328           2 :     hash_free (ht);
     329          12 :   return rname;
     330             : 
     331           2 : error:
     332           2 :   free (extra_buf);
     333           2 :   free (rname);
     334           2 :   if (ht)
     335           0 :     hash_free (ht);
     336           2 :   return NULL;
     337             : }

Generated by: LCOV version 1.10