]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libc/tests/sys/sendfile_test.c
MFV r344063:
[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         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 }