• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Send data from a file to a socket, bypassing userland.
2 
3 use cfg_if::cfg_if;
4 use std::os::unix::io::RawFd;
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)
24 #[cfg(any(target_os = "android", target_os = "linux"))]
25 #[cfg_attr(docsrs, doc(cfg(all())))]
sendfile( out_fd: RawFd, in_fd: RawFd, offset: Option<&mut off_t>, count: usize, ) -> Result<usize>26 pub 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())))]
sendfile64( out_fd: RawFd, in_fd: RawFd, offset: Option<&mut libc::off64_t>, count: usize, ) -> Result<usize>53 pub 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 
66 cfg_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 
112 cfg_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&section=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