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