• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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