#include #include #include #include #include "test_fs.h" // ============================================================================ // Helper function // ============================================================================ #define WRITE_MSG "Hello World" static int create_file_with_content(const char *file_path, const char *msg) { int fd; int flags = O_WRONLY | O_CREAT | O_TRUNC; int mode = 00666; fd = open(file_path, flags, mode); if (fd < 0) { THROW_ERROR("failed to create a file"); } if (msg != NULL) { if (write(fd, msg, strlen(msg)) <= 0) { THROW_ERROR("failed to write to the file"); } } close(fd); return 0; } // ============================================================================ // Test cases for rename // ============================================================================ static int __test_rename(const char *old_path, const char *new_path) { struct stat stat_buf; int ret; if (rename(old_path, new_path) < 0) { THROW_ERROR("failed to rename file"); } if (fs_check_file_content(new_path, WRITE_MSG) < 0) { THROW_ERROR("failed to check file content"); } ret = stat(old_path, &stat_buf); if (!(ret < 0 && errno == ENOENT)) { THROW_ERROR("stat on old path should return ENOENT"); } if (unlink(new_path) < 0) { THROW_ERROR("failed to remove the new file"); } return 0; } static int __test_renameat(const char *old_path, const char *new_path) { struct stat stat_buf; char old_dir_buf[PATH_MAX] = { 0 }; char old_base_buf[PATH_MAX] = { 0 }; char new_dir_buf[PATH_MAX] = { 0 }; char new_base_buf[PATH_MAX] = { 0 }; char *old_dir_name, *old_file_name, *new_dir_name, *new_file_name; int old_dirfd, new_dirfd, ret; if (fs_split_path(old_path, old_dir_buf, &old_dir_name, old_base_buf, &old_file_name) < 0) { THROW_ERROR("failed to split old path"); } old_dirfd = open(old_dir_name, O_RDONLY); if (old_dirfd < 0) { THROW_ERROR("failed to open old dir"); } if (fs_split_path(new_path, new_dir_buf, &new_dir_name, new_base_buf, &new_file_name) < 0) { THROW_ERROR("failed to split new path"); } new_dirfd = open(new_dir_name, O_RDONLY); if (new_dirfd < 0) { THROW_ERROR("failed to open new dir"); } if (renameat(old_dirfd, old_file_name, new_dirfd, new_file_name) < 0) { THROW_ERROR("failed to rename with dirfd"); } close(old_dirfd); close(new_dirfd); if (fs_check_file_content(new_path, WRITE_MSG) < 0) { THROW_ERROR("failed to check file content"); } ret = stat(old_path, &stat_buf); if (!(ret < 0 && errno == ENOENT)) { THROW_ERROR("stat on old path should return ENOENT"); } if (unlink(new_path) < 0) { THROW_ERROR("failed to remove the new file"); } return 0; } typedef int(*test_rename_func_t)(const char *, const char *); static int test_rename_framework(test_rename_func_t fn, bool target_exist) { const char *old_path = "/root/test_filesystem_rename_old.txt"; const char *new_path = "/root/test_filesystem_rename_new.txt"; if (create_file_with_content(old_path, WRITE_MSG) < 0) { THROW_ERROR("failed to create old file with content"); } if (target_exist) { if (create_file_with_content(new_path, NULL) < 0) { THROW_ERROR("failed to create new file"); } } if (fn(old_path, new_path) < 0) { return -1; } return 0; } static int test_rename() { return test_rename_framework(__test_rename, false); } static int test_rename_with_target_exist() { return test_rename_framework(__test_rename, true); } static int test_renameat() { return test_rename_framework(__test_renameat, false); } static int test_rename_dir() { const char *old_dir = "/root/test_old_dir/"; const char *new_dir = "/root/test_new_dir"; const char *file_name = "test_file.txt"; char file_buf[128] = { 0 }; mode_t mode = 00775; struct stat stat_buf; int ret; if (mkdir(old_dir, mode) < 0) { THROW_ERROR("failed to mkdir old dir"); } ret = snprintf(file_buf, sizeof(file_buf), "%s/%s", old_dir, file_name); if (ret >= sizeof(file_buf) || ret < 0) { THROW_ERROR("failed to copy file buf"); } if (create_file_with_content(file_buf, WRITE_MSG) < 0) { THROW_ERROR("failed to create file in old dir"); } if (rename(old_dir, new_dir) < 0) { THROW_ERROR("failed to rename dir"); } ret = snprintf(file_buf, sizeof(file_buf), "%s/%s", new_dir, file_name); if (ret >= sizeof(file_buf) || ret < 0) { THROW_ERROR("failed to copy file buf"); } if (fs_check_file_content(file_buf, WRITE_MSG) < 0) { THROW_ERROR("failed to check file content"); } ret = stat(old_dir, &stat_buf); if (!(ret < 0 && errno == ENOENT)) { THROW_ERROR("stat on old dir should return ENOENT"); } if (unlink(file_buf) < 0) { THROW_ERROR("failed to remove the file in new dir"); } if (rmdir(new_dir) < 0) { THROW_ERROR("failed to remove the new dir"); } return 0; } static int test_rename_dir_to_subdir() { const char *old_dir = "/root/test_old_dir"; mode_t mode = 00775; int ret; char sub_dir[PATH_MAX] = { 0 }; ret = snprintf(sub_dir, sizeof(sub_dir), "%s/test_new_dir", old_dir); if (ret >= sizeof(sub_dir) || ret < 0) { THROW_ERROR("failed to init new dir path"); } if (mkdir(old_dir, mode) < 0) { THROW_ERROR("failed to mkdir"); } ret = rename(old_dir, sub_dir); if (ret == 0 || errno != EINVAL) { THROW_ERROR("failed to check rename dir to subdir"); } if (rmdir(old_dir) < 0) { THROW_ERROR("failed to rmdir"); } return 0; } static int test_rename_file_as_dir() { const char *old_file = "/root/test_old_file"; const char *old_dir = "/root/test_old_file/"; const char *new_dir = "/root/test_new_dir"; if (create_file_with_content(old_file, WRITE_MSG) < 0) { THROW_ERROR("failed to create old file with content"); } int ret = rename(old_dir, new_dir); if (ret == 0 || errno != ENOTDIR) { THROW_ERROR("failed to check rename file as dir"); } if (unlink(old_file) < 0) { THROW_ERROR("failed to remove file"); } return 0; } // ============================================================================ // Test suite main // ============================================================================ static test_case_t test_cases[] = { // TODO: test more corner cases TEST_CASE(test_rename), TEST_CASE(test_rename_with_target_exist), TEST_CASE(test_renameat), TEST_CASE(test_rename_dir), TEST_CASE(test_rename_dir_to_subdir), TEST_CASE(test_rename_file_as_dir), }; int main(int argc, const char *argv[]) { return test_suite_run(test_cases, ARRAY_SIZE(test_cases)); }