13da5c369Sopenharmony_ci//! Send data from a file to a socket, bypassing userland.
23da5c369Sopenharmony_ci
33da5c369Sopenharmony_ciuse cfg_if::cfg_if;
43da5c369Sopenharmony_ciuse std::os::unix::io::RawFd;
53da5c369Sopenharmony_ciuse std::ptr;
63da5c369Sopenharmony_ci
73da5c369Sopenharmony_ciuse libc::{self, off_t};
83da5c369Sopenharmony_ci
93da5c369Sopenharmony_ciuse crate::errno::Errno;
103da5c369Sopenharmony_ciuse crate::Result;
113da5c369Sopenharmony_ci
123da5c369Sopenharmony_ci/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
133da5c369Sopenharmony_ci///
143da5c369Sopenharmony_ci/// Returns a `Result` with the number of bytes written.
153da5c369Sopenharmony_ci///
163da5c369Sopenharmony_ci/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
173da5c369Sopenharmony_ci/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
183da5c369Sopenharmony_ci/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
193da5c369Sopenharmony_ci/// the byte after the last byte copied.
203da5c369Sopenharmony_ci///
213da5c369Sopenharmony_ci/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
223da5c369Sopenharmony_ci///
233da5c369Sopenharmony_ci/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
243da5c369Sopenharmony_ci#[cfg(any(target_os = "android", target_os = "linux"))]
253da5c369Sopenharmony_ci#[cfg_attr(docsrs, doc(cfg(all())))]
263da5c369Sopenharmony_cipub fn sendfile(
273da5c369Sopenharmony_ci    out_fd: RawFd,
283da5c369Sopenharmony_ci    in_fd: RawFd,
293da5c369Sopenharmony_ci    offset: Option<&mut off_t>,
303da5c369Sopenharmony_ci    count: usize,
313da5c369Sopenharmony_ci) -> Result<usize> {
323da5c369Sopenharmony_ci    let offset = offset
333da5c369Sopenharmony_ci        .map(|offset| offset as *mut _)
343da5c369Sopenharmony_ci        .unwrap_or(ptr::null_mut());
353da5c369Sopenharmony_ci    let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) };
363da5c369Sopenharmony_ci    Errno::result(ret).map(|r| r as usize)
373da5c369Sopenharmony_ci}
383da5c369Sopenharmony_ci
393da5c369Sopenharmony_ci/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
403da5c369Sopenharmony_ci///
413da5c369Sopenharmony_ci/// Returns a `Result` with the number of bytes written.
423da5c369Sopenharmony_ci///
433da5c369Sopenharmony_ci/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
443da5c369Sopenharmony_ci/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
453da5c369Sopenharmony_ci/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
463da5c369Sopenharmony_ci/// the byte after the last byte copied.
473da5c369Sopenharmony_ci///
483da5c369Sopenharmony_ci/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
493da5c369Sopenharmony_ci///
503da5c369Sopenharmony_ci/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
513da5c369Sopenharmony_ci#[cfg(target_os = "linux")]
523da5c369Sopenharmony_ci#[cfg_attr(docsrs, doc(cfg(all())))]
533da5c369Sopenharmony_cipub fn sendfile64(
543da5c369Sopenharmony_ci    out_fd: RawFd,
553da5c369Sopenharmony_ci    in_fd: RawFd,
563da5c369Sopenharmony_ci    offset: Option<&mut libc::off64_t>,
573da5c369Sopenharmony_ci    count: usize,
583da5c369Sopenharmony_ci) -> Result<usize> {
593da5c369Sopenharmony_ci    let offset = offset
603da5c369Sopenharmony_ci        .map(|offset| offset as *mut _)
613da5c369Sopenharmony_ci        .unwrap_or(ptr::null_mut());
623da5c369Sopenharmony_ci    let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) };
633da5c369Sopenharmony_ci    Errno::result(ret).map(|r| r as usize)
643da5c369Sopenharmony_ci}
653da5c369Sopenharmony_ci
663da5c369Sopenharmony_cicfg_if! {
673da5c369Sopenharmony_ci    if #[cfg(any(target_os = "dragonfly",
683da5c369Sopenharmony_ci                 target_os = "freebsd",
693da5c369Sopenharmony_ci                 target_os = "ios",
703da5c369Sopenharmony_ci                 target_os = "macos"))] {
713da5c369Sopenharmony_ci        use std::io::IoSlice;
723da5c369Sopenharmony_ci
733da5c369Sopenharmony_ci        #[derive(Clone, Debug)]
743da5c369Sopenharmony_ci        struct SendfileHeaderTrailer<'a>(
753da5c369Sopenharmony_ci            libc::sf_hdtr,
763da5c369Sopenharmony_ci            Option<Vec<IoSlice<'a>>>,
773da5c369Sopenharmony_ci            Option<Vec<IoSlice<'a>>>,
783da5c369Sopenharmony_ci        );
793da5c369Sopenharmony_ci
803da5c369Sopenharmony_ci        impl<'a> SendfileHeaderTrailer<'a> {
813da5c369Sopenharmony_ci            fn new(
823da5c369Sopenharmony_ci                headers: Option<&'a [&'a [u8]]>,
833da5c369Sopenharmony_ci                trailers: Option<&'a [&'a [u8]]>
843da5c369Sopenharmony_ci            ) -> SendfileHeaderTrailer<'a> {
853da5c369Sopenharmony_ci                let header_iovecs: Option<Vec<IoSlice<'_>>> =
863da5c369Sopenharmony_ci                    headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
873da5c369Sopenharmony_ci                let trailer_iovecs: Option<Vec<IoSlice<'_>>> =
883da5c369Sopenharmony_ci                    trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
893da5c369Sopenharmony_ci                SendfileHeaderTrailer(
903da5c369Sopenharmony_ci                    libc::sf_hdtr {
913da5c369Sopenharmony_ci                        headers: {
923da5c369Sopenharmony_ci                            header_iovecs
933da5c369Sopenharmony_ci                                .as_ref()
943da5c369Sopenharmony_ci                                .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
953da5c369Sopenharmony_ci                        },
963da5c369Sopenharmony_ci                        hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32,
973da5c369Sopenharmony_ci                        trailers: {
983da5c369Sopenharmony_ci                            trailer_iovecs
993da5c369Sopenharmony_ci                                .as_ref()
1003da5c369Sopenharmony_ci                                .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
1013da5c369Sopenharmony_ci                        },
1023da5c369Sopenharmony_ci                        trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32
1033da5c369Sopenharmony_ci                    },
1043da5c369Sopenharmony_ci                    header_iovecs,
1053da5c369Sopenharmony_ci                    trailer_iovecs,
1063da5c369Sopenharmony_ci                )
1073da5c369Sopenharmony_ci            }
1083da5c369Sopenharmony_ci        }
1093da5c369Sopenharmony_ci    }
1103da5c369Sopenharmony_ci}
1113da5c369Sopenharmony_ci
1123da5c369Sopenharmony_cicfg_if! {
1133da5c369Sopenharmony_ci    if #[cfg(target_os = "freebsd")] {
1143da5c369Sopenharmony_ci        use libc::c_int;
1153da5c369Sopenharmony_ci
1163da5c369Sopenharmony_ci        libc_bitflags!{
1173da5c369Sopenharmony_ci            /// Configuration options for [`sendfile`.](fn.sendfile.html)
1183da5c369Sopenharmony_ci            pub struct SfFlags: c_int {
1193da5c369Sopenharmony_ci                /// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a
1203da5c369Sopenharmony_ci                /// busy page.
1213da5c369Sopenharmony_ci                SF_NODISKIO;
1223da5c369Sopenharmony_ci                /// Causes `sendfile` to sleep until the network stack releases its reference to the
1233da5c369Sopenharmony_ci                /// VM pages read. When `sendfile` returns, the data is not guaranteed to have been
1243da5c369Sopenharmony_ci                /// sent, but it is safe to modify the file.
1253da5c369Sopenharmony_ci                SF_SYNC;
1263da5c369Sopenharmony_ci                /// Causes `sendfile` to cache exactly the number of pages specified in the
1273da5c369Sopenharmony_ci                /// `readahead` parameter, disabling caching heuristics.
1283da5c369Sopenharmony_ci                SF_USER_READAHEAD;
1293da5c369Sopenharmony_ci                /// Causes `sendfile` not to cache the data read.
1303da5c369Sopenharmony_ci                SF_NOCACHE;
1313da5c369Sopenharmony_ci            }
1323da5c369Sopenharmony_ci        }
1333da5c369Sopenharmony_ci
1343da5c369Sopenharmony_ci        /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
1353da5c369Sopenharmony_ci        ///
1363da5c369Sopenharmony_ci        /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
1373da5c369Sopenharmony_ci        /// an error occurs.
1383da5c369Sopenharmony_ci        ///
1393da5c369Sopenharmony_ci        /// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a
1403da5c369Sopenharmony_ci        /// stream socket.
1413da5c369Sopenharmony_ci        ///
1423da5c369Sopenharmony_ci        /// If `offset` falls past the end of the file, the function returns success and zero bytes
1433da5c369Sopenharmony_ci        /// written.
1443da5c369Sopenharmony_ci        ///
1453da5c369Sopenharmony_ci        /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
1463da5c369Sopenharmony_ci        /// file (EOF).
1473da5c369Sopenharmony_ci        ///
1483da5c369Sopenharmony_ci        /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
1493da5c369Sopenharmony_ci        /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
1503da5c369Sopenharmony_ci        /// is included in the returned count of bytes written. The values of `offset` and `count`
1513da5c369Sopenharmony_ci        /// do not apply to headers or trailers.
1523da5c369Sopenharmony_ci        ///
1533da5c369Sopenharmony_ci        /// `readahead` specifies the minimum number of pages to cache in memory ahead of the page
1543da5c369Sopenharmony_ci        /// currently being sent.
1553da5c369Sopenharmony_ci        ///
1563da5c369Sopenharmony_ci        /// For more information, see
1573da5c369Sopenharmony_ci        /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
1583da5c369Sopenharmony_ci        #[allow(clippy::too_many_arguments)]
1593da5c369Sopenharmony_ci        pub fn sendfile(
1603da5c369Sopenharmony_ci            in_fd: RawFd,
1613da5c369Sopenharmony_ci            out_sock: RawFd,
1623da5c369Sopenharmony_ci            offset: off_t,
1633da5c369Sopenharmony_ci            count: Option<usize>,
1643da5c369Sopenharmony_ci            headers: Option<&[&[u8]]>,
1653da5c369Sopenharmony_ci            trailers: Option<&[&[u8]]>,
1663da5c369Sopenharmony_ci            flags: SfFlags,
1673da5c369Sopenharmony_ci            readahead: u16
1683da5c369Sopenharmony_ci        ) -> (Result<()>, off_t) {
1693da5c369Sopenharmony_ci            // Readahead goes in upper 16 bits
1703da5c369Sopenharmony_ci            // Flags goes in lower 16 bits
1713da5c369Sopenharmony_ci            // see `man 2 sendfile`
1723da5c369Sopenharmony_ci            let ra32 = u32::from(readahead);
1733da5c369Sopenharmony_ci            let flags: u32 = (ra32 << 16) | (flags.bits() as u32);
1743da5c369Sopenharmony_ci            let mut bytes_sent: off_t = 0;
1753da5c369Sopenharmony_ci            let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
1763da5c369Sopenharmony_ci            let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
1773da5c369Sopenharmony_ci            let return_code = unsafe {
1783da5c369Sopenharmony_ci                libc::sendfile(in_fd,
1793da5c369Sopenharmony_ci                               out_sock,
1803da5c369Sopenharmony_ci                               offset,
1813da5c369Sopenharmony_ci                               count.unwrap_or(0),
1823da5c369Sopenharmony_ci                               hdtr_ptr as *mut libc::sf_hdtr,
1833da5c369Sopenharmony_ci                               &mut bytes_sent as *mut off_t,
1843da5c369Sopenharmony_ci                               flags as c_int)
1853da5c369Sopenharmony_ci            };
1863da5c369Sopenharmony_ci            (Errno::result(return_code).and(Ok(())), bytes_sent)
1873da5c369Sopenharmony_ci        }
1883da5c369Sopenharmony_ci    } else if #[cfg(target_os = "dragonfly")] {
1893da5c369Sopenharmony_ci        /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
1903da5c369Sopenharmony_ci        ///
1913da5c369Sopenharmony_ci        /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
1923da5c369Sopenharmony_ci        /// an error occurs.
1933da5c369Sopenharmony_ci        ///
1943da5c369Sopenharmony_ci        /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
1953da5c369Sopenharmony_ci        ///
1963da5c369Sopenharmony_ci        /// If `offset` falls past the end of the file, the function returns success and zero bytes
1973da5c369Sopenharmony_ci        /// written.
1983da5c369Sopenharmony_ci        ///
1993da5c369Sopenharmony_ci        /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
2003da5c369Sopenharmony_ci        /// file (EOF).
2013da5c369Sopenharmony_ci        ///
2023da5c369Sopenharmony_ci        /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
2033da5c369Sopenharmony_ci        /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
2043da5c369Sopenharmony_ci        /// is included in the returned count of bytes written. The values of `offset` and `count`
2053da5c369Sopenharmony_ci        /// do not apply to headers or trailers.
2063da5c369Sopenharmony_ci        ///
2073da5c369Sopenharmony_ci        /// For more information, see
2083da5c369Sopenharmony_ci        /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
2093da5c369Sopenharmony_ci        pub fn sendfile(
2103da5c369Sopenharmony_ci            in_fd: RawFd,
2113da5c369Sopenharmony_ci            out_sock: RawFd,
2123da5c369Sopenharmony_ci            offset: off_t,
2133da5c369Sopenharmony_ci            count: Option<usize>,
2143da5c369Sopenharmony_ci            headers: Option<&[&[u8]]>,
2153da5c369Sopenharmony_ci            trailers: Option<&[&[u8]]>,
2163da5c369Sopenharmony_ci        ) -> (Result<()>, off_t) {
2173da5c369Sopenharmony_ci            let mut bytes_sent: off_t = 0;
2183da5c369Sopenharmony_ci            let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
2193da5c369Sopenharmony_ci            let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
2203da5c369Sopenharmony_ci            let return_code = unsafe {
2213da5c369Sopenharmony_ci                libc::sendfile(in_fd,
2223da5c369Sopenharmony_ci                               out_sock,
2233da5c369Sopenharmony_ci                               offset,
2243da5c369Sopenharmony_ci                               count.unwrap_or(0),
2253da5c369Sopenharmony_ci                               hdtr_ptr as *mut libc::sf_hdtr,
2263da5c369Sopenharmony_ci                               &mut bytes_sent as *mut off_t,
2273da5c369Sopenharmony_ci                               0)
2283da5c369Sopenharmony_ci            };
2293da5c369Sopenharmony_ci            (Errno::result(return_code).and(Ok(())), bytes_sent)
2303da5c369Sopenharmony_ci        }
2313da5c369Sopenharmony_ci    } else if #[cfg(any(target_os = "ios", target_os = "macos"))] {
2323da5c369Sopenharmony_ci        /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
2333da5c369Sopenharmony_ci        /// `out_sock`.
2343da5c369Sopenharmony_ci        ///
2353da5c369Sopenharmony_ci        /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
2363da5c369Sopenharmony_ci        /// an error occurs.
2373da5c369Sopenharmony_ci        ///
2383da5c369Sopenharmony_ci        /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
2393da5c369Sopenharmony_ci        ///
2403da5c369Sopenharmony_ci        /// If `offset` falls past the end of the file, the function returns success and zero bytes
2413da5c369Sopenharmony_ci        /// written.
2423da5c369Sopenharmony_ci        ///
2433da5c369Sopenharmony_ci        /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
2443da5c369Sopenharmony_ci        /// file (EOF).
2453da5c369Sopenharmony_ci        ///
2463da5c369Sopenharmony_ci        /// `hdtr` specifies an optional list of headers and trailers to be sent before and after
2473da5c369Sopenharmony_ci        /// the data read from `in_fd`, respectively. The length of headers and trailers sent is
2483da5c369Sopenharmony_ci        /// included in the returned count of bytes written. If any headers are specified and
2493da5c369Sopenharmony_ci        /// `count` is non-zero, the length of the headers will be counted in the limit of total
2503da5c369Sopenharmony_ci        /// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent
2513da5c369Sopenharmony_ci        /// regardless. The value of `offset` does not affect headers or trailers.
2523da5c369Sopenharmony_ci        ///
2533da5c369Sopenharmony_ci        /// For more information, see
2543da5c369Sopenharmony_ci        /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
2553da5c369Sopenharmony_ci        pub fn sendfile(
2563da5c369Sopenharmony_ci            in_fd: RawFd,
2573da5c369Sopenharmony_ci            out_sock: RawFd,
2583da5c369Sopenharmony_ci            offset: off_t,
2593da5c369Sopenharmony_ci            count: Option<off_t>,
2603da5c369Sopenharmony_ci            headers: Option<&[&[u8]]>,
2613da5c369Sopenharmony_ci            trailers: Option<&[&[u8]]>
2623da5c369Sopenharmony_ci        ) -> (Result<()>, off_t) {
2633da5c369Sopenharmony_ci            let mut len = count.unwrap_or(0);
2643da5c369Sopenharmony_ci            let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
2653da5c369Sopenharmony_ci            let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
2663da5c369Sopenharmony_ci            let return_code = unsafe {
2673da5c369Sopenharmony_ci                libc::sendfile(in_fd,
2683da5c369Sopenharmony_ci                               out_sock,
2693da5c369Sopenharmony_ci                               offset,
2703da5c369Sopenharmony_ci                               &mut len as *mut off_t,
2713da5c369Sopenharmony_ci                               hdtr_ptr as *mut libc::sf_hdtr,
2723da5c369Sopenharmony_ci                               0)
2733da5c369Sopenharmony_ci            };
2743da5c369Sopenharmony_ci            (Errno::result(return_code).and(Ok(())), len)
2753da5c369Sopenharmony_ci        }
2763da5c369Sopenharmony_ci    }
2773da5c369Sopenharmony_ci}
278