summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/libc/stdlib/realpath.320
-rw-r--r--src/lib/libc/stdlib/realpath.c247
2 files changed, 143 insertions, 124 deletions
diff --git a/src/lib/libc/stdlib/realpath.3 b/src/lib/libc/stdlib/realpath.3
index b8093b51e1..c178f0a9d3 100644
--- a/src/lib/libc/stdlib/realpath.3
+++ b/src/lib/libc/stdlib/realpath.3
@@ -28,7 +28,7 @@
28.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29.\" SUCH DAMAGE. 29.\" SUCH DAMAGE.
30.\" 30.\"
31.\" $OpenBSD: realpath.3,v 1.10 2003/06/02 20:18:38 millert Exp $ 31.\" $OpenBSD: realpath.3,v 1.11 2005/03/29 19:34:14 brad Exp $
32.\" 32.\"
33.Dd February 16, 1994 33.Dd February 16, 1994
34.Dt REALPATH 3 34.Dt REALPATH 3
@@ -40,7 +40,7 @@
40.Fd #include <sys/param.h> 40.Fd #include <sys/param.h>
41.Fd #include <stdlib.h> 41.Fd #include <stdlib.h>
42.Ft "char *" 42.Ft "char *"
43.Fn realpath "const char *pathname" "char resolvedname[MAXPATHLEN]" 43.Fn realpath "const char *pathname" "char resolved[PATH_MAX]"
44.Sh DESCRIPTION 44.Sh DESCRIPTION
45The 45The
46.Fn realpath 46.Fn realpath
@@ -53,13 +53,13 @@ and
53in 53in
54.Fa pathname , 54.Fa pathname ,
55and copies the resulting absolute pathname into the memory referenced by 55and copies the resulting absolute pathname into the memory referenced by
56.Fa resolvedname . 56.Fa resolved .
57The 57The
58.Fa resolvedname 58.Fa resolved
59argument 59argument
60.Em must 60.Em must
61refer to a buffer capable of storing at least 61refer to a buffer capable of storing at least
62.Dv MAXPATHLEN 62.Dv PATH_MAX
63characters. 63characters.
64.Pp 64.Pp
65The 65The
@@ -76,14 +76,14 @@ is called.
76The 76The
77.Fn realpath 77.Fn realpath
78function returns 78function returns
79.Fa resolvedname 79.Fa resolved
80on success. 80on success.
81If an error occurs, 81If an error occurs,
82.Fn realpath 82.Fn realpath
83returns 83returns
84.Dv NULL , 84.Dv NULL ,
85and 85and
86.Fa resolvedname 86.Fa resolved
87contains the pathname which caused the problem. 87contains the pathname which caused the problem.
88.Sh ERRORS 88.Sh ERRORS
89The function 89The function
@@ -91,11 +91,7 @@ The function
91may fail and set the external variable 91may fail and set the external variable
92.Va errno 92.Va errno
93for any of the errors specified for the library functions 93for any of the errors specified for the library functions
94.Xr chdir 2 ,
95.Xr close 2 ,
96.Xr fchdir 2 ,
97.Xr lstat 2 , 94.Xr lstat 2 ,
98.Xr open 2 ,
99.Xr readlink 2 , 95.Xr readlink 2 ,
100and 96and
101.Xr getcwd 3 . 97.Xr getcwd 3 .
@@ -115,6 +111,6 @@ The
115version always returns absolute pathnames, 111version always returns absolute pathnames,
116whereas the Solaris implementation will, 112whereas the Solaris implementation will,
117under certain circumstances, return a relative 113under certain circumstances, return a relative
118.Fa resolvedname 114.Fa resolved
119when given a relative 115when given a relative
120.Fa pathname . 116.Fa pathname .
diff --git a/src/lib/libc/stdlib/realpath.c b/src/lib/libc/stdlib/realpath.c
index 37b4ad6159..62e06b00be 100644
--- a/src/lib/libc/stdlib/realpath.c
+++ b/src/lib/libc/stdlib/realpath.c
@@ -1,9 +1,5 @@
1/* 1/*
2 * Copyright (c) 1994 2 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Jan-Simon Pendry.
7 * 3 *
8 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
@@ -13,14 +9,14 @@
13 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors 12 * 3. The names of the authors may not be used to endorse or promote
17 * may be used to endorse or promote products derived from this software 13 * products derived from this software without specific prior written
18 * without specific prior written permission. 14 * permission.
19 * 15 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -30,146 +26,173 @@
30 * SUCH DAMAGE. 26 * SUCH DAMAGE.
31 */ 27 */
32 28
29#if 0
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: /usr/local/www/cvsroot/FreeBSD/src/lib/libc/stdlib/realpath.c,v 1.9.2.1 2003/05/22 17:11:44 fjoe Exp $");
32#endif
33
33#if defined(LIBC_SCCS) && !defined(lint) 34#if defined(LIBC_SCCS) && !defined(lint)
34static char *rcsid = "$OpenBSD: realpath.c,v 1.11 2004/11/30 15:12:59 millert Exp $"; 35static char *rcsid = "$OpenBSD: realpath.c,v 1.12 2005/03/29 19:34:14 brad Exp $";
35#endif /* LIBC_SCCS and not lint */ 36#endif /* LIBC_SCCS and not lint */
36 37
37#include <sys/param.h> 38#include <sys/param.h>
38#include <sys/stat.h> 39#include <sys/stat.h>
39 40
40#include <errno.h> 41#include <errno.h>
41#include <fcntl.h>
42#include <stdlib.h> 42#include <stdlib.h>
43#include <string.h> 43#include <string.h>
44#include <unistd.h> 44#include <unistd.h>
45 45
46/* 46/*
47 * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); 47 * char *realpath(const char *path, char resolved[PATH_MAX]);
48 * 48 *
49 * Find the real name of path, by removing all ".", ".." and symlink 49 * Find the real name of path, by removing all ".", ".." and symlink
50 * components. Returns (resolved) on success, or (NULL) on failure, 50 * components. Returns (resolved) on success, or (NULL) on failure,
51 * in which case the path which caused trouble is left in (resolved). 51 * in which case the path which caused trouble is left in (resolved).
52 */ 52 */
53char * 53char *
54realpath(path, resolved) 54realpath(const char *path, char resolved[PATH_MAX])
55 const char *path;
56 char *resolved;
57{ 55{
58 struct stat sb; 56 struct stat sb;
59 int fd, n, needslash, serrno; 57 char *p, *q, *s;
60 char *p, *q, wbuf[MAXPATHLEN]; 58 size_t left_len, resolved_len;
61 int symlinks = 0; 59 unsigned symlinks;
60 int serrno, slen;
61 char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
62 62
63 /* Save the starting point. */ 63 serrno = errno;
64 if ((fd = open(".", O_RDONLY)) < 0) { 64 symlinks = 0;
65 resolved[0] = '.'; 65 if (path[0] == '/') {
66 resolved[0] = '/';
66 resolved[1] = '\0'; 67 resolved[1] = '\0';
67 return (NULL); 68 if (path[1] == '\0')
68 } 69 return (resolved);
69 70 resolved_len = 1;
70 /* Convert "." -> "" to optimize away a needless lstat() and chdir() */ 71 left_len = strlcpy(left, path + 1, sizeof(left));
71 if (path[0] == '.' && path[1] == '\0') 72 } else {
72 path = ""; 73 if (getcwd(resolved, PATH_MAX) == NULL) {
73 74 strlcpy(resolved, ".", PATH_MAX);
74 /* 75 return (NULL);
75 * Find the dirname and basename from the path to be resolved.
76 * Change directory to the dirname component.
77 * lstat the basename part.
78 * if it is a symlink, read in the value and loop.
79 * if it is a directory, then change to that directory.
80 * get the current directory name and append the basename.
81 */
82 if (strlcpy(resolved, path, MAXPATHLEN) >= MAXPATHLEN) {
83 serrno = ENAMETOOLONG;
84 goto err2;
85 }
86loop:
87 q = strrchr(resolved, '/');
88 if (q != NULL) {
89 p = q + 1;
90 if (q == resolved)
91 q = "/";
92 else {
93 do {
94 --q;
95 } while (q > resolved && *q == '/');
96 q[1] = '\0';
97 q = resolved;
98 }
99 if (chdir(q) < 0)
100 goto err1;
101 } else
102 p = resolved;
103
104 /* Deal with the last component. */
105 if (*p != '\0' && lstat(p, &sb) == 0) {
106 if (S_ISLNK(sb.st_mode)) {
107 if (++symlinks > MAXSYMLINKS) {
108 errno = ELOOP;
109 goto err1;
110 }
111 if ((n = readlink(p, resolved, MAXPATHLEN-1)) < 0)
112 goto err1;
113 resolved[n] = '\0';
114 goto loop;
115 }
116 if (S_ISDIR(sb.st_mode)) {
117 if (chdir(p) < 0)
118 goto err1;
119 p = "";
120 } 76 }
77 resolved_len = strlen(resolved);
78 left_len = strlcpy(left, path, sizeof(left));
121 } 79 }
122 80 if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
123 /*
124 * Save the last component name and get the full pathname of
125 * the current directory.
126 */
127 if (strlcpy(wbuf, p, sizeof(wbuf)) >= sizeof(wbuf)) {
128 errno = ENAMETOOLONG; 81 errno = ENAMETOOLONG;
129 goto err1; 82 return (NULL);
130 } 83 }
131 if (getcwd(resolved, MAXPATHLEN) == NULL)
132 goto err1;
133 84
134 /* 85 /*
135 * Join the two strings together, ensuring that the right thing 86 * Iterate over path components in `left'.
136 * happens if the last component is empty, or the dirname is root.
137 */ 87 */
138 if (resolved[0] == '/' && resolved[1] == '\0') 88 while (left_len != 0) {
139 needslash = 0; 89 /*
140 else 90 * Extract the next path component and adjust `left'
141 needslash = 1; 91 * and its length.
142 92 */
143 if (*wbuf) { 93 p = strchr(left, '/');
144 if (strlen(resolved) + strlen(wbuf) + needslash >= MAXPATHLEN) { 94 s = p ? p : left + left_len;
95 if (s - left >= sizeof(next_token)) {
145 errno = ENAMETOOLONG; 96 errno = ENAMETOOLONG;
146 goto err1; 97 return (NULL);
147 } 98 }
148 if (needslash) { 99 memcpy(next_token, left, s - left);
149 if (strlcat(resolved, "/", MAXPATHLEN) >= MAXPATHLEN) { 100 next_token[s - left] = '\0';
101 left_len -= s - left;
102 if (p != NULL)
103 memmove(left, s + 1, left_len + 1);
104 if (resolved[resolved_len - 1] != '/') {
105 if (resolved_len + 1 >= PATH_MAX) {
150 errno = ENAMETOOLONG; 106 errno = ENAMETOOLONG;
151 goto err1; 107 return (NULL);
152 } 108 }
109 resolved[resolved_len++] = '/';
110 resolved[resolved_len] = '\0';
153 } 111 }
154 if (strlcat(resolved, wbuf, MAXPATHLEN) >= MAXPATHLEN) { 112 if (next_token[0] == '\0')
113 continue;
114 else if (strcmp(next_token, ".") == 0)
115 continue;
116 else if (strcmp(next_token, "..") == 0) {
117 /*
118 * Strip the last path component except when we have
119 * single "/"
120 */
121 if (resolved_len > 1) {
122 resolved[resolved_len - 1] = '\0';
123 q = strrchr(resolved, '/') + 1;
124 *q = '\0';
125 resolved_len = q - resolved;
126 }
127 continue;
128 }
129
130 /*
131 * Append the next path component and lstat() it. If
132 * lstat() fails we still can return successfully if
133 * there are no more path components left.
134 */
135 resolved_len = strlcat(resolved, next_token, PATH_MAX);
136 if (resolved_len >= PATH_MAX) {
155 errno = ENAMETOOLONG; 137 errno = ENAMETOOLONG;
156 goto err1; 138 return (NULL);
157 } 139 }
158 } 140 if (lstat(resolved, &sb) != 0) {
141 if (errno == ENOENT && p == NULL) {
142 errno = serrno;
143 return (resolved);
144 }
145 return (NULL);
146 }
147 if (S_ISLNK(sb.st_mode)) {
148 if (symlinks++ > MAXSYMLINKS) {
149 errno = ELOOP;
150 return (NULL);
151 }
152 slen = readlink(resolved, symlink, sizeof(symlink) - 1);
153 if (slen < 0)
154 return (NULL);
155 symlink[slen] = '\0';
156 if (symlink[0] == '/') {
157 resolved[1] = 0;
158 resolved_len = 1;
159 } else if (resolved_len > 1) {
160 /* Strip the last path component. */
161 resolved[resolved_len - 1] = '\0';
162 q = strrchr(resolved, '/') + 1;
163 *q = '\0';
164 resolved_len = q - resolved;
165 }
159 166
160 /* Go back to where we came from. */ 167 /*
161 if (fchdir(fd) < 0) { 168 * If there are any path components left, then
162 serrno = errno; 169 * append them to symlink. The result is placed
163 goto err2; 170 * in `left'.
171 */
172 if (p != NULL) {
173 if (symlink[slen - 1] != '/') {
174 if (slen + 1 >= sizeof(symlink)) {
175 errno = ENAMETOOLONG;
176 return (NULL);
177 }
178 symlink[slen] = '/';
179 symlink[slen + 1] = 0;
180 }
181 left_len = strlcat(symlink, left, sizeof(left));
182 if (left_len >= sizeof(left)) {
183 errno = ENAMETOOLONG;
184 return (NULL);
185 }
186 }
187 left_len = strlcpy(left, symlink, sizeof(left));
188 }
164 } 189 }
165 190
166 /* It's okay if the close fails, what's an fd more or less? */ 191 /*
167 (void)close(fd); 192 * Remove trailing slash except when the resolved pathname
193 * is a single "/".
194 */
195 if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
196 resolved[resolved_len - 1] = '\0';
168 return (resolved); 197 return (resolved);
169
170err1: serrno = errno;
171 (void)fchdir(fd);
172err2: (void)close(fd);
173 errno = serrno;
174 return (NULL);
175} 198}