From 5f504ae7202c0198873fc338cbd2f60e755be572 Mon Sep 17 00:00:00 2001 From: Koutheir Attouchi Date: Fri, 31 Jan 2025 15:20:23 -0500 Subject: [PATCH] Refactoring and removing of one dependency. --- CHANGELOG.md | 6 +++ Cargo.toml | 1 - README.md | 2 +- src/errors.rs | 23 ++++++++- src/lib.rs | 119 +++++++++++++++++------------------------- src/tests.rs | 107 +++++++++++++++----------------------- src/utils.rs | 126 +++++++++++++++++++++++++++++++++++++++++++++ src/utils/tests.rs | 6 +++ 8 files changed, 248 insertions(+), 142 deletions(-) create mode 100644 src/utils.rs create mode 100644 src/utils/tests.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2968dcf..37666eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.12] - 2025-01-31 + +### Removed + +- Dependency `lazy_static`. Replaced by a lock-free implementation. + ## [1.0.11] - 2024-09-12 ### Changed diff --git a/Cargo.toml b/Cargo.toml index d75c54e..07b58f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,5 @@ assert_matches = { version = "1" } [dependencies] backtrace = { version = "0.3" } -lazy_static = { version = "1" } libc = { version = "0.2" } smallvec = { version = "1" } diff --git a/README.md b/README.md index ab4189d..05b5245 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![crates.io](https://img.shields.io/crates/v/process_vm_io.svg)](https://crates.io/crates/process_vm_io) [![docs.rs](https://docs.rs/process_vm_io/badge.svg)](https://docs.rs/process_vm_io) [![license](https://img.shields.io/github/license/mdcssw/process_vm_io?color=black)](https://raw.githubusercontent.com/mdcssw/process_vm_io/master/LICENSE.txt) -[![dependency status](https://deps.rs/crate/process_vm_io/1.0.10/status.svg)](https://deps.rs/crate/process_vm_io/1.0.10) +[![dependency status](https://deps.rs/crate/process_vm_io/1.0.10/status.svg)](https://deps.rs/crate/process_vm_io/1.0.12) # I/O access to virtual memory contents of processes diff --git a/src/errors.rs b/src/errors.rs index f307977..6fb5e75 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -22,6 +22,12 @@ pub enum ErrorKind { /// Virtual memory address range contains too many pages. TooManyVMPages, + /// Failed to query the system page size. + UnknownPageSize, + + /// Invalid system page size. + InvalidPageSize(u64), + /// Some [`io::Error`](std::io::Error) occurred. #[non_exhaustive] Io { @@ -87,6 +93,12 @@ impl fmt::Display for Error { ErrorKind::TooManyVMPages => { write!(f, "virtual memory address range contains too many pages") } + ErrorKind::UnknownPageSize => { + write!(f, "failed to query the system page size") + } + ErrorKind::InvalidPageSize(size) => { + write!(f, "invalid system page size: {size} bytes") + } ErrorKind::Io { operation, error, @@ -104,7 +116,10 @@ impl core::error::Error for Error { fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { match &self.0.kind { // Errors that are self-descriptive. - ErrorKind::TooManyVMPages | ErrorKind::Io { .. } => None, + ErrorKind::TooManyVMPages + | ErrorKind::UnknownPageSize + | ErrorKind::InvalidPageSize(_) + | ErrorKind::Io { .. } => None, // Errors that defer description to the inner error. ErrorKind::IntegerCast(err) => Some(err), @@ -160,7 +175,11 @@ impl Error { #[must_use] pub fn os_error_code(&self) -> Option { match &self.0.kind { - ErrorKind::TooManyVMPages { .. } | ErrorKind::IntegerCast { .. } => None, + ErrorKind::TooManyVMPages { .. } + | ErrorKind::UnknownPageSize + | ErrorKind::InvalidPageSize(_) + | ErrorKind::IntegerCast { .. } => None, + ErrorKind::Io { error, .. } => error.raw_os_error(), } } diff --git a/src/lib.rs b/src/lib.rs index 9d75baa..dd0bb45 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,50 +52,25 @@ mod errors; #[cfg(test)] mod tests; +mod utils; extern crate alloc; use errors::Result; pub use errors::{Error, ErrorKind}; +use core::cmp; use core::ffi::c_void; -use core::num::NonZero; -use core::{cmp, slice}; use std::io::{IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use std::os::raw::c_ulong; use std::{io, panic}; -use lazy_static::lazy_static; use smallvec::SmallVec; -lazy_static! { - /// Size in bytes of the smallest possible virtual memory page. - /// - /// Failure to fetch the information will result in a size - /// of `u64::max_value()`. - static ref MIN_SYSTEM_PAGE_SIZE: NonZero = - match unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) } { - -1 => NonZero::::MAX, - 0 => unsafe { NonZero::::new_unchecked(4096) }, - result => unsafe { NonZero::::new_unchecked(result as u64) }, - }; - - /// Maximum number of the `iovec` structures that can be provided to - /// one system call. - /// - /// Failure to fetch the information will result in a count of `1`. - static ref SYSTEM_IOV_MAX: usize = - match unsafe { libc::sysconf(libc::_SC_IOV_MAX) } { - -1 => 1, - result => result as usize, - }; -} - -/// Align a given number down to a specified alignment boundary. -const fn align_down(n: u64, alignment: NonZero) -> u64 { - // Notice that the calculation below never causes an overflow. - n & !alignment.get().saturating_sub(1) -} +use crate::utils::{ + align_down, ensure_process_exists, io_vectors_from_io_slices, io_vectors_from_io_slices_mut, + min_system_page_size, system_iov_max, +}; /// Prototype of the APIs `process_vm_readv()` and `process_vm_writev()`. type ProcessVMReadVProc = unsafe extern "C" fn( @@ -124,17 +99,17 @@ struct PageAwareAddressRange { impl PageAwareAddressRange { /// Convert a plain address range into an address range which is split, /// at page boundaries, over multiple sections. - fn new(start_address: u64, mut size: u64) -> Self { + fn new(start_address: u64, mut size: u64) -> Result { if size == 0 { - return Self { + return Ok(Self { start_address, size_in_first_page: 0, size_of_inner_pages: 0, size_in_last_page: 0, - }; + }); } - let min_page_size = *MIN_SYSTEM_PAGE_SIZE; + let min_page_size = min_system_page_size()?; let distance_to_preceeding_page_boundary = start_address - align_down(start_address, min_page_size); @@ -145,19 +120,19 @@ impl PageAwareAddressRange { // | -- distance_to_preceeding_page_boundary -- v ---- size ---- v | // preceeding_page_boundary --> start_address --> end_address --> next_page_boundary return if distance_to_preceeding_page_boundary == 0 && size == min_page_size.get() { - Self { + Ok(Self { start_address, size_in_first_page: 0, size_of_inner_pages: size, size_in_last_page: 0, - } + }) } else { - Self { + Ok(Self { start_address, size_in_first_page: size, size_of_inner_pages: 0, size_in_last_page: 0, - } + }) }; } @@ -180,12 +155,12 @@ impl PageAwareAddressRange { let size_of_inner_pages = align_down(size, min_page_size); let size_in_last_page = size - size_of_inner_pages; - Self { + Ok(Self { start_address, size_in_first_page, size_of_inner_pages, size_in_last_page, - } + }) } /// Transform this address range into a vector of `iovec`s. @@ -196,8 +171,8 @@ impl PageAwareAddressRange { /// (if any) is also returned. Returning a vector of `iovec`s that covers /// only a prefix of this address range is not considered a failure. fn into_iov_buffers(mut self) -> Result<(SmallVec<[libc::iovec; 3]>, u64)> { - let min_page_size = MIN_SYSTEM_PAGE_SIZE.get(); - let max_iov_count = *SYSTEM_IOV_MAX; + let min_page_size = min_system_page_size()?.get(); + let max_iov_count = system_iov_max().get(); let mut size_of_not_covered_suffix = 0; let mut inner_pages_count = usize::try_from(self.size_of_inner_pages / min_page_size)?; @@ -342,7 +317,7 @@ impl ProcessVirtualMemoryIO { )); } - Self::ensure_process_exists(process_id)?; + ensure_process_exists(process_id)?; Ok(Self { process_id, @@ -356,28 +331,12 @@ impl ProcessVirtualMemoryIO { self.process_id as u32 } - /// Ensure that the process, identified by the given process identifier, - /// currently exists in the system. - fn ensure_process_exists(process_id: libc::pid_t) -> Result<()> { - if unsafe { libc::kill(process_id, 0) } != -1 { - return Ok(()); - } - - let mut err = io::Error::last_os_error(); - err = match err.raw_os_error() { - Some(libc::ESRCH) => io::Error::from(io::ErrorKind::NotFound), - Some(libc::EINVAL) => io::Error::from(io::ErrorKind::InvalidInput), - Some(libc::EPERM) => io::Error::from(io::ErrorKind::PermissionDenied), - _ => err, - }; - Err(Error::from_io3(err, "kill", process_id)) - } - /// Perform vectored (i.e., scatter/gather) I/O on the virtual memory of the /// target process. fn io_vectored( &mut self, process_vm_io_v: ProcessVMReadVProc, + process_vm_io_v_name: &'static str, local_io_vectors: &[libc::iovec], mut byte_count: u64, ) -> Result { @@ -392,7 +351,7 @@ impl ProcessVirtualMemoryIO { byte_count = cmp::min(byte_count, max_remaining_bytes); let (remote_io_vectors, _size_of_not_covered_suffix) = - PageAwareAddressRange::new(address, byte_count).into_iov_buffers()?; + PageAwareAddressRange::new(address, byte_count)?.into_iov_buffers()?; let transferred_bytes_count = unsafe { process_vm_io_v( @@ -408,7 +367,7 @@ impl ProcessVirtualMemoryIO { if transferred_bytes_count == -1 { return Err(Error::from_io3( io::Error::last_os_error(), - "process_vm_readv/process_vm_writev", + process_vm_io_v_name, self.process_id, )); } @@ -470,16 +429,25 @@ impl Read for ProcessVirtualMemoryIO { iov_len: buf.len(), }; - self.io_vectored(libc::process_vm_readv, &[local_io_vector], buf.len() as u64) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + self.io_vectored( + libc::process_vm_readv, + "process_vm_readv", + &[local_io_vector], + buf.len() as u64, + ) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) } fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result { - let bytes_to_read = bufs.iter().map(|buf| buf.len() as u64).sum(); - let local_io_vectors = unsafe { slice::from_raw_parts(bufs.as_ptr().cast(), bufs.len()) }; + let (byte_count, local_io_vectors) = io_vectors_from_io_slices_mut(bufs); - self.io_vectored(libc::process_vm_readv, local_io_vectors, bytes_to_read) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + self.io_vectored( + libc::process_vm_readv, + "process_vm_readv", + local_io_vectors, + byte_count, + ) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) } } @@ -492,6 +460,7 @@ impl Write for ProcessVirtualMemoryIO { self.io_vectored( libc::process_vm_writev, + "process_vm_writev", &[local_io_vector], buf.len() as u64, ) @@ -499,11 +468,15 @@ impl Write for ProcessVirtualMemoryIO { } fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result { - let bytes_to_write = bufs.iter().map(|buf| buf.len() as u64).sum(); - let local_io_vectors = unsafe { slice::from_raw_parts(bufs.as_ptr().cast(), bufs.len()) }; + let (byte_count, local_io_vectors) = io_vectors_from_io_slices(bufs); - self.io_vectored(libc::process_vm_writev, local_io_vectors, bytes_to_write) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + self.io_vectored( + libc::process_vm_writev, + "process_vm_writev", + local_io_vectors, + byte_count, + ) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) } fn flush(&mut self) -> io::Result<()> { diff --git a/src/tests.rs b/src/tests.rs index f2c5f86..a2ec295 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -8,48 +8,37 @@ use assert_matches::assert_matches; use super::*; -#[test] -fn sensible_virtual_memory_page_size() { - let size = *MIN_SYSTEM_PAGE_SIZE; - assert!(size.get() < u64::MAX); - assert!(size.is_power_of_two()); -} - -#[test] -fn sensible_io_vectors_count() { - let count = *SYSTEM_IOV_MAX; - assert!(count > 0); -} - #[test] fn new_page_aware_address_range_1page() { + let min_page_size = min_system_page_size().unwrap().get(); + assert_eq!( - PageAwareAddressRange::new(0, 0), + PageAwareAddressRange::new(0, 0).unwrap(), PageAwareAddressRange::default() ); assert_eq!( - PageAwareAddressRange::new(0x1000_0000, 0), + PageAwareAddressRange::new(0x1000_0000, 0).unwrap(), PageAwareAddressRange { start_address: 0x1000_0000, ..Default::default() } ); assert_eq!( - PageAwareAddressRange::new(u64::MAX, 0), + PageAwareAddressRange::new(u64::MAX, 0).unwrap(), PageAwareAddressRange { start_address: u64::MAX, ..Default::default() } ); assert_eq!( - PageAwareAddressRange::new(0, 16), + PageAwareAddressRange::new(0, 16).unwrap(), PageAwareAddressRange { size_in_first_page: 16, ..Default::default() } ); assert_eq!( - PageAwareAddressRange::new(0x1000_0000, 16), + PageAwareAddressRange::new(0x1000_0000, 16).unwrap(), PageAwareAddressRange { start_address: 0x1000_0000, size_in_first_page: 16, @@ -57,7 +46,7 @@ fn new_page_aware_address_range_1page() { } ); assert_eq!( - PageAwareAddressRange::new(0x1000_0000 - 16, 16), + PageAwareAddressRange::new(0x1000_0000 - 16, 16).unwrap(), PageAwareAddressRange { start_address: 0x1000_0000 - 16, size_in_first_page: 16, @@ -65,7 +54,7 @@ fn new_page_aware_address_range_1page() { } ); assert_eq!( - PageAwareAddressRange::new(u64::MAX - 16, 13), + PageAwareAddressRange::new(u64::MAX - 16, 13).unwrap(), PageAwareAddressRange { start_address: u64::MAX - 16, size_in_first_page: 13, @@ -73,7 +62,7 @@ fn new_page_aware_address_range_1page() { } ); assert_eq!( - PageAwareAddressRange::new(u64::MAX - 16, 16), + PageAwareAddressRange::new(u64::MAX - 16, 16).unwrap(), PageAwareAddressRange { start_address: u64::MAX - 16, size_in_first_page: 16, @@ -81,7 +70,7 @@ fn new_page_aware_address_range_1page() { } ); assert_eq!( - PageAwareAddressRange::new(u64::MAX - 16, 17), + PageAwareAddressRange::new(u64::MAX - 16, 17).unwrap(), PageAwareAddressRange { start_address: u64::MAX - 16, size_in_first_page: 17, @@ -89,7 +78,7 @@ fn new_page_aware_address_range_1page() { } ); assert_eq!( - PageAwareAddressRange::new(0x1000_0000 + 16, 32), + PageAwareAddressRange::new(0x1000_0000 + 16, 32).unwrap(), PageAwareAddressRange { start_address: 0x1000_0000 + 16, size_in_first_page: 32, @@ -97,28 +86,25 @@ fn new_page_aware_address_range_1page() { } ); assert_eq!( - PageAwareAddressRange::new(0, MIN_SYSTEM_PAGE_SIZE.get()), + PageAwareAddressRange::new(0, min_page_size).unwrap(), PageAwareAddressRange { - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get(), + size_of_inner_pages: min_page_size, ..Default::default() } ); assert_eq!( - PageAwareAddressRange::new(0x1000_0000, MIN_SYSTEM_PAGE_SIZE.get()), + PageAwareAddressRange::new(0x1000_0000, min_page_size).unwrap(), PageAwareAddressRange { start_address: 0x1000_0000, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get(), + size_of_inner_pages: min_page_size, ..Default::default() } ); assert_eq!( - PageAwareAddressRange::new( - u64::MAX - MIN_SYSTEM_PAGE_SIZE.get() + 1, - MIN_SYSTEM_PAGE_SIZE.get() - ), + PageAwareAddressRange::new(u64::MAX - min_page_size + 1, min_page_size).unwrap(), PageAwareAddressRange { - start_address: u64::MAX - MIN_SYSTEM_PAGE_SIZE.get() + 1, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get(), + start_address: u64::MAX - min_page_size + 1, + size_of_inner_pages: min_page_size, ..Default::default() } ); @@ -126,13 +112,11 @@ fn new_page_aware_address_range_1page() { #[test] fn new_page_aware_address_range_2pages() { - for addr in &[ - u64::MAX - 7, - MIN_SYSTEM_PAGE_SIZE.get() - 8, - 0x1000_0000 - 8, - ] { + let min_page_size = min_system_page_size().unwrap().get(); + + for addr in &[u64::MAX - 7, min_page_size - 8, 0x1000_0000 - 8] { assert_eq!( - PageAwareAddressRange::new(*addr, 32), + PageAwareAddressRange::new(*addr, 32).unwrap(), PageAwareAddressRange { start_address: *addr, size_in_first_page: 8, @@ -145,75 +129,68 @@ fn new_page_aware_address_range_2pages() { #[test] fn new_page_aware_address_range_manypages() { + let min_page_size = min_system_page_size().unwrap().get(); + assert_eq!( - PageAwareAddressRange::new(u64::MAX - 7, 32 + MIN_SYSTEM_PAGE_SIZE.get() * 5), + PageAwareAddressRange::new(u64::MAX - 7, 32 + min_page_size * 5).unwrap(), PageAwareAddressRange { start_address: u64::MAX - 7, size_in_first_page: 8, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get() * 5, + size_of_inner_pages: min_page_size * 5, size_in_last_page: 24, } ); assert_eq!( - PageAwareAddressRange::new(u64::MAX - 7, 32 + MIN_SYSTEM_PAGE_SIZE.get()), + PageAwareAddressRange::new(u64::MAX - 7, 32 + min_page_size).unwrap(), PageAwareAddressRange { start_address: u64::MAX - 7, size_in_first_page: 8, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get(), + size_of_inner_pages: min_page_size, size_in_last_page: 24, } ); assert_eq!( - PageAwareAddressRange::new(u64::MAX - 7, 32 + MIN_SYSTEM_PAGE_SIZE.get() * 2), + PageAwareAddressRange::new(u64::MAX - 7, 32 + min_page_size * 2).unwrap(), PageAwareAddressRange { start_address: u64::MAX - 7, size_in_first_page: 8, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get() * 2, + size_of_inner_pages: min_page_size * 2, size_in_last_page: 24, } ); assert_eq!( - PageAwareAddressRange::new( - u64::MAX - MIN_SYSTEM_PAGE_SIZE.get() - 7, - 32 + MIN_SYSTEM_PAGE_SIZE.get() - ), + PageAwareAddressRange::new(u64::MAX - min_page_size - 7, 32 + min_page_size).unwrap(), PageAwareAddressRange { - start_address: u64::MAX - MIN_SYSTEM_PAGE_SIZE.get() - 7, + start_address: u64::MAX - min_page_size - 7, size_in_first_page: 8, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get(), + size_of_inner_pages: min_page_size, size_in_last_page: 24, } ); assert_eq!( - PageAwareAddressRange::new( - u64::MAX - MIN_SYSTEM_PAGE_SIZE.get() - 7, - 32 + MIN_SYSTEM_PAGE_SIZE.get() * 2 - ), + PageAwareAddressRange::new(u64::MAX - min_page_size - 7, 32 + min_page_size * 2).unwrap(), PageAwareAddressRange { - start_address: u64::MAX - MIN_SYSTEM_PAGE_SIZE.get() - 7, + start_address: u64::MAX - min_page_size - 7, size_in_first_page: 8, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get() * 2, + size_of_inner_pages: min_page_size * 2, size_in_last_page: 24, } ); assert_eq!( - PageAwareAddressRange::new( - MIN_SYSTEM_PAGE_SIZE.get() - 8, - 32 + MIN_SYSTEM_PAGE_SIZE.get() * 5 - ), + PageAwareAddressRange::new(min_page_size - 8, 32 + min_page_size * 5).unwrap(), PageAwareAddressRange { - start_address: MIN_SYSTEM_PAGE_SIZE.get() - 8, + start_address: min_page_size - 8, size_in_first_page: 8, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get() * 5, + size_of_inner_pages: min_page_size * 5, size_in_last_page: 24, } ); assert_eq!( - PageAwareAddressRange::new(0x1000_0000 - 8, 32 + MIN_SYSTEM_PAGE_SIZE.get() * 5), + PageAwareAddressRange::new(0x1000_0000 - 8, 32 + min_page_size * 5).unwrap(), PageAwareAddressRange { start_address: 0x1000_0000 - 8, size_in_first_page: 8, - size_of_inner_pages: MIN_SYSTEM_PAGE_SIZE.get() * 5, + size_of_inner_pages: min_page_size * 5, size_in_last_page: 24, } ); diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..b05b707 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,126 @@ +#[cfg(test)] +mod tests; + +use core::num::NonZero; +use core::slice; +use core::sync::atomic::{AtomicU64, Ordering}; +use std::io::{self, IoSlice, IoSliceMut}; +use std::sync::atomic::AtomicUsize; + +use crate::errors::{Error, ErrorKind, Result}; + +/// Align a given number down to a specified alignment boundary. +pub(crate) const fn align_down(n: u64, alignment: NonZero) -> u64 { + // Notice that the calculation below never causes an overflow. + n & !alignment.get().saturating_sub(1) +} + +pub(crate) fn io_vectors_from_io_slices<'slices>( + bufs: &'slices [IoSlice], +) -> (u64, &'slices [libc::iovec]) { + let byte_count = bufs.iter().map(|buf| buf.len() as u64).sum(); + let local_io_vectors = unsafe { slice::from_raw_parts(bufs.as_ptr().cast(), bufs.len()) }; + (byte_count, local_io_vectors) +} + +pub(crate) fn io_vectors_from_io_slices_mut<'slices>( + bufs: &'slices mut [IoSliceMut], +) -> (u64, &'slices [libc::iovec]) { + let byte_count = bufs.iter().map(|buf| buf.len() as u64).sum(); + let local_io_vectors = unsafe { slice::from_raw_parts(bufs.as_ptr().cast(), bufs.len()) }; + (byte_count, local_io_vectors) +} + +/// Ensure that the process, identified by the given process identifier, +/// currently exists in the system. +pub(crate) fn ensure_process_exists(process_id: libc::pid_t) -> Result<()> { + if unsafe { libc::kill(process_id, 0) } != -1 { + return Ok(()); + } + + let mut err = io::Error::last_os_error(); + err = match err.raw_os_error() { + Some(libc::ESRCH) => io::Error::from(io::ErrorKind::NotFound), + Some(libc::EINVAL) => io::Error::from(io::ErrorKind::InvalidInput), + Some(libc::EPERM) => io::Error::from(io::ErrorKind::PermissionDenied), + _ => err, + }; + Err(Error::from_io3(err, "kill", process_id)) +} + +fn get_min_system_page_size() -> Result> { + let value = match unsafe { libc::sysconf(libc::_SC_PAGE_SIZE) } { + -1 => return Err(Error::from(ErrorKind::UnknownPageSize)), + r => r as u64, + }; + + if let Some(value) = NonZero::new(value) { + if value.is_power_of_two() { + return Ok(value); + } + } + + Err(Error::from(ErrorKind::InvalidPageSize(value))) +} + +/// Size in bytes of the smallest possible virtual memory page. +pub(crate) fn min_system_page_size() -> Result> { + static VALUE: AtomicU64 = AtomicU64::new(0); + + if let Some(value) = NonZero::new(VALUE.load(Ordering::Acquire)) { + return Ok(value); + } + + // Initialize global VALUE. + let value = get_min_system_page_size()?; + + loop { + match VALUE.compare_exchange_weak(0, value.get(), Ordering::Release, Ordering::Acquire) { + Ok(_) => break Ok(value), // Initialized in this thread. + + Err(previous_value) => { + if let Some(previous_value) = NonZero::new(previous_value) { + break Ok(previous_value); // Initialized in another thread. + } + // Otherwise, spurious wake. Loop again. + } + } + } +} + +fn get_system_iov_max() -> NonZero { + let value = match unsafe { libc::sysconf(libc::_SC_IOV_MAX) } { + -1 | 0 => 1, + r => r as usize, + }; + + unsafe { NonZero::new_unchecked(value) } +} + +/// Maximum number of the `iovec` structures that can be provided to +/// one system call. +/// +/// Failure to fetch the information will result in a count of `1`. +pub(crate) fn system_iov_max() -> NonZero { + static VALUE: AtomicUsize = AtomicUsize::new(0); + + if let Some(value) = NonZero::new(VALUE.load(Ordering::Acquire)) { + return value; + } + + // Initialize global VALUE. + let value = get_system_iov_max(); + + loop { + match VALUE.compare_exchange_weak(0, value.get(), Ordering::Release, Ordering::Acquire) { + Ok(_) => break value, // Initialized in this thread. + + Err(previous_value) => { + if let Some(previous_value) = NonZero::new(previous_value) { + break previous_value; // Initialized in another thread. + } + // Otherwise, spurious wake. Loop again. + } + } + } +} diff --git a/src/utils/tests.rs b/src/utils/tests.rs new file mode 100644 index 0000000..b7d7afb --- /dev/null +++ b/src/utils/tests.rs @@ -0,0 +1,6 @@ +#[test] +fn min_system_page_size() { + let size = super::min_system_page_size().unwrap(); + assert!(size.get() < u64::MAX); + assert!(size.is_power_of_two()); +}