Skip to content

Commit

Permalink
passthroughfs: add unit test for inode store
Browse files Browse the repository at this point in the history
Signed-off-by: Eryu Guan <[email protected]>
  • Loading branch information
eryugey authored and bergwolf committed Apr 19, 2023
1 parent 3c20438 commit 4a4887f
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/passthrough/file_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ pub struct FileHandle {
handle: CFileHandle,
}

impl Default for FileHandle {
fn default() -> Self {
Self {
mnt_id: 0,
handle: CFileHandle::new(0),
}
}
}

extern "C" {
fn name_to_handle_at(
dirfd: libc::c_int,
Expand Down
131 changes: 131 additions & 0 deletions src/passthrough/inode_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,134 @@ impl InodeStore {
self.by_handle.get(handle)
}
}

#[cfg(test)]
mod test {
use super::super::*;
use super::*;

use std::ffi::CStr;
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::atomic::Ordering;
use vmm_sys_util::tempfile::TempFile;

impl PartialEq for InodeData {
fn eq(&self, other: &Self) -> bool {
if self.inode != other.inode
|| self.altkey != other.altkey
|| self.mode != other.mode
|| self.refcount.load(Ordering::Relaxed) != other.refcount.load(Ordering::Relaxed)
{
return false;
}

match (&self.file_or_handle, &other.file_or_handle) {
(FileOrHandle::File(f1), FileOrHandle::File(f2)) => {
f1.as_raw_fd() == f2.as_raw_fd()
}
(FileOrHandle::Handle(h1), FileOrHandle::Handle(h2)) => h1 == h2,
_ => false,
}
}
}

fn stat_fd(fd: RawFd) -> io::Result<libc::stat64> {
let mut st = MaybeUninit::<libc::stat64>::zeroed();
let null_path = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };

// Safe because the kernel will only write data in `st` and we check the return value.
let res = unsafe {
libc::fstatat64(
fd,
null_path.as_ptr(),
st.as_mut_ptr(),
libc::AT_EMPTY_PATH | libc::AT_SYMLINK_NOFOLLOW,
)
};
if res >= 0 {
// Safe because the kernel guarantees that the struct is now fully initialized.
Ok(unsafe { st.assume_init() })
} else {
Err(io::Error::last_os_error())
}
}

#[test]
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 = InodeStat {
stat: stat_fd(tmpfile1.as_file().as_raw_fd()).unwrap(),
mnt_id: 0,
};
let inode_stat2 = InodeStat {
stat: stat_fd(tmpfile2.as_file().as_raw_fd()).unwrap(),
mnt_id: 0,
};
let ids1 = InodeAltKey::ids_from_stat(&inode_stat1);
let ids2 = InodeAltKey::ids_from_stat(&inode_stat2);
let file_or_handle1 = FileOrHandle::File(tmpfile1.into_file());
let file_or_handle2 = FileOrHandle::File(tmpfile2.into_file());
let data1 = InodeData::new(
inode1,
file_or_handle1,
2,
ids1,
inode_stat1.get_stat().st_mode,
);
let data2 = InodeData::new(
inode2,
file_or_handle2,
2,
ids2,
inode_stat2.get_stat().st_mode,
);
let data1 = Arc::new(data1);
let data2 = Arc::new(data2);

m.insert(data1.clone());

// get not present key, expect none
assert!(m.get(&1).is_none());

// get just inserted value by key, by id, by handle
assert!(m.get_by_ids(&InodeAltKey::default()).is_none());
assert!(m.get_by_handle(&FileHandle::default()).is_none());
assert_eq!(m.get(&inode1).unwrap(), &data1);
assert_eq!(m.get_by_ids(&ids1).unwrap(), &data1);

// insert another value, and check again
m.insert(data2.clone());
assert!(m.get(&1).is_none());
assert!(m.get_by_ids(&InodeAltKey::default()).is_none());
assert!(m.get_by_handle(&FileHandle::default()).is_none());
assert_eq!(m.get(&inode1).unwrap(), &data1);
assert_eq!(m.get_by_ids(&ids1).unwrap(), &data1);
assert_eq!(m.get(&inode2).unwrap(), &data2);
assert_eq!(m.get_by_ids(&ids2).unwrap(), &data2);

// remove non-present key
assert!(m.remove(&1).is_none());

// remove present key, return its value
assert_eq!(m.remove(&inode1).unwrap(), data1.clone());
assert!(m.get(&inode1).is_none());
assert!(m.get_by_ids(&ids1).is_none());
assert_eq!(m.get(&inode2).unwrap(), &data2);
assert_eq!(m.get_by_ids(&ids2).unwrap(), &data2);

// clear the map
m.clear();
assert!(m.get(&1).is_none());
assert!(m.get_by_ids(&InodeAltKey::default()).is_none());
assert!(m.get_by_handle(&FileHandle::default()).is_none());
assert!(m.get(&inode1).is_none());
assert!(m.get_by_ids(&ids1).is_none());
assert!(m.get(&inode2).is_none());
assert!(m.get_by_ids(&ids2).is_none());
}
}
2 changes: 2 additions & 0 deletions src/passthrough/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ impl InodeAltKey {
}
}

#[derive(Debug)]
enum FileOrHandle {
File(File),
Handle(Arc<FileHandle>),
Expand Down Expand Up @@ -127,6 +128,7 @@ impl AsRawFd for InodeFile<'_> {
}

/// Represents an inode in `PassthroughFs`.
#[derive(Debug)]
pub struct InodeData {
inode: Inode,
// Most of these aren't actually files but ¯\_(ツ)_/¯.
Expand Down

0 comments on commit 4a4887f

Please sign in to comment.