| 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 | |