* elf/dl-load.c (_dl_init_paths): Don't use strdupa in function
[kopensolaris-gnu/glibc.git] / elf / chroot_canon.c
1 /* Return the canonical absolute name of a given file inside chroot.
2    Copyright (C) 1996, 1997, 1998, 1999, 2000 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 Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    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    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the GNU C Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <limits.h>
24 #include <sys/param.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27 #include <stddef.h>
28 #include <stdint.h>
29
30 #include "ldconfig.h"
31
32 #ifndef PATH_MAX
33 #define PATH_MAX 1024
34 #endif
35
36 /* Return the canonical absolute name of file NAME as if chroot(CHROOT) was
37    done first.  A canonical name does not contain any `.', `..' components
38    nor any repeated path separators ('/') or symlinks.  All path components
39    must exist and NAME must be absolute filename.  The result is malloc'd.
40    The returned name includes the CHROOT prefix.  */
41
42 char *
43 chroot_canon (const char *chroot, const char *name)
44 {
45   char *rpath, *dest, *extra_buf = NULL, *rpath_root;
46   const char *start, *end, *rpath_limit;
47   int num_links = 0;
48   size_t chroot_len = strlen (chroot);
49
50   if (chroot_len < 1)
51     {
52       __set_errno (EINVAL);
53       return NULL;
54     }
55
56   rpath = malloc (chroot_len + PATH_MAX);
57   rpath_limit = rpath + chroot_len + PATH_MAX;
58
59   rpath_root = (char *) mempcpy (rpath, chroot, chroot_len) - 1;
60   if (*rpath_root != '/')
61     *++rpath_root = '/';
62   dest = rpath_root + 1;
63
64   for (start = end = name; *start; start = end)
65     {
66       struct stat64 st;
67       int n;
68
69       /* Skip sequence of multiple path-separators.  */
70       while (*start == '/')
71         ++start;
72
73       /* Find end of path component.  */
74       for (end = start; *end && *end != '/'; ++end)
75         /* Nothing.  */;
76
77       if (end - start == 0)
78         break;
79       else if (end - start == 1 && start[0] == '.')
80         /* nothing */;
81       else if (end - start == 2 && start[0] == '.' && start[1] == '.')
82         {
83           /* Back up to previous component, ignore if at root already.  */
84           if (dest > rpath_root + 1)
85             while ((--dest)[-1] != '/');
86         }
87       else
88         {
89           size_t new_size;
90
91           if (dest[-1] != '/')
92             *dest++ = '/';
93
94           if (dest + (end - start) >= rpath_limit)
95             {
96               ptrdiff_t dest_offset = dest - rpath;
97
98               new_size = rpath_limit - rpath;
99               if (end - start + 1 > PATH_MAX)
100                 new_size += end - start + 1;
101               else
102                 new_size += PATH_MAX;
103               rpath = realloc (rpath, new_size);
104               rpath_limit = rpath + new_size;
105               if (rpath == NULL)
106                 return NULL;
107
108               dest = rpath + dest_offset;
109             }
110
111           dest = mempcpy (dest, start, end - start);
112           *dest = '\0';
113
114           if (lstat64 (rpath, &st) < 0)
115             {
116               if (*end == '\0')
117                 goto done;
118               goto error;
119             }
120
121           if (S_ISLNK (st.st_mode))
122             {
123               char *buf = alloca (PATH_MAX);
124               size_t len;
125
126               if (++num_links > MAXSYMLINKS)
127                 {
128                   __set_errno (ELOOP);
129                   goto error;
130                 }
131
132               n = readlink (rpath, buf, PATH_MAX);
133               if (n < 0)
134                 {
135                   if (*end == '\0')
136                     goto done;
137                   goto error;
138                 }
139               buf[n] = '\0';
140
141               if (!extra_buf)
142                 extra_buf = alloca (PATH_MAX);
143
144               len = strlen (end);
145               if ((long int) (n + len) >= PATH_MAX)
146                 {
147                   __set_errno (ENAMETOOLONG);
148                   goto error;
149                 }
150
151               /* Careful here, end may be a pointer into extra_buf... */
152               memmove (&extra_buf[n], end, len + 1);
153               name = end = memcpy (extra_buf, buf, n);
154
155               if (buf[0] == '/')
156                 dest = rpath_root + 1;  /* It's an absolute symlink */
157               else
158                 /* Back up to previous component, ignore if at root already: */
159                 if (dest > rpath_root + 1)
160                   while ((--dest)[-1] != '/');
161             }
162         }
163     }
164  done:
165   if (dest > rpath_root + 1 && dest[-1] == '/')
166     --dest;
167   *dest = '\0';
168
169   return rpath;
170
171  error:
172   free (rpath);
173   return NULL;
174 }