]> git.saurik.com Git - apple/libc.git/blame_incremental - stdlib/FreeBSD/realpath.c.patch
Libc-763.13.tar.gz
[apple/libc.git] / stdlib / FreeBSD / realpath.c.patch
... / ...
CommitLineData
1--- realpath.c.orig 2010-06-24 17:32:55.000000000 -0700
2+++ realpath.c 2010-06-25 13:46:50.000000000 -0700
3@@ -35,13 +35,41 @@ __FBSDID("$FreeBSD: src/lib/libc/stdlib/
4 #include "namespace.h"
5 #include <sys/param.h>
6 #include <sys/stat.h>
7+#include <sys/mount.h>
8
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13+#include <sys/attr.h>
14+#include <sys/vnode.h>
15 #include "un-namespace.h"
16
17+struct attrs {
18+ u_int32_t len;
19+ attrreference_t name;
20+ dev_t dev;
21+ fsobj_type_t type;
22+ fsobj_id_t id;
23+ char buf[PATH_MAX];
24+};
25+
26+#ifndef BUILDING_VARIANT
27+__private_extern__ struct attrlist _rp_alist = {
28+ ATTR_BIT_MAP_COUNT,
29+ 0,
30+ ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_OBJTYPE | ATTR_CMN_OBJID,
31+ 0,
32+ 0,
33+ 0,
34+ 0,
35+};
36+#else /* BUILDING_VARIANT */
37+__private_extern__ struct attrlist _rp_alist;
38+#endif /* BUILDING_VARIANT */
39+
40+extern char * __private_getcwd(char *, size_t, int);
41+
42 /*
43 * char *realpath(const char *path, char resolved[PATH_MAX]);
44 *
45@@ -50,36 +78,89 @@ __FBSDID("$FreeBSD: src/lib/libc/stdlib/
46 * in which case the path which caused trouble is left in (resolved).
47 */
48 char *
49-realpath(const char *path, char resolved[PATH_MAX])
50+realpath(const char *path, char inresolved[PATH_MAX])
51 {
52+ struct attrs attrs;
53 struct stat sb;
54 char *p, *q, *s;
55- size_t left_len, resolved_len;
56+ size_t left_len, resolved_len, save_resolved_len;
57 unsigned symlinks;
58- int serrno, slen;
59+ int serrno, slen, useattrs, islink;
60 char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
61+ dev_t dev, lastdev;
62+ struct statfs sfs;
63+ static dev_t rootdev;
64+ static int rootdev_inited = 0;
65+ ino_t inode;
66+ char *resolved;
67
68+ if (path == NULL) {
69+ errno = EINVAL;
70+ return (NULL);
71+ }
72+#if __DARWIN_UNIX03
73+ if (*path == 0) {
74+ errno = ENOENT;
75+ return (NULL);
76+ }
77+#endif /* __DARWIN_UNIX03 */
78+ /*
79+ * Extension to the standard; if inresolved == NULL, allocate memory
80+ */
81+ if (!inresolved) {
82+ if ((resolved = malloc(PATH_MAX)) == NULL) return (NULL);
83+ } else {
84+ resolved = inresolved;
85+ }
86+ if (!rootdev_inited) {
87+ rootdev_inited = 1;
88+ if (stat("/", &sb) < 0) {
89+error_return:
90+ if (!inresolved) {
91+ int e = errno;
92+ free(resolved);
93+ errno = e;
94+ }
95+ return (NULL);
96+ }
97+ rootdev = sb.st_dev;
98+ }
99 serrno = errno;
100 symlinks = 0;
101 if (path[0] == '/') {
102 resolved[0] = '/';
103 resolved[1] = '\0';
104- if (path[1] == '\0')
105+ if (path[1] == '\0') {
106 return (resolved);
107+ }
108 resolved_len = 1;
109 left_len = strlcpy(left, path + 1, sizeof(left));
110 } else {
111- if (getcwd(resolved, PATH_MAX) == NULL) {
112+#if !defined(VARIANT_DARWINEXTSN) && __DARWIN_UNIX03
113+ /* 4447159: don't use GETPATH, so this will fail if */
114+ /* if parent directories are not readable, as per POSIX */
115+ if (__private_getcwd(resolved, PATH_MAX, 0) == NULL)
116+#else /* VARIANT_DARWINEXTSN || !__DARWIN_UNIX03 */
117+ if (__private_getcwd(resolved, PATH_MAX, 1) == NULL)
118+#endif /* !VARIANT_DARWINEXTSN && __DARWIN_UNIX03 */
119+ {
120 strlcpy(resolved, ".", PATH_MAX);
121- return (NULL);
122+ goto error_return;
123 }
124 resolved_len = strlen(resolved);
125 left_len = strlcpy(left, path, sizeof(left));
126 }
127 if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
128 errno = ENAMETOOLONG;
129- return (NULL);
130+ goto error_return;
131 }
132+ if (resolved_len > 1) {
133+ if (stat(resolved, &sb) < 0) {
134+ goto error_return;
135+ }
136+ lastdev = sb.st_dev;
137+ } else
138+ lastdev = rootdev;
139
140 /*
141 * Iterate over path components in `left'.
142@@ -93,7 +174,7 @@ realpath(const char *path, char resolved
143 s = p ? p : left + left_len;
144 if (s - left >= sizeof(next_token)) {
145 errno = ENAMETOOLONG;
146- return (NULL);
147+ goto error_return;
148 }
149 memcpy(next_token, left, s - left);
150 next_token[s - left] = '\0';
151@@ -103,7 +184,7 @@ realpath(const char *path, char resolved
152 if (resolved[resolved_len - 1] != '/') {
153 if (resolved_len + 1 >= PATH_MAX) {
154 errno = ENAMETOOLONG;
155- return (NULL);
156+ goto error_return;
157 }
158 resolved[resolved_len++] = '/';
159 resolved[resolved_len] = '\0';
160@@ -127,6 +208,13 @@ realpath(const char *path, char resolved
161 }
162
163 /*
164+ * Save resolved_len, so that we can later null out
165+ * the the appended next_token, and replace with the
166+ * real name (matters on case-insensitive filesystems).
167+ */
168+ save_resolved_len = resolved_len;
169+
170+ /*
171 * Append the next path component and lstat() it. If
172 * lstat() fails we still can return successfully if
173 * there are no more path components left.
174@@ -134,27 +222,89 @@ realpath(const char *path, char resolved
175 resolved_len = strlcat(resolved, next_token, PATH_MAX);
176 if (resolved_len >= PATH_MAX) {
177 errno = ENAMETOOLONG;
178- return (NULL);
179+ goto error_return;
180 }
181- if (lstat(resolved, &sb) != 0) {
182+ if (getattrlist(resolved, &_rp_alist, &attrs, sizeof(attrs), FSOPT_NOFOLLOW) == 0) {
183+ useattrs = 1;
184+ islink = (attrs.type == VLNK);
185+ dev = attrs.dev;
186+ inode = attrs.id.fid_objno;
187+ } else if (errno == ENOTSUP || errno == EINVAL) {
188+ if ((useattrs = lstat(resolved, &sb)) == 0) {
189+ islink = S_ISLNK(sb.st_mode);
190+ dev = sb.st_dev;
191+ inode = sb.st_ino;
192+ }
193+ } else
194+ useattrs = -1;
195+ if (useattrs < 0) {
196+#if !__DARWIN_UNIX03
197 if (errno == ENOENT && p == NULL) {
198 errno = serrno;
199 return (resolved);
200 }
201- return (NULL);
202+#endif /* !__DARWIN_UNIX03 */
203+ goto error_return;
204 }
205- if (S_ISLNK(sb.st_mode)) {
206+ if (dev != lastdev) {
207+ /*
208+ * We have crossed a mountpoint. For volumes like UDF
209+ * the getattrlist name may not match the actual
210+ * mountpoint, so we just copy the mountpoint directly.
211+ * (3703138). However, the mountpoint may not be
212+ * accessible, as when chroot-ed, so check first.
213+ * There may be a file on the chroot-ed volume with
214+ * the same name as the mountpoint, so compare device
215+ * and inode numbers.
216+ */
217+ lastdev = dev;
218+ if (statfs(resolved, &sfs) == 0 && lstat(sfs.f_mntonname, &sb) == 0 && dev == sb.st_dev && inode == sb.st_ino) {
219+ /*
220+ * However, it's possible that the mountpoint
221+ * path matches, even though it isn't the real
222+ * path in the chroot-ed environment, so check
223+ * that each component of the mountpoint
224+ * is a directory (and not a symlink)
225+ */
226+ char temp[sizeof(sfs.f_mntonname)];
227+ char *cp;
228+ int ok = 1;
229+
230+ strcpy(temp, sfs.f_mntonname);
231+ for(;;) {
232+ if ((cp = strrchr(temp, '/')) == NULL) {
233+ ok = 0;
234+ break;
235+ }
236+ if (cp <= temp)
237+ break;
238+ *cp = 0;
239+ if (lstat(temp, &sb) < 0 || (sb.st_mode & S_IFMT) != S_IFDIR) {
240+ ok = 0;
241+ break;
242+ }
243+ }
244+ if (ok) {
245+ resolved_len = strlcpy(resolved, sfs.f_mntonname, PATH_MAX);
246+ continue;
247+ }
248+ }
249+ /* if we fail, use the other methods. */
250+ }
251+ if (islink) {
252 if (symlinks++ > MAXSYMLINKS) {
253 errno = ELOOP;
254- return (NULL);
255+ goto error_return;
256 }
257 slen = readlink(resolved, symlink, sizeof(symlink) - 1);
258- if (slen < 0)
259- return (NULL);
260+ if (slen < 0) {
261+ goto error_return;
262+ }
263 symlink[slen] = '\0';
264 if (symlink[0] == '/') {
265 resolved[1] = 0;
266 resolved_len = 1;
267+ lastdev = rootdev;
268 } else if (resolved_len > 1) {
269 /* Strip the last path component. */
270 resolved[resolved_len - 1] = '\0';
271@@ -172,7 +322,7 @@ realpath(const char *path, char resolved
272 if (symlink[slen - 1] != '/') {
273 if (slen + 1 >= sizeof(symlink)) {
274 errno = ENAMETOOLONG;
275- return (NULL);
276+ goto error_return;
277 }
278 symlink[slen] = '/';
279 symlink[slen + 1] = 0;
280@@ -180,11 +330,34 @@ realpath(const char *path, char resolved
281 left_len = strlcat(symlink, left, sizeof(left));
282 if (left_len >= sizeof(left)) {
283 errno = ENAMETOOLONG;
284- return (NULL);
285+ goto error_return;
286 }
287 }
288 left_len = strlcpy(left, symlink, sizeof(left));
289+ } else if (useattrs) {
290+ /*
291+ * attrs already has the real name.
292+ */
293+
294+ resolved[save_resolved_len] = '\0';
295+ resolved_len = strlcat(resolved, (const char *)&attrs.name + attrs.name.attr_dataoffset, PATH_MAX);
296+ if (resolved_len >= PATH_MAX) {
297+ errno = ENAMETOOLONG;
298+ goto error_return;
299+ }
300 }
301+ /*
302+ * For the case of useattrs == 0, we could scan the directory
303+ * and try to match the inode. There are many problems with
304+ * this: (1) the directory may not be readable, (2) for multiple
305+ * hard links, we would find the first, but not necessarily
306+ * the one specified in the path, (3) we can't try to do
307+ * a case-insensitive search to match the right one in (2),
308+ * because the underlying filesystem may do things like
309+ * decompose composed characters. For most cases, doing
310+ * nothing is the right thing when useattrs == 0, so we punt
311+ * for now.
312+ */
313 }
314
315 /*