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