131de2854Smrg/* $OpenBSD: realpath.c,v 1.14 2011/07/24 21:03:00 miod Exp $ */ 25dfecf96Smrg/* 331de2854Smrg * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 45dfecf96Smrg * 55dfecf96Smrg * Redistribution and use in source and binary forms, with or without 65dfecf96Smrg * modification, are permitted provided that the following conditions 75dfecf96Smrg * are met: 85dfecf96Smrg * 1. Redistributions of source code must retain the above copyright 95dfecf96Smrg * notice, this list of conditions and the following disclaimer. 105dfecf96Smrg * 2. Redistributions in binary form must reproduce the above copyright 115dfecf96Smrg * notice, this list of conditions and the following disclaimer in the 125dfecf96Smrg * documentation and/or other materials provided with the distribution. 1331de2854Smrg * 3. The names of the authors may not be used to endorse or promote 1431de2854Smrg * products derived from this software without specific prior written 1531de2854Smrg * permission. 165dfecf96Smrg * 1731de2854Smrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 185dfecf96Smrg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 195dfecf96Smrg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2031de2854Smrg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 215dfecf96Smrg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 225dfecf96Smrg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 235dfecf96Smrg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 245dfecf96Smrg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 255dfecf96Smrg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 265dfecf96Smrg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 275dfecf96Smrg * SUCH DAMAGE. 285dfecf96Smrg */ 295dfecf96Smrg 305dfecf96Smrg#include <sys/param.h> 315dfecf96Smrg#include <sys/stat.h> 325dfecf96Smrg 335dfecf96Smrg#include <errno.h> 345dfecf96Smrg#include <stdlib.h> 355dfecf96Smrg#include <string.h> 365dfecf96Smrg#include <unistd.h> 375dfecf96Smrg 385dfecf96Smrg/* 3931de2854Smrg * char *realpath(const char *path, char resolved[PATH_MAX]); 405dfecf96Smrg * 415dfecf96Smrg * Find the real name of path, by removing all ".", ".." and symlink 425dfecf96Smrg * components. Returns (resolved) on success, or (NULL) on failure, 435dfecf96Smrg * in which case the path which caused trouble is left in (resolved). 445dfecf96Smrg */ 455dfecf96Smrgchar * 4631de2854Smrgrealpath(const char *path, char *resolved) 475dfecf96Smrg{ 485dfecf96Smrg struct stat sb; 4931de2854Smrg char *p, *q, *s; 5031de2854Smrg size_t left_len, resolved_len; 5131de2854Smrg unsigned symlinks; 5231de2854Smrg int serrno, slen, mem_allocated; 5331de2854Smrg char left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX]; 5431de2854Smrg 5531de2854Smrg if (path[0] == '\0') { 5631de2854Smrg errno = ENOENT; 575dfecf96Smrg return (NULL); 585dfecf96Smrg } 595dfecf96Smrg 6031de2854Smrg serrno = errno; 615dfecf96Smrg 6231de2854Smrg if (resolved == NULL) { 6331de2854Smrg resolved = malloc(PATH_MAX); 6431de2854Smrg if (resolved == NULL) 6531de2854Smrg return (NULL); 6631de2854Smrg mem_allocated = 1; 6731de2854Smrg } else 6831de2854Smrg mem_allocated = 0; 6931de2854Smrg 7031de2854Smrg symlinks = 0; 7131de2854Smrg if (path[0] == '/') { 7231de2854Smrg resolved[0] = '/'; 7331de2854Smrg resolved[1] = '\0'; 7431de2854Smrg if (path[1] == '\0') 7531de2854Smrg return (resolved); 7631de2854Smrg resolved_len = 1; 7731de2854Smrg left_len = strlcpy(left, path + 1, sizeof(left)); 7831de2854Smrg } else { 7931de2854Smrg if (getcwd(resolved, PATH_MAX) == NULL) { 8031de2854Smrg if (mem_allocated) 8131de2854Smrg free(resolved); 8231de2854Smrg else 8331de2854Smrg strlcpy(resolved, ".", PATH_MAX); 8431de2854Smrg return (NULL); 855dfecf96Smrg } 8631de2854Smrg resolved_len = strlen(resolved); 8731de2854Smrg left_len = strlcpy(left, path, sizeof(left)); 8831de2854Smrg } 8931de2854Smrg if (left_len >= sizeof(left) || resolved_len >= PATH_MAX) { 9031de2854Smrg errno = ENAMETOOLONG; 9131de2854Smrg goto err; 925dfecf96Smrg } 935dfecf96Smrg 945dfecf96Smrg /* 9531de2854Smrg * Iterate over path components in `left'. 965dfecf96Smrg */ 9731de2854Smrg while (left_len != 0) { 9831de2854Smrg /* 9931de2854Smrg * Extract the next path component and adjust `left' 10031de2854Smrg * and its length. 10131de2854Smrg */ 10231de2854Smrg p = strchr(left, '/'); 10331de2854Smrg s = p ? p : left + left_len; 10431de2854Smrg if (s - left >= sizeof(next_token)) { 1055dfecf96Smrg errno = ENAMETOOLONG; 10631de2854Smrg goto err; 10731de2854Smrg } 10831de2854Smrg memcpy(next_token, left, s - left); 10931de2854Smrg next_token[s - left] = '\0'; 11031de2854Smrg left_len -= s - left; 11131de2854Smrg if (p != NULL) 11231de2854Smrg memmove(left, s + 1, left_len + 1); 11331de2854Smrg if (resolved[resolved_len - 1] != '/') { 11431de2854Smrg if (resolved_len + 1 >= PATH_MAX) { 11531de2854Smrg errno = ENAMETOOLONG; 11631de2854Smrg goto err; 11731de2854Smrg } 11831de2854Smrg resolved[resolved_len++] = '/'; 11931de2854Smrg resolved[resolved_len] = '\0'; 12031de2854Smrg } 12131de2854Smrg if (next_token[0] == '\0') 12231de2854Smrg continue; 12331de2854Smrg else if (strcmp(next_token, ".") == 0) 12431de2854Smrg continue; 12531de2854Smrg else if (strcmp(next_token, "..") == 0) { 12631de2854Smrg /* 12731de2854Smrg * Strip the last path component except when we have 12831de2854Smrg * single "/" 12931de2854Smrg */ 13031de2854Smrg if (resolved_len > 1) { 13131de2854Smrg resolved[resolved_len - 1] = '\0'; 13231de2854Smrg q = strrchr(resolved, '/') + 1; 13331de2854Smrg *q = '\0'; 13431de2854Smrg resolved_len = q - resolved; 13531de2854Smrg } 13631de2854Smrg continue; 1375dfecf96Smrg } 1385dfecf96Smrg 13931de2854Smrg /* 14031de2854Smrg * Append the next path component and lstat() it. If 14131de2854Smrg * lstat() fails we still can return successfully if 14231de2854Smrg * there are no more path components left. 14331de2854Smrg */ 14431de2854Smrg resolved_len = strlcat(resolved, next_token, PATH_MAX); 14531de2854Smrg if (resolved_len >= PATH_MAX) { 14631de2854Smrg errno = ENAMETOOLONG; 14731de2854Smrg goto err; 14831de2854Smrg } 14931de2854Smrg if (lstat(resolved, &sb) != 0) { 15031de2854Smrg if (errno == ENOENT && p == NULL) { 15131de2854Smrg errno = serrno; 15231de2854Smrg return (resolved); 15331de2854Smrg } 15431de2854Smrg goto err; 15531de2854Smrg } 15631de2854Smrg if (S_ISLNK(sb.st_mode)) { 15731de2854Smrg if (symlinks++ > MAXSYMLINKS) { 15831de2854Smrg errno = ELOOP; 15931de2854Smrg goto err; 16031de2854Smrg } 16131de2854Smrg slen = readlink(resolved, symlink, sizeof(symlink) - 1); 16231de2854Smrg if (slen < 0) 16331de2854Smrg goto err; 16431de2854Smrg symlink[slen] = '\0'; 16531de2854Smrg if (symlink[0] == '/') { 16631de2854Smrg resolved[1] = 0; 16731de2854Smrg resolved_len = 1; 16831de2854Smrg } else if (resolved_len > 1) { 16931de2854Smrg /* Strip the last path component. */ 17031de2854Smrg resolved[resolved_len - 1] = '\0'; 17131de2854Smrg q = strrchr(resolved, '/') + 1; 17231de2854Smrg *q = '\0'; 17331de2854Smrg resolved_len = q - resolved; 17431de2854Smrg } 17531de2854Smrg 17631de2854Smrg /* 17731de2854Smrg * If there are any path components left, then 17831de2854Smrg * append them to symlink. The result is placed 17931de2854Smrg * in `left'. 18031de2854Smrg */ 18131de2854Smrg if (p != NULL) { 18231de2854Smrg if (symlink[slen - 1] != '/') { 18331de2854Smrg if (slen + 1 >= sizeof(symlink)) { 18431de2854Smrg errno = ENAMETOOLONG; 18531de2854Smrg goto err; 18631de2854Smrg } 18731de2854Smrg symlink[slen] = '/'; 18831de2854Smrg symlink[slen + 1] = 0; 18931de2854Smrg } 19031de2854Smrg left_len = strlcat(symlink, left, sizeof(left)); 19131de2854Smrg if (left_len >= sizeof(left)) { 19231de2854Smrg errno = ENAMETOOLONG; 19331de2854Smrg goto err; 19431de2854Smrg } 19531de2854Smrg } 19631de2854Smrg left_len = strlcpy(left, symlink, sizeof(left)); 19731de2854Smrg } 1985dfecf96Smrg } 1995dfecf96Smrg 20031de2854Smrg /* 20131de2854Smrg * Remove trailing slash except when the resolved pathname 20231de2854Smrg * is a single "/". 20331de2854Smrg */ 20431de2854Smrg if (resolved_len > 1 && resolved[resolved_len - 1] == '/') 20531de2854Smrg resolved[resolved_len - 1] = '\0'; 2065dfecf96Smrg return (resolved); 2075dfecf96Smrg 20831de2854Smrgerr: 20931de2854Smrg if (mem_allocated) 21031de2854Smrg free(resolved); 2115dfecf96Smrg return (NULL); 2125dfecf96Smrg} 213