use std::any::Any;
use std::collections::HashMap;
use std::ffi::{OsStr, OsString};
use std::io::Result;
use std::os::unix::ffi::OsStrExt;
use std::sync::Arc;
use fuse_backend_rs::abi::fuse_abi;
use fuse_backend_rs::api::filesystem::Entry;
use nydus_storage::device::v5::BlobV5ChunkInfo;
use nydus_storage::device::{BlobChunkInfo, BlobDevice, BlobInfo, BlobIoVec};
use nydus_utils::{digest::RafsDigest, ByteSize};
use super::mock_chunk::MockChunkInfo;
use super::mock_super::CHUNK_SIZE;
use crate::metadata::inode::RafsInodeFlags;
use crate::metadata::layout::v5::{
rafsv5_alloc_bio_vecs, RafsV5BlobTable, RafsV5InodeChunkOps, RafsV5InodeOps,
};
use crate::metadata::{
layout::{XattrName, XattrValue},
Inode, RafsInode, RafsInodeWalkHandler, RafsSuperMeta, RAFS_ATTR_BLOCK_SIZE,
};
use crate::RafsInodeExt;
#[derive(Default, Clone, Debug)]
#[allow(unused)]
pub struct MockInode {
i_ino: Inode,
i_name: OsString,
i_digest: RafsDigest,
i_parent: u64,
i_mode: u32,
i_projid: u32,
i_uid: u32,
i_gid: u32,
i_flags: RafsInodeFlags,
i_size: u64,
i_blocks: u64,
i_nlink: u32,
i_child_idx: u32,
i_child_cnt: u32,
i_blksize: u32,
i_rdev: u32,
i_mtime_nsec: u32,
i_mtime: u64,
i_target: OsString, i_xattr: HashMap<OsString, Vec<u8>>,
i_data: Vec<Arc<MockChunkInfo>>,
i_child: Vec<Arc<MockInode>>,
i_blob_table: Arc<RafsV5BlobTable>,
i_meta: Arc<RafsSuperMeta>,
}
impl MockInode {
pub fn mock(ino: Inode, size: u64, chunks: Vec<Arc<MockChunkInfo>>) -> Self {
Self {
i_ino: ino,
i_size: size,
i_child_cnt: chunks.len() as u32,
i_data: chunks,
i_mode: libc::S_IFREG as u32,
i_blksize: CHUNK_SIZE,
..Default::default()
}
}
}
impl RafsInode for MockInode {
fn validate(&self, _max_inode: Inode, _chunk_size: u64) -> Result<()> {
if self.is_symlink() && self.i_target.is_empty() {
return Err(einval!("invalid inode"));
}
Ok(())
}
#[inline]
fn get_entry(&self) -> Entry {
Entry {
attr: self.get_attr().into(),
inode: self.i_ino,
generation: 0,
attr_flags: 0,
attr_timeout: self.i_meta.attr_timeout,
entry_timeout: self.i_meta.entry_timeout,
}
}
#[inline]
fn get_attr(&self) -> fuse_abi::Attr {
fuse_abi::Attr {
ino: self.i_ino,
size: self.i_size,
blocks: self.i_blocks,
mode: self.i_mode,
nlink: self.i_nlink as u32,
blksize: RAFS_ATTR_BLOCK_SIZE,
rdev: self.i_rdev,
..Default::default()
}
}
fn walk_children_inodes(
&self,
_entry_offset: u64,
_handler: RafsInodeWalkHandler,
) -> Result<()> {
todo!()
}
fn get_symlink(&self) -> Result<OsString> {
if !self.is_symlink() {
Err(einval!("inode is not a symlink"))
} else {
Ok(self.i_target.clone())
}
}
fn get_symlink_size(&self) -> u16 {
if self.is_symlink() {
self.i_target.byte_size() as u16
} else {
0
}
}
fn get_child_by_name(&self, name: &OsStr) -> Result<Arc<dyn RafsInodeExt>> {
let idx = self
.i_child
.binary_search_by(|c| c.i_name.as_os_str().cmp(name))
.map_err(|_| enoent!())?;
Ok(self.i_child[idx].clone())
}
#[inline]
fn get_child_by_index(&self, index: u32) -> Result<Arc<dyn RafsInodeExt>> {
Ok(self.i_child[index as usize].clone())
}
#[inline]
fn get_child_count(&self) -> u32 {
self.i_child_cnt
}
fn get_child_index(&self) -> Result<u32> {
Ok(self.i_child_idx)
}
fn get_chunk_count(&self) -> u32 {
self.get_child_count()
}
fn has_xattr(&self) -> bool {
self.i_flags.contains(RafsInodeFlags::XATTR)
}
#[inline]
fn get_xattr(&self, name: &OsStr) -> Result<Option<XattrValue>> {
Ok(self.i_xattr.get(name).cloned())
}
fn get_xattrs(&self) -> Result<Vec<XattrName>> {
Ok(self
.i_xattr
.keys()
.map(|k| k.as_bytes().to_vec())
.collect::<Vec<XattrName>>())
}
#[inline]
fn is_blkdev(&self) -> bool {
self.i_mode & libc::S_IFMT as u32 == libc::S_IFBLK as u32
}
#[inline]
fn is_chrdev(&self) -> bool {
self.i_mode & libc::S_IFMT as u32 == libc::S_IFCHR as u32
}
#[inline]
fn is_sock(&self) -> bool {
self.i_mode & libc::S_IFMT as u32 == libc::S_IFSOCK as u32
}
#[inline]
fn is_fifo(&self) -> bool {
self.i_mode & libc::S_IFMT as u32 == libc::S_IFIFO as u32
}
fn is_dir(&self) -> bool {
self.i_mode & libc::S_IFMT as u32 == libc::S_IFDIR as u32
}
fn is_symlink(&self) -> bool {
self.i_mode & libc::S_IFMT as u32 == libc::S_IFLNK as u32
}
fn is_reg(&self) -> bool {
self.i_mode & libc::S_IFMT as u32 == libc::S_IFREG as u32
}
fn is_hardlink(&self) -> bool {
!self.is_dir() && self.i_nlink > 1
}
fn collect_descendants_inodes(
&self,
descendants: &mut Vec<Arc<dyn RafsInode>>,
) -> Result<usize> {
if !self.is_dir() {
return Err(enotdir!());
}
let mut child_dirs: Vec<Arc<dyn RafsInode>> = Vec::new();
for child_inode in &self.i_child {
if child_inode.is_dir() {
trace!("Got dir {:?}", child_inode.name());
child_dirs.push(child_inode.clone());
} else {
if child_inode.is_empty_size() {
continue;
}
descendants.push(child_inode.clone());
}
}
for d in child_dirs {
d.collect_descendants_inodes(descendants)?;
}
Ok(0)
}
fn alloc_bio_vecs(
&self,
_device: &BlobDevice,
offset: u64,
size: usize,
user_io: bool,
) -> Result<Vec<BlobIoVec>> {
rafsv5_alloc_bio_vecs(self, offset, size, user_io)
}
fn as_any(&self) -> &dyn Any {
self
}
impl_getter!(ino, i_ino, u64);
impl_getter!(size, i_size, u64);
impl_getter!(rdev, i_rdev, u32);
impl_getter!(projid, i_projid, u32);
}
impl RafsInodeExt for MockInode {
fn name(&self) -> OsString {
self.i_name.clone()
}
fn flags(&self) -> u64 {
self.i_flags.bits()
}
fn get_digest(&self) -> RafsDigest {
self.i_digest
}
fn get_name_size(&self) -> u16 {
self.i_name.byte_size() as u16
}
#[inline]
fn get_chunk_info(&self, idx: u32) -> Result<Arc<dyn BlobChunkInfo>> {
Ok(self.i_data[idx as usize].clone())
}
fn as_inode(&self) -> &dyn RafsInode {
self
}
impl_getter!(parent, i_parent, u64);
}
impl RafsV5InodeChunkOps for MockInode {
fn get_chunk_info_v5(&self, idx: u32) -> Result<Arc<dyn BlobV5ChunkInfo>> {
Ok(self.i_data[idx as usize].clone())
}
}
impl RafsV5InodeOps for MockInode {
fn get_blob_by_index(&self, _idx: u32) -> Result<Arc<BlobInfo>> {
Ok(Arc::new(BlobInfo::default()))
}
fn get_chunk_size(&self) -> u32 {
CHUNK_SIZE
}
fn has_hole(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use nydus_utils::digest::Algorithm;
use crate::metadata::layout::RAFS_V5_ROOT_INODE;
use super::*;
#[test]
fn test_mock_node() {
let size = 20;
let mut chunks = Vec::<Arc<MockChunkInfo>>::new();
let digest = RafsDigest::from_buf("foobar".as_bytes(), Algorithm::Blake3);
let info = MockChunkInfo::mock(0, 1024, 1024, 2048, 1024);
chunks.push(Arc::new(info.clone()));
chunks.push(Arc::new(info));
let mut node = MockInode::mock(13, size, chunks);
node.i_flags = RafsInodeFlags::XATTR;
node.i_mode = libc::S_IFDIR as u32;
node.i_name = "foo".into();
node.i_digest = digest;
node.i_parent = RAFS_V5_ROOT_INODE;
let mut child_node1 = MockInode::mock(14, size, Vec::<Arc<MockChunkInfo>>::new());
child_node1.i_name = OsStr::new("child1").into();
child_node1.i_size = 10;
let mut child_node2 = MockInode::mock(15, size, Vec::<Arc<MockChunkInfo>>::new());
child_node2.i_name = OsStr::new("child2").into();
child_node1.i_size = 20;
node.i_child.push(Arc::new(child_node1));
node.i_child.push(Arc::new(child_node2));
node.i_child_cnt = 2;
node.i_child_idx = 2;
node.i_xattr.insert("attr1".into(), "bar1".into());
node.i_xattr.insert("attr2".into(), "bar2".into());
node.i_xattr.insert("attr3".into(), "bar3".into());
node.i_data.push(Arc::new(MockChunkInfo::default()));
assert!(node.validate(0, 0).is_ok());
assert_eq!(node.ino(), 13);
assert_eq!(node.size(), 20);
assert_eq!(node.rdev(), 0);
assert_eq!(node.projid(), 0);
assert_eq!(node.name(), "foo");
assert_eq!(node.flags(), RafsInodeFlags::XATTR.bits());
assert_eq!(node.get_digest(), digest);
assert_eq!(node.get_name_size(), "foo".len() as u16);
assert!(node.get_chunk_info(0).is_ok());
assert!(node.get_chunk_info_v5(0).is_ok());
assert_eq!(node.parent(), RAFS_V5_ROOT_INODE);
assert!(node.get_blob_by_index(0).is_ok());
assert_eq!(node.get_chunk_size(), CHUNK_SIZE);
assert!(!node.has_hole());
let ent = node.get_entry();
assert_eq!(ent.inode, node.ino());
assert_eq!(ent.attr_timeout, node.i_meta.attr_timeout);
assert_eq!(ent.entry_timeout, node.i_meta.entry_timeout);
assert_eq!(ent.attr, node.get_attr().into());
assert!(node.get_symlink().is_err());
assert_eq!(node.get_symlink_size(), 0 as u16);
assert!(node.get_child_by_name(OsStr::new("child1")).is_ok());
assert!(node.get_child_by_index(0).is_ok());
assert!(node.get_child_by_index(1).is_ok());
assert_eq!(node.get_child_count(), 2 as u32);
assert_eq!(node.get_child_index().unwrap(), 2 as u32);
assert_eq!(node.get_chunk_count(), 2 as u32);
assert!(node.has_xattr());
assert_eq!(
node.get_xattr(OsStr::new("attr2")).unwrap().unwrap(),
"bar2".as_bytes()
);
assert_eq!(node.get_xattrs().unwrap().len(), 3);
assert!(!node.is_blkdev());
assert!(!node.is_chrdev());
assert!(!node.is_sock());
assert!(!node.is_fifo());
assert!(node.is_dir());
assert!(!node.is_symlink());
assert!(!node.is_reg());
assert!(!node.is_hardlink());
let mut inodes = Vec::<Arc<dyn RafsInode>>::new();
node.collect_descendants_inodes(&mut inodes).unwrap();
assert_eq!(inodes.len(), 2);
}
}