Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/tests/sys/sendfile_test.c
39530 views
1
/*-
2
* Copyright (c) 2018 Enji Cooper.
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/param.h>
28
#include <sys/mman.h>
29
#include <sys/socket.h>
30
#include <sys/stat.h>
31
#include <sys/sysctl.h>
32
#include <sys/uio.h>
33
#include <err.h>
34
#include <errno.h>
35
#include <fcntl.h>
36
#include <netdb.h>
37
#include <paths.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
43
#include <atf-c.h>
44
45
const char DETERMINISTIC_PATTERN[] =
46
"The past is already gone, the future is not yet here. There's only one moment for you to live.\n";
47
48
#define SOURCE_FILE "source"
49
#define DESTINATION_FILE "dest"
50
51
#define PORTRANGE_FIRST "net.inet.ip.portrange.first"
52
#define PORTRANGE_LAST "net.inet.ip.portrange.last"
53
54
static int portrange_first, portrange_last;
55
56
static int
57
get_int_via_sysctlbyname(const char *oidname)
58
{
59
size_t oldlen;
60
int int_value;
61
62
oldlen = sizeof(int_value);
63
64
ATF_REQUIRE_EQ_MSG(sysctlbyname(oidname, &int_value, &oldlen, NULL, 0),
65
0, "sysctlbyname(%s, ...) failed: %s", oidname, strerror(errno));
66
ATF_REQUIRE_EQ_MSG(sizeof(int_value), oldlen, "sanity check failed");
67
68
return (int_value);
69
}
70
71
static int
72
generate_random_port(int seed)
73
{
74
int random_port;
75
76
printf("Generating a random port with seed=%d\n", seed);
77
if (portrange_first == 0) {
78
portrange_first = get_int_via_sysctlbyname(PORTRANGE_FIRST);
79
printf("Port range lower bound: %d\n", portrange_first);
80
}
81
82
if (portrange_last == 0) {
83
portrange_last = get_int_via_sysctlbyname(PORTRANGE_LAST);
84
printf("Port range upper bound: %d\n", portrange_last);
85
}
86
87
srand((unsigned)seed);
88
89
random_port = rand() % (portrange_last - portrange_first) +
90
portrange_first;
91
92
printf("Random port generated: %d\n", random_port);
93
return (random_port);
94
}
95
96
static void
97
resolve_localhost(struct addrinfo **res, int domain, int type, int port)
98
{
99
const char *host;
100
char *serv;
101
struct addrinfo hints;
102
int error;
103
104
switch (domain) {
105
case AF_INET:
106
host = "127.0.0.1";
107
break;
108
case AF_INET6:
109
host = "::1";
110
break;
111
default:
112
atf_tc_fail("unhandled domain: %d", domain);
113
}
114
115
ATF_REQUIRE_MSG(asprintf(&serv, "%d", port) >= 0,
116
"asprintf failed: %s", strerror(errno));
117
118
memset(&hints, 0, sizeof(hints));
119
hints.ai_family = domain;
120
hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_NUMERICHOST;
121
hints.ai_socktype = type;
122
123
error = getaddrinfo(host, serv, &hints, res);
124
ATF_REQUIRE_EQ_MSG(error, 0,
125
"getaddrinfo failed: %s", gai_strerror(error));
126
free(serv);
127
}
128
129
static int
130
make_socket(int domain, int type, int protocol)
131
{
132
int sock;
133
134
sock = socket(domain, type, protocol);
135
ATF_REQUIRE_MSG(sock != -1, "socket(%d, %d, 0) failed: %s",
136
domain, type, strerror(errno));
137
138
return (sock);
139
}
140
141
static int
142
setup_client(int domain, int type, int port)
143
{
144
struct addrinfo *res;
145
char host[NI_MAXHOST+1];
146
int error, sock;
147
148
resolve_localhost(&res, domain, type, port);
149
error = getnameinfo(
150
(const struct sockaddr*)res->ai_addr, res->ai_addrlen,
151
host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);
152
ATF_REQUIRE_EQ_MSG(error, 0,
153
"getnameinfo failed: %s", gai_strerror(error));
154
printf(
155
"Will try to connect to host='%s', address_family=%d, "
156
"socket_type=%d\n",
157
host, res->ai_family, res->ai_socktype);
158
/* Avoid a double print when forked by flushing. */
159
fflush(stdout);
160
sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
161
error = connect(sock, (struct sockaddr*)res->ai_addr, res->ai_addrlen);
162
freeaddrinfo(res);
163
ATF_REQUIRE_EQ_MSG(error, 0, "connect failed: %s", strerror(errno));
164
return (sock);
165
}
166
167
/*
168
* XXX: use linear probing to find a free port and eliminate `port` argument as
169
* a [const] int (it will need to be a pointer so it can be passed back out of
170
* the function and can influence which port `setup_client(..)` connects on.
171
*/
172
static int
173
setup_server(int domain, int type, int port)
174
{
175
struct addrinfo *res;
176
char host[NI_MAXHOST+1];
177
int error, sock;
178
179
resolve_localhost(&res, domain, type, port);
180
sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
181
182
error = getnameinfo(
183
(const struct sockaddr*)res->ai_addr, res->ai_addrlen,
184
host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);
185
ATF_REQUIRE_EQ_MSG(error, 0,
186
"getnameinfo failed: %s", gai_strerror(error));
187
printf(
188
"Will try to bind socket to host='%s', address_family=%d, "
189
"socket_type=%d\n",
190
host, res->ai_family, res->ai_socktype);
191
/* Avoid a double print when forked by flushing. */
192
fflush(stdout);
193
error = bind(sock, res->ai_addr, res->ai_addrlen);
194
freeaddrinfo(res);
195
ATF_REQUIRE_EQ_MSG(error, 0, "bind failed: %s", strerror(errno));
196
error = listen(sock, 1);
197
ATF_REQUIRE_EQ_MSG(error, 0, "listen failed: %s", strerror(errno));
198
199
return (sock);
200
}
201
202
/*
203
* This function is a helper routine for taking data being sent by `sendfile` via
204
* `server_sock`, and pushing the received stream out to a file, denoted by
205
* `dest_filename`.
206
*/
207
static void
208
server_cat(const char *dest_filename, int server_sock, size_t len)
209
{
210
char *buffer, *buf_window_ptr;
211
int recv_sock;
212
size_t buffer_size;
213
ssize_t received_bytes, recv_ret;
214
215
/*
216
* Ensure that there isn't excess data sent across the wire by
217
* capturing 10 extra bytes (plus 1 for nul).
218
*/
219
buffer_size = len + 10 + 1;
220
buffer = calloc(buffer_size, sizeof(char));
221
if (buffer == NULL)
222
err(1, "malloc failed");
223
224
recv_sock = accept(server_sock, NULL, 0);
225
if (recv_sock == -1)
226
err(1, "accept failed");
227
228
buf_window_ptr = buffer;
229
received_bytes = 0;
230
do {
231
recv_ret = recv(recv_sock, buf_window_ptr,
232
buffer_size - received_bytes, 0);
233
if (recv_ret <= 0)
234
break;
235
buf_window_ptr += recv_ret;
236
received_bytes += recv_ret;
237
} while (received_bytes < buffer_size);
238
239
atf_utils_create_file(dest_filename, "%s", buffer);
240
241
(void)close(recv_sock);
242
(void)close(server_sock);
243
free(buffer);
244
245
if (received_bytes != len)
246
errx(1, "received unexpected data: %zd != %zd", received_bytes,
247
len);
248
}
249
250
static int
251
setup_tcp_server(int domain, int port)
252
{
253
254
return (setup_server(domain, SOCK_STREAM, port));
255
}
256
257
static int
258
setup_tcp_client(int domain, int port)
259
{
260
261
return (setup_client(domain, SOCK_STREAM, port));
262
}
263
264
static off_t
265
file_size_from_fd(int fd)
266
{
267
struct stat st;
268
269
ATF_REQUIRE_EQ_MSG(0, fstat(fd, &st),
270
"fstat failed: %s", strerror(errno));
271
272
return (st.st_size);
273
}
274
275
/*
276
* NB: `nbytes` == 0 has special connotations given the sendfile(2) API
277
* contract. In short, "send the whole file" (paraphrased).
278
*/
279
static void
280
verify_source_and_dest(const char* dest_filename, int src_fd, off_t offset,
281
size_t nbytes)
282
{
283
char *dest_pointer, *src_pointer;
284
off_t dest_file_size, src_file_size;
285
size_t length;
286
int dest_fd;
287
288
atf_utils_cat_file(dest_filename, "dest_file: ");
289
290
dest_fd = open(dest_filename, O_RDONLY);
291
ATF_REQUIRE_MSG(dest_fd != -1, "open failed");
292
293
dest_file_size = file_size_from_fd(dest_fd);
294
src_file_size = file_size_from_fd(src_fd);
295
296
/*
297
* Per sendfile(2), "send the whole file" (paraphrased). This means
298
* that we need to grab the file size, as passing in length = 0 with
299
* mmap(2) will result in a failure with EINVAL (length = 0 is invalid).
300
*/
301
length = (nbytes == 0) ? (size_t)(src_file_size - offset) : nbytes;
302
303
ATF_REQUIRE_EQ_MSG(dest_file_size, length,
304
"number of bytes written out to %s (%ju) doesn't match the "
305
"expected number of bytes (%zu)", dest_filename, dest_file_size,
306
length);
307
308
ATF_REQUIRE_EQ_MSG(0, lseek(src_fd, offset, SEEK_SET),
309
"lseek failed: %s", strerror(errno));
310
311
dest_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, dest_fd, 0);
312
ATF_REQUIRE_MSG(dest_pointer != MAP_FAILED, "mmap failed: %s",
313
strerror(errno));
314
315
printf("Will mmap in the source file from offset=%jd to length=%zu\n",
316
offset, length);
317
318
src_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, src_fd, offset);
319
ATF_REQUIRE_MSG(src_pointer != MAP_FAILED, "mmap failed: %s",
320
strerror(errno));
321
322
ATF_REQUIRE_EQ_MSG(0, memcmp(src_pointer, dest_pointer, length),
323
"Contents of source and destination do not match. '%s' != '%s'",
324
src_pointer, dest_pointer);
325
326
(void)munmap(src_pointer, length);
327
(void)munmap(dest_pointer, length);
328
(void)close(dest_fd);
329
}
330
331
static void
332
fd_positive_file_test(int domain)
333
{
334
off_t offset;
335
size_t nbytes, pattern_size;
336
int client_sock, error, fd, port, server_sock;
337
pid_t server_pid;
338
339
pattern_size = strlen(DETERMINISTIC_PATTERN);
340
341
atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
342
fd = open(SOURCE_FILE, O_RDONLY);
343
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
344
345
port = generate_random_port(__LINE__ + domain);
346
server_sock = setup_tcp_server(domain, port);
347
client_sock = setup_tcp_client(domain, port);
348
349
server_pid = atf_utils_fork();
350
if (server_pid == 0) {
351
(void)close(client_sock);
352
server_cat(DESTINATION_FILE, server_sock, pattern_size);
353
_exit(0);
354
} else
355
(void)close(server_sock);
356
357
nbytes = 0;
358
offset = 0;
359
error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
360
SF_FLAGS(0, 0));
361
ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));
362
(void)close(client_sock);
363
364
atf_utils_wait(server_pid, 0, "", "");
365
verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
366
367
(void)close(fd);
368
}
369
370
ATF_TC(fd_positive_file_v4);
371
ATF_TC_HEAD(fd_positive_file_v4, tc)
372
{
373
374
atf_tc_set_md_var(tc, "descr",
375
"Verify regular file as file descriptor support (IPv4)");
376
}
377
ATF_TC_BODY(fd_positive_file_v4, tc)
378
{
379
380
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
381
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
382
383
fd_positive_file_test(AF_INET);
384
}
385
386
ATF_TC(fd_positive_file_v6);
387
ATF_TC_HEAD(fd_positive_file_v6, tc)
388
{
389
390
atf_tc_set_md_var(tc, "descr",
391
"Verify regular file as file descriptor support (IPv6)");
392
}
393
ATF_TC_BODY(fd_positive_file_v6, tc)
394
{
395
396
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
397
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
398
399
fd_positive_file_test(AF_INET6);
400
}
401
402
static void
403
fd_positive_shm_test(int domain)
404
{
405
char *shm_pointer;
406
off_t offset;
407
size_t nbytes, pattern_size;
408
pid_t server_pid;
409
int client_sock, error, fd, port, server_sock;
410
411
pattern_size = strlen(DETERMINISTIC_PATTERN);
412
413
printf("pattern size: %zu\n", pattern_size);
414
415
fd = shm_open(SHM_ANON, O_RDWR|O_CREAT, 0600);
416
ATF_REQUIRE_MSG(fd != -1, "shm_open failed: %s", strerror(errno));
417
ATF_REQUIRE_EQ_MSG(0, ftruncate(fd, pattern_size),
418
"ftruncate failed: %s", strerror(errno));
419
shm_pointer = mmap(NULL, pattern_size, PROT_READ|PROT_WRITE,
420
MAP_SHARED, fd, 0);
421
ATF_REQUIRE_MSG(shm_pointer != MAP_FAILED,
422
"mmap failed: %s", strerror(errno));
423
memcpy(shm_pointer, DETERMINISTIC_PATTERN, pattern_size);
424
ATF_REQUIRE_EQ_MSG(0,
425
memcmp(shm_pointer, DETERMINISTIC_PATTERN, pattern_size),
426
"memcmp showed data mismatch: '%s' != '%s'",
427
DETERMINISTIC_PATTERN, shm_pointer);
428
429
port = generate_random_port(__LINE__ + domain);
430
server_sock = setup_tcp_server(domain, port);
431
client_sock = setup_tcp_client(domain, port);
432
433
server_pid = atf_utils_fork();
434
if (server_pid == 0) {
435
(void)close(client_sock);
436
server_cat(DESTINATION_FILE, server_sock, pattern_size);
437
_exit(0);
438
} else
439
(void)close(server_sock);
440
441
nbytes = 0;
442
offset = 0;
443
error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
444
SF_FLAGS(0, 0));
445
ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));
446
(void)close(client_sock);
447
448
atf_utils_wait(server_pid, 0, "", "");
449
verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
450
451
(void)munmap(shm_pointer, sizeof(DETERMINISTIC_PATTERN));
452
(void)close(fd);
453
}
454
455
ATF_TC(fd_positive_shm_v4);
456
ATF_TC_HEAD(fd_positive_shm_v4, tc)
457
{
458
459
atf_tc_set_md_var(tc, "descr",
460
"Verify shared memory as file descriptor support (IPv4)");
461
}
462
ATF_TC_BODY(fd_positive_shm_v4, tc)
463
{
464
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
465
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
466
467
fd_positive_shm_test(AF_INET);
468
}
469
470
ATF_TC(fd_positive_shm_v6);
471
ATF_TC_HEAD(fd_positive_shm_v6, tc)
472
{
473
474
atf_tc_set_md_var(tc, "descr",
475
"Verify shared memory as file descriptor support (IPv6))");
476
}
477
ATF_TC_BODY(fd_positive_shm_v6, tc)
478
{
479
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
480
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
481
482
fd_positive_shm_test(AF_INET6);
483
}
484
485
static void
486
fd_negative_bad_fd_test(int domain)
487
{
488
int client_sock, error, fd, port, server_sock;
489
490
port = generate_random_port(__LINE__ + domain);
491
server_sock = setup_tcp_server(domain, port);
492
client_sock = setup_tcp_client(domain, port);
493
494
fd = -1;
495
496
error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
497
ATF_REQUIRE_ERRNO(EBADF, error == -1);
498
499
(void)close(client_sock);
500
(void)close(server_sock);
501
}
502
503
ATF_TC(fd_negative_bad_fd_v4);
504
ATF_TC_HEAD(fd_negative_bad_fd_v4, tc)
505
{
506
507
atf_tc_set_md_var(tc, "descr",
508
"Verify bad file descriptor returns EBADF (IPv4)");
509
}
510
ATF_TC_BODY(fd_negative_bad_fd_v4, tc)
511
{
512
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
513
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
514
515
fd_negative_bad_fd_test(AF_INET);
516
}
517
518
ATF_TC(fd_negative_bad_fd_v6);
519
ATF_TC_HEAD(fd_negative_bad_fd_v6, tc)
520
{
521
522
atf_tc_set_md_var(tc, "descr",
523
"Verify bad file descriptor returns EBADF (IPv6)");
524
}
525
ATF_TC_BODY(fd_negative_bad_fd_v6, tc)
526
{
527
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
528
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
529
530
fd_negative_bad_fd_test(AF_INET6);
531
}
532
533
static void
534
flags_test(int domain)
535
{
536
off_t offset;
537
size_t nbytes, pattern_size;
538
int client_sock, error, fd, i, port, server_sock;
539
pid_t server_pid;
540
int16_t number_pages = 10;
541
542
pattern_size = strlen(DETERMINISTIC_PATTERN);
543
544
struct testcase {
545
int16_t readahead_pages, flags;
546
} testcases[] = {
547
/* This is covered in `:fd_positive_file` */
548
#if 0
549
{
550
.readahead_pages = 0,
551
.flags = 0
552
},
553
#endif
554
{
555
.readahead_pages = 0,
556
.flags = SF_NOCACHE
557
},
558
#ifdef SF_USER_READAHEAD
559
{
560
.readahead_pages = 0,
561
.flags = SF_NOCACHE|SF_USER_READAHEAD
562
},
563
{
564
.readahead_pages = 0,
565
.flags = SF_USER_READAHEAD
566
},
567
#endif
568
{
569
.readahead_pages = number_pages,
570
.flags = 0
571
},
572
{
573
.readahead_pages = number_pages,
574
.flags = SF_NOCACHE
575
},
576
#ifdef SF_USER_READAHEAD
577
{
578
.readahead_pages = number_pages,
579
.flags = SF_NOCACHE|SF_USER_READAHEAD
580
},
581
#endif
582
{
583
.readahead_pages = number_pages,
584
.flags = SF_NOCACHE
585
},
586
{
587
.readahead_pages = number_pages,
588
.flags = SF_NODISKIO
589
}
590
};
591
592
atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
593
for (i = 0; i < nitems(testcases); i++) {
594
fd = open(SOURCE_FILE, O_RDONLY);
595
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
596
597
port = generate_random_port(i * __LINE__ + domain);
598
server_sock = setup_tcp_server(domain, port);
599
client_sock = setup_tcp_client(domain, port);
600
601
server_pid = atf_utils_fork();
602
if (server_pid == 0) {
603
(void)close(client_sock);
604
server_cat(DESTINATION_FILE, server_sock, pattern_size);
605
_exit(0);
606
} else
607
(void)close(server_sock);
608
609
nbytes = 0;
610
offset = 0;
611
error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
612
SF_FLAGS(testcases[i].readahead_pages, testcases[i].flags));
613
ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",
614
i, strerror(errno));
615
(void)close(client_sock);
616
617
atf_utils_wait(server_pid, 0, "", "");
618
verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
619
620
(void)close(fd);
621
}
622
}
623
624
ATF_TC(flags_v4);
625
ATF_TC_HEAD(flags_v4, tc)
626
{
627
628
atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)");
629
}
630
ATF_TC_BODY(flags_v4, tc)
631
{
632
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
633
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
634
635
flags_test(AF_INET);
636
}
637
638
ATF_TC(flags_v6);
639
ATF_TC_HEAD(flags_v6, tc)
640
{
641
642
atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)");
643
}
644
ATF_TC_BODY(flags_v6, tc)
645
{
646
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
647
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
648
649
flags_test(AF_INET6);
650
}
651
652
static void
653
hdtr_positive_test(int domain)
654
{
655
struct iovec headers[1], trailers[1];
656
struct testcase {
657
bool include_headers, include_trailers;
658
} testcases[] = {
659
/* This is covered in `:fd_positive_file` */
660
#if 0
661
{
662
.include_headers = false,
663
.include_trailers = false
664
},
665
#endif
666
{
667
.include_headers = true,
668
.include_trailers = false
669
},
670
{
671
.include_headers = false,
672
.include_trailers = true
673
},
674
{
675
.include_headers = true,
676
.include_trailers = true
677
}
678
};
679
off_t offset;
680
size_t nbytes;
681
int client_sock, error, fd, fd2, i, port, rc, server_sock;
682
pid_t server_pid;
683
684
headers[0].iov_base = "This is a header";
685
headers[0].iov_len = strlen(headers[0].iov_base);
686
trailers[0].iov_base = "This is a trailer";
687
trailers[0].iov_len = strlen(trailers[0].iov_base);
688
offset = 0;
689
nbytes = 0;
690
691
for (i = 0; i < nitems(testcases); i++) {
692
struct sf_hdtr hdtr;
693
char *pattern;
694
695
if (testcases[i].include_headers) {
696
hdtr.headers = headers;
697
hdtr.hdr_cnt = nitems(headers);
698
} else {
699
hdtr.headers = NULL;
700
hdtr.hdr_cnt = 0;
701
}
702
703
if (testcases[i].include_trailers) {
704
hdtr.trailers = trailers;
705
hdtr.trl_cnt = nitems(trailers);
706
} else {
707
hdtr.trailers = NULL;
708
hdtr.trl_cnt = 0;
709
}
710
711
port = generate_random_port(i * __LINE__ + domain);
712
server_sock = setup_tcp_server(domain, port);
713
client_sock = setup_tcp_client(domain, port);
714
715
rc = asprintf(&pattern, "%s%s%s",
716
testcases[i].include_headers ? (char *)headers[0].iov_base : "",
717
DETERMINISTIC_PATTERN,
718
testcases[i].include_trailers ? (char *)trailers[0].iov_base : "");
719
ATF_REQUIRE_MSG(rc != -1, "asprintf failed: %s", strerror(errno));
720
721
atf_utils_create_file(SOURCE_FILE ".full", "%s", pattern);
722
atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
723
724
fd = open(SOURCE_FILE, O_RDONLY);
725
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
726
727
fd2 = open(SOURCE_FILE ".full", O_RDONLY);
728
ATF_REQUIRE_MSG(fd2 != -1, "open failed: %s", strerror(errno));
729
730
server_pid = atf_utils_fork();
731
if (server_pid == 0) {
732
(void)close(client_sock);
733
server_cat(DESTINATION_FILE, server_sock,
734
strlen(pattern));
735
_exit(0);
736
} else
737
(void)close(server_sock);
738
739
error = sendfile(fd, client_sock, offset, nbytes, &hdtr,
740
NULL, SF_FLAGS(0, 0));
741
ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",
742
i, strerror(errno));
743
(void)close(client_sock);
744
745
atf_utils_wait(server_pid, 0, "", "");
746
verify_source_and_dest(DESTINATION_FILE, fd2, offset, nbytes);
747
748
(void)close(fd);
749
(void)close(fd2);
750
free(pattern);
751
pattern = NULL;
752
}
753
}
754
755
ATF_TC(hdtr_positive_v4);
756
ATF_TC_HEAD(hdtr_positive_v4, tc)
757
{
758
759
atf_tc_set_md_var(tc, "descr",
760
"Verify positive hdtr functionality (IPv4)");
761
}
762
ATF_TC_BODY(hdtr_positive_v4, tc)
763
{
764
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
765
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
766
767
hdtr_positive_test(AF_INET);
768
}
769
770
ATF_TC(hdtr_positive_v6);
771
ATF_TC_HEAD(hdtr_positive_v6, tc)
772
{
773
774
atf_tc_set_md_var(tc, "descr",
775
"Verify positive hdtr functionality (IPv6)");
776
}
777
ATF_TC_BODY(hdtr_positive_v6, tc)
778
{
779
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
780
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
781
782
hdtr_positive_test(AF_INET);
783
}
784
785
static void
786
hdtr_negative_bad_pointers_test(int domain)
787
{
788
int client_sock, error, fd, port, server_sock;
789
struct sf_hdtr *hdtr1, hdtr2, hdtr3;
790
791
port = generate_random_port(__LINE__ + domain);
792
793
hdtr1 = (struct sf_hdtr*)-1;
794
795
memset(&hdtr2, 0, sizeof(hdtr2));
796
hdtr2.hdr_cnt = 1;
797
hdtr2.headers = (struct iovec*)-1;
798
799
memset(&hdtr3, 0, sizeof(hdtr3));
800
hdtr3.trl_cnt = 1;
801
hdtr3.trailers = (struct iovec*)-1;
802
803
fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
804
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
805
806
server_sock = setup_tcp_server(domain, port);
807
client_sock = setup_tcp_client(domain, port);
808
809
error = sendfile(fd, client_sock, 0, 0, hdtr1, NULL, SF_FLAGS(0, 0));
810
ATF_CHECK_ERRNO(EFAULT, error == -1);
811
812
error = sendfile(fd, client_sock, 0, 0, &hdtr2, NULL, SF_FLAGS(0, 0));
813
ATF_CHECK_ERRNO(EFAULT, error == -1);
814
815
error = sendfile(fd, client_sock, 0, 0, &hdtr3, NULL, SF_FLAGS(0, 0));
816
ATF_CHECK_ERRNO(EFAULT, error == -1);
817
818
(void)close(fd);
819
(void)close(client_sock);
820
(void)close(server_sock);
821
}
822
823
ATF_TC(hdtr_negative_bad_pointers_v4);
824
ATF_TC_HEAD(hdtr_negative_bad_pointers_v4, tc)
825
{
826
827
atf_tc_set_md_var(tc, "descr",
828
"Verify that bad pointers for hdtr storage result in EFAULT (IPv4)");
829
}
830
ATF_TC_BODY(hdtr_negative_bad_pointers_v4, tc)
831
{
832
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
833
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
834
835
hdtr_negative_bad_pointers_test(AF_INET);
836
}
837
838
ATF_TC(hdtr_negative_bad_pointers_v6);
839
ATF_TC_HEAD(hdtr_negative_bad_pointers_v6, tc)
840
{
841
842
atf_tc_set_md_var(tc, "descr",
843
"Verify that bad pointers for hdtr storage result in EFAULT (IPv6)");
844
}
845
ATF_TC_BODY(hdtr_negative_bad_pointers_v6, tc)
846
{
847
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
848
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
849
850
hdtr_negative_bad_pointers_test(AF_INET6);
851
}
852
853
static void
854
offset_negative_value_less_than_zero_test(int domain)
855
{
856
int client_sock, error, fd, port, server_sock;
857
858
port = generate_random_port(__LINE__ + domain);
859
server_sock = setup_tcp_server(domain, port);
860
client_sock = setup_tcp_client(domain, port);
861
862
fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
863
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
864
865
error = sendfile(fd, client_sock, -1, 0, NULL, NULL, SF_FLAGS(0, 0));
866
ATF_REQUIRE_ERRNO(EINVAL, error == -1);
867
868
(void)close(fd);
869
(void)close(client_sock);
870
(void)close(server_sock);
871
}
872
873
ATF_TC(offset_negative_value_less_than_zero_v4);
874
ATF_TC_HEAD(offset_negative_value_less_than_zero_v4, tc)
875
{
876
877
atf_tc_set_md_var(tc, "descr",
878
"Verify that a negative offset results in EINVAL (IPv4)");
879
}
880
ATF_TC_BODY(offset_negative_value_less_than_zero_v4, tc)
881
{
882
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
883
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
884
885
offset_negative_value_less_than_zero_test(AF_INET);
886
}
887
888
ATF_TC(offset_negative_value_less_than_zero_v6);
889
ATF_TC_HEAD(offset_negative_value_less_than_zero_v6, tc)
890
{
891
892
atf_tc_set_md_var(tc, "descr",
893
"Verify that a negative offset results in EINVAL (IPv6)");
894
}
895
ATF_TC_BODY(offset_negative_value_less_than_zero_v6, tc)
896
{
897
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
898
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
899
900
offset_negative_value_less_than_zero_test(AF_INET6);
901
}
902
903
static void
904
sbytes_positive_test(int domain)
905
{
906
size_t pattern_size = strlen(DETERMINISTIC_PATTERN);
907
off_t sbytes;
908
int client_sock, error, fd, port, server_sock;
909
910
port = generate_random_port(__LINE__ + domain);
911
server_sock = setup_tcp_server(domain, port);
912
client_sock = setup_tcp_client(domain, port);
913
914
atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
915
fd = open(SOURCE_FILE, O_RDONLY);
916
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
917
918
error = sendfile(fd, client_sock, 0, 0, NULL, &sbytes, SF_FLAGS(0, 0));
919
ATF_CHECK_EQ_MSG(error, 0, "sendfile failed: %s", strerror(errno));
920
921
(void)close(fd);
922
(void)close(client_sock);
923
(void)close(server_sock);
924
925
ATF_CHECK_EQ_MSG(pattern_size, sbytes,
926
"the value returned by sbytes does not match the expected pattern "
927
"size");
928
}
929
930
ATF_TC(sbytes_positive_v4);
931
ATF_TC_HEAD(sbytes_positive_v4, tc)
932
{
933
934
atf_tc_set_md_var(tc, "descr",
935
"Verify positive `sbytes` functionality (IPv4)");
936
}
937
ATF_TC_BODY(sbytes_positive_v4, tc)
938
{
939
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
940
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
941
942
sbytes_positive_test(AF_INET);
943
}
944
945
ATF_TC(sbytes_positive_v6);
946
ATF_TC_HEAD(sbytes_positive_v6, tc)
947
{
948
949
atf_tc_set_md_var(tc, "descr",
950
"Verify positive `sbytes` functionality (IPv6)");
951
}
952
ATF_TC_BODY(sbytes_positive_v6, tc)
953
{
954
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
955
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
956
957
sbytes_positive_test(AF_INET6);
958
}
959
960
static void
961
sbytes_negative_test(int domain)
962
{
963
off_t *sbytes_p = (off_t*)-1;
964
int client_sock, error, fd, port, server_sock;
965
966
port = generate_random_port(__LINE__ + domain);
967
server_sock = setup_tcp_server(domain, port);
968
client_sock = setup_tcp_client(domain, port);
969
970
atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
971
fd = open(SOURCE_FILE, O_RDONLY);
972
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
973
974
atf_tc_expect_fail(
975
"bug 232210: EFAULT assert fails because copyout(9) call is not checked");
976
977
error = sendfile(fd, client_sock, 0, 0, NULL, sbytes_p, SF_FLAGS(0, 0));
978
ATF_REQUIRE_ERRNO(EFAULT, error == -1);
979
980
(void)close(fd);
981
(void)close(client_sock);
982
(void)close(server_sock);
983
}
984
985
ATF_TC(sbytes_negative_v4);
986
ATF_TC_HEAD(sbytes_negative_v4, tc)
987
{
988
989
atf_tc_set_md_var(tc, "descr",
990
"Verify negative `sbytes` functionality (IPv4)");
991
}
992
ATF_TC_BODY(sbytes_negative_v4, tc)
993
{
994
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
995
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
996
997
sbytes_negative_test(AF_INET);
998
}
999
1000
ATF_TC(sbytes_negative_v6);
1001
ATF_TC_HEAD(sbytes_negative_v6, tc)
1002
{
1003
1004
atf_tc_set_md_var(tc, "descr",
1005
"Verify negative `sbytes` functionality (IPv6)");
1006
}
1007
ATF_TC_BODY(sbytes_negative_v6, tc)
1008
{
1009
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
1010
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
1011
1012
sbytes_negative_test(AF_INET6);
1013
}
1014
1015
static void
1016
s_negative_not_connected_socket_test(int domain)
1017
{
1018
int client_sock, error, fd, port;
1019
1020
port = generate_random_port(__LINE__ + domain);
1021
client_sock = setup_tcp_server(domain, port);
1022
1023
fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
1024
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1025
1026
error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
1027
ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);
1028
1029
(void)close(fd);
1030
(void)close(client_sock);
1031
}
1032
1033
ATF_TC(s_negative_not_connected_socket_v4);
1034
ATF_TC_HEAD(s_negative_not_connected_socket_v4, tc)
1035
{
1036
1037
atf_tc_set_md_var(tc, "descr",
1038
"Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv4)");
1039
}
1040
1041
ATF_TC_BODY(s_negative_not_connected_socket_v4, tc)
1042
{
1043
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
1044
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
1045
1046
s_negative_not_connected_socket_test(AF_INET);
1047
}
1048
1049
ATF_TC(s_negative_not_connected_socket_v6);
1050
ATF_TC_HEAD(s_negative_not_connected_socket_v6, tc)
1051
{
1052
1053
atf_tc_set_md_var(tc, "descr",
1054
"Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv6)");
1055
}
1056
1057
ATF_TC_BODY(s_negative_not_connected_socket_v6, tc)
1058
{
1059
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
1060
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
1061
1062
s_negative_not_connected_socket_test(AF_INET6);
1063
}
1064
1065
ATF_TC(s_negative_not_descriptor);
1066
ATF_TC_HEAD(s_negative_not_descriptor, tc)
1067
{
1068
1069
atf_tc_set_md_var(tc, "descr",
1070
"Verify that an invalid file descriptor, e.g., -1, fails with EBADF");
1071
}
1072
1073
ATF_TC_BODY(s_negative_not_descriptor, tc)
1074
{
1075
int client_sock, error, fd;
1076
1077
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
1078
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
1079
1080
client_sock = -1;
1081
1082
fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
1083
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1084
1085
error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
1086
ATF_REQUIRE_ERRNO(EBADF, error == -1);
1087
1088
(void)close(fd);
1089
}
1090
1091
ATF_TC(s_negative_not_socket_file_descriptor);
1092
ATF_TC_HEAD(s_negative_not_socket_file_descriptor, tc)
1093
{
1094
1095
atf_tc_set_md_var(tc, "descr",
1096
"Verify that a non-socket file descriptor fails with ENOTSOCK");
1097
}
1098
1099
ATF_TC_BODY(s_negative_not_socket_file_descriptor, tc)
1100
{
1101
int client_sock, error, fd;
1102
1103
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
1104
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
1105
1106
fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
1107
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1108
1109
client_sock = open(_PATH_DEVNULL, O_WRONLY);
1110
ATF_REQUIRE_MSG(client_sock != -1, "open failed: %s", strerror(errno));
1111
1112
error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
1113
ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1);
1114
1115
(void)close(fd);
1116
(void)close(client_sock);
1117
}
1118
1119
static void
1120
s_negative_udp_socket_test(int domain)
1121
{
1122
int client_sock, error, fd, port;
1123
1124
port = generate_random_port(__LINE__ + domain);
1125
client_sock = setup_client(domain, SOCK_DGRAM, port);
1126
1127
fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
1128
ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
1129
1130
error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
1131
ATF_REQUIRE_ERRNO(EINVAL, error == -1);
1132
1133
(void)close(fd);
1134
(void)close(client_sock);
1135
}
1136
1137
ATF_TC(s_negative_udp_socket_v4);
1138
ATF_TC_HEAD(s_negative_udp_socket_v4, tc)
1139
{
1140
1141
atf_tc_set_md_var(tc, "descr",
1142
"Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv4)");
1143
}
1144
ATF_TC_BODY(s_negative_udp_socket_v4, tc)
1145
{
1146
1147
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
1148
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
1149
1150
s_negative_udp_socket_test(AF_INET);
1151
}
1152
1153
ATF_TC(s_negative_udp_socket_v6);
1154
ATF_TC_HEAD(s_negative_udp_socket_v6, tc)
1155
{
1156
1157
atf_tc_set_md_var(tc, "descr",
1158
"Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv6)");
1159
}
1160
ATF_TC_BODY(s_negative_udp_socket_v6, tc)
1161
{
1162
1163
if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
1164
atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
1165
1166
s_negative_udp_socket_test(AF_INET6);
1167
}
1168
1169
ATF_TP_ADD_TCS(tp)
1170
{
1171
1172
ATF_TP_ADD_TC(tp, fd_positive_file_v4);
1173
ATF_TP_ADD_TC(tp, fd_positive_file_v6);
1174
ATF_TP_ADD_TC(tp, fd_positive_shm_v4);
1175
ATF_TP_ADD_TC(tp, fd_positive_shm_v6);
1176
ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v4);
1177
ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v6);
1178
ATF_TP_ADD_TC(tp, flags_v4);
1179
ATF_TP_ADD_TC(tp, flags_v6);
1180
/*
1181
* TODO: the negative case for SF_NODISKIO (returns EBUSY if file in
1182
* use) is not covered yet.
1183
*
1184
* Need to lock a file in a subprocess in write mode, then try and
1185
* send the data in read mode with sendfile.
1186
*
1187
* This should work with FFS/UFS, but there are no guarantees about
1188
* other filesystem implementations of sendfile(2), e.g., ZFS.
1189
*/
1190
ATF_TP_ADD_TC(tp, hdtr_positive_v4);
1191
ATF_TP_ADD_TC(tp, hdtr_positive_v6);
1192
ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v4);
1193
ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v6);
1194
ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v4);
1195
ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v6);
1196
ATF_TP_ADD_TC(tp, sbytes_positive_v4);
1197
ATF_TP_ADD_TC(tp, sbytes_positive_v6);
1198
ATF_TP_ADD_TC(tp, sbytes_negative_v4);
1199
ATF_TP_ADD_TC(tp, sbytes_negative_v6);
1200
ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v4);
1201
ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v6);
1202
ATF_TP_ADD_TC(tp, s_negative_not_descriptor);
1203
ATF_TP_ADD_TC(tp, s_negative_not_socket_file_descriptor);
1204
ATF_TP_ADD_TC(tp, s_negative_udp_socket_v4);
1205
ATF_TP_ADD_TC(tp, s_negative_udp_socket_v6);
1206
1207
return (atf_no_error());
1208
}
1209
1210