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