Mark compat code with attribute_compat_text_section.
[kopensolaris-gnu/glibc.git] / stdlib / canonicalize.c
1 /* Return the canonical absolute name of a given file.
2    Copyright (C) 1996-2001, 2002, 2004 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 = strchr (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);
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         }
203     }
204   if (dest > rpath + 1 && dest[-1] == '/')
205     --dest;
206   *dest = '\0';
207
208   assert (resolved == NULL || resolved == rpath);
209   return rpath;
210
211 error:
212   assert (resolved == NULL || resolved == rpath);
213   if (resolved == NULL)
214     free (rpath);
215   return NULL;
216 }
217 versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
218
219
220 #if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
221 char *
222 attribute_compat_text_section
223 __old_realpath (const char *name, char *resolved)
224 {
225   if (resolved == NULL)
226     {
227       __set_errno (EINVAL);
228       return NULL;
229     }
230
231   return __realpath (name, resolved);
232 }
233 compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
234 #endif
235
236
237 char *
238 __canonicalize_file_name (const char *name)
239 {
240   return __realpath (name, NULL);
241 }
242 weak_alias (__canonicalize_file_name, canonicalize_file_name)