Fix conflict between modules 'canonicalize' and 'canonicalize-lgpl' differently.
[gnulib.git] / lib / canonicalize.c
1 /* Return the canonical absolute name of a given file.
2    Copyright (C) 1996-2008 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 || GNULIB_CANONICALIZE_LGPL)
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 seen_triple (Hash_table **ht, char const *filename, struct stat const *st)
129 {
130   if (*ht == NULL)
131     {
132       size_t initial_capacity = 7;
133       *ht = hash_initialize (initial_capacity,
134                             NULL,
135                             triple_hash,
136                             triple_compare_ino_str,
137                             triple_free);
138       if (*ht == NULL)
139         xalloc_die ();
140     }
141
142   if (seen_file (*ht, filename, st))
143     return true;
144
145   record_file (*ht, filename, st);
146   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 canonicalize_filename_mode (const char *name, canonicalize_mode_t can_mode)
156 {
157   char *rname, *dest, *extra_buf = NULL;
158   char const *start;
159   char const *end;
160   char const *rname_limit;
161   size_t extra_len = 0;
162   Hash_table *ht = NULL;
163
164   if (name == NULL)
165     {
166       __set_errno (EINVAL);
167       return NULL;
168     }
169
170   if (name[0] == '\0')
171     {
172       __set_errno (ENOENT);
173       return NULL;
174     }
175
176   if (name[0] != '/')
177     {
178       rname = xgetcwd ();
179       if (!rname)
180         return NULL;
181       dest = strchr (rname, '\0');
182       if (dest - rname < PATH_MAX)
183         {
184           char *p = xrealloc (rname, PATH_MAX);
185           dest = p + (dest - rname);
186           rname = p;
187           rname_limit = rname + PATH_MAX;
188         }
189       else
190         {
191           rname_limit = dest;
192         }
193     }
194   else
195     {
196       rname = xmalloc (PATH_MAX);
197       rname_limit = rname + PATH_MAX;
198       rname[0] = '/';
199       dest = rname + 1;
200     }
201
202   for (start = end = name; *start; start = end)
203     {
204       /* Skip sequence of multiple file name separators.  */
205       while (*start == '/')
206         ++start;
207
208       /* Find end of component.  */
209       for (end = start; *end && *end != '/'; ++end)
210         /* Nothing.  */;
211
212       if (end - start == 0)
213         break;
214       else if (end - start == 1 && start[0] == '.')
215         /* nothing */;
216       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
217         {
218           /* Back up to previous component, ignore if at root already.  */
219           if (dest > rname + 1)
220             while ((--dest)[-1] != '/');
221         }
222       else
223         {
224           struct stat st;
225
226           if (dest[-1] != '/')
227             *dest++ = '/';
228
229           if (dest + (end - start) >= rname_limit)
230             {
231               ptrdiff_t dest_offset = dest - rname;
232               size_t new_size = rname_limit - rname;
233
234               if (end - start + 1 > PATH_MAX)
235                 new_size += end - start + 1;
236               else
237                 new_size += PATH_MAX;
238               rname = xrealloc (rname, new_size);
239               rname_limit = rname + new_size;
240
241               dest = rname + dest_offset;
242             }
243
244           dest = memcpy (dest, start, end - start);
245           dest += end - start;
246           *dest = '\0';
247
248           if (lstat (rname, &st) != 0)
249             {
250               if (can_mode == CAN_EXISTING)
251                 goto error;
252               if (can_mode == CAN_ALL_BUT_LAST && *end)
253                 goto error;
254               st.st_mode = 0;
255             }
256
257           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               if (seen_triple (&ht, name, &st))
267                 {
268                   __set_errno (ELOOP);
269                   if (can_mode == CAN_MISSING)
270                     continue;
271                   else
272                     goto error;
273                 }
274
275               buf = areadlink_with_size (rname, st.st_size);
276               if (!buf)
277                 {
278                   if (can_mode == CAN_MISSING && errno != ENOMEM)
279                     continue;
280                   else
281                     goto error;
282                 }
283
284               n = strlen (buf);
285               len = strlen (end);
286
287               if (!extra_len)
288                 {
289                   extra_len =
290                     ((n + len + 1) > PATH_MAX) ? (n + len + 1) : PATH_MAX;
291                   extra_buf = xmalloc (extra_len);
292                 }
293               else if ((n + len + 1) > extra_len)
294                 {
295                   extra_len = n + len + 1;
296                   extra_buf = xrealloc (extra_buf, extra_len);
297                 }
298
299               /* Careful here, end may be a pointer into extra_buf... */
300               memmove (&extra_buf[n], end, len + 1);
301               name = end = memcpy (extra_buf, buf, n);
302
303               if (buf[0] == '/')
304                 dest = rname + 1;       /* It's an absolute symlink */
305               else
306                 /* Back up to previous component, ignore if at root already: */
307                 if (dest > rname + 1)
308                   while ((--dest)[-1] != '/');
309
310               free (buf);
311             }
312           else
313             {
314               if (!S_ISDIR (st.st_mode) && *end && (can_mode != CAN_MISSING))
315                 {
316                   errno = ENOTDIR;
317                   goto error;
318                 }
319             }
320         }
321     }
322   if (dest > rname + 1 && dest[-1] == '/')
323     --dest;
324   *dest = '\0';
325
326   free (extra_buf);
327   if (ht)
328     hash_free (ht);
329   return rname;
330
331 error:
332   free (extra_buf);
333   free (rname);
334   if (ht)
335     hash_free (ht);
336   return NULL;
337 }