| //! Send data from a file to a socket, bypassing userland. |
| |
| use cfg_if::cfg_if; |
| use std::os::unix::io::{AsFd, AsRawFd}; |
| use std::ptr; |
| |
| use libc::{self, off_t}; |
| |
| use crate::errno::Errno; |
| use crate::Result; |
| |
| /// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`. |
| /// |
| /// Returns a `Result` with the number of bytes written. |
| /// |
| /// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will |
| /// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified |
| /// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to |
| /// the byte after the last byte copied. |
| /// |
| /// `in_fd` must support `mmap`-like operations and therefore cannot be a socket. |
| /// |
| /// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) for Linux, |
| /// see [the sendfile(2) man page.](https://docs.oracle.com/cd/E88353_01/html/E37843/sendfile-3c.html) for Solaris. |
| #[cfg(any(linux_android, solarish))] |
| pub fn sendfile<F1: AsFd, F2: AsFd>( |
| out_fd: F1, |
| in_fd: F2, |
| offset: Option<&mut off_t>, |
| count: usize, |
| ) -> Result<usize> { |
| let offset = offset |
| .map(|offset| offset as *mut _) |
| .unwrap_or(ptr::null_mut()); |
| let ret = unsafe { |
| libc::sendfile( |
| out_fd.as_fd().as_raw_fd(), |
| in_fd.as_fd().as_raw_fd(), |
| offset, |
| count, |
| ) |
| }; |
| Errno::result(ret).map(|r| r as usize) |
| } |
| |
| /// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`. |
| /// |
| /// Returns a `Result` with the number of bytes written. |
| /// |
| /// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will |
| /// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified |
| /// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to |
| /// the byte after the last byte copied. |
| /// |
| /// `in_fd` must support `mmap`-like operations and therefore cannot be a socket. |
| /// |
| /// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) |
| #[cfg(target_os = "linux")] |
| pub fn sendfile64<F1: AsFd, F2: AsFd>( |
| out_fd: F1, |
| in_fd: F2, |
| offset: Option<&mut libc::off64_t>, |
| count: usize, |
| ) -> Result<usize> { |
| let offset = offset |
| .map(|offset| offset as *mut _) |
| .unwrap_or(ptr::null_mut()); |
| let ret = unsafe { |
| libc::sendfile64( |
| out_fd.as_fd().as_raw_fd(), |
| in_fd.as_fd().as_raw_fd(), |
| offset, |
| count, |
| ) |
| }; |
| Errno::result(ret).map(|r| r as usize) |
| } |
| |
| cfg_if! { |
| if #[cfg(any(freebsdlike, apple_targets))] { |
| use std::io::IoSlice; |
| |
| #[derive(Clone, Debug)] |
| struct SendfileHeaderTrailer<'a> { |
| raw: libc::sf_hdtr, |
| _headers: Option<Vec<IoSlice<'a>>>, |
| _trailers: Option<Vec<IoSlice<'a>>>, |
| } |
| |
| impl<'a> SendfileHeaderTrailer<'a> { |
| fn new( |
| headers: Option<&'a [&'a [u8]]>, |
| trailers: Option<&'a [&'a [u8]]> |
| ) -> SendfileHeaderTrailer<'a> { |
| let mut header_iovecs: Option<Vec<IoSlice<'_>>> = |
| headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect()); |
| let mut trailer_iovecs: Option<Vec<IoSlice<'_>>> = |
| trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect()); |
| |
| SendfileHeaderTrailer { |
| raw: libc::sf_hdtr { |
| headers: { |
| header_iovecs |
| .as_mut() |
| .map_or(ptr::null_mut(), |v| v.as_mut_ptr()) |
| .cast() |
| }, |
| hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32, |
| trailers: { |
| trailer_iovecs |
| .as_mut() |
| .map_or(ptr::null_mut(), |v| v.as_mut_ptr()) |
| .cast() |
| }, |
| trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32 |
| }, |
| _headers: header_iovecs, |
| _trailers: trailer_iovecs, |
| } |
| } |
| } |
| } else if #[cfg(solarish)] { |
| use std::os::unix::io::BorrowedFd; |
| use std::marker::PhantomData; |
| |
| #[derive(Debug, Copy, Clone)] |
| /// Mapping of the raw C sendfilevec_t struct |
| pub struct SendfileVec<'fd> { |
| raw: libc::sendfilevec_t, |
| phantom: PhantomData<BorrowedFd<'fd>> |
| } |
| |
| impl<'fd> SendfileVec<'fd> { |
| /// initialises SendfileVec to send data directly from the process's address space |
| /// same in C with sfv_fd set to SFV_FD_SELF. |
| pub fn newself( |
| off: off_t, |
| len: usize |
| ) -> Self { |
| Self{raw: libc::sendfilevec_t{sfv_fd: libc::SFV_FD_SELF, sfv_flag: 0, sfv_off: off, sfv_len: len}, phantom: PhantomData} |
| } |
| |
| /// initialises SendfileVec to send data from `fd`. |
| pub fn new( |
| fd: BorrowedFd<'fd>, |
| off: off_t, |
| len: usize |
| ) -> SendfileVec<'fd> { |
| Self{raw: libc::sendfilevec_t{sfv_fd: fd.as_raw_fd(), sfv_flag: 0, sfv_off:off, sfv_len: len}, phantom: PhantomData} |
| } |
| } |
| |
| impl From<SendfileVec<'_>> for libc::sendfilevec_t { |
| fn from<'fd>(vec: SendfileVec) -> libc::sendfilevec_t { |
| vec.raw |
| } |
| } |
| } |
| } |
| |
| cfg_if! { |
| if #[cfg(target_os = "freebsd")] { |
| use libc::c_int; |
| |
| libc_bitflags!{ |
| /// Configuration options for [`sendfile`.](fn.sendfile.html) |
| pub struct SfFlags: c_int { |
| /// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a |
| /// busy page. |
| SF_NODISKIO; |
| /// Causes `sendfile` to sleep until the network stack releases its reference to the |
| /// VM pages read. When `sendfile` returns, the data is not guaranteed to have been |
| /// sent, but it is safe to modify the file. |
| SF_SYNC; |
| /// Causes `sendfile` to cache exactly the number of pages specified in the |
| /// `readahead` parameter, disabling caching heuristics. |
| SF_USER_READAHEAD; |
| /// Causes `sendfile` not to cache the data read. |
| SF_NOCACHE; |
| } |
| } |
| |
| /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`. |
| /// |
| /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if |
| /// an error occurs. |
| /// |
| /// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a |
| /// stream socket. |
| /// |
| /// If `offset` falls past the end of the file, the function returns success and zero bytes |
| /// written. |
| /// |
| /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of |
| /// file (EOF). |
| /// |
| /// `headers` and `trailers` specify optional slices of byte slices to be sent before and |
| /// after the data read from `in_fd`, respectively. The length of headers and trailers sent |
| /// is included in the returned count of bytes written. The values of `offset` and `count` |
| /// do not apply to headers or trailers. |
| /// |
| /// `readahead` specifies the minimum number of pages to cache in memory ahead of the page |
| /// currently being sent. |
| /// |
| /// For more information, see |
| /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2) |
| #[allow(clippy::too_many_arguments)] |
| pub fn sendfile<F1: AsFd, F2: AsFd>( |
| in_fd: F1, |
| out_sock: F2, |
| offset: off_t, |
| count: Option<usize>, |
| headers: Option<&[&[u8]]>, |
| trailers: Option<&[&[u8]]>, |
| flags: SfFlags, |
| readahead: u16 |
| ) -> (Result<()>, off_t) { |
| // Readahead goes in upper 16 bits |
| // Flags goes in lower 16 bits |
| // see `man 2 sendfile` |
| let ra32 = u32::from(readahead); |
| let flags: u32 = (ra32 << 16) | (flags.bits() as u32); |
| let mut bytes_sent: off_t = 0; |
| let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); |
| let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr); |
| let return_code = unsafe { |
| libc::sendfile(in_fd.as_fd().as_raw_fd(), |
| out_sock.as_fd().as_raw_fd(), |
| offset, |
| count.unwrap_or(0), |
| hdtr_ptr as *mut libc::sf_hdtr, |
| &mut bytes_sent as *mut off_t, |
| flags as c_int) |
| }; |
| (Errno::result(return_code).and(Ok(())), bytes_sent) |
| } |
| } else if #[cfg(target_os = "dragonfly")] { |
| /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`. |
| /// |
| /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if |
| /// an error occurs. |
| /// |
| /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket. |
| /// |
| /// If `offset` falls past the end of the file, the function returns success and zero bytes |
| /// written. |
| /// |
| /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of |
| /// file (EOF). |
| /// |
| /// `headers` and `trailers` specify optional slices of byte slices to be sent before and |
| /// after the data read from `in_fd`, respectively. The length of headers and trailers sent |
| /// is included in the returned count of bytes written. The values of `offset` and `count` |
| /// do not apply to headers or trailers. |
| /// |
| /// For more information, see |
| /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile§ion=2) |
| pub fn sendfile<F1: AsFd, F2: AsFd>( |
| in_fd: F1, |
| out_sock: F2, |
| offset: off_t, |
| count: Option<usize>, |
| headers: Option<&[&[u8]]>, |
| trailers: Option<&[&[u8]]>, |
| ) -> (Result<()>, off_t) { |
| let mut bytes_sent: off_t = 0; |
| let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); |
| let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr); |
| let return_code = unsafe { |
| libc::sendfile(in_fd.as_fd().as_raw_fd(), |
| out_sock.as_fd().as_raw_fd(), |
| offset, |
| count.unwrap_or(0), |
| hdtr_ptr as *mut libc::sf_hdtr, |
| &mut bytes_sent as *mut off_t, |
| 0) |
| }; |
| (Errno::result(return_code).and(Ok(())), bytes_sent) |
| } |
| } else if #[cfg(apple_targets)] { |
| /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to |
| /// `out_sock`. |
| /// |
| /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if |
| /// an error occurs. |
| /// |
| /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket. |
| /// |
| /// If `offset` falls past the end of the file, the function returns success and zero bytes |
| /// written. |
| /// |
| /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of |
| /// file (EOF). |
| /// |
| /// `hdtr` specifies an optional list of headers and trailers to be sent before and after |
| /// the data read from `in_fd`, respectively. The length of headers and trailers sent is |
| /// included in the returned count of bytes written. If any headers are specified and |
| /// `count` is non-zero, the length of the headers will be counted in the limit of total |
| /// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent |
| /// regardless. The value of `offset` does not affect headers or trailers. |
| /// |
| /// For more information, see |
| /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html) |
| pub fn sendfile<F1: AsFd, F2: AsFd>( |
| in_fd: F1, |
| out_sock: F2, |
| offset: off_t, |
| count: Option<off_t>, |
| headers: Option<&[&[u8]]>, |
| trailers: Option<&[&[u8]]> |
| ) -> (Result<()>, off_t) { |
| let mut len = count.unwrap_or(0); |
| let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); |
| let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr); |
| let return_code = unsafe { |
| libc::sendfile(in_fd.as_fd().as_raw_fd(), |
| out_sock.as_fd().as_raw_fd(), |
| offset, |
| &mut len as *mut off_t, |
| hdtr_ptr as *mut libc::sf_hdtr, |
| 0) |
| }; |
| (Errno::result(return_code).and(Ok(())), len) |
| } |
| } else if #[cfg(solarish)] { |
| /// Write data from the vec arrays to `out_sock` and returns a `Result` and a |
| /// count of bytes written. |
| /// |
| /// Each `SendfileVec` set needs to be instantiated either with `SendfileVec::new` or |
| /// `SendfileVec::newself`. |
| /// |
| /// The former allows to send data from a file descriptor through `fd`, |
| /// from an offset `off` and for a given amount of data `len`. |
| /// |
| /// The latter allows to send data from the process's address space, from an offset `off` |
| /// and for a given amount of data `len`. |
| /// |
| /// For more information, see |
| /// [the sendfilev(3) man page.](https://illumos.org/man/3EXT/sendfilev) |
| pub fn sendfilev<F: AsFd>( |
| out_sock: F, |
| vec: &[SendfileVec] |
| ) -> (Result<()>, usize) { |
| let mut len = 0usize; |
| let return_code = unsafe { |
| libc::sendfilev(out_sock.as_fd().as_raw_fd(), vec.as_ptr() as *const libc::sendfilevec_t, vec.len() as i32, &mut len) |
| }; |
| (Errno::result(return_code).and(Ok(())), len) |
| } |
| } |
| } |