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