13da5c369Sopenharmony_ci//! Vectored I/O
23da5c369Sopenharmony_ci
33da5c369Sopenharmony_ciuse crate::errno::Errno;
43da5c369Sopenharmony_ciuse crate::Result;
53da5c369Sopenharmony_ciuse libc::{self, c_int, c_void, off_t, size_t};
63da5c369Sopenharmony_ciuse std::io::{IoSlice, IoSliceMut};
73da5c369Sopenharmony_ciuse std::marker::PhantomData;
83da5c369Sopenharmony_ciuse std::os::unix::io::RawFd;
93da5c369Sopenharmony_ci
103da5c369Sopenharmony_ci/// Low-level vectored write to a raw file descriptor
113da5c369Sopenharmony_ci///
123da5c369Sopenharmony_ci/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html)
133da5c369Sopenharmony_cipub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize> {
143da5c369Sopenharmony_ci    // SAFETY: to quote the documentation for `IoSlice`:
153da5c369Sopenharmony_ci    //
163da5c369Sopenharmony_ci    // [IoSlice] is semantically a wrapper around a &[u8], but is
173da5c369Sopenharmony_ci    // guaranteed to be ABI compatible with the iovec type on Unix
183da5c369Sopenharmony_ci    // platforms.
193da5c369Sopenharmony_ci    //
203da5c369Sopenharmony_ci    // Because it is ABI compatible, a pointer cast here is valid
213da5c369Sopenharmony_ci    let res = unsafe {
223da5c369Sopenharmony_ci        libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
233da5c369Sopenharmony_ci    };
243da5c369Sopenharmony_ci
253da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
263da5c369Sopenharmony_ci}
273da5c369Sopenharmony_ci
283da5c369Sopenharmony_ci/// Low-level vectored read from a raw file descriptor
293da5c369Sopenharmony_ci///
303da5c369Sopenharmony_ci/// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html)
313da5c369Sopenharmony_cipub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
323da5c369Sopenharmony_ci    // SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
333da5c369Sopenharmony_ci    let res = unsafe {
343da5c369Sopenharmony_ci        libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
353da5c369Sopenharmony_ci    };
363da5c369Sopenharmony_ci
373da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
383da5c369Sopenharmony_ci}
393da5c369Sopenharmony_ci
403da5c369Sopenharmony_ci/// Write to `fd` at `offset` from buffers in `iov`.
413da5c369Sopenharmony_ci///
423da5c369Sopenharmony_ci/// Buffers in `iov` will be written in order until all buffers have been written
433da5c369Sopenharmony_ci/// or an error occurs. The file offset is not changed.
443da5c369Sopenharmony_ci///
453da5c369Sopenharmony_ci/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
463da5c369Sopenharmony_ci#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
473da5c369Sopenharmony_ci#[cfg_attr(docsrs, doc(cfg(all())))]
483da5c369Sopenharmony_cipub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize> {
493da5c369Sopenharmony_ci    #[cfg(target_env = "uclibc")]
503da5c369Sopenharmony_ci    let offset = offset as libc::off64_t; // uclibc doesn't use off_t
513da5c369Sopenharmony_ci
523da5c369Sopenharmony_ci    // SAFETY: same as in writev()
533da5c369Sopenharmony_ci    let res = unsafe {
543da5c369Sopenharmony_ci        libc::pwritev(
553da5c369Sopenharmony_ci            fd,
563da5c369Sopenharmony_ci            iov.as_ptr() as *const libc::iovec,
573da5c369Sopenharmony_ci            iov.len() as c_int,
583da5c369Sopenharmony_ci            offset,
593da5c369Sopenharmony_ci        )
603da5c369Sopenharmony_ci    };
613da5c369Sopenharmony_ci
623da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
633da5c369Sopenharmony_ci}
643da5c369Sopenharmony_ci
653da5c369Sopenharmony_ci/// Read from `fd` at `offset` filling buffers in `iov`.
663da5c369Sopenharmony_ci///
673da5c369Sopenharmony_ci/// Buffers in `iov` will be filled in order until all buffers have been filled,
683da5c369Sopenharmony_ci/// no more bytes are available, or an error occurs. The file offset is not
693da5c369Sopenharmony_ci/// changed.
703da5c369Sopenharmony_ci///
713da5c369Sopenharmony_ci/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
723da5c369Sopenharmony_ci#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
733da5c369Sopenharmony_ci#[cfg_attr(docsrs, doc(cfg(all())))]
743da5c369Sopenharmony_cipub fn preadv(
753da5c369Sopenharmony_ci    fd: RawFd,
763da5c369Sopenharmony_ci    iov: &mut [IoSliceMut<'_>],
773da5c369Sopenharmony_ci    offset: off_t,
783da5c369Sopenharmony_ci) -> Result<usize> {
793da5c369Sopenharmony_ci    #[cfg(target_env = "uclibc")]
803da5c369Sopenharmony_ci    let offset = offset as libc::off64_t; // uclibc doesn't use off_t
813da5c369Sopenharmony_ci
823da5c369Sopenharmony_ci    // SAFETY: same as in readv()
833da5c369Sopenharmony_ci    let res = unsafe {
843da5c369Sopenharmony_ci        libc::preadv(
853da5c369Sopenharmony_ci            fd,
863da5c369Sopenharmony_ci            iov.as_ptr() as *const libc::iovec,
873da5c369Sopenharmony_ci            iov.len() as c_int,
883da5c369Sopenharmony_ci            offset,
893da5c369Sopenharmony_ci        )
903da5c369Sopenharmony_ci    };
913da5c369Sopenharmony_ci
923da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
933da5c369Sopenharmony_ci}
943da5c369Sopenharmony_ci
953da5c369Sopenharmony_ci/// Low-level write to a file, with specified offset.
963da5c369Sopenharmony_ci///
973da5c369Sopenharmony_ci/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html)
983da5c369Sopenharmony_ci// TODO: move to unistd
993da5c369Sopenharmony_cipub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
1003da5c369Sopenharmony_ci    let res = unsafe {
1013da5c369Sopenharmony_ci        libc::pwrite(
1023da5c369Sopenharmony_ci            fd,
1033da5c369Sopenharmony_ci            buf.as_ptr() as *const c_void,
1043da5c369Sopenharmony_ci            buf.len() as size_t,
1053da5c369Sopenharmony_ci            offset,
1063da5c369Sopenharmony_ci        )
1073da5c369Sopenharmony_ci    };
1083da5c369Sopenharmony_ci
1093da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
1103da5c369Sopenharmony_ci}
1113da5c369Sopenharmony_ci
1123da5c369Sopenharmony_ci/// Low-level read from a file, with specified offset.
1133da5c369Sopenharmony_ci///
1143da5c369Sopenharmony_ci/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html)
1153da5c369Sopenharmony_ci// TODO: move to unistd
1163da5c369Sopenharmony_cipub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize> {
1173da5c369Sopenharmony_ci    let res = unsafe {
1183da5c369Sopenharmony_ci        libc::pread(
1193da5c369Sopenharmony_ci            fd,
1203da5c369Sopenharmony_ci            buf.as_mut_ptr() as *mut c_void,
1213da5c369Sopenharmony_ci            buf.len() as size_t,
1223da5c369Sopenharmony_ci            offset,
1233da5c369Sopenharmony_ci        )
1243da5c369Sopenharmony_ci    };
1253da5c369Sopenharmony_ci
1263da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
1273da5c369Sopenharmony_ci}
1283da5c369Sopenharmony_ci
1293da5c369Sopenharmony_ci/// A slice of memory in a remote process, starting at address `base`
1303da5c369Sopenharmony_ci/// and consisting of `len` bytes.
1313da5c369Sopenharmony_ci///
1323da5c369Sopenharmony_ci/// This is the same underlying C structure as `IoSlice`,
1333da5c369Sopenharmony_ci/// except that it refers to memory in some other process, and is
1343da5c369Sopenharmony_ci/// therefore not represented in Rust by an actual slice as `IoSlice` is. It
1353da5c369Sopenharmony_ci/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
1363da5c369Sopenharmony_ci/// and [`process_vm_writev`](fn.process_vm_writev.html).
1373da5c369Sopenharmony_ci#[cfg(any(target_os = "linux", target_os = "android"))]
1383da5c369Sopenharmony_ci#[cfg_attr(docsrs, doc(cfg(all())))]
1393da5c369Sopenharmony_ci#[repr(C)]
1403da5c369Sopenharmony_ci#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1413da5c369Sopenharmony_cipub struct RemoteIoVec {
1423da5c369Sopenharmony_ci    /// The starting address of this slice (`iov_base`).
1433da5c369Sopenharmony_ci    pub base: usize,
1443da5c369Sopenharmony_ci    /// The number of bytes in this slice (`iov_len`).
1453da5c369Sopenharmony_ci    pub len: usize,
1463da5c369Sopenharmony_ci}
1473da5c369Sopenharmony_ci
1483da5c369Sopenharmony_ci/// A vector of buffers.
1493da5c369Sopenharmony_ci///
1503da5c369Sopenharmony_ci/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for
1513da5c369Sopenharmony_ci/// both reading and writing.  Each `IoVec` specifies the base address and
1523da5c369Sopenharmony_ci/// length of an area in memory.
1533da5c369Sopenharmony_ci#[deprecated(
1543da5c369Sopenharmony_ci    since = "0.24.0",
1553da5c369Sopenharmony_ci    note = "`IoVec` is no longer used in the public interface, use `IoSlice` or `IoSliceMut` instead"
1563da5c369Sopenharmony_ci)]
1573da5c369Sopenharmony_ci#[repr(transparent)]
1583da5c369Sopenharmony_ci#[allow(renamed_and_removed_lints)]
1593da5c369Sopenharmony_ci#[allow(clippy::unknown_clippy_lints)]
1603da5c369Sopenharmony_ci// Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/8867
1613da5c369Sopenharmony_ci#[allow(clippy::derive_partial_eq_without_eq)]
1623da5c369Sopenharmony_ci#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1633da5c369Sopenharmony_cipub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
1643da5c369Sopenharmony_ci
1653da5c369Sopenharmony_ci#[allow(deprecated)]
1663da5c369Sopenharmony_ciimpl<T> IoVec<T> {
1673da5c369Sopenharmony_ci    /// View the `IoVec` as a Rust slice.
1683da5c369Sopenharmony_ci    #[deprecated(
1693da5c369Sopenharmony_ci        since = "0.24.0",
1703da5c369Sopenharmony_ci        note = "Use the `Deref` impl of `IoSlice` or `IoSliceMut` instead"
1713da5c369Sopenharmony_ci    )]
1723da5c369Sopenharmony_ci    #[inline]
1733da5c369Sopenharmony_ci    pub fn as_slice(&self) -> &[u8] {
1743da5c369Sopenharmony_ci        use std::slice;
1753da5c369Sopenharmony_ci
1763da5c369Sopenharmony_ci        unsafe {
1773da5c369Sopenharmony_ci            slice::from_raw_parts(self.0.iov_base as *const u8, self.0.iov_len)
1783da5c369Sopenharmony_ci        }
1793da5c369Sopenharmony_ci    }
1803da5c369Sopenharmony_ci}
1813da5c369Sopenharmony_ci
1823da5c369Sopenharmony_ci#[allow(deprecated)]
1833da5c369Sopenharmony_ciimpl<'a> IoVec<&'a [u8]> {
1843da5c369Sopenharmony_ci    /// Create an `IoVec` from a Rust slice.
1853da5c369Sopenharmony_ci    #[deprecated(since = "0.24.0", note = "Use `IoSlice::new` instead")]
1863da5c369Sopenharmony_ci    pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
1873da5c369Sopenharmony_ci        IoVec(
1883da5c369Sopenharmony_ci            libc::iovec {
1893da5c369Sopenharmony_ci                iov_base: buf.as_ptr() as *mut c_void,
1903da5c369Sopenharmony_ci                iov_len: buf.len() as size_t,
1913da5c369Sopenharmony_ci            },
1923da5c369Sopenharmony_ci            PhantomData,
1933da5c369Sopenharmony_ci        )
1943da5c369Sopenharmony_ci    }
1953da5c369Sopenharmony_ci}
1963da5c369Sopenharmony_ci
1973da5c369Sopenharmony_ci#[allow(deprecated)]
1983da5c369Sopenharmony_ciimpl<'a> IoVec<&'a mut [u8]> {
1993da5c369Sopenharmony_ci    /// Create an `IoVec` from a mutable Rust slice.
2003da5c369Sopenharmony_ci    #[deprecated(since = "0.24.0", note = "Use `IoSliceMut::new` instead")]
2013da5c369Sopenharmony_ci    pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> {
2023da5c369Sopenharmony_ci        IoVec(
2033da5c369Sopenharmony_ci            libc::iovec {
2043da5c369Sopenharmony_ci                iov_base: buf.as_ptr() as *mut c_void,
2053da5c369Sopenharmony_ci                iov_len: buf.len() as size_t,
2063da5c369Sopenharmony_ci            },
2073da5c369Sopenharmony_ci            PhantomData,
2083da5c369Sopenharmony_ci        )
2093da5c369Sopenharmony_ci    }
2103da5c369Sopenharmony_ci}
2113da5c369Sopenharmony_ci
2123da5c369Sopenharmony_ci// The only reason IoVec isn't automatically Send+Sync is because libc::iovec
2133da5c369Sopenharmony_ci// contains raw pointers.
2143da5c369Sopenharmony_ci#[allow(deprecated)]
2153da5c369Sopenharmony_ciunsafe impl<T> Send for IoVec<T> where T: Send {}
2163da5c369Sopenharmony_ci#[allow(deprecated)]
2173da5c369Sopenharmony_ciunsafe impl<T> Sync for IoVec<T> where T: Sync {}
2183da5c369Sopenharmony_ci
2193da5c369Sopenharmony_cifeature! {
2203da5c369Sopenharmony_ci#![feature = "process"]
2213da5c369Sopenharmony_ci
2223da5c369Sopenharmony_ci/// Write data directly to another process's virtual memory
2233da5c369Sopenharmony_ci/// (see [`process_vm_writev`(2)]).
2243da5c369Sopenharmony_ci///
2253da5c369Sopenharmony_ci/// `local_iov` is a list of [`IoSlice`]s containing the data to be written,
2263da5c369Sopenharmony_ci/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the
2273da5c369Sopenharmony_ci/// data should be written in the target process. On success, returns the
2283da5c369Sopenharmony_ci/// number of bytes written, which will always be a whole
2293da5c369Sopenharmony_ci/// number of `remote_iov` chunks.
2303da5c369Sopenharmony_ci///
2313da5c369Sopenharmony_ci/// This requires the same permissions as debugging the process using
2323da5c369Sopenharmony_ci/// [ptrace]: you must either be a privileged process (with
2333da5c369Sopenharmony_ci/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
2343da5c369Sopenharmony_ci/// target process and the OS must have unprivileged debugging enabled.
2353da5c369Sopenharmony_ci///
2363da5c369Sopenharmony_ci/// This function is only available on Linux and Android(SDK23+).
2373da5c369Sopenharmony_ci///
2383da5c369Sopenharmony_ci/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html
2393da5c369Sopenharmony_ci/// [ptrace]: ../ptrace/index.html
2403da5c369Sopenharmony_ci/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html
2413da5c369Sopenharmony_ci/// [`RemoteIoVec`]: struct.RemoteIoVec.html
2423da5c369Sopenharmony_ci#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
2433da5c369Sopenharmony_cipub fn process_vm_writev(
2443da5c369Sopenharmony_ci    pid: crate::unistd::Pid,
2453da5c369Sopenharmony_ci    local_iov: &[IoSlice<'_>],
2463da5c369Sopenharmony_ci    remote_iov: &[RemoteIoVec]) -> Result<usize>
2473da5c369Sopenharmony_ci{
2483da5c369Sopenharmony_ci    let res = unsafe {
2493da5c369Sopenharmony_ci        libc::process_vm_writev(pid.into(),
2503da5c369Sopenharmony_ci                                local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
2513da5c369Sopenharmony_ci                                remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
2523da5c369Sopenharmony_ci    };
2533da5c369Sopenharmony_ci
2543da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
2553da5c369Sopenharmony_ci}
2563da5c369Sopenharmony_ci
2573da5c369Sopenharmony_ci/// Read data directly from another process's virtual memory
2583da5c369Sopenharmony_ci/// (see [`process_vm_readv`(2)]).
2593da5c369Sopenharmony_ci///
2603da5c369Sopenharmony_ci/// `local_iov` is a list of [`IoSliceMut`]s containing the buffer to copy
2613da5c369Sopenharmony_ci/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying
2623da5c369Sopenharmony_ci/// where the source data is in the target process. On success,
2633da5c369Sopenharmony_ci/// returns the number of bytes written, which will always be a whole
2643da5c369Sopenharmony_ci/// number of `remote_iov` chunks.
2653da5c369Sopenharmony_ci///
2663da5c369Sopenharmony_ci/// This requires the same permissions as debugging the process using
2673da5c369Sopenharmony_ci/// [`ptrace`]: you must either be a privileged process (with
2683da5c369Sopenharmony_ci/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
2693da5c369Sopenharmony_ci/// target process and the OS must have unprivileged debugging enabled.
2703da5c369Sopenharmony_ci///
2713da5c369Sopenharmony_ci/// This function is only available on Linux and Android(SDK23+).
2723da5c369Sopenharmony_ci///
2733da5c369Sopenharmony_ci/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html
2743da5c369Sopenharmony_ci/// [`ptrace`]: ../ptrace/index.html
2753da5c369Sopenharmony_ci/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html
2763da5c369Sopenharmony_ci/// [`RemoteIoVec`]: struct.RemoteIoVec.html
2773da5c369Sopenharmony_ci#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
2783da5c369Sopenharmony_cipub fn process_vm_readv(
2793da5c369Sopenharmony_ci    pid: crate::unistd::Pid,
2803da5c369Sopenharmony_ci    local_iov: &mut [IoSliceMut<'_>],
2813da5c369Sopenharmony_ci    remote_iov: &[RemoteIoVec]) -> Result<usize>
2823da5c369Sopenharmony_ci{
2833da5c369Sopenharmony_ci    let res = unsafe {
2843da5c369Sopenharmony_ci        libc::process_vm_readv(pid.into(),
2853da5c369Sopenharmony_ci                               local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
2863da5c369Sopenharmony_ci                               remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
2873da5c369Sopenharmony_ci    };
2883da5c369Sopenharmony_ci
2893da5c369Sopenharmony_ci    Errno::result(res).map(|r| r as usize)
2903da5c369Sopenharmony_ci}
2913da5c369Sopenharmony_ci}
292