1 | use nix::sys::uio::*; |
2 | use nix::unistd::*; |
3 | use rand::distributions::Alphanumeric; |
4 | use rand::{thread_rng, Rng}; |
5 | use std::fs::OpenOptions; |
6 | use std::io::IoSlice; |
7 | use std::os::unix::io::{FromRawFd, OwnedFd}; |
8 | use std::{cmp, iter}; |
9 | |
10 | #[cfg (not(target_os = "redox" ))] |
11 | use std::io::IoSliceMut; |
12 | |
13 | use tempfile::tempdir; |
14 | #[cfg (not(target_os = "redox" ))] |
15 | use tempfile::tempfile; |
16 | |
17 | #[test] |
18 | fn 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" ))] |
67 | fn 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" ))] |
116 | fn 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] |
130 | fn 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" )))] |
154 | fn 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" )))] |
189 | fn 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)] |
228 | fn 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 | |