From c381997c0ae55cea2e1e6ff1e89f424472eda0ef Mon Sep 17 00:00:00 2001 From: Kamal Marhubi Date: Tue, 9 Feb 2016 21:15:59 -0500 Subject: linux: Add splice(2), tee(2), vmsplice(2) --- src/fcntl.rs | 42 ++++++++++++++++++++++++++- test/test.rs | 1 + test/test_fcntl.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 test/test_fcntl.rs diff --git a/src/fcntl.rs b/src/fcntl.rs index 2b1c7b89..c98674c7 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -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; @@ -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 { + 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 { + 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 { + 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 { diff --git a/test/test.rs b/test/test.rs index 53f61fb5..045f8f18 100644 --- a/test/test.rs +++ b/test/test.rs @@ -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"))] diff --git a/test/test_fcntl.rs b/test/test_fcntl.rs new file mode 100644 index 00000000..ca2e89a3 --- /dev/null +++ b/test/test_fcntl.rs @@ -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(); + } + +} -- cgit v1.2.3