Loading include/client/path.hpp +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ std::pair<bool, std::string> resolve(const std::string& path, bool resolve_last_link = true); std::pair<bool, std::string> resolve_new(const std::string& path, const std::string& mountdir); resolve_new(const std::string& path); [[deprecated( "Use GKFS_USE_LEGACY_PATH_RESOLVE to use old implementation")]] bool Loading src/client/path.cpp +32 −15 Original line number Diff line number Diff line Loading @@ -131,46 +131,63 @@ resolve(const string& path, bool resolve_last_link) { bool is_in_path = resolve(path, resolved, resolve_last_link); return make_pair(is_in_path, resolved); #else return resolve_new(path, CTX->mountdir()); return resolve_new(path); #endif } pair<bool, string> resolve_new(const string& path, const string& mountdir) { LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", path, mountdir); resolve_new(const string& path) { LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", path, CTX->mountdir()); if(path.empty()) { return make_pair(false, "/"); } string resolved = ""; stack<size_t> last_component_pos; const string absolute_path = (path.at(0) == path::separator) ? path : CTX->cwd() + path::separator + path; for(size_t start = 0; start < path.size(); start++) { size_t end = path.find(path::separator, start); for(size_t start = 0; start < absolute_path.size(); start++) { size_t end = absolute_path.find(path::separator, start); // catches the case without separator at the end if(end == string::npos) { end = absolute_path.size(); } 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? if(comp_size == 0 && absolute_path.at(start) == path::separator) { continue; } if(comp_size == 1 && path.at(start) == '.') { if(comp_size == 1 && absolute_path.at(start) == '.') { // component is '.', we skip it continue; } if(comp_size == 2 && path.at(start) == '.' && path.at(start + 1) == '.') { if(comp_size == 2 && absolute_path.at(start) == '.' && absolute_path.at(start + 1) == '.') { // component is '..', we skip it LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", path, mountdir); LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", absolute_path, CTX->mountdir()); if(last_component_pos.empty()) { resolved = "/"; } else { 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); resolved.append(absolute_path, start, comp_size); start = end; #ifdef GKFS_FOLLOW_EXTERNAL_SYMLINKS resolved = follow_symlinks(resolved); #endif } if(resolved.substr(0, mountdir.size()) == mountdir) { if(resolved.substr(0, CTX->mountdir().size()) == CTX->mountdir()) { resolved.erase(1, CTX->mountdir().size()); LOG(DEBUG, "internal: \"{}\"", resolved); return make_pair(true, resolved); Loading tests/unit/test_path.cpp +74 −49 Original line number Diff line number Diff line Loading @@ -28,86 +28,111 @@ #include <catch2/catch.hpp> #include <client/path.hpp> #include <client/preload_context.hpp> #include <client/preload.hpp> // mock CTX class text_context { std::string mntdir = "/tmp/gkfs_mount"; const std::string& mountdir(){ return mntdir; } }; #define CTX text_context #define TEST_PAIR(p, s, s2) \ REQUIRE(p.first == s); \ REQUIRE(p.second == s2); SCENARIO(" resolve fn should handle empty path ", "[test_path][empty]") { SCENARIO(" resolve fn should handle empty path ", "[test_path][empty]") { GIVEN(" a mount path ") { std::string mntpath = "/tmp/gkfs_mount"; CTX->mountdir("/home/foo/tmp/gkfs_mount"); CTX->cwd("/home/foo"); WHEN(" resolve with empty path ") { THEN(" / should be returned ") { REQUIRE(gkfs::path::resolve_new("").second == "/"); THEN("") { TEST_PAIR(gkfs::path::resolve_new("./tmp/gkfs_mount"), true, "/"); TEST_PAIR(gkfs::path::resolve_new(""), false, "/"); TEST_PAIR(gkfs::path::resolve_new("///"), false, "/"); TEST_PAIR(gkfs::path::resolve_new("tmp/../gkfs_mount"), false, "/home/foo/gkfs_mount"); } } } } // 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/./").second == "/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/tmp/../tmp/gkfs_mount/bar/./").second == "/bar/"); CTX->mountdir("/home/foo/tmp/gkfs_mount"); CTX->cwd("/home/foo"); WHEN(" resolve with absolute path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new( "/home/foo/../foo//tmp/./gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/tmp/../tmp/gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/tmp/./gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/tmp/gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/../../home/foo/./tmp/gkfs_mount/"), true, "/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/tmp/./gkfs_mount/bar/./").second == "/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/tmp/gkfs_mount/bar/./").second == "/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../.././tmp/gkfs_mount/").second == "/"); WHEN(" resolve with relative path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new( "./sme/blub/../../tmp/./gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "./tmp/../tmp/gkfs_mount/bar/./"), true, "/bar"); } } } } // 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/../ar/.").second == "/home/bar"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../bar/./").second == "/home/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../bar/../").second == "/home/"); CTX->mountdir("/home/foo/tmp/gkfs_mount"); CTX->cwd("/home/foo"); WHEN(" resolve with absolute path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new("/home/foo/../bar/."), false, "/home/bar"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/../bar/./"), false, "/home/bar"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/../bar/../"), false, "/home"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/../../"), false, "/"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/./bar/../"), false, "/home/foo"); TEST_PAIR(gkfs::path::resolve_new("/home/./../tmp/"), false, "/tmp"); TEST_PAIR(gkfs::path::resolve_new( "/home/./../tmp/gkfs_mount/../"), false, "/tmp"); TEST_PAIR(gkfs::path::resolve_new("/home/random/device"), false, "/home/random/device"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../../").second == "/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/./bar/../").second == "/home/foo/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/./../tmp/").second == "/tmp/"); WHEN(" resolve with relative path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new( "./sme/blub/../tmp/./gkfs_mount/bar/./"), false, "/home/foo/sme/tmp/gkfs_mount/bar"); TEST_PAIR(gkfs::path::resolve_new("./tmp//bar/./"), false, "/home/foo/tmp/bar"); TEST_PAIR(gkfs::path::resolve_new("../../../.."), false, "/"); } } } Loading Loading
include/client/path.hpp +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ std::pair<bool, std::string> resolve(const std::string& path, bool resolve_last_link = true); std::pair<bool, std::string> resolve_new(const std::string& path, const std::string& mountdir); resolve_new(const std::string& path); [[deprecated( "Use GKFS_USE_LEGACY_PATH_RESOLVE to use old implementation")]] bool Loading
src/client/path.cpp +32 −15 Original line number Diff line number Diff line Loading @@ -131,46 +131,63 @@ resolve(const string& path, bool resolve_last_link) { bool is_in_path = resolve(path, resolved, resolve_last_link); return make_pair(is_in_path, resolved); #else return resolve_new(path, CTX->mountdir()); return resolve_new(path); #endif } pair<bool, string> resolve_new(const string& path, const string& mountdir) { LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", path, mountdir); resolve_new(const string& path) { LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", path, CTX->mountdir()); if(path.empty()) { return make_pair(false, "/"); } string resolved = ""; stack<size_t> last_component_pos; const string absolute_path = (path.at(0) == path::separator) ? path : CTX->cwd() + path::separator + path; for(size_t start = 0; start < path.size(); start++) { size_t end = path.find(path::separator, start); for(size_t start = 0; start < absolute_path.size(); start++) { size_t end = absolute_path.find(path::separator, start); // catches the case without separator at the end if(end == string::npos) { end = absolute_path.size(); } 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? if(comp_size == 0 && absolute_path.at(start) == path::separator) { continue; } if(comp_size == 1 && path.at(start) == '.') { if(comp_size == 1 && absolute_path.at(start) == '.') { // component is '.', we skip it continue; } if(comp_size == 2 && path.at(start) == '.' && path.at(start + 1) == '.') { if(comp_size == 2 && absolute_path.at(start) == '.' && absolute_path.at(start + 1) == '.') { // component is '..', we skip it LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", path, mountdir); LOG(DEBUG, "path: \"{}\", mountdir: \"{}\"", absolute_path, CTX->mountdir()); if(last_component_pos.empty()) { resolved = "/"; } else { 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); resolved.append(absolute_path, start, comp_size); start = end; #ifdef GKFS_FOLLOW_EXTERNAL_SYMLINKS resolved = follow_symlinks(resolved); #endif } if(resolved.substr(0, mountdir.size()) == mountdir) { if(resolved.substr(0, CTX->mountdir().size()) == CTX->mountdir()) { resolved.erase(1, CTX->mountdir().size()); LOG(DEBUG, "internal: \"{}\"", resolved); return make_pair(true, resolved); Loading
tests/unit/test_path.cpp +74 −49 Original line number Diff line number Diff line Loading @@ -28,86 +28,111 @@ #include <catch2/catch.hpp> #include <client/path.hpp> #include <client/preload_context.hpp> #include <client/preload.hpp> // mock CTX class text_context { std::string mntdir = "/tmp/gkfs_mount"; const std::string& mountdir(){ return mntdir; } }; #define CTX text_context #define TEST_PAIR(p, s, s2) \ REQUIRE(p.first == s); \ REQUIRE(p.second == s2); SCENARIO(" resolve fn should handle empty path ", "[test_path][empty]") { SCENARIO(" resolve fn should handle empty path ", "[test_path][empty]") { GIVEN(" a mount path ") { std::string mntpath = "/tmp/gkfs_mount"; CTX->mountdir("/home/foo/tmp/gkfs_mount"); CTX->cwd("/home/foo"); WHEN(" resolve with empty path ") { THEN(" / should be returned ") { REQUIRE(gkfs::path::resolve_new("").second == "/"); THEN("") { TEST_PAIR(gkfs::path::resolve_new("./tmp/gkfs_mount"), true, "/"); TEST_PAIR(gkfs::path::resolve_new(""), false, "/"); TEST_PAIR(gkfs::path::resolve_new("///"), false, "/"); TEST_PAIR(gkfs::path::resolve_new("tmp/../gkfs_mount"), false, "/home/foo/gkfs_mount"); } } } } // 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/./").second == "/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/tmp/../tmp/gkfs_mount/bar/./").second == "/bar/"); CTX->mountdir("/home/foo/tmp/gkfs_mount"); CTX->cwd("/home/foo"); WHEN(" resolve with absolute path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new( "/home/foo/../foo//tmp/./gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/tmp/../tmp/gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/tmp/./gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/tmp/gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "/home/foo/../../home/foo/./tmp/gkfs_mount/"), true, "/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/tmp/./gkfs_mount/bar/./").second == "/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/tmp/gkfs_mount/bar/./").second == "/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../.././tmp/gkfs_mount/").second == "/"); WHEN(" resolve with relative path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new( "./sme/blub/../../tmp/./gkfs_mount/bar/./"), true, "/bar"); TEST_PAIR(gkfs::path::resolve_new( "./tmp/../tmp/gkfs_mount/bar/./"), true, "/bar"); } } } } // 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/../ar/.").second == "/home/bar"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../bar/./").second == "/home/bar/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../bar/../").second == "/home/"); CTX->mountdir("/home/foo/tmp/gkfs_mount"); CTX->cwd("/home/foo"); WHEN(" resolve with absolute path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new("/home/foo/../bar/."), false, "/home/bar"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/../bar/./"), false, "/home/bar"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/../bar/../"), false, "/home"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/../../"), false, "/"); TEST_PAIR(gkfs::path::resolve_new("/home/foo/./bar/../"), false, "/home/foo"); TEST_PAIR(gkfs::path::resolve_new("/home/./../tmp/"), false, "/tmp"); TEST_PAIR(gkfs::path::resolve_new( "/home/./../tmp/gkfs_mount/../"), false, "/tmp"); TEST_PAIR(gkfs::path::resolve_new("/home/random/device"), false, "/home/random/device"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/../../").second == "/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/foo/./bar/../").second == "/home/foo/"); } THEN(" iwas ") { REQUIRE(gkfs::path::resolve_new("/home/./../tmp/").second == "/tmp/"); WHEN(" resolve with relative path ") { THEN(" ") { TEST_PAIR(gkfs::path::resolve_new( "./sme/blub/../tmp/./gkfs_mount/bar/./"), false, "/home/foo/sme/tmp/gkfs_mount/bar"); TEST_PAIR(gkfs::path::resolve_new("./tmp//bar/./"), false, "/home/foo/tmp/bar"); TEST_PAIR(gkfs::path::resolve_new("../../../.."), false, "/"); } } } Loading