Skip to content

Commit ab5c032

Browse files
committed
feat: I/O safety for 'sys/sendfile'
1 parent 7f18847 commit ab5c032

File tree

2 files changed

+58
-35
lines changed

2 files changed

+58
-35
lines changed

src/sys/sendfile.rs

Lines changed: 38 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Send data from a file to a socket, bypassing userland.
22
33
use cfg_if::cfg_if;
4-
use std::os::unix::io::RawFd;
4+
use std::os::unix::io::{AsFd, AsRawFd};
55
use std::ptr;
66

77
use libc::{self, off_t};
@@ -23,16 +23,23 @@ use crate::Result;
2323
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
2424
#[cfg(any(target_os = "android", target_os = "linux"))]
2525
#[cfg_attr(docsrs, doc(cfg(all())))]
26-
pub fn sendfile(
27-
out_fd: RawFd,
28-
in_fd: RawFd,
26+
pub fn sendfile<F1: AsFd, F2: AsFd>(
27+
out_fd: F1,
28+
in_fd: F2,
2929
offset: Option<&mut off_t>,
3030
count: usize,
3131
) -> Result<usize> {
3232
let offset = offset
3333
.map(|offset| offset as *mut _)
3434
.unwrap_or(ptr::null_mut());
35-
let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) };
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+
};
3643
Errno::result(ret).map(|r| r as usize)
3744
}
3845

@@ -50,16 +57,23 @@ pub fn sendfile(
5057
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
5158
#[cfg(target_os = "linux")]
5259
#[cfg_attr(docsrs, doc(cfg(all())))]
53-
pub fn sendfile64(
54-
out_fd: RawFd,
55-
in_fd: RawFd,
60+
pub fn sendfile64<F1: AsFd, F2: AsFd>(
61+
out_fd: F1,
62+
in_fd: F2,
5663
offset: Option<&mut libc::off64_t>,
5764
count: usize,
5865
) -> Result<usize> {
5966
let offset = offset
6067
.map(|offset| offset as *mut _)
6168
.unwrap_or(ptr::null_mut());
62-
let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) };
69+
let ret = unsafe {
70+
libc::sendfile64(
71+
out_fd.as_fd().as_raw_fd(),
72+
in_fd.as_fd().as_raw_fd(),
73+
offset,
74+
count,
75+
)
76+
};
6377
Errno::result(ret).map(|r| r as usize)
6478
}
6579

