Loading src/client/gkfs_functions.cpp +57 −0 Original line number Diff line number Diff line Loading @@ -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<gkfs::filemap::OpenFile>(new_path, flags)); } } #endif if(S_ISDIR(md.mode())) { Loading Loading @@ -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; Loading tests/integration/harness/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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) Loading tests/integration/harness/gkfs.io/main.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ init_commands(CLI::App& app) { dup_validate_init(app); syscall_coverage_init(app); rename_init(app); unlink_init(app); } Loading tests/integration/harness/io.py +10 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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: Loading Loading @@ -509,6 +517,7 @@ class IOParser: 'dup_validate' : DupValidateOutputSchema(), 'syscall_coverage' : SyscallCoverageOutputSchema(), 'rename' : RenameOutputSchema(), 'unlink' : UnlinkOutputSchema(), } def parse(self, command, output): Loading tests/integration/rename/test_rename_operation.py +248 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
src/client/gkfs_functions.cpp +57 −0 Original line number Diff line number Diff line Loading @@ -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<gkfs::filemap::OpenFile>(new_path, flags)); } } #endif if(S_ISDIR(md.mode())) { Loading Loading @@ -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; Loading
tests/integration/harness/CMakeLists.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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) Loading
tests/integration/harness/gkfs.io/main.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -68,6 +68,7 @@ init_commands(CLI::App& app) { dup_validate_init(app); syscall_coverage_init(app); rename_init(app); unlink_init(app); } Loading
tests/integration/harness/io.py +10 −1 Original line number Diff line number Diff line Loading @@ -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) Loading @@ -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: Loading Loading @@ -509,6 +517,7 @@ class IOParser: 'dup_validate' : DupValidateOutputSchema(), 'syscall_coverage' : SyscallCoverageOutputSchema(), 'rename' : RenameOutputSchema(), 'unlink' : UnlinkOutputSchema(), } def parse(self, command, output): Loading
tests/integration/rename/test_rename_operation.py +248 −0 Original line number Diff line number Diff line Loading @@ -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