Skip to content

Commit

Permalink
feat: I/O safety for 'sys/sendfile'
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveLauC committed Dec 8, 2022
1 parent 7f18847 commit d53db5c
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 35 deletions.
62 changes: 38 additions & 24 deletions src/sys/sendfile.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Send data from a file to a socket, bypassing userland.
use cfg_if::cfg_if;
use std::os::unix::io::RawFd;
use std::os::unix::io::{AsFd, AsRawFd};
use std::ptr;

use libc::{self, off_t};
Expand All @@ -23,16 +23,23 @@ use crate::Result;
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(any(target_os = "android", target_os = "linux"))]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn sendfile(
out_fd: RawFd,
in_fd: RawFd,
pub fn sendfile<F1: AsFd, F2: AsFd>(
out_fd: &F1,
in_fd: &F2,
offset: Option<&mut off_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) };
let ret = unsafe {
libc::sendfile(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}

Expand All @@ -50,16 +57,23 @@ pub fn sendfile(
/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(target_os = "linux")]
#[cfg_attr(docsrs, doc(cfg(all())))]
pub fn sendfile64(
out_fd: RawFd,
in_fd: RawFd,
pub fn sendfile64<F1: AsFd, F2: AsFd>(
out_fd: &F1,
in_fd: &F2,
offset: Option<&mut libc::off64_t>,
count: usize,
) -> Result<usize> {
let offset = offset
.map(|offset| offset as *mut _)
.unwrap_or(ptr::null_mut());
let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) };
let ret = unsafe {
libc::sendfile64(
out_fd.as_fd().as_raw_fd(),
in_fd.as_fd().as_raw_fd(),
offset,
count,
)
};
Errno::result(ret).map(|r| r as usize)
}

Expand Down Expand Up @@ -156,9 +170,9 @@ cfg_if! {
/// For more information, see
/// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
#[allow(clippy::too_many_arguments)]
pub fn sendfile(
in_fd: RawFd,
out_sock: RawFd,
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: &F1,
out_sock: &F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
Expand All @@ -175,8 +189,8 @@ cfg_if! {
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd,
out_sock,
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
Expand Down Expand Up @@ -206,9 +220,9 @@ cfg_if! {
///
/// For more information, see
/// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
pub fn sendfile(
in_fd: RawFd,
out_sock: RawFd,
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: &F1,
out_sock: &F2,
offset: off_t,
count: Option<usize>,
headers: Option<&[&[u8]]>,
Expand All @@ -218,8 +232,8 @@ cfg_if! {
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd,
out_sock,
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
count.unwrap_or(0),
hdtr_ptr as *mut libc::sf_hdtr,
Expand Down Expand Up @@ -252,9 +266,9 @@ cfg_if! {
///
/// For more information, see
/// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
pub fn sendfile(
in_fd: RawFd,
out_sock: RawFd,
pub fn sendfile<F1: AsFd, F2: AsFd>(
in_fd: &F1,
out_sock: &F2,
offset: off_t,
count: Option<off_t>,
headers: Option<&[&[u8]]>,
Expand All @@ -264,8 +278,8 @@ cfg_if! {
let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
let return_code = unsafe {
libc::sendfile(in_fd,
out_sock,
libc::sendfile(in_fd.as_fd().as_raw_fd(),
out_sock.as_fd().as_raw_fd(),
offset,
&mut len as *mut off_t,
hdtr_ptr as *mut libc::sf_hdtr,
Expand Down
31 changes: 20 additions & 11 deletions test/test_sendfile.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::io::prelude::*;
use std::os::unix::prelude::*;
#[cfg(any(target_os = "android", target_os = "linux"))]
use std::os::unix::io::{FromRawFd, OwnedFd};

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

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

assert_eq!(2, res);

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

close(rd).unwrap();
close(wr).unwrap();
}

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

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

assert_eq!(2, res);

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

close(rd).unwrap();
close(wr).unwrap();
}

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

// Call the test method
let (res, bytes_written) = sendfile(
tmp.as_raw_fd(),
wr.as_raw_fd(),
&tmp,
&wr,
body_offset as off_t,
None,
Some(headers.as_slice()),
Expand Down Expand Up @@ -134,8 +143,8 @@ fn test_sendfile_dragonfly() {

// Call the test method
let (res, bytes_written) = sendfile(
tmp.as_raw_fd(),
wr.as_raw_fd(),
&tmp,
&wr,
body_offset as off_t,
None,
Some(headers.as_slice()),
Expand Down Expand Up @@ -183,8 +192,8 @@ fn test_sendfile_darwin() {

// Call the test method
let (res, bytes_written) = sendfile(
tmp.as_raw_fd(),
wr.as_raw_fd(),
&tmp,
&wr,
body_offset as off_t,
None,
Some(headers.as_slice()),
Expand Down

0 comments on commit d53db5c

Please sign in to comment.