@@ -156,9 +170,9 @@ cfg_if! {
156170
/// For more information, see
157171
/// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
158172
#[allow(clippy::too_many_arguments)]
159-
pub fn sendfile(
160-
in_fd: RawFd,
161-
out_sock: RawFd,
173+
pub fn sendfile<F1: AsFd, F2: AsFd>(
174+
in_fd: F1,
175+
out_sock: F2,
162176
offset: off_t,
163177
count: Option<usize>,
164178
headers: Option<&[&[u8]]>,
@@ -175,8 +189,8 @@ cfg_if! {
175189
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
176190
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
177191
let return_code = unsafe {
178-
libc::sendfile(in_fd,
179-
out_sock,
192+
libc::sendfile(in_fd.as_fd().as_raw_fd(),
193+
out_sock.as_fd().as_raw_fd(),
180194
offset,
181195
count.unwrap_or(0),
182196
hdtr_ptr as *mut libc::sf_hdtr,
@@ -206,9 +220,9 @@ cfg_if! {
206220
///
207221
/// For more information, see
208222
/// [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,
223+
pub fn sendfile<F1: AsFd, F2: AsFd>(
224+
in_fd: F1,
225+
out_sock: F2,
212226
offset: off_t,
213227
count: Option<usize>,
214228
headers: Option<&[&[u8]]>,
@@ -218,8 +232,8 @@ cfg_if! {
218232
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
219233
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
220234
let return_code = unsafe {
221-
libc::sendfile(in_fd,
222-
out_sock,
235+
libc::sendfile(in_fd.as_fd().as_raw_fd(),
236+
out_sock.as_fd().as_raw_fd(),
223237
offset,
224238
count.unwrap_or(0),
225239
hdtr_ptr as *mut libc::sf_hdtr,
@@ -252,9 +266,9 @@ cfg_if! {
252266
///
253267
/// For more information, see
254268
/// [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,
269+
pub fn sendfile<F1: AsFd, F2: AsFd>(
270+
in_fd: F1,
271+
out_sock: F2,
258272
offset: off_t,
259273
count: Option<off_t>,
260274
headers: Option<&[&[u8]]>,
@@ -264,8 +278,8 @@ cfg_if! {
264278
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
265279
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
266280
let return_code = unsafe {
267-
libc::sendfile(in_fd,
268-
out_sock,
281+
libc::sendfile(in_fd.as_fd().as_raw_fd(),
282+
out_sock.as_fd().as_raw_fd(),
269283
offset,
270284
&mut len as *mut off_t,
271285
hdtr_ptr as *mut libc::sf_hdtr,

test/test_sendfile.rs

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::io::prelude::*;
2-
use std::os::unix::prelude::*;
2+
#[cfg(any(target_os = "android", target_os = "linux"))]
3+
use std::os::unix::io::{FromRawFd, OwnedFd};
34

45
use libc::off_t;
56
use nix::sys::sendfile::*;
@@ -23,7 +24,12 @@ fn test_sendfile_linux() {
2324

2425
let (rd, wr) = pipe().unwrap();
2526
let mut offset: off_t = 5;
26-
let res = sendfile(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap();
27+
// The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)`
28+
// becomes I/O-safe:
29+
// pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error>
30+
// then it is no longer needed.
31+
let wr = unsafe { OwnedFd::from_raw_fd(wr) };
32+
let res = sendfile(&wr, &tmp, Some(&mut offset), 2).unwrap();
2733

2834
assert_eq!(2, res);
2935

@@ -33,7 +39,6 @@ fn test_sendfile_linux() {
3339
assert_eq!(7, offset);
3440

3541
close(rd).unwrap();
36-
close(wr).unwrap();
3742
}
3843

3944
#[cfg(target_os = "linux")]
@@ -45,7 +50,12 @@ fn test_sendfile64_linux() {
4550

4651
let (rd, wr) = pipe().unwrap();
4752
let mut offset: libc::off64_t = 5;
48-
let res = sendfile64(wr, tmp.as_raw_fd(), Some(&mut offset), 2).unwrap();
53+
// The construct of this `OwnedFd` is a temporary workaround, when `pipe(2)`
54+
// becomes I/O-safe:
55+
// pub fn pipe() -> std::result::Result<(OwnedFd, OwnedFd), Error>
56+
// then it is no longer needed.
57+
let wr = unsafe { OwnedFd::from_raw_fd(wr) };
58+
let res = sendfile64(&wr, &tmp, Some(&mut offset), 2).unwrap();
4959

5060
assert_eq!(2, res);
5161

@@ -55,7 +65,6 @@ fn test_sendfile64_linux() {
5565
assert_eq!(7, offset);
5666

5767
close(rd).unwrap();
58-
close(wr).unwrap();
5968
}
6069

6170
#[cfg(target_os = "freebsd")]
@@ -83,8 +92,8 @@ fn test_sendfile_freebsd() {
8392

8493
// Call the test method
8594
let (res, bytes_written) = sendfile(
86-
tmp.as_raw_fd(),
87-
wr.as_raw_fd(),
95+
&tmp,
96+
&wr,
8897
body_offset as off_t,
8998
None,
9099
Some(headers.as_slice()),
@@ -134,8 +143,8 @@ fn test_sendfile_dragonfly() {
134143

135144
// Call the test method
136145
let (res, bytes_written) = sendfile(
137-
tmp.as_raw_fd(),
138-
wr.as_raw_fd(),
146+
&tmp,
147+
&wr,
139148
body_offset as off_t,
140149
None,
141150
Some(headers.as_slice()),
@@ -183,8 +192,8 @@ fn test_sendfile_darwin() {
183192

184193
// Call the test method
185194
let (res, bytes_written) = sendfile(
186-
tmp.as_raw_fd(),
187-
wr.as_raw_fd(),
195+
&tmp,
196+
&wr,
188197
body_offset as off_t,
189198
None,
190199
Some(headers.as_slice()),

0 commit comments

Comments
 (0)