xref: /freebsd/lib/libc/tests/sys/sendfile_test.c (revision fe75646a0234a261c0013bf1840fdac4acaf0cec)
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