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&section=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