The Pedigree Project 0.1
queue.c
1#define _GNU_SOURCE
2#include <string.h>
3#include <unistd.h>
4
5#include "contract.h"
6#include <sys/epoll.h>
7#include <sys/ioctl.h>
8#include <sys/mman.h>
9#include <sys/uio.h>
10
11static int copies(void) {
12 int failed = 0, group = -1, queued = -1;
13 struct fh_file first = {.fd = -1}, second = {.fd = -1};
14 struct fh_record record;
15 unsigned char bytes[256];
16 void* bad = MAP_FAILED;
17 CHECK(!fh_create(&first) && !fh_create(&second));
18 CHECK(fh_record_size(&first.handle) == fh_record_size(&second.handle));
19 size_t length = fh_record_size(&first.handle);
20 CHECK(length <= sizeof(bytes));
21 group = fh_group(1);
22 CHECK(group >= 0 && !fh_mark(group, &first, FAN_MARK_ADD, FAN_MODIFY) &&
23 !fh_mark(group, &second, FAN_MARK_ADD, FAN_MODIFY));
24 errno = 0;
25 CHECK(read(group, bytes, sizeof(bytes)) == -1 && errno == EAGAIN);
26 CHECK(!fh_modify(&first));
27 CHECK(!ioctl(group, FIONREAD, &queued) && queued == 24);
28 errno = 0;
29 CHECK(read(group, bytes, length - 1) == -1 && errno == EINVAL);
30 CHECK(!ioctl(group, FIONREAD, &queued) && queued == 24);
31 CHECK(!fh_event(group, &first, FAN_MODIFY, getpid()));
32 bad = mmap(NULL, fh_page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
33 CHECK(bad != MAP_FAILED && !fh_modify(&first));
34 errno = 0;
35 CHECK(read(group, bad, length) == -1 && errno == EFAULT);
36 CHECK(!ioctl(group, FIONREAD, &queued) && queued == 0);
37 CHECK(!fh_modify(&first));
38 struct iovec split[] = {{bytes, 3}, {bytes + 3, length - 3}};
39 CHECK(readv(group, split, 2) == (ssize_t)length);
40 CHECK(!fh_parse(bytes, length, &record) && fh_equal(&record.handle, &first.handle));
41 CHECK(!fh_modify(&first));
42 struct iovec partial[] = {{bytes, 3}, {bad, length - 3}};
43 errno = 0;
44 CHECK(readv(group, partial, 2) == -1 && errno == EFAULT);
45 CHECK(!ioctl(group, FIONREAD, &queued) && queued == 0);
46 CHECK(!fh_modify(&first) && !fh_modify(&second));
47 CHECK(!ioctl(group, FIONREAD, &queued) && queued == 48);
48 memset(bytes, 0, sizeof(bytes));
49 struct iovec fault[] = {{bytes, length}, {bad, length}};
50 errno = 0;
51 CHECK(readv(group, fault, 2) == -1 && errno == EFAULT);
52 CHECK(!fh_parse(bytes, length, &record) && fh_equal(&record.handle, &first.handle));
53 CHECK(!ioctl(group, FIONREAD, &queued) && queued == 0);
54 errno = 0;
55 CHECK(read(group, bytes, sizeof(bytes)) == -1 && errno == EAGAIN);
56out:
57 if (bad != MAP_FAILED)
58 munmap(bad, fh_page);
59 if (group >= 0)
60 close(group);
61 fh_close(&second);
62 fh_close(&first);
63 return failed;
64}
65
66static int readiness(void) {
67 int failed = 0, group = -1, epoll = -1;
68 struct fh_file file = {.fd = -1};
69 struct epoll_event event = {.events = EPOLLIN | EPOLLET, .data.u64 = 0xf1d}, output;
70 CHECK(!fh_create(&file));
71 group = fh_group(1);
72 epoll = epoll_create1(EPOLL_CLOEXEC);
73 CHECK(group >= 0 && epoll >= 0 && !fh_mark(group, &file, FAN_MARK_ADD, FAN_MODIFY));
74 CHECK(!epoll_ctl(epoll, EPOLL_CTL_ADD, group, &event));
75 CHECK(fh_readable(group, 0) == 0 && epoll_wait(epoll, &output, 1, 0) == 0);
76 CHECK(!fh_modify(&file) && fh_readable(group, 0) == 1);
77 CHECK(epoll_wait(epoll, &output, 1, 1000) == 1 && (output.events & EPOLLIN) &&
78 output.data.u64 == event.data.u64);
79 CHECK(epoll_wait(epoll, &output, 1, 0) == 0);
80 CHECK(!fh_event(group, &file, FAN_MODIFY, getpid()));
81 CHECK(fh_readable(group, 0) == 0);
82 CHECK(!fh_modify(&file));
83 CHECK(epoll_wait(epoll, &output, 1, 1000) == 1);
84 CHECK(!fh_event(group, &file, FAN_MODIFY, getpid()));
85 event.events = EPOLLIN;
86 CHECK(!epoll_ctl(epoll, EPOLL_CTL_MOD, group, &event));
87 CHECK(!fh_modify(&file));
88 CHECK(epoll_wait(epoll, &output, 1, 1000) == 1);
89 CHECK(epoll_wait(epoll, &output, 1, 0) == 1);
90 CHECK(!fh_event(group, &file, FAN_MODIFY, getpid()));
91 CHECK(epoll_wait(epoll, &output, 1, 0) == 0);
92out:
93 if (epoll >= 0)
94 close(epoll);
95 if (group >= 0)
96 close(group);
97 fh_close(&file);
98 return failed;
99}
100
101static int overflow(void) {
102 enum { Targets = 32, Producers = 9 };
103 int failed = 0, group = -1, queued = -1, overflow_count = 0, ordinary = 0, recovery = -1;
104 pid_t child = -1, producers[Producers] = {0};
105 struct fh_file files[Targets];
106 memset(files, 0, sizeof(files));
107 for (int n = 0; n < Targets; ++n)
108 files[n].fd = -1;
109 group = fh_group(1);
110 CHECK(group >= 0);
111 for (int n = 0; n < Targets; ++n) {
112 CHECK(!fh_create(&files[n]));
113 CHECK(!fh_mark(group, &files[n], FAN_MARK_ADD, FAN_OPEN));
114 }
115 for (int producer = 0; producer < Producers; ++producer) {
116 child = fork();
117 CHECK(child >= 0);
118 if (!child) {
119 alarm(10);
120 for (int target = 0; target < Targets; ++target) {
121 int fd = open(files[target].path, O_RDONLY | O_CLOEXEC);
122 if (fd < 0 || close(fd)) {
123 fprintf(stderr, "overflow producer=%d target=%d errno=%d\n", producer, target, errno);
124 _exit(10);
125 }
126 }
127 _exit(0);
128 }
129 producers[producer] = child;
130 /* Reaping fixes producer order without adding writes to the marked files. */
131 int status = fh_reap(child, 12000);
132 child = -1;
133 if (status)
134 fprintf(stderr, "overflow producer=%d pid=%d status=%d\n", producer, producers[producer],
135 status);
136 CHECK(status == 0);
137 }
138 CHECK(!ioctl(group, FIONREAD, &queued) && queued == (FH_QUEUE_LIMIT + 1) * 24);
139 for (int n = 0; n < FH_QUEUE_LIMIT + 1; ++n) {
140 struct fh_record record;
141 CHECK(!fh_take(group, &files[0].handle, &record));
142 if (record.mask & FAN_Q_OVERFLOW) {
143 CHECK(n == FH_QUEUE_LIMIT && record.mask == FAN_Q_OVERFLOW);
144 ++overflow_count;
145 } else {
146 CHECK(ordinary < FH_QUEUE_LIMIT && record.mask == FAN_OPEN);
147 CHECK(record.pid == producers[ordinary / Targets]);
148 CHECK(fh_equal(&record.handle, &files[ordinary % Targets].handle));
149 ++ordinary;
150 }
151 }
152 CHECK(ordinary == FH_QUEUE_LIMIT && overflow_count == 1 && fh_readable(group, 0) == 0);
153 recovery = open(files[0].path, O_RDONLY | O_CLOEXEC);
154 CHECK(recovery >= 0 && !fh_event(group, &files[0], FAN_OPEN, getpid()));
155 CHECK(fh_readable(group, 0) == 0);
156out:
157 if (child > 0)
158 fh_reap(child, 100);
159 if (recovery >= 0)
160 close(recovery);
161 if (group >= 0)
162 close(group);
163 for (int n = 0; n < Targets; ++n)
164 fh_close(&files[n]);
165 return failed;
166}
167int fh_queue(void) {
168 return copies() || readiness() || overflow();
169}