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