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 */ 50d933a4c0SKonstantin Belousov static char * 51d933a4c0SKonstantin Belousov realpath1(const char *path, char *resolved) 52d01a28e2SPeter Wemm { 53839e119eSMax Khon struct stat sb; 54f81e5b2dSKonstantin Belousov char *p, *q; 55f81e5b2dSKonstantin Belousov size_t left_len, resolved_len, next_token_len; 56839e119eSMax Khon unsigned symlinks; 57f81e5b2dSKonstantin Belousov ssize_t slen; 58839e119eSMax Khon char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 59d01a28e2SPeter Wemm 60839e119eSMax Khon symlinks = 0; 61226a0f0fSMax Khon if (path[0] == '/') { 62839e119eSMax Khon resolved[0] = '/'; 63839e119eSMax Khon resolved[1] = '\0'; 64226a0f0fSMax Khon if (path[1] == '\0') 65839e119eSMax Khon return (resolved); 66226a0f0fSMax Khon resolved_len = 1; 67d0509082SJacques Vidrine left_len = strlcpy(left, path + 1, sizeof(left)); 68226a0f0fSMax Khon } else { 69839e119eSMax Khon if (getcwd(resolved, PATH_MAX) == NULL) { 704e738f5aSKonstantin Belousov resolved[0] = '.'; 714e738f5aSKonstantin Belousov resolved[1] = '\0'; 72839e119eSMax Khon return (NULL); 73d01a28e2SPeter Wemm } 74839e119eSMax Khon resolved_len = strlen(resolved); 75d0509082SJacques Vidrine left_len = strlcpy(left, path, sizeof(left)); 76e53211ceSPoul-Henning Kamp } 77839e119eSMax Khon if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { 78d01a28e2SPeter Wemm errno = ENAMETOOLONG; 79839e119eSMax Khon return (NULL); 80d01a28e2SPeter Wemm } 81d01a28e2SPeter Wemm 82839e119eSMax Khon /* 83839e119eSMax Khon * Iterate over path components in `left'. 84839e119eSMax Khon */ 85839e119eSMax Khon while (left_len != 0) { 86839e119eSMax Khon /* 87839e119eSMax Khon * Extract the next path component and adjust `left' 88839e119eSMax Khon * and its length. 89839e119eSMax Khon */ 90839e119eSMax Khon p = strchr(left, '/'); 91f81e5b2dSKonstantin Belousov 92f81e5b2dSKonstantin Belousov next_token_len = p ? p - left : left_len; 93f81e5b2dSKonstantin Belousov memcpy(next_token, left, next_token_len); 94f81e5b2dSKonstantin Belousov next_token[next_token_len] = '\0'; 95f81e5b2dSKonstantin Belousov 96f81e5b2dSKonstantin Belousov if (p != NULL) { 97f81e5b2dSKonstantin Belousov left_len -= next_token_len + 1; 98f81e5b2dSKonstantin Belousov memmove(left, p + 1, left_len + 1); 99f81e5b2dSKonstantin Belousov } else { 100f81e5b2dSKonstantin Belousov left[0] = '\0'; 101f81e5b2dSKonstantin Belousov left_len = 0; 102839e119eSMax Khon } 103f81e5b2dSKonstantin Belousov 104839e119eSMax Khon if (resolved[resolved_len - 1] != '/') { 105be6a158eSMax Khon if (resolved_len + 1 >= PATH_MAX) { 106226a0f0fSMax Khon errno = ENAMETOOLONG; 107839e119eSMax Khon return (NULL); 108d01a28e2SPeter Wemm } 109839e119eSMax Khon resolved[resolved_len++] = '/'; 110839e119eSMax Khon resolved[resolved_len] = '\0'; 111226a0f0fSMax Khon } 112fdbe55fcSKonstantin Belousov if (next_token[0] == '\0') { 113310c8f3cSJilles Tjoelker /* Handle consequential slashes. */ 114226a0f0fSMax Khon continue; 115fdbe55fcSKonstantin Belousov } 116839e119eSMax Khon else if (strcmp(next_token, ".") == 0) 117226a0f0fSMax Khon continue; 118839e119eSMax Khon else if (strcmp(next_token, "..") == 0) { 119839e119eSMax Khon /* 120839e119eSMax Khon * Strip the last path component except when we have 121839e119eSMax Khon * single "/" 122839e119eSMax Khon */ 123226a0f0fSMax Khon if (resolved_len > 1) { 124839e119eSMax Khon resolved[resolved_len - 1] = '\0'; 125f4203da8SMax Khon q = strrchr(resolved, '/') + 1; 126226a0f0fSMax Khon *q = '\0'; 127839e119eSMax Khon resolved_len = q - resolved; 128226a0f0fSMax Khon } 129226a0f0fSMax Khon continue; 130226a0f0fSMax Khon } 131226a0f0fSMax Khon 132839e119eSMax Khon /* 133fdbe55fcSKonstantin Belousov * Append the next path component and lstat() it. 134839e119eSMax Khon */ 135d0509082SJacques Vidrine resolved_len = strlcat(resolved, next_token, PATH_MAX); 136be6a158eSMax Khon if (resolved_len >= PATH_MAX) { 137226a0f0fSMax Khon errno = ENAMETOOLONG; 138839e119eSMax Khon return (NULL); 139226a0f0fSMax Khon } 140d933a4c0SKonstantin Belousov if (lstat(resolved, &sb) != 0) 141839e119eSMax Khon return (NULL); 142839e119eSMax Khon if (S_ISLNK(sb.st_mode)) { 143839e119eSMax Khon if (symlinks++ > MAXSYMLINKS) { 144226a0f0fSMax Khon errno = ELOOP; 145839e119eSMax Khon return (NULL); 146226a0f0fSMax Khon } 147f81e5b2dSKonstantin Belousov slen = readlink(resolved, symlink, sizeof(symlink)); 148f81e5b2dSKonstantin Belousov if (slen <= 0 || slen >= sizeof(symlink)) { 149f81e5b2dSKonstantin Belousov if (slen < 0) { 150f81e5b2dSKonstantin Belousov /* keep errno from readlink(2) call */ 151f81e5b2dSKonstantin Belousov } else if (slen == 0) { 152f81e5b2dSKonstantin Belousov errno = ENOENT; 153f81e5b2dSKonstantin Belousov } else { 154f81e5b2dSKonstantin Belousov errno = ENAMETOOLONG; 155f81e5b2dSKonstantin Belousov } 156839e119eSMax Khon return (NULL); 1579d79ec20SKonstantin Belousov } 158226a0f0fSMax Khon symlink[slen] = '\0'; 159226a0f0fSMax Khon if (symlink[0] == '/') { 160839e119eSMax Khon resolved[1] = 0; 161226a0f0fSMax Khon resolved_len = 1; 162f81e5b2dSKonstantin Belousov } else { 163839e119eSMax Khon /* Strip the last path component. */ 164f4203da8SMax Khon q = strrchr(resolved, '/') + 1; 165226a0f0fSMax Khon *q = '\0'; 166839e119eSMax Khon resolved_len = q - resolved; 167226a0f0fSMax Khon } 168226a0f0fSMax Khon 169839e119eSMax Khon /* 170839e119eSMax Khon * If there are any path components left, then 171839e119eSMax Khon * append them to symlink. The result is placed 172839e119eSMax Khon * in `left'. 173839e119eSMax Khon */ 174057e4034SMax Khon if (p != NULL) { 175057e4034SMax Khon if (symlink[slen - 1] != '/') { 176839e119eSMax Khon if (slen + 1 >= sizeof(symlink)) { 177226a0f0fSMax Khon errno = ENAMETOOLONG; 178839e119eSMax Khon return (NULL); 179226a0f0fSMax Khon } 180226a0f0fSMax Khon symlink[slen] = '/'; 181226a0f0fSMax Khon symlink[slen + 1] = 0; 182226a0f0fSMax Khon } 18324a92ae0SEd Schouten left_len = strlcat(symlink, left, 18424a92ae0SEd Schouten sizeof(symlink)); 185f81e5b2dSKonstantin Belousov if (left_len >= sizeof(symlink)) { 186226a0f0fSMax Khon errno = ENAMETOOLONG; 187839e119eSMax Khon return (NULL); 188226a0f0fSMax Khon } 189057e4034SMax Khon } 190d0509082SJacques Vidrine left_len = strlcpy(left, symlink, sizeof(left)); 191310c8f3cSJilles Tjoelker } else if (!S_ISDIR(sb.st_mode) && p != NULL) { 192310c8f3cSJilles Tjoelker errno = ENOTDIR; 193310c8f3cSJilles Tjoelker return (NULL); 194226a0f0fSMax Khon } 195226a0f0fSMax Khon } 196226a0f0fSMax Khon 197839e119eSMax Khon /* 198839e119eSMax Khon * Remove trailing slash except when the resolved pathname 199839e119eSMax Khon * is a single "/". 200839e119eSMax Khon */ 201839e119eSMax Khon if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 202839e119eSMax Khon resolved[resolved_len - 1] = '\0'; 203839e119eSMax Khon return (resolved); 204d01a28e2SPeter Wemm } 205d933a4c0SKonstantin Belousov 206d933a4c0SKonstantin Belousov char * 207d933a4c0SKonstantin Belousov realpath(const char * __restrict path, char * __restrict resolved) 208d933a4c0SKonstantin Belousov { 209d933a4c0SKonstantin Belousov char *m, *res; 210d933a4c0SKonstantin Belousov 211d933a4c0SKonstantin Belousov if (path == NULL) { 212d933a4c0SKonstantin Belousov errno = EINVAL; 213d933a4c0SKonstantin Belousov return (NULL); 214d933a4c0SKonstantin Belousov } 215d933a4c0SKonstantin Belousov if (path[0] == '\0') { 216d933a4c0SKonstantin Belousov errno = ENOENT; 217d933a4c0SKonstantin Belousov return (NULL); 218d933a4c0SKonstantin Belousov } 219d933a4c0SKonstantin Belousov if (resolved != NULL) { 220d933a4c0SKonstantin Belousov m = NULL; 221d933a4c0SKonstantin Belousov } else { 222d933a4c0SKonstantin Belousov m = resolved = malloc(PATH_MAX); 223d933a4c0SKonstantin Belousov if (resolved == NULL) 224d933a4c0SKonstantin Belousov return (NULL); 225d933a4c0SKonstantin Belousov } 226d933a4c0SKonstantin Belousov res = realpath1(path, resolved); 227d933a4c0SKonstantin Belousov if (res == NULL) 228d933a4c0SKonstantin Belousov free(m); 229d933a4c0SKonstantin Belousov return (res); 230d933a4c0SKonstantin Belousov } 231