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