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