blob: 0f4b8a65680cc01205b7a991d080e3796f37f061 [file] [log] [blame]
use nix::sys::uio::*;
use nix::unistd::*;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use std::fs::OpenOptions;
use std::io::IoSlice;
use std::os::unix::io::AsRawFd;
use std::{cmp, iter};
#[cfg(not(target_os = "redox"))]
use std::io::IoSliceMut;
use tempfile::tempdir;
#[cfg(not(target_os = "redox"))]
use tempfile::tempfile;
#[test]
fn test_writev() {
let mut to_write = Vec::with_capacity(16 * 128);
for _ in 0..16 {
let s: String = thread_rng()
.sample_iter(&Alphanumeric)
.map(char::from)
.take(128)
.collect();
let b = s.as_bytes();
to_write.extend(b.iter().cloned());
}
// Allocate and fill iovecs
let mut iovecs = Vec::new();
let mut consumed = 0;
while consumed < to_write.len() {
let left = to_write.len() - consumed;
let slice_len = if left <= 64 {
left
} else {
thread_rng().gen_range(64..cmp::min(256, left))
};
let b = &to_write[consumed..consumed + slice_len];
iovecs.push(IoSlice::new(b));
consumed += slice_len;
}
let pipe_res = pipe();
let (reader, writer) = pipe_res.expect("Couldn't create pipe");
// FileDesc will close its filedesc (reader).
let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
// Blocking io, should write all data.
let write_res = writev(writer, &iovecs);
let written = write_res.expect("couldn't write");
// Check whether we written all data
assert_eq!(to_write.len(), written);
let read_res = read(reader, &mut read_buf[..]);
let read = read_res.expect("couldn't read");
// Check we have read as much as we written
assert_eq!(read, written);
// Check equality of written and read data
assert_eq!(&to_write, &read_buf);
close(writer).expect("closed writer");
close(reader).expect("closed reader");
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_readv() {
let s: String = thread_rng()
.sample_iter(&Alphanumeric)
.map(char::from)
.take(128)
.collect();
let to_write = s.as_bytes().to_vec();
let mut storage = Vec::new();
let mut allocated = 0;
while allocated < to_write.len() {
let left = to_write.len() - allocated;
let vec_len = if left <= 64 {
left
} else {
thread_rng().gen_range(64..cmp::min(256, left))
};
let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
storage.push(v);
allocated += vec_len;
}
let mut iovecs = Vec::with_capacity(storage.len());
for v in &mut storage {
iovecs.push(IoSliceMut::new(&mut v[..]));
}
let (reader, writer) = pipe().expect("couldn't create pipe");
// Blocking io, should write all data.
write(writer, &to_write).expect("write failed");
let read = readv(reader, &mut iovecs[..]).expect("read failed");
// Check whether we've read all data
assert_eq!(to_write.len(), read);
// Cccumulate data from iovecs
let mut read_buf = Vec::with_capacity(to_write.len());
for iovec in &iovecs {
read_buf.extend(iovec.iter().cloned());
}
// Check whether iovecs contain all written data
assert_eq!(read_buf.len(), to_write.len());
// Check equality of written and read data
assert_eq!(&read_buf, &to_write);
close(reader).expect("couldn't close reader");
close(writer).expect("couldn't close writer");
}
#[test]
#[cfg(not(target_os = "redox"))]
fn test_pwrite() {
use std::io::Read;
let mut file = tempfile().unwrap();
let buf = [1u8; 8];
assert_eq!(Ok(8), pwrite(file.as_raw_fd(), &buf, 8));
let mut file_content = Vec::new();
file.read_to_end(&mut file_content).unwrap();
let mut expected = vec![0u8; 8];
expected.extend(vec![1; 8]);
assert_eq!(file_content, expected);
}
#[test]
fn test_pread() {
use std::io::Write;
let tempdir = tempdir().unwrap();
let path = tempdir.path().join("pread_test_file");
let mut file = OpenOptions::new()
.write(true)
.read(true)
.create(true)
.truncate(true)
.open(path)
.unwrap();
let file_content: Vec<u8> = (0..64).collect();
file.write_all(&file_content).unwrap();
let mut buf = [0u8; 16];
assert_eq!(Ok(16), pread(file.as_raw_fd(), &mut buf, 16));
let expected: Vec<_> = (16..32).collect();
assert_eq!(&buf[..], &expected[..]);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_pwritev() {
use std::io::Read;
let to_write: Vec<u8> = (0..128).collect();
let expected: Vec<u8> = [vec![0; 100], to_write.clone()].concat();
let iovecs = [
IoSlice::new(&to_write[0..17]),
IoSlice::new(&to_write[17..64]),
IoSlice::new(&to_write[64..128]),
];
let tempdir = tempdir().unwrap();
// pwritev them into a temporary file
let path = tempdir.path().join("pwritev_test_file");
let mut file = OpenOptions::new()
.write(true)
.read(true)
.create(true)
.truncate(true)
.open(path)
.unwrap();
let written = pwritev(file.as_raw_fd(), &iovecs, 100).ok().unwrap();
assert_eq!(written, to_write.len());
// Read the data back and make sure it matches
let mut contents = Vec::new();
file.read_to_end(&mut contents).unwrap();
assert_eq!(contents, expected);
}
#[test]
#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
fn test_preadv() {
use std::io::Write;
let to_write: Vec<u8> = (0..200).collect();
let expected: Vec<u8> = (100..200).collect();
let tempdir = tempdir().unwrap();
let path = tempdir.path().join("preadv_test_file");
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(path)
.unwrap();
file.write_all(&to_write).unwrap();
let mut buffers: Vec<Vec<u8>> = vec![vec![0; 24], vec![0; 1], vec![0; 75]];
{
// Borrow the buffers into IoVecs and preadv into them
let mut iovecs: Vec<_> = buffers
.iter_mut()
.map(|buf| IoSliceMut::new(&mut buf[..]))
.collect();
assert_eq!(Ok(100), preadv(file.as_raw_fd(), &mut iovecs, 100));
}
let all = buffers.concat();
assert_eq!(all, expected);
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
// uclibc doesn't implement process_vm_readv
// qemu-user doesn't implement process_vm_readv/writev on most arches
#[cfg_attr(qemu, ignore)]
fn test_process_vm_readv() {
use crate::*;
use nix::sys::signal::*;
use nix::sys::wait::*;
use nix::unistd::ForkResult::*;
require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
let _m = crate::FORK_MTX.lock();
// Pre-allocate memory in the child, since allocation isn't safe
// post-fork (~= async-signal-safe)
let mut vector = vec![1u8, 2, 3, 4, 5];
let (r, w) = pipe().unwrap();
match unsafe { fork() }.expect("Error: Fork Failed") {
Parent { child } => {
close(w).unwrap();
// wait for child
read(r, &mut [0u8]).unwrap();
close(r).unwrap();
let ptr = vector.as_ptr() as usize;
let remote_iov = RemoteIoVec { base: ptr, len: 5 };
let mut buf = vec![0u8; 5];
let ret = process_vm_readv(
child,
&mut [IoSliceMut::new(&mut buf)],
&[remote_iov],
);
kill(child, SIGTERM).unwrap();
waitpid(child, None).unwrap();
assert_eq!(Ok(5), ret);
assert_eq!(20u8, buf.iter().sum());
}
Child => {
let _ = close(r);
for i in &mut vector {
*i += 1;
}
let _ = write(w, b"\0");
let _ = close(w);
loop {
pause();
}
}
}
}