In order to use a filesystem as a lower filesystem in an overlay, it must implement statfs. Signed-off-by: Ariel Miculas <amiculas@xxxxxxxxx> --- fs/puzzlefs/puzzle/inode.rs | 26 ++++++++++++++++++++++++-- fs/puzzlefs/puzzlefs.rs | 21 +++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/fs/puzzlefs/puzzle/inode.rs b/fs/puzzlefs/puzzle/inode.rs index 318edbdc5163..a34f1064b632 100644 --- a/fs/puzzlefs/puzzle/inode.rs +++ b/fs/puzzlefs/puzzle/inode.rs @@ -14,6 +14,8 @@ pub(crate) struct PuzzleFS { pub(crate) oci: Image, layers: Vec<format::MetadataBlob>, + pub(crate) total_inodes: u64, + pub(crate) total_block_size: u64, } impl PuzzleFS { @@ -22,13 +24,33 @@ pub(crate) fn open(oci_root_dir: &CStr, rootfs_path: &CStr) -> Result<PuzzleFS> let oci = Image::open(vfs_mount)?; let rootfs = oci.open_rootfs_blob(rootfs_path)?; + let mut total_block_size = 0; + let mut total_inodes: u64 = 0; let mut layers = Vec::new(); for md in rootfs.metadatas.iter() { let digest = Digest::try_from(md)?; - layers.push(oci.open_metadata_blob(&digest)?, GFP_KERNEL)?; + let layer = oci.open_metadata_blob(&digest)?; + + // This may take up too much time, but we need to implement statfs if we want to use + // puzzlefs as a lower filesystem in overlayfs + let inodes = layer.get_inode_vector()?; + total_inodes += u64::from(inodes.len()); + for inode_number in 0..inodes.len() { + let inode = Inode::from_capnp(inodes.get(inode_number))?; + if let InodeMode::File { chunks } = inode.mode { + total_block_size += chunks.iter().map(|chunk| chunk.len).sum::<u64>(); + } + } + + layers.push(layer, GFP_KERNEL)?; } - Ok(PuzzleFS { oci, layers }) + Ok(PuzzleFS { + oci, + layers, + total_inodes, + total_block_size, + }) } pub(crate) fn find_inode(&self, ino: u64) -> Result<Inode> { diff --git a/fs/puzzlefs/puzzlefs.rs b/fs/puzzlefs/puzzlefs.rs index 932f31917992..633f60983849 100644 --- a/fs/puzzlefs/puzzlefs.rs +++ b/fs/puzzlefs/puzzlefs.rs @@ -4,7 +4,7 @@ use kernel::fs::{ address_space, dentry, dentry::DEntry, file, file::DirEntryType, file::File, inode, - inode::INode, sb, Offset, + inode::INode, sb, Offset, Stat, }; use kernel::prelude::*; use kernel::types::{ARef, Either, Locked}; @@ -23,6 +23,10 @@ license: "GPL", } +const PUZZLEFS_BSIZE: u64 = 1 << PUZZLEFS_BSIZE_BITS; +const PUZZLEFS_BSIZE_BITS: u8 = 12; +const PUZZLEFS_MAGIC: usize = 0x7a7a7570; + fn mode_to_fs_type(inode: &Inode) -> Result<DirEntryType> { Ok(match inode.mode { InodeMode::File { .. } => DirEntryType::Reg, @@ -156,7 +160,7 @@ fn fill_super( ); let puzzlefs = puzzlefs?; - sb.set_magic(0x7a7a7570); + sb.set_magic(PUZZLEFS_MAGIC); Ok(Box::new(puzzlefs, GFP_KERNEL)?) } @@ -194,6 +198,19 @@ fn read_xattr( } Err(ENODATA) } + + fn statfs(dentry: &DEntry<Self>) -> Result<Stat> { + let puzzlefs = dentry.super_block().data(); + + Ok(Stat { + magic: PUZZLEFS_MAGIC, + namelen: isize::MAX, + bsize: PUZZLEFS_BSIZE as _, + // Round total_block_size up + blocks: (puzzlefs.total_block_size + PUZZLEFS_BSIZE - 1) / PUZZLEFS_BSIZE, + files: puzzlefs.total_inodes, + }) + } } #[vtable] -- 2.34.1