123833Speter/* 2112726Sfjoe * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 323833Speter * 423833Speter * Redistribution and use in source and binary forms, with or without 523833Speter * modification, are permitted provided that the following conditions 623833Speter * are met: 723833Speter * 1. Redistributions of source code must retain the above copyright 823833Speter * notice, this list of conditions and the following disclaimer. 923833Speter * 2. Redistributions in binary form must reproduce the above copyright 1023833Speter * notice, this list of conditions and the following disclaimer in the 1123833Speter * documentation and/or other materials provided with the distribution. 12112726Sfjoe * 3. The names of the authors may not be used to endorse or promote 13112726Sfjoe * products derived from this software without specific prior written 14112726Sfjoe * permission. 1523833Speter * 16112726Sfjoe * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1723833Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1823833Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19112726Sfjoe * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2023833Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2123833Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2223833Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2323833Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2423833Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2523833Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2623833Speter * SUCH DAMAGE. 2723833Speter */ 2823833Speter 2923833Speter#if defined(LIBC_SCCS) && !defined(lint) 3023833Speterstatic char sccsid[] = "@(#)realpath.c 8.1 (Berkeley) 2/16/94"; 3123833Speter#endif /* LIBC_SCCS and not lint */ 3292986Sobrien#include <sys/cdefs.h> 3392986Sobrien__FBSDID("$FreeBSD$"); 3423833Speter 3571579Sdeischen#include "namespace.h" 3623833Speter#include <sys/param.h> 3723833Speter#include <sys/stat.h> 3823833Speter 3923833Speter#include <errno.h> 40112823Sfjoe#include <stdlib.h> 4123833Speter#include <string.h> 4223833Speter#include <unistd.h> 4371579Sdeischen#include "un-namespace.h" 4423833Speter 4523833Speter/* 4623833Speter * Find the real name of path, by removing all ".", ".." and symlink 4723833Speter * components. Returns (resolved) on success, or (NULL) on failure, 4823833Speter * in which case the path which caused trouble is left in (resolved). 4923833Speter */ 5023833Speterchar * 51206893Skibrealpath(const char * __restrict path, char * __restrict resolved) 5223833Speter{ 53112823Sfjoe struct stat sb; 54112823Sfjoe char *p, *q, *s; 55112726Sfjoe size_t left_len, resolved_len; 56112823Sfjoe unsigned symlinks; 57236618Sache int m, slen; 58112823Sfjoe char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 59112726Sfjoe 60206893Skib if (path == NULL) { 61206893Skib errno = EINVAL; 62206893Skib return (NULL); 63206893Skib } 64206893Skib if (path[0] == '\0') { 65206893Skib errno = ENOENT; 66206893Skib return (NULL); 67206893Skib } 68206893Skib if (resolved == NULL) { 69206893Skib resolved = malloc(PATH_MAX); 70206893Skib if (resolved == NULL) 71206893Skib return (NULL); 72206893Skib m = 1; 73206893Skib } else 74206893Skib m = 0; 75112823Sfjoe symlinks = 0; 76112726Sfjoe if (path[0] == '/') { 77112823Sfjoe resolved[0] = '/'; 78112823Sfjoe resolved[1] = '\0'; 79112726Sfjoe if (path[1] == '\0') 80112823Sfjoe return (resolved); 81112726Sfjoe resolved_len = 1; 82114443Snectar left_len = strlcpy(left, path + 1, sizeof(left)); 83112726Sfjoe } else { 84112823Sfjoe if (getcwd(resolved, PATH_MAX) == NULL) { 85236618Sache if (m) 86206893Skib free(resolved); 87236618Sache else { 88217144Skib resolved[0] = '.'; 89217144Skib resolved[1] = '\0'; 90217144Skib } 91112823Sfjoe return (NULL); 92112726Sfjoe } 93112823Sfjoe resolved_len = strlen(resolved); 94114443Snectar left_len = strlcpy(left, path, sizeof(left)); 9523833Speter } 96112823Sfjoe if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { 97206893Skib if (m) 98206893Skib free(resolved); 99112726Sfjoe errno = ENAMETOOLONG; 100112823Sfjoe return (NULL); 101112726Sfjoe } 10223833Speter 103112823Sfjoe /* 104112823Sfjoe * Iterate over path components in `left'. 105112823Sfjoe */ 106112823Sfjoe while (left_len != 0) { 107112823Sfjoe /* 108112823Sfjoe * Extract the next path component and adjust `left' 109112823Sfjoe * and its length. 110112823Sfjoe */ 111112823Sfjoe p = strchr(left, '/'); 112112823Sfjoe s = p ? p : left + left_len; 113112823Sfjoe if (s - left >= sizeof(next_token)) { 114206893Skib if (m) 115206893Skib free(resolved); 116112823Sfjoe errno = ENAMETOOLONG; 117112823Sfjoe return (NULL); 118112823Sfjoe } 119112823Sfjoe memcpy(next_token, left, s - left); 120112823Sfjoe next_token[s - left] = '\0'; 121112726Sfjoe left_len -= s - left; 122112726Sfjoe if (p != NULL) 123112726Sfjoe memmove(left, s + 1, left_len + 1); 124112823Sfjoe if (resolved[resolved_len - 1] != '/') { 125112743Sfjoe if (resolved_len + 1 >= PATH_MAX) { 126206893Skib if (m) 127206893Skib free(resolved); 128112726Sfjoe errno = ENAMETOOLONG; 129112823Sfjoe return (NULL); 130112726Sfjoe } 131112823Sfjoe resolved[resolved_len++] = '/'; 132112823Sfjoe resolved[resolved_len] = '\0'; 13323833Speter } 134235266Skib if (next_token[0] == '\0') { 135267161Sjilles /* Handle consequential slashes. */ 136112726Sfjoe continue; 137235266Skib } 138112823Sfjoe else if (strcmp(next_token, ".") == 0) 139112726Sfjoe continue; 140112823Sfjoe else if (strcmp(next_token, "..") == 0) { 141112823Sfjoe /* 142112823Sfjoe * Strip the last path component except when we have 143112823Sfjoe * single "/" 144112823Sfjoe */ 145112726Sfjoe if (resolved_len > 1) { 146112823Sfjoe resolved[resolved_len - 1] = '\0'; 147115362Sfjoe q = strrchr(resolved, '/') + 1; 148112726Sfjoe *q = '\0'; 149112823Sfjoe resolved_len = q - resolved; 15043937Sache } 151112726Sfjoe continue; 15223833Speter } 153112726Sfjoe 154112823Sfjoe /* 155235266Skib * Append the next path component and lstat() it. 156112823Sfjoe */ 157114443Snectar resolved_len = strlcat(resolved, next_token, PATH_MAX); 158112743Sfjoe if (resolved_len >= PATH_MAX) { 159206893Skib if (m) 160206893Skib free(resolved); 161112726Sfjoe errno = ENAMETOOLONG; 162112823Sfjoe return (NULL); 16323833Speter } 164112823Sfjoe if (lstat(resolved, &sb) != 0) { 165236618Sache if (m) 166206893Skib free(resolved); 167112823Sfjoe return (NULL); 168112726Sfjoe } 169112823Sfjoe if (S_ISLNK(sb.st_mode)) { 170112823Sfjoe if (symlinks++ > MAXSYMLINKS) { 171206893Skib if (m) 172206893Skib free(resolved); 173112726Sfjoe errno = ELOOP; 174112823Sfjoe return (NULL); 175112726Sfjoe } 176112823Sfjoe slen = readlink(resolved, symlink, sizeof(symlink) - 1); 177206893Skib if (slen < 0) { 178236618Sache if (m) 179206893Skib free(resolved); 180112823Sfjoe return (NULL); 181206893Skib } 182112726Sfjoe symlink[slen] = '\0'; 183112726Sfjoe if (symlink[0] == '/') { 184112823Sfjoe resolved[1] = 0; 185112726Sfjoe resolved_len = 1; 186112726Sfjoe } else if (resolved_len > 1) { 187112823Sfjoe /* Strip the last path component. */ 188112823Sfjoe resolved[resolved_len - 1] = '\0'; 189115362Sfjoe q = strrchr(resolved, '/') + 1; 190112726Sfjoe *q = '\0'; 191112823Sfjoe resolved_len = q - resolved; 192112726Sfjoe } 193112726Sfjoe 194112823Sfjoe /* 195112823Sfjoe * If there are any path components left, then 196112823Sfjoe * append them to symlink. The result is placed 197112823Sfjoe * in `left'. 198112823Sfjoe */ 199112820Sfjoe if (p != NULL) { 200112820Sfjoe if (symlink[slen - 1] != '/') { 201112823Sfjoe if (slen + 1 >= sizeof(symlink)) { 202206893Skib if (m) 203206893Skib free(resolved); 204112820Sfjoe errno = ENAMETOOLONG; 205112823Sfjoe return (NULL); 206112820Sfjoe } 207112820Sfjoe symlink[slen] = '/'; 208112820Sfjoe symlink[slen + 1] = 0; 209112820Sfjoe } 210227090Sed left_len = strlcat(symlink, left, 211227090Sed sizeof(symlink)); 212112823Sfjoe if (left_len >= sizeof(left)) { 213206893Skib if (m) 214206893Skib free(resolved); 215112726Sfjoe errno = ENAMETOOLONG; 216112823Sfjoe return (NULL); 217112726Sfjoe } 218112726Sfjoe } 219114443Snectar left_len = strlcpy(left, symlink, sizeof(left)); 220267161Sjilles } else if (!S_ISDIR(sb.st_mode) && p != NULL) { 221267161Sjilles if (m) 222267161Sjilles free(resolved); 223267161Sjilles errno = ENOTDIR; 224267161Sjilles return (NULL); 22523833Speter } 22623833Speter } 22723833Speter 228112823Sfjoe /* 229112823Sfjoe * Remove trailing slash except when the resolved pathname 230112823Sfjoe * is a single "/". 231112823Sfjoe */ 232112823Sfjoe if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 233112823Sfjoe resolved[resolved_len - 1] = '\0'; 234112823Sfjoe return (resolved); 23523833Speter} 236