195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes/* 295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru> 395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * 495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * Redistribution and use in source and binary forms, with or without 595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * modification, are permitted provided that the following conditions 695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * are met: 795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * 1. Redistributions of source code must retain the above copyright 895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * notice, this list of conditions and the following disclaimer. 995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * 2. Redistributions in binary form must reproduce the above copyright 1095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * notice, this list of conditions and the following disclaimer in the 1195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * documentation and/or other materials provided with the distribution. 1295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * 3. The names of the authors may not be used to endorse or promote 1395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * products derived from this software without specific prior written 1495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * permission. 1595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * 1695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * SUCH DAMAGE. 2795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes */ 2895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 2995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes#include "readlink.h" 3095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 3195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes#include <string> 3295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 3395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes#include <errno.h> 3495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes#include <sys/param.h> 3595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes#include <sys/stat.h> 3695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes#include <unistd.h> 3795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 3895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes/** 3995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * This differs from realpath(3) mainly in its behavior when a path element does not exist or can 4095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * not be searched. realpath(3) treats that as an error and gives up, but we have Java-compatible 4195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * behavior where we just assume the path element was not a symbolic link. This leads to a textual 4295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * treatment of ".." from that point in the path, which may actually lead us back to a path we 4395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * can resolve (as in "/tmp/does-not-exist/../blah.txt" which would be an error for realpath(3) 4495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * but "/tmp/blah.txt" under the traditional Java interpretation). 4595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * 4695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes * This implementation also removes all the fixed-length buffers of the C original. 4795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes */ 4895101d3d5a3417755c88fded1600e039fb363019Elliott Hughesbool realpath(const char* path, std::string& resolved) { 4995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // 'path' must be an absolute path. 5095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (path[0] != '/') { 5195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes errno = EINVAL; 5295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes return false; 5395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 5495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 5595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes resolved = "/"; 5695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (path[1] == '\0') { 5795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes return true; 5895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 5995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 6095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // Iterate over path components in 'left'. 6195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes int symlinkCount = 0; 6295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes std::string left(path + 1); 6395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes while (!left.empty()) { 6495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // Extract the next path component. 6595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes size_t nextSlash = left.find('/'); 6695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes std::string nextPathComponent = left.substr(0, nextSlash); 6795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (nextSlash != std::string::npos) { 6895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes left.erase(0, nextSlash + 1); 6995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } else { 7095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes left.clear(); 7195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 7295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (nextPathComponent.empty()) { 7395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes continue; 7495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } else if (nextPathComponent == ".") { 7595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes continue; 7695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } else if (nextPathComponent == "..") { 7795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // Strip the last path component except when we have single "/". 7895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (resolved.size() > 1) { 7995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes resolved.erase(resolved.rfind('/')); 8095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 8195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes continue; 8295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 8395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 8495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // Append the next path component. 8595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (resolved[resolved.size() - 1] != '/') { 8695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes resolved += '/'; 8795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 8895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes resolved += nextPathComponent; 8995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 9095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // See if we've got a symbolic link, and resolve it if so. 9195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes struct stat sb; 9295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (lstat(resolved.c_str(), &sb) == 0 && S_ISLNK(sb.st_mode)) { 9395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (symlinkCount++ > MAXSYMLINKS) { 9495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes errno = ELOOP; 9595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes return false; 9695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 9795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 9895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes std::string symlink; 9995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (!readlink(resolved.c_str(), symlink)) { 10095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes return false; 10195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 10295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (symlink[0] == '/') { 10395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // The symbolic link is absolute, so we need to start from scratch. 10495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes resolved = "/"; 10595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } else if (resolved.size() > 1) { 10695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // The symbolic link is relative, so we just lose the last path component (which 10795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // was the link). 10895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes resolved.erase(resolved.rfind('/')); 10995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 11095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 11195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (!left.empty()) { 11295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes const char* maybeSlash = (symlink[symlink.size() - 1] != '/') ? "/" : ""; 11395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes left = symlink + maybeSlash + left; 11495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } else { 11595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes left = symlink; 11695101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 11795101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 11895101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 11995101d3d5a3417755c88fded1600e039fb363019Elliott Hughes 12095101d3d5a3417755c88fded1600e039fb363019Elliott Hughes // Remove trailing slash except when the resolved pathname is a single "/". 12195101d3d5a3417755c88fded1600e039fb363019Elliott Hughes if (resolved.size() > 1 && resolved[resolved.size() - 1] == '/') { 12295101d3d5a3417755c88fded1600e039fb363019Elliott Hughes resolved.erase(resolved.size() - 1, 1); 12395101d3d5a3417755c88fded1600e039fb363019Elliott Hughes } 12495101d3d5a3417755c88fded1600e039fb363019Elliott Hughes return true; 12595101d3d5a3417755c88fded1600e039fb363019Elliott Hughes} 126