Loading src/client/fuse/fuse_client.cpp +88 −4 Original line number Diff line number Diff line Loading @@ -678,7 +678,8 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, if(target.rfind(ud->source, 0) == 0) { // starts with mount path target = get_path(parent_inode, target.substr(strlen(ud->source)).c_str()); target = get_path(parent_inode, target.substr(strlen(ud->source)).c_str()); } fuse_log(FUSE_LOG_DEBUG, "mk symlink path %s target %s\n", path.c_str(), Loading Loading @@ -712,6 +713,89 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, #endif } static void rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, fuse_ino_t new_parent, const char* new_name, unsigned int flags) { #ifdef HAS_RENAME auto* old_parent_inode = get_inode(old_parent); if(!old_parent_inode) { fuse_reply_err(req, ENOENT); return; } auto* new_parent_inode = get_inode(new_parent); if(!new_parent_inode) { fuse_reply_err(req, ENOENT); return; } std::string old_path = get_path(old_parent_inode, old_name); std::string new_path = get_path(new_parent_inode, new_name); struct stat st_src{}; if(gkfs::syscall::gkfs_stat(old_path, &st_src) < 0) { fuse_reply_err(req, errno == 0 ? ENOENT : errno); return; } struct stat st_dst{}; const bool dst_exists = (gkfs::syscall::gkfs_stat(new_path, &st_dst) == 0); if((flags & RENAME_NOREPLACE) && dst_exists) { fuse_reply_err(req, EEXIST); return; } if(flags & RENAME_EXCHANGE) { fuse_reply_err(req, EOPNOTSUPP); return; } int rc = gkfs::syscall::gkfs_rename(old_path, new_path); if(rc < 0) { fuse_reply_err(req, 1); return; } fuse_ino_t src_ino = 0; auto it_src = path_map.find(old_path); if(it_src != path_map.end()) { 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; } // If destination existed and was overwritten, detach its mapping if(dst_exists) { auto it_dst = path_map.find(new_path); if(it_dst != path_map.end()) { fuse_ino_t dst_ino = it_dst->second; path_map.erase(it_dst); // Mark the old dst inode as disconnected (no pathname). // Keep it alive until lookup_count==0 and open_count==0, then free // in forget(). auto& dst_rec = ino_map[dst_ino]; dst_rec.path.clear(); } } // 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); #endif } static void init_gekkofs() { Loading Loading @@ -754,7 +838,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ops->readlink = readlink_handler; // ops->mknod ops->symlink = symlink_handler; // ops->rename ops->rename = rename_handler; // ops->link ops->flush = flush_handler; ops->release = release_handler; Loading tests/integration/fuse/test_basic_operations.py +4 −0 Original line number Diff line number Diff line Loading @@ -86,3 +86,7 @@ def test_read(gkfs_daemon, fuse_client): sh.ln("-s", str(file), "link") assert sh.ls(str(dir)) == "link\n" assert sh.cat("link") == "baum\n" # renaming sh.mv("link", "../lonk") assert sh.ls("..") == "dir file lonk\n" Loading
src/client/fuse/fuse_client.cpp +88 −4 Original line number Diff line number Diff line Loading @@ -678,7 +678,8 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, if(target.rfind(ud->source, 0) == 0) { // starts with mount path target = get_path(parent_inode, target.substr(strlen(ud->source)).c_str()); target = get_path(parent_inode, target.substr(strlen(ud->source)).c_str()); } fuse_log(FUSE_LOG_DEBUG, "mk symlink path %s target %s\n", path.c_str(), Loading Loading @@ -712,6 +713,89 @@ symlink_handler(fuse_req_t req, const char* linkname, fuse_ino_t parent, #endif } static void rename_handler(fuse_req_t req, fuse_ino_t old_parent, const char* old_name, fuse_ino_t new_parent, const char* new_name, unsigned int flags) { #ifdef HAS_RENAME auto* old_parent_inode = get_inode(old_parent); if(!old_parent_inode) { fuse_reply_err(req, ENOENT); return; } auto* new_parent_inode = get_inode(new_parent); if(!new_parent_inode) { fuse_reply_err(req, ENOENT); return; } std::string old_path = get_path(old_parent_inode, old_name); std::string new_path = get_path(new_parent_inode, new_name); struct stat st_src{}; if(gkfs::syscall::gkfs_stat(old_path, &st_src) < 0) { fuse_reply_err(req, errno == 0 ? ENOENT : errno); return; } struct stat st_dst{}; const bool dst_exists = (gkfs::syscall::gkfs_stat(new_path, &st_dst) == 0); if((flags & RENAME_NOREPLACE) && dst_exists) { fuse_reply_err(req, EEXIST); return; } if(flags & RENAME_EXCHANGE) { fuse_reply_err(req, EOPNOTSUPP); return; } int rc = gkfs::syscall::gkfs_rename(old_path, new_path); if(rc < 0) { fuse_reply_err(req, 1); return; } fuse_ino_t src_ino = 0; auto it_src = path_map.find(old_path); if(it_src != path_map.end()) { 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; } // If destination existed and was overwritten, detach its mapping if(dst_exists) { auto it_dst = path_map.find(new_path); if(it_dst != path_map.end()) { fuse_ino_t dst_ino = it_dst->second; path_map.erase(it_dst); // Mark the old dst inode as disconnected (no pathname). // Keep it alive until lookup_count==0 and open_count==0, then free // in forget(). auto& dst_rec = ino_map[dst_ino]; dst_rec.path.clear(); } } // 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); #endif } static void init_gekkofs() { Loading Loading @@ -754,7 +838,7 @@ init_ll_ops(fuse_lowlevel_ops* ops) { ops->readlink = readlink_handler; // ops->mknod ops->symlink = symlink_handler; // ops->rename ops->rename = rename_handler; // ops->link ops->flush = flush_handler; ops->release = release_handler; Loading
tests/integration/fuse/test_basic_operations.py +4 −0 Original line number Diff line number Diff line Loading @@ -86,3 +86,7 @@ def test_read(gkfs_daemon, fuse_client): sh.ln("-s", str(file), "link") assert sh.ls(str(dir)) == "link\n" assert sh.cat("link") == "baum\n" # renaming sh.mv("link", "../lonk") assert sh.ls("..") == "dir file lonk\n"