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