209 lines
5.7 KiB
C
209 lines
5.7 KiB
C
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <spawn.h>
|
|
#include <stdlib.h>
|
|
#include <sys/wait.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include "test.h"
|
|
|
|
// ============================================================================
|
|
// Helper structs & variables & functions
|
|
// ============================================================================
|
|
|
|
const char **g_argv;
|
|
int g_argc;
|
|
const char *g_file_path = "/root/test_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] = {
|
|
"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/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;
|
|
}
|