diff options
Diffstat (limited to 'src/lib/test-file-create-locked.c')
-rw-r--r-- | src/lib/test-file-create-locked.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/src/lib/test-file-create-locked.c b/src/lib/test-file-create-locked.c new file mode 100644 index 0000000..f5b4f6a --- /dev/null +++ b/src/lib/test-file-create-locked.c @@ -0,0 +1,142 @@ +/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ + +#include "test-lib.h" +#include "unlink-directory.h" +#include "file-create-locked.h" +#include "sleep.h" + +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> + +static void create_file(const char *path) +{ + int fd; + + fd = creat(path, 0600); + if (fd == -1) + i_fatal("creat(%s) failed: %m", path); + i_close_fd(&fd); +} + +static bool wait_for_file(pid_t pid, const char *path) +{ + struct stat st; + + for (unsigned int i = 0; i < 1000; i++) { + if (stat(path, &st) == 0) + return TRUE; + if (errno != ENOENT) + i_fatal("stat(%s) failed: %m", path); + if (kill(pid, 0) < 0) { + if (errno == ESRCH) + return FALSE; + i_fatal("kill(SIGSRCH) failed: %m"); + } + i_sleep_msecs(10); + } + i_error("%s isn't being created", path); + return FALSE; +} + +static void test_file_create_locked_basic(void) +{ + struct file_create_settings set = { + .lock_timeout_secs = 0, + .lock_settings = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }, + }; + const char *path = ".test-file-create-locked"; + struct file_lock *lock; + const char *error; + bool created; + pid_t pid; + int fd; + + test_begin("file_create_locked()"); + + i_unlink_if_exists(path); + i_unlink_if_exists(".test-temp-file-create-locked-child"); + pid = fork(); + switch (pid) { + case (pid_t)-1: + i_error("fork() failed: %m"); + break; + case 0: + /* child */ + fd = file_create_locked(path, &set, &lock, &created, &error); + test_assert(fd > 0); + test_assert(created); + if (test_has_failed()) + lib_exit(1); + create_file(".test-temp-file-create-locked-child"); + sleep(60); + i_close_fd(&fd); + lib_exit(0); + default: + /* parent */ + test_assert(wait_for_file(pid, ".test-temp-file-create-locked-child")); + if (test_has_failed()) + break; + test_assert(file_create_locked(path, &set, &lock, &created, &error) == -1); + test_assert(errno == EAGAIN); + if (kill(pid, SIGKILL) < 0) + i_error("kill(SIGKILL) failed: %m"); + break; + } + i_unlink_if_exists(".test-temp-file-create-locked-child"); + i_unlink_if_exists(path); + test_end(); +} + +static void test_file_create_locked_mkdir(void) +{ + struct file_create_settings set = { + .lock_timeout_secs = 0, + .lock_settings = { + .lock_method = FILE_LOCK_METHOD_FCNTL, + }, + }; + const char *path; + struct file_lock *lock; + const char *error, *dir; + bool created; + int fd; + + test_begin("file_create_locked() with mkdir"); + + dir = ".test-temp-file-create-locked-dir"; + if (unlink_directory(dir, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) + i_fatal("unlink_directory(%s) failed: %s", dir, error); + path = t_strconcat(dir, "/lockfile", NULL); + + /* try without mkdir enabled */ + test_assert(file_create_locked(path, &set, &lock, &created, &error) == -1); + test_assert(errno == ENOENT); + + /* try with mkdir enabled */ + set.mkdir_mode = 0700; + fd = file_create_locked(path, &set, &lock, &created, &error); + test_assert(fd > 0); + test_assert(created); + i_close_fd(&fd); + + struct stat st; + if (stat(dir, &st) < 0) + i_error("stat(%s) failed: %m", dir); + test_assert((st.st_mode & 0777) == 0700); + i_unlink(path); + file_lock_free(&lock); + + if (unlink_directory(dir, UNLINK_DIRECTORY_FLAG_RMDIR, &error) < 0) + i_fatal("unlink_directory(%s) failed: %s", dir, error); + + test_end(); +} + +void test_file_create_locked(void) +{ + test_file_create_locked_basic(); + test_file_create_locked_mkdir(); +} |