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