Commit c009d4b0 authored by Julius Athenstaedt's avatar Julius Athenstaedt Committed by Marc Vef
Browse files

refactor resolve path

parent cddedd6f
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -36,6 +36,9 @@ unsigned int
match_components(const std::string& path, unsigned int& path_components,
                 const std::vector<std::string>& components);

std::pair<bool, std::string>
resolve_new(const std::string& path, std::string mountdir);

bool
resolve(const std::string& path, std::string& resolved,
        bool resolve_last_link = true);
+74 −1
Original line number Diff line number Diff line
@@ -34,6 +34,8 @@

#include <common/path_util.hpp>

#include <stack>
#include <utility>
#include <vector>
#include <string>
#include <cassert>
@@ -100,6 +102,77 @@ match_components(const string& path, unsigned int& path_components,
    return matched;
}

string
follow_symlinks(const string& path) {
    struct stat st {};
    if(lstat(path.c_str(), &st) < 0) {
        LOG(DEBUG, "path \"{}\" does not exist", path);
        return path;
    }
    if(S_ISLNK(st.st_mode)) {
        auto link_resolved = ::unique_ptr<char[]>(new char[PATH_MAX]);
        if(realpath(path.c_str(), link_resolved.get()) == nullptr) {

            LOG(ERROR,
                "Failed to get realpath for link \"{}\". "
                "Error: {}",
                path, ::strerror(errno));
            return path;
        }
        // substituute resolved with new link path
        return link_resolved.get();
    }
    return path;
}

pair<bool, string>
resolve_new(const string& path, string mountdir) {
    LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", path, mountdir);
    string resolved = "";
    stack<size_t> last_component_pos;

    for(size_t start = 0; start < path.size(); start++) {
        size_t end = path.find(path::separator, start);
        size_t comp_size = end - start;
        if(comp_size == 1 && path.at(start) == path::separator) {
            // should I use same while loop as in the original here?
            continue;
        }
        if(comp_size == 1 && path.at(start) == '.') {
            // component is '.', we skip it
            continue;
        }
        if(comp_size == 2 && path.at(start) == '.' &&
           path.at(start + 1) == '.') {
            // component is '..', we skip it
            assert(!last_component_pos.empty());
            resolved.erase(last_component_pos.top());
            last_component_pos.pop();
            continue;
        }
        // add `/<component>` to the reresolved path
        resolved.push_back(path::separator);
        last_component_pos.push(resolved.size() - 1);
        resolved.append(path, start, comp_size);

#ifdef GKFS_FOLLOW_SYMLINKS // HAS_SYMLINKS ???
        resolved = follow_symlinks(resolved);
#endif
    }

    if(resolved.substr(0, mountdir.size()) == mountdir) {
        resolved.erase(1, CTX->mountdir().size());
        LOG(DEBUG, "internal: \"{}\"", resolved);
        return make_pair(true, resolved);
    }

    if(resolved.empty()) {
        resolved.push_back(path::separator);
    }
    LOG(DEBUG, "external: \"{}\"", resolved);
    return make_pair(false, resolved);
}

/** Resolve path to its canonical representation
 *
 * Populate `resolved` with the canonical representation of `path`.
+102 −0
Original line number Diff line number Diff line
/*
  Copyright 2018-2024, Barcelona Supercomputing Center (BSC), Spain
  Copyright 2015-2024, Johannes Gutenberg Universitaet Mainz, Germany

  This software was partially supported by the
  EC H2020 funded project NEXTGenIO (Project ID: 671951, www.nextgenio.eu).

  This software was partially supported by the
  ADA-FS project under the SPPEXA project funded by the DFG.

  This file is part of GekkoFS.

  GekkoFS is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  GekkoFS is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with GekkoFS.  If not, see <https://www.gnu.org/licenses/>.

  SPDX-License-Identifier: GPL-3.0-or-later
*/

#include <catch2/catch.hpp>
#include <client/path.hpp>
#include <utility>

SCENARIO(" resolve fn should handle empty path ",
         "[test_path][empty]") {

    GIVEN(" a mount path ") {

		    std::string mntpath = "/tmp/gkfs_mount";

        WHEN(" resolve with empty path ") {
            THEN(" / should be returned ") {
                REQUIRE(gkfs::path::resolve_new("", mntpath).second ==  "/");
            }
        }
    }
}

// TODO check pair.first is true
SCENARIO(" resolve fn should handle internal paths ",
         "[test_path][external paths]") {

    GIVEN(" a mount path ") {

		    std::string mntpath = "/tmp/gkfs_mount";

        WHEN(" resolve with relative path ") {
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/home/foo/../../tmp/./gkfs_mount/bar/./", mntpath).second ==  "/bar/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/tmp/../tmp/gkfs_mount/bar/./", mntpath).second ==  "/bar/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/tmp/./gkfs_mount/bar/./", mntpath).second ==  "/bar/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/tmp/gkfs_mount/bar/./", mntpath).second ==  "/bar/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/home/foo/../.././tmp/gkfs_mount/", mntpath).second ==  "/");
            }
        }
    }
}

// TODO check pair.first is false
SCENARIO(" resolve fn should handle external paths ",
         "[test_path][external paths]") {

    GIVEN(" a mount path ") {

		    std::string mntpath = "/tmp/gkfs_mount";

        WHEN(" resolve with relative path ") {
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/home/foo/../bar/./", mntpath).second ==  "/home/bar/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/home/foo/../bar/../", mntpath).second ==  "/home/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/home/foo/../../", mntpath).second ==  "/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/home/foo/./bar/../", mntpath).second ==  "/home/foo/");
            }
            THEN(" iwas ") {
                REQUIRE(gkfs::path::resolve_new("/home/./../tmp/", mntpath).second ==  "/tmp/");
            }
        }
    }
}