#![allow(clippy::useless_conversion)]
use std::collections::BTreeMap;
use std::sync::Arc;
#[cfg(target_os = "linux")]
use super::InodeHandle;
#[cfg(target_os = "linux")]
use super::file_handle::FileHandle;
use super::statx::StatExt;
use super::{Inode, InodeData};
#[derive(Clone, Copy, Default, PartialOrd, Ord, PartialEq, Eq, Debug, Hash)]
pub struct InodeId {
#[cfg(target_os = "linux")]
pub ino: libc::ino64_t,
#[cfg(target_os = "macos")]
pub ino: libc::ino_t,
pub dev: libc::dev_t,
pub mnt: u64,
}
impl InodeId {
#[inline]
pub(super) fn from_stat(st: &StatExt) -> Self {
InodeId {
ino: st.st.st_ino,
dev: st.st.st_dev,
mnt: st.mnt_id,
}
}
}
#[derive(Default)]
pub struct InodeStore {
data: BTreeMap<Inode, Arc<InodeData>>, by_id: BTreeMap<InodeId, Inode>, #[cfg(target_os = "linux")]
by_handle: BTreeMap<Arc<FileHandle>, Inode>,
}
impl InodeStore {
pub fn insert(&mut self, data: Arc<InodeData>) {
self.by_id.insert(data.id, data.inode);
#[cfg(target_os = "linux")]
if let InodeHandle::Handle(handle) = &data.handle {
self.by_handle
.insert(handle.file_handle().clone(), data.inode);
}
self.data.insert(data.inode, data);
}
#[allow(unused)]
pub fn remove(&mut self, inode: &Inode, remove_data_only: bool) -> Option<Arc<InodeData>> {
let data = self.data.remove(inode);
if remove_data_only {
return data;
}
if let Some(data) = data.as_ref() {
#[cfg(target_os = "linux")]
if let InodeHandle::Handle(handle) = &data.handle {
self.by_handle.remove(handle.file_handle());
}
self.by_id.remove(&data.id);
}
data
}
pub fn clear(&mut self) {
self.data.clear();
#[cfg(target_os = "linux")]
self.by_handle.clear();
self.by_id.clear();
}
pub fn get(&self, inode: &Inode) -> Option<&Arc<InodeData>> {
self.data.get(inode)
}
pub fn get_by_id(&self, id: &InodeId) -> Option<&Arc<InodeData>> {
let inode = self.inode_by_id(id)?;
self.get(inode)
}
#[cfg(target_os = "linux")]
pub fn get_by_handle(&self, handle: &FileHandle) -> Option<&Arc<InodeData>> {
let inode = self.inode_by_handle(handle)?;
self.get(inode)
}
pub fn inode_by_id(&self, id: &InodeId) -> Option<&Inode> {
self.by_id.get(id)
}
#[cfg(target_os = "linux")]
pub fn inode_by_handle(&self, handle: &FileHandle) -> Option<&Inode> {
self.by_handle.get(handle)
}
#[cfg_attr(not(target_os = "macos"), allow(dead_code))]
pub fn iter(&self) -> impl Iterator<Item = (&Inode, &Arc<InodeData>)> {
self.data.iter()
}
}
#[cfg(test)]
mod test {
#[cfg(target_os = "linux")]
use super::super::*;
#[cfg(target_os = "linux")]
use super::*;
#[cfg(target_os = "linux")]
use vmm_sys_util::tempfile::TempFile;
#[test]
#[cfg(target_os = "linux")]
fn test_inode_store() {
let mut m = InodeStore::default();
let tmpfile1 = TempFile::new().unwrap();
let tmpfile2 = TempFile::new().unwrap();
let inode1: Inode = 3;
let inode2: Inode = 4;
let inode_stat1 = statx::statx(tmpfile1.as_file(), None).unwrap();
let inode_stat2 = statx::statx(tmpfile2.as_file(), None).unwrap();
let id1 = InodeId::from_stat(&inode_stat1);
let id2 = InodeId::from_stat(&inode_stat2);
let file_or_handle1 = InodeHandle::File(tmpfile1.into_file());
let file_or_handle2 = InodeHandle::File(tmpfile2.into_file());
let data1 = InodeData::new(
inode1,
file_or_handle1,
2,
id1,
inode_stat1.st.st_mode.into(),
inode_stat1.btime.unwrap(),
);
let data2 = InodeData::new(
inode2,
file_or_handle2,
2,
id2,
inode_stat2.st.st_mode.into(),
inode_stat2.btime.unwrap(),
);
let data1 = Arc::new(data1);
let data2 = Arc::new(data2);
m.insert(data1.clone());
assert!(m.get(&1001).is_none());
assert!(m.get_by_id(&InodeId::default()).is_none());
assert!(m.get_by_handle(&FileHandle::default()).is_none());
m.insert(data2.clone());
assert!(m.get(&1).is_none());
assert!(m.get_by_id(&InodeId::default()).is_none());
assert!(m.get_by_handle(&FileHandle::default()).is_none());
assert!(m.remove(&1, false).is_none());
m.clear();
assert!(m.get(&1).is_none());
assert!(m.get_by_id(&InodeId::default()).is_none());
assert!(m.get_by_handle(&FileHandle::default()).is_none());
assert!(m.get(&inode1).is_none());
assert!(m.get_by_id(&id1).is_none());
assert!(m.get(&inode2).is_none());
assert!(m.get_by_id(&id2).is_none());
}
}