• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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)
writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize>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 = 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)
readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result<usize>31 pub 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())))]
pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize>48 pub 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())))]
preadv( fd: RawFd, iov: &mut [IoSliceMut<'_>], offset: off_t, ) -> Result<usize>74 pub 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
pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize>99 pub 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
pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>116 pub 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)]
141 pub 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)]
163 pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
164 
165 #[allow(deprecated)]
166 impl<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]
as_slice(&self) -> &[u8]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)]
183 impl<'a> IoVec<&'a [u8]> {
184     /// Create an `IoVec` from a Rust slice.
185     #[deprecated(since = "0.24.0", note = "Use `IoSlice::new` instead")]
from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]>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)]
198 impl<'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")]
from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]>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)]
215 unsafe impl<T> Send for IoVec<T> where T: Send {}
216 #[allow(deprecated)]
217 unsafe impl<T> Sync for IoVec<T> where T: Sync {}
218 
219 feature! {
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")))]
243 pub 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")))]
278 pub 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