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