Path: blob/main/lib/libc/tests/sys/sendfile_test.c
39530 views
/*-1* Copyright (c) 2018 Enji Cooper.2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#include <sys/param.h>27#include <sys/mman.h>28#include <sys/socket.h>29#include <sys/stat.h>30#include <sys/sysctl.h>31#include <sys/uio.h>32#include <err.h>33#include <errno.h>34#include <fcntl.h>35#include <netdb.h>36#include <paths.h>37#include <stdio.h>38#include <stdlib.h>39#include <string.h>40#include <unistd.h>4142#include <atf-c.h>4344const char DETERMINISTIC_PATTERN[] =45"The past is already gone, the future is not yet here. There's only one moment for you to live.\n";4647#define SOURCE_FILE "source"48#define DESTINATION_FILE "dest"4950#define PORTRANGE_FIRST "net.inet.ip.portrange.first"51#define PORTRANGE_LAST "net.inet.ip.portrange.last"5253static int portrange_first, portrange_last;5455static int56get_int_via_sysctlbyname(const char *oidname)57{58size_t oldlen;59int int_value;6061oldlen = sizeof(int_value);6263ATF_REQUIRE_EQ_MSG(sysctlbyname(oidname, &int_value, &oldlen, NULL, 0),640, "sysctlbyname(%s, ...) failed: %s", oidname, strerror(errno));65ATF_REQUIRE_EQ_MSG(sizeof(int_value), oldlen, "sanity check failed");6667return (int_value);68}6970static int71generate_random_port(int seed)72{73int random_port;7475printf("Generating a random port with seed=%d\n", seed);76if (portrange_first == 0) {77portrange_first = get_int_via_sysctlbyname(PORTRANGE_FIRST);78printf("Port range lower bound: %d\n", portrange_first);79}8081if (portrange_last == 0) {82portrange_last = get_int_via_sysctlbyname(PORTRANGE_LAST);83printf("Port range upper bound: %d\n", portrange_last);84}8586srand((unsigned)seed);8788random_port = rand() % (portrange_last - portrange_first) +89portrange_first;9091printf("Random port generated: %d\n", random_port);92return (random_port);93}9495static void96resolve_localhost(struct addrinfo **res, int domain, int type, int port)97{98const char *host;99char *serv;100struct addrinfo hints;101int error;102103switch (domain) {104case AF_INET:105host = "127.0.0.1";106break;107case AF_INET6:108host = "::1";109break;110default:111atf_tc_fail("unhandled domain: %d", domain);112}113114ATF_REQUIRE_MSG(asprintf(&serv, "%d", port) >= 0,115"asprintf failed: %s", strerror(errno));116117memset(&hints, 0, sizeof(hints));118hints.ai_family = domain;119hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_NUMERICHOST;120hints.ai_socktype = type;121122error = getaddrinfo(host, serv, &hints, res);123ATF_REQUIRE_EQ_MSG(error, 0,124"getaddrinfo failed: %s", gai_strerror(error));125free(serv);126}127128static int129make_socket(int domain, int type, int protocol)130{131int sock;132133sock = socket(domain, type, protocol);134ATF_REQUIRE_MSG(sock != -1, "socket(%d, %d, 0) failed: %s",135domain, type, strerror(errno));136137return (sock);138}139140static int141setup_client(int domain, int type, int port)142{143struct addrinfo *res;144char host[NI_MAXHOST+1];145int error, sock;146147resolve_localhost(&res, domain, type, port);148error = getnameinfo(149(const struct sockaddr*)res->ai_addr, res->ai_addrlen,150host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);151ATF_REQUIRE_EQ_MSG(error, 0,152"getnameinfo failed: %s", gai_strerror(error));153printf(154"Will try to connect to host='%s', address_family=%d, "155"socket_type=%d\n",156host, res->ai_family, res->ai_socktype);157/* Avoid a double print when forked by flushing. */158fflush(stdout);159sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);160error = connect(sock, (struct sockaddr*)res->ai_addr, res->ai_addrlen);161freeaddrinfo(res);162ATF_REQUIRE_EQ_MSG(error, 0, "connect failed: %s", strerror(errno));163return (sock);164}165166/*167* XXX: use linear probing to find a free port and eliminate `port` argument as168* a [const] int (it will need to be a pointer so it can be passed back out of169* the function and can influence which port `setup_client(..)` connects on.170*/171static int172setup_server(int domain, int type, int port)173{174struct addrinfo *res;175char host[NI_MAXHOST+1];176int error, sock;177178resolve_localhost(&res, domain, type, port);179sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);180181error = getnameinfo(182(const struct sockaddr*)res->ai_addr, res->ai_addrlen,183host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);184ATF_REQUIRE_EQ_MSG(error, 0,185"getnameinfo failed: %s", gai_strerror(error));186printf(187"Will try to bind socket to host='%s', address_family=%d, "188"socket_type=%d\n",189host, res->ai_family, res->ai_socktype);190/* Avoid a double print when forked by flushing. */191fflush(stdout);192error = bind(sock, res->ai_addr, res->ai_addrlen);193freeaddrinfo(res);194ATF_REQUIRE_EQ_MSG(error, 0, "bind failed: %s", strerror(errno));195error = listen(sock, 1);196ATF_REQUIRE_EQ_MSG(error, 0, "listen failed: %s", strerror(errno));197198return (sock);199}200201/*202* This function is a helper routine for taking data being sent by `sendfile` via203* `server_sock`, and pushing the received stream out to a file, denoted by204* `dest_filename`.205*/206static void207server_cat(const char *dest_filename, int server_sock, size_t len)208{209char *buffer, *buf_window_ptr;210int recv_sock;211size_t buffer_size;212ssize_t received_bytes, recv_ret;213214/*215* Ensure that there isn't excess data sent across the wire by216* capturing 10 extra bytes (plus 1 for nul).217*/218buffer_size = len + 10 + 1;219buffer = calloc(buffer_size, sizeof(char));220if (buffer == NULL)221err(1, "malloc failed");222223recv_sock = accept(server_sock, NULL, 0);224if (recv_sock == -1)225err(1, "accept failed");226227buf_window_ptr = buffer;228received_bytes = 0;229do {230recv_ret = recv(recv_sock, buf_window_ptr,231buffer_size - received_bytes, 0);232if (recv_ret <= 0)233break;234buf_window_ptr += recv_ret;235received_bytes += recv_ret;236} while (received_bytes < buffer_size);237238atf_utils_create_file(dest_filename, "%s", buffer);239240(void)close(recv_sock);241(void)close(server_sock);242free(buffer);243244if (received_bytes != len)245errx(1, "received unexpected data: %zd != %zd", received_bytes,246len);247}248249static int250setup_tcp_server(int domain, int port)251{252253return (setup_server(domain, SOCK_STREAM, port));254}255256static int257setup_tcp_client(int domain, int port)258{259260return (setup_client(domain, SOCK_STREAM, port));261}262263static off_t264file_size_from_fd(int fd)265{266struct stat st;267268ATF_REQUIRE_EQ_MSG(0, fstat(fd, &st),269"fstat failed: %s", strerror(errno));270271return (st.st_size);272}273274/*275* NB: `nbytes` == 0 has special connotations given the sendfile(2) API276* contract. In short, "send the whole file" (paraphrased).277*/278static void279verify_source_and_dest(const char* dest_filename, int src_fd, off_t offset,280size_t nbytes)281{282char *dest_pointer, *src_pointer;283off_t dest_file_size, src_file_size;284size_t length;285int dest_fd;286287atf_utils_cat_file(dest_filename, "dest_file: ");288289dest_fd = open(dest_filename, O_RDONLY);290ATF_REQUIRE_MSG(dest_fd != -1, "open failed");291292dest_file_size = file_size_from_fd(dest_fd);293src_file_size = file_size_from_fd(src_fd);294295/*296* Per sendfile(2), "send the whole file" (paraphrased). This means297* that we need to grab the file size, as passing in length = 0 with298* mmap(2) will result in a failure with EINVAL (length = 0 is invalid).299*/300length = (nbytes == 0) ? (size_t)(src_file_size - offset) : nbytes;301302ATF_REQUIRE_EQ_MSG(dest_file_size, length,303"number of bytes written out to %s (%ju) doesn't match the "304"expected number of bytes (%zu)", dest_filename, dest_file_size,305length);306307ATF_REQUIRE_EQ_MSG(0, lseek(src_fd, offset, SEEK_SET),308"lseek failed: %s", strerror(errno));309310dest_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, dest_fd, 0);311ATF_REQUIRE_MSG(dest_pointer != MAP_FAILED, "mmap failed: %s",312strerror(errno));313314printf("Will mmap in the source file from offset=%jd to length=%zu\n",315offset, length);316317src_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, src_fd, offset);318ATF_REQUIRE_MSG(src_pointer != MAP_FAILED, "mmap failed: %s",319strerror(errno));320321ATF_REQUIRE_EQ_MSG(0, memcmp(src_pointer, dest_pointer, length),322"Contents of source and destination do not match. '%s' != '%s'",323src_pointer, dest_pointer);324325(void)munmap(src_pointer, length);326(void)munmap(dest_pointer, length);327(void)close(dest_fd);328}329330static void331fd_positive_file_test(int domain)332{333off_t offset;334size_t nbytes, pattern_size;335int client_sock, error, fd, port, server_sock;336pid_t server_pid;337338pattern_size = strlen(DETERMINISTIC_PATTERN);339340atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);341fd = open(SOURCE_FILE, O_RDONLY);342ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));343344port = generate_random_port(__LINE__ + domain);345server_sock = setup_tcp_server(domain, port);346client_sock = setup_tcp_client(domain, port);347348server_pid = atf_utils_fork();349if (server_pid == 0) {350(void)close(client_sock);351server_cat(DESTINATION_FILE, server_sock, pattern_size);352_exit(0);353} else354(void)close(server_sock);355356nbytes = 0;357offset = 0;358error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,359SF_FLAGS(0, 0));360ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));361(void)close(client_sock);362363atf_utils_wait(server_pid, 0, "", "");364verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);365366(void)close(fd);367}368369ATF_TC(fd_positive_file_v4);370ATF_TC_HEAD(fd_positive_file_v4, tc)371{372373atf_tc_set_md_var(tc, "descr",374"Verify regular file as file descriptor support (IPv4)");375}376ATF_TC_BODY(fd_positive_file_v4, tc)377{378379if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))380atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");381382fd_positive_file_test(AF_INET);383}384385ATF_TC(fd_positive_file_v6);386ATF_TC_HEAD(fd_positive_file_v6, tc)387{388389atf_tc_set_md_var(tc, "descr",390"Verify regular file as file descriptor support (IPv6)");391}392ATF_TC_BODY(fd_positive_file_v6, tc)393{394395if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))396atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");397398fd_positive_file_test(AF_INET6);399}400401static void402fd_positive_shm_test(int domain)403{404char *shm_pointer;405off_t offset;406size_t nbytes, pattern_size;407pid_t server_pid;408int client_sock, error, fd, port, server_sock;409410pattern_size = strlen(DETERMINISTIC_PATTERN);411412printf("pattern size: %zu\n", pattern_size);413414fd = shm_open(SHM_ANON, O_RDWR|O_CREAT, 0600);415ATF_REQUIRE_MSG(fd != -1, "shm_open failed: %s", strerror(errno));416ATF_REQUIRE_EQ_MSG(0, ftruncate(fd, pattern_size),417"ftruncate failed: %s", strerror(errno));418shm_pointer = mmap(NULL, pattern_size, PROT_READ|PROT_WRITE,419MAP_SHARED, fd, 0);420ATF_REQUIRE_MSG(shm_pointer != MAP_FAILED,421"mmap failed: %s", strerror(errno));422memcpy(shm_pointer, DETERMINISTIC_PATTERN, pattern_size);423ATF_REQUIRE_EQ_MSG(0,424memcmp(shm_pointer, DETERMINISTIC_PATTERN, pattern_size),425"memcmp showed data mismatch: '%s' != '%s'",426DETERMINISTIC_PATTERN, shm_pointer);427428port = generate_random_port(__LINE__ + domain);429server_sock = setup_tcp_server(domain, port);430client_sock = setup_tcp_client(domain, port);431432server_pid = atf_utils_fork();433if (server_pid == 0) {434(void)close(client_sock);435server_cat(DESTINATION_FILE, server_sock, pattern_size);436_exit(0);437} else438(void)close(server_sock);439440nbytes = 0;441offset = 0;442error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,443SF_FLAGS(0, 0));444ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));445(void)close(client_sock);446447atf_utils_wait(server_pid, 0, "", "");448verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);449450(void)munmap(shm_pointer, sizeof(DETERMINISTIC_PATTERN));451(void)close(fd);452}453454ATF_TC(fd_positive_shm_v4);455ATF_TC_HEAD(fd_positive_shm_v4, tc)456{457458atf_tc_set_md_var(tc, "descr",459"Verify shared memory as file descriptor support (IPv4)");460}461ATF_TC_BODY(fd_positive_shm_v4, tc)462{463if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))464atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");465466fd_positive_shm_test(AF_INET);467}468469ATF_TC(fd_positive_shm_v6);470ATF_TC_HEAD(fd_positive_shm_v6, tc)471{472473atf_tc_set_md_var(tc, "descr",474"Verify shared memory as file descriptor support (IPv6))");475}476ATF_TC_BODY(fd_positive_shm_v6, tc)477{478if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))479atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");480481fd_positive_shm_test(AF_INET6);482}483484static void485fd_negative_bad_fd_test(int domain)486{487int client_sock, error, fd, port, server_sock;488489port = generate_random_port(__LINE__ + domain);490server_sock = setup_tcp_server(domain, port);491client_sock = setup_tcp_client(domain, port);492493fd = -1;494495error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));496ATF_REQUIRE_ERRNO(EBADF, error == -1);497498(void)close(client_sock);499(void)close(server_sock);500}501502ATF_TC(fd_negative_bad_fd_v4);503ATF_TC_HEAD(fd_negative_bad_fd_v4, tc)504{505506atf_tc_set_md_var(tc, "descr",507"Verify bad file descriptor returns EBADF (IPv4)");508}509ATF_TC_BODY(fd_negative_bad_fd_v4, tc)510{511if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))512atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");513514fd_negative_bad_fd_test(AF_INET);515}516517ATF_TC(fd_negative_bad_fd_v6);518ATF_TC_HEAD(fd_negative_bad_fd_v6, tc)519{520521atf_tc_set_md_var(tc, "descr",522"Verify bad file descriptor returns EBADF (IPv6)");523}524ATF_TC_BODY(fd_negative_bad_fd_v6, tc)525{526if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))527atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");528529fd_negative_bad_fd_test(AF_INET6);530}531532static void533flags_test(int domain)534{535off_t offset;536size_t nbytes, pattern_size;537int client_sock, error, fd, i, port, server_sock;538pid_t server_pid;539int16_t number_pages = 10;540541pattern_size = strlen(DETERMINISTIC_PATTERN);542543struct testcase {544int16_t readahead_pages, flags;545} testcases[] = {546/* This is covered in `:fd_positive_file` */547#if 0548{549.readahead_pages = 0,550.flags = 0551},552#endif553{554.readahead_pages = 0,555.flags = SF_NOCACHE556},557#ifdef SF_USER_READAHEAD558{559.readahead_pages = 0,560.flags = SF_NOCACHE|SF_USER_READAHEAD561},562{563.readahead_pages = 0,564.flags = SF_USER_READAHEAD565},566#endif567{568.readahead_pages = number_pages,569.flags = 0570},571{572.readahead_pages = number_pages,573.flags = SF_NOCACHE574},575#ifdef SF_USER_READAHEAD576{577.readahead_pages = number_pages,578.flags = SF_NOCACHE|SF_USER_READAHEAD579},580#endif581{582.readahead_pages = number_pages,583.flags = SF_NOCACHE584},585{586.readahead_pages = number_pages,587.flags = SF_NODISKIO588}589};590591atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);592for (i = 0; i < nitems(testcases); i++) {593fd = open(SOURCE_FILE, O_RDONLY);594ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));595596port = generate_random_port(i * __LINE__ + domain);597server_sock = setup_tcp_server(domain, port);598client_sock = setup_tcp_client(domain, port);599600server_pid = atf_utils_fork();601if (server_pid == 0) {602(void)close(client_sock);603server_cat(DESTINATION_FILE, server_sock, pattern_size);604_exit(0);605} else606(void)close(server_sock);607608nbytes = 0;609offset = 0;610error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,611SF_FLAGS(testcases[i].readahead_pages, testcases[i].flags));612ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",613i, strerror(errno));614(void)close(client_sock);615616atf_utils_wait(server_pid, 0, "", "");617verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);618619(void)close(fd);620}621}622623ATF_TC(flags_v4);624ATF_TC_HEAD(flags_v4, tc)625{626627atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)");628}629ATF_TC_BODY(flags_v4, tc)630{631if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))632atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");633634flags_test(AF_INET);635}636637ATF_TC(flags_v6);638ATF_TC_HEAD(flags_v6, tc)639{640641atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)");642}643ATF_TC_BODY(flags_v6, tc)644{645if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))646atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");647648flags_test(AF_INET6);649}650651static void652hdtr_positive_test(int domain)653{654struct iovec headers[1], trailers[1];655struct testcase {656bool include_headers, include_trailers;657} testcases[] = {658/* This is covered in `:fd_positive_file` */659#if 0660{661.include_headers = false,662.include_trailers = false663},664#endif665{666.include_headers = true,667.include_trailers = false668},669{670.include_headers = false,671.include_trailers = true672},673{674.include_headers = true,675.include_trailers = true676}677};678off_t offset;679size_t nbytes;680int client_sock, error, fd, fd2, i, port, rc, server_sock;681pid_t server_pid;682683headers[0].iov_base = "This is a header";684headers[0].iov_len = strlen(headers[0].iov_base);685trailers[0].iov_base = "This is a trailer";686trailers[0].iov_len = strlen(trailers[0].iov_base);687offset = 0;688nbytes = 0;689690for (i = 0; i < nitems(testcases); i++) {691struct sf_hdtr hdtr;692char *pattern;693694if (testcases[i].include_headers) {695hdtr.headers = headers;696hdtr.hdr_cnt = nitems(headers);697} else {698hdtr.headers = NULL;699hdtr.hdr_cnt = 0;700}701702if (testcases[i].include_trailers) {703hdtr.trailers = trailers;704hdtr.trl_cnt = nitems(trailers);705} else {706hdtr.trailers = NULL;707hdtr.trl_cnt = 0;708}709710port = generate_random_port(i * __LINE__ + domain);711server_sock = setup_tcp_server(domain, port);712client_sock = setup_tcp_client(domain, port);713714rc = asprintf(&pattern, "%s%s%s",715testcases[i].include_headers ? (char *)headers[0].iov_base : "",716DETERMINISTIC_PATTERN,717testcases[i].include_trailers ? (char *)trailers[0].iov_base : "");718ATF_REQUIRE_MSG(rc != -1, "asprintf failed: %s", strerror(errno));719720atf_utils_create_file(SOURCE_FILE ".full", "%s", pattern);721atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);722723fd = open(SOURCE_FILE, O_RDONLY);724ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));725726fd2 = open(SOURCE_FILE ".full", O_RDONLY);727ATF_REQUIRE_MSG(fd2 != -1, "open failed: %s", strerror(errno));728729server_pid = atf_utils_fork();730if (server_pid == 0) {731(void)close(client_sock);732server_cat(DESTINATION_FILE, server_sock,733strlen(pattern));734_exit(0);735} else736(void)close(server_sock);737738error = sendfile(fd, client_sock, offset, nbytes, &hdtr,739NULL, SF_FLAGS(0, 0));740ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",741i, strerror(errno));742(void)close(client_sock);743744atf_utils_wait(server_pid, 0, "", "");745verify_source_and_dest(DESTINATION_FILE, fd2, offset, nbytes);746747(void)close(fd);748(void)close(fd2);749free(pattern);750pattern = NULL;751}752}753754ATF_TC(hdtr_positive_v4);755ATF_TC_HEAD(hdtr_positive_v4, tc)756{757758atf_tc_set_md_var(tc, "descr",759"Verify positive hdtr functionality (IPv4)");760}761ATF_TC_BODY(hdtr_positive_v4, tc)762{763if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))764atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");765766hdtr_positive_test(AF_INET);767}768769ATF_TC(hdtr_positive_v6);770ATF_TC_HEAD(hdtr_positive_v6, tc)771{772773atf_tc_set_md_var(tc, "descr",774"Verify positive hdtr functionality (IPv6)");775}776ATF_TC_BODY(hdtr_positive_v6, tc)777{778if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))779atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");780781hdtr_positive_test(AF_INET);782}783784static void785hdtr_negative_bad_pointers_test(int domain)786{787int client_sock, error, fd, port, server_sock;788struct sf_hdtr *hdtr1, hdtr2, hdtr3;789790port = generate_random_port(__LINE__ + domain);791792hdtr1 = (struct sf_hdtr*)-1;793794memset(&hdtr2, 0, sizeof(hdtr2));795hdtr2.hdr_cnt = 1;796hdtr2.headers = (struct iovec*)-1;797798memset(&hdtr3, 0, sizeof(hdtr3));799hdtr3.trl_cnt = 1;800hdtr3.trailers = (struct iovec*)-1;801802fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);803ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));804805server_sock = setup_tcp_server(domain, port);806client_sock = setup_tcp_client(domain, port);807808error = sendfile(fd, client_sock, 0, 0, hdtr1, NULL, SF_FLAGS(0, 0));809ATF_CHECK_ERRNO(EFAULT, error == -1);810811error = sendfile(fd, client_sock, 0, 0, &hdtr2, NULL, SF_FLAGS(0, 0));812ATF_CHECK_ERRNO(EFAULT, error == -1);813814error = sendfile(fd, client_sock, 0, 0, &hdtr3, NULL, SF_FLAGS(0, 0));815ATF_CHECK_ERRNO(EFAULT, error == -1);816817(void)close(fd);818(void)close(client_sock);819(void)close(server_sock);820}821822ATF_TC(hdtr_negative_bad_pointers_v4);823ATF_TC_HEAD(hdtr_negative_bad_pointers_v4, tc)824{825826atf_tc_set_md_var(tc, "descr",827"Verify that bad pointers for hdtr storage result in EFAULT (IPv4)");828}829ATF_TC_BODY(hdtr_negative_bad_pointers_v4, tc)830{831if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))832atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");833834hdtr_negative_bad_pointers_test(AF_INET);835}836837ATF_TC(hdtr_negative_bad_pointers_v6);838ATF_TC_HEAD(hdtr_negative_bad_pointers_v6, tc)839{840841atf_tc_set_md_var(tc, "descr",842"Verify that bad pointers for hdtr storage result in EFAULT (IPv6)");843}844ATF_TC_BODY(hdtr_negative_bad_pointers_v6, tc)845{846if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))847atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");848849hdtr_negative_bad_pointers_test(AF_INET6);850}851852static void853offset_negative_value_less_than_zero_test(int domain)854{855int client_sock, error, fd, port, server_sock;856857port = generate_random_port(__LINE__ + domain);858server_sock = setup_tcp_server(domain, port);859client_sock = setup_tcp_client(domain, port);860861fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);862ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));863864error = sendfile(fd, client_sock, -1, 0, NULL, NULL, SF_FLAGS(0, 0));865ATF_REQUIRE_ERRNO(EINVAL, error == -1);866867(void)close(fd);868(void)close(client_sock);869(void)close(server_sock);870}871872ATF_TC(offset_negative_value_less_than_zero_v4);873ATF_TC_HEAD(offset_negative_value_less_than_zero_v4, tc)874{875876atf_tc_set_md_var(tc, "descr",877"Verify that a negative offset results in EINVAL (IPv4)");878}879ATF_TC_BODY(offset_negative_value_less_than_zero_v4, tc)880{881if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))882atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");883884offset_negative_value_less_than_zero_test(AF_INET);885}886887ATF_TC(offset_negative_value_less_than_zero_v6);888ATF_TC_HEAD(offset_negative_value_less_than_zero_v6, tc)889{890891atf_tc_set_md_var(tc, "descr",892"Verify that a negative offset results in EINVAL (IPv6)");893}894ATF_TC_BODY(offset_negative_value_less_than_zero_v6, tc)895{896if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))897atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");898899offset_negative_value_less_than_zero_test(AF_INET6);900}901902static void903sbytes_positive_test(int domain)904{905size_t pattern_size = strlen(DETERMINISTIC_PATTERN);906off_t sbytes;907int client_sock, error, fd, port, server_sock;908909port = generate_random_port(__LINE__ + domain);910server_sock = setup_tcp_server(domain, port);911client_sock = setup_tcp_client(domain, port);912913atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);914fd = open(SOURCE_FILE, O_RDONLY);915ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));916917error = sendfile(fd, client_sock, 0, 0, NULL, &sbytes, SF_FLAGS(0, 0));918ATF_CHECK_EQ_MSG(error, 0, "sendfile failed: %s", strerror(errno));919920(void)close(fd);921(void)close(client_sock);922(void)close(server_sock);923924ATF_CHECK_EQ_MSG(pattern_size, sbytes,925"the value returned by sbytes does not match the expected pattern "926"size");927}928929ATF_TC(sbytes_positive_v4);930ATF_TC_HEAD(sbytes_positive_v4, tc)931{932933atf_tc_set_md_var(tc, "descr",934"Verify positive `sbytes` functionality (IPv4)");935}936ATF_TC_BODY(sbytes_positive_v4, tc)937{938if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))939atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");940941sbytes_positive_test(AF_INET);942}943944ATF_TC(sbytes_positive_v6);945ATF_TC_HEAD(sbytes_positive_v6, tc)946{947948atf_tc_set_md_var(tc, "descr",949"Verify positive `sbytes` functionality (IPv6)");950}951ATF_TC_BODY(sbytes_positive_v6, tc)952{953if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))954atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");955956sbytes_positive_test(AF_INET6);957}958959static void960sbytes_negative_test(int domain)961{962off_t *sbytes_p = (off_t*)-1;963int client_sock, error, fd, port, server_sock;964965port = generate_random_port(__LINE__ + domain);966server_sock = setup_tcp_server(domain, port);967client_sock = setup_tcp_client(domain, port);968969atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);970fd = open(SOURCE_FILE, O_RDONLY);971ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));972973atf_tc_expect_fail(974"bug 232210: EFAULT assert fails because copyout(9) call is not checked");975976error = sendfile(fd, client_sock, 0, 0, NULL, sbytes_p, SF_FLAGS(0, 0));977ATF_REQUIRE_ERRNO(EFAULT, error == -1);978979(void)close(fd);980(void)close(client_sock);981(void)close(server_sock);982}983984ATF_TC(sbytes_negative_v4);985ATF_TC_HEAD(sbytes_negative_v4, tc)986{987988atf_tc_set_md_var(tc, "descr",989"Verify negative `sbytes` functionality (IPv4)");990}991ATF_TC_BODY(sbytes_negative_v4, tc)992{993if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))994atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");995996sbytes_negative_test(AF_INET);997}998999ATF_TC(sbytes_negative_v6);1000ATF_TC_HEAD(sbytes_negative_v6, tc)1001{10021003atf_tc_set_md_var(tc, "descr",1004"Verify negative `sbytes` functionality (IPv6)");1005}1006ATF_TC_BODY(sbytes_negative_v6, tc)1007{1008if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))1009atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");10101011sbytes_negative_test(AF_INET6);1012}10131014static void1015s_negative_not_connected_socket_test(int domain)1016{1017int client_sock, error, fd, port;10181019port = generate_random_port(__LINE__ + domain);1020client_sock = setup_tcp_server(domain, port);10211022fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);1023ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));10241025error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));1026ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);10271028(void)close(fd);1029(void)close(client_sock);1030}10311032ATF_TC(s_negative_not_connected_socket_v4);1033ATF_TC_HEAD(s_negative_not_connected_socket_v4, tc)1034{10351036atf_tc_set_md_var(tc, "descr",1037"Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv4)");1038}10391040ATF_TC_BODY(s_negative_not_connected_socket_v4, tc)1041{1042if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))1043atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");10441045s_negative_not_connected_socket_test(AF_INET);1046}10471048ATF_TC(s_negative_not_connected_socket_v6);1049ATF_TC_HEAD(s_negative_not_connected_socket_v6, tc)1050{10511052atf_tc_set_md_var(tc, "descr",1053"Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv6)");1054}10551056ATF_TC_BODY(s_negative_not_connected_socket_v6, tc)1057{1058if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))1059atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");10601061s_negative_not_connected_socket_test(AF_INET6);1062}10631064ATF_TC(s_negative_not_descriptor);1065ATF_TC_HEAD(s_negative_not_descriptor, tc)1066{10671068atf_tc_set_md_var(tc, "descr",1069"Verify that an invalid file descriptor, e.g., -1, fails with EBADF");1070}10711072ATF_TC_BODY(s_negative_not_descriptor, tc)1073{1074int client_sock, error, fd;10751076if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))1077atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");10781079client_sock = -1;10801081fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);1082ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));10831084error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));1085ATF_REQUIRE_ERRNO(EBADF, error == -1);10861087(void)close(fd);1088}10891090ATF_TC(s_negative_not_socket_file_descriptor);1091ATF_TC_HEAD(s_negative_not_socket_file_descriptor, tc)1092{10931094atf_tc_set_md_var(tc, "descr",1095"Verify that a non-socket file descriptor fails with ENOTSOCK");1096}10971098ATF_TC_BODY(s_negative_not_socket_file_descriptor, tc)1099{1100int client_sock, error, fd;11011102if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))1103atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");11041105fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);1106ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));11071108client_sock = open(_PATH_DEVNULL, O_WRONLY);1109ATF_REQUIRE_MSG(client_sock != -1, "open failed: %s", strerror(errno));11101111error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));1112ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1);11131114(void)close(fd);1115(void)close(client_sock);1116}11171118static void1119s_negative_udp_socket_test(int domain)1120{1121int client_sock, error, fd, port;11221123port = generate_random_port(__LINE__ + domain);1124client_sock = setup_client(domain, SOCK_DGRAM, port);11251126fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);1127ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));11281129error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));1130ATF_REQUIRE_ERRNO(EINVAL, error == -1);11311132(void)close(fd);1133(void)close(client_sock);1134}11351136ATF_TC(s_negative_udp_socket_v4);1137ATF_TC_HEAD(s_negative_udp_socket_v4, tc)1138{11391140atf_tc_set_md_var(tc, "descr",1141"Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv4)");1142}1143ATF_TC_BODY(s_negative_udp_socket_v4, tc)1144{11451146if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))1147atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");11481149s_negative_udp_socket_test(AF_INET);1150}11511152ATF_TC(s_negative_udp_socket_v6);1153ATF_TC_HEAD(s_negative_udp_socket_v6, tc)1154{11551156atf_tc_set_md_var(tc, "descr",1157"Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv6)");1158}1159ATF_TC_BODY(s_negative_udp_socket_v6, tc)1160{11611162if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))1163atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");11641165s_negative_udp_socket_test(AF_INET6);1166}11671168ATF_TP_ADD_TCS(tp)1169{11701171ATF_TP_ADD_TC(tp, fd_positive_file_v4);1172ATF_TP_ADD_TC(tp, fd_positive_file_v6);1173ATF_TP_ADD_TC(tp, fd_positive_shm_v4);1174ATF_TP_ADD_TC(tp, fd_positive_shm_v6);1175ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v4);1176ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v6);1177ATF_TP_ADD_TC(tp, flags_v4);1178ATF_TP_ADD_TC(tp, flags_v6);1179/*1180* TODO: the negative case for SF_NODISKIO (returns EBUSY if file in1181* use) is not covered yet.1182*1183* Need to lock a file in a subprocess in write mode, then try and1184* send the data in read mode with sendfile.1185*1186* This should work with FFS/UFS, but there are no guarantees about1187* other filesystem implementations of sendfile(2), e.g., ZFS.1188*/1189ATF_TP_ADD_TC(tp, hdtr_positive_v4);1190ATF_TP_ADD_TC(tp, hdtr_positive_v6);1191ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v4);1192ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v6);1193ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v4);1194ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v6);1195ATF_TP_ADD_TC(tp, sbytes_positive_v4);1196ATF_TP_ADD_TC(tp, sbytes_positive_v6);1197ATF_TP_ADD_TC(tp, sbytes_negative_v4);1198ATF_TP_ADD_TC(tp, sbytes_negative_v6);1199ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v4);1200ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v6);1201ATF_TP_ADD_TC(tp, s_negative_not_descriptor);1202ATF_TP_ADD_TC(tp, s_negative_not_socket_file_descriptor);1203ATF_TP_ADD_TC(tp, s_negative_udp_socket_v4);1204ATF_TP_ADD_TC(tp, s_negative_udp_socket_v6);12051206return (atf_no_error());1207}120812091210