1//! Vectored I/O 2 3use crate::errno::Errno; 4use crate::Result; 5use libc::{self, c_int, c_void, off_t, size_t}; 6use std::io::{IoSlice, IoSliceMut}; 7use std::marker::PhantomData; 8use std::os::unix::io::RawFd; 9 10/// Low-level vectored write to a raw file descriptor 11/// 12/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html) 13pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize> { 14 // SAFETY: to quote the documentation for `IoSlice`: 15 // 16 // [IoSlice] is semantically a wrapper around a &[u8], but is 17 // guaranteed to be ABI compatible with the iovec type on Unix 18 // platforms. 19 // 20 // Because it is ABI compatible, a pointer cast here is valid 21 let res = unsafe { 22 libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) 23 }; 24 25 Errno::result(res).map(|r| r as usize) 26} 27 28/// Low-level vectored read from a raw file descriptor 29/// 30/// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html) 31pub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> { 32 // SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec 33 let res = unsafe { 34 libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int) 35 }; 36 37 Errno::result(res).map(|r| r as usize) 38} 39 40/// Write to `fd` at `offset` from buffers in `iov`. 41/// 42/// Buffers in `iov` will be written in order until all buffers have been written 43/// or an error occurs. The file offset is not changed. 44/// 45/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html) 46#[cfg(not(any(target_os = "redox", target_os = "haiku")))] 47#[cfg_attr(docsrs, doc(cfg(all())))] 48pub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize> { 49 #[cfg(target_env = "uclibc")] 50 let offset = offset as libc::off64_t; // uclibc doesn't use off_t 51 52 // SAFETY: same as in writev() 53 let res = unsafe { 54 libc::pwritev( 55 fd, 56 iov.as_ptr() as *const libc::iovec, 57 iov.len() as c_int, 58 offset, 59 ) 60 }; 61 62 Errno::result(res).map(|r| r as usize) 63} 64 65/// Read from `fd` at `offset` filling buffers in `iov`. 66/// 67/// Buffers in `iov` will be filled in order until all buffers have been filled, 68/// no more bytes are available, or an error occurs. The file offset is not 69/// changed. 70/// 71/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html) 72#[cfg(not(any(target_os = "redox", target_os = "haiku")))] 73#[cfg_attr(docsrs, doc(cfg(all())))] 74pub fn preadv( 75 fd: RawFd, 76 iov: &mut [IoSliceMut<'_>], 77 offset: off_t, 78) -> Result<usize> { 79 #[cfg(target_env = "uclibc")] 80 let offset = offset as libc::off64_t; // uclibc doesn't use off_t 81 82 // SAFETY: same as in readv() 83 let res = unsafe { 84 libc::preadv( 85 fd, 86 iov.as_ptr() as *const libc::iovec, 87 iov.len() as c_int, 88 offset, 89 ) 90 }; 91 92 Errno::result(res).map(|r| r as usize) 93} 94 95/// Low-level write to a file, with specified offset. 96/// 97/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html) 98// TODO: move to unistd 99pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> { 100 let res = unsafe { 101 libc::pwrite( 102 fd, 103 buf.as_ptr() as *const c_void, 104 buf.len() as size_t, 105 offset, 106 ) 107 }; 108 109 Errno::result(res).map(|r| r as usize) 110} 111 112/// Low-level read from a file, with specified offset. 113/// 114/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html) 115// TODO: move to unistd 116pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize> { 117 let res = unsafe { 118 libc::pread( 119 fd, 120 buf.as_mut_ptr() as *mut c_void, 121 buf.len() as size_t, 122 offset, 123 ) 124 }; 125 126 Errno::result(res).map(|r| r as usize) 127} 128 129/// A slice of memory in a remote process, starting at address `base` 130/// and consisting of `len` bytes. 131/// 132/// This is the same underlying C structure as `IoSlice`, 133/// except that it refers to memory in some other process, and is 134/// therefore not represented in Rust by an actual slice as `IoSlice` is. It 135/// is used with [`process_vm_readv`](fn.process_vm_readv.html) 136/// and [`process_vm_writev`](fn.process_vm_writev.html). 137#[cfg(any(target_os = "linux", target_os = "android"))] 138#[cfg_attr(docsrs, doc(cfg(all())))] 139#[repr(C)] 140#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 141pub struct RemoteIoVec { 142 /// The starting address of this slice (`iov_base`). 143 pub base: usize, 144 /// The number of bytes in this slice (`iov_len`). 145 pub len: usize, 146} 147 148/// A vector of buffers. 149/// 150/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for 151/// both reading and writing. Each `IoVec` specifies the base address and 152/// length of an area in memory. 153#[deprecated( 154 since = "0.24.0", 155 note = "`IoVec` is no longer used in the public interface, use `IoSlice` or `IoSliceMut` instead" 156)] 157#[repr(transparent)] 158#[allow(renamed_and_removed_lints)] 159#[allow(clippy::unknown_clippy_lints)] 160// Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/8867 161#[allow(clippy::derive_partial_eq_without_eq)] 162#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 163pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>); 164 165#[allow(deprecated)] 166impl<T> IoVec<T> { 167 /// View the `IoVec` as a Rust slice. 168 #[deprecated( 169 since = "0.24.0", 170 note = "Use the `Deref` impl of `IoSlice` or `IoSliceMut` instead" 171 )] 172 #[inline] 173 pub fn as_slice(&self) -> &[u8] { 174 use std::slice; 175 176 unsafe { 177 slice::from_raw_parts(self.0.iov_base as *const u8, self.0.iov_len) 178 } 179 } 180} 181 182#[allow(deprecated)] 183impl<'a> IoVec<&'a [u8]> { 184 /// Create an `IoVec` from a Rust slice. 185 #[deprecated(since = "0.24.0", note = "Use `IoSlice::new` instead")] 186 pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> { 187 IoVec( 188 libc::iovec { 189 iov_base: buf.as_ptr() as *mut c_void, 190 iov_len: buf.len() as size_t, 191 }, 192 PhantomData, 193 ) 194 } 195} 196 197#[allow(deprecated)] 198impl<'a> IoVec<&'a mut [u8]> { 199 /// Create an `IoVec` from a mutable Rust slice. 200 #[deprecated(since = "0.24.0", note = "Use `IoSliceMut::new` instead")] 201 pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> { 202 IoVec( 203 libc::iovec { 204 iov_base: buf.as_ptr() as *mut c_void, 205 iov_len: buf.len() as size_t, 206 }, 207 PhantomData, 208 ) 209 } 210} 211 212// The only reason IoVec isn't automatically Send+Sync is because libc::iovec 213// contains raw pointers. 214#[allow(deprecated)] 215unsafe impl<T> Send for IoVec<T> where T: Send {} 216#[allow(deprecated)] 217unsafe impl<T> Sync for IoVec<T> where T: Sync {} 218 219feature! { 220#![feature = "process"] 221 222/// Write data directly to another process's virtual memory 223/// (see [`process_vm_writev`(2)]). 224/// 225/// `local_iov` is a list of [`IoSlice`]s containing the data to be written, 226/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the 227/// data should be written in the target process. On success, returns the 228/// number of bytes written, which will always be a whole 229/// number of `remote_iov` chunks. 230/// 231/// This requires the same permissions as debugging the process using 232/// [ptrace]: you must either be a privileged process (with 233/// `CAP_SYS_PTRACE`), or you must be running as the same user as the 234/// target process and the OS must have unprivileged debugging enabled. 235/// 236/// This function is only available on Linux and Android(SDK23+). 237/// 238/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html 239/// [ptrace]: ../ptrace/index.html 240/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html 241/// [`RemoteIoVec`]: struct.RemoteIoVec.html 242#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))] 243pub fn process_vm_writev( 244 pid: crate::unistd::Pid, 245 local_iov: &[IoSlice<'_>], 246 remote_iov: &[RemoteIoVec]) -> Result<usize> 247{ 248 let res = unsafe { 249 libc::process_vm_writev(pid.into(), 250 local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong, 251 remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0) 252 }; 253 254 Errno::result(res).map(|r| r as usize) 255} 256 257/// Read data directly from another process's virtual memory 258/// (see [`process_vm_readv`(2)]). 259/// 260/// `local_iov` is a list of [`IoSliceMut`]s containing the buffer to copy 261/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying 262/// where the source data is in the target process. On success, 263/// returns the number of bytes written, which will always be a whole 264/// number of `remote_iov` chunks. 265/// 266/// This requires the same permissions as debugging the process using 267/// [`ptrace`]: you must either be a privileged process (with 268/// `CAP_SYS_PTRACE`), or you must be running as the same user as the 269/// target process and the OS must have unprivileged debugging enabled. 270/// 271/// This function is only available on Linux and Android(SDK23+). 272/// 273/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html 274/// [`ptrace`]: ../ptrace/index.html 275/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html 276/// [`RemoteIoVec`]: struct.RemoteIoVec.html 277#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))] 278pub fn process_vm_readv( 279 pid: crate::unistd::Pid, 280 local_iov: &mut [IoSliceMut<'_>], 281 remote_iov: &[RemoteIoVec]) -> Result<usize> 282{ 283 let res = unsafe { 284 libc::process_vm_readv(pid.into(), 285 local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong, 286 remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0) 287 }; 288 289 Errno::result(res).map(|r| r as usize) 290} 291} 292