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