7#include <sys/sendfile.h>
10static volatile int pipe_seen;
11static void caught(
int number) {
12 if (number == SIGPIPE)
13 __atomic_store_n(&pipe_seen, 1, __ATOMIC_RELEASE);
15static int nonblocking(
void) {
16 int failed = 0, pair[2] = {-1, -1};
17 struct tf_file input = {.fd = -1};
18 CHECK(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
19 ssize_t capacity = tf_saturate(pair[0]);
20 CHECK(capacity > 0 && !tf_socket_read(pair[1], capacity, 0, TF_FILLER));
21 CHECK(!tf_create(&input,
"/tmp", capacity + TF_CHUNK, 53));
22 CHECK(!fcntl(pair[0], F_SETFL, O_NONBLOCK));
23 ssize_t result = sendfile(pair[0], input.fd, NULL, capacity + TF_CHUNK);
24 CHECK(result > 0 && result < capacity + TF_CHUNK);
25 CHECK(lseek(input.fd, 0, SEEK_CUR) == result);
27 ssize_t filler = tf_saturate(pair[0]);
29 CHECK(sendfile(pair[0], input.fd, NULL, 1) == -1 && errno == EAGAIN);
30 CHECK(lseek(input.fd, 0, SEEK_CUR) == result);
31 CHECK(!tf_socket_read(pair[1], result, 0, 53));
32 CHECK(!tf_socket_read(pair[1], filler, 0, TF_FILLER));
34 CHECK(sendfile(pair[0], input.fd, &offset, 7) == 7);
35 CHECK(offset == 10 && lseek(input.fd, 0, SEEK_CUR) == result);
36 CHECK(!tf_socket_read(pair[1], 7, 3, 53));
45static int interrupted(
int after_progress) {
46 int failed = 0, pair[2] = {-1, -1}, running = 0;
47 struct tf_file input = {.fd = -1};
48 struct tf_transfer transfer = {.kind = TF_SENDFILE, .result = -2};
50 CHECK(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
51 ssize_t capacity = tf_saturate(pair[0]);
54 CHECK(!tf_socket_read(pair[1], capacity, 0, TF_FILLER));
55 CHECK(!tf_create(&input,
"/tmp", capacity + 2 * TF_CHUNK, 37));
56 transfer.input = input.fd;
57 transfer.output = pair[0];
58 transfer.count = capacity + 2 * TF_CHUNK;
59 CHECK(!pthread_create(&
worker, NULL, tf_worker, &transfer));
61 CHECK(!tf_wait(&transfer.ready, 5000));
62 __atomic_store_n(&transfer.gate, 1, __ATOMIC_RELEASE);
64 struct pollfd entry = {.fd = pair[1], .events = POLLIN};
66 CHECK(poll(&entry, 1, 5000) == 1 && (entry.revents & POLLIN));
67 CHECK(!__atomic_load_n(&transfer.done, __ATOMIC_ACQUIRE));
69 const int64_t until = tf_now() + 3000000000;
70 while (!__atomic_load_n(&transfer.done, __ATOMIC_ACQUIRE) && tf_now() < until) {
71 CHECK(!pthread_kill(
worker, SIGUSR1));
74 CHECK(!tf_wait(&transfer.done, 1000));
75 CHECK(!tf_join(
worker, &transfer));
78 CHECK(transfer.result > 0 && transfer.result < (ssize_t)transfer.count && transfer.error == 0);
79 CHECK(lseek(input.fd, 0, SEEK_CUR) == transfer.result);
80 CHECK(!tf_socket_read(pair[1], transfer.result, 0, 37));
82 CHECK(transfer.result == -1 && transfer.error == EINTR);
83 CHECK(lseek(input.fd, 0, SEEK_CUR) == 0);
84 CHECK(!tf_socket_read(pair[1], capacity, 0, TF_FILLER));
88 tf_diagnostic(&transfer);
90 shutdown(pair[1], SHUT_RD);
91 tf_join(
worker, &transfer);
100static int broken_peer(
void) {
101 int failed = 0, pair[2] = {-1, -1};
102 struct tf_file input = {.fd = -1};
103 CHECK(!tf_create(&input,
"/tmp", 32, 5) && !socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
104 CHECK(!close(pair[1]));
106 __atomic_store_n(&pipe_seen, 0, __ATOMIC_RELEASE);
107 CHECK(sendfile(pair[0], input.fd, NULL, 1) == -1 && errno == EPIPE);
108 CHECK(!tf_wait(&pipe_seen, 1000));
109 CHECK(lseek(input.fd, 0, SEEK_CUR) == 0);
118static int excluded_endpoints(
void) {
119 int failed = 0, pipefd[2] = {-1, -1}, datagram = -1, device = -1, directory = -1,
121 struct tf_file input = {.fd = -1}, output = {.fd = -1};
122 CHECK(!tf_create(&input,
"/tmp", 32, 9) && !tf_create(&output,
"/tmp", 32, 71));
123 CHECK(!pipe(pipefd));
124 CHECK((datagram = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0 &&
125 (device = open(
"/dev/null", O_RDWR)) >= 0 && (directory = open(
"/tmp", O_RDONLY)) >= 0);
126 CHECK((unconnected = socket(AF_UNIX, SOCK_STREAM, 0)) >= 0);
127 for (
int kind = 0; kind < 2; ++kind) {
128 CHECK(tf_copy(kind, input.fd, NULL, pipefd[1], NULL, 1) == -1 && errno == EINVAL);
129 CHECK(tf_copy(kind, pipefd[0], NULL, output.fd, NULL, 1) == -1 && errno == EINVAL);
130 CHECK(tf_copy(kind, input.fd, NULL, datagram, NULL, 1) == -1 && errno == EINVAL);
131 CHECK(tf_copy(kind, input.fd, NULL, device, NULL, 1) == -1 && errno == EINVAL);
132 CHECK(tf_copy(kind, device, NULL, output.fd, NULL, 1) == -1 && errno == EINVAL);
133 CHECK(tf_copy(kind, directory, NULL, output.fd, NULL, 1) == -1 && errno == EISDIR);
136 CHECK(sendfile(output.fd, pipefd[0], &offset, 1) == -1 && errno == ESPIPE);
137 CHECK(sendfile(unconnected, input.fd, NULL, 1) == -1 && errno == ENOTCONN);
138 CHECK(sendfile(unconnected, input.fd, NULL, 0) == -1 && errno == ENOTCONN);
139 CHECK(lseek(input.fd, 0, SEEK_CUR) == 0 && lseek(output.fd, 0, SEEK_CUR) == 0);
140 CHECK(!tf_verify(output.fd, 0, 32, 0, 71));
142 if (unconnected >= 0)
158int transfer_stream(
void) {
159 struct sigaction action = {.sa_handler = caught}, previous_usr, previous_pipe;
160 sigemptyset(&action.sa_mask);
161 if (sigaction(SIGUSR1, &action, &previous_usr))
163 if (sigaction(SIGPIPE, &action, &previous_pipe)) {
164 sigaction(SIGUSR1, &previous_usr, NULL);
168 nonblocking() || interrupted(0) || interrupted(1) || broken_peer() || excluded_endpoints();
169 sigaction(SIGPIPE, &previous_pipe, NULL);
170 sigaction(SIGUSR1, &previous_usr, NULL);