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§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