83
83
#include <sys/mman.h> /* munlock(), mlock() */
84
84
#include <fcntl.h> /* O_CLOEXEC, O_NONBLOCK, fcntl(),
85
85
F_GETFD, F_GETFL, FD_CLOEXEC,
86
open(), O_WRONLY, O_NOCTTY,
86
open(), O_WRONLY, O_RDONLY */
88
87
#include <sys/wait.h> /* waitpid(), WNOHANG, WIFEXITED(),
90
89
#include <limits.h> /* PIPE_BUF, NAME_MAX, INT_MAX */
3457
3452
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3460
static void test_add_inotify_dir_watch_nondir(__attribute__((unused))
3461
test_fixture *fixture,
3462
__attribute__((unused))
3465
__attribute__((cleanup(cleanup_close)))
3466
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3467
g_assert_cmpint(epoll_fd, >=, 0);
3468
__attribute__((cleanup(cleanup_queue)))
3469
task_queue *queue = create_queue();
3470
g_assert_nonnull(queue);
3471
__attribute__((cleanup(string_set_clear)))
3472
string_set cancelled_filenames = {};
3473
const mono_microsecs current_time = 0;
3475
bool quit_now = false;
3476
buffer password = {};
3477
bool mandos_client_exited = false;
3478
bool password_is_read = false;
3480
const char not_a_directory[] = "/dev/tty";
3482
FILE *real_stderr = stderr;
3483
FILE *devnull = fopen("/dev/null", "we");
3484
g_assert_nonnull(devnull);
3486
g_assert_false(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3487
&password, not_a_directory,
3488
&cancelled_filenames,
3490
&mandos_client_exited,
3491
&password_is_read));
3492
stderr = real_stderr;
3493
g_assert_cmpint(fclose(devnull), ==, 0);
3495
g_assert_cmpuint((unsigned int)queue->length, ==, 0);
3498
3455
static void test_add_inotify_dir_watch_EAGAIN(__attribute__((unused))
3499
3456
test_fixture *fixture,
3500
3457
__attribute__((unused))
3718
void test_add_inotify_dir_watch_IN_MOVED_FROM(__attribute__((unused))
3719
test_fixture *fixture,
3720
__attribute__((unused))
3723
__attribute__((cleanup(cleanup_close)))
3724
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3725
g_assert_cmpint(epoll_fd, >=, 0);
3726
__attribute__((cleanup(cleanup_queue)))
3727
task_queue *queue = create_queue();
3728
g_assert_nonnull(queue);
3729
__attribute__((cleanup(string_set_clear)))
3730
string_set cancelled_filenames = {};
3731
const mono_microsecs current_time = 0;
3733
bool quit_now = false;
3734
buffer password = {};
3735
bool mandos_client_exited = false;
3736
bool password_is_read = false;
3738
__attribute__((cleanup(cleanup_string)))
3739
char *tempdir = make_temporary_directory();
3740
g_assert_nonnull(tempdir);
3742
__attribute__((cleanup(cleanup_string)))
3743
char *tempfilename = make_temporary_file_in_directory(tempdir);
3744
g_assert_nonnull(tempfilename);
3746
__attribute__((cleanup(cleanup_string)))
3747
char *targetdir = make_temporary_directory();
3748
g_assert_nonnull(targetdir);
3750
__attribute__((cleanup(cleanup_string)))
3751
char *targetfilename = NULL;
3752
g_assert_cmpint(asprintf(&targetfilename, "%s/%s", targetdir,
3753
basename(tempfilename)), >, 0);
3754
g_assert_nonnull(targetfilename);
3756
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3758
&cancelled_filenames,
3760
&mandos_client_exited,
3761
&password_is_read));
3763
g_assert_cmpint(rename(tempfilename, targetfilename), ==, 0);
3765
const task_context *const added_read_task
3766
= find_matching_task(queue,
3767
(task_context){ .func=read_inotify_event });
3768
g_assert_nonnull(added_read_task);
3770
/* "sufficient to read at least one event." - inotify(7) */
3771
const size_t ievent_size = (sizeof(struct inotify_event)
3773
struct inotify_event *ievent = malloc(ievent_size);
3774
g_assert_nonnull(ievent);
3776
ssize_t read_size = read(added_read_task->fd, ievent, ievent_size);
3778
g_assert_cmpint((int)read_size, >, 0);
3779
g_assert_true(ievent->mask & IN_MOVED_FROM);
3780
g_assert_cmpstr(ievent->name, ==, basename(tempfilename));
3784
g_assert_cmpint(unlink(targetfilename), ==, 0);
3785
g_assert_cmpint(rmdir(targetdir), ==, 0);
3786
g_assert_cmpint(rmdir(tempdir), ==, 0);
3790
3675
void test_add_inotify_dir_watch_IN_DELETE(__attribute__((unused))
3791
3676
test_fixture *fixture,
3792
3677
__attribute__((unused))
3850
3735
g_assert_cmpint(rmdir(tempdir), ==, 0);
3854
void test_add_inotify_dir_watch_IN_EXCL_UNLINK(__attribute__((unused))
3855
test_fixture *fixture,
3856
__attribute__((unused))
3859
__attribute__((cleanup(cleanup_close)))
3860
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
3861
g_assert_cmpint(epoll_fd, >=, 0);
3862
__attribute__((cleanup(cleanup_queue)))
3863
task_queue *queue = create_queue();
3864
g_assert_nonnull(queue);
3865
__attribute__((cleanup(string_set_clear)))
3866
string_set cancelled_filenames = {};
3867
const mono_microsecs current_time = 0;
3869
bool quit_now = false;
3870
buffer password = {};
3871
bool mandos_client_exited = false;
3872
bool password_is_read = false;
3874
__attribute__((cleanup(cleanup_string)))
3875
char *tempdir = make_temporary_directory();
3876
g_assert_nonnull(tempdir);
3878
__attribute__((cleanup(cleanup_string)))
3879
char *tempfile = make_temporary_file_in_directory(tempdir);
3880
g_assert_nonnull(tempfile);
3881
int tempfile_fd = open(tempfile, O_WRONLY | O_CLOEXEC | O_NOCTTY
3883
g_assert_cmpint(tempfile_fd, >, 2);
3885
g_assert_true(add_inotify_dir_watch(queue, epoll_fd, &quit_now,
3887
&cancelled_filenames,
3889
&mandos_client_exited,
3890
&password_is_read));
3891
g_assert_cmpint(unlink(tempfile), ==, 0);
3893
g_assert_cmpuint((unsigned int)queue->length, >, 0);
3895
const task_context *const added_read_task
3896
= find_matching_task(queue,
3897
(task_context){ .func=read_inotify_event });
3898
g_assert_nonnull(added_read_task);
3900
g_assert_cmpint(added_read_task->fd, >, 2);
3901
g_assert_true(fd_has_cloexec_and_nonblock(added_read_task->fd));
3903
/* "sufficient to read at least one event." - inotify(7) */
3904
const size_t ievent_size = (sizeof(struct inotify_event)
3906
struct inotify_event *ievent = malloc(ievent_size);
3907
g_assert_nonnull(ievent);
3909
ssize_t read_size = 0;
3910
read_size = read(added_read_task->fd, ievent, ievent_size);
3912
g_assert_cmpint((int)read_size, >, 0);
3913
g_assert_true(ievent->mask & IN_DELETE);
3914
g_assert_cmpstr(ievent->name, ==, basename(tempfile));
3916
g_assert_cmpint(close(tempfile_fd), ==, 0);
3918
/* IN_EXCL_UNLINK should make the closing of the previously unlinked
3919
file not appear as an ievent, so we should not see it now. */
3920
read_size = read(added_read_task->fd, ievent, ievent_size);
3921
g_assert_cmpint((int)read_size, ==, -1);
3922
g_assert_true(errno == EAGAIN);
3926
g_assert_cmpint(rmdir(tempdir), ==, 0);
3929
3738
static void test_read_inotify_event_readerror(__attribute__((unused))
3930
3739
test_fixture *fixture,
3931
3740
__attribute__((unused))
4300
void test_read_inotify_event_IN_MOVED_FROM(__attribute__((unused))
4301
test_fixture *fixture,
4302
__attribute__((unused))
4303
gconstpointer user_data){
4304
__attribute__((cleanup(cleanup_close)))
4305
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4306
g_assert_cmpint(epoll_fd, >=, 0);
4307
__attribute__((cleanup(string_set_clear)))
4308
string_set cancelled_filenames = {};
4309
const mono_microsecs current_time = 0;
4312
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4314
/* "sufficient to read at least one event." - inotify(7) */
4315
const size_t ievent_max_size = (sizeof(struct inotify_event)
4317
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4319
struct inotify_event event;
4320
char name_buffer[NAME_MAX + 1];
4322
struct inotify_event *const ievent = &ievent_buffer.event;
4324
const char dummy_file_name[] = "ask.dummy_file_name";
4325
ievent->mask = IN_MOVED_FROM;
4326
ievent->len = sizeof(dummy_file_name);
4327
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4328
const size_t ievent_size = (sizeof(struct inotify_event)
4329
+ sizeof(dummy_file_name));
4330
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4332
g_assert_cmpint(close(pipefds[1]), ==, 0);
4334
bool quit_now = false;
4335
buffer password = {};
4336
bool mandos_client_exited = false;
4337
bool password_is_read = false;
4338
__attribute__((cleanup(cleanup_queue)))
4339
task_queue *queue = create_queue();
4340
g_assert_nonnull(queue);
4342
task_context task = {
4343
.func=read_inotify_event,
4346
.quit_now=&quit_now,
4347
.password=&password,
4348
.filename=strdup("/nonexistent"),
4349
.cancelled_filenames=&cancelled_filenames,
4350
.current_time=¤t_time,
4351
.mandos_client_exited=&mandos_client_exited,
4352
.password_is_read=&password_is_read,
4354
task.func(task, queue);
4355
g_assert_false(quit_now);
4356
g_assert_true(queue->next_run == 0);
4357
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4359
g_assert_nonnull(find_matching_task(queue, (task_context){
4360
.func=read_inotify_event,
4363
.quit_now=&quit_now,
4364
.password=&password,
4365
.filename=task.filename,
4366
.cancelled_filenames=&cancelled_filenames,
4367
.current_time=¤t_time,
4368
.mandos_client_exited=&mandos_client_exited,
4369
.password_is_read=&password_is_read,
4372
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4373
EPOLLIN | EPOLLRDHUP));
4375
__attribute__((cleanup(cleanup_string)))
4376
char *filename = NULL;
4377
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4378
dummy_file_name), >, 0);
4379
g_assert_nonnull(filename);
4380
g_assert_true(string_set_contains(*task.cancelled_filenames,
4384
4107
static void test_read_inotify_event_IN_DELETE(__attribute__((unused))
4385
4108
test_fixture *fixture,
4386
4109
__attribute__((unused))
4619
4342
EPOLLIN | EPOLLRDHUP));
4623
test_read_inotify_event_IN_MOVED_FROM_badname(__attribute__((unused))
4624
test_fixture *fixture,
4625
__attribute__((unused))
4628
__attribute__((cleanup(cleanup_close)))
4629
const int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
4630
g_assert_cmpint(epoll_fd, >=, 0);
4631
__attribute__((cleanup(string_set_clear)))
4632
string_set cancelled_filenames = {};
4633
const mono_microsecs current_time = 0;
4636
g_assert_cmpint(pipe2(pipefds, O_CLOEXEC | O_NONBLOCK), ==, 0);
4638
/* "sufficient to read at least one event." - inotify(7) */
4639
const size_t ievent_max_size = (sizeof(struct inotify_event)
4641
g_assert_cmpint(ievent_max_size, <=, PIPE_BUF);
4643
struct inotify_event event;
4644
char name_buffer[NAME_MAX + 1];
4646
struct inotify_event *const ievent = &ievent_buffer.event;
4648
const char dummy_file_name[] = "ignored.dummy_file_name";
4649
ievent->mask = IN_MOVED_FROM;
4650
ievent->len = sizeof(dummy_file_name);
4651
memcpy(ievent->name, dummy_file_name, sizeof(dummy_file_name));
4652
const size_t ievent_size = (sizeof(struct inotify_event)
4653
+ sizeof(dummy_file_name));
4654
g_assert_cmpint(write(pipefds[1], (char *)ievent, ievent_size),
4656
g_assert_cmpint(close(pipefds[1]), ==, 0);
4658
bool quit_now = false;
4659
buffer password = {};
4660
bool mandos_client_exited = false;
4661
bool password_is_read = false;
4662
__attribute__((cleanup(cleanup_queue)))
4663
task_queue *queue = create_queue();
4664
g_assert_nonnull(queue);
4666
task_context task = {
4667
.func=read_inotify_event,
4670
.quit_now=&quit_now,
4671
.password=&password,
4672
.filename=strdup("/nonexistent"),
4673
.cancelled_filenames=&cancelled_filenames,
4674
.current_time=¤t_time,
4675
.mandos_client_exited=&mandos_client_exited,
4676
.password_is_read=&password_is_read,
4678
task.func(task, queue);
4679
g_assert_false(quit_now);
4680
g_assert_true(queue->next_run == 0);
4681
g_assert_cmpuint((unsigned int)queue->length, ==, 1);
4683
g_assert_nonnull(find_matching_task(queue, (task_context){
4684
.func=read_inotify_event,
4687
.quit_now=&quit_now,
4688
.password=&password,
4689
.filename=task.filename,
4690
.cancelled_filenames=&cancelled_filenames,
4691
.current_time=¤t_time,
4692
.mandos_client_exited=&mandos_client_exited,
4693
.password_is_read=&password_is_read,
4696
g_assert_true(epoll_set_contains(epoll_fd, pipefds[0],
4697
EPOLLIN | EPOLLRDHUP));
4699
__attribute__((cleanup(cleanup_string)))
4700
char *filename = NULL;
4701
g_assert_cmpint(asprintf(&filename, "%s/%s", task.filename,
4702
dummy_file_name), >, 0);
4703
g_assert_nonnull(filename);
4704
g_assert_false(string_set_contains(cancelled_filenames, filename));
4708
4346
void test_read_inotify_event_IN_DELETE_badname(__attribute__((unused))
4709
4347
test_fixture *fixture,
7952
7587
test_add_inotify_dir_watch);
7953
7588
test_add_st("/task-creators/add_inotify_dir_watch/fail",
7954
7589
test_add_inotify_dir_watch_fail);
7955
test_add_st("/task-creators/add_inotify_dir_watch/not-a-directory",
7956
test_add_inotify_dir_watch_nondir);
7957
7590
test_add_st("/task-creators/add_inotify_dir_watch/EAGAIN",
7958
7591
test_add_inotify_dir_watch_EAGAIN);
7959
7592
test_add_st("/task-creators/add_inotify_dir_watch/IN_CLOSE_WRITE",
7960
7593
test_add_inotify_dir_watch_IN_CLOSE_WRITE);
7961
7594
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_TO",
7962
7595
test_add_inotify_dir_watch_IN_MOVED_TO);
7963
test_add_st("/task-creators/add_inotify_dir_watch/IN_MOVED_FROM",
7964
test_add_inotify_dir_watch_IN_MOVED_FROM);
7965
test_add_st("/task-creators/add_inotify_dir_watch/IN_EXCL_UNLINK",
7966
test_add_inotify_dir_watch_IN_EXCL_UNLINK);
7967
7596
test_add_st("/task-creators/add_inotify_dir_watch/IN_DELETE",
7968
7597
test_add_inotify_dir_watch_IN_DELETE);
7969
7598
test_add_st("/task/read_inotify_event/readerror",
7978
7607
test_read_inotify_event_IN_CLOSE_WRITE);
7979
7608
test_add_st("/task/read_inotify_event/IN_MOVED_TO",
7980
7609
test_read_inotify_event_IN_MOVED_TO);
7981
test_add_st("/task/read_inotify_event/IN_MOVED_FROM",
7982
test_read_inotify_event_IN_MOVED_FROM);
7983
7610
test_add_st("/task/read_inotify_event/IN_DELETE",
7984
7611
test_read_inotify_event_IN_DELETE);
7985
7612
test_add_st("/task/read_inotify_event/IN_CLOSE_WRITE/badname",
7986
7613
test_read_inotify_event_IN_CLOSE_WRITE_badname);
7987
7614
test_add_st("/task/read_inotify_event/IN_MOVED_TO/badname",
7988
7615
test_read_inotify_event_IN_MOVED_TO_badname);
7989
test_add_st("/task/read_inotify_event/IN_MOVED_FROM/badname",
7990
test_read_inotify_event_IN_MOVED_FROM_badname);
7991
7616
test_add_st("/task/read_inotify_event/IN_DELETE/badname",
7992
7617
test_read_inotify_event_IN_DELETE_badname);
7993
7618
test_add_st("/task/open_and_parse_question/ENOENT",