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