Loading CMake/gkfs-options.cmake +1 −1 Original line number Diff line number Diff line Loading @@ -248,7 +248,7 @@ gkfs_define_option( DEFAULT_VALUE ON ) # use old resolve function # build fuse client for gekkofs gkfs_define_option( GKFS_BUILD_FUSE HELP_TEXT "Build FUSE client" Loading include/client/fuse/fuse_client.hpp +21 −9 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ extern "C" { #endif // GekkoFS Project Headers #include <config.hpp> #include <common/path_util.hpp> #include <client/hooks.hpp> #include <client/logging.hpp> // Assumed to provide LOG and CTX Loading @@ -87,10 +88,28 @@ extern "C" { #include <client/preload_context.hpp> #include <client/user_functions.hpp> // TODO do we really need the stat here? no i dont think so // Pointer trick to save a hash table and the lookup for inodes. // Only works if the fuse_ino_t has the size of uintptr_t. // For older compilers, the else case fails during compile time if the size does // not match. From libfuse passthrough example #if defined(__GNUC__) && \ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ !defined __cplusplus _Static_assert(!gkfs::config::fuse::pointertrick || sizeof(fuse_ino_t) >= sizeof(uintptr_t), "fuse_ino_t too small to hold uintptr_t values!"); #else struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { unsigned _uintptr_to_must_hold_fuse_ino_t : (!gkfs::config::fuse::pointertrick || (sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; #endif struct Inode { std::string path; struct stat st; uint64_t lookup_count; }; Loading @@ -115,11 +134,4 @@ struct u_data { int timeout_set; }; struct GkfsDir { // Hypothetical structure that might be used if DIR is cast int fd; long int tell_pos; // for telldir/seekdir char* path; // other members libc DIR might have }; #endif // GKFS_CLIENT_FUSE_CONTEXT_HPP include/config.hpp +10 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,16 @@ constexpr auto max_stats = 1000000; ///< How many stats will be stored constexpr auto prometheus_gateway = "127.0.0.1:9091"; } // namespace stats namespace fuse { // use pointertrick for fuse client // only works if sizeof(fuse_ino_t) >= sizeof(uintptr_t) constexpr auto pointertrick = false; // on read and write, check existence of the inode for better errors // not needed constexpr auto check_inode = true; } // namespace fuse } // namespace gkfs::config #endif // GEKKOFS_CONFIG_HPP src/client/fuse/fuse_client.cpp +83 −64 Original line number Diff line number Diff line Loading @@ -61,8 +61,14 @@ static const std::string fifo_path = "/tmp/gekkofs_fifos/"; static fuse_ino_t alloc_inode(const std::string& path) { std::lock_guard<std::mutex> lk(ino_mutex); fuse_ino_t ino = next_ino++; ino_map[ino] = {path, {}, 1}; fuse_ino_t ino; if(gkfs::config::fuse::pointertrick) { Inode* inode = new Inode{path, 1}; ino = (fuse_ino_t) inode; } else { ino = next_ino++; ino_map[ino] = {path, 1}; } path_map[path] = ino; return ino; } Loading @@ -70,9 +76,29 @@ alloc_inode(const std::string& path) { static Inode* get_inode(fuse_ino_t ino) { std::lock_guard<std::mutex> lk(ino_mutex); if(gkfs::config::fuse::pointertrick) { if(ino == FUSE_ROOT_ID) { return &ino_map[FUSE_ROOT_ID]; } return (Inode*) ino; } else { auto it = ino_map.find(ino); return it != ino_map.end() ? &it->second : nullptr; } } static void remove_inode_by_path(const std::string path) { auto it_src = path_map.find(path); if(it_src != path_map.end()) { path_map.erase(it_src); if(gkfs::config::fuse::pointertrick) { delete get_inode(it_src->second); } else { ino_map.erase(it_src->second); } } } static struct u_data* udata(fuse_req_t req) { Loading Loading @@ -196,23 +222,26 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { } } struct stat st; int rc = gkfs::syscall::gkfs_stat(child, &st); if(rc < 0) { fuse_reply_err(req, ENOENT); return; } // See if we already have this path auto it = path_map.find(child); fuse_ino_t ino; if(it != path_map.end()) { ino = it->second; ino_map[ino].lookup_count++; auto inode = get_inode(ino); if(inode) { inode->lookup_count++; } } else { ino = alloc_inode(child); } struct stat st; int rc = gkfs::syscall::gkfs_stat(child, &st); if(rc < 0) { fuse_reply_err(req, ENOENT); return; } ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; e.attr = st; Loading @@ -238,7 +267,6 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, errno); return; } inode->st = st; fuse_reply_attr(req, &st, ud->timeout); } Loading @@ -262,18 +290,19 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, fuse_reply_err(req, EIO); return; } // Update cached stat so users see the new size inode->st.st_size = new_size; } if(to_set & FUSE_SET_ATTR_ATIME) inode->st.st_atim = attr->st_atim; if(to_set & FUSE_SET_ATTR_MTIME) inode->st.st_mtim = attr->st_mtim; struct stat st; int rc = gkfs::syscall::gkfs_stat(inode->path, &st); if(rc) { DEBUG_INFO(ud, "getattr error %u", rc); fuse_reply_err(req, errno); return; } // TODO because we cannot save the attributes in gekko, we just return the // buffered results of stat fuse_reply_attr(req, &inode->st, ud->timeout); fuse_reply_attr(req, &st, ud->timeout); return; } Loading @@ -293,7 +322,7 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, ENOENT); return; } fi->fh = fd; // TODO file handle == file descriptor? fi->fh = fd; fi->direct_io = ud->direct_io; fuse_reply_open(req, fi); } Loading @@ -316,11 +345,13 @@ read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { auto* ud = udata(req); DEBUG_INFO(ud, "read handler"); if(gkfs::config::fuse::check_inode) { auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } } std::vector<char> buf(size); int rc = gkfs::syscall::gkfs_pread(fi->fh, buf.data(), size, off); if(rc < 0) { Loading @@ -336,11 +367,13 @@ write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, off_t off, struct fuse_file_info* fi) { auto* ud = udata(req); DEBUG_INFO(ud, "write handler"); if(gkfs::config::fuse::check_inode) { auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } } int rc = gkfs::syscall::gkfs_pwrite(fi->fh, buf, size, off); if(rc < 0) { DEBUG_INFO(ud, "write fail"); Loading Loading @@ -377,7 +410,6 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, } fuse_ino_t ino = alloc_inode(path); DEBUG_INFO(ud, "create new inode ino %i", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; e.attr = st; Loading Loading @@ -411,11 +443,7 @@ unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { } int rc = gkfs::syscall::gkfs_remove(path); auto it_src = path_map.find(path); if(it_src != path_map.end()) { path_map.erase(it_src); ino_map.erase(it_src->second); } remove_inode_by_path(path); if(rc == -1) { fuse_reply_err(req, 1); return; Loading Loading @@ -563,21 +591,19 @@ forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { auto* ud = udata(req); DEBUG_INFO(ud, "forget handler"); auto it = ino_map.find(ino); if(it == ino_map.end()) { Inode* inode = get_inode(ino); if(!inode) { fuse_reply_none(req); return; } Inode& inode = it->second; if(inode.lookup_count > nlookup) inode.lookup_count -= nlookup; if(inode->lookup_count > nlookup) inode->lookup_count -= nlookup; else inode.lookup_count = 0; inode->lookup_count = 0; if(inode.lookup_count == 0) { // && inode.open_count == 0 path_map.erase(inode.path); ino_map.erase(it); if(inode->lookup_count == 0) { // && inode.open_count == 0 remove_inode_by_path(inode->path); DEBUG_INFO(ud, "reached lookup_count 0"); } Loading @@ -589,11 +615,13 @@ static void flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); DEBUG_INFO(ud, "flush handler"); if(gkfs::config::fuse::check_inode) { auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } } int lc = gkfs::syscall::gkfs_fsync(fi->fh); if(lc < 0) { fuse_reply_err(req, 1); Loading Loading @@ -656,7 +684,6 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, } fuse_ino_t ino = alloc_inode(path); DEBUG_INFO(ud, "create new inode ino %i", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; e.attr = st; Loading @@ -680,11 +707,7 @@ rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { fuse_reply_err(req, errno); return; } auto it_src = path_map.find(path); if(it_src != path_map.end()) { path_map.erase(it_src); ino_map.erase(it_src->second); } remove_inode_by_path(path); fuse_reply_err(req, 0); } Loading Loading @@ -811,12 +834,13 @@ rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, src_ino = it_src->second; path_map.erase(it_src); path_map[new_path] = src_ino; ino_map[src_ino].path = new_path; } else { src_ino = alloc_inode(new_path); path_map[new_path] = src_ino; ino_map[src_ino].path = new_path; ino_map[src_ino].st = st_src; } auto* inode = get_inode(src_ino); if(inode) { inode->path = new_path; } // If destination existed and was overwritten, detach its mapping Loading @@ -834,11 +858,6 @@ rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, } } // Refresh src inode attributes under its new name struct stat st_new{}; if(gkfs::syscall::gkfs_stat(new_path, &st_new) == 0) { ino_map[src_ino].st = st_new; } fuse_reply_err(req, 0); #else fuse_reply_err(req, ENOTSUP); Loading @@ -860,6 +879,7 @@ mknod_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, } mkdir(fifo_path.c_str(), 0700); // TODO name should somehow include the parent path to avoid name collision std::string path = fifo_path + "/" + name; if(!S_ISFIFO(mode)) { Loading Loading @@ -913,8 +933,7 @@ init_gekkofs() { fuse_log(FUSE_LOG_ERR, "failed to open root\n"); exit(1); } ino_map[FUSE_ROOT_ID] = {root_path, {}, 1}; ino_map[FUSE_ROOT_ID].st = st; ino_map[FUSE_ROOT_ID] = {root_path, 1}; path_map[root_path] = FUSE_ROOT_ID; std::cout << "root node allocated" << std::endl; } Loading Loading
CMake/gkfs-options.cmake +1 −1 Original line number Diff line number Diff line Loading @@ -248,7 +248,7 @@ gkfs_define_option( DEFAULT_VALUE ON ) # use old resolve function # build fuse client for gekkofs gkfs_define_option( GKFS_BUILD_FUSE HELP_TEXT "Build FUSE client" Loading
include/client/fuse/fuse_client.hpp +21 −9 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ extern "C" { #endif // GekkoFS Project Headers #include <config.hpp> #include <common/path_util.hpp> #include <client/hooks.hpp> #include <client/logging.hpp> // Assumed to provide LOG and CTX Loading @@ -87,10 +88,28 @@ extern "C" { #include <client/preload_context.hpp> #include <client/user_functions.hpp> // TODO do we really need the stat here? no i dont think so // Pointer trick to save a hash table and the lookup for inodes. // Only works if the fuse_ino_t has the size of uintptr_t. // For older compilers, the else case fails during compile time if the size does // not match. From libfuse passthrough example #if defined(__GNUC__) && \ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ !defined __cplusplus _Static_assert(!gkfs::config::fuse::pointertrick || sizeof(fuse_ino_t) >= sizeof(uintptr_t), "fuse_ino_t too small to hold uintptr_t values!"); #else struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { unsigned _uintptr_to_must_hold_fuse_ino_t : (!gkfs::config::fuse::pointertrick || (sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; #endif struct Inode { std::string path; struct stat st; uint64_t lookup_count; }; Loading @@ -115,11 +134,4 @@ struct u_data { int timeout_set; }; struct GkfsDir { // Hypothetical structure that might be used if DIR is cast int fd; long int tell_pos; // for telldir/seekdir char* path; // other members libc DIR might have }; #endif // GKFS_CLIENT_FUSE_CONTEXT_HPP
include/config.hpp +10 −0 Original line number Diff line number Diff line Loading @@ -183,6 +183,16 @@ constexpr auto max_stats = 1000000; ///< How many stats will be stored constexpr auto prometheus_gateway = "127.0.0.1:9091"; } // namespace stats namespace fuse { // use pointertrick for fuse client // only works if sizeof(fuse_ino_t) >= sizeof(uintptr_t) constexpr auto pointertrick = false; // on read and write, check existence of the inode for better errors // not needed constexpr auto check_inode = true; } // namespace fuse } // namespace gkfs::config #endif // GEKKOFS_CONFIG_HPP
src/client/fuse/fuse_client.cpp +83 −64 Original line number Diff line number Diff line Loading @@ -61,8 +61,14 @@ static const std::string fifo_path = "/tmp/gekkofs_fifos/"; static fuse_ino_t alloc_inode(const std::string& path) { std::lock_guard<std::mutex> lk(ino_mutex); fuse_ino_t ino = next_ino++; ino_map[ino] = {path, {}, 1}; fuse_ino_t ino; if(gkfs::config::fuse::pointertrick) { Inode* inode = new Inode{path, 1}; ino = (fuse_ino_t) inode; } else { ino = next_ino++; ino_map[ino] = {path, 1}; } path_map[path] = ino; return ino; } Loading @@ -70,9 +76,29 @@ alloc_inode(const std::string& path) { static Inode* get_inode(fuse_ino_t ino) { std::lock_guard<std::mutex> lk(ino_mutex); if(gkfs::config::fuse::pointertrick) { if(ino == FUSE_ROOT_ID) { return &ino_map[FUSE_ROOT_ID]; } return (Inode*) ino; } else { auto it = ino_map.find(ino); return it != ino_map.end() ? &it->second : nullptr; } } static void remove_inode_by_path(const std::string path) { auto it_src = path_map.find(path); if(it_src != path_map.end()) { path_map.erase(it_src); if(gkfs::config::fuse::pointertrick) { delete get_inode(it_src->second); } else { ino_map.erase(it_src->second); } } } static struct u_data* udata(fuse_req_t req) { Loading Loading @@ -196,23 +222,26 @@ lookup_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { } } struct stat st; int rc = gkfs::syscall::gkfs_stat(child, &st); if(rc < 0) { fuse_reply_err(req, ENOENT); return; } // See if we already have this path auto it = path_map.find(child); fuse_ino_t ino; if(it != path_map.end()) { ino = it->second; ino_map[ino].lookup_count++; auto inode = get_inode(ino); if(inode) { inode->lookup_count++; } } else { ino = alloc_inode(child); } struct stat st; int rc = gkfs::syscall::gkfs_stat(child, &st); if(rc < 0) { fuse_reply_err(req, ENOENT); return; } ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; e.attr = st; Loading @@ -238,7 +267,6 @@ getattr_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, errno); return; } inode->st = st; fuse_reply_attr(req, &st, ud->timeout); } Loading @@ -262,18 +290,19 @@ setattr_handler(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, fuse_reply_err(req, EIO); return; } // Update cached stat so users see the new size inode->st.st_size = new_size; } if(to_set & FUSE_SET_ATTR_ATIME) inode->st.st_atim = attr->st_atim; if(to_set & FUSE_SET_ATTR_MTIME) inode->st.st_mtim = attr->st_mtim; struct stat st; int rc = gkfs::syscall::gkfs_stat(inode->path, &st); if(rc) { DEBUG_INFO(ud, "getattr error %u", rc); fuse_reply_err(req, errno); return; } // TODO because we cannot save the attributes in gekko, we just return the // buffered results of stat fuse_reply_attr(req, &inode->st, ud->timeout); fuse_reply_attr(req, &st, ud->timeout); return; } Loading @@ -293,7 +322,7 @@ open_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { fuse_reply_err(req, ENOENT); return; } fi->fh = fd; // TODO file handle == file descriptor? fi->fh = fd; fi->direct_io = ud->direct_io; fuse_reply_open(req, fi); } Loading @@ -316,11 +345,13 @@ read_handler(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info* fi) { auto* ud = udata(req); DEBUG_INFO(ud, "read handler"); if(gkfs::config::fuse::check_inode) { auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } } std::vector<char> buf(size); int rc = gkfs::syscall::gkfs_pread(fi->fh, buf.data(), size, off); if(rc < 0) { Loading @@ -336,11 +367,13 @@ write_handler(fuse_req_t req, fuse_ino_t ino, const char* buf, size_t size, off_t off, struct fuse_file_info* fi) { auto* ud = udata(req); DEBUG_INFO(ud, "write handler"); if(gkfs::config::fuse::check_inode) { auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } } int rc = gkfs::syscall::gkfs_pwrite(fi->fh, buf, size, off); if(rc < 0) { DEBUG_INFO(ud, "write fail"); Loading Loading @@ -377,7 +410,6 @@ create_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, } fuse_ino_t ino = alloc_inode(path); DEBUG_INFO(ud, "create new inode ino %i", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; e.attr = st; Loading Loading @@ -411,11 +443,7 @@ unlink_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { } int rc = gkfs::syscall::gkfs_remove(path); auto it_src = path_map.find(path); if(it_src != path_map.end()) { path_map.erase(it_src); ino_map.erase(it_src->second); } remove_inode_by_path(path); if(rc == -1) { fuse_reply_err(req, 1); return; Loading Loading @@ -563,21 +591,19 @@ forget_handler(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) { auto* ud = udata(req); DEBUG_INFO(ud, "forget handler"); auto it = ino_map.find(ino); if(it == ino_map.end()) { Inode* inode = get_inode(ino); if(!inode) { fuse_reply_none(req); return; } Inode& inode = it->second; if(inode.lookup_count > nlookup) inode.lookup_count -= nlookup; if(inode->lookup_count > nlookup) inode->lookup_count -= nlookup; else inode.lookup_count = 0; inode->lookup_count = 0; if(inode.lookup_count == 0) { // && inode.open_count == 0 path_map.erase(inode.path); ino_map.erase(it); if(inode->lookup_count == 0) { // && inode.open_count == 0 remove_inode_by_path(inode->path); DEBUG_INFO(ud, "reached lookup_count 0"); } Loading @@ -589,11 +615,13 @@ static void flush_handler(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi) { auto* ud = udata(req); DEBUG_INFO(ud, "flush handler"); if(gkfs::config::fuse::check_inode) { auto* inode = get_inode(ino); if(!inode) { fuse_reply_err(req, ENOENT); return; } } int lc = gkfs::syscall::gkfs_fsync(fi->fh); if(lc < 0) { fuse_reply_err(req, 1); Loading Loading @@ -656,7 +684,6 @@ mkdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name, } fuse_ino_t ino = alloc_inode(path); DEBUG_INFO(ud, "create new inode ino %i", ino); ino_map[ino].st = st; fuse_entry_param e = {}; e.ino = ino; e.attr = st; Loading @@ -680,11 +707,7 @@ rmdir_handler(fuse_req_t req, fuse_ino_t parent, const char* name) { fuse_reply_err(req, errno); return; } auto it_src = path_map.find(path); if(it_src != path_map.end()) { path_map.erase(it_src); ino_map.erase(it_src->second); } remove_inode_by_path(path); fuse_reply_err(req, 0); } Loading Loading @@ -811,12 +834,13 @@ rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, src_ino = it_src->second; path_map.erase(it_src); path_map[new_path] = src_ino; ino_map[src_ino].path = new_path; } else { src_ino = alloc_inode(new_path); path_map[new_path] = src_ino; ino_map[src_ino].path = new_path; ino_map[src_ino].st = st_src; } auto* inode = get_inode(src_ino); if(inode) { inode->path = new_path; } // If destination existed and was overwritten, detach its mapping Loading @@ -834,11 +858,6 @@ rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, } } // Refresh src inode attributes under its new name struct stat st_new{}; if(gkfs::syscall::gkfs_stat(new_path, &st_new) == 0) { ino_map[src_ino].st = st_new; } fuse_reply_err(req, 0); #else fuse_reply_err(req, ENOTSUP); Loading @@ -860,6 +879,7 @@ mknod_handler(fuse_req_t req, fuse_ino_t parent, const char* name, mode_t mode, } mkdir(fifo_path.c_str(), 0700); // TODO name should somehow include the parent path to avoid name collision std::string path = fifo_path + "/" + name; if(!S_ISFIFO(mode)) { Loading Loading @@ -913,8 +933,7 @@ init_gekkofs() { fuse_log(FUSE_LOG_ERR, "failed to open root\n"); exit(1); } ino_map[FUSE_ROOT_ID] = {root_path, {}, 1}; ino_map[FUSE_ROOT_ID].st = st; ino_map[FUSE_ROOT_ID] = {root_path, 1}; path_map[root_path] = FUSE_ROOT_ID; std::cout << "root node allocated" << std::endl; } Loading