1157016Sdes/* $OpenBSD: realpath.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ 298937Sdes/* 3149749Sdes * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 498937Sdes * 598937Sdes * Redistribution and use in source and binary forms, with or without 698937Sdes * modification, are permitted provided that the following conditions 798937Sdes * are met: 898937Sdes * 1. Redistributions of source code must retain the above copyright 998937Sdes * notice, this list of conditions and the following disclaimer. 1098937Sdes * 2. Redistributions in binary form must reproduce the above copyright 1198937Sdes * notice, this list of conditions and the following disclaimer in the 1298937Sdes * documentation and/or other materials provided with the distribution. 13149749Sdes * 3. The names of the authors may not be used to endorse or promote 14149749Sdes * products derived from this software without specific prior written 15149749Sdes * permission. 1698937Sdes * 17149749Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1898937Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1998937Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20149749Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2198937Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2298937Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2398937Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2498937Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2598937Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2698937Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2798937Sdes * SUCH DAMAGE. 2898937Sdes */ 2998937Sdes 30157016Sdes/* OPENBSD ORIGINAL: lib/libc/stdlib/realpath.c */ 31157016Sdes 3298937Sdes#include "includes.h" 3398937Sdes 3498937Sdes#if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) 3598937Sdes 3698937Sdes#include <sys/param.h> 3798937Sdes#include <sys/stat.h> 3898937Sdes 3998937Sdes#include <errno.h> 4098937Sdes#include <stdlib.h> 4198937Sdes#include <string.h> 4298937Sdes#include <unistd.h> 4398937Sdes 4498937Sdes/* 45149749Sdes * char *realpath(const char *path, char resolved[PATH_MAX]); 4698937Sdes * 4798937Sdes * Find the real name of path, by removing all ".", ".." and symlink 4898937Sdes * components. Returns (resolved) on success, or (NULL) on failure, 4998937Sdes * in which case the path which caused trouble is left in (resolved). 5098937Sdes */ 5198937Sdeschar * 52149749Sdesrealpath(const char *path, char resolved[PATH_MAX]) 5398937Sdes{ 5498937Sdes struct stat sb; 55149749Sdes char *p, *q, *s; 56149749Sdes size_t left_len, resolved_len; 57149749Sdes unsigned symlinks; 58149749Sdes int serrno, slen; 59149749Sdes char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 6098937Sdes 61149749Sdes serrno = errno; 62149749Sdes symlinks = 0; 63149749Sdes if (path[0] == '/') { 64149749Sdes resolved[0] = '/'; 65146998Sdes resolved[1] = '\0'; 66149749Sdes if (path[1] == '\0') 67149749Sdes return (resolved); 68149749Sdes resolved_len = 1; 69149749Sdes left_len = strlcpy(left, path + 1, sizeof(left)); 70149749Sdes } else { 71149749Sdes if (getcwd(resolved, PATH_MAX) == NULL) { 72149749Sdes strlcpy(resolved, ".", PATH_MAX); 73149749Sdes return (NULL); 74149749Sdes } 75149749Sdes resolved_len = strlen(resolved); 76149749Sdes left_len = strlcpy(left, path, sizeof(left)); 77146998Sdes } 78149749Sdes if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { 79149749Sdes errno = ENAMETOOLONG; 8098937Sdes return (NULL); 8198937Sdes } 8298937Sdes 8398937Sdes /* 84149749Sdes * Iterate over path components in `left'. 8598937Sdes */ 86149749Sdes while (left_len != 0) { 87149749Sdes /* 88149749Sdes * Extract the next path component and adjust `left' 89149749Sdes * and its length. 90149749Sdes */ 91149749Sdes p = strchr(left, '/'); 92149749Sdes s = p ? p : left + left_len; 93149749Sdes if (s - left >= sizeof(next_token)) { 94149749Sdes errno = ENAMETOOLONG; 95149749Sdes return (NULL); 9698937Sdes } 97149749Sdes memcpy(next_token, left, s - left); 98149749Sdes next_token[s - left] = '\0'; 99149749Sdes left_len -= s - left; 100149749Sdes if (p != NULL) 101149749Sdes memmove(left, s + 1, left_len + 1); 102149749Sdes if (resolved[resolved_len - 1] != '/') { 103149749Sdes if (resolved_len + 1 >= PATH_MAX) { 104149749Sdes errno = ENAMETOOLONG; 105149749Sdes return (NULL); 10698937Sdes } 107149749Sdes resolved[resolved_len++] = '/'; 108149749Sdes resolved[resolved_len] = '\0'; 10998937Sdes } 110149749Sdes if (next_token[0] == '\0') 111149749Sdes continue; 112149749Sdes else if (strcmp(next_token, ".") == 0) 113149749Sdes continue; 114149749Sdes else if (strcmp(next_token, "..") == 0) { 115149749Sdes /* 116149749Sdes * Strip the last path component except when we have 117149749Sdes * single "/" 118149749Sdes */ 119149749Sdes if (resolved_len > 1) { 120149749Sdes resolved[resolved_len - 1] = '\0'; 121149749Sdes q = strrchr(resolved, '/') + 1; 122149749Sdes *q = '\0'; 123149749Sdes resolved_len = q - resolved; 124149749Sdes } 125149749Sdes continue; 12698937Sdes } 12798937Sdes 128149749Sdes /* 129149749Sdes * Append the next path component and lstat() it. If 130149749Sdes * lstat() fails we still can return successfully if 131149749Sdes * there are no more path components left. 132149749Sdes */ 133149749Sdes resolved_len = strlcat(resolved, next_token, PATH_MAX); 134149749Sdes if (resolved_len >= PATH_MAX) { 135146998Sdes errno = ENAMETOOLONG; 136149749Sdes return (NULL); 13798937Sdes } 138149749Sdes if (lstat(resolved, &sb) != 0) { 139149749Sdes if (errno == ENOENT && p == NULL) { 140149749Sdes errno = serrno; 141149749Sdes return (resolved); 142146998Sdes } 143149749Sdes return (NULL); 144146998Sdes } 145149749Sdes if (S_ISLNK(sb.st_mode)) { 146149749Sdes if (symlinks++ > MAXSYMLINKS) { 147149749Sdes errno = ELOOP; 148149749Sdes return (NULL); 149149749Sdes } 150149749Sdes slen = readlink(resolved, symlink, sizeof(symlink) - 1); 151149749Sdes if (slen < 0) 152149749Sdes return (NULL); 153149749Sdes symlink[slen] = '\0'; 154149749Sdes if (symlink[0] == '/') { 155149749Sdes resolved[1] = 0; 156149749Sdes resolved_len = 1; 157149749Sdes } else if (resolved_len > 1) { 158149749Sdes /* Strip the last path component. */ 159149749Sdes resolved[resolved_len - 1] = '\0'; 160149749Sdes q = strrchr(resolved, '/') + 1; 161149749Sdes *q = '\0'; 162149749Sdes resolved_len = q - resolved; 163149749Sdes } 164149749Sdes 165149749Sdes /* 166149749Sdes * If there are any path components left, then 167149749Sdes * append them to symlink. The result is placed 168149749Sdes * in `left'. 169149749Sdes */ 170149749Sdes if (p != NULL) { 171149749Sdes if (symlink[slen - 1] != '/') { 172149749Sdes if (slen + 1 >= sizeof(symlink)) { 173149749Sdes errno = ENAMETOOLONG; 174149749Sdes return (NULL); 175149749Sdes } 176149749Sdes symlink[slen] = '/'; 177149749Sdes symlink[slen + 1] = 0; 178149749Sdes } 179149749Sdes left_len = strlcat(symlink, left, sizeof(left)); 180149749Sdes if (left_len >= sizeof(left)) { 181149749Sdes errno = ENAMETOOLONG; 182149749Sdes return (NULL); 183149749Sdes } 184149749Sdes } 185149749Sdes left_len = strlcpy(left, symlink, sizeof(left)); 186146998Sdes } 18798937Sdes } 18898937Sdes 189149749Sdes /* 190149749Sdes * Remove trailing slash except when the resolved pathname 191149749Sdes * is a single "/". 192149749Sdes */ 193149749Sdes if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 194149749Sdes resolved[resolved_len - 1] = '\0'; 19598937Sdes return (resolved); 19698937Sdes} 19798937Sdes#endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */ 198