Line data Source code
1 : /* Return the canonical absolute name of a given file.
2 : Copyright (C) 1996-2007 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 3 of the License, or
7 : (at your option) 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. If not, see <http://www.gnu.org/licenses/>. */
16 :
17 : #include <config.h>
18 :
19 : #include "canonicalize.h"
20 :
21 : #include <stdlib.h>
22 : #include <string.h>
23 :
24 : #if HAVE_SYS_PARAM_H
25 : # include <sys/param.h>
26 : #endif
27 :
28 : #include <sys/stat.h>
29 :
30 : #include <unistd.h>
31 :
32 : #include <errno.h>
33 : #include <stddef.h>
34 :
35 : #include "file-set.h"
36 : #include "filenamecat.h"
37 : #include "hash-triple.h"
38 : #include "xalloc.h"
39 : #include "xgetcwd.h"
40 :
41 : #ifndef ELOOP
42 : # define ELOOP 0
43 : #endif
44 : #ifndef __set_errno
45 : # define __set_errno(Val) errno = (Val)
46 : #endif
47 :
48 : #include "pathmax.h"
49 : #include "areadlink.h"
50 :
51 : #if !HAVE_CANONICALIZE_FILE_NAME
52 : /* Return the canonical absolute name of file NAME. A canonical name
53 : does not contain any `.', `..' components nor any repeated file name
54 : separators ('/') or symlinks. All components must exist.
55 : The result is malloc'd. */
56 :
57 : char *
58 : canonicalize_file_name (const char *name)
59 : {
60 : # if HAVE_RESOLVEPATH
61 :
62 : char *resolved, *extra_buf = NULL;
63 : size_t resolved_size;
64 : ssize_t resolved_len;
65 :
66 : if (name == NULL)
67 : {
68 : __set_errno (EINVAL);
69 : return NULL;
70 : }
71 :
72 : if (name[0] == '\0')
73 : {
74 : __set_errno (ENOENT);
75 : return NULL;
76 : }
77 :
78 : /* All known hosts with resolvepath (e.g. Solaris 7) don't turn
79 : relative names into absolute ones, so prepend the working
80 : directory if the file name is not absolute. */
81 : if (name[0] != '/')
82 : {
83 : char *wd;
84 :
85 : if (!(wd = xgetcwd ()))
86 : return NULL;
87 :
88 : extra_buf = file_name_concat (wd, name, NULL);
89 : name = extra_buf;
90 : free (wd);
91 : }
92 :
93 : resolved_size = strlen (name);
94 : while (1)
95 : {
96 : resolved_size = 2 * resolved_size + 1;
97 : resolved = xmalloc (resolved_size);
98 : resolved_len = resolvepath (name, resolved, resolved_size);
99 : if (resolved_len < 0)
100 : {
101 : free (resolved);
102 : free (extra_buf);
103 : return NULL;
104 : }
105 : if (resolved_len < resolved_size)
106 : break;
107 : free (resolved);
108 : }
109 :
110 : free (extra_buf);
111 :
112 : /* NUL-terminate the resulting name. */
113 : resolved[resolved_len] = '\0';
114 :
115 : return resolved;
116 :
117 : # else
118 :
119 : return canonicalize_filename_mode (name, CAN_EXISTING);
120 :
121 : # endif /* !HAVE_RESOLVEPATH */
122 : }
123 : #endif /* !HAVE_CANONICALIZE_FILE_NAME */
124 :
125 : /* Return true if we've already seen the triple, <FILENAME, dev, ino>.
126 : If *HT is not initialized, initialize it. */
127 : static bool
128 2 : seen_triple (Hash_table **ht, char const *filename, struct stat const *st)
129 : {
130 2 : if (*ht == NULL)
131 : {
132 2 : size_t initial_capacity = 7;
133 2 : *ht = hash_initialize (initial_capacity,
134 : NULL,
135 : triple_hash,
136 : triple_compare_ino_str,
137 : triple_free);
138 2 : if (*ht == NULL)
139 0 : xalloc_die ();
140 : }
141 :
142 2 : if (seen_file (*ht, filename, st))
143 0 : return true;
144 :
145 2 : record_file (*ht, filename, st);
146 2 : return false;
147 : }
148 :
149 : /* Return the canonical absolute name of file NAME. A canonical name
150 : does not contain any `.', `..' components nor any repeated file name
151 : separators ('/') or symlinks. Whether components must exist
152 : or not depends on canonicalize mode. The result is malloc'd. */
153 :
154 : char *
155 15 : canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
156 : {
157 15 : char *rname, *dest, *extra_buf = NULL;
158 : char const *start;
159 : char const *end;
160 : char const *rname_limit;
161 15 : size_t extra_len = 0;
162 15 : Hash_table *ht = NULL;
163 :
164 15 : if (name == NULL)
165 : {
166 0 : __set_errno (EINVAL);
167 0 : return NULL;
168 : }
169 :
170 15 : if (name[0] == '\0')
171 : {
172 1 : __set_errno (ENOENT);
173 1 : return NULL;
174 : }
175 :
176 14 : if (name[0] != '/')
177 : {
178 9 : rname = xgetcwd ();
179 9 : if (!rname)
180 0 : return NULL;
181 9 : dest = strchr (rname, '\0');
182 9 : if (dest - rname < PATH_MAX)
183 : {
184 9 : char *p = xrealloc (rname, PATH_MAX);
185 9 : dest = p + (dest - rname);
186 9 : rname = p;
187 9 : rname_limit = rname + PATH_MAX;
188 : }
189 : else
190 : {
191 0 : rname_limit = dest;
192 : }
193 : }
194 : else
195 : {
196 5 : rname = xmalloc (PATH_MAX);
197 5 : rname_limit = rname + PATH_MAX;
198 5 : rname[0] = '/';
199 5 : dest = rname + 1;
200 : }
201 :
202 26 : for (start = end = name; *start; start = end)
203 : {
204 : /* Skip sequence of multiple file name separators. */
205 55 : while (*start == '/')
206 17 : ++start;
207 :
208 : /* Find end of component. */
209 19 : for (end = start; *end && *end != '/'; ++end)
210 : /* Nothing. */;
211 :
212 19 : if (end - start == 0)
213 5 : break;
214 14 : else if (end - start == 1 && start[0] == '.')
215 : /* nothing */;
216 11 : else if (end - start == 2 && start[0] == '.' && start[1] == '.')
217 : {
218 : /* Back up to previous component, ignore if at root already. */
219 4 : if (dest > rname + 1)
220 1 : while ((--dest)[-1] != '/');
221 : }
222 : else
223 : {
224 : struct stat st;
225 :
226 9 : if (dest[-1] != '/')
227 5 : *dest++ = '/';
228 :
229 9 : if (dest + (end - start) >= rname_limit)
230 : {
231 0 : ptrdiff_t dest_offset = dest - rname;
232 0 : size_t new_size = rname_limit - rname;
233 :
234 0 : if (end - start + 1 > PATH_MAX)
235 0 : new_size += end - start + 1;
236 : else
237 0 : new_size += PATH_MAX;
238 0 : rname = xrealloc (rname, new_size);
239 0 : rname_limit = rname + new_size;
240 :
241 0 : dest = rname + dest_offset;
242 : }
243 :
244 9 : dest = memcpy (dest, start, end - start);
245 9 : dest += end - start;
246 9 : *dest = '\0';
247 :
248 9 : if (lstat (rname, &st) != 0)
249 : {
250 3 : if (can_mode == CAN_EXISTING)
251 3 : goto error;
252 2 : if (can_mode == CAN_ALL_BUT_LAST && *end)
253 1 : goto error;
254 1 : st.st_mode = 0;
255 : }
256 :
257 7 : if (S_ISLNK (st.st_mode))
258 : {
259 : char *buf;
260 : size_t n, len;
261 :
262 : /* Detect loops. We cannot use the cycle-check module here,
263 : since it's actually possible to encounter the same symlink
264 : more than once in a given traversal. However, encountering
265 : the same symlink,NAME pair twice does indicate a loop. */
266 2 : if (seen_triple (&ht, name, &st))
267 : {
268 0 : __set_errno (ELOOP);
269 0 : if (can_mode == CAN_MISSING)
270 0 : continue;
271 : else
272 0 : goto error;
273 : }
274 :
275 2 : buf = areadlink_with_size (rname, st.st_size);
276 2 : if (!buf)
277 : {
278 0 : if (can_mode == CAN_MISSING && errno != ENOMEM)
279 0 : continue;
280 : else
281 : goto error;
282 : }
283 :
284 2 : n = strlen (buf);
285 2 : len = strlen (end);
286 :
287 2 : if (!extra_len)
288 : {
289 2 : extra_len =
290 2 : ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX;
291 2 : extra_buf = xmalloc (extra_len);
292 : }
293 0 : else if ((n + len + 1) > extra_len)
294 : {
295 0 : extra_len = n + len + 1;
296 0 : extra_buf = xrealloc (extra_buf, extra_len);
297 : }
298 :
299 : /* Careful here, end may be a pointer into extra_buf... */
300 2 : memmove (&extra_buf[n], end, len + 1);
301 2 : name = end = memcpy (extra_buf, buf, n);
302 :
303 2 : if (buf[0] == '/')
304 0 : dest = rname + 1; /* It's an absolute symlink */
305 : else
306 : /* Back up to previous component, ignore if at root already: */
307 2 : if (dest > rname + 1)
308 2 : while ((--dest)[-1] != '/');
309 :
310 2 : free (buf);
311 : }
312 : else
313 : {
314 5 : if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING))
315 : {
316 0 : errno = ENOTDIR;
317 0 : goto error;
318 : }
319 : }
320 : }
321 : }
322 12 : if (dest > rname + 1 && dest[-1] == '/')
323 1 : --dest;
324 12 : *dest = '\0';
325 :
326 12 : free (extra_buf);
327 12 : if (ht)
328 2 : hash_free (ht);
329 12 : return rname;
330 :
331 2 : error:
332 2 : free (extra_buf);
333 2 : free (rname);
334 2 : if (ht)
335 0 : hash_free (ht);
336 2 : return NULL;
337 : }
|