1 /*
2 * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 /*
27 * Pathname canonicalization for Unix file systems
28 */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/stat.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <unistd.h>
37 #if !defined(_ALLBSD_SOURCE)
38 #include <alloca.h>
39 #endif
40
41
42 /* Note: The comments in this file use the terminology
43 defined in the java.io.File class */
44
45
46 // BEGIN Android-added: Remove consecutive duplicate path separators "//". b/267617531
47 // and the trailing path separator `/` if it's not root fs.
removeDupSeparator(char * path)48 char* removeDupSeparator(char *path)
49 {
50 if (path == NULL || *path == '\0') {
51 return NULL;
52 }
53
54 char *in = path;
55 char *out = path;
56 char prevChar = 0;
57 int n = 0;
58 for (; *in != '\0'; in++) {
59 // Remove duplicate path separators
60 if (!(*in == '/' && prevChar == '/')) {
61 *(out++) = *in;
62 n++;
63 }
64 prevChar = *in;
65 }
66 *out = '\0';
67
68 // Remove the trailing path separator, except when path equals `/`
69 if (prevChar == '/' && n > 1) {
70 *(--out) = '\0';
71 }
72
73 return path;
74 }
75 // END Android-added: Remove consecutive duplicate path separators "//". b/267617531
76
77 /* Check the given name sequence to see if it can be further collapsed.
78 Return zero if not, otherwise return the number of names in the sequence. */
79
80 static int
collapsible(char * names)81 collapsible(char *names)
82 {
83 char *p = names;
84 int dots = 0, n = 0;
85
86 while (*p) {
87 if ((p[0] == '.') && ((p[1] == '\0')
88 || (p[1] == '/')
89 || ((p[1] == '.') && ((p[2] == '\0')
90 || (p[2] == '/'))))) {
91 dots = 1;
92 }
93 n++;
94 while (*p) {
95 if (*p == '/') {
96 // Android-changed: Remove consecutive duplicate path separators "//". b/267617531
97 // p++
98 while (*p == '/') {
99 p++;
100 }
101 break;
102 }
103 p++;
104 }
105 }
106 return (dots ? n : 0);
107 }
108
109
110 /* Split the names in the given name sequence,
111 replacing slashes with nulls and filling in the given index array */
112
113 static void
splitNames(char * names,char ** ix)114 splitNames(char *names, char **ix)
115 {
116 char *p = names;
117 int i = 0;
118
119 while (*p) {
120 ix[i++] = p++;
121 while (*p) {
122 if (*p == '/') {
123 // Android-changed: Remove consecutive duplicate path separators "//". b/267617531
124 // *p++ = '\0';
125 while (*p == '/') {
126 *p++ = '\0';
127 }
128 break;
129 }
130 p++;
131 }
132 }
133 }
134
135
136 /* Join the names in the given name sequence, ignoring names whose index
137 entries have been cleared and replacing nulls with slashes as needed */
138
139 static void
joinNames(char * names,int nc,char ** ix)140 joinNames(char *names, int nc, char **ix)
141 {
142 int i;
143 char *p;
144
145 for (i = 0, p = names; i < nc; i++) {
146 if (!ix[i]) continue;
147 if (i > 0) {
148 p[-1] = '/';
149 }
150 if (p == ix[i]) {
151 p += strlen(p) + 1;
152 } else {
153 char *q = ix[i];
154 while ((*p++ = *q++));
155 }
156 }
157 *p = '\0';
158 }
159
160
161 /* Collapse "." and ".." names in the given path wherever possible.
162 A "." name may always be eliminated; a ".." name may be eliminated if it
163 follows a name that is neither "." nor "..". This is a syntactic operation
164 that performs no filesystem queries, so it should only be used to cleanup
165 after invoking the realpath() procedure. */
166
167 static void
collapse(char * path)168 collapse(char *path)
169 {
170 // Android-changed: Remove consecutive duplicate path separators "//". b/267617531
171 removeDupSeparator(path);
172
173 char *names = (path[0] == '/') ? path + 1 : path; /* Preserve first '/' */
174 int nc;
175 char **ix;
176 int i, j;
177 // Android-removed: unused variables.
178 // char *p, *q;
179
180 nc = collapsible(names);
181 if (nc < 2) return; /* Nothing to do */
182 ix = (char **)alloca(nc * sizeof(char *));
183 splitNames(names, ix);
184
185 for (i = 0; i < nc; i++) {
186 int dots = 0;
187
188 /* Find next occurrence of "." or ".." */
189 do {
190 char *p = ix[i];
191 // Android-changed: null pointer check.
192 // if (p[0] == '.') {
193 if (p != NULL && p[0] == '.') {
194 if (p[1] == '\0') {
195 dots = 1;
196 break;
197 }
198 if ((p[1] == '.') && (p[2] == '\0')) {
199 dots = 2;
200 break;
201 }
202 }
203 i++;
204 } while (i < nc);
205 if (i >= nc) break;
206
207 /* At this point i is the index of either a "." or a "..", so take the
208 appropriate action and then continue the outer loop */
209 if (dots == 1) {
210 /* Remove this instance of "." */
211 ix[i] = 0;
212 }
213 else {
214 /* If there is a preceding name, remove both that name and this
215 instance of ".."; otherwise, leave the ".." as is */
216 for (j = i - 1; j >= 0; j--) {
217 if (ix[j]) break;
218 }
219 if (j < 0) continue;
220 ix[j] = 0;
221 ix[i] = 0;
222 }
223 /* i will be incremented at the top of the loop */
224 }
225
226 joinNames(names, nc, ix);
227 }
228
229
230 /* Convert a pathname to canonical form. The input path is assumed to contain
231 no duplicate slashes. On Solaris we can use realpath() to do most of the
232 work, though once that's done we still must collapse any remaining "." and
233 ".." names by hand. */
234
235 // Android-changed: hidden to avoid conflict with libm (b/135018555)
236 __attribute__((visibility("hidden")))
237 int
canonicalize(char * original,char * resolved,int len)238 canonicalize(char *original, char *resolved, int len)
239 {
240 if (len < PATH_MAX) {
241 errno = EINVAL;
242 return -1;
243 }
244
245 // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
246 // if (strlen(original) > PATH_MAX) {
247 if (strlen(original) >= PATH_MAX) {
248 errno = ENAMETOOLONG;
249 return -1;
250 }
251
252 /* First try realpath() on the entire path */
253 if (realpath(original, resolved)) {
254 /* That worked, so return it */
255 collapse(resolved);
256 return 0;
257 }
258 else {
259 // Android-changed: Avoid crash in getCanonicalPath(). b/266432364
260 if (errno == EINVAL || errno == ELOOP || errno == ENAMETOOLONG || errno == ENOMEM) {
261 return -1;
262 }
263
264 /* Something's bogus in the original path, so remove names from the end
265 until either some subpath works or we run out of names */
266 char *p, *end, *r = NULL;
267 // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
268 char path[PATH_MAX];
269
270 strncpy(path, original, sizeof(path));
271 // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
272 if (path[PATH_MAX - 1] != '\0') {
273 errno = ENAMETOOLONG;
274 return -1;
275 }
276 end = path + strlen(path);
277
278 for (p = end; p > path;) {
279
280 /* Skip last element */
281 while ((--p > path) && (*p != '/'));
282 if (p == path) break;
283
284 /* Try realpath() on this subpath */
285 *p = '\0';
286 r = realpath(path, resolved);
287 *p = (p == end) ? '\0' : '/';
288
289 if (r != NULL) {
290 /* The subpath has a canonical path */
291 break;
292 }
293 // Android-changed: Added ENOTCONN case (b/26645585, b/26070583)
294 else if (errno == ENOENT || errno == ENOTDIR || errno == EACCES || errno == ENOTCONN) {
295 /* If the lookup of a particular subpath fails because the file
296 does not exist, because it is of the wrong type, or because
297 access is denied, then remove its last name and try again.
298 Other I/O problems cause an error return. */
299
300 /* NOTE: ENOTCONN seems like an odd errno to expect, but this is
301 the behaviour on linux for fuse filesystems when the fuse device
302 associated with the FS is closed but the filesystem is not
303 unmounted. */
304 continue;
305 }
306 else {
307 return -1;
308 }
309 }
310
311 size_t nameMax;
312 if (r != NULL) {
313 /* Append unresolved subpath to resolved subpath */
314 int rn = strlen(r);
315 if (rn + (int)strlen(p) >= len) {
316 /* Buffer overflow */
317 errno = ENAMETOOLONG;
318 return -1;
319 }
320
321 // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
322 nameMax = pathconf(r, _PC_NAME_MAX);
323
324 if ((rn > 0) && (r[rn - 1] == '/') && (*p == '/')) {
325 /* Avoid duplicate slashes */
326 p++;
327 }
328 strcpy(r + rn, p);
329 collapse(r);
330 }
331 else {
332 /* Nothing resolved, so just return the original path */
333 // Android-changed: Avoid crash in getCanonicalPath() due to a long path. b/266432364
334 nameMax = pathconf("/", _PC_NAME_MAX);
335 strcpy(resolved, path);
336 collapse(resolved);
337 }
338
339 // BEGIN Android-added: Avoid crash in getCanonicalPath() due to a long path. b/266432364
340 // Ensure resolve path length is "< PATH_MAX" and collapse() did not overwrite
341 // terminating null byte
342 char resolvedPath[PATH_MAX];
343 strncpy(resolvedPath, resolved, sizeof(resolvedPath));
344 if (resolvedPath[PATH_MAX - 1] != '\0') {
345 errno = ENAMETOOLONG;
346 return -1;
347 }
348
349 // Ensure resolve path does not contain any components who length is "> NAME_MAX"
350 // If pathconf call failed with -1 or returned 0 in case of permission denial
351 if (nameMax < 1) {
352 nameMax = NAME_MAX;
353 }
354
355 char *component;
356 char *rest = resolvedPath;
357 while ((component = strtok_r(rest, "/", &rest))) {
358 if (strlen(component) > nameMax) {
359 errno = ENAMETOOLONG;
360 return -1;
361 }
362 }
363
364 return 0;
365 // END Android-added: Avoid crash in getCanonicalPath() due to a long path. b/266432364
366 }
367
368 }
369