use super::error::{OpenFailed, ReadAtFailed, ReadExactAtError}; use core::{ ffi::{CStr, c_int}, fmt, }; pub(crate) struct ByteSliceWriter<'a>(Option<&'a mut [u8]>); impl<'a> ByteSliceWriter<'a> { pub(crate) fn new(b: &'a mut [u8]) -> Self { Self(Some(b)) } } impl<'a> fmt::Write for ByteSliceWriter<'a> { fn write_str(&mut self, s: &str) -> fmt::Result { let buf = self.0.take().unwrap(); let (dst, tail) = buf.split_at_mut_checked(s.len()).ok_or(fmt::Error)?; dst.copy_from_slice(s.as_bytes()); self.0 = Some(tail); Ok(()) } } #[derive(Debug)] pub(crate) struct File(c_int); impl File { pub(crate) fn open(path: &CStr) -> Result { loop { let rv = unsafe { libc::open64(path.as_ptr(), libc::O_RDONLY | libc::O_CLOEXEC, 0) }; if rv != -1 { return Ok(Self(rv)); } let err = errno(); if err != libc::EINTR { return Err(OpenFailed(err)); } } } pub(crate) fn read_exact_at( &self, position: usize, buf: &mut [u8], ) -> Result<(), ReadExactAtError> { let mut offset = 0; while offset < buf.len() { let position = position .checked_add(offset) .ok_or(ReadExactAtError::AddressOverflow)?; let bytes_read = self .read_at(position, &mut buf[offset..]) .map_err(ReadExactAtError::ReadAt)?; if bytes_read == 0 { return Err(ReadExactAtError::UnexpectedEof { position }); } offset += bytes_read; } Ok(()) } pub(crate) fn read_at(&self, position: usize, buf: &mut [u8]) -> Result { let position = libc::off64_t::try_from(position).map_err(|_| ReadAtFailed::AddressOutOfBounds)?; loop { let rv = unsafe { libc::pread64(self.0, buf.as_mut_ptr().cast(), buf.len(), position) }; if rv != -1 { return Ok(usize::try_from(rv).unwrap()); } let err = errno(); if err != libc::EINTR { return Err(ReadAtFailed::Syscall(err)); } } } } impl Drop for File { fn drop(&mut self) { unsafe { let rv = libc::close(self.0); if rv == -1 { // Not much we can really do here } } } } pub(crate) fn errno() -> c_int { unsafe { *errno_location() } } pub(crate) fn set_errno(value: c_int) { unsafe { *errno_location() = value }; } #[cfg(target_os = "linux")] fn errno_location() -> *mut c_int { unsafe { libc::__errno_location() } } #[cfg(target_os = "android")] fn errno_location() -> *mut c_int { unsafe { libc::__errno() } }