use std::{
collections::HashMap,
fmt,
hash::BuildHasherDefault,
os::fd::{FromRawFd, OwnedFd, RawFd},
sync::Arc,
};
use nohash::NoHashHasher;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GuestFd(pub i32);
impl fmt::Display for GuestFd {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "GuestFd({})", self.0)
}
}
impl From<u32> for GuestFd {
fn from(x: u32) -> Self {
Self(x.try_into().unwrap())
}
}
impl From<&u32> for GuestFd {
fn from(x: &u32) -> Self {
Self((*x).try_into().unwrap())
}
}
impl GuestFd {
pub(crate) fn is_standard(self) -> bool {
self.0 < 3
}
fn get(self) -> u32 {
self.0.try_into().unwrap()
}
}
#[derive(Clone)]
pub enum FdData {
Raw(RawFd),
Virtual { data: Arc<[u8]>, offset: u64 },
}
impl fmt::Debug for FdData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FdData::Raw(r) => write!(f, "Raw({r})"),
FdData::Virtual { data, offset } => {
let data_snip = &data[..core::cmp::min(10, data.len())];
write!(f, "Virtual({data_snip:?} @ {offset})")
}
}
}
}
impl FromRawFd for FdData {
#[inline(always)]
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self::Raw(fd)
}
}
#[derive(Debug)]
pub struct UhyveFileDescriptorLayer {
fds: HashMap<u32, FdData, BuildHasherDefault<NoHashHasher<u32>>>,
next_fd: u32,
}
impl Default for UhyveFileDescriptorLayer {
fn default() -> Self {
let mut fds = HashMap::with_capacity_and_hasher(3, BuildHasherDefault::default());
fds.insert(0, FdData::Raw(0));
fds.insert(1, FdData::Raw(1));
fds.insert(2, FdData::Raw(2));
Self { fds, next_fd: 3 }
}
}
impl UhyveFileDescriptorLayer {
pub fn insert(&mut self, data: FdData) -> Option<GuestFd> {
if let FdData::Raw(r) = data
&& r < 3
{
warn!("Guest attempted to insert negative/standard stream {r}, ignoring...");
return None;
}
debug!("Adding fd {data:?} to fdset: {self}");
let ret = self.next_fd;
assert!(self.fds.insert(ret, data).is_none());
self.next_fd = ret.checked_add(1).unwrap();
debug!("=> {ret}");
Some(ret.into())
}
pub fn remove(&mut self, fd: GuestFd) -> Option<FdData> {
debug!("Trying to remove {fd} from fdset: {self}");
let ret = if fd.is_standard() {
None
} else {
self.fds.remove(&fd.get())
};
if ret.is_none() {
warn!("Guest attempted to remove invalid {fd}, ignoring...")
}
ret
}
pub fn get_mut(&mut self, fd: GuestFd) -> Option<&mut FdData> {
self.fds.get_mut(&fd.get())
}
}
impl Drop for UhyveFileDescriptorLayer {
fn drop(&mut self) {
for (fd, fdata) in self.fds.iter() {
if !GuestFd::from(fd).is_standard()
&& let FdData::Raw(rfd) = fdata
{
unsafe { OwnedFd::from_raw_fd(*rfd) };
}
}
}
}
impl fmt::Display for UhyveFileDescriptorLayer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.fds.iter()).finish()
}
}