The Pedigree Project 0.1
file-lock-contract-test/lifetime.c
1#define _GNU_SOURCE
2#include <pthread.h>
3#include <signal.h>
4#include <stdlib.h>
5#include <unistd.h>
6
7#include "contract.h"
8#include <sys/socket.h>
9#include <sys/stat.h>
10
11static void* unlock_thread(void* argument) {
12 int* result = argument;
13 *result = fl_lock(*result, FL_CLASSIC, F_UNLCK, 0);
14 return NULL;
15}
16static int process_lifetime(void) {
17 int failed = 0, a = -1, b = -1, alias = -1, other = -1;
18 char path[128] = {0}, other_path[128] = {0};
19 pid_t child = -1;
20 CHECK((a = fl_file(path, "/tmp")) >= 0 && (b = open(path, O_RDWR)) >= 0);
21 CHECK((other = fl_file(other_path, "/tmp")) >= 0);
22 for (int mode = 0; mode < 2; ++mode) {
23 CHECK((alias = open(path, O_RDWR)) >= 0);
24 CHECK(fl_lock(a, FL_CLASSIC, F_WRLCK, 0) == 0);
25 CHECK(fl_query(b, F_OFD_GETLK, 0, 1, F_WRLCK, 0, 0, getpid()) == 0);
26 if (!mode) {
27 CHECK(close(alias) == 0);
28 alias = -1;
29 } else {
30 CHECK(dup2(other, alias) == alias);
31 }
32 CHECK(fl_query(b, F_OFD_GETLK, 0, 1, F_UNLCK, 0, 0, 0) == 0);
33 if (alias >= 0) {
34 close(alias);
35 alias = -1;
36 }
37 }
38 CHECK(fl_lock(a, FL_CLASSIC, F_WRLCK, 0) == 0);
39 int thread_result = a;
40 pthread_t worker;
41 CHECK(pthread_create(&worker, NULL, unlock_thread, &thread_result) == 0);
42 CHECK(pthread_join(worker, NULL) == 0 && thread_result == 0);
43 CHECK(fl_query(b, F_OFD_GETLK, 0, 1, F_UNLCK, 0, 0, 0) == 0);
44 CHECK(fl_lock(a, FL_CLASSIC, F_WRLCK, 0) == 0);
45 const pid_t owner = getpid();
46 CHECK((child = fork()) >= 0);
47 if (!child) {
48 alarm(5);
49 if (fl_query(b, F_GETLK, 0, 1, F_WRLCK, 0, 0, owner) ||
50 fl_lock(a, FL_CLASSIC, F_WRLCK, 0) != -1 || errno != EAGAIN)
51 _exit(10);
52 close(a);
53 close(b);
54 _exit(0);
55 }
56 CHECK(fl_reap(child, 6000) == 0);
57 child = -1;
58 CHECK(fl_query(b, F_OFD_GETLK, 0, 1, F_WRLCK, 0, 0, owner) == 0);
59out:
60 if (child > 0) {
61 kill(child, SIGKILL);
62 fl_reap(child, 1000);
63 }
64 if (alias >= 0)
65 close(alias);
66 if (other >= 0)
67 close(other);
68 if (b >= 0)
69 close(b);
70 if (a >= 0)
71 close(a);
72 if (*path)
73 unlink(path);
74 if (*other_path)
75 unlink(other_path);
76 return failed;
77}
78
79static int shared_fork(int kind) {
80 int failed = 0, a = -1, b = -1, alias = -1, pair[2] = {-1, -1};
81 pid_t child = -1;
82 char path[128] = {0};
83 CHECK((a = fl_file(path, "/tmp")) >= 0 && (b = open(path, O_RDWR)) >= 0);
84 CHECK(fl_lock(a, kind, F_WRLCK, 0) == 0 && (alias = dup(a)) >= 0);
85 CHECK(close(a) == 0);
86 a = -1;
87 CHECK(fl_lock(b, kind, F_WRLCK, 0) == -1 && errno == EAGAIN);
88 CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0 && (child = fork()) >= 0);
89 if (!child) {
90 alarm(6);
91 close(pair[0]);
92 close(b);
93 if (fl_lock(alias, kind, F_WRLCK, 0) || write(pair[1], "h", 1) != 1 || fl_byte(pair[1], 'x'))
94 _exit(10);
95 close(alias);
96 _exit(0);
97 }
98 close(pair[1]);
99 pair[1] = -1;
100 CHECK(fl_byte(pair[0], 'h') == 0);
101 CHECK(close(alias) == 0);
102 alias = -1;
103 CHECK(fl_lock(b, kind, F_WRLCK, 0) == -1 && errno == EAGAIN);
104 CHECK(write(pair[0], "x", 1) == 1 && fl_reap(child, 7000) == 0);
105 child = -1;
106 CHECK(fl_lock(b, kind, F_WRLCK, 0) == 0);
107out:
108 if (child > 0) {
109 kill(child, SIGKILL);
110 fl_reap(child, 1000);
111 }
112 for (int n = 0; n < 2; ++n)
113 if (pair[n] >= 0)
114 close(pair[n]);
115 if (alias >= 0)
116 close(alias);
117 if (b >= 0)
118 close(b);
119 if (a >= 0)
120 close(a);
121 if (*path)
122 unlink(path);
123 return failed;
124}
125
126static int shared_rights(int kind) {
127 int failed = 0, a = -1, b = -1, control[2] = {-1, -1}, transport[2] = {-1, -1};
128 pid_t child = -1;
129 char path[128] = {0};
130 CHECK((a = fl_file(path, "/tmp")) >= 0 && (b = open(path, O_RDWR)) >= 0);
131 CHECK(fl_lock(a, kind, F_WRLCK, 0) == 0);
132 CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, control) == 0);
133 CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, transport) == 0 && (child = fork()) >= 0);
134 if (!child) {
135 alarm(8);
136 close(a);
137 close(b);
138 close(control[0]);
139 close(transport[0]);
140 if (write(control[1], "r", 1) != 1 || fl_byte(control[1], 'g'))
141 _exit(10);
142 int received = fl_receive_fd(transport[1]);
143 if (received < 0 || fl_lock(received, kind, F_WRLCK, 0) || write(control[1], "h", 1) != 1 ||
144 fl_byte(control[1], 'x'))
145 _exit(11);
146 close(received);
147 _exit(0);
148 }
149 close(control[1]);
150 control[1] = -1;
151 close(transport[1]);
152 transport[1] = -1;
153 CHECK(fl_byte(control[0], 'r') == 0 && fl_send_fd(transport[0], a) == 0);
154 CHECK(close(a) == 0);
155 a = -1;
156 CHECK(fl_lock(b, kind, F_WRLCK, 0) == -1 && errno == EAGAIN);
157 CHECK(write(control[0], "g", 1) == 1 && fl_byte(control[0], 'h') == 0);
158 CHECK(fl_lock(b, kind, F_WRLCK, 0) == -1 && errno == EAGAIN);
159 CHECK(write(control[0], "x", 1) == 1 && fl_reap(child, 9000) == 0);
160 child = -1;
161 CHECK(fl_lock(b, kind, F_WRLCK, 0) == 0);
162out:
163 if (child > 0) {
164 kill(child, SIGKILL);
165 fl_reap(child, 1000);
166 }
167 for (int n = 0; n < 2; ++n) {
168 if (control[n] >= 0)
169 close(control[n]);
170 if (transport[n] >= 0)
171 close(transport[n]);
172 }
173 if (b >= 0)
174 close(b);
175 if (a >= 0)
176 close(a);
177 if (*path)
178 unlink(path);
179 return failed;
180}
181
182int file_lock_exec(int argc, char** argv) {
183 if (argc != 5)
184 return 20;
185 alarm(6);
186 const int fd = atoi(argv[2]), socket = atoi(argv[3]), closed = atoi(argv[4]);
187 const int result = fcntl(fd, F_GETFD, 0);
188 if (closed ? result != -1 || errno != EBADF : result < 0)
189 return 21;
190 return write(socket, "e", 1) == 1 && fl_byte(socket, 'x') == 0 ? 0 : 22;
191}
192static int exec_lifetime(int kind, int cloexec) {
193 int failed = 0, a = -1, probe = -1, pair[2] = {-1, -1};
194 pid_t child = -1;
195 char path[128] = {0};
196 CHECK((a = fl_file(path, "/tmp")) >= 0 && (probe = open(path, O_RDWR)) >= 0);
197 CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == 0 && (child = fork()) >= 0);
198 if (!child) {
199 alarm(6);
200 close(pair[0]);
201 close(probe);
202 if (cloexec == 2 && open(path, O_RDONLY | O_CLOEXEC) < 0)
203 _exit(12);
204 if (fl_lock(a, kind, F_WRLCK, 0) || fcntl(a, F_SETFD, cloexec == 1 ? FD_CLOEXEC : 0))
205 _exit(10);
206 char descriptor[20], socket[20], flag[4];
207 snprintf(descriptor, sizeof(descriptor), "%d", a);
208 snprintf(socket, sizeof(socket), "%d", pair[1]);
209 snprintf(flag, sizeof(flag), "%d", cloexec == 1);
210 execl(LOCK_APP, LOCK_APP, "lock-exec", descriptor, socket, flag, (char*)NULL);
211 _exit(11);
212 }
213 close(pair[1]);
214 pair[1] = -1;
215 close(a);
216 a = -1;
217 CHECK(fl_byte(pair[0], 'e') == 0);
218 if (cloexec)
219 CHECK(fl_lock(probe, kind, F_WRLCK, 0) == 0);
220 else
221 CHECK(fl_lock(probe, kind, F_WRLCK, 0) == -1 && errno == EAGAIN);
222 CHECK(write(pair[0], "x", 1) == 1 && fl_reap(child, 7000) == 0);
223 child = -1;
224 CHECK(fl_lock(probe, kind, F_WRLCK, 0) == 0);
225out:
226 if (child > 0) {
227 kill(child, SIGKILL);
228 fl_reap(child, 1000);
229 }
230 for (int n = 0; n < 2; ++n)
231 if (pair[n] >= 0)
232 close(pair[n]);
233 if (probe >= 0)
234 close(probe);
235 if (a >= 0)
236 close(a);
237 if (*path)
238 unlink(path);
239 return failed;
240}
241
242static int inode_aliases(void) {
243 int failed = 0, a = -1, b = -1, c = -1;
244 char path[128] = {0}, linked[160] = {0}, renamed[160] = {0};
245 struct stat first, second;
246 if (geteuid()) {
247 puts("FILE-LOCK-CONTRACT: SKIP disk hard-link fixture requires root");
248 return 0;
249 }
250 CHECK((a = fl_file(path, "")) >= 0);
251 snprintf(linked, sizeof(linked), "%s.link", path);
252 snprintf(renamed, sizeof(renamed), "%s.renamed", path);
253 CHECK(link(path, linked) == 0 && (b = open(linked, O_RDWR)) >= 0);
254 CHECK(fstat(a, &first) == 0 && fstat(b, &second) == 0 && first.st_dev == second.st_dev &&
255 first.st_ino == second.st_ino);
256 CHECK(fl_lock(a, FL_OFD, F_WRLCK, 0) == 0);
257 CHECK(fl_lock(b, FL_OFD, F_WRLCK, 0) == -1 && errno == EAGAIN);
258 CHECK(rename(path, renamed) == 0 && unlink(linked) == 0);
259 CHECK((c = open(renamed, O_RDWR)) >= 0 && unlink(renamed) == 0);
260 CHECK(fl_query(c, F_GETLK, 0, 1, F_WRLCK, 0, 0, -1) == 0);
261 CHECK(fl_lock(a, FL_OFD, F_UNLCK, 0) == 0);
262 CHECK(fl_lock(a, FL_CLASSIC, F_WRLCK, 0) == 0);
263 CHECK(close(b) == 0);
264 b = -1;
265 CHECK(fl_query(c, F_OFD_GETLK, 0, 1, F_UNLCK, 0, 0, 0) == 0);
266 CHECK(fl_lock(a, FL_FLOCK, F_WRLCK, 0) == 0);
267 CHECK(fl_lock(c, FL_FLOCK, F_WRLCK, 0) == -1 && errno == EAGAIN);
268 CHECK(close(a) == 0);
269 a = -1;
270 CHECK(fl_lock(c, FL_FLOCK, F_WRLCK, 0) == 0);
271out:
272 if (c >= 0)
273 close(c);
274 if (b >= 0)
275 close(b);
276 if (a >= 0)
277 close(a);
278 if (*renamed)
279 unlink(renamed);
280 if (*linked)
281 unlink(linked);
282 if (*path)
283 unlink(path);
284 return failed;
285}
286int file_lock_lifetime(void) {
287 if (process_lifetime() || exec_lifetime(FL_CLASSIC, 2))
288 return 1;
289 for (int kind = 0; kind < 3; ++kind) {
290 if (kind != FL_CLASSIC && (shared_fork(kind) || shared_rights(kind)))
291 return 1;
292 if (exec_lifetime(kind, 0) || exec_lifetime(kind, 1))
293 return 1;
294 }
295 return inode_aliases();
296}