]> git.ipfire.org Git - thirdparty/glibc.git/blame - stdlib/canonicalize.c
Update copyright dates with scripts/update-copyrights
[thirdparty/glibc.git] / stdlib / canonicalize.c
CommitLineData
fa0bc87c 1/* Return the canonical absolute name of a given file.
2b778ceb 2 Copyright (C) 1996-2021 Free Software Foundation, Inc.
a18f587d 3 This file is part of the GNU C Library.
fa0bc87c 4
a18f587d 5 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
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.
fa0bc87c 9
a18f587d
UD
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
41bdb6e2 13 Lesser General Public License for more details.
fa0bc87c 14
41bdb6e2 15 You should have received a copy of the GNU Lesser General Public
59ba27a6 16 License along with the GNU C Library; if not, see
5a82c748 17 <https://www.gnu.org/licenses/>. */
fa0bc87c 18
33d52792 19#include <assert.h>
fa0bc87c
RM
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23#include <limits.h>
24#include <sys/stat.h>
25#include <errno.h>
e5aa91c3 26#include <stddef.h>
fa0bc87c 27
4f75b7a0 28#include <eloop-threshold.h>
3e0dd85b
UD
29#include <shlib-compat.h>
30
394f4f17
RM
31/* Return the canonical absolute name of file NAME. A canonical name
32 does not contain any `.', `..' components nor any repeated path
33 separators ('/') or symlinks. All path components must exist. If
34 RESOLVED is null, the result is malloc'd; otherwise, if the
35 canonical name is PATH_MAX chars or more, returns null with `errno'
36 set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars,
37 returns the name in RESOLVED. If the name cannot be resolved and
38 RESOLVED is non-NULL, it contains the path of the first component
39 that cannot be resolved. If the path can be resolved, RESOLVED
40 holds the same value as the value returned. */
fa0bc87c 41
f7501ae6
UD
42char *
43__realpath (const char *name, char *resolved)
fa0bc87c 44{
394f4f17
RM
45 char *rpath, *dest, *extra_buf = NULL;
46 const char *start, *end, *rpath_limit;
fa0bc87c 47 long int path_max;
394f4f17 48 int num_links = 0;
fa0bc87c 49
310b3460 50 if (name == NULL)
86187531
UD
51 {
52 /* As per Single Unix Specification V2 we must return an error if
310b3460 53 either parameter is a null pointer. We extend this to allow
49c091e5 54 the RESOLVED parameter to be NULL in case the we are expected to
310b3460 55 allocate the room for the return value. */
86187531
UD
56 __set_errno (EINVAL);
57 return NULL;
58 }
59
60 if (name[0] == '\0')
61 {
62 /* As per Single Unix Specification V2 we must return an error if
63 the name argument points to an empty string. */
64 __set_errno (ENOENT);
65 return NULL;
66 }
67
fa0bc87c 68#ifdef PATH_MAX
394f4f17 69 path_max = PATH_MAX;
fa0bc87c 70#else
dba2bdbe 71 path_max = __pathconf (name, _PC_PATH_MAX);
394f4f17
RM
72 if (path_max <= 0)
73 path_max = 1024;
fa0bc87c 74#endif
fa0bc87c 75
92712dee
RM
76 if (resolved == NULL)
77 {
78 rpath = malloc (path_max);
79 if (rpath == NULL)
80 return NULL;
81 }
82 else
83 rpath = resolved;
394f4f17 84 rpath_limit = rpath + path_max;
fa0bc87c 85
a18f587d
UD
86 if (name[0] != '/')
87 {
2ad4fab2 88 if (!__getcwd (rpath, path_max))
a1d84548
UD
89 {
90 rpath[0] = '\0';
91 goto error;
92 }
e7c8359e 93 dest = __rawmemchr (rpath, '\0');
a18f587d
UD
94 }
95 else
96 {
97 rpath[0] = '/';
98 dest = rpath + 1;
99 }
fa0bc87c 100
394f4f17 101 for (start = end = name; *start; start = end)
fa0bc87c 102 {
8edf6e0d 103 struct stat64 st;
394f4f17
RM
104 int n;
105
86187531 106 /* Skip sequence of multiple path-separators. */
2ac51313
UD
107 while (*start == '/')
108 ++start;
394f4f17 109
86187531 110 /* Find end of path component. */
2ac51313
UD
111 for (end = start; *end && *end != '/'; ++end)
112 /* Nothing. */;
df21c858 113
394f4f17
RM
114 if (end - start == 0)
115 break;
2ac51313 116 else if (end - start == 1 && start[0] == '.')
394f4f17 117 /* nothing */;
2ac51313 118 else if (end - start == 2 && start[0] == '.' && start[1] == '.')
86187531
UD
119 {
120 /* Back up to previous component, ignore if at root already. */
121 if (dest > rpath + 1)
122 while ((--dest)[-1] != '/');
123 }
124 else
fa0bc87c 125 {
394f4f17 126 size_t new_size;
fa0bc87c 127
394f4f17
RM
128 if (dest[-1] != '/')
129 *dest++ = '/';
130
131 if (dest + (end - start) >= rpath_limit)
fa0bc87c 132 {
2ac51313 133 ptrdiff_t dest_offset = dest - rpath;
d1dddedf 134 char *new_rpath;
2ac51313 135
394f4f17 136 if (resolved)
fa0bc87c 137 {
c4029823 138 __set_errno (ENAMETOOLONG);
a1d84548
UD
139 if (dest > rpath + 1)
140 dest--;
141 *dest = '\0';
394f4f17 142 goto error;
fa0bc87c 143 }
394f4f17
RM
144 new_size = rpath_limit - rpath;
145 if (end - start + 1 > path_max)
146 new_size += end - start + 1;
147 else
148 new_size += path_max;
d1dddedf
UD
149 new_rpath = (char *) realloc (rpath, new_size);
150 if (new_rpath == NULL)
151 goto error;
152 rpath = new_rpath;
394f4f17 153 rpath_limit = rpath + new_size;
2ac51313
UD
154
155 dest = rpath + dest_offset;
fa0bc87c 156 }
fa0bc87c 157
86187531 158 dest = __mempcpy (dest, start, end - start);
394f4f17 159 *dest = '\0';
df21c858 160
04986243 161 if (__lstat64 (rpath, &st) < 0)
394f4f17 162 goto error;
fa0bc87c 163
394f4f17 164 if (S_ISLNK (st.st_mode))
fa0bc87c 165 {
f65fd747 166 char *buf = __alloca (path_max);
40a55d20 167 size_t len;
fa0bc87c 168
4f75b7a0 169 if (++num_links > __eloop_threshold ())
394f4f17 170 {
c4029823 171 __set_errno (ELOOP);
394f4f17
RM
172 goto error;
173 }
fa0bc87c 174
104426b6 175 n = __readlink (rpath, buf, path_max - 1);
394f4f17
RM
176 if (n < 0)
177 goto error;
178 buf[n] = '\0';
fa0bc87c 179
394f4f17
RM
180 if (!extra_buf)
181 extra_buf = __alloca (path_max);
fa0bc87c 182
40a55d20 183 len = strlen (end);
5460617d 184 if (path_max - n <= len)
394f4f17 185 {
c4029823 186 __set_errno (ENAMETOOLONG);
394f4f17
RM
187 goto error;
188 }
fa0bc87c 189
40a55d20 190 /* Careful here, end may be a pointer into extra_buf... */
86187531 191 memmove (&extra_buf[n], end, len + 1);
2ac51313 192 name = end = memcpy (extra_buf, buf, n);
394f4f17
RM
193
194 if (buf[0] == '/')
40a55d20 195 dest = rpath + 1; /* It's an absolute symlink */
394f4f17 196 else
40a55d20 197 /* Back up to previous component, ignore if at root already: */
394f4f17
RM
198 if (dest > rpath + 1)
199 while ((--dest)[-1] != '/');
200 }
0f888d8e
UD
201 else if (!S_ISDIR (st.st_mode) && *end != '\0')
202 {
203 __set_errno (ENOTDIR);
204 goto error;
205 }
394f4f17
RM
206 }
207 }
208 if (dest > rpath + 1 && dest[-1] == '/')
209 --dest;
210 *dest = '\0';
f65fd747 211
33d52792 212 assert (resolved == NULL || resolved == rpath);
d3a4681c 213 return rpath;
394f4f17
RM
214
215error:
33d52792
UD
216 assert (resolved == NULL || resolved == rpath);
217 if (resolved == NULL)
394f4f17
RM
218 free (rpath);
219 return NULL;
fa0bc87c 220}
8ed3b643 221libc_hidden_def (__realpath)
3e0dd85b
UD
222versioned_symbol (libc, __realpath, realpath, GLIBC_2_3);
223
224
225#if SHLIB_COMPAT(libc, GLIBC_2_0, GLIBC_2_3)
226char *
4a381a81 227attribute_compat_text_section
3e0dd85b
UD
228__old_realpath (const char *name, char *resolved)
229{
230 if (resolved == NULL)
231 {
232 __set_errno (EINVAL);
233 return NULL;
234 }
235
236 return __realpath (name, resolved);
237}
238compat_symbol (libc, __old_realpath, realpath, GLIBC_2_0);
239#endif
fa0bc87c 240
a18f587d 241
fa0bc87c 242char *
2ad4fab2 243__canonicalize_file_name (const char *name)
fa0bc87c 244{
f7501ae6 245 return __realpath (name, NULL);
fa0bc87c 246}
2ad4fab2 247weak_alias (__canonicalize_file_name, canonicalize_file_name)