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§ion=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