1d01a28e2SPeter Wemm /* 2226a0f0fSMax Khon * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 3d01a28e2SPeter Wemm * 4d01a28e2SPeter Wemm * Redistribution and use in source and binary forms, with or without 5d01a28e2SPeter Wemm * modification, are permitted provided that the following conditions 6d01a28e2SPeter Wemm * are met: 7d01a28e2SPeter Wemm * 1. Redistributions of source code must retain the above copyright 8d01a28e2SPeter Wemm * notice, this list of conditions and the following disclaimer. 9d01a28e2SPeter Wemm * 2. Redistributions in binary form must reproduce the above copyright 10d01a28e2SPeter Wemm * notice, this list of conditions and the following disclaimer in the 11d01a28e2SPeter Wemm * documentation and/or other materials provided with the distribution. 12226a0f0fSMax Khon * 3. The names of the authors may not be used to endorse or promote 13226a0f0fSMax Khon * products derived from this software without specific prior written 14226a0f0fSMax Khon * permission. 15d01a28e2SPeter Wemm * 16226a0f0fSMax Khon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17d01a28e2SPeter Wemm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18d01a28e2SPeter Wemm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19226a0f0fSMax Khon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20d01a28e2SPeter Wemm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21d01a28e2SPeter Wemm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22d01a28e2SPeter Wemm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23d01a28e2SPeter Wemm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24d01a28e2SPeter Wemm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25d01a28e2SPeter Wemm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26d01a28e2SPeter Wemm * SUCH DAMAGE. 27d01a28e2SPeter Wemm */ 28d01a28e2SPeter Wemm 29d01a28e2SPeter Wemm #if defined(LIBC_SCCS) && !defined(lint) 30d01a28e2SPeter Wemm static char sccsid[] = "@(#)realpath.c 8.1 (Berkeley) 2/16/94"; 31d01a28e2SPeter Wemm #endif /* LIBC_SCCS and not lint */ 32333fc21eSDavid E. O'Brien #include <sys/cdefs.h> 33333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 34d01a28e2SPeter Wemm 35d201fe46SDaniel Eischen #include "namespace.h" 36d01a28e2SPeter Wemm #include <sys/param.h> 37d01a28e2SPeter Wemm #include <sys/stat.h> 38d01a28e2SPeter Wemm 39d01a28e2SPeter Wemm #include <errno.h> 40839e119eSMax Khon #include <stdlib.h> 41d01a28e2SPeter Wemm #include <string.h> 42d01a28e2SPeter Wemm #include <unistd.h> 43d201fe46SDaniel Eischen #include "un-namespace.h" 44d01a28e2SPeter Wemm 45d01a28e2SPeter Wemm /* 46d01a28e2SPeter Wemm * Find the real name of path, by removing all ".", ".." and symlink 47d01a28e2SPeter Wemm * components. Returns (resolved) on success, or (NULL) on failure, 48d01a28e2SPeter Wemm * in which case the path which caused trouble is left in (resolved). 49d01a28e2SPeter Wemm */ 50d01a28e2SPeter Wemm char * 519d79ec20SKonstantin Belousov realpath(const char * __restrict path, char * __restrict resolved) 52d01a28e2SPeter Wemm { 53839e119eSMax Khon struct stat sb; 54839e119eSMax Khon char *p, *q, *s; 55226a0f0fSMax Khon size_t left_len, resolved_len; 56839e119eSMax Khon unsigned symlinks; 574e738f5aSKonstantin Belousov int m, serrno, slen; 58839e119eSMax Khon char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 59d01a28e2SPeter Wemm 609d79ec20SKonstantin Belousov if (path == NULL) { 619d79ec20SKonstantin Belousov errno = EINVAL; 629d79ec20SKonstantin Belousov return (NULL); 639d79ec20SKonstantin Belousov } 649d79ec20SKonstantin Belousov if (path[0] == '\0') { 659d79ec20SKonstantin Belousov errno = ENOENT; 669d79ec20SKonstantin Belousov return (NULL); 679d79ec20SKonstantin Belousov } 68839e119eSMax Khon serrno = errno; 699d79ec20SKonstantin Belousov if (resolved == NULL) { 709d79ec20SKonstantin Belousov resolved = malloc(PATH_MAX); 719d79ec20SKonstantin Belousov if (resolved == NULL) 729d79ec20SKonstantin Belousov return (NULL); 739d79ec20SKonstantin Belousov m = 1; 749d79ec20SKonstantin Belousov } else 759d79ec20SKonstantin Belousov m = 0; 76839e119eSMax Khon symlinks = 0; 77226a0f0fSMax Khon if (path[0] == '/') { 78839e119eSMax Khon resolved[0] = '/'; 79839e119eSMax Khon resolved[1] = '\0'; 80226a0f0fSMax Khon if (path[1] == '\0') 81839e119eSMax Khon return (resolved); 82226a0f0fSMax Khon resolved_len = 1; 83d0509082SJacques Vidrine left_len = strlcpy(left, path + 1, sizeof(left)); 84226a0f0fSMax Khon } else { 85839e119eSMax Khon if (getcwd(resolved, PATH_MAX) == NULL) { 86655c8a60SKonstantin Belousov if (m) 879d79ec20SKonstantin Belousov free(resolved); 884e738f5aSKonstantin Belousov else { 894e738f5aSKonstantin Belousov resolved[0] = '.'; 904e738f5aSKonstantin Belousov resolved[1] = '\0'; 914e738f5aSKonstantin Belousov } 92839e119eSMax Khon return (NULL); 93d01a28e2SPeter Wemm } 94839e119eSMax Khon resolved_len = strlen(resolved); 95d0509082SJacques Vidrine left_len = strlcpy(left, path, sizeof(left)); 96e53211ceSPoul-Henning Kamp } 97839e119eSMax Khon if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { 989d79ec20SKonstantin Belousov if (m) 999d79ec20SKonstantin Belousov free(resolved); 100d01a28e2SPeter Wemm errno = ENAMETOOLONG; 101839e119eSMax Khon return (NULL); 102d01a28e2SPeter Wemm } 103d01a28e2SPeter Wemm 104839e119eSMax Khon /* 105839e119eSMax Khon * Iterate over path components in `left'. 106839e119eSMax Khon */ 107839e119eSMax Khon while (left_len != 0) { 108839e119eSMax Khon /* 109839e119eSMax Khon * Extract the next path component and adjust `left' 110839e119eSMax Khon * and its length. 111839e119eSMax Khon */ 112839e119eSMax Khon p = strchr(left, '/'); 113839e119eSMax Khon s = p ? p : left + left_len; 114839e119eSMax Khon if (s - left >= sizeof(next_token)) { 1159d79ec20SKonstantin Belousov if (m) 1169d79ec20SKonstantin Belousov free(resolved); 117839e119eSMax Khon errno = ENAMETOOLONG; 118839e119eSMax Khon return (NULL); 119839e119eSMax Khon } 120839e119eSMax Khon memcpy(next_token, left, s - left); 121839e119eSMax Khon next_token[s - left] = '\0'; 122226a0f0fSMax Khon left_len -= s - left; 123226a0f0fSMax Khon if (p != NULL) 124226a0f0fSMax Khon memmove(left, s + 1, left_len + 1); 125839e119eSMax Khon if (resolved[resolved_len - 1] != '/') { 126be6a158eSMax Khon if (resolved_len + 1 >= PATH_MAX) { 1279d79ec20SKonstantin Belousov if (m) 1289d79ec20SKonstantin Belousov free(resolved); 129226a0f0fSMax Khon errno = ENAMETOOLONG; 130839e119eSMax Khon return (NULL); 131d01a28e2SPeter Wemm } 132839e119eSMax Khon resolved[resolved_len++] = '/'; 133839e119eSMax Khon resolved[resolved_len] = '\0'; 134226a0f0fSMax Khon } 135fdbe55fcSKonstantin Belousov if (next_token[0] == '\0') { 136fdbe55fcSKonstantin Belousov /* 137fdbe55fcSKonstantin Belousov * Handle consequential slashes. The path 138fdbe55fcSKonstantin Belousov * before slash shall point to a directory. 139fdbe55fcSKonstantin Belousov * 140fdbe55fcSKonstantin Belousov * Only the trailing slashes are not covered 141fdbe55fcSKonstantin Belousov * by other checks in the loop, but we verify 142fdbe55fcSKonstantin Belousov * the prefix for any (rare) "//" or "/\0" 143fdbe55fcSKonstantin Belousov * occurence to not implement lookahead. 144fdbe55fcSKonstantin Belousov */ 145fdbe55fcSKonstantin Belousov if (lstat(resolved, &sb) != 0) { 146fdbe55fcSKonstantin Belousov if (m) 147fdbe55fcSKonstantin Belousov free(resolved); 148fdbe55fcSKonstantin Belousov return (NULL); 149fdbe55fcSKonstantin Belousov } 150fdbe55fcSKonstantin Belousov if (!S_ISDIR(sb.st_mode)) { 151fdbe55fcSKonstantin Belousov if (m) 152fdbe55fcSKonstantin Belousov free(resolved); 153fdbe55fcSKonstantin Belousov errno = ENOTDIR; 154fdbe55fcSKonstantin Belousov return (NULL); 155fdbe55fcSKonstantin Belousov } 156226a0f0fSMax Khon continue; 157fdbe55fcSKonstantin Belousov } 158839e119eSMax Khon else if (strcmp(next_token, ".") == 0) 159226a0f0fSMax Khon continue; 160839e119eSMax Khon else if (strcmp(next_token, "..") == 0) { 161839e119eSMax Khon /* 162839e119eSMax Khon * Strip the last path component except when we have 163839e119eSMax Khon * single "/" 164839e119eSMax Khon */ 165226a0f0fSMax Khon if (resolved_len > 1) { 166839e119eSMax Khon resolved[resolved_len - 1] = '\0'; 167f4203da8SMax Khon q = strrchr(resolved, '/') + 1; 168226a0f0fSMax Khon *q = '\0'; 169839e119eSMax Khon resolved_len = q - resolved; 170226a0f0fSMax Khon } 171226a0f0fSMax Khon continue; 172226a0f0fSMax Khon } 173226a0f0fSMax Khon 174839e119eSMax Khon /* 175fdbe55fcSKonstantin Belousov * Append the next path component and lstat() it. 176839e119eSMax Khon */ 177d0509082SJacques Vidrine resolved_len = strlcat(resolved, next_token, PATH_MAX); 178be6a158eSMax Khon if (resolved_len >= PATH_MAX) { 1799d79ec20SKonstantin Belousov if (m) 1809d79ec20SKonstantin Belousov free(resolved); 181226a0f0fSMax Khon errno = ENAMETOOLONG; 182839e119eSMax Khon return (NULL); 183226a0f0fSMax Khon } 184839e119eSMax Khon if (lstat(resolved, &sb) != 0) { 185fdbe55fcSKonstantin Belousov if (errno != ENOENT || p != NULL) 186fdbe55fcSKonstantin Belousov errno = ENOTDIR; 187655c8a60SKonstantin Belousov if (m) 1889d79ec20SKonstantin Belousov free(resolved); 189839e119eSMax Khon return (NULL); 190226a0f0fSMax Khon } 191839e119eSMax Khon if (S_ISLNK(sb.st_mode)) { 192839e119eSMax Khon if (symlinks++ > MAXSYMLINKS) { 1939d79ec20SKonstantin Belousov if (m) 1949d79ec20SKonstantin Belousov free(resolved); 195226a0f0fSMax Khon errno = ELOOP; 196839e119eSMax Khon return (NULL); 197226a0f0fSMax Khon } 198839e119eSMax Khon slen = readlink(resolved, symlink, sizeof(symlink) - 1); 1999d79ec20SKonstantin Belousov if (slen < 0) { 200655c8a60SKonstantin Belousov if (m) 2019d79ec20SKonstantin Belousov free(resolved); 202839e119eSMax Khon return (NULL); 2039d79ec20SKonstantin Belousov } 204226a0f0fSMax Khon symlink[slen] = '\0'; 205226a0f0fSMax Khon if (symlink[0] == '/') { 206839e119eSMax Khon resolved[1] = 0; 207226a0f0fSMax Khon resolved_len = 1; 208226a0f0fSMax Khon } else if (resolved_len > 1) { 209839e119eSMax Khon /* Strip the last path component. */ 210839e119eSMax Khon resolved[resolved_len - 1] = '\0'; 211f4203da8SMax Khon q = strrchr(resolved, '/') + 1; 212226a0f0fSMax Khon *q = '\0'; 213839e119eSMax Khon resolved_len = q - resolved; 214226a0f0fSMax Khon } 215226a0f0fSMax Khon 216839e119eSMax Khon /* 217839e119eSMax Khon * If there are any path components left, then 218839e119eSMax Khon * append them to symlink. The result is placed 219839e119eSMax Khon * in `left'. 220839e119eSMax Khon */ 221057e4034SMax Khon if (p != NULL) { 222057e4034SMax Khon if (symlink[slen - 1] != '/') { 223839e119eSMax Khon if (slen + 1 >= sizeof(symlink)) { 2249d79ec20SKonstantin Belousov if (m) 2259d79ec20SKonstantin Belousov free(resolved); 226226a0f0fSMax Khon errno = ENAMETOOLONG; 227839e119eSMax Khon return (NULL); 228226a0f0fSMax Khon } 229226a0f0fSMax Khon symlink[slen] = '/'; 230226a0f0fSMax Khon symlink[slen + 1] = 0; 231226a0f0fSMax Khon } 23224a92ae0SEd Schouten left_len = strlcat(symlink, left, 23324a92ae0SEd Schouten sizeof(symlink)); 234839e119eSMax Khon if (left_len >= sizeof(left)) { 2359d79ec20SKonstantin Belousov if (m) 2369d79ec20SKonstantin Belousov free(resolved); 237226a0f0fSMax Khon errno = ENAMETOOLONG; 238839e119eSMax Khon return (NULL); 239226a0f0fSMax Khon } 240057e4034SMax Khon } 241d0509082SJacques Vidrine left_len = strlcpy(left, symlink, sizeof(left)); 242226a0f0fSMax Khon } 243226a0f0fSMax Khon } 244226a0f0fSMax Khon 245839e119eSMax Khon /* 246839e119eSMax Khon * Remove trailing slash except when the resolved pathname 247839e119eSMax Khon * is a single "/". 248839e119eSMax Khon */ 249839e119eSMax Khon if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 250839e119eSMax Khon resolved[resolved_len - 1] = '\0'; 251839e119eSMax Khon return (resolved); 252d01a28e2SPeter Wemm } 253