Updated to fedora-glibc-20080703T1203
[kopensolaris-gnu/glibc.git] / stdlib / canonicalize.c
1 /* Return the canonical absolute name of a given file.
2    Copyright (C) 1996-2002,2004,2005,2006,2008 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <assert.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <limits.h>
25 #include <sys/param.h>
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <stddef.h>
29
30 #include <shlib-compat.h>
31
32 /* Return the canonical absolute name of file NAME.  A canonical name
33    does not contain any `.', `..' components nor any repeated path
34    separators ('/') or symlinks.  All path components must exist.  If
35    RESOLVED is null, the result is malloc'd; otherwise, if the
36    canonical name is PATH_MAX chars or more, returns null with `errno'
37    set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
38    returns the name in RESOLVED.  If the name cannot be resolved and
39    RESOLVED is non-NULL, it contains the path of the first component
40    that cannot be resolved.  If the path can be resolved, RESOLVED
41    holds the same value as the value returned.  */
42
43 char *
44 __realpath (const char *name, char *resolved)
45 {
46   char *rpath, *dest, *extra_buf = NULL;
47   const char *start, *end, *rpath_limit;
48   long int path_max;
49   int num_links = 0;
50
51   if (name == NULL)
52     {
53       /* As per Single Unix Specification V2 we must return an error if
54          either parameter is a null pointer.  We extend this to allow
55          the RESOLVED parameter to be NULL in case the we are expected to
56          allocate the room for the return value.  */
57       __set_errno (EINVAL);
58       return NULL;
59     }
60
61   if (name[0] == '\0')
62     {
63       /* As per Single Unix Specification V2 we must return an error if
64          the name argument points to an empty string.  */
65       __set_errno (ENOENT);
66       return NULL;
67     }
68
69 #ifdef PATH_MAX
70   path_max = PATH_MAX;
71 #else
72   path_max = pathconf (name, _PC_PATH_MAX);
73   if (path_max <= 0)
74     path_max = 1024;
75 #endif
76
77   if (resolved == NULL)
78     {
79       rpath = malloc (path_max);
80       if (rpath == NULL)
81         return NULL;
82     }
83   else
84     rpath = resolved;
85   rpath_limit = rpath + path_max;
86
87   if (name[0] != '/')
88     {
89       if (!__getcwd (rpath, path_max))
90         {
91           rpath[0] = '\0';
92           goto error;
93         }
94       dest = __rawmemchr (rpath, '\0');
95     }
96   else
97     {
98       rpath[0] = '/';
99       dest = rpath + 1;
100     }
101
102   for (start = end = name; *start; start = end)
103     {
104       struct stat64 st;
105       int n;
106
107       /* Skip sequence of multiple path-separators.  */
108       while (*start == '/')
109         ++start;
110
111       /* Find end of path component.  */
112       for (end = start; *end && *end != '/'; ++end)
113         /* Nothing.  */;
114
115       if (end - start == 0)
116         break;
117       else if (end - start == 1 && start[0] == '.')
118         /* nothing */;
119       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
120         {
121           /* Back up to previous component, ignore if at root already.  */
122           if (dest > rpath + 1)
123             while ((--dest)[-1] != '/');
124         }
125       else
126         {
127           size_t new_size;
128
129           if (dest[-1] != '/')
130             *dest++ = '/';
131
132           if (dest + (end - start) >= rpath_limit)
133             {
134               ptrdiff_t dest_offset = dest - rpath;
135               char *new_rpath;
136
137               if (resolved)
138                 {
139                   __set_errno (ENAMETOOLONG);
140                   if (dest > rpath + 1)
141                     dest--;
142                   *dest = '\0';
143                   goto error;
144                 }
145               new_size = rpath_limit - rpath;
146               if (end - start + 1 > path_max)
147                 new_size += end - start + 1;
148               else
149                 new_size += path_max;
150               new_rpath = (char *) realloc (rpath, new_size);
151               if (new_rpath == NULL)
152                 goto error;
153               rpath = new_rpath;
154               rpath_limit = rpath + new_size;
155
156               dest = rpath + dest_offset;
157             }
158
159           dest = __mempcpy (dest, start, end - start);
160           *dest = '\0';
161
162           if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
163             goto error;
164
165           if (S_ISLNK (st.st_mode))
166             {
167               char *buf = __alloca (path_max);
168               size_t len;
169
170               if (++num_links > MAXSYMLINKS)
171                 {
172                   __set_errno (ELOOP);
173                   goto error;
174                 }
175
176               n = __readlink (rpath, buf, path_max - 1);
177               if (n < 0)
178                 goto error;
179               buf[n] = '\0';
180
181               if (!extra_buf)
182                 extra_buf = __alloca (path_max);
183
184               len = strlen (end);
185               if ((long int) (n + len) >= path_max)
186                 {
187                   __set_errno (ENAMETOOLONG);
188                   goto error;
189                 }
190
191               /* Careful here, end may be a pointer into extra_buf... */
192               memmove (&extra_buf[n], end, len + 1);
193               name = end = memcpy (extra_buf, buf, n);
194
195               if (buf[0] == '/')
196                 dest = rpath + 1;       /* It's an absolute symlink */
197               else
198                 /* Back up to previous component, ignore if at root already: */
199                 if (dest > rpath + 1)
200                   while ((--dest)[-1] != '/');
201             }
202           else if (!S_ISDIR (st.st_mode) && *end != '\0')
203             {
204               __set_errno (ENOTDIR);
205               goto error;
206             }
207         }
208     }
209   if (dest > rpath + 1 && dest[-1] == '/')
210     --dest;
211   *dest = '\0';
212
213   assert (resolved == NULL || resolved == rpath);
214   return rpath;
215
216 error:
217   assert (resolved == NULL || resolved == rpath);
218   if (resolved == NULL)
219     free (rpath);
220   return NULL;
221 }
222 versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
223
224
225 #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
226 char *
227 attribute_compat_text_section
228 __old_realpath (const char *name, char *resolved)
229 {
230   if (resolved == NULL)
231     {
232       __set_errno (EINVAL);
233       return NULL;
234     }
235
236   return __realpath (name, resolved);
237 }
238 compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
239 #endif
240
241
242 char *
243 __canonicalize_file_name (const char *name)
244 {
245   return __realpath (name, NULL);
246 }
247 weak_alias (__canonicalize_file_name, canonicalize_file_name)