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