Fortune for collect's current commit: Curse − 凶safe-memfd
parent
80a2d65262
commit
65c297b228
@ -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…
Reference in new issue