#include #include #include #include #include #include #include #include #include "test.h" // ============================================================================ // Helper structs & variables & functions // ============================================================================ const char **g_argv; int g_argc; const char *g_file_path = "/root/test_posix_flock_file.txt"; int g_fd; off_t g_file_len = 128; // Expected child arguments const int child_argc = 2; const char *child_argv[3] = { "posix_flock", "child", NULL }; static int open_or_create_file() { int flags = O_RDWR | O_CREAT; int mode = 00666; int fd = open(g_file_path, flags, mode); if (fd < 0) { THROW_ERROR("failed to open or create file"); } if (ftruncate(fd, g_file_len) < 0) { THROW_ERROR("failed to expand the file len"); } return fd; } static int remove_file() { if (unlink(g_file_path) < 0) { THROW_ERROR("failed to unlink the created file"); } return 0; } // ============================================================================ // Test cases for file POSIX advisory lock // ============================================================================ static int test_getlk() { struct flock fl = { F_RDLCK, SEEK_SET, 0, 0, 0 }; if (fcntl(g_fd, F_GETLK, &fl) < 0) { THROW_ERROR("failed to call getlk"); } if (fl.l_type != F_UNLCK) { THROW_ERROR("failed to get correct fl type"); } return 0; } static int test_setlk() { struct flock fl = { F_RDLCK, SEEK_SET, 0, g_file_len / 2, 0 }; if (fcntl(g_fd, F_SETLK, &fl) < 0) { THROW_ERROR("failed to call setlk"); } fl.l_len = g_file_len; if (fcntl(g_fd, F_SETLK, &fl) < 0) { THROW_ERROR("failed to expand the lock"); } fl.l_type = F_WRLCK; fl.l_len = g_file_len / 2; if (fcntl(g_fd, F_SETLK, &fl) < 0) { THROW_ERROR("failed change the lock type of existing lock"); } return 0; } static int test_spawn_child_and_unlock() { int status, child_pid; int ret = posix_spawn(&child_pid, "/bin/posix_flock", NULL, NULL, (char *const *)child_argv, NULL); if (ret < 0) { THROW_ERROR("spawn process error"); } printf("Spawn a child process with pid=%d\n", child_pid); // Sleep 3s for the child to run setlkw test and wait, is 3s enough? sleep(3); // Unlock the flock will cause child process to finish running struct flock fl = { F_UNLCK, SEEK_SET, 0, 0, 0 }; if (fcntl(g_fd, F_SETLK, &fl) < 0) { THROW_ERROR("failed to unlock the lock"); } // Wait for child exit ret = wait4(child_pid, &status, 0, NULL); if (ret < 0) { THROW_ERROR("failed to wait4 the child process"); } if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) { THROW_ERROR("test cases in child faild"); } // The lock will be unlocked on child exit, so we can lock again struct flock fl2 = { F_WRLCK, SEEK_SET, 0, g_file_len / 4, 0 }; ret = fcntl(g_fd, F_SETLKW, &fl2); if (ret < 0 && errno != EINTR) { THROW_ERROR("failed to check the result of setlkw"); } return 0; } // ============================================================================ // Child Test cases // ============================================================================ static int test_child_getlk() { struct flock fl = { F_RDLCK, SEEK_SET, 0, g_file_len / 4, 0 }; if (fcntl(g_fd, F_GETLK, &fl) < 0) { THROW_ERROR("failed to call getlk"); } if (fl.l_type != F_WRLCK) { THROW_ERROR("failed to get correct fl type"); } if (fl.l_pid == 0) { THROW_ERROR("failed to get correct fl pid"); } if (fl.l_len != g_file_len / 2) { THROW_ERROR("failed to get correct fl len"); } return 0; } static int test_child_setlk() { struct flock fl = { F_RDLCK, SEEK_SET, 0, g_file_len / 4, 0 }; int res = fcntl(g_fd, F_SETLK, &fl); if (!(res < 0 && errno == EAGAIN)) { THROW_ERROR("failed to check the result of setlk with conflict lock"); } return 0; } static int test_child_setlkw() { struct flock fl = { F_RDLCK, SEEK_SET, 0, g_file_len / 4, 0 }; int res = fcntl(g_fd, F_SETLKW, &fl); if (res < 0 && errno != EINTR) { THROW_ERROR("failed to check the result of setlkw with conflict lock"); } return 0; } // ============================================================================ // Test suite main // ============================================================================ static test_case_t test_cases[] = { TEST_CASE(test_getlk), TEST_CASE(test_setlk), TEST_CASE(test_spawn_child_and_unlock), }; static test_case_t child_test_cases[] = { TEST_CASE(test_child_getlk), TEST_CASE(test_child_setlk), TEST_CASE(test_child_setlkw), }; int main(int argc, const char *argv[]) { // Save argument for test cases g_argc = argc; g_argv = argv; g_fd = open_or_create_file(); if (g_fd < 0) { THROW_ERROR("failed to open/create file"); } // Test argc if (argc == 2) { if (test_suite_run(child_test_cases, ARRAY_SIZE(child_test_cases)) < 0) { THROW_ERROR("failed run child test"); } // Donot close file intentionally to unlock the lock on exit // close(g_fd); } else { if (test_suite_run(test_cases, ARRAY_SIZE(test_cases)) < 0) { THROW_ERROR("failed run test"); } close(g_fd); if (remove_file() < 0) { THROW_ERROR("failed to remove file after test"); } } return 0; }