1296853Sdes/* $OpenBSD: realpath.c,v 1.20 2015/10/13 20:55:37 millert 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 36295367Sdes#include <sys/types.h> 3798937Sdes#include <sys/param.h> 3898937Sdes#include <sys/stat.h> 3998937Sdes 4098937Sdes#include <errno.h> 4198937Sdes#include <stdlib.h> 42295367Sdes#include <stddef.h> 4398937Sdes#include <string.h> 4498937Sdes#include <unistd.h> 45296853Sdes#include <limits.h> 4698937Sdes 47296853Sdes#ifndef SYMLOOP_MAX 48296853Sdes# define SYMLOOP_MAX 32 49296853Sdes#endif 50296853Sdes 51296853Sdes/* A slightly modified copy of this file exists in libexec/ld.so */ 52296853Sdes 5398937Sdes/* 54149749Sdes * char *realpath(const char *path, char resolved[PATH_MAX]); 5598937Sdes * 5698937Sdes * Find the real name of path, by removing all ".", ".." and symlink 5798937Sdes * components. Returns (resolved) on success, or (NULL) on failure, 5898937Sdes * in which case the path which caused trouble is left in (resolved). 5998937Sdes */ 6098937Sdeschar * 61296853Sdesrealpath(const char *path, char *resolved) 6298937Sdes{ 6398937Sdes struct stat sb; 64149749Sdes char *p, *q, *s; 65149749Sdes size_t left_len, resolved_len; 66149749Sdes unsigned symlinks; 67296853Sdes int serrno, slen, mem_allocated; 68149749Sdes char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 6998937Sdes 70296853Sdes if (path[0] == '\0') { 71296853Sdes errno = ENOENT; 72296853Sdes return (NULL); 73296853Sdes } 74296853Sdes 75149749Sdes serrno = errno; 76296853Sdes 77296853Sdes if (resolved == NULL) { 78296853Sdes resolved = malloc(PATH_MAX); 79296853Sdes if (resolved == NULL) 80296853Sdes return (NULL); 81296853Sdes mem_allocated = 1; 82296853Sdes } else 83296853Sdes mem_allocated = 0; 84296853Sdes 85149749Sdes symlinks = 0; 86149749Sdes if (path[0] == '/') { 87149749Sdes resolved[0] = '/'; 88146998Sdes resolved[1] = '\0'; 89149749Sdes if (path[1] == '\0') 90149749Sdes return (resolved); 91149749Sdes resolved_len = 1; 92149749Sdes left_len = strlcpy(left, path + 1, sizeof(left)); 93149749Sdes } else { 94149749Sdes if (getcwd(resolved, PATH_MAX) == NULL) { 95296853Sdes if (mem_allocated) 96296853Sdes free(resolved); 97296853Sdes else 98296853Sdes strlcpy(resolved, ".", PATH_MAX); 99149749Sdes return (NULL); 100149749Sdes } 101149749Sdes resolved_len = strlen(resolved); 102149749Sdes left_len = strlcpy(left, path, sizeof(left)); 103146998Sdes } 104149749Sdes if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { 105149749Sdes errno = ENAMETOOLONG; 106296853Sdes goto err; 10798937Sdes } 10898937Sdes 10998937Sdes /* 110149749Sdes * Iterate over path components in `left'. 11198937Sdes */ 112149749Sdes while (left_len != 0) { 113149749Sdes /* 114149749Sdes * Extract the next path component and adjust `left' 115149749Sdes * and its length. 116149749Sdes */ 117149749Sdes p = strchr(left, '/'); 118149749Sdes s = p ? p : left + left_len; 119295367Sdes if (s - left >= (ptrdiff_t)sizeof(next_token)) { 120149749Sdes errno = ENAMETOOLONG; 121296853Sdes goto err; 12298937Sdes } 123149749Sdes memcpy(next_token, left, s - left); 124149749Sdes next_token[s - left] = '\0'; 125149749Sdes left_len -= s - left; 126149749Sdes if (p != NULL) 127149749Sdes memmove(left, s + 1, left_len + 1); 128149749Sdes if (resolved[resolved_len - 1] != '/') { 129149749Sdes if (resolved_len + 1 >= PATH_MAX) { 130149749Sdes errno = ENAMETOOLONG; 131296853Sdes goto err; 13298937Sdes } 133149749Sdes resolved[resolved_len++] = '/'; 134149749Sdes resolved[resolved_len] = '\0'; 13598937Sdes } 136149749Sdes if (next_token[0] == '\0') 137149749Sdes continue; 138149749Sdes else if (strcmp(next_token, ".") == 0) 139149749Sdes continue; 140149749Sdes else if (strcmp(next_token, "..") == 0) { 141149749Sdes /* 142149749Sdes * Strip the last path component except when we have 143149749Sdes * single "/" 144149749Sdes */ 145149749Sdes if (resolved_len > 1) { 146149749Sdes resolved[resolved_len - 1] = '\0'; 147149749Sdes q = strrchr(resolved, '/') + 1; 148149749Sdes *q = '\0'; 149149749Sdes resolved_len = q - resolved; 150149749Sdes } 151149749Sdes continue; 15298937Sdes } 15398937Sdes 154149749Sdes /* 155149749Sdes * Append the next path component and lstat() it. If 156149749Sdes * lstat() fails we still can return successfully if 157149749Sdes * there are no more path components left. 158149749Sdes */ 159149749Sdes resolved_len = strlcat(resolved, next_token, PATH_MAX); 160149749Sdes if (resolved_len >= PATH_MAX) { 161146998Sdes errno = ENAMETOOLONG; 162296853Sdes goto err; 16398937Sdes } 164149749Sdes if (lstat(resolved, &sb) != 0) { 165149749Sdes if (errno == ENOENT && p == NULL) { 166149749Sdes errno = serrno; 167149749Sdes return (resolved); 168146998Sdes } 169296853Sdes goto err; 170146998Sdes } 171149749Sdes if (S_ISLNK(sb.st_mode)) { 172296853Sdes if (symlinks++ > SYMLOOP_MAX) { 173149749Sdes errno = ELOOP; 174296853Sdes goto err; 175149749Sdes } 176149749Sdes slen = readlink(resolved, symlink, sizeof(symlink) - 1); 177149749Sdes if (slen < 0) 178296853Sdes goto err; 179149749Sdes symlink[slen] = '\0'; 180149749Sdes if (symlink[0] == '/') { 181149749Sdes resolved[1] = 0; 182149749Sdes resolved_len = 1; 183149749Sdes } else if (resolved_len > 1) { 184149749Sdes /* Strip the last path component. */ 185149749Sdes resolved[resolved_len - 1] = '\0'; 186149749Sdes q = strrchr(resolved, '/') + 1; 187149749Sdes *q = '\0'; 188149749Sdes resolved_len = q - resolved; 189149749Sdes } 190149749Sdes 191149749Sdes /* 192149749Sdes * If there are any path components left, then 193149749Sdes * append them to symlink. The result is placed 194149749Sdes * in `left'. 195149749Sdes */ 196149749Sdes if (p != NULL) { 197149749Sdes if (symlink[slen - 1] != '/') { 198295367Sdes if (slen + 1 >= 199295367Sdes (ptrdiff_t)sizeof(symlink)) { 200149749Sdes errno = ENAMETOOLONG; 201296853Sdes goto err; 202149749Sdes } 203149749Sdes symlink[slen] = '/'; 204149749Sdes symlink[slen + 1] = 0; 205149749Sdes } 206296853Sdes left_len = strlcat(symlink, left, sizeof(symlink)); 207296853Sdes if (left_len >= sizeof(symlink)) { 208149749Sdes errno = ENAMETOOLONG; 209296853Sdes goto err; 210149749Sdes } 211149749Sdes } 212149749Sdes left_len = strlcpy(left, symlink, sizeof(left)); 213146998Sdes } 21498937Sdes } 21598937Sdes 216149749Sdes /* 217149749Sdes * Remove trailing slash except when the resolved pathname 218149749Sdes * is a single "/". 219149749Sdes */ 220149749Sdes if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 221149749Sdes resolved[resolved_len - 1] = '\0'; 22298937Sdes return (resolved); 223296853Sdes 224296853Sdeserr: 225296853Sdes if (mem_allocated) 226296853Sdes free(resolved); 227296853Sdes return (NULL); 22898937Sdes} 22998937Sdes#endif /* !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) */ 230