//! LOw Level Interface //! //! Provides interfaces to low-level operations and syscalls. use std::ops::{Deref, DerefMut}; use std::{ fmt, error, }; use bytes::{ Buf, }; use std::os::unix::io::*; use std::ffi::{ CStr, CString, }; //TODO: Provide adequate wrappers for the extern functions below /// Raw exports mod raw { pub(super) use std::os::raw::*; use libc::{ off64_t as c_off64_t, off_t as c_off_t, pid_t as c_pid_t, size_t as c_size_t, }; // Function definitions extern "C" { pub fn memfd_create(name: *const c_char, flags: c_int) -> c_int; pub fn dup(fd: c_int) -> c_int; pub fn dup2(from: c_int, to: c_int) -> c_int; pub fn ftruncate(fd: c_int, length: c_off_t) -> c_int; pub fn ftruncate64(fd: c_int, length: c_off64_t) -> c_int; pub fn close(fd: c_int) -> c_int; pub fn getenv(name: *const c_char) -> *mut c_char; pub fn getpid() -> c_pid_t; pub fn mmap( addr: *mut c_void, len: c_size_t, prot: c_int, flags: c_int, fd: c_int, offset: c_off_t ) -> *mut c_void; pub fn mmap64( addr: *mut c_void, len: c_size_t, prot: c_int, flags: c_int, fd: c_int, offset: c_off64_t ) -> *mut c_void; } // Re-exports pub use libc::MAP_FAILED; pub use std::ffi::c_void as Opaque; pub use libc::c_char as Char; } // Errno error handling //TODO: Move all errno related stuff into a submodule inside `srd/loli/` to un-clutter this file; and add a `RawErrno`-like struct for errors that are *just* an errno value (that implements `SystemError`) /// A raw `errno` value pub type RawErrno = raw::c_int; /// Default max buffer size for formatting `errno` message codes. /// /// This is the size used by glibc's `perror()`. It should cover all internationalised error messages. pub const DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE: usize = 1024; /// Default buffer size short `errno` message code strings. /// /// # Internationalisation /// This size covers all current English `errno` messages. However, if the program is ran with a non-ASCII locale, the size may exceed this by far. See `DEFAULT_ERRNO_MESSAGE_BUFFER_SIZE` for those cases. pub const DEFAULT_ASCII_ERRNO_MESSAGE_BUFFER_SIZE: usize = 64; /// An error type which also contains an `errno` code. //TODO: A way to integrate this into eyre::Reports? Maybe in the extension trait? `.to_report_with_errno()` or something? pub trait SystemError: std::error::Error { fn code(&self) -> RawErrno; #[inline(always)] fn fmt_message(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { errno_fmt_message::<1024>(self.code(), f) } #[inline] fn get_message(&self) -> ErrnoFormatter { ErrnoFormatter::new_default(self.code()) } #[inline] fn message(&self) -> String { self.get_message().to_string() } } /// Extension trait for `SystemError` types. /// /// # Specific buffered sizes /// Provides user-configurable buffer sized methods for message formatting pub trait SystemErrorExt: SystemError { fn fmt_message_with_buffer(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; fn get_message_with_buffer(&self) -> ErrnoFormatter; fn message_with_buffer(&self) -> String; } impl SystemErrorExt for T where T: SystemError { #[inline] fn fmt_message_with_buffer(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { errno_fmt_message::(self.code(), f) } #[inline] fn get_message_with_buffer(&self) -> ErrnoFormatter { ErrnoFormatter::::new(self.code()) } #[inline] fn message_with_buffer(&self) -> String { ErrnoFormatter::::new(self.code()).to_string() } } /// String formatter for an `errno` value. /// /// # `MESSAGE_BUFFER_SIZE` /// This structure contains only the `errno` code. The `MESSAGE_BUFFER_SIZE` const generic is used as a max size for formatting the error messages. /// It **must** be above 0. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)] #[repr(transparent)] pub struct ErrnoFormatter(RawErrno); //TODO: Document the following impl ErrnoFormatter { #[inline(always)] pub const fn new_ascii(code: RawErrno) -> Self { Self(code) } } impl ErrnoFormatter { #[inline(always)] pub const fn new_default(code: RawErrno) -> Self { Self(code) } } impl ErrnoFormatter { #[inline(always)] pub const fn new(code: RawErrno) -> Self { Self(code) } #[inline] pub const fn raw(self) -> RawErrno { self.0 } #[inline] pub const fn buffer_size(self) -> usize { BUFF_SIZE } #[inline] pub const fn with_buffer_size(self) -> ErrnoFormatter { ErrnoFormatter::::new(self.0) } pub fn try_to_string(&self) -> Result> { match self.try_to_raw_string() { Ok(string) => string.into_string().map_err(|us| Ok(us)), Err(en) => Err(Err(en)), } } #[inline] pub fn try_to_raw_string(&self) -> Result { errno_ustring_buffer::(self.0) } #[inline] pub fn try_to_c_string(&self) -> Result { errno_cstring_buffer::(self.0) } #[inline] pub fn to_raw_buffer(&self) -> Result<[u8; BUFF_SIZE], RawErrno> { errno_raw_buffer(self.0) } } impl fmt::Display for ErrnoFormatter { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { errno_fmt_message::(self.0, f) } } fn errno_fmt_message(code: RawErrno, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Ok(buffer) = errno_raw_buffer::(code) { if let Some(first) = buffer.split(|&b| b == 0).next() { f.write_str(String::from_utf8_lossy(first).as_ref()) } else { #[cold] #[inline(never)] //XXX: Should we noinline this cold path? I think we should. fn _failed_path(code: RawErrno, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "", code) } _failed_path(code, f) } } else { #[cold] #[inline(never)] fn _failed_path(code: RawErrno, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "", code) } _failed_path(code, f) } } fn errno_raw_buffer(errno: RawErrno) -> Result<[u8; BUFF_SIZE], RawErrno> { use std::mem::MaybeUninit; let mut buffer: MaybeUninit<[u8; BUFF_SIZE]> = MaybeUninit::uninit(); Ok(unsafe { errno_if_nz(libc::strerror_r(errno, buffer.as_mut_ptr() as *mut raw::c_char, BUFF_SIZE))?; buffer.assume_init() }) } fn errno_raw_ustr(errno: RawErrno, buffer: &mut [u8]) -> Result<&UStr, RawErrno> { unsafe { errno_if_nz(libc::strerror_r(errno, buffer.as_mut_ptr() as *mut raw::c_char, buffer.len()))?; } Ok(UStr::new_mut(buffer)) } #[inline(always)] fn errno_raw_cstr(errno: RawErrno, buffer: &mut [u8]) -> Result<&CStr, RawErrno> { errno_raw_ustr(errno, buffer).map(|x| unsafe {x.to_c_str()}) } fn errno_ustring_buffer(errno: RawErrno) -> Result { let buf = errno_raw_buffer::(errno)?; Ok(UString::new_from_buffer(&mut &buf[..], BUFF_SIZE)) } fn errno_cstring_buffer(errno: RawErrno) -> Result { errno_ustring_buffer::(errno).map(|x| unsafe {x.to_c_string()}) } fn errno_lossy_string_buffer(errno: RawErrno) -> Result { errno_ustring_buffer::(errno).map(|x| x.to_string_lossy().into_owned()) } /// An error when `dup()` or `dup2()`ing a stream. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct DupError(RawErrno); impl SystemError for DupError { /// The `errno` code for this error. #[inline] fn code(&self) -> RawErrno { self.0 } } impl error::Error for DupError{} impl fmt::Display for DupError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "dup() failed with {}: ", self.0)?; self.fmt_message(f) } } /// Get the current thread's `errno` value pub fn current_errno() -> RawErrno { unsafe {*libc::__errno_location()} } /// Check FFI function returns for errors and return the appropriate errno value if needed. /// /// # Returns /// If `chk(rv)` returns `true`, `Err()` is returned. Otherwise, `Ok(rv)` is returned. #[inline(always)] pub fn errno_if(rv: T, chk: F) -> Result where F: Fn(T) -> bool { if !chk(rv) { Ok(rv) } else { #[cold] #[inline(never)] fn _error() -> RawErrno { current_errno() } Err(_error()) } } #[inline(always)] fn errno_if_not(rv: raw::c_int) -> Result { if rv == EXPECTED { Ok(rv) } else { #[cold] #[inline(never)] fn _error() -> RawErrno { current_errno() } Err(_error()) } } #[inline(always)] fn errno_if_nz(rv: raw::c_int) -> Result<(), RawErrno> { errno_if_not::<0>(rv).map(|_| ()) } // Wrapper traits //TODO: Move wrapper traits into a seperate module in src/loli/ to unclutter this file /// Extension methods for `dup()`ing file descriptors pub trait DupExt: Sized { unsafe fn dup(&self) -> Result; } /// Extension methods for `dup2()`ing file descriptors pub trait Dup2Ext { unsafe fn dup2(&self, other: &mut U) -> Result<(), DupError>; } impl Dup2Ext for T where T: AsRawFd, U: AsRawFd { unsafe fn dup2(&self, other: &mut U) -> Result<(), DupError> { errno_if(raw::dup2(self.as_raw_fd(), other.as_raw_fd()), |v| v < 0).map_err(DupError)?; Ok(()) } } impl DupExt for T where T: FromRawFd + AsRawFd { unsafe fn dup(&self) -> Result { errno_if(raw::dup(self.as_raw_fd()), |v| v < 0) .map(|fd| Self::from_raw_fd(fd)) // WHY are `unsafe fn`s not first class in an `unsafe` context?? .map_err(DupError) } } //TODO: Document the following, and make sure it works like CStr/CString (minus the nul-termination bs) //TODO: Move UStr/UString and related stuffs into a seperate module in src/loli/ to unclutter this file /// A reference to an unmanaged string that may contain non-utf8 characters. /// /// This differs from `CStr` in that it is **not** nul-terminated. #[repr(transparent)] pub struct UStr([u8]); impl UStr { #[inline(always)] pub unsafe fn from_raw_mut<'a>(ptr: *mut u8, size: usize) -> &'a mut Self { &mut *(std::ptr::slice_from_raw_parts_mut::(ptr, size) as *mut Self) } #[inline(always)] pub unsafe fn from_raw<'a>(ptr: *const u8, size: usize) -> &'a Self { &*(std::ptr::slice_from_raw_parts::(ptr, size) as *const Self) } #[inline] pub fn new<'a>(bytes: &'a [u8]) -> &'a Self { // SAFETY: ustr is transparent. unsafe { std::mem::transmute::<&'a [u8], &'a Self>(bytes) } } #[inline] pub fn new_mut<'a>(bytes: &'a mut [u8]) -> &'a mut Self { // SAFETY: ustr is transparent. unsafe { std::mem::transmute::<&'a mut [u8], &'a mut Self>(bytes) } } /// Convert to a reference to a nul-terminated byte string /// /// # Safety /// The unmanaged string must contain a null byte in its slice. pub unsafe fn to_c_str(&self) -> &CStr { CStr::from_ptr(self.0.as_ptr() as *const raw::c_char) } /// Create a `CStr` from this nul-terminated unmanaged byte string /// /// # Returns /// The final byte in the slice must be `\0`, or `Err` will be returned. pub fn as_c_str(&self) -> Result<&CStr, std::ffi::FromBytesWithNulError> { CStr::from_bytes_with_nul(&self.0[..]) } /// Create an unmanaged string from a nul-terminated string /// /// # Nul-terminator /// The returned unmanaged string reference will contain the nul-terminator as the final byte in the slice. #[inline] pub fn from_c_str(raw: &CStr) -> &Self { Self::new(raw.to_bytes_with_nul()) } /// Returns `true` if the underlying slice is nul-terminated, and can be safely converted back/forth with `CStr`. #[inline] pub fn is_nul_terminated(&self) -> bool { self.0.last().map(|&x| x == 0).unwrap_or(false) } #[inline] pub fn to_str(&self) -> Result<&str, std::str::Utf8Error> { std::str::from_utf8(&self.0[..]) } #[inline] pub fn to_mut_str(&mut self) -> Result<&mut str, std::str::Utf8Error> { std::str::from_utf8_mut(&mut self.0[..]) } pub fn to_string_lossy(&self) -> std::borrow::Cow<'_, str> { String::from_utf8_lossy(&self.0[..]) } #[inline] pub fn into_ustring(self: Box) -> UString { UString::from_boxed_ustr(self) } #[inline] pub fn from_ustring(owned: UString) -> Box { owned.into_boxed_ustr() } } /// An owned unmanaged string that may contain non-utf8 characters. /// /// This differs from `CString` in that it is **not** nul-terminated. pub struct UString(Box<[u8]>); impl UString { /// Create a new unmanaged string from `len` bytes from a buffer pub fn new_from_buffer(buffer: &mut B, len: usize) -> Self { let mut vec = Vec::with_capacity(len); buffer.copy_to_slice(&mut vec[..]); Self(vec.into()) } pub fn new_from_bytes(bytes: impl Into) -> Self { Self::new_from_slice(bytes.into()) } /// Create a new unmanaged string by copying it from a slice #[inline] pub fn new_from_slice(slice: impl AsRef<[u8]>) -> Self { Self::new(slice.as_ref().to_owned()) } /// Create a new unmanaged string from a container containing arbitrary bytes #[inline] pub fn new(bytes: impl Into>) -> Self { Self(bytes.into()) } /// Create an owned (allocated) unmanaged string from a pointer and size /// /// # Safety /// `ptr` *must* have been originally created form a `Box::into_raw()` call. #[inline] pub unsafe fn from_raw(ptr: *mut u8, size: usize) -> Self { Self(Box::from_raw(std::ptr::slice_from_raw_parts_mut(ptr, size))) } #[inline] pub fn into_raw(self) -> Box<[u8]> { self.0 } pub fn to_mut_bytes(&self) -> bytes::BytesMut { bytes::BytesMut::from(&self.0[..]) } pub fn into_bytes(self) -> bytes::Bytes { bytes::Bytes::from(self.0) } pub fn to_bytes(&self) -> bytes::Bytes { bytes::Bytes::copy_from_slice(&self.0[..]) } pub fn into_boxed_ustr(self) -> Box { unsafe { Box::::from_raw(Box::<[u8]>::into_raw(self.0) as *mut UStr) } } pub fn from_boxed_ustr(us: Box) -> Self { Self(unsafe { Box::<[u8]>::from_raw(Box::::into_raw(us) as *mut [u8]) }) } pub fn into_string(self) -> Result { let vec = Vec::from(self.0); String::from_utf8(vec).map_err(|e| { Self(e.into_bytes().into()) }) } pub fn try_into_string(self) -> Result { let vec = Vec::from(self.0); String::from_utf8(vec)/*.map_err(|e| { Self(e.into_bytes().into()) })*/ } /// Allocate a new owned `CString` from this nul-terminated unmanaged string. /// /// # Safety /// The underlying slice *must* contain a null byte. #[inline] pub unsafe fn to_c_string(&self) -> CString { self.deref().to_c_str().into() } /// Convert this nul-terminated `UString` into a `CString`. /// /// # Returns /// If the last bytes of the sequence is not `\0`, `Err` is returned. #[inline] pub fn into_c_string(self) -> Result { CString::from_vec_with_nul(self.0.into()) } /// Convert this owned `CString` into a nul-terminated `UString`. #[inline] pub fn from_c_string(cstring: CString) -> Self { Self(cstring.into_bytes_with_nul().into()) } } impl From for Box { #[inline] fn from(from: UString) -> Self { from.into_boxed_ustr() } } impl From> for UString { #[inline] fn from(from: Box) -> Self { from.into_ustring() } } impl AsRef<[u8]> for UStr { #[inline] fn as_ref(&self) -> &[u8] { &self.0[..] } } /* XXX: Is this needed? impl Deref for ustr { type Target = [u8]; #[inline] fn deref(&self) -> &Self::Target { &self.0[..] } } XXX: Is DerefMut needed, too? XXX: What about AsMut? I think those two are unsafe, especially if the &mut UStr comes from an &mut str. There are unsafe methods for this anyway. */ impl AsRef for UString { #[inline(always)] fn as_ref(&self) -> &UStr { UStr::new(&self.0[..]) } } impl Deref for UString { type Target = UStr; #[inline] fn deref(&self) -> &Self::Target { self.as_ref() } } //Should we allow mutation of the `ustr` here? I think so, because we don't allow mutation of the underlying [u8] impl AsMut for UString { #[inline(always)] fn as_mut(&mut self) -> &mut UStr { UStr::new_mut(&mut self.0[..]) } } impl DerefMut for UString { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { self.as_mut() } } // UStr/ing <--?> CStr/ing impl From for UString { #[inline] fn from(from: CString) -> Self { Self::from_c_string(from) } } impl<'a> From<&'a CStr> for &'a UStr { #[inline] fn from(from: &'a CStr) -> Self { UStr::from_c_str(from) } } impl TryFrom for CString { type Error = std::ffi::FromVecWithNulError; #[inline] fn try_from(from: UString) -> Result { from.into_c_string() } } impl<'a> TryFrom<&'a UStr> for &'a CStr { type Error = std::ffi::FromBytesWithNulError; #[inline] fn try_from(from: &'a UStr) -> Result { from.as_c_str() } } // UStr/ing <--?> str/ing impl From for UString { #[inline] fn from(from: String) -> Self { Self::new(from.into_bytes()) } } impl<'a> From<&'a str> for &'a UStr { #[inline] fn from(from: &'a str) -> Self { UStr::new(from.as_bytes()) } } // No From<&'_ mut str> for &'_ mut UStr: The bytes belonging to the &'_ str may be modified in safe code if this were allowed. impl TryFrom for String { type Error = std::string::FromUtf8Error; #[inline] fn try_from(from: UString) -> Result { from.try_into_string() } } impl<'a> TryFrom<&'a UStr> for &'a str { type Error = std::str::Utf8Error; #[inline] fn try_from(from: &'a UStr) -> Result { from.to_str() } } #[cfg(test)] mod tests { use super::*; #[test] fn dup2() { use std::fs; unsafe { std::io::stderr().dup2(&mut std::io::stdout()) } .expect("Failed to route stdout to stderr"); let null = fs::OpenOptions::new() .read(true) .write(true) .open("/dev/null").unwrap(); unsafe { null.dup2(&mut std::io::stdin()) }.expect("Failed to shunt stdin to /dev/null"); unsafe { null.dup2(&mut std::io::stderr()) }.expect("Failed to shunt stderr to /dev/null"); } }