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