summaryrefslogtreecommitdiff
path: root/src/lib/libc/stdlib/realpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libc/stdlib/realpath.c')
-rw-r--r--src/lib/libc/stdlib/realpath.c253
1 files changed, 154 insertions, 99 deletions
diff --git a/src/lib/libc/stdlib/realpath.c b/src/lib/libc/stdlib/realpath.c
index e349b7e068..e0f9b123b3 100644
--- a/src/lib/libc/stdlib/realpath.c
+++ b/src/lib/libc/stdlib/realpath.c
@@ -1,9 +1,6 @@
1/* $OpenBSD: realpath.c,v 1.16 2013/04/05 12:59:54 kurt Exp $ */
1/* 2/*
2 * Copyright (c) 1994 3 * 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 * 4 *
8 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
@@ -13,18 +10,14 @@
13 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the 11 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution. 12 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software 13 * 3. The names of the authors may not be used to endorse or promote
17 * must display the following acknowledgement: 14 * products derived from this software without specific prior written
18 * This product includes software developed by the University of 15 * permission.
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 * 16 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -34,126 +27,188 @@
34 * SUCH DAMAGE. 27 * SUCH DAMAGE.
35 */ 28 */
36 29
37#if defined(LIBC_SCCS) && !defined(lint)
38/*static char sccsid[] = "from: @(#)realpath.c 8.1 (Berkeley) 2/16/94";*/
39static char *rcsid = "$Id: realpath.c,v 1.1.1.1 1995/10/18 08:42:19 deraadt Exp $";
40#endif /* LIBC_SCCS and not lint */
41
42#include <sys/param.h> 30#include <sys/param.h>
43#include <sys/stat.h> 31#include <sys/stat.h>
44 32
45#include <errno.h> 33#include <errno.h>
46#include <fcntl.h>
47#include <stdlib.h> 34#include <stdlib.h>
48#include <string.h> 35#include <string.h>
49#include <unistd.h> 36#include <unistd.h>
50 37
38/* A slightly modified copy of this file exists in libexec/ld.so */
39
51/* 40/*
52 * char *realpath(const char *path, char resolved_path[MAXPATHLEN]); 41 * char *realpath(const char *path, char resolved[PATH_MAX]);
53 * 42 *
54 * Find the real name of path, by removing all ".", ".." and symlink 43 * Find the real name of path, by removing all ".", ".." and symlink
55 * components. Returns (resolved) on success, or (NULL) on failure, 44 * components. Returns (resolved) on success, or (NULL) on failure,
56 * in which case the path which caused trouble is left in (resolved). 45 * in which case the path which caused trouble is left in (resolved).
57 */ 46 */
58char * 47char *
59realpath(path, resolved) 48realpath(const char *path, char *resolved)
60 const char *path;
61 char *resolved;
62{ 49{
63 struct stat sb; 50 struct stat sb;
64 int fd, n, rootd, serrno; 51 char *p, *q, *s;
65 char *p, *q, wbuf[MAXPATHLEN]; 52 size_t left_len, resolved_len;
53 unsigned symlinks;
54 int serrno, slen, mem_allocated;
55 char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];
66 56
67 /* Save the starting point. */ 57 if (path[0] == '\0') {
68 if ((fd = open(".", O_RDONLY)) < 0) { 58 errno = ENOENT;
69 (void)strcpy(resolved, ".");
70 return (NULL); 59 return (NULL);
71 } 60 }
72 61
73 /* 62 serrno = errno;
74 * Find the dirname and basename from the path to be resolved. 63
75 * Change directory to the dirname component. 64 if (resolved == NULL) {
76 * lstat the basename part. 65 resolved = malloc(PATH_MAX);
77 * if it is a symlink, read in the value and loop. 66 if (resolved == NULL)
78 * if it is a directory, then change to that directory. 67 return (NULL);
79 * get the current directory name and append the basename. 68 mem_allocated = 1;
80 */
81 (void)strncpy(resolved, path, MAXPATHLEN - 1);
82 resolved[MAXPATHLEN - 1] = '\0';
83loop:
84 q = strrchr(resolved, '/');
85 if (q != NULL) {
86 p = q + 1;
87 if (q == resolved)
88 q = "/";
89 else {
90 do {
91 --q;
92 } while (q > resolved && *q == '/');
93 q[1] = '\0';
94 q = resolved;
95 }
96 if (chdir(q) < 0)
97 goto err1;
98 } else 69 } else
99 p = resolved; 70 mem_allocated = 0;
100 71
101 /* Deal with the last component. */ 72 symlinks = 0;
102 if (lstat(p, &sb) == 0) { 73 if (path[0] == '/') {
103 if (S_ISLNK(sb.st_mode)) { 74 resolved[0] = '/';
104 n = readlink(p, resolved, MAXPATHLEN); 75 resolved[1] = '\0';
105 if (n < 0) 76 if (path[1] == '\0')
106 goto err1; 77 return (resolved);
107 resolved[n] = '\0'; 78 resolved_len = 1;
108 goto loop; 79 left_len = strlcpy(left, path + 1, sizeof(left));
109 } 80 } else {
110 if (S_ISDIR(sb.st_mode)) { 81 if (getcwd(resolved, PATH_MAX) == NULL) {
111 if (chdir(p) < 0) 82 if (mem_allocated)
112 goto err1; 83 free(resolved);
113 p = ""; 84 else
85 strlcpy(resolved, ".", PATH_MAX);
86 return (NULL);
114 } 87 }
88 resolved_len = strlen(resolved);
89 left_len = strlcpy(left, path, sizeof(left));
90 }
91 if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {
92 errno = ENAMETOOLONG;
93 goto err;
115 } 94 }
116 95
117 /* 96 /*
118 * Save the last component name and get the full pathname of 97 * Iterate over path components in `left'.
119 * the current directory.
120 */
121 (void)strcpy(wbuf, p);
122 if (getcwd(resolved, MAXPATHLEN) == 0)
123 goto err1;
124
125 /*
126 * Join the two strings together, ensuring that the right thing
127 * happens if the last component is empty, or the dirname is root.
128 */ 98 */
129 if (resolved[0] == '/' && resolved[1] == '\0') 99 while (left_len != 0) {
130 rootd = 1; 100 /*
131 else 101 * Extract the next path component and adjust `left'
132 rootd = 0; 102 * and its length.
103 */
104 p = strchr(left, '/');
105 s = p ? p : left + left_len;
106 if (s - left >= sizeof(next_token)) {
107 errno = ENAMETOOLONG;
108 goto err;
109 }
110 memcpy(next_token, left, s - left);
111 next_token[s - left] = '\0';
112 left_len -= s - left;
113 if (p != NULL)
114 memmove(left, s + 1, left_len + 1);
115 if (resolved[resolved_len - 1] != '/') {
116 if (resolved_len + 1 >= PATH_MAX) {
117 errno = ENAMETOOLONG;
118 goto err;
119 }
120 resolved[resolved_len++] = '/';
121 resolved[resolved_len] = '\0';
122 }
123 if (next_token[0] == '\0')
124 continue;
125 else if (strcmp(next_token, ".") == 0)
126 continue;
127 else if (strcmp(next_token, "..") == 0) {
128 /*
129 * Strip the last path component except when we have
130 * single "/"
131 */
132 if (resolved_len > 1) {
133 resolved[resolved_len - 1] = '\0';
134 q = strrchr(resolved, '/') + 1;
135 *q = '\0';
136 resolved_len = q - resolved;
137 }
138 continue;
139 }
133 140
134 if (*wbuf) { 141 /*
135 if (strlen(resolved) + strlen(wbuf) + rootd + 1 > MAXPATHLEN) { 142 * Append the next path component and lstat() it. If
143 * lstat() fails we still can return successfully if
144 * there are no more path components left.
145 */
146 resolved_len = strlcat(resolved, next_token, PATH_MAX);
147 if (resolved_len >= PATH_MAX) {
136 errno = ENAMETOOLONG; 148 errno = ENAMETOOLONG;
137 goto err1; 149 goto err;
138 } 150 }
139 if (rootd == 0) 151 if (lstat(resolved, &sb) != 0) {
140 (void)strcat(resolved, "/"); 152 if (errno == ENOENT && p == NULL) {
141 (void)strcat(resolved, wbuf); 153 errno = serrno;
142 } 154 return (resolved);
155 }
156 goto err;
157 }
158 if (S_ISLNK(sb.st_mode)) {
159 if (symlinks++ > MAXSYMLINKS) {
160 errno = ELOOP;
161 goto err;
162 }
163 slen = readlink(resolved, symlink, sizeof(symlink) - 1);
164 if (slen < 0)
165 goto err;
166 symlink[slen] = '\0';
167 if (symlink[0] == '/') {
168 resolved[1] = 0;
169 resolved_len = 1;
170 } else if (resolved_len > 1) {
171 /* Strip the last path component. */
172 resolved[resolved_len - 1] = '\0';
173 q = strrchr(resolved, '/') + 1;
174 *q = '\0';
175 resolved_len = q - resolved;
176 }
143 177
144 /* Go back to where we came from. */ 178 /*
145 if (fchdir(fd) < 0) { 179 * If there are any path components left, then
146 serrno = errno; 180 * append them to symlink. The result is placed
147 goto err2; 181 * in `left'.
182 */
183 if (p != NULL) {
184 if (symlink[slen - 1] != '/') {
185 if (slen + 1 >= sizeof(symlink)) {
186 errno = ENAMETOOLONG;
187 goto err;
188 }
189 symlink[slen] = '/';
190 symlink[slen + 1] = 0;
191 }
192 left_len = strlcat(symlink, left, sizeof(symlink));
193 if (left_len >= sizeof(left)) {
194 errno = ENAMETOOLONG;
195 goto err;
196 }
197 }
198 left_len = strlcpy(left, symlink, sizeof(left));
199 }
148 } 200 }
149 201
150 /* It's okay if the close fails, what's an fd more or less? */ 202 /*
151 (void)close(fd); 203 * Remove trailing slash except when the resolved pathname
204 * is a single "/".
205 */
206 if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
207 resolved[resolved_len - 1] = '\0';
152 return (resolved); 208 return (resolved);
153 209
154err1: serrno = errno; 210err:
155 (void)fchdir(fd); 211 if (mem_allocated)
156err2: (void)close(fd); 212 free(resolved);
157 errno = serrno;
158 return (NULL); 213 return (NULL);
159} 214}