This patch implements the renameat() function and enhances
tst-rename.cc to unit test it.
This patch also exposes renameat as a syscall.
#Refs 1188
Signed-off-by: Waldemar Kozaczuk <
jwkoz...@gmail.com>
---
exported_symbols/osv_ld-musl.so.1.symbols | 1 +
exported_symbols/osv_libc.so.6.symbols | 1 +
fs/vfs/main.cc | 30 ++++++++++++++
linux.cc | 1 +
tests/tst-rename.cc | 48 ++++++++++++++++++++++-
5 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/exported_symbols/osv_ld-musl.so.1.symbols b/exported_symbols/osv_ld-musl.so.1.symbols
index 3db22e0d..6d88fda4 100644
--- a/exported_symbols/osv_ld-musl.so.1.symbols
+++ b/exported_symbols/osv_ld-musl.so.1.symbols
@@ -868,6 +868,7 @@ remquo
remquof
remquol
rename
+renameat
res_init
res_mkquery
rewind
diff --git a/exported_symbols/osv_libc.so.6.symbols b/exported_symbols/osv_libc.so.6.symbols
index e29059bb..b27fbba1 100644
--- a/exported_symbols/osv_libc.so.6.symbols
+++ b/exported_symbols/osv_libc.so.6.symbols
@@ -696,6 +696,7 @@ register_printf_specifier
register_printf_type
remove
rename
+renameat
__res_init
rewind
rewinddir
diff --git a/fs/vfs/main.cc b/fs/vfs/main.cc
index bdedc6c6..76e1ee0e 100644
--- a/fs/vfs/main.cc
+++ b/fs/vfs/main.cc
@@ -1043,6 +1043,36 @@ int rename(const char *oldpath, const char *newpath)
return -1;
}
+OSV_LIBC_API
+int renameat(int olddirfd, const char *oldpath,
+ int newdirfd, const char *newpath)
+{
+ if (!oldpath || !newpath) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (newpath[0] == '/' || newdirfd == AT_FDCWD) {
+ return vfs_fun_at2(olddirfd, oldpath, [newpath](const char *path) {
+ return rename(path, newpath);
+ });
+ } else {
+ char absolute_newpath[PATH_MAX];
+ auto error = vfs_fun_at(newdirfd, newpath, [&absolute_newpath](const char *absolute_path) {
+ strcpy(absolute_newpath, absolute_path);
+ return 0;
+ });
+
+ if (error) {
+ return error;
+ } else {
+ return vfs_fun_at2(olddirfd, oldpath, [absolute_newpath](const char *path) {
+ return rename(path, absolute_newpath);
+ });
+ }
+ }
+}
+
TRACEPOINT(trace_vfs_chdir, "\"%s\"", const char*);
TRACEPOINT(trace_vfs_chdir_ret, "");
TRACEPOINT(trace_vfs_chdir_err, "%d", int);
diff --git a/linux.cc b/linux.cc
index f60489e3..1fa01326 100644
--- a/linux.cc
+++ b/linux.cc
@@ -516,6 +516,7 @@ OSV_LIBC_API long syscall(long number, ...)
SYSCALL3(unlinkat, int, const char *, int);
SYSCALL3(symlinkat, const char *, int, const char *);
SYSCALL3(sys_getdents64, int, void *, size_t);
+ SYSCALL4(renameat, int, const char *, int, const char *);
}
debug_always("syscall(): unimplemented system call %d\n", number);
diff --git a/tests/tst-rename.cc b/tests/tst-rename.cc
index 722fdc4c..0668bf77 100644
--- a/tests/tst-rename.cc
+++ b/tests/tst-rename.cc
@@ -73,6 +73,9 @@ static void assert_rename_fails(const fs::path &src, const fs::path &dst, std::v
BOOST_TEST_MESSAGE("Renaming " + src.string() + " to " + dst.string());
BOOST_REQUIRE(rename(src.c_str(), dst.c_str()) == -1);
assert_one_of(errno, errnos);
+ BOOST_TEST_MESSAGE("Renaming(at) " + src.string() + " to " + dst.string());
+ BOOST_REQUIRE(renameat(AT_FDCWD, src.c_str(), AT_FDCWD, dst.c_str()) == -1);
+ assert_one_of(errno, errnos);
}
static void assert_renames(const fs::path src, const fs::path dst)
@@ -82,11 +85,27 @@ static void assert_renames(const fs::path src, const fs::path dst)
BOOST_REQUIRE_MESSAGE(result == 0, fmt("Rename should succeed, errno=%d") % errno);
}
-static void test_rename(const fs::path &src, const fs::path &dst)
+static void assert_renames_at(const fs::path src, const fs::path dst)
+{
+ BOOST_TEST_MESSAGE("Renaming " + src.string() + " to " + dst.string());
+ auto src_dir_fd = open(src.parent_path().c_str(), O_DIRECTORY);
+ auto dst_dir_fd = open(dst.parent_path().c_str(), O_DIRECTORY);
+ int result = renameat(src_dir_fd, src.filename().c_str(), dst_dir_fd, dst.filename().c_str());
+ BOOST_REQUIRE_MESSAGE(result == 0, fmt("Renameat should succeed, errno=%d") % errno);
+ close(src_dir_fd);
+ close(dst_dir_fd);
+}
+
+static void test_rename(const fs::path &src, const fs::path &dst, bool at = false)
{
prepare_file(src);
- assert_renames(src, dst);
+ if (at) {
+ assert_renames_at(src, dst);
+ }
+ else {
+ assert_renames(src, dst);
+ }
check_file(dst);
BOOST_CHECK_MESSAGE(!fs::exists(src), "Old file should not exist");
@@ -136,6 +155,10 @@ BOOST_AUTO_TEST_CASE(test_renaming_in_the_same_directory)
dir / "file1",
dir / "file2");
+ test_rename(
+ dir / "file1",
+ dir / "file2", true);
+
test_rename_from_open_file(
dir / "file1",
dir / "file2");
@@ -148,9 +171,17 @@ BOOST_AUTO_TEST_CASE(test_renaming_in_the_same_directory)
dir / "a",
dir / "aaaaa");
+ test_rename(
+ dir / "a",
+ dir / "aaaaa", true);
+
test_rename(
dir / "aaaaaaaaa",
dir / "aa");
+
+ test_rename(
+ dir / "aaaaaaaaa",
+ dir / "aa", true);
}
BOOST_AUTO_TEST_CASE(test_renaming_to_child_path_should_fail) {
@@ -169,13 +200,25 @@ BOOST_AUTO_TEST_CASE(test_moving_file_to_another_directory)
dir / "file",
dir / sub / "file");
+ test_rename(
+ dir / "file",
+ dir / sub / "file", true);
+
test_rename(
dir / sub / "file2",
dir / "file2");
+ test_rename(
+ dir / sub / "file2",
+ dir / "file2", true);
+
test_rename(
dir / sub / "a",
dir / "aaaa");
+
+ test_rename(
+ dir / sub / "a",
+ dir / "aaaa", true);
}
BOOST_AUTO_TEST_CASE(test_renaming_when_destination_is_substring)
@@ -201,6 +244,7 @@ BOOST_AUTO_TEST_CASE(test_renaming_works_with_non_uniform_paths)
test_rename(file, dir / "/file2");
test_rename(file, dir / "/sub///file2");
+ test_rename(file, dir / "/sub///file2", true);
}
BOOST_AUTO_TEST_CASE(test_file_can_be_located_using_different_paths_after_rename)
--
2.34.1