]> git.saurik.com Git - apple/libc.git/blame - stdlib/FreeBSD/realpath.c.patch
Libc-763.13.tar.gz
[apple/libc.git] / stdlib / FreeBSD / realpath.c.patch
CommitLineData
1f2f436a
A
1--- realpath.c.orig 2010-06-24 17:32:55.000000000 -0700
2+++ realpath.c 2010-06-25 13:46:50.000000000 -0700
34e8f829 3@@ -35,13 +35,41 @@ __FBSDID("$FreeBSD: src/lib/libc/stdlib/
3d9156a7
A
4 #include "namespace.h"
5 #include <sys/param.h>
6 #include <sys/stat.h>
7+#include <sys/mount.h>
8
9 #include <errno.h>
59e0d9fe
A
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;
3d9156a7 20+ dev_t dev;
59e0d9fe 21+ fsobj_type_t type;
3d9156a7 22+ fsobj_id_t id;
59e0d9fe
A
23+ char buf[PATH_MAX];
24+};
25+
224c7076
A
26+#ifndef BUILDING_VARIANT
27+__private_extern__ struct attrlist _rp_alist = {
59e0d9fe
A
28+ ATTR_BIT_MAP_COUNT,
29+ 0,
3d9156a7 30+ ATTR_CMN_NAME | ATTR_CMN_DEVID | ATTR_CMN_OBJTYPE | ATTR_CMN_OBJID,
59e0d9fe
A
31+ 0,
32+ 0,
33+ 0,
34+ 0,
35+};
224c7076
A
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);
59e0d9fe
A
41+
42 /*
43 * char *realpath(const char *path, char resolved[PATH_MAX]);
44 *
1f2f436a 45@@ -50,36 +78,89 @@ __FBSDID("$FreeBSD: src/lib/libc/stdlib/
34e8f829
A
46 * in which case the path which caused trouble is left in (resolved).
47 */
59e0d9fe 48 char *
34e8f829
A
49-realpath(const char *path, char resolved[PATH_MAX])
50+realpath(const char *path, char inresolved[PATH_MAX])
59e0d9fe
A
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];
3d9156a7
A
61+ dev_t dev, lastdev;
62+ struct statfs sfs;
63+ static dev_t rootdev;
64+ static int rootdev_inited = 0;
65+ ino_t inode;
34e8f829 66+ char *resolved;
59e0d9fe 67
224c7076
A
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 */
34e8f829
A
78+ /*
79+ * Extension to the standard; if inresolved == NULL, allocate memory
34e8f829
A
80+ */
81+ if (!inresolved) {
1f2f436a 82+ if ((resolved = malloc(PATH_MAX)) == NULL) return (NULL);
34e8f829
A
83+ } else {
84+ resolved = inresolved;
85+ }
3d9156a7
A
86+ if (!rootdev_inited) {
87+ rootdev_inited = 1;
224c7076 88+ if (stat("/", &sb) < 0) {
1f2f436a
A
89+error_return:
90+ if (!inresolved) {
91+ int e = errno;
92+ free(resolved);
93+ errno = e;
224c7076 94+ }
1f2f436a
A
95+ return (NULL);
96+ }
3d9156a7
A
97+ rootdev = sb.st_dev;
98+ }
59e0d9fe 99 serrno = errno;
3d9156a7
A
100 symlinks = 0;
101 if (path[0] == '/') {
224c7076
A
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);
1f2f436a
A
121- return (NULL);
122+ goto error_return;
224c7076 123 }
1f2f436a
A
124 resolved_len = strlen(resolved);
125 left_len = strlcpy(left, path, sizeof(left));
126 }
127 if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
3d9156a7 128 errno = ENAMETOOLONG;
1f2f436a
A
129- return (NULL);
130+ goto error_return;
3d9156a7
A
131 }
132+ if (resolved_len > 1) {
224c7076 133+ if (stat(resolved, &sb) < 0) {
1f2f436a 134+ goto error_return;
224c7076 135+ }
3d9156a7
A
136+ lastdev = sb.st_dev;
137+ } else
138+ lastdev = rootdev;
139
140 /*
141 * Iterate over path components in `left'.
1f2f436a
A
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
59e0d9fe
A
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.
1f2f436a
A
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) {
59e0d9fe 177 errno = ENAMETOOLONG;
1f2f436a
A
178- return (NULL);
179+ goto error_return;
59e0d9fe
A
180 }
181- if (lstat(resolved, &sb) != 0) {
224c7076 182+ if (getattrlist(resolved, &_rp_alist, &attrs, sizeof(attrs), FSOPT_NOFOLLOW) == 0) {
59e0d9fe
A
183+ useattrs = 1;
184+ islink = (attrs.type == VLNK);
3d9156a7
A
185+ dev = attrs.dev;
186+ inode = attrs.id.fid_objno;
224c7076 187+ } else if (errno == ENOTSUP || errno == EINVAL) {
3d9156a7 188+ if ((useattrs = lstat(resolved, &sb)) == 0) {
59e0d9fe 189+ islink = S_ISLNK(sb.st_mode);
3d9156a7
A
190+ dev = sb.st_dev;
191+ inode = sb.st_ino;
192+ }
59e0d9fe
A
193+ } else
194+ useattrs = -1;
195+ if (useattrs < 0) {
224c7076 196+#if !__DARWIN_UNIX03
59e0d9fe
A
197 if (errno == ENOENT && p == NULL) {
198 errno = serrno;
199 return (resolved);
200 }
1f2f436a 201- return (NULL);
224c7076 202+#endif /* !__DARWIN_UNIX03 */
1f2f436a 203+ goto error_return;
59e0d9fe
A
204 }
205- if (S_ISLNK(sb.st_mode)) {
3d9156a7
A
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+ */
34e8f829 226+ char temp[sizeof(sfs.f_mntonname)];
3d9156a7
A
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+ }
59e0d9fe
A
251+ if (islink) {
252 if (symlinks++ > MAXSYMLINKS) {
253 errno = ELOOP;
1f2f436a
A
254- return (NULL);
255+ goto error_return;
224c7076
A
256 }
257 slen = readlink(resolved, symlink, sizeof(symlink) - 1);
258- if (slen < 0)
1f2f436a 259- return (NULL);
224c7076 260+ if (slen < 0) {
1f2f436a 261+ goto error_return;
224c7076
A
262+ }
263 symlink[slen] = '\0';
3d9156a7
A
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';
1f2f436a
A
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;
59e0d9fe
A
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;
1f2f436a 298+ goto error_return;
59e0d9fe
A
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 /*