Skip to content

linux: Add splice(2), tee(2), vmsplice(2) #267

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion src/fcntl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ use libc::{c_int, c_uint};
use sys::stat::Mode;
use std::os::unix::io::RawFd;

#[cfg(any(target_os = "linux", target_os = "android"))]
use sys::uio::IoVec; // For vmsplice
#[cfg(any(target_os = "linux", target_os = "android"))]
use libc;

pub use self::consts::*;
pub use self::ffi::flock;

Expand Down Expand Up @@ -181,9 +186,44 @@ pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
Errno::result(res).map(drop)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>,
fd_out: RawFd, off_out: Option<&mut libc::loff_t>,
len: usize, flags: SpliceFFlags) -> Result<usize> {
use std::ptr;
let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());

let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
let ret = unsafe {
libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
};
Errno::result(ret).map(|r| r as usize)
}

#[cfg(any(target_os = "linux", target_os = "android"))]
mod consts {
use libc::c_int;
use libc::{self, c_int, c_uint};

bitflags! {
flags SpliceFFlags: c_uint {
const SPLICE_F_MOVE = libc::SPLICE_F_MOVE,
const SPLICE_F_NONBLOCK = libc::SPLICE_F_NONBLOCK,
const SPLICE_F_MORE = libc::SPLICE_F_MORE,
const SPLICE_F_GIFT = libc::SPLICE_F_GIFT,
}
}

bitflags!(
flags OFlag: c_int {
Expand Down
1 change: 1 addition & 0 deletions test/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ extern crate tempfile;
extern crate nix_test as nixtest;

mod sys;
mod test_fcntl;
mod test_net;
mod test_nix_path;
#[cfg(any(target_os = "linux", target_os = "android"))]
Expand Down
85 changes: 85 additions & 0 deletions test/test_fcntl.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#[cfg(any(target_os = "linux", target_os = "android"))]
mod linux_android {
use std::io::prelude::*;
use std::os::unix::prelude::*;

use libc::loff_t;

use nix::fcntl::{SpliceFFlags, splice, tee, vmsplice};
use nix::sys::uio::IoVec;
use nix::unistd::{close, pipe, read, write};

use tempfile::tempfile;

#[test]
fn test_splice() {
const CONTENTS: &'static [u8] = b"abcdef123456";
let mut tmp = tempfile().unwrap();
tmp.write(CONTENTS).unwrap();

let (rd, wr) = pipe().unwrap();
let mut offset: loff_t = 5;
let res = splice(tmp.as_raw_fd(), Some(&mut offset),
wr, None, 2, SpliceFFlags::empty()).unwrap();

assert_eq!(2, res);

let mut buf = [0u8; 1024];
assert_eq!(2, read(rd, &mut buf).unwrap());
assert_eq!(b"f1", &buf[0..2]);
assert_eq!(7, offset);

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

#[test]
fn test_tee() {
let (rd1, wr1) = pipe().unwrap();
let (rd2, wr2) = pipe().unwrap();

write(wr1, b"abc").unwrap();
let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();

assert_eq!(2, res);

let mut buf = [0u8; 1024];

// Check the tee'd bytes are at rd2.
assert_eq!(2, read(rd2, &mut buf).unwrap());
assert_eq!(b"ab", &buf[0..2]);

// Check all the bytes are still at rd1.
assert_eq!(3, read(rd1, &mut buf).unwrap());
assert_eq!(b"abc", &buf[0..3]);

close(rd1).unwrap();
close(wr1).unwrap();
close(rd2).unwrap();
close(wr2).unwrap();
}

#[test]
fn test_vmsplice() {
let (rd, wr) = pipe().unwrap();

let buf1 = b"abcdef";
let buf2 = b"defghi";
let mut iovecs = Vec::with_capacity(2);
iovecs.push(IoVec::from_slice(&buf1[0..3]));
iovecs.push(IoVec::from_slice(&buf2[0..3]));

let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();

assert_eq!(6, res);

// Check the bytes can be read at rd.
let mut buf = [0u8; 32];
assert_eq!(6, read(rd, &mut buf).unwrap());
assert_eq!(b"abcdef", &buf[0..6]);

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

}