1/* Return the canonical absolute name of a given file.
2   Copyright (C) 1996-2003, 2005 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., 51 Franklin Street, Fifth Floor, Boston,
18   MA 02110-1301, USA.  */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24/* Avoid a clash of our rpl_realpath() function with the prototype in
25   <stdlib.h> on Solaris 2.5.1.  */
26#undef realpath
27
28#if !HAVE_CANONICALIZE_FILE_NAME || defined _LIBC
29
30#include <alloca.h>
31
32/* Specification.  */
33#include "canonicalize.h"
34
35#include <stddef.h>
36#include <stdlib.h>
37#include <string.h>
38
39#if HAVE_UNISTD_H || defined _LIBC
40# include <unistd.h>
41#endif
42
43#include <limits.h>
44
45#if HAVE_SYS_PARAM_H || defined _LIBC
46# include <sys/param.h>
47#endif
48#ifndef MAXSYMLINKS
49# define MAXSYMLINKS 20
50#endif
51
52#include <sys/stat.h>
53
54#include <errno.h>
55#ifndef _LIBC
56# define __set_errno(e) errno = (e)
57# ifndef ENAMETOOLONG
58#  define ENAMETOOLONG EINVAL
59# endif
60#endif
61
62#ifdef _LIBC
63# include <shlib-compat.h>
64#else
65# define SHLIB_COMPAT(lib, introduced, obsoleted) 0
66# define versioned_symbol(lib, local, symbol, version)
67# define compat_symbol(lib, local, symbol, version)
68# define weak_alias(local, symbol)
69# define __canonicalize_file_name canonicalize_file_name
70# define __realpath rpl_realpath
71# include "pathmax.h"
72# include "allocsa.h"
73# if HAVE_GETCWD
74#  ifdef VMS
75    /* We want the directory in Unix syntax, not in VMS syntax.  */
76#   define __getcwd(buf, max) getcwd (buf, max, 0)
77#  else
78#   define __getcwd getcwd
79#  endif
80# else
81#  define __getcwd(buf, max) getwd (buf)
82# endif
83# define __readlink readlink
84  /* On systems without symbolic links, call stat() instead of lstat().  */
85# if !defined S_ISNLK && !HAVE_READLINK
86#  define lstat stat
87# endif
88#endif
89
90/* Return the canonical absolute name of file NAME.  A canonical name
91   does not contain any `.', `..' components nor any repeated path
92   separators ('/') or symlinks.  All path components must exist.  If
93   RESOLVED is null, the result is malloc'd; otherwise, if the
94   canonical name is PATH_MAX chars or more, returns null with `errno'
95   set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
96   returns the name in RESOLVED.  If the name cannot be resolved and
97   RESOLVED is non-NULL, it contains the path of the first component
98   that cannot be resolved.  If the path can be resolved, RESOLVED
99   holds the same value as the value returned.  */
100
101char *
102__realpath (const char *name, char *resolved)
103{
104  char *rpath, *dest, *extra_buf = NULL;
105  const char *start, *end, *rpath_limit;
106  long int path_max;
107#ifdef S_ISLNK
108  int num_links = 0;
109#endif
110
111  if (name == NULL)
112    {
113      /* As per Single Unix Specification V2 we must return an error if
114	 either parameter is a null pointer.  We extend this to allow
115	 the RESOLVED parameter to be NULL in case the we are expected to
116	 allocate the room for the return value.  */
117      __set_errno (EINVAL);
118      return NULL;
119    }
120
121  if (name[0] == '\0')
122    {
123      /* As per Single Unix Specification V2 we must return an error if
124	 the name argument points to an empty string.  */
125      __set_errno (ENOENT);
126      return NULL;
127    }
128
129#ifdef PATH_MAX
130  path_max = PATH_MAX;
131#else
132  path_max = pathconf (name, _PC_PATH_MAX);
133  if (path_max <= 0)
134    path_max = 1024;
135#endif
136
137  if (resolved == NULL)
138    {
139      rpath = malloc (path_max);
140      if (rpath == NULL)
141	return NULL;
142    }
143  else
144    rpath = resolved;
145  rpath_limit = rpath + path_max;
146
147  if (name[0] != '/')
148    {
149      if (!__getcwd (rpath, path_max))
150	{
151	  rpath[0] = '\0';
152	  goto error;
153	}
154      dest = strchr (rpath, '\0');
155    }
156  else
157    {
158      rpath[0] = '/';
159      dest = rpath + 1;
160    }
161
162  for (start = end = name; *start; start = end)
163    {
164#ifdef _LIBC
165      struct stat64 st;
166#else
167      struct stat st;
168#endif
169
170      /* Skip sequence of multiple path-separators.  */
171      while (*start == '/')
172	++start;
173
174      /* Find end of path component.  */
175      for (end = start; *end && *end != '/'; ++end)
176	/* Nothing.  */;
177
178      if (end - start == 0)
179	break;
180      else if (end - start == 1 && start[0] == '.')
181	/* nothing */;
182      else if (end - start == 2 && start[0] == '.' && start[1] == '.')
183	{
184	  /* Back up to previous component, ignore if at root already.  */
185	  if (dest > rpath + 1)
186	    while ((--dest)[-1] != '/');
187	}
188      else
189	{
190	  size_t new_size;
191
192	  if (dest[-1] != '/')
193	    *dest++ = '/';
194
195	  if (dest + (end - start) >= rpath_limit)
196	    {
197	      ptrdiff_t dest_offset = dest - rpath;
198	      char *new_rpath;
199
200	      if (resolved)
201		{
202		  __set_errno (ENAMETOOLONG);
203		  if (dest > rpath + 1)
204		    dest--;
205		  *dest = '\0';
206		  goto error;
207		}
208	      new_size = rpath_limit - rpath;
209	      if (end - start + 1 > path_max)
210		new_size += end - start + 1;
211	      else
212		new_size += path_max;
213	      new_rpath = (char *) realloc (rpath, new_size);
214	      if (new_rpath == NULL)
215		goto error;
216	      rpath = new_rpath;
217	      rpath_limit = rpath + new_size;
218
219	      dest = rpath + dest_offset;
220	    }
221
222#ifdef _LIBC
223	  dest = __mempcpy (dest, start, end - start);
224#else
225	  memcpy (dest, start, end - start);
226	  dest += end - start;
227#endif
228	  *dest = '\0';
229
230#ifdef _LIBC
231	  if (__lxstat64 (_STAT_VER, rpath, &st) < 0)
232#else
233	  if (lstat (rpath, &st) < 0)
234#endif
235	    goto error;
236
237#ifdef S_ISLNK
238	  if (S_ISLNK (st.st_mode))
239	    {
240	      char *buf;
241	      size_t len;
242	      ssize_t n;
243
244	      if (++num_links > MAXSYMLINKS)
245		{
246		  __set_errno (ELOOP);
247		  goto error;
248		}
249
250	      buf = allocsa (path_max);
251	      if (!buf)
252		{
253		  errno = ENOMEM;
254		  goto error;
255		}
256
257	      n = __readlink (rpath, buf, path_max);
258	      if (n < 0)
259		{
260		  int saved_errno = errno;
261		  freesa (buf);
262		  errno = saved_errno;
263		  goto error;
264		}
265	      buf[n] = '\0';
266
267	      if (!extra_buf)
268		{
269		  extra_buf = allocsa (path_max);
270		  if (!extra_buf)
271		    {
272		      freesa (buf);
273		      errno = ENOMEM;
274		      goto error;
275		    }
276		}
277
278	      len = strlen (end);
279	      if ((long int) (n + len) >= path_max)
280		{
281		  freesa (buf);
282		  __set_errno (ENAMETOOLONG);
283		  goto error;
284		}
285
286	      /* Careful here, end may be a pointer into extra_buf... */
287	      memmove (&extra_buf[n], end, len + 1);
288	      name = end = memcpy (extra_buf, buf, n);
289
290	      if (buf[0] == '/')
291		dest = rpath + 1;	/* It's an absolute symlink */
292	      else
293		/* Back up to previous component, ignore if at root already: */
294		if (dest > rpath + 1)
295		  while ((--dest)[-1] != '/');
296	    }
297#endif
298	}
299    }
300  if (dest > rpath + 1 && dest[-1] == '/')
301    --dest;
302  *dest = '\0';
303
304  if (extra_buf)
305    freesa (extra_buf);
306
307  return resolved ? memcpy (resolved, rpath, dest - rpath + 1) : rpath;
308
309error:
310  {
311    int saved_errno = errno;
312    if (extra_buf)
313      freesa (extra_buf);
314    if (resolved)
315      strcpy (resolved, rpath);
316    else
317      free (rpath);
318    errno = saved_errno;
319  }
320  return NULL;
321}
322#ifdef _LIBC
323versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
324#endif
325
326
327#if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
328char *
329__old_realpath (const char *name, char *resolved)
330{
331  if (resolved == NULL)
332    {
333      __set_errno (EINVAL);
334      return NULL;
335    }
336
337  return __realpath (name, resolved);
338}
339compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
340#endif
341
342
343char *
344__canonicalize_file_name (const char *name)
345{
346  return __realpath (name, NULL);
347}
348weak_alias (__canonicalize_file_name, canonicalize_file_name)
349
350#else
351
352/* This declaration is solely to ensure that after preprocessing
353   this file is never empty.  */
354typedef int dummy;
355
356#endif
357