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