From 7682954ffd63042568cd02195098d37fb1c36e18 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 14 Feb 2022 11:09:29 +0100 Subject: [PATCH 01/15] Add Rename tests Comp errors Rename Steps 2 Rename test passed --- .gitlab-ci.yml | 2 +- include/client/gkfs_functions.hpp | 7 +- include/client/rpc/forward_metadata.hpp | 4 + include/config.hpp | 2 +- src/client/gkfs_functions.cpp | 57 +++++++- src/client/hooks.cpp | 8 +- src/client/rpc/forward_metadata.cpp | 110 ++++++++++++++ src/common/metadata.cpp | 10 +- src/daemon/handler/srv_metadata.cpp | 17 +-- tests/integration/CMakeLists.txt | 12 +- tests/integration/harness/CMakeLists.txt | 1 + .../integration/harness/gkfs.io/commands.hpp | 1 + tests/integration/harness/gkfs.io/main.cpp | 1 + tests/integration/harness/gkfs.io/rename.cpp | 110 ++++++++++++++ tests/integration/harness/io.py | 9 ++ .../rename/test_rename_operation.py | 135 ++++++++++++++++++ 16 files changed, 462 insertions(+), 24 deletions(-) create mode 100644 tests/integration/harness/gkfs.io/rename.cpp create mode 100644 tests/integration/rename/test_rename_operation.py diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fef21fef0..ceaec224c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -137,7 +137,7 @@ gkfs:integration: needs: ['gkfs'] parallel: matrix: - - SUBTEST: [ data, status, syscalls, directories, operations, position, shell ] + - SUBTEST: [ data, status, syscalls, directories, operations, position, shell, rename ] script: ## run tests diff --git a/include/client/gkfs_functions.hpp b/include/client/gkfs_functions.hpp index d9d9d8d86..273a606ba 100644 --- a/include/client/gkfs_functions.hpp +++ b/include/client/gkfs_functions.hpp @@ -41,7 +41,7 @@ struct linux_dirent64; namespace gkfs::syscall { int -gkfs_open(const std::string& path, mode_t mode, int flags); +gkfs_open(const std::string& path, mode_t mode, int flags, bool rename = false); int gkfs_create(const std::string& path, mode_t mode); @@ -149,6 +149,11 @@ gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp, int gkfs_rmdir(const std::string& path); + +int +gkfs_rename(const std::string& old_path_resolved, + const std::string& new_path_resolved); + } // namespace gkfs::syscall // gkfs_getsingleserverdir is using extern "C" to demangle it for C usage diff --git a/include/client/rpc/forward_metadata.hpp b/include/client/rpc/forward_metadata.hpp index 8ece28e4a..7060e1efc 100644 --- a/include/client/rpc/forward_metadata.hpp +++ b/include/client/rpc/forward_metadata.hpp @@ -55,6 +55,10 @@ forward_create(const std::string& path, mode_t mode); int forward_stat(const std::string& path, std::string& attr); +int +forward_rename(const std::string& path, const std::string& path2, + const gkfs::metadata::Metadata& md); + int forward_remove(const std::string& path); diff --git a/include/config.hpp b/include/config.hpp index 44f0bb60e..49f8bf1c2 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -66,7 +66,7 @@ constexpr auto use_atime = false; constexpr auto use_ctime = false; constexpr auto use_mtime = false; constexpr auto use_link_cnt = false; -constexpr auto use_blocks = false; +constexpr auto use_blocks = true; /* * If true, all chunks on the same host are removed during a metadata remove * rpc. This is a technical optimization that reduces the number of RPCs for diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 95a09f1c1..d25bdb03b 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -130,7 +130,7 @@ namespace gkfs::syscall { * @return 0 on success, -1 on failure */ int -gkfs_open(const std::string& path, mode_t mode, int flags) { +gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { if(flags & O_PATH) { LOG(ERROR, "`O_PATH` flag is not supported"); @@ -172,6 +172,12 @@ gkfs_open(const std::string& path, mode_t mode, int flags) { return -1; } md = *md_; + + if(rename == false && md.blocks() == -1) { + LOG(ERROR, "File is renamed '{}': '{}' - rename: {}", path, + rename); + return -1; + } } else { LOG(ERROR, "Error creating file: '{}'", strerror(errno)); return -1; @@ -202,8 +208,25 @@ gkfs_open(const std::string& path, mode_t mode, int flags) { } return gkfs_open(md.target_path(), mode, flags); } + + + /// The file is a renamed file, so we need to get the metadata of the + /// original file. + /// This does not work as we will check that this is a -1. + if(!md.target_path().empty()) { + LOG(ERROR, "File '{}' is renamed, reentering with '{}'", path, + md.target_path()); + return gkfs_open(md.target_path(), mode, flags, true); + } + #endif + if(rename == false && md.blocks() == -1) { + LOG(ERROR, "File '{}' is renamed __", path); + errno = ENOENT; + return -1; + } + if(S_ISDIR(md.mode())) { return gkfs_opendir(path); } @@ -309,6 +332,33 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { return 0; } +/** + * gkfs wrapper for rename() system calls + * errno may be set + * @param old_path + * @param new_path + * @return 0 on success, -1 on failure + */ +int +gkfs_rename(const string& old_path, const string& new_path) { + auto md = gkfs::utils::get_metadata(old_path, false); + if(!md) { + return -1; + } + auto md2 = gkfs::utils::get_metadata(new_path, false); + if(md2) { + return -1; + } + + auto err = gkfs::rpc::forward_rename(old_path, new_path, md.value()); + if(err) { + errno = err; + return -1; + } + + return 0; +} + /** * gkfs wrapper for stat() system calls * errno may be set @@ -320,7 +370,7 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { int gkfs_stat(const string& path, struct stat* buf, bool follow_links) { auto md = gkfs::utils::get_metadata(path, follow_links); - if(!md) { + if(!md or md.value().blocks() == -1) { return -1; } gkfs::utils::metadata_to_stat(path, *md, *buf); @@ -344,7 +394,8 @@ int gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask, struct statx* buf, bool follow_links) { auto md = gkfs::utils::get_metadata(path, follow_links); - if(!md) { + + if(!md or md.value().blocks() == -1) { return -1; } diff --git a/src/client/hooks.cpp b/src/client/hooks.cpp index 49a166ce1..d4b0665cd 100644 --- a/src/client/hooks.cpp +++ b/src/client/hooks.cpp @@ -865,8 +865,8 @@ hook_renameat(int olddfd, const char* oldname, int newdfd, const char* newname, return -ENOTDIR; case gkfs::preload::RelativizeStatus::internal: - LOG(WARNING, "{}() not supported", __func__); - return -ENOTSUP; + // LOG(WARNING, "{}() not supported", __func__); + break; default: LOG(ERROR, "{}() relativize status unknown", __func__); @@ -890,8 +890,8 @@ hook_renameat(int olddfd, const char* oldname, int newdfd, const char* newname, return -ENOTDIR; case gkfs::preload::RelativizeStatus::internal: - LOG(WARNING, "{}() not supported", __func__); - return -ENOTSUP; + return with_errno(gkfs::syscall::gkfs_rename(oldpath_resolved, + newpath_resolved)); default: LOG(ERROR, "{}() relativize status unknown", __func__); diff --git a/src/client/rpc/forward_metadata.cpp b/src/client/rpc/forward_metadata.cpp index dd76a638e..ab9ecb384 100644 --- a/src/client/rpc/forward_metadata.cpp +++ b/src/client/rpc/forward_metadata.cpp @@ -341,6 +341,116 @@ forward_update_metadentry( } } +/** + * Send an RPC for a rename metadentry request. + * Steps.. SetUp a blkcnt of -1 + * This marks that this file doesn't have to be accessed directly + * Create a new md with the new name, which should have as value the old name + * All operations should check blockcnt and extract a NOTEXISTS + * @param path + * @param path2 + * @param md + * + * @return error code + */ +int +forward_rename(const string& path, const string& path2, + const gkfs::metadata::Metadata& md) { + + auto endp = CTX->hosts().at(CTX->distributor()->locate_file_metadata(path)); + + try { + LOG(DEBUG, "Sending RPC ..."); + // TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we + // can retry for RPC_TRIES (see old commits with margo) + // TODO(amiranda): hermes will eventually provide a post(endpoint) + // returning one result and a broadcast(endpoint_set) returning a + // result_set. When that happens we can remove the .at(0) :/ + auto out = ld_network_service + ->post( + endp, path, (md.link_count()), + /* mode */ 0, + /* uid */ 0, + /* gid */ 0, md.size(), + /* blockcnt */ -1, (md.atime()), + (md.mtime()), (md.ctime()), + bool_to_merc_bool(md.link_count()), + /* mode_flag */ false, + bool_to_merc_bool(md.size()), 1, + bool_to_merc_bool(md.atime()), + bool_to_merc_bool(md.mtime()), + bool_to_merc_bool(md.ctime())) + .get() + .at(0); + + LOG(DEBUG, "Got response success: {}", out.err()); + + // Now create the new file + + } catch(const std::exception& ex) { + LOG(ERROR, "while getting rpc output"); + return EBUSY; + } + + auto md2 = md; + + md2.target_path(path); + /* + * Now create the new file + */ + // TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we + // can retry for RPC_TRIES (see old commits with margo) + // TODO(amiranda): hermes will eventually provide a post(endpoint) + // returning one result and a broadcast(endpoint_set) returning a + // result_set. When that happens we can remove the .at(0) :/ + auto endp2 = + CTX->hosts().at(CTX->distributor()->locate_file_metadata(path2)); + + try { + LOG(DEBUG, "Sending RPC ..."); + // TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we + // can retry for RPC_TRIES (see old commits with margo) + // TODO(amiranda): hermes will eventually provide a post(endpoint) + // returning one result and a broadcast(endpoint_set) returning a + // result_set. When that happens we can remove the .at(0) :/ + + + auto out = ld_network_service + ->post(endp2, path2, md2.mode()) + .get() + .at(0); + LOG(DEBUG, "Got response success: {}", out.err()); + + + } catch(const std::exception& ex) { + LOG(ERROR, "while getting rpc output"); + return EBUSY; + } + + + try { + LOG(DEBUG, "Sending RPC ..."); + // TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we + // can retry for RPC_TRIES (see old commits with margo) + // TODO(amiranda): hermes will eventually provide a post(endpoint) + // returning one result and a broadcast(endpoint_set) returning a + // result_set. When that happens we can remove the .at(0) :/ + auto out = ld_network_service + ->post(endp, path2, path) + .get() + .at(0); + + LOG(DEBUG, "Got response success: {}", out.err()); + + // return out.err() ? out.err() : 0; + return 0; + } catch(const std::exception& ex) { + LOG(ERROR, "while getting rpc output"); + return EBUSY; + } +} + + /** * Send an RPC request for an update to the file size. * This is called during a write() call or similar diff --git a/src/common/metadata.cpp b/src/common/metadata.cpp index d75f6681b..43d5a1537 100644 --- a/src/common/metadata.cpp +++ b/src/common/metadata.cpp @@ -109,7 +109,7 @@ Metadata::Metadata(const std::string& binary_str) { // encounter a // delimiter anymore assert(*ptr == MSP); - blocks_ = static_cast(std::stoul(++ptr, &read)); + blocks_ = static_cast(std::stol(++ptr, &read)); assert(read > 0); ptr += read; } @@ -119,7 +119,7 @@ Metadata::Metadata(const std::string& binary_str) { assert(*ptr == MSP); target_path_ = ++ptr; // target_path should be there only if this is a link - assert(target_path_.empty() || S_ISLNK(mode_)); + // assert(target_path_.empty() || S_ISLNK(mode_)); ptr += target_path_.size(); #endif @@ -260,16 +260,16 @@ Metadata::blocks(blkcnt_t blocks) { std::string Metadata::target_path() const { - assert(!target_path_.empty()); + // assert(!target_path_.empty()); return target_path_; } void Metadata::target_path(const std::string& target_path) { // target_path should be there only if this is a link - assert(target_path.empty() || S_ISLNK(mode_)); + // assert(target_path.empty() || S_ISLNK(mode_)); // target_path should be absolute - assert(target_path.empty() || target_path[0] == '/'); + // assert(target_path.empty() || target_path[0] == '/'); target_path_ = target_path; } diff --git a/src/daemon/handler/srv_metadata.cpp b/src/daemon/handler/srv_metadata.cpp index eb1045628..5fb720c39 100644 --- a/src/daemon/handler/srv_metadata.cpp +++ b/src/daemon/handler/srv_metadata.cpp @@ -852,18 +852,19 @@ rpc_srv_mk_symlink(hg_handle_t handle) { } GKFS_DATA->spdlogger()->debug("{}() Got RPC with path '{}'", __func__, in.path); - + // do update try { - gkfs::metadata::Metadata md = {gkfs::metadata::LINK_MODE, - in.target_path}; - // create metadentry - gkfs::metadata::create(in.path, md); + gkfs::metadata::Metadata md = gkfs::metadata::get(in.path); + md.target_path(in.target_path); + gkfs::metadata::update(in.path, md); out.err = 0; } catch(const std::exception& e) { - GKFS_DATA->spdlogger()->error("{}() Failed to create metadentry: '{}'", - __func__, e.what()); - out.err = -1; + // TODO handle NotFoundException + GKFS_DATA->spdlogger()->error("{}() Failed to update entry", __func__); + out.err = 1; } + + GKFS_DATA->spdlogger()->debug("{}() Sending output err '{}'", __func__, out.err); auto hret = margo_respond(handle, &out); diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 775cfe4f3..2fd88be7f 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -125,6 +125,10 @@ gkfs_add_python_test( PYTHON_VERSION 3.6 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration SOURCE syscalls/ + NAME test_rename + PYTHON_VERSION 3.6 + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration + SOURCE rename/ ) if(GKFS_INSTALL_TESTS) @@ -214,7 +218,13 @@ if(GKFS_INSTALL_TESTS) ) endif () - + install(DIRECTORY rename + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration + FILES_MATCHING + REGEX ".*\\.py" + PATTERN "__pycache__" EXCLUDE + PATTERN ".pytest_cache" EXCLUDE + ) endif() diff --git a/tests/integration/harness/CMakeLists.txt b/tests/integration/harness/CMakeLists.txt index 39bd95bb2..25440c4a4 100644 --- a/tests/integration/harness/CMakeLists.txt +++ b/tests/integration/harness/CMakeLists.txt @@ -67,6 +67,7 @@ add_executable(gkfs.io gkfs.io/statfs.cpp gkfs.io/dup_validate.cpp gkfs.io/syscall_coverage.cpp + gkfs.io/rename.cpp ) include(FetchContent) diff --git a/tests/integration/harness/gkfs.io/commands.hpp b/tests/integration/harness/gkfs.io/commands.hpp index c57ef5c40..8acb30da9 100644 --- a/tests/integration/harness/gkfs.io/commands.hpp +++ b/tests/integration/harness/gkfs.io/commands.hpp @@ -116,6 +116,7 @@ symlink_init(CLI::App& app); void unlink_init(CLI::App& app); +rename_init(CLI::App& app); void dup_validate_init(CLI::App& app); diff --git a/tests/integration/harness/gkfs.io/main.cpp b/tests/integration/harness/gkfs.io/main.cpp index 91514bba3..91911d500 100644 --- a/tests/integration/harness/gkfs.io/main.cpp +++ b/tests/integration/harness/gkfs.io/main.cpp @@ -67,6 +67,7 @@ init_commands(CLI::App& app) { unlink_init(app); dup_validate_init(app); syscall_coverage_init(app); + rename_init(app); } diff --git a/tests/integration/harness/gkfs.io/rename.cpp b/tests/integration/harness/gkfs.io/rename.cpp new file mode 100644 index 000000000..c47a23251 --- /dev/null +++ b/tests/integration/harness/gkfs.io/rename.cpp @@ -0,0 +1,110 @@ +/* + Copyright 2018-2021, Barcelona Supercomputing Center (BSC), Spain + Copyright 2015-2021, 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 . + + SPDX-License-Identifier: GPL-3.0-or-later +*/ + +/* C++ includes */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* C includes */ +#include +#include +#include +#include + +using json = nlohmann::json; + +struct rename_options { + bool verbose{}; + std::string pathname; + std::string pathname2; + + REFL_DECL_STRUCT(rename_options, REFL_DECL_MEMBER(bool, verbose), + REFL_DECL_MEMBER(std::string, pathname), + REFL_DECL_MEMBER(std::string, pathname2)); +}; + +struct rename_output { + ::off_t retval; + int errnum; + + REFL_DECL_STRUCT(rename_output, REFL_DECL_MEMBER(::off_t, retval), + REFL_DECL_MEMBER(int, errnum)); +}; + +void +to_json(json& record, const rename_output& out) { + record = serialize(out); +} + + +void +rename_exec(const rename_options& opts) { + + int fd = ::rename(opts.pathname.c_str(), opts.pathname2.c_str()); + + if(fd == -1) { + if(opts.verbose) { + fmt::print("rename(pathname=\"{}\", pathname2=\"{}\") = {}, errno: {} [{}]\n", + opts.pathname, opts.pathname2, fd, errno, ::strerror(errno)); + return; + } + } + + json out = rename_output{fd, errno}; + fmt::print("{}\n", out.dump(2)); + + return; +} + +void +rename_init(CLI::App& app) { + + // Create the option and subcommand objects + auto opts = std::make_shared(); + auto* cmd = app.add_subcommand("rename", "Execute the rename() system call"); + + // Add options to cmd, binding them to opts + cmd->add_flag("-v,--verbose", opts->verbose, + "Produce human writeable output"); + + cmd->add_option("pathname", opts->pathname, "old name") + ->required() + ->type_name(""); + + cmd->add_option("pathname2", opts->pathname2, "new name") + ->required() + ->type_name(""); + + cmd->callback([opts]() { rename_exec(*opts); }); +} diff --git a/tests/integration/harness/io.py b/tests/integration/harness/io.py index 834174a41..b596bc056 100644 --- a/tests/integration/harness/io.py +++ b/tests/integration/harness/io.py @@ -465,6 +465,14 @@ class FileCompareOutputSchema(Schema): def make_object(self, data, **kwargs): return namedtuple('FileCompareReturn', ['retval', 'errno'])(**data) +class RenameOutputSchema(Schema): + """Schema to deserialize the results of an lrename() execution""" + retval = fields.Integer(required=True) + errno = Errno(data_key='errnum', required=True) + + @post_load + def make_object(self, data, **kwargs): + return namedtuple('RenameReturn', ['retval', 'errno'])(**data) class IOParser: @@ -500,6 +508,7 @@ class IOParser: 'symlink' : SymlinkOutputSchema(), 'dup_validate' : DupValidateOutputSchema(), 'syscall_coverage' : SyscallCoverageOutputSchema(), + 'rename' : RenameOutputSchema(), } def parse(self, command, output): diff --git a/tests/integration/rename/test_rename_operation.py b/tests/integration/rename/test_rename_operation.py new file mode 100644 index 000000000..e1b1a9caf --- /dev/null +++ b/tests/integration/rename/test_rename_operation.py @@ -0,0 +1,135 @@ +################################################################################ +# Copyright 2018-2022, Barcelona Supercomputing Center (BSC), Spain # +# Copyright 2015-2022, 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 . # +# # +# SPDX-License-Identifier: GPL-3.0-or-later # +################################################################################ + +import harness +from pathlib import Path +import errno +import stat +import os +import ctypes +import sys +import pytest +from harness.logger import logger + +nonexisting = "nonexisting" + + +def test_rename(gkfs_daemon, gkfs_client): + + file = gkfs_daemon.mountdir / "file" + file2 = gkfs_daemon.mountdir / "file2" + + # create a file in gekkofs + ret = gkfs_client.open(file, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 10000 + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(file, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + ret = gkfs_client.stat(file) + assert ret.retval != 1 + + ret = gkfs_client.stat(file2) + assert ret.retval == -1 + + ret = gkfs_client.rename(file, file2) + + assert ret.retval == 0 + + ret = gkfs_client.stat(file) + assert ret.retval == -1 + + ret = gkfs_client.stat(file2) + assert ret.retval != 1 + + # File is renamed, and innacesible + + # Read contents of file2 + ret = gkfs_client.open(file2, os.O_RDONLY) + assert ret.retval == 10000 + + ret = gkfs_client.read(file2, len(buf)) + assert ret.retval == len(buf) + assert ret.buf == buf + + + +def test_rename_inverse(gkfs_daemon, gkfs_client): + + file3 = gkfs_daemon.mountdir / "file3" + file4 = gkfs_daemon.mountdir / "file4" + + # create a file in gekkofs + ret = gkfs_client.open(file3, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 10000 + + + + ret = gkfs_client.stat(file3) + assert ret.retval != 1 + + ret = gkfs_client.stat(file4) + assert ret.retval == -1 + + ret = gkfs_client.rename(file3, file4) + + assert ret.retval == 0 + + ret = gkfs_client.stat(file3) + assert ret.retval == -1 + + ret = gkfs_client.stat(file4) + assert ret.retval != 1 + + # File is renamed, and innacesible + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(file4, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + # Read contents of file2 + ret = gkfs_client.open(file4, os.O_RDONLY) + assert ret.retval == 10000 + + ret = gkfs_client.read(file4, len(buf)) + assert ret.retval == len(buf) + assert ret.buf == buf + + # It should work but the data should be on file 2 really + -- GitLab From a83f8926800f0cbd9a1f5a838313d4dbadd5503b Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Wed, 6 Apr 2022 14:22:07 +0200 Subject: [PATCH 02/15] IFdef HAS_RENAME ERROR to DEBUG --- CMakeLists.txt | 6 ++++ include/client/gkfs_functions.hpp | 4 +-- include/client/rpc/forward_metadata.hpp | 2 ++ include/config.hpp | 5 +++ src/client/gkfs_functions.cpp | 44 ++++++++++++++++--------- src/client/hooks.cpp | 5 ++- src/client/rpc/forward_metadata.cpp | 2 ++ src/daemon/handler/srv_metadata.cpp | 2 +- 8 files changed, 50 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a41ef5e4..b68125c07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,6 +153,12 @@ if (SYMLINK_SUPPORT) endif () message(STATUS "[gekkofs] Symlink support: ${SYMLINK_SUPPORT}") +option(RENAME_SUPPORT "Compile with support for rename ops" ON) +if (RENAME_SUPPORT) + add_definitions(-DHAS_RENAME) +endif () +message(STATUS "[gekkofs] Rename support: ${RENAME_SUPPORT}") + set(MAX_INTERNAL_FDS 256 CACHE STRING "Number of file descriptors reserved for internal use") add_definitions(-DMAX_INTERNAL_FDS=${MAX_INTERNAL_FDS}) message(STATUS "[gekkofs] File descriptors reserved for internal use: ${MAX_INTERNAL_FDS}") diff --git a/include/client/gkfs_functions.hpp b/include/client/gkfs_functions.hpp index 273a606ba..147222f0c 100644 --- a/include/client/gkfs_functions.hpp +++ b/include/client/gkfs_functions.hpp @@ -149,11 +149,11 @@ gkfs_getdents64(unsigned int fd, struct linux_dirent64* dirp, int gkfs_rmdir(const std::string& path); - +#ifdef HAS_RENAME int gkfs_rename(const std::string& old_path_resolved, const std::string& new_path_resolved); - +#endif } // namespace gkfs::syscall // gkfs_getsingleserverdir is using extern "C" to demangle it for C usage diff --git a/include/client/rpc/forward_metadata.hpp b/include/client/rpc/forward_metadata.hpp index 7060e1efc..870deb901 100644 --- a/include/client/rpc/forward_metadata.hpp +++ b/include/client/rpc/forward_metadata.hpp @@ -55,9 +55,11 @@ forward_create(const std::string& path, mode_t mode); int forward_stat(const std::string& path, std::string& attr); +#ifdef HAS_RENAME int forward_rename(const std::string& path, const std::string& path2, const gkfs::metadata::Metadata& md); +#endif int forward_remove(const std::string& path); diff --git a/include/config.hpp b/include/config.hpp index 49f8bf1c2..0b9473118 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -62,11 +62,16 @@ namespace metadata { constexpr auto dir = "metadata"; // which metadata should be considered apart from size and mode +// Blocks are used to store the rename status (-1 is a renamed file) constexpr auto use_atime = false; constexpr auto use_ctime = false; constexpr auto use_mtime = false; constexpr auto use_link_cnt = false; +#ifdef HAS_RENAME constexpr auto use_blocks = true; +#else +constexpr auto use_blocks = false; +#endif /* * If true, all chunks on the same host are removed during a metadata remove * rpc. This is a technical optimization that reduces the number of RPCs for diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index d25bdb03b..45bbd8e20 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -172,12 +172,13 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { return -1; } md = *md_; - - if(rename == false && md.blocks() == -1) { - LOG(ERROR, "File is renamed '{}': '{}' - rename: {}", path, - rename); - return -1; - } + #ifdef HAS_RENAME + if(rename == false && md.blocks() == -1) { + LOG(DEBUG, "File is renamed '{}': '{}' - rename: {}", path, + rename); + return -1; + } + #endif } else { LOG(ERROR, "Error creating file: '{}'", strerror(errno)); return -1; @@ -208,25 +209,23 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { } return gkfs_open(md.target_path(), mode, flags); } - +#endif +#ifdef HAS_RENAME /// The file is a renamed file, so we need to get the metadata of the /// original file. - /// This does not work as we will check that this is a -1. if(!md.target_path().empty()) { - LOG(ERROR, "File '{}' is renamed, reentering with '{}'", path, + LOG(DEBUG, "File '{}' is renamed, reentering with '{}'", path, md.target_path()); return gkfs_open(md.target_path(), mode, flags, true); } -#endif - if(rename == false && md.blocks() == -1) { - LOG(ERROR, "File '{}' is renamed __", path); + LOG(DEBUG, "File '{}' is renamed __", path); errno = ENOENT; return -1; } - +#endif if(S_ISDIR(md.mode())) { return gkfs_opendir(path); } @@ -332,6 +331,7 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { return 0; } +#ifdef HAS_RENAME /** * gkfs wrapper for rename() system calls * errno may be set @@ -358,6 +358,7 @@ gkfs_rename(const string& old_path, const string& new_path) { return 0; } +#endif /** * gkfs wrapper for stat() system calls @@ -370,9 +371,15 @@ gkfs_rename(const string& old_path, const string& new_path) { int gkfs_stat(const string& path, struct stat* buf, bool follow_links) { auto md = gkfs::utils::get_metadata(path, follow_links); - if(!md or md.value().blocks() == -1) { + if(!md) { return -1; } + #ifdef HAS_RENAME + if (md.value().blocks() == -1) { + errno = ENOENT; + return -1; + } + #endif gkfs::utils::metadata_to_stat(path, *md, *buf); return 0; } @@ -395,10 +402,15 @@ gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask, struct statx* buf, bool follow_links) { auto md = gkfs::utils::get_metadata(path, follow_links); - if(!md or md.value().blocks() == -1) { + if(!md) { return -1; } - + #ifdef HAS_RENAME + if (md.value().blocks() == -1) { + errno = ENOENT; + return -1; + } + #endif struct stat tmp {}; gkfs::utils::metadata_to_stat(path, *md, tmp); diff --git a/src/client/hooks.cpp b/src/client/hooks.cpp index d4b0665cd..2000d8e58 100644 --- a/src/client/hooks.cpp +++ b/src/client/hooks.cpp @@ -890,9 +890,12 @@ hook_renameat(int olddfd, const char* oldname, int newdfd, const char* newname, return -ENOTDIR; case gkfs::preload::RelativizeStatus::internal: + #ifdef HAS_RENAME return with_errno(gkfs::syscall::gkfs_rename(oldpath_resolved, newpath_resolved)); - + #else + return -ENOTSUP; + #endif default: LOG(ERROR, "{}() relativize status unknown", __func__); return -EINVAL; diff --git a/src/client/rpc/forward_metadata.cpp b/src/client/rpc/forward_metadata.cpp index ab9ecb384..184e11d50 100644 --- a/src/client/rpc/forward_metadata.cpp +++ b/src/client/rpc/forward_metadata.cpp @@ -341,6 +341,7 @@ forward_update_metadentry( } } +#ifdef HAS_RENAME /** * Send an RPC for a rename metadentry request. * Steps.. SetUp a blkcnt of -1 @@ -450,6 +451,7 @@ forward_rename(const string& path, const string& path2, } } +#endif /** * Send an RPC request for an update to the file size. diff --git a/src/daemon/handler/srv_metadata.cpp b/src/daemon/handler/srv_metadata.cpp index 5fb720c39..dc7706d2d 100644 --- a/src/daemon/handler/srv_metadata.cpp +++ b/src/daemon/handler/srv_metadata.cpp @@ -828,7 +828,7 @@ rpc_srv_get_dirents_extended(hg_handle_t handle) { return gkfs::rpc::cleanup_respond(&handle, &in, &out, &bulk_handle); } -#ifdef HAS_SYMLINKS +#if defined(HAS_SYMLINKS) || defined(HAS_RENAME) /** * @brief Serves a request create a symbolic link. This function is UNUSED. * @internal -- GitLab From aac5b4ae70f7320c0fcf8cf6eacd3a5545343054 Mon Sep 17 00:00:00 2001 From: rnou Date: Tue, 10 May 2022 13:36:25 +0200 Subject: [PATCH 03/15] rename plus unlink md typo Format --- src/client/gkfs_functions.cpp | 57 ++++ tests/integration/harness/CMakeLists.txt | 1 + tests/integration/harness/gkfs.io/main.cpp | 1 + tests/integration/harness/io.py | 11 +- .../rename/test_rename_operation.py | 248 ++++++++++++++++++ 5 files changed, 317 insertions(+), 1 deletion(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 45bbd8e20..60ce5f7f2 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -224,6 +224,39 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { LOG(DEBUG, "File '{}' is renamed __", path); errno = ENOENT; return -1; + } else { + if(md.target_path() != "") { + auto md_ = gkfs::utils::get_metadata(md.target_path()); + new_path = md.target_path(); + while(md_.value().target_path() != "") { + new_path = md_.value().target_path(); + md_ = gkfs::utils::get_metadata(md_.value().target_path(), + false); + if(!md_) { + return -1; + } + } + md = *md_; + // Code is replicated, to avoid changing the const std::string path + // to a non-const + if(S_ISDIR(md.mode())) { + return gkfs_opendir(new_path); + } + + + /*** Regular file exists ***/ + assert(S_ISREG(md.mode())); + + if((flags & O_TRUNC) && ((flags & O_RDWR) || (flags & O_WRONLY))) { + if(gkfs_truncate(new_path, md.size(), 0)) { + LOG(ERROR, "Error truncating file"); + return -1; + } + } + + return CTX->file_map()->add( + std::make_shared(new_path, flags)); + } } #endif if(S_ISDIR(md.mode())) { @@ -306,6 +339,30 @@ gkfs_remove(const std::string& path) { return -1; } +#ifdef HAS_RENAME + if(md.value().blocks() == -1) { + errno = ENOENT; + return -1; + } else { + if(md.value().target_path() != "") { + auto md_ = gkfs::utils::get_metadata(md.value().target_path()); + std::string new_path = md.value().target_path(); + while(md.value().target_path() != "") { + new_path = md.value().target_path(); + md = gkfs::utils::get_metadata(md.value().target_path(), false); + if(!md) { + return -1; + } + } + auto err = gkfs::rpc::forward_remove(new_path); + if(err) { + errno = err; + return -1; + } + return 0; + } + } +#endif auto err = gkfs::rpc::forward_remove(path); if(err) { errno = err; diff --git a/tests/integration/harness/CMakeLists.txt b/tests/integration/harness/CMakeLists.txt index 25440c4a4..29bf09cde 100644 --- a/tests/integration/harness/CMakeLists.txt +++ b/tests/integration/harness/CMakeLists.txt @@ -68,6 +68,7 @@ add_executable(gkfs.io gkfs.io/dup_validate.cpp gkfs.io/syscall_coverage.cpp gkfs.io/rename.cpp + gkfs.io/unlink.cpp ) include(FetchContent) diff --git a/tests/integration/harness/gkfs.io/main.cpp b/tests/integration/harness/gkfs.io/main.cpp index 91911d500..c17fca7e5 100644 --- a/tests/integration/harness/gkfs.io/main.cpp +++ b/tests/integration/harness/gkfs.io/main.cpp @@ -68,6 +68,7 @@ init_commands(CLI::App& app) { dup_validate_init(app); syscall_coverage_init(app); rename_init(app); + unlink_init(app); } diff --git a/tests/integration/harness/io.py b/tests/integration/harness/io.py index b596bc056..fde1c877c 100644 --- a/tests/integration/harness/io.py +++ b/tests/integration/harness/io.py @@ -466,7 +466,7 @@ class FileCompareOutputSchema(Schema): return namedtuple('FileCompareReturn', ['retval', 'errno'])(**data) class RenameOutputSchema(Schema): - """Schema to deserialize the results of an lrename() execution""" + """Schema to deserialize the results of an rename() execution""" retval = fields.Integer(required=True) errno = Errno(data_key='errnum', required=True) @@ -474,6 +474,14 @@ class RenameOutputSchema(Schema): def make_object(self, data, **kwargs): return namedtuple('RenameReturn', ['retval', 'errno'])(**data) +class UnlinkOutputSchema(Schema): + """Schema to deserialize the results of an unlink() execution""" + retval = fields.Integer(required=True) + errno = Errno(data_key='errnum', required=True) + + @post_load + def make_object(self, data, **kwargs): + return namedtuple('UnlinkReturn', ['retval', 'errno'])(**data) class IOParser: @@ -509,6 +517,7 @@ class IOParser: 'dup_validate' : DupValidateOutputSchema(), 'syscall_coverage' : SyscallCoverageOutputSchema(), 'rename' : RenameOutputSchema(), + 'unlink' : UnlinkOutputSchema(), } def parse(self, command, output): diff --git a/tests/integration/rename/test_rename_operation.py b/tests/integration/rename/test_rename_operation.py index e1b1a9caf..c23993e55 100644 --- a/tests/integration/rename/test_rename_operation.py +++ b/tests/integration/rename/test_rename_operation.py @@ -133,3 +133,251 @@ def test_rename_inverse(gkfs_daemon, gkfs_client): # It should work but the data should be on file 2 really +def test_chain_rename(gkfs_daemon, gkfs_client): + + filea = gkfs_daemon.mountdir / "filea" + fileb = gkfs_daemon.mountdir / "fileb" + filec = gkfs_daemon.mountdir / "filec" + filed = gkfs_daemon.mountdir / "filed" + filee = gkfs_daemon.mountdir / "filee" + + # create a file in gekkofs + ret = gkfs_client.open(filea, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 10000 + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(filea, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + ret = gkfs_client.stat(filea) + assert ret.retval != 1 + + ret = gkfs_client.stat(fileb) + assert ret.retval == -1 + + ret = gkfs_client.rename(filea, fileb) + + assert ret.retval == 0 + + ret = gkfs_client.stat(filea) + assert ret.retval == -1 + + ret = gkfs_client.stat(fileb) + assert ret.retval != 1 + + # File is renamed, and innacesible + + # Read contents of file2 + ret = gkfs_client.open(fileb, os.O_RDONLY) + assert ret.retval == 10000 + + ret = gkfs_client.read(fileb, len(buf)) + assert ret.retval == len(buf) + assert ret.buf == buf + + ret = gkfs_client.rename(filea, filec) + + #filea should be gone + assert ret.retval == -1 + + ret = gkfs_client.rename(fileb, filec) + assert ret.retval == 0 + + ret = gkfs_client.read(filec, len(buf)) + assert ret.retval == len(buf) + assert ret.buf == buf + + ret = gkfs_client.rename(filec, filed) + assert ret.retval == 0 + + ret = gkfs_client.read(filed, len(buf)) + assert ret.retval == len(buf) + assert ret.buf == buf + + + ret = gkfs_client.rename(filed, filee) + assert ret.retval == 0 + + ret = gkfs_client.read(filee, len(buf)) + assert ret.retval == len(buf) + assert ret.buf == buf + + + # check the stat of old files + ret = gkfs_client.stat(fileb) + assert ret.retval == -1 + + ret = gkfs_client.stat(filec) + assert ret.retval == -1 + + ret = gkfs_client.stat(filed) + assert ret.retval == -1 + + ret = gkfs_client.stat(filee) + assert ret.retval == 0 + +def test_cyclic_rename(gkfs_daemon, gkfs_client): + + fileold = gkfs_daemon.mountdir / "fileold" + filenew = gkfs_daemon.mountdir / "filenew" + + + # create a file in gekkofs + ret = gkfs_client.open(fileold, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 10000 + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(fileold, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + ret = gkfs_client.stat(fileold) + assert ret.retval != 1 + + ret = gkfs_client.stat(filenew) + assert ret.retval == -1 + + ret = gkfs_client.rename(fileold, filenew) + + assert ret.retval == 0 + + ret = gkfs_client.stat(fileold) + assert ret.retval == -1 + + ret = gkfs_client.stat(filenew) + assert ret.retval != 1 + + + #Cyclic rename + ret = gkfs_client.rename(filenew, fileold) + + ret = gkfs_client.stat(fileold) + assert ret.retval != -1 + + ret = gkfs_client.stat(filenew) + assert ret.retval == -1 + # Read contents of filee + ret = gkfs_client.open(fileold, os.O_RDONLY) + assert ret.retval == 10000 + + ret = gkfs_client.read(fileold, len(buf)) + assert ret.retval == len(buf) + assert ret.buf == buf + +def test_rename_plus_trunc(gkfs_daemon, gkfs_client): + + fileold = gkfs_daemon.mountdir / "fileoldtr" + filenew = gkfs_daemon.mountdir / "filenewtr" + + + # create a file in gekkofs + ret = gkfs_client.open(fileold, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 10000 + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(fileold, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + # rename file + ret = gkfs_client.rename(fileold, filenew) + assert ret.retval == 0 + + ret = gkfs_client.stat(filenew) + assert ret.retval != -1 + assert ret.statbuf.st_size == len(buf) + + # truncate file + ret = gkfs_client.truncate(filenew, 1) + assert ret.retval == 0 + + ret = gkfs_client.read(filenew, len(buf)) + assert ret.retval == 1 + assert ret.buf == b'4\x00' # buf includes the null byte + + ret = gkfs_client.stat(filenew) + assert ret.retval != -1 + assert ret.statbuf.st_size == 1 + +def test_rename_plus_lseek(gkfs_daemon, gkfs_client): + + fileold = gkfs_daemon.mountdir / "fileoldlseek" + filenew = gkfs_daemon.mountdir / "filenewlseek" + + + # create a file in gekkofs + ret = gkfs_client.open(fileold, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 10000 + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(fileold, buf, len(buf)) + + assert ret.retval == len(buf) # Return the number of written bytes + + # rename file + ret = gkfs_client.rename(fileold, filenew) + assert ret.retval == 0 + + ret = gkfs_client.stat(filenew) + assert ret.retval != -1 + assert ret.statbuf.st_size == len(buf) + + ret = gkfs_client.lseek(filenew, 0, os.SEEK_END) + assert ret.retval == 2 #Two bytes written + + + +def test_rename_delete(gkfs_daemon, gkfs_client): + + fileold = gkfs_daemon.mountdir / "fileoldrename" + filenew = gkfs_daemon.mountdir / "filenewrename" + + + # create a file in gekkofs + ret = gkfs_client.open(fileold, + os.O_CREAT | os.O_WRONLY, + stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) + + assert ret.retval == 10000 + + # write a buffer we know + buf = b'42' + ret = gkfs_client.write(fileold, buf, len(buf)) + assert ret.retval == len(buf) # Return the number of written bytes + + # rename file + ret = gkfs_client.rename(fileold, filenew) + assert ret.retval == 0 + + ret = gkfs_client.stat(filenew) + assert ret.retval != -1 + assert ret.statbuf.st_size == len(buf) + + ret = gkfs_client.unlink(fileold) # Remove original file (error) + assert ret.retval != 0 + + ret = gkfs_client.unlink(filenew) # Remove renamed file (success) + assert ret.retval == 0 + + ret = gkfs_client.stat(filenew) + assert ret.retval == -1 + + + -- GitLab From cb1f9344914f17b765dc456c28d17173cafc17d4 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Wed, 11 May 2022 09:34:18 +0200 Subject: [PATCH 04/15] Format --- src/client/gkfs_functions.cpp | 133 ++++++++++++++++++++++++++-------- src/client/hooks.cpp | 6 +- 2 files changed, 104 insertions(+), 35 deletions(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 60ce5f7f2..3db70ba83 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -172,13 +172,13 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { return -1; } md = *md_; - #ifdef HAS_RENAME - if(rename == false && md.blocks() == -1) { - LOG(DEBUG, "File is renamed '{}': '{}' - rename: {}", path, - rename); - return -1; - } - #endif +#ifdef HAS_RENAME + if(rename == false && md.blocks() == -1) { + LOG(DEBUG, "File is renamed '{}': '{}' - rename: {}", path, + rename); + return -1; + } +#endif } else { LOG(ERROR, "Error creating file: '{}'", strerror(errno)); return -1; @@ -200,6 +200,7 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { md = *md_; } + #ifdef HAS_SYMLINKS if(md.is_link()) { if(flags & O_NOFOLLOW) { @@ -207,21 +208,12 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { errno = ELOOP; return -1; } - return gkfs_open(md.target_path(), mode, flags); + return gkfs_open(md.target_path(), mode, flags, true); } #endif #ifdef HAS_RENAME - - /// The file is a renamed file, so we need to get the metadata of the - /// original file. - if(!md.target_path().empty()) { - LOG(DEBUG, "File '{}' is renamed, reentering with '{}'", path, - md.target_path()); - return gkfs_open(md.target_path(), mode, flags, true); - } - - if(rename == false && md.blocks() == -1) { - LOG(DEBUG, "File '{}' is renamed __", path); + std::string new_path = path; + if(md.blocks() == -1) { errno = ENOENT; return -1; } else { @@ -237,8 +229,6 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { } } md = *md_; - // Code is replicated, to avoid changing the const std::string path - // to a non-const if(S_ISDIR(md.mode())) { return gkfs_opendir(new_path); } @@ -363,6 +353,7 @@ gkfs_remove(const std::string& path) { } } #endif + auto err = gkfs::rpc::forward_remove(path); if(err) { errno = err; @@ -392,6 +383,9 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { /** * gkfs wrapper for rename() system calls * errno may be set + * We use blocks to determine if the file is a renamed file. + * If the file is re-renamed (a->b->a) a recovers the block of b + * and we delete b. * @param old_path * @param new_path * @return 0 on success, -1 on failure @@ -399,11 +393,49 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { int gkfs_rename(const string& old_path, const string& new_path) { auto md = gkfs::utils::get_metadata(old_path, false); - if(!md) { + + // if the file is not found, or it is a renamed one cancel. + if(!md or md.value().blocks() == -1) { return -1; } auto md2 = gkfs::utils::get_metadata(new_path, false); if(md2) { + // the new file exists... check for circular... + if(md2.value().blocks() == -1 and + md.value().target_path() == new_path) { + // the new file is a renamed file, so we need to get the metadata of + // the original file. + LOG(DEBUG, "Destroying Circular Rename '{}' --> '{}'", old_path, + new_path); + gkfs::metadata::MetadentryUpdateFlags flags; + flags.atime = false; + flags.mtime = false; + flags.ctime = false; + flags.blocks = true; + flags.mode = false; + flags.size = false; + flags.uid = false; + flags.gid = false; + flags.link_count = false; + md.value().blocks(0); + md.value().target_path(""); + + auto err = gkfs::rpc::forward_update_metadentry(new_path, + md.value(), flags); + + if(err) { + errno = err; + return -1; + } + // Delete old file + err = gkfs::rpc::forward_remove(old_path); + if(err) { + errno = err; + return -1; + } + return 0; + } + return -1; } @@ -431,12 +463,19 @@ gkfs_stat(const string& path, struct stat* buf, bool follow_links) { if(!md) { return -1; } - #ifdef HAS_RENAME - if (md.value().blocks() == -1) { - errno = ENOENT; - return -1; +#ifdef HAS_RENAME + if(md.value().blocks() == -1) { + errno = ENOENT; + return -1; + } else { + while(md.value().target_path() != "") { + md = gkfs::utils::get_metadata(md.value().target_path(), false); + if(!md) { + return -1; + } } - #endif + } +#endif gkfs::utils::metadata_to_stat(path, *md, *buf); return 0; } @@ -462,12 +501,19 @@ gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask, if(!md) { return -1; } - #ifdef HAS_RENAME - if (md.value().blocks() == -1) { - errno = ENOENT; - return -1; +#ifdef HAS_RENAME + if(md.value().blocks() == -1) { + errno = ENOENT; + return -1; + } else { + while(md.value().target_path() != "") { + md = gkfs::utils::get_metadata(md.value().target_path(), false); + if(!md) { + return -1; + } } - #endif + } +#endif struct stat tmp {}; gkfs::utils::metadata_to_stat(path, *md, tmp); @@ -697,6 +743,29 @@ gkfs_truncate(const std::string& path, off_t length) { return -1; } + // If we have rename enabled we need to check if the file is renamed +#ifdef HAS_RENAME + if(md.value().blocks() == -1) { + errno = ENOENT; + return -1; + } else if(md.value().target_path() != "") { + std::string new_path; + while(md.value().target_path() != "") { + new_path = md.value().target_path(); + md = gkfs::utils::get_metadata(md.value().target_path()); + } + // This could be optimized + auto size = md->size(); + if(static_cast(length) > size) { + LOG(DEBUG, "Length is greater then file size: {} > {}", length, + size); + errno = EINVAL; + return -1; + } + return gkfs_truncate(new_path, size, length); + } +#endif + auto size = md->size(); if(static_cast(length) > size) { LOG(DEBUG, "Length is greater then file size: '{}' > '{}'", length, diff --git a/src/client/hooks.cpp b/src/client/hooks.cpp index 2000d8e58..8874e06b9 100644 --- a/src/client/hooks.cpp +++ b/src/client/hooks.cpp @@ -890,12 +890,12 @@ hook_renameat(int olddfd, const char* oldname, int newdfd, const char* newname, return -ENOTDIR; case gkfs::preload::RelativizeStatus::internal: - #ifdef HAS_RENAME +#ifdef HAS_RENAME return with_errno(gkfs::syscall::gkfs_rename(oldpath_resolved, newpath_resolved)); - #else +#else return -ENOTSUP; - #endif +#endif default: LOG(ERROR, "{}() relativize status unknown", __func__); return -EINVAL; -- GitLab From 6e1e69bd05919f07f0c769e17c1f3bf1c8dd9193 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Tue, 17 May 2022 14:42:51 +0200 Subject: [PATCH 05/15] rebase errors --- tests/integration/harness/CMakeLists.txt | 1 - tests/integration/harness/gkfs.io/commands.hpp | 5 ++++- tests/integration/harness/gkfs.io/main.cpp | 1 - tests/integration/harness/io.py | 13 ++----------- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/tests/integration/harness/CMakeLists.txt b/tests/integration/harness/CMakeLists.txt index 29bf09cde..25440c4a4 100644 --- a/tests/integration/harness/CMakeLists.txt +++ b/tests/integration/harness/CMakeLists.txt @@ -68,7 +68,6 @@ add_executable(gkfs.io gkfs.io/dup_validate.cpp gkfs.io/syscall_coverage.cpp gkfs.io/rename.cpp - gkfs.io/unlink.cpp ) include(FetchContent) diff --git a/tests/integration/harness/gkfs.io/commands.hpp b/tests/integration/harness/gkfs.io/commands.hpp index 8acb30da9..ab1c83f1a 100644 --- a/tests/integration/harness/gkfs.io/commands.hpp +++ b/tests/integration/harness/gkfs.io/commands.hpp @@ -116,7 +116,6 @@ symlink_init(CLI::App& app); void unlink_init(CLI::App& app); -rename_init(CLI::App& app); void dup_validate_init(CLI::App& app); @@ -124,4 +123,8 @@ dup_validate_init(CLI::App& app); void syscall_coverage_init(CLI::App& app); +void +rename_init(CLI::App& app); + + #endif // IO_COMMANDS_HPP diff --git a/tests/integration/harness/gkfs.io/main.cpp b/tests/integration/harness/gkfs.io/main.cpp index c17fca7e5..91911d500 100644 --- a/tests/integration/harness/gkfs.io/main.cpp +++ b/tests/integration/harness/gkfs.io/main.cpp @@ -68,7 +68,6 @@ init_commands(CLI::App& app) { dup_validate_init(app); syscall_coverage_init(app); rename_init(app); - unlink_init(app); } diff --git a/tests/integration/harness/io.py b/tests/integration/harness/io.py index fde1c877c..d6a6c0ddc 100644 --- a/tests/integration/harness/io.py +++ b/tests/integration/harness/io.py @@ -474,15 +474,6 @@ class RenameOutputSchema(Schema): def make_object(self, data, **kwargs): return namedtuple('RenameReturn', ['retval', 'errno'])(**data) -class UnlinkOutputSchema(Schema): - """Schema to deserialize the results of an unlink() execution""" - retval = fields.Integer(required=True) - errno = Errno(data_key='errnum', required=True) - - @post_load - def make_object(self, data, **kwargs): - return namedtuple('UnlinkReturn', ['retval', 'errno'])(**data) - class IOParser: OutputSchemas = { @@ -509,6 +500,7 @@ class IOParser: 'unlink' : UnlinkOutputSchema(), 'access' : AccessOutputSchema(), 'statfs' : StatfsOutputSchema(), + 'rename' : RenameOutputSchema(), # UTIL 'file_compare': FileCompareOutputSchema(), 'chdir' : ChdirOutputSchema(), @@ -516,8 +508,7 @@ class IOParser: 'symlink' : SymlinkOutputSchema(), 'dup_validate' : DupValidateOutputSchema(), 'syscall_coverage' : SyscallCoverageOutputSchema(), - 'rename' : RenameOutputSchema(), - 'unlink' : UnlinkOutputSchema(), + } def parse(self, command, output): -- GitLab From aa69c39b03b26bde4e87567f72b6ec1b2bd11c08 Mon Sep 17 00:00:00 2001 From: rnou Date: Tue, 28 Jun 2022 13:32:45 +0200 Subject: [PATCH 06/15] Modify ext to int rename test --- src/client/hooks.cpp | 8 ++++++-- tests/integration/CMakeLists.txt | 7 +++++++ .../harness/gkfs.io/syscall_coverage.cpp | 14 +++++++++----- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/client/hooks.cpp b/src/client/hooks.cpp index 8874e06b9..5498cdfb3 100644 --- a/src/client/hooks.cpp +++ b/src/client/hooks.cpp @@ -891,8 +891,12 @@ hook_renameat(int olddfd, const char* oldname, int newdfd, const char* newname, case gkfs::preload::RelativizeStatus::internal: #ifdef HAS_RENAME - return with_errno(gkfs::syscall::gkfs_rename(oldpath_resolved, - newpath_resolved)); + if(oldpath_status == gkfs::preload::RelativizeStatus::internal) { + return with_errno(gkfs::syscall::gkfs_rename(oldpath_resolved, + newpath_resolved)); + } else { + return -ENOTSUP; + } #else return -ENOTSUP; #endif diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 2fd88be7f..95f4b246d 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -125,11 +125,16 @@ gkfs_add_python_test( PYTHON_VERSION 3.6 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration SOURCE syscalls/ +) + +if (RENAME_SUPPORT) +gkfs_add_python_test( NAME test_rename PYTHON_VERSION 3.6 WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests/integration SOURCE rename/ ) +endif() if(GKFS_INSTALL_TESTS) install(DIRECTORY harness @@ -218,6 +223,7 @@ if(GKFS_INSTALL_TESTS) ) endif () + if (RENAME_SUPPORT) install(DIRECTORY rename DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration FILES_MATCHING @@ -225,6 +231,7 @@ if(GKFS_INSTALL_TESTS) PATTERN "__pycache__" EXCLUDE PATTERN ".pytest_cache" EXCLUDE ) + endif() endif() diff --git a/tests/integration/harness/gkfs.io/syscall_coverage.cpp b/tests/integration/harness/gkfs.io/syscall_coverage.cpp index ddb25cf5d..e07668ff2 100644 --- a/tests/integration/harness/gkfs.io/syscall_coverage.cpp +++ b/tests/integration/harness/gkfs.io/syscall_coverage.cpp @@ -366,21 +366,25 @@ syscall_coverage_exec(const syscall_coverage_options& opts) { return; } + std::string pid = std::to_string(getpid()); + std::string path1 = "/tmp/"+pid+"test_rename"; + std::string path2 = "/tmp/"+pid+"test_rename2"; + // renameat external auto fdtmp = ::open("/tmp/test_rename", O_CREAT | O_WRONLY, 0644); ::close(fdtmp); - rv = ::renameat(AT_FDCWD, "/tmp/test_rename", AT_FDCWD, + rv = ::renameat(AT_FDCWD, path1.c_str(), AT_FDCWD, opts.pathname.c_str()); if(errno != ENOTSUP) { - output("renameat", rv, opts); + output("renameat_ext_to_int", rv, opts); return; } - rv = ::renameat(AT_FDCWD, "/tmp/test_rename", AT_FDCWD, - "/tmp/test_rename2"); + rv = ::renameat(AT_FDCWD, path1.c_str(), AT_FDCWD, + path2.c_str()); if(rv < 0) { - output("renameat", rv, opts); + output("renameat_ext_to_ext", rv, opts); return; } // sys_open -- GitLab From c3f9622486dd8c9a0afbd2f2a813df7c170e56b3 Mon Sep 17 00:00:00 2001 From: rnou Date: Thu, 21 Jul 2022 14:00:09 +0200 Subject: [PATCH 07/15] Bad name in rename test --- tests/integration/harness/gkfs.io/syscall_coverage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/harness/gkfs.io/syscall_coverage.cpp b/tests/integration/harness/gkfs.io/syscall_coverage.cpp index e07668ff2..9c7a55467 100644 --- a/tests/integration/harness/gkfs.io/syscall_coverage.cpp +++ b/tests/integration/harness/gkfs.io/syscall_coverage.cpp @@ -371,7 +371,7 @@ syscall_coverage_exec(const syscall_coverage_options& opts) { std::string path2 = "/tmp/"+pid+"test_rename2"; // renameat external - auto fdtmp = ::open("/tmp/test_rename", O_CREAT | O_WRONLY, 0644); + auto fdtmp = ::open(path1.c_str(), O_CREAT | O_WRONLY, 0644); ::close(fdtmp); rv = ::renameat(AT_FDCWD, path1.c_str(), AT_FDCWD, -- GitLab From 7449381020f00c78a6959034bdf3d7a7a5bdceee Mon Sep 17 00:00:00 2001 From: rnou Date: Thu, 15 Sep 2022 10:46:56 +0200 Subject: [PATCH 08/15] NeedHAS_SYMLINKS+HAS_RENAME --- src/client/gkfs_functions.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 3db70ba83..64afccae3 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -210,7 +210,6 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { } return gkfs_open(md.target_path(), mode, flags, true); } -#endif #ifdef HAS_RENAME std::string new_path = path; if(md.blocks() == -1) { @@ -248,6 +247,7 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { std::make_shared(new_path, flags)); } } +#endif #endif if(S_ISDIR(md.mode())) { return gkfs_opendir(path); @@ -328,7 +328,7 @@ gkfs_remove(const std::string& path) { errno = EISDIR; return -1; } - +#ifdef HAS_SYMLINKS #ifdef HAS_RENAME if(md.value().blocks() == -1) { errno = ENOENT; @@ -352,6 +352,7 @@ gkfs_remove(const std::string& path) { return 0; } } +#endif #endif auto err = gkfs::rpc::forward_remove(path); @@ -379,6 +380,7 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { return 0; } +#ifdef HAS_SYMLINKS #ifdef HAS_RENAME /** * gkfs wrapper for rename() system calls @@ -448,6 +450,7 @@ gkfs_rename(const string& old_path, const string& new_path) { return 0; } #endif +#endif /** * gkfs wrapper for stat() system calls @@ -501,6 +504,7 @@ gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask, if(!md) { return -1; } +#ifdef HAS_SYMLINKS #ifdef HAS_RENAME if(md.value().blocks() == -1) { errno = ENOENT; @@ -513,6 +517,7 @@ gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask, } } } +#endif #endif struct stat tmp {}; @@ -744,6 +749,7 @@ gkfs_truncate(const std::string& path, off_t length) { } // If we have rename enabled we need to check if the file is renamed +#ifdef HAS_SYMLINKS #ifdef HAS_RENAME if(md.value().blocks() == -1) { errno = ENOENT; @@ -764,6 +770,7 @@ gkfs_truncate(const std::string& path, off_t length) { } return gkfs_truncate(new_path, size, length); } +#endif #endif auto size = md->size(); -- GitLab From 6722e4fe2d31c69ab01c975f6f3ac58c95d099df Mon Sep 17 00:00:00 2001 From: rnou Date: Thu, 6 Oct 2022 13:29:46 +0200 Subject: [PATCH 09/15] update access (debug) --- src/client/gkfs_functions.cpp | 28 +++++++++++++++++++ .../rename/test_rename_operation.py | 8 ++++++ 2 files changed, 36 insertions(+) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index 64afccae3..daefc0c21 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -373,10 +373,36 @@ gkfs_remove(const std::string& path) { */ int gkfs_access(const std::string& path, const int mask, bool follow_links) { + LOG(DEBUG, "Checking '{}'", path); auto md = gkfs::utils::get_metadata(path, follow_links); + LOG(DEBUG, "Checked '{}'", path); if(!md) { + LOG(DEBUG, "File does not exist '{}'", path); return -1; } +#ifdef HAS_SYMLINKS +#ifdef HAS_RENAME + LOG(DEBUG, "Checking for renamed file '{}'", path); + if(md.value().blocks() == -1) { + errno = ENOENT; + LOG(DEBUG, "File exist but it is renamed '{}'", path); + return -1; + + } else { + while(md.value().target_path() != "") { + LOG(DEBUG, "File exist but it is renamed '{} -> {}'", path, + md.value().target_path()); + md = gkfs::utils::get_metadata(md.value().target_path(), false); + if(!md) { + LOG(DEBUG, + "File does not al all exist but it is renamed '{} -> {}'", + path, md.value().target_path()); + return -1; + } + } + } +#endif +#endif return 0; } @@ -466,6 +492,7 @@ gkfs_stat(const string& path, struct stat* buf, bool follow_links) { if(!md) { return -1; } +#ifdef HAS_SYMLINKS #ifdef HAS_RENAME if(md.value().blocks() == -1) { errno = ENOENT; @@ -478,6 +505,7 @@ gkfs_stat(const string& path, struct stat* buf, bool follow_links) { } } } +#endif #endif gkfs::utils::metadata_to_stat(path, *md, *buf); return 0; diff --git a/tests/integration/rename/test_rename_operation.py b/tests/integration/rename/test_rename_operation.py index c23993e55..f8d2c6811 100644 --- a/tests/integration/rename/test_rename_operation.py +++ b/tests/integration/rename/test_rename_operation.py @@ -50,6 +50,9 @@ def test_rename(gkfs_daemon, gkfs_client): stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) assert ret.retval == 10000 + + ret = gkfs_client.access(file, os.R_OK) + assert ret.retval == 0 # write a buffer we know buf = b'42' @@ -64,7 +67,12 @@ def test_rename(gkfs_daemon, gkfs_client): assert ret.retval == -1 ret = gkfs_client.rename(file, file2) + assert ret.retval == 0 + + ret = gkfs_client.access(file, os.R_OK) + assert ret.retval == -1 + ret = gkfs_client.access(file2, os.R_OK) assert ret.retval == 0 ret = gkfs_client.stat(file) -- GitLab From ce9e7bd40689eb72c24be0ffafa2656c7b0d3c61 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Wed, 14 Dec 2022 08:37:02 +0000 Subject: [PATCH 10/15] Bug fix of renamed unlink --- src/client/gkfs_functions.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index daefc0c21..ce1baea84 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -349,7 +349,6 @@ gkfs_remove(const std::string& path) { errno = err; return -1; } - return 0; } } #endif -- GitLab From 5a0a43939fa58f5a53f7340a97f973f6687f3326 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Mon, 19 Dec 2022 14:18:13 +0100 Subject: [PATCH 11/15] Updated tests and dirents for rename --- .../backend/metadata/parallax_backend.cpp | 18 ++++++++++++++++++ .../backend/metadata/rocksdb_backend.cpp | 12 ++++++++++++ .../rename/test_rename_operation.py | 6 ++++++ 3 files changed, 36 insertions(+) diff --git a/src/daemon/backend/metadata/parallax_backend.cpp b/src/daemon/backend/metadata/parallax_backend.cpp index 4cb02b8c8..484f58da4 100644 --- a/src/daemon/backend/metadata/parallax_backend.cpp +++ b/src/daemon/backend/metadata/parallax_backend.cpp @@ -415,6 +415,15 @@ ParallaxBackend::get_dirents_impl(const std::string& dir) const { assert(!name.empty()); Metadata md(v); +#ifdef HAS_RENAME + // Remove entries with negative blocks (rename) + if(md.blocks() == -1) { + if(par_get_next(S) && !par_is_valid(S)) + break; + else + continue; + } +#endif // HAS_RENAME auto is_dir = S_ISDIR(md.mode()); entries.emplace_back(std::move(name), is_dir); @@ -489,6 +498,15 @@ ParallaxBackend::get_dirents_extended_impl(const std::string& dir) const { assert(!name.empty()); Metadata md(v); +#ifdef HAS_RENAME + // Remove entries with negative blocks (rename) + if(md.blocks() == -1) { + if(par_get_next(S) && !par_is_valid(S)) + break; + else + continue; + } +#endif // HAS_RENAME auto is_dir = S_ISDIR(md.mode()); entries.emplace_back(std::forward_as_tuple(std::move(name), is_dir, diff --git a/src/daemon/backend/metadata/rocksdb_backend.cpp b/src/daemon/backend/metadata/rocksdb_backend.cpp index 0a4deca4d..99a0263d1 100644 --- a/src/daemon/backend/metadata/rocksdb_backend.cpp +++ b/src/daemon/backend/metadata/rocksdb_backend.cpp @@ -265,6 +265,12 @@ RocksDBBackend::get_dirents_impl(const std::string& dir) const { assert(!name.empty()); Metadata md(it->value().ToString()); +#ifdef HAS_RENAME + // Remove entries with negative blocks (rename) + if(md.blocks() == -1) { + continue; + } +#endif // HAS_RENAME auto is_dir = S_ISDIR(md.mode()); entries.emplace_back(std::move(name), is_dir); @@ -309,6 +315,12 @@ RocksDBBackend::get_dirents_extended_impl(const std::string& dir) const { assert(!name.empty()); Metadata md(it->value().ToString()); +#ifdef HAS_RENAME + // Remove entries with negative blocks (rename) + if(md.blocks() == -1) { + continue; + } +#endif // HAS_RENAME auto is_dir = S_ISDIR(md.mode()); entries.emplace_back(std::forward_as_tuple(std::move(name), is_dir, diff --git a/tests/integration/rename/test_rename_operation.py b/tests/integration/rename/test_rename_operation.py index f8d2c6811..97ab88b11 100644 --- a/tests/integration/rename/test_rename_operation.py +++ b/tests/integration/rename/test_rename_operation.py @@ -378,6 +378,9 @@ def test_rename_delete(gkfs_daemon, gkfs_client): assert ret.retval != -1 assert ret.statbuf.st_size == len(buf) + ret = gkfs_client.readdir(gkfs_daemon.mountdir) + assert len(ret.dirents) == 1 + ret = gkfs_client.unlink(fileold) # Remove original file (error) assert ret.retval != 0 @@ -386,6 +389,9 @@ def test_rename_delete(gkfs_daemon, gkfs_client): ret = gkfs_client.stat(filenew) assert ret.retval == -1 + + ret = gkfs_client.readdir(gkfs_daemon.mountdir) + assert len(ret.dirents) == 0 -- GitLab From b78d96a56bc47bba1a3ab3d8cb1933a78b6bcfc8 Mon Sep 17 00:00:00 2001 From: Ramon Nou Date: Thu, 22 Dec 2022 14:48:16 +0100 Subject: [PATCH 12/15] Rename + flock (not supported, but enabled) --- CHANGELOG.md | 2 ++ README.md | 3 ++ include/client/hooks.hpp | 3 ++ include/common/metadata.hpp | 12 ++++++++ src/client/hooks.cpp | 27 +++++++++++++++++ src/client/intercept.cpp | 5 ++++ src/client/rpc/forward_metadata.cpp | 45 ++++++++++++++++++++++------- src/common/metadata.cpp | 28 ++++++++++++++++++ src/daemon/handler/srv_metadata.cpp | 12 +++++++- 9 files changed, 125 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6818f1da8..fd5b88c7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Support for increasing file size via `truncate()` added ([!159](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/159) - Added PowerPC support ([!151](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/151)). +- RENAME_SUPPORT adds support for renaming files. It includes the use case of renaming opened files using the fd +- FLOCK and fcntl functions for locks, are not supported, but they are available. ### Changed diff --git a/README.md b/README.md index 696aebdc9..52077365b 100644 --- a/README.md +++ b/README.md @@ -287,6 +287,9 @@ instead or in addition to the output file. It must be enabled at compile time vi argument `-DGKFS_ENABLE_PROMETHEUS` and the daemon argument `--enable-prometheus`. The corresponding statistics are then pushed to the Prometheus instance. +### Rename +`-DRENAME_SUPPORT` allows the application to rename files. This is an experimental feature, and some scenarios may not +work properly. Support for fstat in renamed files is included. ### Acknowledgment This software was partially supported by the EC H2020 funded NEXTGenIO project (Project ID: 671951, www.nextgenio.eu). diff --git a/include/client/hooks.hpp b/include/client/hooks.hpp index 6871d8b79..95d0e7516 100644 --- a/include/client/hooks.hpp +++ b/include/client/hooks.hpp @@ -120,6 +120,9 @@ hook_unlinkat(int dirfd, const char* cpath, int flags); int hook_symlinkat(const char* oldname, int newdfd, const char* newname); +int +hook_flock(unsigned long fd, int flags); + int hook_access(const char* path, int mask); diff --git a/include/common/metadata.hpp b/include/common/metadata.hpp index 7939e5c47..22d004111 100644 --- a/include/common/metadata.hpp +++ b/include/common/metadata.hpp @@ -53,6 +53,10 @@ private: blkcnt_t blocks_{}; // allocated file system blocks_ #ifdef HAS_SYMLINKS std::string target_path_; // For links this is the path of the target file +#ifdef HAS_RENAME + std::string rename_path_; // In some cases fd is maintained so we need the + // renamed path +#endif #endif @@ -133,6 +137,14 @@ public: bool is_link() const; +#ifdef HAS_RENAME + std::string + rename_path() const; + + void + rename_path(const std::string& rename_path); +#endif + #endif }; diff --git a/src/client/hooks.cpp b/src/client/hooks.cpp index 5498cdfb3..d68af9e42 100644 --- a/src/client/hooks.cpp +++ b/src/client/hooks.cpp @@ -178,6 +178,16 @@ hook_fstat(unsigned int fd, struct stat* buf) { if(CTX->file_map()->exist(fd)) { auto path = CTX->file_map()->get(fd)->path(); +#ifdef HAS_RENAME + // Special case for fstat and rename, fd points new file... + // We can change file_map and recall + + auto md = gkfs::utils::get_metadata(path, false); + if(md.has_value() and md.value().blocks() == -1) { + path = md.value().rename_path(); + std::cout << " Fstat " << path << std::endl; + } +#endif return with_errno(gkfs::syscall::gkfs_stat(path, buf)); } return syscall_no_intercept_wrapper(SYS_fstat, fd, buf); @@ -395,6 +405,16 @@ hook_symlinkat(const char* oldname, int newdfd, const char* newname) { } } +int +hook_flock(unsigned long fd, int flags) { + LOG(ERROR, "{}() called flock (Not Supported) with fd: {}, flags: {}", + __func__, fd, flags); + + if(CTX->file_map()->exist(fd)) { + return 0; + } else + return -EBADF; +} int hook_access(const char* path, int mask) { @@ -831,6 +851,13 @@ hook_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) { gkfs::filemap::OpenFile_flags::cloexec, (arg & FD_CLOEXEC)); return 0; + case F_GETLK: + LOG(ERROR, "{}() F_GETLK on fd (Not Supported) {}", __func__, fd); + return 0; + + case F_SETLK: + LOG(ERROR, "{}() F_SETLK on fd (Not Supported) {}", __func__, fd); + return 0; default: LOG(ERROR, "{}() unrecognized command {} on fd {}", __func__, cmd, diff --git a/src/client/intercept.cpp b/src/client/intercept.cpp index fce6e79c6..f962d97bb 100644 --- a/src/client/intercept.cpp +++ b/src/client/intercept.cpp @@ -683,6 +683,10 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, static_cast(arg2)); break; + case SYS_flock: + *result = gkfs::hook::hook_flock(static_cast(arg0), + static_cast(arg1)); + break; case SYS_chdir: *result = gkfs::hook::hook_chdir(reinterpret_cast(arg0)); @@ -747,6 +751,7 @@ hook(long syscall_number, long arg0, long arg1, long arg2, long arg3, long arg4, reinterpret_cast(arg1)); break; + case SYS_fdatasync: case SYS_fsync: *result = gkfs::hook::hook_fsync(static_cast(arg0)); break; diff --git a/src/client/rpc/forward_metadata.cpp b/src/client/rpc/forward_metadata.cpp index 184e11d50..a087590c8 100644 --- a/src/client/rpc/forward_metadata.cpp +++ b/src/client/rpc/forward_metadata.cpp @@ -348,17 +348,18 @@ forward_update_metadentry( * This marks that this file doesn't have to be accessed directly * Create a new md with the new name, which should have as value the old name * All operations should check blockcnt and extract a NOTEXISTS - * @param path - * @param path2 + * @param oldpath + * @param newpath * @param md * * @return error code */ int -forward_rename(const string& path, const string& path2, +forward_rename(const string& oldpath, const string& newpath, const gkfs::metadata::Metadata& md) { - auto endp = CTX->hosts().at(CTX->distributor()->locate_file_metadata(path)); + auto endp = + CTX->hosts().at(CTX->distributor()->locate_file_metadata(oldpath)); try { LOG(DEBUG, "Sending RPC ..."); @@ -369,7 +370,7 @@ forward_rename(const string& path, const string& path2, // result_set. When that happens we can remove the .at(0) :/ auto out = ld_network_service ->post( - endp, path, (md.link_count()), + endp, oldpath, (md.link_count()), /* mode */ 0, /* uid */ 0, /* gid */ 0, md.size(), @@ -395,7 +396,7 @@ forward_rename(const string& path, const string& path2, auto md2 = md; - md2.target_path(path); + md2.target_path(oldpath); /* * Now create the new file */ @@ -405,7 +406,7 @@ forward_rename(const string& path, const string& path2, // returning one result and a broadcast(endpoint_set) returning a // result_set. When that happens we can remove the .at(0) :/ auto endp2 = - CTX->hosts().at(CTX->distributor()->locate_file_metadata(path2)); + CTX->hosts().at(CTX->distributor()->locate_file_metadata(newpath)); try { LOG(DEBUG, "Sending RPC ..."); @@ -415,20 +416,42 @@ forward_rename(const string& path, const string& path2, // returning one result and a broadcast(endpoint_set) returning a // result_set. When that happens we can remove the .at(0) :/ - auto out = ld_network_service - ->post(endp2, path2, md2.mode()) + ->post(endp2, newpath, md2.mode()) .get() .at(0); LOG(DEBUG, "Got response success: {}", out.err()); - } catch(const std::exception& ex) { LOG(ERROR, "while getting rpc output"); return EBUSY; } + try { + LOG(DEBUG, "Sending RPC ..."); + // TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we + // can retry for RPC_TRIES (see old commits with margo) + // TODO(amiranda): hermes will eventually provide a post(endpoint) + // returning one result and a broadcast(endpoint_set) returning a + // result_set. When that happens we can remove the .at(0) :/ + // Update new file with target link = oldpath + auto out = + ld_network_service + ->post(endp2, newpath, oldpath) + .get() + .at(0); + + LOG(DEBUG, "Got response success: {}", out.err()); + + // return out.err() ? out.err() : 0; + + } catch(const std::exception& ex) { + LOG(ERROR, "while getting rpc output"); + return EBUSY; + } + + // Update the renamed path to solve the issue with fstat with fd) try { LOG(DEBUG, "Sending RPC ..."); // TODO(amiranda): add a post() with RPC_TIMEOUT to hermes so that we @@ -437,7 +460,7 @@ forward_rename(const string& path, const string& path2, // returning one result and a broadcast(endpoint_set) returning a // result_set. When that happens we can remove the .at(0) :/ auto out = ld_network_service - ->post(endp, path2, path) + ->post(endp, oldpath, newpath) .get() .at(0); diff --git a/src/common/metadata.cpp b/src/common/metadata.cpp index 43d5a1537..4a12e8bc8 100644 --- a/src/common/metadata.cpp +++ b/src/common/metadata.cpp @@ -121,6 +121,18 @@ Metadata::Metadata(const std::string& binary_str) { // target_path should be there only if this is a link // assert(target_path_.empty() || S_ISLNK(mode_)); ptr += target_path_.size(); +#ifdef HAS_RENAME + // Read rename target, we had captured '|' so we need to recover it + if(!target_path_.empty()) { + auto index = target_path_.find_last_of(MSP); + auto size = target_path_.size(); + target_path_ = target_path_.substr(0, index); + ptr -= (size - index); + } + assert(*ptr == MSP); + rename_path_ = ++ptr; + ptr += rename_path_.size(); +#endif #endif // we consumed all the binary string @@ -158,6 +170,10 @@ Metadata::serialize() const { #ifdef HAS_SYMLINKS s += MSP; s += target_path_; +#ifdef HAS_RENAME + s += MSP; + s += rename_path_; +#endif #endif return s; @@ -278,6 +294,18 @@ Metadata::is_link() const { return S_ISLNK(mode_); } +#ifdef HAS_RENAME +std::string +Metadata::rename_path() const { + return rename_path_; +} + +void +Metadata::rename_path(const std::string& rename_path) { + rename_path_ = rename_path; +} + +#endif #endif } // namespace gkfs::metadata diff --git a/src/daemon/handler/srv_metadata.cpp b/src/daemon/handler/srv_metadata.cpp index dc7706d2d..ca6e06d68 100644 --- a/src/daemon/handler/srv_metadata.cpp +++ b/src/daemon/handler/srv_metadata.cpp @@ -855,7 +855,17 @@ rpc_srv_mk_symlink(hg_handle_t handle) { // do update try { gkfs::metadata::Metadata md = gkfs::metadata::get(in.path); - md.target_path(in.target_path); +#ifdef HAS_RENAME + if(md.blocks() == -1) { + // We need to fill the rename path as this is an inverse path + // old -> new + md.rename_path(in.target_path); + } else { +#endif + md.target_path(in.target_path); +#ifdef HAS_RENAME + } +#endif gkfs::metadata::update(in.path, md); out.err = 0; } catch(const std::exception& e) { -- GitLab From e2805cd30c7a73f6eb4408eaf5f0faf3909270c8 Mon Sep 17 00:00:00 2001 From: Marc Vef Date: Thu, 2 Mar 2023 16:27:26 +0100 Subject: [PATCH 13/15] Review: Comments, removed unused code, syntax --- README.md | 6 +- include/client/gkfs_functions.hpp | 7 +-- include/client/rpc/forward_metadata.hpp | 4 +- include/common/metadata.hpp | 4 +- include/config.hpp | 2 +- src/client/gkfs_functions.cpp | 84 ++++++++++++------------- src/client/hooks.cpp | 11 ++-- src/common/metadata.cpp | 18 ++---- src/daemon/handler/srv_metadata.cpp | 17 ++--- 9 files changed, 73 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 52077365b..5e2ba8d03 100644 --- a/README.md +++ b/README.md @@ -288,8 +288,10 @@ argument `-DGKFS_ENABLE_PROMETHEUS` and the daemon argument `--enable-prometheus pushed to the Prometheus instance. ### Rename -`-DRENAME_SUPPORT` allows the application to rename files. This is an experimental feature, and some scenarios may not -work properly. Support for fstat in renamed files is included. +`-DRENAME_SUPPORT` allows the application to rename files. +This is an experimental feature, and some scenarios may not work properly. +Support for fstat in renamed files is included. + ### Acknowledgment This software was partially supported by the EC H2020 funded NEXTGenIO project (Project ID: 671951, www.nextgenio.eu). diff --git a/include/client/gkfs_functions.hpp b/include/client/gkfs_functions.hpp index 147222f0c..437b052ef 100644 --- a/include/client/gkfs_functions.hpp +++ b/include/client/gkfs_functions.hpp @@ -41,7 +41,7 @@ struct linux_dirent64; namespace gkfs::syscall { int -gkfs_open(const std::string& path, mode_t mode, int flags, bool rename = false); +gkfs_open(const std::string& path, mode_t mode, int flags); int gkfs_create(const std::string& path, mode_t mode); @@ -151,9 +151,8 @@ gkfs_rmdir(const std::string& path); #ifdef HAS_RENAME int -gkfs_rename(const std::string& old_path_resolved, - const std::string& new_path_resolved); -#endif +gkfs_rename(const std::string& old_path, const std::string& new_path); +#endif // HAS_RENAME } // namespace gkfs::syscall // gkfs_getsingleserverdir is using extern "C" to demangle it for C usage diff --git a/include/client/rpc/forward_metadata.hpp b/include/client/rpc/forward_metadata.hpp index 870deb901..564dc903b 100644 --- a/include/client/rpc/forward_metadata.hpp +++ b/include/client/rpc/forward_metadata.hpp @@ -57,9 +57,9 @@ forward_stat(const std::string& path, std::string& attr); #ifdef HAS_RENAME int -forward_rename(const std::string& path, const std::string& path2, +forward_rename(const std::string& oldpath, const std::string& newpath, const gkfs::metadata::Metadata& md); -#endif +#endif // HAS_RENAME int forward_remove(const std::string& path); diff --git a/include/common/metadata.hpp b/include/common/metadata.hpp index 22d004111..6332b511a 100644 --- a/include/common/metadata.hpp +++ b/include/common/metadata.hpp @@ -143,9 +143,9 @@ public: void rename_path(const std::string& rename_path); -#endif +#endif // HAS_RENAME -#endif +#endif // HAS_SYMLINKS }; } // namespace gkfs::metadata diff --git a/include/config.hpp b/include/config.hpp index 0b9473118..ad162fb6a 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -71,7 +71,7 @@ constexpr auto use_link_cnt = false; constexpr auto use_blocks = true; #else constexpr auto use_blocks = false; -#endif +#endif // HAS_RENAME /* * If true, all chunks on the same host are removed during a metadata remove * rpc. This is a technical optimization that reduces the number of RPCs for diff --git a/src/client/gkfs_functions.cpp b/src/client/gkfs_functions.cpp index ce1baea84..1d664ed5e 100644 --- a/src/client/gkfs_functions.cpp +++ b/src/client/gkfs_functions.cpp @@ -130,7 +130,7 @@ namespace gkfs::syscall { * @return 0 on success, -1 on failure */ int -gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { +gkfs_open(const std::string& path, mode_t mode, int flags) { if(flags & O_PATH) { LOG(ERROR, "`O_PATH` flag is not supported"); @@ -173,12 +173,14 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { } md = *md_; #ifdef HAS_RENAME - if(rename == false && md.blocks() == -1) { - LOG(DEBUG, "File is renamed '{}': '{}' - rename: {}", path, - rename); + // This is an old file that was renamed which we do not open + if(md.blocks() == -1) { + LOG(DEBUG, + "This file was renamed and we do not open. path '{}'", + path); return -1; } -#endif +#endif // HAS_RENAME } else { LOG(ERROR, "Error creating file: '{}'", strerror(errno)); return -1; @@ -194,7 +196,7 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { if(errno != ENOENT) { LOG(ERROR, "Error stating existing file '{}'", path); } - // file doesn't exists and O_CREAT was not set + // file doesn't exist and O_CREAT was not set return -1; } md = *md_; @@ -208,18 +210,20 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { errno = ELOOP; return -1; } - return gkfs_open(md.target_path(), mode, flags, true); + return gkfs_open(md.target_path(), mode, flags); } #ifdef HAS_RENAME - std::string new_path = path; + auto new_path = path; if(md.blocks() == -1) { + // This is an old file that was renamed and essentially no longer exists errno = ENOENT; return -1; } else { - if(md.target_path() != "") { + if(!md.target_path().empty()) { + // get renamed path from target and retrieve metadata from it auto md_ = gkfs::utils::get_metadata(md.target_path()); new_path = md.target_path(); - while(md_.value().target_path() != "") { + while(!md_.value().target_path().empty()) { new_path = md_.value().target_path(); md_ = gkfs::utils::get_metadata(md_.value().target_path(), false); @@ -232,7 +236,6 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { return gkfs_opendir(new_path); } - /*** Regular file exists ***/ assert(S_ISREG(md.mode())); @@ -247,13 +250,12 @@ gkfs_open(const std::string& path, mode_t mode, int flags, bool rename) { std::make_shared(new_path, flags)); } } -#endif -#endif +#endif // HAS_RENAME +#endif // HAS_SYMLINKS if(S_ISDIR(md.mode())) { return gkfs_opendir(path); } - /*** Regular file exists ***/ assert(S_ISREG(md.mode())); @@ -334,10 +336,10 @@ gkfs_remove(const std::string& path) { errno = ENOENT; return -1; } else { - if(md.value().target_path() != "") { + if(!md.value().target_path().empty()) { auto md_ = gkfs::utils::get_metadata(md.value().target_path()); std::string new_path = md.value().target_path(); - while(md.value().target_path() != "") { + while(!md.value().target_path().empty()) { new_path = md.value().target_path(); md = gkfs::utils::get_metadata(md.value().target_path(), false); if(!md) { @@ -351,8 +353,8 @@ gkfs_remove(const std::string& path) { } } } -#endif -#endif +#endif // HAS_RENAME +#endif // HAS_SYMLINKS auto err = gkfs::rpc::forward_remove(path); if(err) { @@ -372,9 +374,7 @@ gkfs_remove(const std::string& path) { */ int gkfs_access(const std::string& path, const int mask, bool follow_links) { - LOG(DEBUG, "Checking '{}'", path); auto md = gkfs::utils::get_metadata(path, follow_links); - LOG(DEBUG, "Checked '{}'", path); if(!md) { LOG(DEBUG, "File does not exist '{}'", path); return -1; @@ -388,20 +388,19 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { return -1; } else { - while(md.value().target_path() != "") { + while(!md.value().target_path().empty()) { LOG(DEBUG, "File exist but it is renamed '{} -> {}'", path, md.value().target_path()); md = gkfs::utils::get_metadata(md.value().target_path(), false); if(!md) { - LOG(DEBUG, - "File does not al all exist but it is renamed '{} -> {}'", + LOG(DEBUG, "File does not exist but it is renamed '{} -> {}'", path, md.value().target_path()); return -1; } } } -#endif -#endif +#endif // HAS_RENAME +#endif // HAS_SYMLINKS return 0; } @@ -419,22 +418,22 @@ gkfs_access(const std::string& path, const int mask, bool follow_links) { */ int gkfs_rename(const string& old_path, const string& new_path) { - auto md = gkfs::utils::get_metadata(old_path, false); + auto md_old = gkfs::utils::get_metadata(old_path, false); // if the file is not found, or it is a renamed one cancel. - if(!md or md.value().blocks() == -1) { + if(!md_old || md_old.value().blocks() == -1) { return -1; } - auto md2 = gkfs::utils::get_metadata(new_path, false); - if(md2) { + auto md_new = gkfs::utils::get_metadata(new_path, false); + if(md_new) { // the new file exists... check for circular... - if(md2.value().blocks() == -1 and - md.value().target_path() == new_path) { + if(md_new.value().blocks() == -1 && + md_old.value().target_path() == new_path) { // the new file is a renamed file, so we need to get the metadata of // the original file. LOG(DEBUG, "Destroying Circular Rename '{}' --> '{}'", old_path, new_path); - gkfs::metadata::MetadentryUpdateFlags flags; + gkfs::metadata::MetadentryUpdateFlags flags{}; flags.atime = false; flags.mtime = false; flags.ctime = false; @@ -444,11 +443,11 @@ gkfs_rename(const string& old_path, const string& new_path) { flags.uid = false; flags.gid = false; flags.link_count = false; - md.value().blocks(0); - md.value().target_path(""); + md_old.value().blocks(0); + md_old.value().target_path(""); - auto err = gkfs::rpc::forward_update_metadentry(new_path, - md.value(), flags); + auto err = gkfs::rpc::forward_update_metadentry( + new_path, md_old.value(), flags); if(err) { errno = err; @@ -462,11 +461,10 @@ gkfs_rename(const string& old_path, const string& new_path) { } return 0; } - return -1; } - auto err = gkfs::rpc::forward_rename(old_path, new_path, md.value()); + auto err = gkfs::rpc::forward_rename(old_path, new_path, md_old.value()); if(err) { errno = err; return -1; @@ -497,7 +495,7 @@ gkfs_stat(const string& path, struct stat* buf, bool follow_links) { errno = ENOENT; return -1; } else { - while(md.value().target_path() != "") { + while(!md.value().target_path().empty()) { md = gkfs::utils::get_metadata(md.value().target_path(), false); if(!md) { return -1; @@ -537,7 +535,7 @@ gkfs_statx(int dirfs, const std::string& path, int flags, unsigned int mask, errno = ENOENT; return -1; } else { - while(md.value().target_path() != "") { + while(!md.value().target_path().empty()) { md = gkfs::utils::get_metadata(md.value().target_path(), false); if(!md) { return -1; @@ -775,15 +773,15 @@ gkfs_truncate(const std::string& path, off_t length) { return -1; } - // If we have rename enabled we need to check if the file is renamed + // If rename is enabled we need to check if the file is renamed #ifdef HAS_SYMLINKS #ifdef HAS_RENAME if(md.value().blocks() == -1) { errno = ENOENT; return -1; - } else if(md.value().target_path() != "") { + } else if(!md.value().target_path().empty()) { std::string new_path; - while(md.value().target_path() != "") { + while(!md.value().target_path().empty()) { new_path = md.value().target_path(); md = gkfs::utils::get_metadata(md.value().target_path()); } diff --git a/src/client/hooks.cpp b/src/client/hooks.cpp index d68af9e42..fa557a1f8 100644 --- a/src/client/hooks.cpp +++ b/src/client/hooks.cpp @@ -179,13 +179,11 @@ hook_fstat(unsigned int fd, struct stat* buf) { if(CTX->file_map()->exist(fd)) { auto path = CTX->file_map()->get(fd)->path(); #ifdef HAS_RENAME - // Special case for fstat and rename, fd points new file... - // We can change file_map and recall - + // Special case for fstat and rename, fd points to new file... + // We can change file_map and recall auto md = gkfs::utils::get_metadata(path, false); - if(md.has_value() and md.value().blocks() == -1) { + if(md.has_value() && md.value().blocks() == -1) { path = md.value().rename_path(); - std::cout << " Fstat " << path << std::endl; } #endif return with_errno(gkfs::syscall::gkfs_stat(path, buf)); @@ -407,7 +405,7 @@ hook_symlinkat(const char* oldname, int newdfd, const char* newname) { int hook_flock(unsigned long fd, int flags) { - LOG(ERROR, "{}() called flock (Not Supported) with fd: {}, flags: {}", + LOG(ERROR, "{}() called flock (Not Supported) with fd '{}' flags '{}'", __func__, fd, flags); if(CTX->file_map()->exist(fd)) { @@ -892,7 +890,6 @@ hook_renameat(int olddfd, const char* oldname, int newdfd, const char* newname, return -ENOTDIR; case gkfs::preload::RelativizeStatus::internal: - // LOG(WARNING, "{}() not supported", __func__); break; default: diff --git a/src/common/metadata.cpp b/src/common/metadata.cpp index 4a12e8bc8..d5836216a 100644 --- a/src/common/metadata.cpp +++ b/src/common/metadata.cpp @@ -119,7 +119,6 @@ Metadata::Metadata(const std::string& binary_str) { assert(*ptr == MSP); target_path_ = ++ptr; // target_path should be there only if this is a link - // assert(target_path_.empty() || S_ISLNK(mode_)); ptr += target_path_.size(); #ifdef HAS_RENAME // Read rename target, we had captured '|' so we need to recover it @@ -132,8 +131,8 @@ Metadata::Metadata(const std::string& binary_str) { assert(*ptr == MSP); rename_path_ = ++ptr; ptr += rename_path_.size(); -#endif -#endif +#endif // HAS_RENAME +#endif // HAS_SYMLINKS // we consumed all the binary string assert(*ptr == '\0'); @@ -173,8 +172,8 @@ Metadata::serialize() const { #ifdef HAS_RENAME s += MSP; s += rename_path_; -#endif -#endif +#endif // HAS_RENAME +#endif // HAS_SYMLINKS return s; } @@ -276,16 +275,11 @@ Metadata::blocks(blkcnt_t blocks) { std::string Metadata::target_path() const { - // assert(!target_path_.empty()); return target_path_; } void Metadata::target_path(const std::string& target_path) { - // target_path should be there only if this is a link - // assert(target_path.empty() || S_ISLNK(mode_)); - // target_path should be absolute - // assert(target_path.empty() || target_path[0] == '/'); target_path_ = target_path; } @@ -305,7 +299,7 @@ Metadata::rename_path(const std::string& rename_path) { rename_path_ = rename_path; } -#endif -#endif +#endif // HAS_RENAME +#endif // HAS_SYMLINKS } // namespace gkfs::metadata diff --git a/src/daemon/handler/srv_metadata.cpp b/src/daemon/handler/srv_metadata.cpp index ca6e06d68..7dcec929d 100644 --- a/src/daemon/handler/srv_metadata.cpp +++ b/src/daemon/handler/srv_metadata.cpp @@ -830,7 +830,7 @@ rpc_srv_get_dirents_extended(hg_handle_t handle) { #if defined(HAS_SYMLINKS) || defined(HAS_RENAME) /** - * @brief Serves a request create a symbolic link. This function is UNUSED. + * @brief Serves a request create a symbolic link and supports rename * @internal * The state of this function is unclear and requires a complete refactor. * @@ -850,8 +850,9 @@ rpc_srv_mk_symlink(hg_handle_t handle) { GKFS_DATA->spdlogger()->error( "{}() Failed to retrieve input from handle", __func__); } - GKFS_DATA->spdlogger()->debug("{}() Got RPC with path '{}'", __func__, - in.path); + GKFS_DATA->spdlogger()->debug( + "{}() Got RPC with path '{}' and target path '{}'", __func__, + in.path, in.target_path); // do update try { gkfs::metadata::Metadata md = gkfs::metadata::get(in.path); @@ -861,11 +862,14 @@ rpc_srv_mk_symlink(hg_handle_t handle) { // old -> new md.rename_path(in.target_path); } else { -#endif +#endif // HAS_RENAME md.target_path(in.target_path); #ifdef HAS_RENAME } -#endif +#endif // HAS_RENAME + GKFS_DATA->spdlogger()->debug( + "{}() Updating path '{}' with metadata '{}'", __func__, in.path, + md.serialize()); gkfs::metadata::update(in.path, md); out.err = 0; } catch(const std::exception& e) { @@ -874,7 +878,6 @@ rpc_srv_mk_symlink(hg_handle_t handle) { out.err = 1; } - GKFS_DATA->spdlogger()->debug("{}() Sending output err '{}'", __func__, out.err); auto hret = margo_respond(handle, &out); @@ -888,7 +891,7 @@ rpc_srv_mk_symlink(hg_handle_t handle) { return HG_SUCCESS; } -#endif +#endif // HAS_SYMLINKS || HAS_RENAME } // namespace -- GitLab From 3505bc9dbdfcd657671b9f467890eb03ecc414e8 Mon Sep 17 00:00:00 2001 From: Marc Vef Date: Thu, 2 Mar 2023 16:29:45 +0100 Subject: [PATCH 14/15] Disable symlinks and rename support by default --- CMakeLists.txt | 10 ++++++---- README.md | 17 +++++++++++------ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b68125c07..c61c58f78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,16 +147,18 @@ endif() option(CREATE_CHECK_PARENTS "Check parent directory existance before creating child node" ON) message(STATUS "[gekkofs] Create checks parents: ${CREATE_CHECK_PARENTS}") -option(SYMLINK_SUPPORT "Compile with support for symlinks" ON) +option(SYMLINK_SUPPORT "Compile with support for symlinks" OFF) if (SYMLINK_SUPPORT) add_definitions(-DHAS_SYMLINKS) endif () -message(STATUS "[gekkofs] Symlink support: ${SYMLINK_SUPPORT}") - -option(RENAME_SUPPORT "Compile with support for rename ops" ON) +option(RENAME_SUPPORT "Compile with support for rename ops" OFF) if (RENAME_SUPPORT) + # Rename depends on symlink support + add_definitions(-DHAS_SYMLINKS) + set(SYMLINK_SUPPORT ON) add_definitions(-DHAS_RENAME) endif () +message(STATUS "[gekkofs] Symlink support: ${SYMLINK_SUPPORT}") message(STATUS "[gekkofs] Rename support: ${RENAME_SUPPORT}") set(MAX_INTERNAL_FDS 256 CACHE STRING "Number of file descriptors reserved for internal use") diff --git a/README.md b/README.md index 5e2ba8d03..37665f5a8 100644 --- a/README.md +++ b/README.md @@ -270,7 +270,7 @@ Then, the `examples/distributors/guided/generate.py` scrpt is used to create the Finally, modify `guided_config.txt` to your distribution requirements. -### Metadata Backends +## Metadata Backends There are two different metadata backends in GekkoFS. The default one uses `rocksdb`, however an alternative based on `PARALLAX` from `FORTH` @@ -279,7 +279,7 @@ with `-DGKFS_ENABLE_ROCKSDB:BOOL=OFF`. Once it is enabled, `--dbbackend` option will be functional. -### Statistics +## Statistics GekkoFS daemons are able to output general operations (`--enable-collection`) and data chunk statistics (`--enable-chunkstats`) to a specified output file via `--output-stats `. Prometheus can also be used @@ -287,12 +287,17 @@ instead or in addition to the output file. It must be enabled at compile time vi argument `-DGKFS_ENABLE_PROMETHEUS` and the daemon argument `--enable-prometheus`. The corresponding statistics are then pushed to the Prometheus instance. +## Advanced experimental features + ### Rename -`-DRENAME_SUPPORT` allows the application to rename files. -This is an experimental feature, and some scenarios may not work properly. -Support for fstat in renamed files is included. -### Acknowledgment +`-DRENAME_SUPPORT` allows the application to rename files. +This is an experimental feature, and some scenarios may not work properly. +Support for fstat in renamed files is included. + +This is disabled by default. + +## Acknowledgment This software was partially supported by the EC H2020 funded NEXTGenIO project (Project ID: 671951, www.nextgenio.eu). -- GitLab From f1004a547259230b1bd1dd48d7f8f36534bb94eb Mon Sep 17 00:00:00 2001 From: Marc Vef Date: Fri, 3 Mar 2023 02:47:58 +0100 Subject: [PATCH 15/15] Adding rename support flag for CI cmake and cmake option renaming --- .gitlab-ci.yml | 1 + CHANGELOG.md | 2 +- CMakeLists.txt | 14 +++++++------- README.md | 2 +- tests/integration/CMakeLists.txt | 6 +++--- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ceaec224c..b8bd60cb4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -69,6 +69,7 @@ gkfs: -DGKFS_ENABLE_ROCKSDB:BOOL=ON -DGKFS_CHUNK_STATS:BOOL=ON -DGKFS_ENABLE_PROMETHEUS:BOOL=ON + -DGKFS_RENAME_SUPPORT:BOOL=ON ${CI_PROJECT_DIR} - make -j$(nproc) install # reduce artifacts size diff --git a/CHANGELOG.md b/CHANGELOG.md index fd5b88c7f..7a0debe6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Support for increasing file size via `truncate()` added ([!159](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/159) - Added PowerPC support ([!151](https://storage.bsc.es/gitlab/hpc/gekkofs/-/merge_requests/151)). -- RENAME_SUPPORT adds support for renaming files. It includes the use case of renaming opened files using the fd +- GKFS_RENAME_SUPPORT adds support for renaming files. It includes the use case of renaming opened files using the fd - FLOCK and fcntl functions for locks, are not supported, but they are available. ### Changed diff --git a/CMakeLists.txt b/CMakeLists.txt index c61c58f78..d26697861 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,19 +147,19 @@ endif() option(CREATE_CHECK_PARENTS "Check parent directory existance before creating child node" ON) message(STATUS "[gekkofs] Create checks parents: ${CREATE_CHECK_PARENTS}") -option(SYMLINK_SUPPORT "Compile with support for symlinks" OFF) -if (SYMLINK_SUPPORT) +option(GKFS_SYMLINK_SUPPORT "Compile with support for symlinks" OFF) +if (GKFS_SYMLINK_SUPPORT) add_definitions(-DHAS_SYMLINKS) endif () -option(RENAME_SUPPORT "Compile with support for rename ops" OFF) -if (RENAME_SUPPORT) +option(GKFS_RENAME_SUPPORT "Compile with support for rename ops" OFF) +if (GKFS_RENAME_SUPPORT) # Rename depends on symlink support add_definitions(-DHAS_SYMLINKS) - set(SYMLINK_SUPPORT ON) + set(GKFS_SYMLINK_SUPPORT ON) add_definitions(-DHAS_RENAME) endif () -message(STATUS "[gekkofs] Symlink support: ${SYMLINK_SUPPORT}") -message(STATUS "[gekkofs] Rename support: ${RENAME_SUPPORT}") +message(STATUS "[gekkofs] Symlink support: ${GKFS_SYMLINK_SUPPORT}") +message(STATUS "[gekkofs] Rename support: ${GKFS_RENAME_SUPPORT}") set(MAX_INTERNAL_FDS 256 CACHE STRING "Number of file descriptors reserved for internal use") add_definitions(-DMAX_INTERNAL_FDS=${MAX_INTERNAL_FDS}) diff --git a/README.md b/README.md index 37665f5a8..b712ce043 100644 --- a/README.md +++ b/README.md @@ -291,7 +291,7 @@ pushed to the Prometheus instance. ### Rename -`-DRENAME_SUPPORT` allows the application to rename files. +`-DGKFS_RENAME_SUPPORT` allows the application to rename files. This is an experimental feature, and some scenarios may not work properly. Support for fstat in renamed files is included. diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index 95f4b246d..9e76e90c5 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -127,7 +127,7 @@ gkfs_add_python_test( SOURCE syscalls/ ) -if (RENAME_SUPPORT) +if (GKFS_RENAME_SUPPORT) gkfs_add_python_test( NAME test_rename PYTHON_VERSION 3.6 @@ -146,7 +146,7 @@ if(GKFS_INSTALL_TESTS) PATTERN "gkfs.io" EXCLUDE ) - if(SYMLINK_SUPPORT) + if(GKFS_SYMLINK_SUPPORT) install(DIRECTORY directories DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration FILES_MATCHING @@ -223,7 +223,7 @@ if(GKFS_INSTALL_TESTS) ) endif () - if (RENAME_SUPPORT) + if (GKFS_RENAME_SUPPORT) install(DIRECTORY rename DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gkfs/tests/integration FILES_MATCHING -- GitLab