Skip to content

Commit f6268d9

Browse files
bors[bot]rtzoeller
andauthored
Merge #1615
1615: Add sendfile(2) for DragonFly r=rtzoeller a=rtzoeller The code is copied from the Mac OS and FreeBSD implementations. Co-authored-by: Ryan Zoeller <rtzoeller@rtzoeller.com>
2 parents e4e17f2 + 4d5c090 commit f6268d9

File tree

5 files changed

+95
-2
lines changed

5 files changed

+95
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
2929
(#[1564](https://github.com/nix-rust/nix/pull/1564))
3030
- Added POSIX per-process timer support
3131
(#[1622](https://github.com/nix-rust/nix/pull/1622))
32+
- Added `sendfile` on DragonFly.
33+
(#[1615](https://github.com/nix-rust/nix/pull/1615))
3234

3335
### Changed
3436
### Fixed

src/sys/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ feature! {
110110
}
111111

112112
#[cfg(any(target_os = "android",
113+
target_os = "dragonfly",
113114
target_os = "freebsd",
114115
target_os = "ios",
115116
target_os = "linux",

src/sys/sendfile.rs

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ pub fn sendfile64(
6464
}
6565

6666
cfg_if! {
67-
if #[cfg(any(target_os = "freebsd",
67+
if #[cfg(any(target_os = "dragonfly",
68+
target_os = "freebsd",
6869
target_os = "ios",
6970
target_os = "macos"))] {
7071
use crate::sys::uio::IoVec;
@@ -184,6 +185,49 @@ cfg_if! {
184185
};
185186
(Errno::result(return_code).and(Ok(())), bytes_sent)
186187
}
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+
}
187231
} else if #[cfg(any(target_os = "ios", target_os = "macos"))] {
188232
/// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
189233
/// `out_sock`.

test/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ mod test_pty;
3333
target_os = "linux"))]
3434
mod test_sched;
3535
#[cfg(any(target_os = "android",
36+
target_os = "dragonfly",
3637
target_os = "freebsd",
3738
target_os = "ios",
3839
target_os = "linux",

test/test_sendfile.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use tempfile::tempfile;
88
cfg_if! {
99
if #[cfg(any(target_os = "android", target_os = "linux"))] {
1010
use nix::unistd::{close, pipe, read};
11-
} else if #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] {
11+
} else if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos"))] {
1212
use std::net::Shutdown;
1313
use std::os::unix::net::UnixStream;
1414
}
@@ -105,6 +105,51 @@ fn test_sendfile_freebsd() {
105105
assert_eq!(expected_string, read_string);
106106
}
107107

108+
#[cfg(target_os = "dragonfly")]
109+
#[test]
110+
fn test_sendfile_dragonfly() {
111+
// Declare the content
112+
let header_strings = vec!["HTTP/1.1 200 OK\n", "Content-Type: text/plain\n", "\n"];
113+
let body = "Xabcdef123456";
114+
let body_offset = 1;
115+
let trailer_strings = vec!["\n", "Served by Make Believe\n"];
116+
117+
// Write the body to a file
118+
let mut tmp = tempfile().unwrap();
119+
tmp.write_all(body.as_bytes()).unwrap();
120+
121+
// Prepare headers and trailers for sendfile
122+
let headers: Vec<&[u8]> = header_strings.iter().map(|s| s.as_bytes()).collect();
123+
let trailers: Vec<&[u8]> = trailer_strings.iter().map(|s| s.as_bytes()).collect();
124+
125+
// Prepare socket pair
126+
let (mut rd, wr) = UnixStream::pair().unwrap();
127+
128+
// Call the test method
129+
let (res, bytes_written) = sendfile(
130+
tmp.as_raw_fd(),
131+
wr.as_raw_fd(),
132+
body_offset as off_t,
133+
None,
134+
Some(headers.as_slice()),
135+
Some(trailers.as_slice()),
136+
);
137+
assert!(res.is_ok());
138+
wr.shutdown(Shutdown::Both).unwrap();
139+
140+
// Prepare the expected result
141+
let expected_string =
142+
header_strings.concat() + &body[body_offset..] + &trailer_strings.concat();
143+
144+
// Verify the message that was sent
145+
assert_eq!(bytes_written as usize, expected_string.as_bytes().len());
146+
147+
let mut read_string = String::new();
148+
let bytes_read = rd.read_to_string(&mut read_string).unwrap();
149+
assert_eq!(bytes_written as usize, bytes_read);
150+
assert_eq!(expected_string, read_string);
151+
}
152+
108153
#[cfg(any(target_os = "ios", target_os = "macos"))]
109154
#[test]
110155
fn test_sendfile_darwin() {

0 commit comments

Comments
 (0)