Started adding `memfile` feature: Use a `memfd_create()`d in-memory temporary file.

Fortune for collect's current commit: Curse − 凶
safe-memfd
Avril 2 years ago
parent 80a2d65262
commit 65c297b228
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -6,9 +6,10 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["jemalloc", "logging", "tracing/release_max_level_warn"]
default = ["jemalloc", "memfile", "logging", "tracing/release_max_level_warn"]
# TODO: mmap, memfd_create() ver
memfile = []
# bytes: use `bytes` crate for collecting instead of `std::vec`

@ -217,7 +217,7 @@ impl MutBuffer for bytes::BytesMut
fn freeze(self) -> Self::Frozen {
bytes::BytesMut::freeze(self)
}
//TODO: XXX: Impl copy_from_slice() as is done in impl for Vec<u8>
//TODO: XXX: Impl copy_from_slice() as is done in impl for Vec<u8>? Or change how `.writer()` works for us to return the BytesMut writer which seems more efficient.
/*#[instrument]
fn copy_from_slice(&mut self, st: usize, buf: &[u8]) -> usize
{

@ -51,6 +51,8 @@ use color_eyre::{
mod buffers;
use buffers::prelude::*;
#[cfg(feature="memfile")] mod memfile;
#[cfg(feature="bytes")]
use bytes::{
Buf,

@ -0,0 +1,224 @@
//! Memory file handling
use super::*;
use std::os::unix::io::*;
use std::{
mem,
ops,
fs,
io,
path::Path,
borrow::{
Borrow,
BorrowMut,
},
};
mod fd;
pub mod error;
#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct RawFile(fd::RawFileDescriptor);
impl RawFile
{
/// Get the raw fd for this raw file
#[inline(always)]
pub const fn fileno(&self) -> RawFd
{
self.0.get()
}
#[inline(always)]
pub fn into_fileno(self) -> RawFd
{
self.into_raw_fd()
}
#[inline(always)]
pub unsafe fn from_fileno(fd: RawFd) -> Self
{
Self::from_raw_fd(fd)
}
/// Attempt to link this instance's fd to another container over an fd
///
/// This is a safe wrapper around `dup2()`, as `clone()` is a safe wrapper around `dup()`.
///
/// # Note
/// If `T` is a buffered container (e.g. `std::fs::File`), make sure the buffer is flushed *before* calling this method on it, or the buffered data will be lost.
pub fn try_link<'o, T: ?Sized>(&self, other: &'o mut T) -> Result<&'o mut T, error::DuplicateError>
where T: AsRawFd
{
if unsafe {
libc::dup2(self.fileno(), other.as_raw_fd())
} < 0 {
Err(error::DuplicateError::new_dup2(self, other))
} else {
Ok(other)
}
}
/// Consume a managed file into a raw file, attempting to synchronise it first.
///
/// # Note
/// This method attempts to sync the file's data.
/// To also attempt to sync the file's metadata, set `metadata` to true.
///
/// # Returns
/// If the sync should fail, the original file is returned, along with the error from the sync.
#[inline(always)]
pub fn try_from_file_synced(file: fs::File, metadata: bool) -> Result<Self, (fs::File, io::Error)>
{
match if metadata {
file.sync_all()
} else {
file.sync_data()
} {
Ok(()) => unsafe {
Ok(Self::from_raw_fd(file.into_raw_fd()))
},
Err(ioe) => Err((file, ioe))
}
}
/// Consume a managed fd type into a raw file
#[inline(always)]
pub fn from_file(file: impl IntoRawFd) -> Self
{
unsafe {
Self::from_raw_fd(file.into_raw_fd())
}
}
/// Consume into a managed file
#[inline(always)]
pub fn into_file(self) -> fs::File
{
unsafe {
fs::File::from_raw_fd(self.into_raw_fd())
}
}
/// Attempt to open a new raw file with these options
#[inline]
pub fn open(path: impl AsRef<Path>, opt: impl Borrow<fs::OpenOptions>) -> io::Result<Self>
{
opt.borrow().open(path).map(Into::into)
}
}
impl io::Write for RawFile
{
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match unsafe {
libc::write(self.fileno(), buf.as_ptr() as *const _, buf.len())
} {
-1 => Err(io::Error::last_os_error()),
wr => Ok(wr as usize)
}
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
// Not buffered
Ok(())
}
#[inline]
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
// SAFETY: IoSlice is guaranteed to be ABI-compatible with `struct iovec`
match unsafe {
libc::writev(self.fileno(), bufs.as_ptr() as *const _, bufs.len() as i32)
} {
-1 => Err(io::Error::last_os_error()),
wr => Ok(wr as usize)
}
}
}
impl io::Read for RawFile
{
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match unsafe {
libc::read(self.fileno(), buf.as_mut_ptr() as *mut _, buf.len())
} {
-1 => Err(io::Error::last_os_error()),
wr => Ok(wr as usize)
}
}
#[inline]
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
// SAFETY: IoSlice is guaranteed to be ABI-compatible with `struct iovec`
match unsafe {
libc::readv(self.fileno(), bufs.as_mut_ptr() as *mut _, bufs.len() as i32)
} {
-1 => Err(io::Error::last_os_error()),
wr => Ok(wr as usize)
}
}
}
impl From<fs::File> for RawFile
{
#[inline]
fn from(from: fs::File) -> Self
{
Self::from_file(from)
}
}
impl From<RawFile> for fs::File
{
#[inline]
fn from(from: RawFile) -> Self
{
from.into_file()
}
}
impl Clone for RawFile
{
#[inline]
fn clone(&self) -> Self {
unsafe { Self::from_raw_fd(libc::dup(self.0.get())) }
}
}
impl ops::Drop for RawFile
{
#[inline]
fn drop(&mut self) {
unsafe {
libc::close(self.0.get());
}
}
}
impl AsRawFd for RawFile
{
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.0.get()
}
}
impl FromRawFd for RawFile
{
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self(fd::RawFileDescriptor::new(fd))
}
}
impl IntoRawFd for RawFile
{
#[inline]
fn into_raw_fd(self) -> RawFd {
let fd = self.0.get();
mem::forget(self); // prevent close
fd
}
}

@ -0,0 +1,112 @@
//! Errors
use super::*;
use std::{fmt, error};
/// The kind of duplicate fd syscall that was attempted
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
pub enum DuplicateKind
{
/// A `dup()` call failed
Duplicate,
/// A `dup2(fd)` call failed
Link(RawFd),
}
/// Error returned when duplicating a file descriptor fails
#[derive(Debug)]
pub struct DuplicateError {
pub(super) from: RawFd,
pub(super) to: DuplicateKind,
pub(super) inner: io::Error,
}
impl DuplicateError
{
#[inline(always)]
pub fn new_dup<T: ?Sized + AsRawFd>(from: &T) -> Self
{
Self{
inner: io::Error::last_os_error(),
from: from.as_raw_fd(),
to: DuplicateKind::Duplicate,
}
}
#[inline(always)]
pub fn new_dup2<T: ?Sized + AsRawFd, U: ?Sized+ AsRawFd>(from: &T, to: &U) -> Self
{
Self {
inner: io::Error::last_os_error(),
from: from.as_raw_fd(),
to: DuplicateKind::Link(to.as_raw_fd()),
}
}
#[inline]
pub fn new<T: ?Sized + AsRawFd>(from: &T, kind: DuplicateKind, reason: impl Into<io::Error>) -> Self
{
Self {
from: from.as_raw_fd(),
to: kind,
inner: reason.into()
}
}
#[inline(always)]
pub fn reason(&self) -> &io::Error
{
&self.inner
}
#[inline(always)]
pub fn kind(&self) -> &DuplicateKind
{
&self.to
}
#[inline(always)]
pub fn source_fileno(&self) -> RawFd
{
self.from
}
}
impl fmt::Display for DuplicateKind
{
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Duplicate => f.write_str("dup()"),
Self::Link(fd) => write!(f, "dup2({fd})"),
}
}
}
impl error::Error for DuplicateError
{
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(&self.inner)
}
}
impl std::borrow::Borrow<io::Error> for DuplicateError
{
#[inline]
fn borrow(&self) -> &io::Error
{
self.reason()
}
}
impl fmt::Display for DuplicateError
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "failed to {} fd {}", self.to, self.from)
}
}

@ -0,0 +1,198 @@
//! Managing raw `fd`s
use super::*;
use std::num::NonZeroU32;
use libc::{
c_int,
};
use std::{
fmt, error
};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
#[repr(transparent)]
struct NonNegativeI32(NonZeroU32);
impl NonNegativeI32
{
pub const MASK: u32 = c_int::MIN as u32; //0b10000000_00000000_00000000_00000000;
#[inline(always)]
pub const fn new(from: i32) -> Option<Self>
{
if from < 0 {
None
} else {
Some(unsafe {
Self::new_unchecked(from)
})
}
}
#[inline(always)]
pub const unsafe fn new_unchecked(from: i32) -> Self
{
Self(NonZeroU32::new_unchecked( (from as u32) | Self::MASK ))
}
#[inline(always)]
pub const fn get(self) -> i32
{
(self.0.get() & (!Self::MASK)) as i32
}
}
impl PartialEq<i32> for NonNegativeI32
{
#[inline]
fn eq(&self, other: &i32) -> bool
{
self.get() == *other
}
}
impl PartialOrd<i32> for NonNegativeI32
{
#[inline]
fn partial_cmp(&self, other: &i32) -> Option<std::cmp::Ordering> {
self.get().partial_cmp(other)
}
}
impl Default for NonNegativeI32
{
#[inline(always)]
fn default() -> Self
{
unsafe {
Self::new_unchecked(0)
}
}
}
impl From<NonNegativeI32> for i32
{
#[inline(always)]
fn from(from: NonNegativeI32) -> Self
{
from.get()
}
}
impl TryFrom<i32> for NonNegativeI32
{
type Error = std::num::TryFromIntError;
#[inline(always)]
fn try_from(from: i32) -> Result<Self, Self::Error>
{
NonZeroU32::try_from((!from as u32) & Self::MASK)?;
debug_assert!(from >= 0, "Bad check");
unsafe {
Ok(Self::new_unchecked(from))
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BadFDError(());
impl error::Error for BadFDError{}
impl fmt::Display for BadFDError
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
f.write_str("invalid file descriptor")
}
}
pub type FileNo = RawFd;
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct RawFileDescriptor(NonNegativeI32);
impl RawFileDescriptor
{
pub const STDIN: Self = Self(unsafe { NonNegativeI32::new_unchecked(0) });
pub const STDOUT: Self = Self(unsafe { NonNegativeI32::new_unchecked(1) });
pub const STDERR: Self = Self(unsafe { NonNegativeI32::new_unchecked(2) });
#[inline(always)]
pub fn try_new(fd: FileNo) -> Result<Self, BadFDError>
{
NonNegativeI32::new(fd).ok_or(BadFDError(())).map(Self)
}
#[inline]
pub fn new(fd: FileNo) -> Self
{
Self::try_new(fd).expect("Invalid fileno")
}
#[inline(always)]
pub const unsafe fn new_unchecked(fd: FileNo) -> Self
{
Self(NonNegativeI32::new_unchecked(fd))
}
#[inline(always)]
pub const fn get(&self) -> FileNo
{
self.0.get()
}
}
impl PartialEq<FileNo> for RawFileDescriptor
{
#[inline]
fn eq(&self, other: &FileNo) -> bool
{
self.get() == *other
}
}
impl PartialOrd<FileNo> for RawFileDescriptor
{
#[inline]
fn partial_cmp(&self, other: &FileNo) -> Option<std::cmp::Ordering> {
self.get().partial_cmp(other)
}
}
impl From<NonNegativeI32> for RawFileDescriptor
{
#[inline(always)]
fn from(from: NonNegativeI32) -> Self
{
Self(from)
}
}
impl TryFrom<FileNo> for RawFileDescriptor
{
type Error = BadFDError;
#[inline(always)]
fn try_from(from: FileNo) -> Result<Self, Self::Error>
{
Self::try_new(from)
}
}
impl From<RawFileDescriptor> for FileNo
{
#[inline(always)]
fn from(from: RawFileDescriptor) -> Self
{
from.get()
}
}
impl AsRawFd for RawFileDescriptor
{
fn as_raw_fd(&self) -> RawFd {
self.get()
}
}
Loading…
Cancel
Save