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