Loading include/client/path.hpp +11 −1 Original line number Diff line number Diff line Loading @@ -17,12 +17,22 @@ namespace gkfs { namespace path { enum class NormalizeStatus { ok, fd_unknown, fd_not_a_dir }; unsigned int match_components(const std::string& path, unsigned int& path_components, const std::vector<std::string>& components); bool resolve(const std::string& path, std::string& resolved, bool resolve_last_link = true); std::string normalize(const std::string& path); NormalizeStatus normalize(int dirfd, const char* raw_path, std::string& normalized_path, bool resolve_last_link = true); std::string normalize(const char* raw_path, bool resolve_last_link = true); bool is_in_gkfs(std::string& path, bool cut_mountdir_prefix = false); std::string get_sys_cwd(); Loading include/client/preload_context.hpp +0 −20 Original line number Diff line number Diff line Loading @@ -54,12 +54,6 @@ struct FsConfig { }; enum class RelativizeStatus { ok, fd_unknown, fd_not_a_dir }; /** * Singleton class of the client context with all relevant global data */ Loading Loading @@ -117,24 +111,10 @@ public: void clear_hosts(); uint64_t local_host_id() const; void local_host_id(uint64_t id); bool is_gkfs_path(std::string& path); RelativizeStatus normalize_path(int dirfd, const char* raw_path, std::string& normalized_path, bool resolve_last_link = true); std::string normalize_path(const char* raw_path, bool resolve_last_link = true); RelativizeStatus relativize_fd_path(int dirfd, const char* raw_path, std::string& relative_path, bool resolve_last_link = true) const; bool relativize_path(const char* raw_path, std::string& relative_path, bool resolve_last_link = true) const; const std::shared_ptr<gkfs::filemap::OpenFileMap>& file_map() const; void distributor(std::shared_ptr<gkfs::rpc::Distributor> distributor); Loading src/client/hooks.cpp +71 −71 Original line number Diff line number Diff line Loading @@ -47,17 +47,17 @@ int hook_openat(int dirfd, const char* cpath, int flags, mode_t mode) { __func__, dirfd, cpath, flags, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_openat, dirfd, cpath, flags, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_open(normalized_path, mode, flags)); else return syscall_no_intercept(SYS_openat, dirfd, normalized_path.c_str(), flags, mode); Loading Loading @@ -113,8 +113,8 @@ int hook_stat(const char* path, struct stat* buf) { LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path, fmt::ptr(buf)); auto normalized_path = CTX->normalize_path(path, false); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path, false); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_stat(normalized_path, buf)); else return syscall_no_intercept(SYS_stat, normalized_path.c_str(), buf); Loading @@ -127,17 +127,17 @@ int hook_statx(int dirfd, const char* path, int flags, unsigned int mask, struct __func__, dirfd, path, flags, mask, fmt::ptr(buf)); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, path, normalized_path); auto status = gkfs::path::normalize(dirfd, path, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_statx, dirfd, path, flags, mask, buf); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_statx(dirfd, normalized_path.c_str() , flags, mask, buf)); else return syscall_no_intercept(SYS_statx, dirfd, normalized_path.c_str(), flags, mask, buf); Loading @@ -153,8 +153,8 @@ int hook_lstat(const char* path, struct stat* buf) { LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path, fmt::ptr(buf)); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_stat(normalized_path, buf)); else return syscall_no_intercept(SYS_lstat, normalized_path.c_str(), buf); Loading Loading @@ -183,17 +183,17 @@ int hook_fstatat(int dirfd, const char* cpath, struct stat* buf, int flags) { } std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_newfstatat, dirfd, cpath, buf, flags); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_stat(normalized_path, buf)); else return syscall_no_intercept(SYS_newfstatat, dirfd, normalized_path.c_str(), buf, flags); Loading Loading @@ -309,17 +309,17 @@ int hook_unlinkat(int dirfd, const char* cpath, int flags) { } std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path, false); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path, false); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_unlinkat, dirfd, cpath, flags); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) { if (flags & AT_REMOVEDIR) { return with_errno(gkfs::syscall::gkfs_rmdir(normalized_path)); } else { Loading @@ -337,24 +337,24 @@ int hook_symlinkat(const char* oldname, int newdfd, const char* newname) { LOG(DEBUG, "{}() called with oldname: \"{}\", newfd: {}, newname: \"{}\"", __func__, oldname, newdfd, newname); auto old_normalized_path = CTX->normalize_path(oldname); if (CTX->is_gkfs_path(old_normalized_path)) { auto old_normalized_path = gkfs::path::normalize(oldname); if (gkfs::path::is_in_gkfs(old_normalized_path)) { LOG(WARNING, "{}() operation not supported", __func__); return -ENOTSUP; } std::string new_normalized_path{}; auto status = CTX->normalize_path(newdfd, newname, new_normalized_path, false); auto status = gkfs::path::normalize(newdfd, newname, new_normalized_path, false); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_symlinkat, oldname, newdfd, newname); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(new_normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(new_normalized_path)) { LOG(WARNING, "{}() operation not supported", __func__); return -ENOTSUP; } else Loading @@ -371,8 +371,8 @@ int hook_access(const char* path, int mask) { LOG(DEBUG, "{}() called path: \"{}\", mask: {}", __func__, path, mask); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) { auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) { auto ret = gkfs::syscall::gkfs_access(normalized_path, mask); if (ret < 0) { return -errno; Loading @@ -388,17 +388,17 @@ int hook_faccessat(int dirfd, const char* cpath, int mode) { __func__, dirfd, cpath, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_faccessat, dirfd, cpath, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_access(normalized_path, mode)); else return syscall_no_intercept(SYS_faccessat, dirfd, normalized_path.c_str(), mode); Loading Loading @@ -431,8 +431,8 @@ int hook_truncate(const char* path, long length) { LOG(DEBUG, "{}() called with path: {}, offset: {}", __func__, path, length); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_truncate(normalized_path, length)); else return syscall_no_intercept(SYS_truncate, normalized_path.c_str(), length); Loading Loading @@ -516,17 +516,17 @@ int hook_mkdirat(int dirfd, const char* cpath, mode_t mode) { __func__, dirfd, cpath, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_mkdirat, dirfd, cpath, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_create(normalized_path, mode | S_IFDIR)); else return syscall_no_intercept(SYS_mkdirat, dirfd, normalized_path.c_str(), mode); Loading @@ -542,17 +542,17 @@ int hook_fchmodat(int dirfd, const char* cpath, mode_t mode) { __func__, dirfd, cpath, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_fchmodat, dirfd, cpath, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path)) { LOG(WARNING, "{}() operation not supported", __func__); return -ENOTSUP; } else Loading Loading @@ -580,8 +580,8 @@ int hook_chdir(const char* path) { LOG(DEBUG, "{}() called with path: \"{}\"", __func__, path); auto normalized_path = CTX->normalize_path(path); auto internal = CTX->is_gkfs_path(normalized_path); auto normalized_path = gkfs::path::normalize(path); auto internal = gkfs::path::is_in_gkfs(normalized_path, true); if (internal) { //path falls in our namespace auto md = gkfs::util::get_metadata(normalized_path); Loading @@ -593,14 +593,14 @@ int hook_chdir(const char* path) { LOG(ERROR, "{}() path is not a directory", __func__); return -ENOTDIR; } //TODO get complete path from relativize_path instead of // removing mountdir and then adding again here // readd mountdir prefix again for setting current working directory normalized_path.insert(0, CTX->mountdir()); if (gkfs::path::has_trailing_slash(normalized_path)) { // open_dir is '/' normalized_path.pop_back(); } } // TODO fix with LEAF try { gkfs::path::set_cwd(normalized_path, internal); } catch (const std::system_error& se) { Loading Loading @@ -667,17 +667,17 @@ int hook_readlinkat(int dirfd, const char* cpath, char* buf, int bufsiz) { __func__, dirfd, cpath, fmt::ptr(buf), bufsiz); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path, false); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path, false); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_readlinkat, dirfd, cpath, buf, bufsiz); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path)) { LOG(WARNING, "{}() not supported", __func__); return -ENOTSUP; } else Loading Loading @@ -762,18 +762,18 @@ int hook_renameat(int olddfd, const char* oldname, const char* oldpath_pass; std::string oldpath_normalized_path{}; auto status = CTX->normalize_path(olddfd, oldname, oldpath_normalized_path); auto status = gkfs::path::normalize(olddfd, oldname, oldpath_normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: oldpath_pass = oldname; break; case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(oldpath_normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(oldpath_normalized_path)) { LOG(WARNING, "{}() not supported", __func__); return -ENOTSUP; } else { Loading @@ -788,18 +788,18 @@ int hook_renameat(int olddfd, const char* oldname, const char* newpath_pass; std::string newpath_normalized_path{}; status = CTX->normalize_path(newdfd, newname, newpath_normalized_path); status = gkfs::path::normalize(newdfd, newname, newpath_normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: newpath_pass = newname; break; case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(newpath_normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(newpath_normalized_path)) { LOG(WARNING, "{}() not supported", __func__); return -ENOTSUP; } else { Loading @@ -820,8 +820,8 @@ int hook_statfs(const char* path, struct statfs* buf) { LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path, fmt::ptr(buf)); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_statfs(buf)); else return syscall_no_intercept(SYS_statfs, normalized_path.c_str(), buf); Loading src/client/path.cpp +132 −46 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ #include <client/preload.hpp> #include <client/logging.hpp> #include <client/env.hpp> #include <client/open_file_map.hpp> #include <client/open_dir.hpp> #include <global/path_util.hpp> Loading @@ -31,6 +33,61 @@ extern "C" { using namespace std; namespace { /** * Normalize a given path with `.` and `..` components. This will not resolve symlinks * @param path * @return normalized path */ string normalize(const string& path) { string normalized{}; // final normalized path normalized.reserve(path.size()); string::size_type comp_size = 0; // size of current component string::size_type start = 0; // start index of curr component string::size_type end = 0; // end index of curr component (last processed Path Separator "separator") stack<string::size_type> slash_idx{}; slash_idx.push(0); // index of all slashes in resolved path (used for rollback due to `..`) while (++end < path.size()) { start = end; // Skip sequence of multiple path-separators. while (start < path.size() && path[start] == gkfs::path::separator) { start++; } // Find next component end = path.find_first_of(gkfs::path::separator, start); if (end == string::npos) { end = path.size(); } comp_size = end - start; // component is empty (this must be the last component) if (comp_size == 0) { break; } // component is '.', we skip it if (comp_size == 1 && path.at(start) == '.') { continue; } // component is '..' we need to rollback normalized path if (comp_size == 2 && path.at(start) == '.' && path.at(start + 1) == '.') { if (!normalized.empty()) { normalized.erase(slash_idx.top()); slash_idx.pop(); } continue; } // add `/<component>` to the normalized path normalized.push_back(gkfs::path::separator); slash_idx.push(normalized.size() - 1); normalized.append(path, start, comp_size); } LOG(INFO, "path: '{}', normalized: '{}'", path, normalized); return normalized; } } namespace gkfs { namespace path { Loading Loading @@ -78,58 +135,87 @@ unsigned int match_components(const string& path, unsigned int& path_components, return matched; } NormalizeStatus normalize(int dirfd, const char* raw_path, std::string& normalized_path, bool resolve_last_link) { // TODO when LEAF is available: return concated path and throw Status instead. // Relativize path should be called only after the library constructor has been executed assert(CTX->interception_enabled()); // If we run the constructor we also already setup the mountdir assert(!CTX->mountdir().empty()); /** * Normalize a given path with `.` and `..` components. This will not resolve symlinks * @param path * @return normalized path */ string normalize(const string& path) { // We assume raw path is valid assert(raw_path != nullptr); string normalized{}; // final normalized path normalized.reserve(path.size()); string::size_type comp_size = 0; // size of current component string::size_type start = 0; // start index of curr component string::size_type end = 0; // end index of curr component (last processed Path Separator "separator") stack<string::size_type> slash_idx{}; slash_idx.push(0); // index of all slashes in resolved path (used for rollback due to `..`) std::string path{}; while (++end < path.size()) { start = end; // Skip sequence of multiple path-separators. while (start < path.size() && path[start] == path::separator) { start++; if (raw_path[0] != gkfs::path::separator) { // path is relative if (dirfd == AT_FDCWD) { // path is relative to cwd path = gkfs::path::prepend_path(CTX->cwd(), raw_path); } else { if (!CTX->file_map()->exist(dirfd)) { return NormalizeStatus::fd_unknown; } // Find next component end = path.find_first_of(path::separator, start); if (end == string::npos) { end = path.size(); // path is relative to fd auto dir = CTX->file_map()->get_dir(dirfd); if (dir == nullptr) { return NormalizeStatus::fd_not_a_dir; } comp_size = end - start; // component is empty (this must be the last component) if (comp_size == 0) { break; path = CTX->mountdir(); path.append(dir->path()); path.push_back(gkfs::path::separator); path.append(raw_path); } // component is '.', we skip it if (comp_size == 1 && path.at(start) == '.') { continue; } else { path = raw_path; } // component is '..' we need to rollback normalized path if (comp_size == 2 && path.at(start) == '.' && path.at(start + 1) == '.') { if (!normalized.empty()) { normalized.erase(slash_idx.top()); slash_idx.pop(); normalized_path = ::normalize(path); return NormalizeStatus::ok; } continue; std::string normalize(const char* raw_path, bool resolve_last_link) { // TODO when LEAF is available: return concated path and throw Status instead. // Relativize path should be called only after the library constructor has been executed assert(CTX->interception_enabled()); // If we run the constructor we also already setup the mountdir assert(!CTX->mountdir().empty()); // We assume raw path is valid assert(raw_path != nullptr); std::string path; if (raw_path[0] != gkfs::path::separator) { /* Path is not absolute, we need to prepend CWD; * First reserve enough space to minimize memory copy */ path = gkfs::path::prepend_path(CTX->cwd(), raw_path); } else { path = raw_path; } // add `/<component>` to the normalized path normalized.push_back(path::separator); slash_idx.push(normalized.size() - 1); normalized.append(path, start, comp_size); return ::normalize(path); } /** * Checks if a path is within gkfs namespace. normalize_path() should have been called before * as the mountpoint in the path is checked from the beginning and must therefore be absolute. * * cut_mountdir_prefix can be passed to remove the mountdir prefix from path: /tmp/mountdir/gkfsfile -> /gkfsfile * It modifies the given path instead of returning a new string to avoid copying, * as this function is performance critical * * @param path (absolute path) * @param cut_mountdir_prefix (default false, if true gkfs mountpoint path is cut if it is within gkfs namespace) * @return true if within gkfs namespace else false */ bool is_in_gkfs(std::string& path, bool cut_mountdir_prefix) { if (path.rfind(CTX->mountdir(), 0) != std::string::npos) { if (cut_mountdir_prefix) path.erase(1, CTX->mountdir().size()); return true; } else { return false; } LOG(INFO, "path: '{}', normalized: '{}'", path, normalized); return normalized; } /** Resolve path to its canonical representation Loading src/client/preload_context.cpp +0 −79 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
include/client/path.hpp +11 −1 Original line number Diff line number Diff line Loading @@ -17,12 +17,22 @@ namespace gkfs { namespace path { enum class NormalizeStatus { ok, fd_unknown, fd_not_a_dir }; unsigned int match_components(const std::string& path, unsigned int& path_components, const std::vector<std::string>& components); bool resolve(const std::string& path, std::string& resolved, bool resolve_last_link = true); std::string normalize(const std::string& path); NormalizeStatus normalize(int dirfd, const char* raw_path, std::string& normalized_path, bool resolve_last_link = true); std::string normalize(const char* raw_path, bool resolve_last_link = true); bool is_in_gkfs(std::string& path, bool cut_mountdir_prefix = false); std::string get_sys_cwd(); Loading
include/client/preload_context.hpp +0 −20 Original line number Diff line number Diff line Loading @@ -54,12 +54,6 @@ struct FsConfig { }; enum class RelativizeStatus { ok, fd_unknown, fd_not_a_dir }; /** * Singleton class of the client context with all relevant global data */ Loading Loading @@ -117,24 +111,10 @@ public: void clear_hosts(); uint64_t local_host_id() const; void local_host_id(uint64_t id); bool is_gkfs_path(std::string& path); RelativizeStatus normalize_path(int dirfd, const char* raw_path, std::string& normalized_path, bool resolve_last_link = true); std::string normalize_path(const char* raw_path, bool resolve_last_link = true); RelativizeStatus relativize_fd_path(int dirfd, const char* raw_path, std::string& relative_path, bool resolve_last_link = true) const; bool relativize_path(const char* raw_path, std::string& relative_path, bool resolve_last_link = true) const; const std::shared_ptr<gkfs::filemap::OpenFileMap>& file_map() const; void distributor(std::shared_ptr<gkfs::rpc::Distributor> distributor); Loading
src/client/hooks.cpp +71 −71 Original line number Diff line number Diff line Loading @@ -47,17 +47,17 @@ int hook_openat(int dirfd, const char* cpath, int flags, mode_t mode) { __func__, dirfd, cpath, flags, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_openat, dirfd, cpath, flags, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_open(normalized_path, mode, flags)); else return syscall_no_intercept(SYS_openat, dirfd, normalized_path.c_str(), flags, mode); Loading Loading @@ -113,8 +113,8 @@ int hook_stat(const char* path, struct stat* buf) { LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path, fmt::ptr(buf)); auto normalized_path = CTX->normalize_path(path, false); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path, false); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_stat(normalized_path, buf)); else return syscall_no_intercept(SYS_stat, normalized_path.c_str(), buf); Loading @@ -127,17 +127,17 @@ int hook_statx(int dirfd, const char* path, int flags, unsigned int mask, struct __func__, dirfd, path, flags, mask, fmt::ptr(buf)); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, path, normalized_path); auto status = gkfs::path::normalize(dirfd, path, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_statx, dirfd, path, flags, mask, buf); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_statx(dirfd, normalized_path.c_str() , flags, mask, buf)); else return syscall_no_intercept(SYS_statx, dirfd, normalized_path.c_str(), flags, mask, buf); Loading @@ -153,8 +153,8 @@ int hook_lstat(const char* path, struct stat* buf) { LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path, fmt::ptr(buf)); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_stat(normalized_path, buf)); else return syscall_no_intercept(SYS_lstat, normalized_path.c_str(), buf); Loading Loading @@ -183,17 +183,17 @@ int hook_fstatat(int dirfd, const char* cpath, struct stat* buf, int flags) { } std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_newfstatat, dirfd, cpath, buf, flags); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_stat(normalized_path, buf)); else return syscall_no_intercept(SYS_newfstatat, dirfd, normalized_path.c_str(), buf, flags); Loading Loading @@ -309,17 +309,17 @@ int hook_unlinkat(int dirfd, const char* cpath, int flags) { } std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path, false); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path, false); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_unlinkat, dirfd, cpath, flags); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) { if (flags & AT_REMOVEDIR) { return with_errno(gkfs::syscall::gkfs_rmdir(normalized_path)); } else { Loading @@ -337,24 +337,24 @@ int hook_symlinkat(const char* oldname, int newdfd, const char* newname) { LOG(DEBUG, "{}() called with oldname: \"{}\", newfd: {}, newname: \"{}\"", __func__, oldname, newdfd, newname); auto old_normalized_path = CTX->normalize_path(oldname); if (CTX->is_gkfs_path(old_normalized_path)) { auto old_normalized_path = gkfs::path::normalize(oldname); if (gkfs::path::is_in_gkfs(old_normalized_path)) { LOG(WARNING, "{}() operation not supported", __func__); return -ENOTSUP; } std::string new_normalized_path{}; auto status = CTX->normalize_path(newdfd, newname, new_normalized_path, false); auto status = gkfs::path::normalize(newdfd, newname, new_normalized_path, false); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_symlinkat, oldname, newdfd, newname); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(new_normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(new_normalized_path)) { LOG(WARNING, "{}() operation not supported", __func__); return -ENOTSUP; } else Loading @@ -371,8 +371,8 @@ int hook_access(const char* path, int mask) { LOG(DEBUG, "{}() called path: \"{}\", mask: {}", __func__, path, mask); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) { auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) { auto ret = gkfs::syscall::gkfs_access(normalized_path, mask); if (ret < 0) { return -errno; Loading @@ -388,17 +388,17 @@ int hook_faccessat(int dirfd, const char* cpath, int mode) { __func__, dirfd, cpath, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_faccessat, dirfd, cpath, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_access(normalized_path, mode)); else return syscall_no_intercept(SYS_faccessat, dirfd, normalized_path.c_str(), mode); Loading Loading @@ -431,8 +431,8 @@ int hook_truncate(const char* path, long length) { LOG(DEBUG, "{}() called with path: {}, offset: {}", __func__, path, length); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_truncate(normalized_path, length)); else return syscall_no_intercept(SYS_truncate, normalized_path.c_str(), length); Loading Loading @@ -516,17 +516,17 @@ int hook_mkdirat(int dirfd, const char* cpath, mode_t mode) { __func__, dirfd, cpath, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_mkdirat, dirfd, cpath, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_create(normalized_path, mode | S_IFDIR)); else return syscall_no_intercept(SYS_mkdirat, dirfd, normalized_path.c_str(), mode); Loading @@ -542,17 +542,17 @@ int hook_fchmodat(int dirfd, const char* cpath, mode_t mode) { __func__, dirfd, cpath, mode); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_fchmodat, dirfd, cpath, mode); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path)) { LOG(WARNING, "{}() operation not supported", __func__); return -ENOTSUP; } else Loading Loading @@ -580,8 +580,8 @@ int hook_chdir(const char* path) { LOG(DEBUG, "{}() called with path: \"{}\"", __func__, path); auto normalized_path = CTX->normalize_path(path); auto internal = CTX->is_gkfs_path(normalized_path); auto normalized_path = gkfs::path::normalize(path); auto internal = gkfs::path::is_in_gkfs(normalized_path, true); if (internal) { //path falls in our namespace auto md = gkfs::util::get_metadata(normalized_path); Loading @@ -593,14 +593,14 @@ int hook_chdir(const char* path) { LOG(ERROR, "{}() path is not a directory", __func__); return -ENOTDIR; } //TODO get complete path from relativize_path instead of // removing mountdir and then adding again here // readd mountdir prefix again for setting current working directory normalized_path.insert(0, CTX->mountdir()); if (gkfs::path::has_trailing_slash(normalized_path)) { // open_dir is '/' normalized_path.pop_back(); } } // TODO fix with LEAF try { gkfs::path::set_cwd(normalized_path, internal); } catch (const std::system_error& se) { Loading Loading @@ -667,17 +667,17 @@ int hook_readlinkat(int dirfd, const char* cpath, char* buf, int bufsiz) { __func__, dirfd, cpath, fmt::ptr(buf), bufsiz); std::string normalized_path{}; auto status = CTX->normalize_path(dirfd, cpath, normalized_path, false); auto status = gkfs::path::normalize(dirfd, cpath, normalized_path, false); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: return syscall_no_intercept(SYS_readlinkat, dirfd, cpath, buf, bufsiz); case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(normalized_path)) { LOG(WARNING, "{}() not supported", __func__); return -ENOTSUP; } else Loading Loading @@ -762,18 +762,18 @@ int hook_renameat(int olddfd, const char* oldname, const char* oldpath_pass; std::string oldpath_normalized_path{}; auto status = CTX->normalize_path(olddfd, oldname, oldpath_normalized_path); auto status = gkfs::path::normalize(olddfd, oldname, oldpath_normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: oldpath_pass = oldname; break; case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(oldpath_normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(oldpath_normalized_path)) { LOG(WARNING, "{}() not supported", __func__); return -ENOTSUP; } else { Loading @@ -788,18 +788,18 @@ int hook_renameat(int olddfd, const char* oldname, const char* newpath_pass; std::string newpath_normalized_path{}; status = CTX->normalize_path(newdfd, newname, newpath_normalized_path); status = gkfs::path::normalize(newdfd, newname, newpath_normalized_path); switch (status) { case gkfs::preload::RelativizeStatus::fd_unknown: case gkfs::path::NormalizeStatus::fd_unknown: newpath_pass = newname; break; case gkfs::preload::RelativizeStatus::fd_not_a_dir: case gkfs::path::NormalizeStatus::fd_not_a_dir: return -ENOTDIR; case gkfs::preload::RelativizeStatus::ok: if (CTX->is_gkfs_path(newpath_normalized_path)) { case gkfs::path::NormalizeStatus::ok: if (gkfs::path::is_in_gkfs(newpath_normalized_path)) { LOG(WARNING, "{}() not supported", __func__); return -ENOTSUP; } else { Loading @@ -820,8 +820,8 @@ int hook_statfs(const char* path, struct statfs* buf) { LOG(DEBUG, "{}() called with path: \"{}\", buf: {}", __func__, path, fmt::ptr(buf)); auto normalized_path = CTX->normalize_path(path); if (CTX->is_gkfs_path(normalized_path)) auto normalized_path = gkfs::path::normalize(path); if (gkfs::path::is_in_gkfs(normalized_path, true)) return with_errno(gkfs::syscall::gkfs_statfs(buf)); else return syscall_no_intercept(SYS_statfs, normalized_path.c_str(), buf); Loading
src/client/path.cpp +132 −46 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ #include <client/preload.hpp> #include <client/logging.hpp> #include <client/env.hpp> #include <client/open_file_map.hpp> #include <client/open_dir.hpp> #include <global/path_util.hpp> Loading @@ -31,6 +33,61 @@ extern "C" { using namespace std; namespace { /** * Normalize a given path with `.` and `..` components. This will not resolve symlinks * @param path * @return normalized path */ string normalize(const string& path) { string normalized{}; // final normalized path normalized.reserve(path.size()); string::size_type comp_size = 0; // size of current component string::size_type start = 0; // start index of curr component string::size_type end = 0; // end index of curr component (last processed Path Separator "separator") stack<string::size_type> slash_idx{}; slash_idx.push(0); // index of all slashes in resolved path (used for rollback due to `..`) while (++end < path.size()) { start = end; // Skip sequence of multiple path-separators. while (start < path.size() && path[start] == gkfs::path::separator) { start++; } // Find next component end = path.find_first_of(gkfs::path::separator, start); if (end == string::npos) { end = path.size(); } comp_size = end - start; // component is empty (this must be the last component) if (comp_size == 0) { break; } // component is '.', we skip it if (comp_size == 1 && path.at(start) == '.') { continue; } // component is '..' we need to rollback normalized path if (comp_size == 2 && path.at(start) == '.' && path.at(start + 1) == '.') { if (!normalized.empty()) { normalized.erase(slash_idx.top()); slash_idx.pop(); } continue; } // add `/<component>` to the normalized path normalized.push_back(gkfs::path::separator); slash_idx.push(normalized.size() - 1); normalized.append(path, start, comp_size); } LOG(INFO, "path: '{}', normalized: '{}'", path, normalized); return normalized; } } namespace gkfs { namespace path { Loading Loading @@ -78,58 +135,87 @@ unsigned int match_components(const string& path, unsigned int& path_components, return matched; } NormalizeStatus normalize(int dirfd, const char* raw_path, std::string& normalized_path, bool resolve_last_link) { // TODO when LEAF is available: return concated path and throw Status instead. // Relativize path should be called only after the library constructor has been executed assert(CTX->interception_enabled()); // If we run the constructor we also already setup the mountdir assert(!CTX->mountdir().empty()); /** * Normalize a given path with `.` and `..` components. This will not resolve symlinks * @param path * @return normalized path */ string normalize(const string& path) { // We assume raw path is valid assert(raw_path != nullptr); string normalized{}; // final normalized path normalized.reserve(path.size()); string::size_type comp_size = 0; // size of current component string::size_type start = 0; // start index of curr component string::size_type end = 0; // end index of curr component (last processed Path Separator "separator") stack<string::size_type> slash_idx{}; slash_idx.push(0); // index of all slashes in resolved path (used for rollback due to `..`) std::string path{}; while (++end < path.size()) { start = end; // Skip sequence of multiple path-separators. while (start < path.size() && path[start] == path::separator) { start++; if (raw_path[0] != gkfs::path::separator) { // path is relative if (dirfd == AT_FDCWD) { // path is relative to cwd path = gkfs::path::prepend_path(CTX->cwd(), raw_path); } else { if (!CTX->file_map()->exist(dirfd)) { return NormalizeStatus::fd_unknown; } // Find next component end = path.find_first_of(path::separator, start); if (end == string::npos) { end = path.size(); // path is relative to fd auto dir = CTX->file_map()->get_dir(dirfd); if (dir == nullptr) { return NormalizeStatus::fd_not_a_dir; } comp_size = end - start; // component is empty (this must be the last component) if (comp_size == 0) { break; path = CTX->mountdir(); path.append(dir->path()); path.push_back(gkfs::path::separator); path.append(raw_path); } // component is '.', we skip it if (comp_size == 1 && path.at(start) == '.') { continue; } else { path = raw_path; } // component is '..' we need to rollback normalized path if (comp_size == 2 && path.at(start) == '.' && path.at(start + 1) == '.') { if (!normalized.empty()) { normalized.erase(slash_idx.top()); slash_idx.pop(); normalized_path = ::normalize(path); return NormalizeStatus::ok; } continue; std::string normalize(const char* raw_path, bool resolve_last_link) { // TODO when LEAF is available: return concated path and throw Status instead. // Relativize path should be called only after the library constructor has been executed assert(CTX->interception_enabled()); // If we run the constructor we also already setup the mountdir assert(!CTX->mountdir().empty()); // We assume raw path is valid assert(raw_path != nullptr); std::string path; if (raw_path[0] != gkfs::path::separator) { /* Path is not absolute, we need to prepend CWD; * First reserve enough space to minimize memory copy */ path = gkfs::path::prepend_path(CTX->cwd(), raw_path); } else { path = raw_path; } // add `/<component>` to the normalized path normalized.push_back(path::separator); slash_idx.push(normalized.size() - 1); normalized.append(path, start, comp_size); return ::normalize(path); } /** * Checks if a path is within gkfs namespace. normalize_path() should have been called before * as the mountpoint in the path is checked from the beginning and must therefore be absolute. * * cut_mountdir_prefix can be passed to remove the mountdir prefix from path: /tmp/mountdir/gkfsfile -> /gkfsfile * It modifies the given path instead of returning a new string to avoid copying, * as this function is performance critical * * @param path (absolute path) * @param cut_mountdir_prefix (default false, if true gkfs mountpoint path is cut if it is within gkfs namespace) * @return true if within gkfs namespace else false */ bool is_in_gkfs(std::string& path, bool cut_mountdir_prefix) { if (path.rfind(CTX->mountdir(), 0) != std::string::npos) { if (cut_mountdir_prefix) path.erase(1, CTX->mountdir().size()); return true; } else { return false; } LOG(INFO, "path: '{}', normalized: '{}'", path, normalized); return normalized; } /** Resolve path to its canonical representation Loading
src/client/preload_context.cpp +0 −79 File changed.Preview size limit exceeded, changes collapsed. Show changes