1use nix::sys::uio::*;
2use nix::unistd::*;
3use rand::distributions::Alphanumeric;
4use rand::{thread_rng, Rng};
5use std::fs::OpenOptions;
6use std::io::IoSlice;
7use std::os::unix::io::{FromRawFd, OwnedFd};
8use std::{cmp, iter};
9
10#[cfg(not(target_os = "redox"))]
11use std::io::IoSliceMut;
12
13use tempfile::tempdir;
14#[cfg(not(target_os = "redox"))]
15use tempfile::tempfile;
16
17#[test]
18fn test_writev() {
19 let mut to_write = Vec::with_capacity(16 * 128);
20 for _ in 0..16 {
21 let s: String = thread_rng()
22 .sample_iter(&Alphanumeric)
23 .map(char::from)
24 .take(128)
25 .collect();
26 let b = s.as_bytes();
27 to_write.extend(b.iter().cloned());
28 }
29 // Allocate and fill iovecs
30 let mut iovecs = Vec::new();
31 let mut consumed = 0;
32 while consumed < to_write.len() {
33 let left = to_write.len() - consumed;
34 let slice_len = if left <= 64 {
35 left
36 } else {
37 thread_rng().gen_range(64..cmp::min(256, left))
38 };
39 let b = &to_write[consumed..consumed + slice_len];
40 iovecs.push(IoSlice::new(b));
41 consumed += slice_len;
42 }
43 let (reader, writer) = pipe().expect("Couldn't create pipe");
44 // FileDesc will close its filedesc (reader).
45 let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
46
47 // Temporary workaround to cope with the existing RawFd pipe(2), should be
48 // removed when pipe(2) becomes I/O-safe.
49 let writer = unsafe { OwnedFd::from_raw_fd(writer) };
50
51 // Blocking io, should write all data.
52 let write_res = writev(&writer, &iovecs);
53 let written = write_res.expect("couldn't write");
54 // Check whether we written all data
55 assert_eq!(to_write.len(), written);
56 let read_res = read(reader, &mut read_buf[..]);
57 let read = read_res.expect("couldn't read");
58 // Check we have read as much as we written
59 assert_eq!(read, written);
60 // Check equality of written and read data
61 assert_eq!(&to_write, &read_buf);
62 close(reader).expect("closed reader");
63}
64
65#[test]
66#[cfg(not(target_os = "redox"))]
67fn test_readv() {
68 let s: String = thread_rng()
69 .sample_iter(&Alphanumeric)
70 .map(char::from)
71 .take(128)
72 .collect();
73 let to_write = s.as_bytes().to_vec();
74 let mut storage = Vec::new();
75 let mut allocated = 0;
76 while allocated < to_write.len() {
77 let left = to_write.len() - allocated;
78 let vec_len = if left <= 64 {
79 left
80 } else {
81 thread_rng().gen_range(64..cmp::min(256, left))
82 };
83 let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
84 storage.push(v);
85 allocated += vec_len;
86 }
87 let mut iovecs = Vec::with_capacity(storage.len());
88 for v in &mut storage {
89 iovecs.push(IoSliceMut::new(&mut v[..]));
90 }
91 let (reader, writer) = pipe().expect("couldn't create pipe");
92 // Blocking io, should write all data.
93 write(writer, &to_write).expect("write failed");
94
95 // Temporary workaround to cope with the existing RawFd pipe(2), should be
96 // removed when pipe(2) becomes I/O-safe.
97 let reader = unsafe { OwnedFd::from_raw_fd(reader) };
98
99 let read = readv(&reader, &mut iovecs[..]).expect("read failed");
100 // Check whether we've read all data
101 assert_eq!(to_write.len(), read);
102 // Cccumulate data from iovecs
103 let mut read_buf = Vec::with_capacity(to_write.len());
104 for iovec in &iovecs {
105 read_buf.extend(iovec.iter().cloned());
106 }
107 // Check whether iovecs contain all written data
108 assert_eq!(read_buf.len(), to_write.len());
109 // Check equality of written and read data
110 assert_eq!(&read_buf, &to_write);
111 close(writer).expect("couldn't close writer");
112}
113
114#[test]
115#[cfg(not(target_os = "redox"))]
116fn test_pwrite() {
117 use std::io::Read;
118
119 let mut file = tempfile().unwrap();
120 let buf = [1u8; 8];
121 assert_eq!(Ok(8), pwrite(&file, &buf, 8));
122 let mut file_content = Vec::new();
123 file.read_to_end(&mut file_content).unwrap();
124 let mut expected = vec![0u8; 8];
125 expected.extend(vec![1; 8]);
126 assert_eq!(file_content, expected);
127}
128
129#[test]
130fn test_pread() {
131 use std::io::Write;
132
133 let tempdir = tempdir().unwrap();
134
135 let path = tempdir.path().join("pread_test_file");
136 let mut file = OpenOptions::new()
137 .write(true)
138 .read(true)
139 .create(true)
140 .truncate(true)
141 .open(path)
142 .unwrap();
143 let file_content: Vec<u8> = (0..64).collect();
144 file.write_all(&file_content).unwrap();
145
146 let mut buf = [0u8; 16];
147 assert_eq!(Ok(16), pread(&file, &mut buf, 16));
148 let expected: Vec<_> = (16..32).collect();
149 assert_eq!(&buf[..], &expected[..]);
150}
151
152#[test]
153#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
154fn test_pwritev() {
155 use std::io::Read;
156
157 let to_write: Vec<u8> = (0..128).collect();
158 let expected: Vec<u8> = [vec![0; 100], to_write.clone()].concat();
159
160 let iovecs = [
161 IoSlice::new(&to_write[0..17]),
162 IoSlice::new(&to_write[17..64]),
163 IoSlice::new(&to_write[64..128]),
164 ];
165
166 let tempdir = tempdir().unwrap();
167
168 // pwritev them into a temporary file
169 let path = tempdir.path().join("pwritev_test_file");
170 let mut file = OpenOptions::new()
171 .write(true)
172 .read(true)
173 .create(true)
174 .truncate(true)
175 .open(path)
176 .unwrap();
177
178 let written = pwritev(&file, &iovecs, 100).ok().unwrap();
179 assert_eq!(written, to_write.len());
180
181 // Read the data back and make sure it matches
182 let mut contents = Vec::new();
183 file.read_to_end(&mut contents).unwrap();
184 assert_eq!(contents, expected);
185}
186
187#[test]
188#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
189fn test_preadv() {
190 use std::io::Write;
191
192 let to_write: Vec<u8> = (0..200).collect();
193 let expected: Vec<u8> = (100..200).collect();
194
195 let tempdir = tempdir().unwrap();
196
197 let path = tempdir.path().join("preadv_test_file");
198
199 let mut file = OpenOptions::new()
200 .read(true)
201 .write(true)
202 .create(true)
203 .truncate(true)
204 .open(path)
205 .unwrap();
206 file.write_all(&to_write).unwrap();
207
208 let mut buffers: Vec<Vec<u8>> = vec![vec![0; 24], vec![0; 1], vec![0; 75]];
209
210 {
211 // Borrow the buffers into IoVecs and preadv into them
212 let mut iovecs: Vec<_> = buffers
213 .iter_mut()
214 .map(|buf| IoSliceMut::new(&mut buf[..]))
215 .collect();
216 assert_eq!(Ok(100), preadv(&file, &mut iovecs, 100));
217 }
218
219 let all = buffers.concat();
220 assert_eq!(all, expected);
221}
222
223#[test]
224#[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
225// uclibc doesn't implement process_vm_readv
226// qemu-user doesn't implement process_vm_readv/writev on most arches
227#[cfg_attr(qemu, ignore)]
228fn test_process_vm_readv() {
229 use crate::*;
230 use nix::sys::signal::*;
231 use nix::sys::wait::*;
232 use nix::unistd::ForkResult::*;
233
234 require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
235 let _m = crate::FORK_MTX.lock();
236
237 // Pre-allocate memory in the child, since allocation isn't safe
238 // post-fork (~= async-signal-safe)
239 let mut vector = vec![1u8, 2, 3, 4, 5];
240
241 let (r, w) = pipe().unwrap();
242 match unsafe { fork() }.expect("Error: Fork Failed") {
243 Parent { child } => {
244 close(w).unwrap();
245 // wait for child
246 read(r, &mut [0u8]).unwrap();
247 close(r).unwrap();
248
249 let ptr = vector.as_ptr() as usize;
250 let remote_iov = RemoteIoVec { base: ptr, len: 5 };
251 let mut buf = vec![0u8; 5];
252
253 let ret = process_vm_readv(
254 child,
255 &mut [IoSliceMut::new(&mut buf)],
256 &[remote_iov],
257 );
258
259 kill(child, SIGTERM).unwrap();
260 waitpid(child, None).unwrap();
261
262 assert_eq!(Ok(5), ret);
263 assert_eq!(20u8, buf.iter().sum());
264 }
265 Child => {
266 let _ = close(r);
267 for i in &mut vector {
268 *i += 1;
269 }
270 let _ = write(w, b"\0");
271 let _ = close(w);
272 loop {
273 pause();
274 }
275 }
276 }
277}
278