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