The Pedigree Project 0.1
transfer-contract-test/races.c
1#define _GNU_SOURCE
2#include <unistd.h>
3
4#include "contract.h"
5
6struct flag_change {
7 struct tf_transfer* transfer;
8 volatile int ready, done;
9 int result;
10};
11static void* set_append(void* argument) {
12 struct flag_change* change = argument;
13 __atomic_store_n(&change->ready, 1, __ATOMIC_RELEASE);
14 if (!tf_wait(&change->transfer->gate, 5000))
15 change->result = fcntl(change->transfer->output, F_SETFL, O_APPEND);
16 __atomic_store_n(&change->done, 1, __ATOMIC_RELEASE);
17 return NULL;
18}
19static int explicit_output_flags(void) {
20 int failed = 0, copy_started = 0, flags_started = 0;
21 struct tf_file input = {.fd = -1}, output = {.fd = -1};
22 off_t from = 3, to = 24;
23 struct tf_transfer transfer = {.kind = TF_COPY_RANGE,
24 .input_offset = &from,
25 .output_offset = &to,
26 .count = 32,
27 .result = -2};
28 struct flag_change change = {.transfer = &transfer, .result = -2};
29 pthread_t copy, flags;
30 CHECK(!tf_create(&input, "/tmp", 128, 19) && !tf_create(&output, "/tmp", 128, 83));
31 CHECK(lseek(output.fd, 17, SEEK_SET) == 17);
32 transfer.input = input.fd;
33 transfer.output = output.fd;
34 CHECK(!pthread_create(&copy, NULL, tf_worker, &transfer));
35 copy_started = 1;
36 CHECK(!pthread_create(&flags, NULL, set_append, &change));
37 flags_started = 1;
38 CHECK(!tf_wait(&transfer.ready, 5000) && !tf_wait(&change.ready, 5000));
39 __atomic_store_n(&transfer.gate, 1, __ATOMIC_RELEASE);
40 CHECK(!tf_wait(&transfer.done, 5000) && !tf_wait(&change.done, 5000));
41 CHECK(change.result == 0 && (fcntl(output.fd, F_GETFL) & O_APPEND));
42 if (transfer.result == 32) {
43 CHECK(from == 35 && to == 56 && !tf_verify(output.fd, 24, 32, 3, 19));
44 } else {
45 CHECK(transfer.result == -1 && transfer.error == EBADF && from == 3 && to == 24);
46 CHECK(!tf_verify(output.fd, 0, 128, 0, 83));
47 }
48 CHECK(lseek(input.fd, 0, SEEK_CUR) == 0 && lseek(output.fd, 0, SEEK_CUR) == 17);
49 CHECK(copy_file_range(input.fd, &from, output.fd, &to, 1, 0) == -1 && errno == EBADF);
50out:
51 if (failed)
52 tf_diagnostic(&transfer);
53 __atomic_store_n(&transfer.gate, 1, __ATOMIC_RELEASE);
54 if (copy_started)
55 tf_join(copy, &transfer);
56 if (flags_started) {
57 if (tf_wait(&change.done, 5000))
58 _exit(61);
59 pthread_join(flags, NULL);
60 }
61 tf_destroy(&output);
62 tf_destroy(&input);
63 return failed;
64}
65
66static int reversed_pair(int kind) {
67 int failed = 0, started = 0;
68 const size_t length = TF_CHUNK + 17;
69 struct tf_file left = {.fd = -1}, right = {.fd = -1};
70 struct tf_transfer transfers[2] = {{.kind = kind, .result = -2}, {.kind = kind, .result = -2}};
71 pthread_t workers[2];
72 CHECK(!tf_create(&left, "/tmp", 3 * length, 23) && !tf_create(&right, "/tmp", 3 * length, 67));
73 transfers[0].input = left.fd;
74 transfers[0].output = right.fd;
75 transfers[1].input = right.fd;
76 transfers[1].output = left.fd;
77 for (int n = 0; n < 2; ++n) {
78 transfers[n].count = length;
79 CHECK(!pthread_create(&workers[n], NULL, tf_worker, &transfers[n]));
80 ++started;
81 }
82 CHECK(!tf_wait(&transfers[0].ready, 5000) && !tf_wait(&transfers[1].ready, 5000));
83 __atomic_store_n(&transfers[0].gate, 1, __ATOMIC_RELEASE);
84 __atomic_store_n(&transfers[1].gate, 1, __ATOMIC_RELEASE);
85 CHECK(!tf_wait(&transfers[0].done, 5000) && !tf_wait(&transfers[1].done, 5000));
86 CHECK(transfers[0].result == (ssize_t)length && transfers[1].result == (ssize_t)length);
87 CHECK(lseek(left.fd, 0, SEEK_CUR) == (off_t)(2 * length) &&
88 lseek(right.fd, 0, SEEK_CUR) == (off_t)(2 * length));
89 unsigned char first;
90 CHECK(pread(left.fd, &first, 1, 0) == 1);
91 const int first_seed = first == tf_pattern(0, 23) ? 23 : 67;
92 CHECK(!tf_verify(left.fd, 0, length, 0, first_seed));
93 CHECK(!tf_verify(right.fd, 0, length, 0, first_seed));
94 const int second_seed = first_seed == 23 ? 67 : 23;
95 CHECK(!tf_verify(left.fd, length, length, length, second_seed));
96 CHECK(!tf_verify(right.fd, length, length, length, second_seed));
97 CHECK(!tf_verify(left.fd, 2 * length, length, 2 * length, 23));
98 CHECK(!tf_verify(right.fd, 2 * length, length, 2 * length, 67));
99out:
100 if (failed) {
101 tf_diagnostic(&transfers[0]);
102 tf_diagnostic(&transfers[1]);
103 }
104 for (int n = 0; n < started; ++n)
105 __atomic_store_n(&transfers[n].gate, 1, __ATOMIC_RELEASE);
106 for (int n = 0; n < started; ++n)
107 tf_join(workers[n], &transfers[n]);
108 tf_destroy(&right);
109 tf_destroy(&left);
110 return failed;
111}
112int transfer_races(void) {
113 for (int n = 0; n < 4; ++n)
114 if (reversed_pair(TF_SENDFILE) || reversed_pair(TF_COPY_RANGE) || explicit_output_flags())
115 return 1;
116 return 0;
117}