The Pedigree Project 0.1
fanotify-handle-contract-test/events.c
1#define _GNU_SOURCE
2#include <string.h>
3#include <unistd.h>
4
5#include "contract.h"
6#include <sys/stat.h>
7
8static int aliases_and_marks(void) {
9 int failed = 0, group = -1, alias_fd = -1, opened = -1;
10 struct fh_file file = {.fd = -1};
11 struct fh_record record;
12 struct fh_handle alias_handle;
13 char alias[224] = {0};
14 int mount_id;
15 CHECK(!fh_create(&file));
16 snprintf(alias, sizeof(alias), "%s.alias", file.path);
17 CHECK(!link(file.path, alias));
18 alias_fd = open(alias, O_RDONLY | O_CLOEXEC);
19 CHECK(alias_fd >= 0 && !fh_export(alias_fd, &alias_handle, &mount_id));
20 CHECK(fh_equal(&alias_handle, &file.handle));
21 group = fh_group(1);
22 CHECK(group >= 0 && !fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
23 CHECK(!fanotify_mark(group, FAN_MARK_ADD, FAN_ATTRIB, alias_fd, NULL));
24 CHECK(!fchmod(alias_fd, 0644) && !fh_modify(&file));
25 CHECK(!fh_take(group, &file.handle, &record));
26 CHECK(record.mask == (FAN_ATTRIB | FAN_MODIFY) && record.pid == getpid());
27 CHECK(fh_equal(&record.handle, &file.handle) && fh_readable(group, 0) == 0);
28 opened = open_by_handle_at(file.fd, (struct file_handle*)&record.handle, O_RDONLY);
29 char byte;
30 CHECK(opened >= 0 && pread(opened, &byte, 1, 0) == 1 && byte == '!');
31 CHECK(!close(opened));
32 opened = -1;
33 CHECK(!fanotify_mark(group, FAN_MARK_REMOVE, FAN_MODIFY, alias_fd, NULL));
34 CHECK(!fh_modify(&file) && fh_readable(group, 0) == 0);
35 CHECK(!fchmod(file.fd, 0600));
36 CHECK(!fh_event(group, &file, FAN_ATTRIB, getpid()));
37 CHECK(!fh_mark(group, &file, FAN_MARK_REMOVE, FAN_ATTRIB));
38 errno = 0;
39 CHECK(fh_mark(group, &file, FAN_MARK_REMOVE, FAN_ATTRIB) == -1 && errno == ENOENT);
40 CHECK(!fh_mark(group, &file, FAN_MARK_ADD, FAN_OPEN | FAN_CLOSE));
41 opened = open(alias, O_RDONLY | O_CLOEXEC);
42 CHECK(opened >= 0 && !fh_event(group, &file, FAN_OPEN, getpid()));
43 CHECK(!close(opened));
44 opened = -1;
45 CHECK(!fh_event(group, &file, FAN_CLOSE_NOWRITE, getpid()));
46 opened = open(file.path, O_RDWR | O_CLOEXEC);
47 CHECK(opened >= 0 && !fh_event(group, &file, FAN_OPEN, getpid()));
48 CHECK(!close(opened));
49 opened = -1;
50 CHECK(!fh_event(group, &file, FAN_CLOSE_WRITE, getpid()));
51 CHECK(!fanotify_mark(group, FAN_MARK_FLUSH, UINT64_MAX, -1, (const char*)1));
52 CHECK(!fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
53 CHECK(!fh_modify(&file));
54 CHECK(!fh_mark(group, &file, FAN_MARK_REMOVE, FAN_MODIFY));
55 CHECK(!fh_event(group, &file, FAN_MODIFY, getpid()));
56 CHECK(!fh_modify(&file) && fh_readable(group, 0) == 0);
57out:
58 if (opened >= 0)
59 close(opened);
60 if (group >= 0)
61 close(group);
62 if (alias_fd >= 0)
63 close(alias_fd);
64 if (alias[0])
65 unlink(alias);
66 fh_close(&file);
67 return failed;
68}
69
70static int producer_exit(void) {
71 int failed = 0, group = -1, gate[2] = {-1, -1}, completed[2] = {-1, -1};
72 pid_t child = -1;
73 struct fh_file file = {.fd = -1};
74 CHECK(!fh_create(&file));
75 group = fh_group(1);
76 CHECK(group >= 0 && !fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
77 CHECK(!pipe(gate) && !pipe(completed));
78 child = fork();
79 CHECK(child >= 0);
80 if (!child) {
81 alarm(10);
82 close(gate[1]);
83 close(completed[0]);
84 if (fh_receive(gate[0], 'g') || fh_modify(&file) || fh_send(completed[1], 'd'))
85 _exit(10);
86 _exit(0);
87 }
88 CHECK(!fh_send(gate[1], 'g') && !fh_receive(completed[0], 'd'));
89 pid_t producer = child;
90 CHECK(!fh_reap(child, 12000));
91 child = -1;
92 CHECK(!fh_event(group, &file, FAN_MODIFY, producer));
93 CHECK(fh_readable(group, 0) == 0);
94out:
95 if (child > 0)
96 fh_reap(child, 100);
97 for (int n = 0; n < 2; ++n) {
98 if (gate[n] >= 0)
99 close(gate[n]);
100 if (completed[n] >= 0)
101 close(completed[n]);
102 }
103 if (group >= 0)
104 close(group);
105 fh_close(&file);
106 return failed;
107}
108int fh_events(void) {
109 return aliases_and_marks() || producer_exit();
110}