1//! Send data from a file to a socket, bypassing userland. 2 3use cfg_if::cfg_if; 4use std::os::unix::io::RawFd; 5use std::ptr; 6 7use libc::{self, off_t}; 8 9use crate::errno::Errno; 10use crate::Result; 11 12/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`. 13/// 14/// Returns a `Result` with the number of bytes written. 15/// 16/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will 17/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified 18/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to 19/// the byte after the last byte copied. 20/// 21/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket. 22/// 23/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) 24#[cfg(any(target_os = "android", target_os = "linux"))] 25#[cfg_attr(docsrs, doc(cfg(all())))] 26pub fn sendfile( 27 out_fd: RawFd, 28 in_fd: RawFd, 29 offset: Option<&mut off_t>, 30 count: usize, 31) -> Result<usize> { 32 let offset = offset 33 .map(|offset| offset as *mut _) 34 .unwrap_or(ptr::null_mut()); 35 let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) }; 36 Errno::result(ret).map(|r| r as usize) 37} 38 39/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`. 40/// 41/// Returns a `Result` with the number of bytes written. 42/// 43/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will 44/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified 45/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to 46/// the byte after the last byte copied. 47/// 48/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket. 49/// 50/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html) 51#[cfg(target_os = "linux")] 52#[cfg_attr(docsrs, doc(cfg(all())))] 53pub fn sendfile64( 54 out_fd: RawFd, 55 in_fd: RawFd, 56 offset: Option<&mut libc::off64_t>, 57 count: usize, 58) -> Result<usize> { 59 let offset = offset 60 .map(|offset| offset as *mut _) 61 .unwrap_or(ptr::null_mut()); 62 let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) }; 63 Errno::result(ret).map(|r| r as usize) 64} 65 66cfg_if! { 67 if #[cfg(any(target_os = "dragonfly", 68 target_os = "freebsd", 69 target_os = "ios", 70 target_os = "macos"))] { 71 use std::io::IoSlice; 72 73 #[derive(Clone, Debug)] 74 struct SendfileHeaderTrailer<'a>( 75 libc::sf_hdtr, 76 Option<Vec<IoSlice<'a>>>, 77 Option<Vec<IoSlice<'a>>>, 78 ); 79 80 impl<'a> SendfileHeaderTrailer<'a> { 81 fn new( 82 headers: Option<&'a [&'a [u8]]>, 83 trailers: Option<&'a [&'a [u8]]> 84 ) -> SendfileHeaderTrailer<'a> { 85 let header_iovecs: Option<Vec<IoSlice<'_>>> = 86 headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect()); 87 let trailer_iovecs: Option<Vec<IoSlice<'_>>> = 88 trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect()); 89 SendfileHeaderTrailer( 90 libc::sf_hdtr { 91 headers: { 92 header_iovecs 93 .as_ref() 94 .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec 95 }, 96 hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32, 97 trailers: { 98 trailer_iovecs 99 .as_ref() 100 .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec 101 }, 102 trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32 103 }, 104 header_iovecs, 105 trailer_iovecs, 106 ) 107 } 108 } 109 } 110} 111 112cfg_if! { 113 if #[cfg(target_os = "freebsd")] { 114 use libc::c_int; 115 116 libc_bitflags!{ 117 /// Configuration options for [`sendfile`.](fn.sendfile.html) 118 pub struct SfFlags: c_int { 119 /// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a 120 /// busy page. 121 SF_NODISKIO; 122 /// Causes `sendfile` to sleep until the network stack releases its reference to the 123 /// VM pages read. When `sendfile` returns, the data is not guaranteed to have been 124 /// sent, but it is safe to modify the file. 125 SF_SYNC; 126 /// Causes `sendfile` to cache exactly the number of pages specified in the 127 /// `readahead` parameter, disabling caching heuristics. 128 SF_USER_READAHEAD; 129 /// Causes `sendfile` not to cache the data read. 130 SF_NOCACHE; 131 } 132 } 133 134 /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`. 135 /// 136 /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if 137 /// an error occurs. 138 /// 139 /// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a 140 /// stream socket. 141 /// 142 /// If `offset` falls past the end of the file, the function returns success and zero bytes 143 /// written. 144 /// 145 /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of 146 /// file (EOF). 147 /// 148 /// `headers` and `trailers` specify optional slices of byte slices to be sent before and 149 /// after the data read from `in_fd`, respectively. The length of headers and trailers sent 150 /// is included in the returned count of bytes written. The values of `offset` and `count` 151 /// do not apply to headers or trailers. 152 /// 153 /// `readahead` specifies the minimum number of pages to cache in memory ahead of the page 154 /// currently being sent. 155 /// 156 /// For more information, see 157 /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2) 158 #[allow(clippy::too_many_arguments)] 159 pub fn sendfile( 160 in_fd: RawFd, 161 out_sock: RawFd, 162 offset: off_t, 163 count: Option<usize>, 164 headers: Option<&[&[u8]]>, 165 trailers: Option<&[&[u8]]>, 166 flags: SfFlags, 167 readahead: u16 168 ) -> (Result<()>, off_t) { 169 // Readahead goes in upper 16 bits 170 // Flags goes in lower 16 bits 171 // see `man 2 sendfile` 172 let ra32 = u32::from(readahead); 173 let flags: u32 = (ra32 << 16) | (flags.bits() as u32); 174 let mut bytes_sent: off_t = 0; 175 let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); 176 let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); 177 let return_code = unsafe { 178 libc::sendfile(in_fd, 179 out_sock, 180 offset, 181 count.unwrap_or(0), 182 hdtr_ptr as *mut libc::sf_hdtr, 183 &mut bytes_sent as *mut off_t, 184 flags as c_int) 185 }; 186 (Errno::result(return_code).and(Ok(())), bytes_sent) 187 } 188 } else if #[cfg(target_os = "dragonfly")] { 189 /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`. 190 /// 191 /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if 192 /// an error occurs. 193 /// 194 /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket. 195 /// 196 /// If `offset` falls past the end of the file, the function returns success and zero bytes 197 /// written. 198 /// 199 /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of 200 /// file (EOF). 201 /// 202 /// `headers` and `trailers` specify optional slices of byte slices to be sent before and 203 /// after the data read from `in_fd`, respectively. The length of headers and trailers sent 204 /// is included in the returned count of bytes written. The values of `offset` and `count` 205 /// do not apply to headers or trailers. 206 /// 207 /// For more information, see 208 /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile§ion=2) 209 pub fn sendfile( 210 in_fd: RawFd, 211 out_sock: RawFd, 212 offset: off_t, 213 count: Option<usize>, 214 headers: Option<&[&[u8]]>, 215 trailers: Option<&[&[u8]]>, 216 ) -> (Result<()>, off_t) { 217 let mut bytes_sent: off_t = 0; 218 let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); 219 let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); 220 let return_code = unsafe { 221 libc::sendfile(in_fd, 222 out_sock, 223 offset, 224 count.unwrap_or(0), 225 hdtr_ptr as *mut libc::sf_hdtr, 226 &mut bytes_sent as *mut off_t, 227 0) 228 }; 229 (Errno::result(return_code).and(Ok(())), bytes_sent) 230 } 231 } else if #[cfg(any(target_os = "ios", target_os = "macos"))] { 232 /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to 233 /// `out_sock`. 234 /// 235 /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if 236 /// an error occurs. 237 /// 238 /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket. 239 /// 240 /// If `offset` falls past the end of the file, the function returns success and zero bytes 241 /// written. 242 /// 243 /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of 244 /// file (EOF). 245 /// 246 /// `hdtr` specifies an optional list of headers and trailers to be sent before and after 247 /// the data read from `in_fd`, respectively. The length of headers and trailers sent is 248 /// included in the returned count of bytes written. If any headers are specified and 249 /// `count` is non-zero, the length of the headers will be counted in the limit of total 250 /// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent 251 /// regardless. The value of `offset` does not affect headers or trailers. 252 /// 253 /// For more information, see 254 /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html) 255 pub fn sendfile( 256 in_fd: RawFd, 257 out_sock: RawFd, 258 offset: off_t, 259 count: Option<off_t>, 260 headers: Option<&[&[u8]]>, 261 trailers: Option<&[&[u8]]> 262 ) -> (Result<()>, off_t) { 263 let mut len = count.unwrap_or(0); 264 let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers)); 265 let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr); 266 let return_code = unsafe { 267 libc::sendfile(in_fd, 268 out_sock, 269 offset, 270 &mut len as *mut off_t, 271 hdtr_ptr as *mut libc::sf_hdtr, 272 0) 273 }; 274 (Errno::result(return_code).and(Ok(())), len) 275 } 276 } 277} 278