summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamal Marhubi <kamal@marhubi.com>2016-02-09 21:15:59 -0500
committerKamal Marhubi <kamal@marhubi.com>2016-02-14 23:41:57 -0500
commitc381997c0ae55cea2e1e6ff1e89f424472eda0ef (patch)
treea16a7761a932b720fc4c9d5c2c9a02c07c652518
parent548f2b1b2696ecb581c79688a130d7f9cd4d1e28 (diff)
downloadnix-c381997c0ae55cea2e1e6ff1e89f424472eda0ef.zip
linux: Add splice(2), tee(2), vmsplice(2)
-rw-r--r--src/fcntl.rs42
-rw-r--r--test/test.rs1
-rw-r--r--test/test_fcntl.rs85
3 files changed, 127 insertions, 1 deletions
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;
@@ -182,8 +187,43 @@ pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
}
#[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 {
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();
+ }
+
+}