1 //! Send data from a file to a socket, bypassing userland.
2
3 use cfg_if::cfg_if;
4 use std::os::unix::io::{AsFd, AsRawFd};
5 use std::ptr;
6
7 use libc::{self, off_t};
8
9 use crate::errno::Errno;
10 use 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) for Linux,
24 /// see [the sendfile(2) man page.](https://docs.oracle.com/cd/E88353_01/html/E37843/sendfile-3c.html) for Solaris.
25 #[cfg(any(linux_android, solarish))]
sendfile<F1: AsFd, F2: AsFd>( out_fd: F1, in_fd: F2, offset: Option<&mut off_t>, count: usize, ) -> Result<usize>26 pub fn sendfile<F1: AsFd, F2: AsFd>(
27 out_fd: F1,
28 in_fd: F2,
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 {
36 libc::sendfile(
37 out_fd.as_fd().as_raw_fd(),
38 in_fd.as_fd().as_raw_fd(),
39 offset,
40 count,
41 )
42 };
43 Errno::result(ret).map(|r| r as usize)
44 }
45
46 /// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
47 ///
48 /// Returns a `Result` with the number of bytes written.
49 ///
50 /// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
51 /// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
52 /// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
53 /// the byte after the last byte copied.
54 ///
55 /// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
56 ///
57 /// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
58 #[cfg(target_os = "linux")]
sendfile64<F1: AsFd, F2: AsFd>( out_fd: F1, in_fd: F2, offset: Option<&mut libc::off64_t>, count: usize, ) -> Result<usize>59 pub fn sendfile64<F1: AsFd, F2: AsFd>(
60 out_fd: F1,
61 in_fd: F2,
62 offset: Option<&mut libc::off64_t>,
63 count: usize,
64 ) -> Result<usize> {
65 let offset = offset
66 .map(|offset| offset as *mut _)
67 .unwrap_or(ptr::null_mut());
68 let ret = unsafe {
69 libc::sendfile64(
70 out_fd.as_fd().as_raw_fd(),
71 in_fd.as_fd().as_raw_fd(),
72 offset,
73 count,
74 )
75 };
76 Errno::result(ret).map(|r| r as usize)
77 }
78
79 cfg_if! {
80 if #[cfg(any(freebsdlike, apple_targets))] {
81 use std::io::IoSlice;
82
83 #[derive(Clone, Debug)]
84 struct SendfileHeaderTrailer<'a> {
85 raw: libc::sf_hdtr,
86 _headers: Option<Vec<IoSlice<'a>>>,
87 _trailers: Option<Vec<IoSlice<'a>>>,
88 }
89
90 impl<'a> SendfileHeaderTrailer<'a> {
91 fn new(
92 headers: Option<&'a [&'a [u8]]>,
93 trailers: Option<&'a [&'a [u8]]>
94 ) -> SendfileHeaderTrailer<'a> {
95 let mut header_iovecs: Option<Vec<IoSlice<'_>>> =
96 headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
97 let mut trailer_iovecs: Option<Vec<IoSlice<'_>>> =
98 trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
99
100 SendfileHeaderTrailer {
101 raw: libc::sf_hdtr {
102 headers: {
103 header_iovecs
104 .as_mut()
105 .map_or(ptr::null_mut(), |v| v.as_mut_ptr())
106 .cast()
107 },
108 hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32,
109 trailers: {
110 trailer_iovecs
111 .as_mut()
112 .map_or(ptr::null_mut(), |v| v.as_mut_ptr())
113 .cast()
114 },
115 trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32
116 },
117 _headers: header_iovecs,
118 _trailers: trailer_iovecs,
119 }
120 }
121 }
122 } else if #[cfg(solarish)] {
123 use std::os::unix::io::BorrowedFd;
124 use std::marker::PhantomData;
125
126 #[derive(Debug, Copy, Clone)]
127 /// Mapping of the raw C sendfilevec_t struct
128 pub struct SendfileVec<'fd> {
129 raw: libc::sendfilevec_t,
130 phantom: PhantomData<BorrowedFd<'fd>>
131 }
132
133 impl<'fd> SendfileVec<'fd> {
134 /// initialises SendfileVec to send data directly from the process's address space
135 /// same in C with sfv_fd set to SFV_FD_SELF.
136 pub fn newself(
137 off: off_t,
138 len: usize
139 ) -> Self {
140 Self{raw: libc::sendfilevec_t{sfv_fd: libc::SFV_FD_SELF, sfv_flag: 0, sfv_off: off, sfv_len: len}, phantom: PhantomData}
141 }
142
143 /// initialises SendfileVec to send data from `fd`.
144 pub fn new(
145 fd: BorrowedFd<'fd>,
146 off: off_t,
147 len: usize
148 ) -> SendfileVec<'fd> {
149 Self{raw: libc::sendfilevec_t{sfv_fd: fd.as_raw_fd(), sfv_flag: 0, sfv_off:off, sfv_len: len}, phantom: PhantomData}
150 }
151 }
152
153 impl From<SendfileVec<'_>> for libc::sendfilevec_t {
154 fn from<'fd>(vec: SendfileVec) -> libc::sendfilevec_t {
155 vec.raw
156 }
157 }
158 }
159 }
160
161 cfg_if! {
162 if #[cfg(target_os = "freebsd")] {
163 use libc::c_int;
164
165 libc_bitflags!{
166 /// Configuration options for [`sendfile`.](fn.sendfile.html)
167 pub struct SfFlags: c_int {
168 /// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a
169 /// busy page.
170 SF_NODISKIO;
171 /// Causes `sendfile` to sleep until the network stack releases its reference to the
172 /// VM pages read. When `sendfile` returns, the data is not guaranteed to have been
173 /// sent, but it is safe to modify the file.
174 SF_SYNC;
175 /// Causes `sendfile` to cache exactly the number of pages specified in the
176 /// `readahead` parameter, disabling caching heuristics.
177 SF_USER_READAHEAD;
178 /// Causes `sendfile` not to cache the data read.
179 SF_NOCACHE;
180 }
181 }
182
183 /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
184 ///
185 /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
186 /// an error occurs.
187 ///
188 /// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a
189 /// stream socket.
190 ///
191 /// If `offset` falls past the end of the file, the function returns success and zero bytes
192 /// written.
193 ///
194 /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
195 /// file (EOF).
196 ///
197 /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
198 /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
199 /// is included in the returned count of bytes written. The values of `offset` and `count`
200 /// do not apply to headers or trailers.
201 ///
202 /// `readahead` specifies the minimum number of pages to cache in memory ahead of the page
203 /// currently being sent.
204 ///
205 /// For more information, see
206 /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
207 #[allow(clippy::too_many_arguments)]
208 pub fn sendfile<F1: AsFd, F2: AsFd>(
209 in_fd: F1,
210 out_sock: F2,
211 offset: off_t,
212 count: Option<usize>,
213 headers: Option<&[&[u8]]>,
214 trailers: Option<&[&[u8]]>,
215 flags: SfFlags,
216 readahead: u16
217 ) -> (Result<()>, off_t) {
218 // Readahead goes in upper 16 bits
219 // Flags goes in lower 16 bits
220 // see `man 2 sendfile`
221 let ra32 = u32::from(readahead);
222 let flags: u32 = (ra32 << 16) | (flags.bits() as u32);
223 let mut bytes_sent: off_t = 0;
224 let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
225 let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
226 let return_code = unsafe {
227 libc::sendfile(in_fd.as_fd().as_raw_fd(),
228 out_sock.as_fd().as_raw_fd(),
229 offset,
230 count.unwrap_or(0),
231 hdtr_ptr as *mut libc::sf_hdtr,
232 &mut bytes_sent as *mut off_t,
233 flags as c_int)
234 };
235 (Errno::result(return_code).and(Ok(())), bytes_sent)
236 }
237 } else if #[cfg(target_os = "dragonfly")] {
238 /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
239 ///
240 /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
241 /// an error occurs.
242 ///
243 /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
244 ///
245 /// If `offset` falls past the end of the file, the function returns success and zero bytes
246 /// written.
247 ///
248 /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
249 /// file (EOF).
250 ///
251 /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
252 /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
253 /// is included in the returned count of bytes written. The values of `offset` and `count`
254 /// do not apply to headers or trailers.
255 ///
256 /// For more information, see
257 /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile§ion=2)
258 pub fn sendfile<F1: AsFd, F2: AsFd>(
259 in_fd: F1,
260 out_sock: F2,
261 offset: off_t,
262 count: Option<usize>,
263 headers: Option<&[&[u8]]>,
264 trailers: Option<&[&[u8]]>,
265 ) -> (Result<()>, off_t) {
266 let mut bytes_sent: off_t = 0;
267 let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
268 let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
269 let return_code = unsafe {
270 libc::sendfile(in_fd.as_fd().as_raw_fd(),
271 out_sock.as_fd().as_raw_fd(),
272 offset,
273 count.unwrap_or(0),
274 hdtr_ptr as *mut libc::sf_hdtr,
275 &mut bytes_sent as *mut off_t,
276 0)
277 };
278 (Errno::result(return_code).and(Ok(())), bytes_sent)
279 }
280 } else if #[cfg(apple_targets)] {
281 /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
282 /// `out_sock`.
283 ///
284 /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
285 /// an error occurs.
286 ///
287 /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
288 ///
289 /// If `offset` falls past the end of the file, the function returns success and zero bytes
290 /// written.
291 ///
292 /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
293 /// file (EOF).
294 ///
295 /// `hdtr` specifies an optional list of headers and trailers to be sent before and after
296 /// the data read from `in_fd`, respectively. The length of headers and trailers sent is
297 /// included in the returned count of bytes written. If any headers are specified and
298 /// `count` is non-zero, the length of the headers will be counted in the limit of total
299 /// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent
300 /// regardless. The value of `offset` does not affect headers or trailers.
301 ///
302 /// For more information, see
303 /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
304 pub fn sendfile<F1: AsFd, F2: AsFd>(
305 in_fd: F1,
306 out_sock: F2,
307 offset: off_t,
308 count: Option<off_t>,
309 headers: Option<&[&[u8]]>,
310 trailers: Option<&[&[u8]]>
311 ) -> (Result<()>, off_t) {
312 let mut len = count.unwrap_or(0);
313 let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
314 let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.raw as *const libc::sf_hdtr);
315 let return_code = unsafe {
316 libc::sendfile(in_fd.as_fd().as_raw_fd(),
317 out_sock.as_fd().as_raw_fd(),
318 offset,
319 &mut len as *mut off_t,
320 hdtr_ptr as *mut libc::sf_hdtr,
321 0)
322 };
323 (Errno::result(return_code).and(Ok(())), len)
324 }
325 } else if #[cfg(solarish)] {
326 /// Write data from the vec arrays to `out_sock` and returns a `Result` and a
327 /// count of bytes written.
328 ///
329 /// Each `SendfileVec` set needs to be instantiated either with `SendfileVec::new` or
330 /// `SendfileVec::newself`.
331 ///
332 /// The former allows to send data from a file descriptor through `fd`,
333 /// from an offset `off` and for a given amount of data `len`.
334 ///
335 /// The latter allows to send data from the process's address space, from an offset `off`
336 /// and for a given amount of data `len`.
337 ///
338 /// For more information, see
339 /// [the sendfilev(3) man page.](https://illumos.org/man/3EXT/sendfilev)
340 pub fn sendfilev<F: AsFd>(
341 out_sock: F,
342 vec: &[SendfileVec]
343 ) -> (Result<()>, usize) {
344 let mut len = 0usize;
345 let return_code = unsafe {
346 libc::sendfilev(out_sock.as_fd().as_raw_fd(), vec.as_ptr() as *const libc::sendfilevec_t, vec.len() as i32, &mut len)
347 };
348 (Errno::result(return_code).and(Ok(())), len)
349 }
350 }
351 }
352