Skip to content

Commit 7fa7206

Browse files
committed
Auto merge of #267 - kamalmarhubi:linux-splice-etc, r=arcnmx
linux: Add splice(2), tee(2), vmsplice(2)
2 parents 548f2b1 + c381997 commit 7fa7206

File tree

3 files changed

+127
-1
lines changed

3 files changed

+127
-1
lines changed

src/fcntl.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ use libc::{c_int, c_uint};
33
use sys::stat::Mode;
44
use std::os::unix::io::RawFd;
55

6+
#[cfg(any(target_os = "linux", target_os = "android"))]
7+
use sys::uio::IoVec; // For vmsplice
8+
#[cfg(any(target_os = "linux", target_os = "android"))]
9+
use libc;
10+
611
pub use self::consts::*;
712
pub use self::ffi::flock;
813

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

189+
#[cfg(any(target_os = "linux", target_os = "android"))]
190+
pub fn splice(fd_in: RawFd, off_in: Option<&mut libc::loff_t>,
191+
fd_out: RawFd, off_out: Option<&mut libc::loff_t>,
192+
len: usize, flags: SpliceFFlags) -> Result<usize> {
193+
use std::ptr;
194+
let off_in = off_in.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
195+
let off_out = off_out.map(|offset| offset as *mut _).unwrap_or(ptr::null_mut());
196+
197+
let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
198+
Errno::result(ret).map(|r| r as usize)
199+
}
200+
201+
#[cfg(any(target_os = "linux", target_os = "android"))]
202+
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
203+
let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
204+
Errno::result(ret).map(|r| r as usize)
205+
}
206+
207+
#[cfg(any(target_os = "linux", target_os = "android"))]
208+
pub fn vmsplice(fd: RawFd, iov: &[IoVec<&[u8]>], flags: SpliceFFlags) -> Result<usize> {
209+
let ret = unsafe {
210+
libc::vmsplice(fd, iov.as_ptr() as *const libc::iovec, iov.len(), flags.bits())
211+
};
212+
Errno::result(ret).map(|r| r as usize)
213+
}
214+
184215
#[cfg(any(target_os = "linux", target_os = "android"))]
185216
mod consts {
186-
use libc::c_int;
217+
use libc::{self, c_int, c_uint};
218+
219+
bitflags! {
220+
flags SpliceFFlags: c_uint {
221+
const SPLICE_F_MOVE = libc::SPLICE_F_MOVE,
222+
const SPLICE_F_NONBLOCK = libc::SPLICE_F_NONBLOCK,
223+
const SPLICE_F_MORE = libc::SPLICE_F_MORE,
224+
const SPLICE_F_GIFT = libc::SPLICE_F_GIFT,
225+
}
226+
}
187227

188228
bitflags!(
189229
flags OFlag: c_int {

test/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extern crate tempfile;
88
extern crate nix_test as nixtest;
99

1010
mod sys;
11+
mod test_fcntl;
1112
mod test_net;
1213
mod test_nix_path;
1314
#[cfg(any(target_os = "linux", target_os = "android"))]

test/test_fcntl.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#[cfg(any(target_os = "linux", target_os = "android"))]
2+
mod linux_android {
3+
use std::io::prelude::*;
4+
use std::os::unix::prelude::*;
5+
6+
use libc::loff_t;
7+
8+
use nix::fcntl::{SpliceFFlags, splice, tee, vmsplice};
9+
use nix::sys::uio::IoVec;
10+
use nix::unistd::{close, pipe, read, write};
11+
12+
use tempfile::tempfile;
13+
14+
#[test]
15+
fn test_splice() {
16+
const CONTENTS: &'static [u8] = b"abcdef123456";
17+
let mut tmp = tempfile().unwrap();
18+
tmp.write(CONTENTS).unwrap();
19+
20+
let (rd, wr) = pipe().unwrap();
21+
let mut offset: loff_t = 5;
22+
let res = splice(tmp.as_raw_fd(), Some(&mut offset),
23+
wr, None, 2, SpliceFFlags::empty()).unwrap();
24+
25+
assert_eq!(2, res);
26+
27+
let mut buf = [0u8; 1024];
28+
assert_eq!(2, read(rd, &mut buf).unwrap());
29+
assert_eq!(b"f1", &buf[0..2]);
30+
assert_eq!(7, offset);
31+
32+
close(rd).unwrap();
33+
close(wr).unwrap();
34+
}
35+
36+
#[test]
37+
fn test_tee() {
38+
let (rd1, wr1) = pipe().unwrap();
39+
let (rd2, wr2) = pipe().unwrap();
40+
41+
write(wr1, b"abc").unwrap();
42+
let res = tee(rd1, wr2, 2, SpliceFFlags::empty()).unwrap();
43+
44+
assert_eq!(2, res);
45+
46+
let mut buf = [0u8; 1024];
47+
48+
// Check the tee'd bytes are at rd2.
49+
assert_eq!(2, read(rd2, &mut buf).unwrap());
50+
assert_eq!(b"ab", &buf[0..2]);
51+
52+
// Check all the bytes are still at rd1.
53+
assert_eq!(3, read(rd1, &mut buf).unwrap());
54+
assert_eq!(b"abc", &buf[0..3]);
55+
56+
close(rd1).unwrap();
57+
close(wr1).unwrap();
58+
close(rd2).unwrap();
59+
close(wr2).unwrap();
60+
}
61+
62+
#[test]
63+
fn test_vmsplice() {
64+
let (rd, wr) = pipe().unwrap();
65+
66+
let buf1 = b"abcdef";
67+
let buf2 = b"defghi";
68+
let mut iovecs = Vec::with_capacity(2);
69+
iovecs.push(IoVec::from_slice(&buf1[0..3]));
70+
iovecs.push(IoVec::from_slice(&buf2[0..3]));
71+
72+
let res = vmsplice(wr, &iovecs[..], SpliceFFlags::empty()).unwrap();
73+
74+
assert_eq!(6, res);
75+
76+
// Check the bytes can be read at rd.
77+
let mut buf = [0u8; 32];
78+
assert_eq!(6, read(rd, &mut buf).unwrap());
79+
assert_eq!(b"abcdef", &buf[0..6]);
80+
81+
close(rd).unwrap();
82+
close(wr).unwrap();
83+
}
84+
85+
}

0 commit comments

Comments
 (0)