Started `file` module: Will have file interfaces from `memfd`: `ManagedFD`, `UnmanagedFD`, and `MemoryFile`.
Fortune for mapped-file's current commit: Small curse − 小凶master
parent
5ba18c9358
commit
d3cc7b45a5
@ -0,0 +1,198 @@
|
|||||||
|
//! Useful for C-interop
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
macro_rules! c_try {
|
||||||
|
($call:expr => $invalid:literal; $fmt:literal $(, $args:expr)*) => {
|
||||||
|
{
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
fn _panic_bad_c_call<'a>(args: ::std::fmt::Arguments<'a>) -> !
|
||||||
|
{
|
||||||
|
panic!("C call failed (invalid return {}): {}", $invalid, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
let res = unsafe { $call };
|
||||||
|
if res == $invalid {
|
||||||
|
_panic_bad_c_call(format_args!($fmt $(, $args)*))
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($call:expr => if $func:expr; $fmt:literal $(, $args:expr)*) => {
|
||||||
|
{
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
fn _panic_bad_c_call<'a, T: ::std::fmt::Display>(invalid: T, args: ::std::fmt::Arguments<'a>) -> !
|
||||||
|
{
|
||||||
|
panic!("C call failed (invalid return {}): {}", invalid, args)
|
||||||
|
}
|
||||||
|
let res = unsafe { $call };
|
||||||
|
if $func(res) {
|
||||||
|
_panic_bad_c_call(res, format_args!($fmt $(, $args)*));
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(? $call:expr => if $func:expr; $fmt:literal $(, $args:expr)*) => {
|
||||||
|
{
|
||||||
|
let res = unsafe { $call };
|
||||||
|
if $func(res) {
|
||||||
|
Err(FFIError::from_last_error(res, format_args!($fmt $(, $args)*)))
|
||||||
|
} else {
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(? $call:expr => $invalid:literal; $fmt:literal $(, $args:expr)*) => {
|
||||||
|
{
|
||||||
|
let res = unsafe { $call };
|
||||||
|
if res == $invalid {
|
||||||
|
Err(FFIError::from_last_error($invalid, format_args!($fmt $(, $args)*)))
|
||||||
|
} else {
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/* Eh... Idk why this doesn't work...
|
||||||
|
($call:expr => {$($invalid:pat $(if $pred:pat)?),+} => $fmt:literal $(, $args:expr)*) => {
|
||||||
|
{
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
fn _panic_bad_c_call<'a, T: ::std::fmt::Display>(invalid: T, args: ::std::fmt::Arguments<'a>) -> !
|
||||||
|
{
|
||||||
|
panic!("C call failed (invalid return {}): {}", invalid, args)
|
||||||
|
}
|
||||||
|
let res = $call;
|
||||||
|
match res {
|
||||||
|
$($invalid $(if $pred)? => _panic_bad_c_call(res, format_args!($fmt $(, $args)*))),*
|
||||||
|
x => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};*/
|
||||||
|
}
|
||||||
|
pub(crate) use c_try;
|
||||||
|
|
||||||
|
/// Error context for a failed C call.
|
||||||
|
/// Returns the invalid return value, the `errno` error, and a message.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct FFIError<'a, T>(T, io::Error, fmt::Arguments<'a>);
|
||||||
|
|
||||||
|
impl<'a, T> FFIError<'a, T>
|
||||||
|
where FFIError<'a, T>: error::Error
|
||||||
|
{
|
||||||
|
#[inline(never)]
|
||||||
|
#[cold]
|
||||||
|
fn from_last_error(value: T, arguments: fmt::Arguments<'a>) -> Self
|
||||||
|
{
|
||||||
|
Self(value, io::Error::last_os_error(), arguments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, T> AsRef<io::Error> for FFIError<'a, T>
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &io::Error {
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> FFIError<'a, T>
|
||||||
|
{
|
||||||
|
/// A reference to the value
|
||||||
|
#[inline]
|
||||||
|
pub fn value(&self) -> &T
|
||||||
|
{
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone an instance of the value
|
||||||
|
#[inline]
|
||||||
|
pub fn to_value(&self) -> T
|
||||||
|
where T: Clone
|
||||||
|
{
|
||||||
|
self.0.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume into the value
|
||||||
|
#[inline]
|
||||||
|
pub fn into_value(self) -> T
|
||||||
|
{
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Consume into a recursive 2-tuple of `((value, error), message)`.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_parts(self) -> ((T, io::Error), impl fmt::Display + fmt::Debug + 'a)
|
||||||
|
{
|
||||||
|
((self.0, self.1), self.2)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A reference to the inner OS error
|
||||||
|
#[inline]
|
||||||
|
pub fn error(&self) -> &io::Error
|
||||||
|
{
|
||||||
|
&self.1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to an opaque type that can be formatted into the message
|
||||||
|
#[inline]
|
||||||
|
pub fn message(&self) -> &(impl fmt::Display + fmt::Debug + 'a)
|
||||||
|
{
|
||||||
|
&self.2
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consume an opaque type that can be formatted into the message
|
||||||
|
pub fn into_message(self) -> impl fmt::Display + fmt::Debug + 'a
|
||||||
|
{
|
||||||
|
self.2
|
||||||
|
}
|
||||||
|
/* This doesn't work...
|
||||||
|
/// Render any referenced arguments in the message into a string, reducing the lifetime requirement of the message to `'static`.
|
||||||
|
///
|
||||||
|
/// # Notes
|
||||||
|
/// If `T` is not also `'static`, then the resulting instance will not be `'static` itself. If `T` is not `'static`, use `into_owned()` instead.
|
||||||
|
#[inline]
|
||||||
|
pub fn message_into_owned(self) -> FFIError<'static, T>
|
||||||
|
{
|
||||||
|
FFIError(self.0, self.1, format_args!("{}", self.2.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone any referenced arguments of the message and the value into a non-referential object, reducing the lifetime requirements of the returned instance to `'static`.
|
||||||
|
#[inline]
|
||||||
|
pub fn into_owned(self) -> FFIError<'static, T::Owned>
|
||||||
|
where T: ToOwned,
|
||||||
|
T::Owned: 'static
|
||||||
|
{
|
||||||
|
FFIError(self.0.to_owned(), self.1, format_args!("{}", self.2.to_string()))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> error::Error for FFIError<'a, T>
|
||||||
|
where FFIError<'a, T>: fmt::Display + fmt::Debug
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
|
Some(&self.1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, T: fmt::Debug> fmt::Display for FFIError<'a, T>
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
|
||||||
|
{
|
||||||
|
write!(f, "C call failed (invalid return {:?}): {}", self.0, &self.2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> From<FFIError<'a, T>> for io::Error
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn from(from: FFIError<'a, T>) -> Self
|
||||||
|
{
|
||||||
|
from.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
//! Types for, and operations on file descriptors. Useful for mapping
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Raw file-descriptor for standard input
|
||||||
|
pub const STDIN_FILENO: RawFd = libc::STDIN_FILENO;
|
||||||
|
/// Raw file-descriptor for standard output
|
||||||
|
pub const STDOUT_FILENO: RawFd = libc::STDOUT_FILENO;
|
||||||
|
/// Raw file-descriptor for standard error
|
||||||
|
pub const STDERR_FILENO: RawFd = libc::STDERR_FILENO;
|
||||||
|
|
||||||
|
mod managed;
|
||||||
|
mod unmanaged;
|
||||||
|
|
||||||
|
pub use self::{
|
||||||
|
managed::*,
|
||||||
|
unmanaged::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod memory;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests
|
||||||
|
{
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn std_in_out_err_fileno()
|
||||||
|
{
|
||||||
|
#[inline(always)]
|
||||||
|
fn test_fileno<const EXPECTED: RawFd>(expected_name: &'static str, got: RawFd)
|
||||||
|
{
|
||||||
|
assert_eq!(EXPECTED, got, "{expected_name} invalid: expected: {EXPECTED}, got {got}");
|
||||||
|
}
|
||||||
|
|
||||||
|
test_fileno::<STDIN_FILENO>("STDIN_FILENO", std::io::stdin().as_raw_fd());
|
||||||
|
test_fileno::<STDOUT_FILENO>("STDOUT_FILENO", std::io::stdout().as_raw_fd());
|
||||||
|
test_fileno::<STDERR_FILENO>("STDERR_FILENO", std::io::stderr().as_raw_fd());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
//! Represents a managed `RawFd`.
|
||||||
|
//! This will `close()` its contained `RawFd` on drop.
|
||||||
|
//!
|
||||||
|
//! Can be useful for OS operations on file descriptors without leaking open fds.
|
||||||
|
use super::*;
|
||||||
|
use std::{
|
||||||
|
ops,
|
||||||
|
};
|
||||||
|
use libc::{
|
||||||
|
dup, dup2,
|
||||||
|
close,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct ManagedFD(RawFd);
|
||||||
|
|
||||||
|
impl Clone for ManagedFD {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self(c_try!(dup(self.0) => if |x| x < 0; "dup(): failed to duplicate file descriptor {}", self.0))
|
||||||
|
}
|
||||||
|
fn clone_from(&mut self, source: &Self) {
|
||||||
|
c_try!(dup2(self.0, source.0) => -1; "dup2(): failed to set file descriptor {} to alias {}", self.0, source.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Drop for ManagedFD
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
close(self.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: implement the rest of ManagedFD from `memfd` module in `utf8encode`
|
@ -0,0 +1,12 @@
|
|||||||
|
//! Provides physical in-memory file descriptors.
|
||||||
|
//!
|
||||||
|
//! This can be useful for temporary buffers where a file descriptor is required.
|
||||||
|
//! Huge-pages can also be used for this memory.
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct MemoryFile(ManagedFD);
|
||||||
|
|
||||||
|
mod hugetlb;
|
||||||
|
//TODO: implement `memfd` (and its hugetlb interface, extracted to `hugetlb.rs`) from `utf8encode`.
|
@ -0,0 +1,4 @@
|
|||||||
|
//! Huge-page interface for `MemoryFile`.
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
//TODO: implement `memfd`'s hugetlb interface from `utf8encode` here.
|
@ -0,0 +1,9 @@
|
|||||||
|
//! Provides a wrapper over `RawFd` that does not close it on drop.
|
||||||
|
//! This can be useful for aliasing file descriptors.
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// Represents a `RawFd` but does not provide any ownership of it.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct UnmanagedFD(RawFd);
|
||||||
|
|
||||||
|
//TODO: implement a full version of the temporary struct `UnmanagedFD` from `utf8encode`
|
Loading…
Reference in new issue