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