Implement block mapping in rust and implement map iterators over Inode which will be used in data access. Signed-off-by: Yiyang Wu <toolmanp@xxxxxxx> --- fs/erofs/rust/erofs_sys/map.rs | 54 +++++++++++ fs/erofs/rust/erofs_sys/superblock.rs | 129 ++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/fs/erofs/rust/erofs_sys/map.rs b/fs/erofs/rust/erofs_sys/map.rs index 757e8083c8f1..f56f31cefcd5 100644 --- a/fs/erofs/rust/erofs_sys/map.rs +++ b/fs/erofs/rust/erofs_sys/map.rs @@ -1,7 +1,10 @@ // Copyright 2024 Yiyang Wu // SPDX-License-Identifier: MIT or GPL-2.0-or-later +use super::inode::*; +use super::superblock::*; use super::*; + pub(crate) const MAP_MAPPED: u32 = 0x0001; pub(crate) const MAP_META: u32 = 0x0002; pub(crate) const MAP_ENCODED: u32 = 0x0004; @@ -43,3 +46,54 @@ fn from(value: MapType) -> Self { } pub(crate) type MapResult = PosixResult<Map>; + +/// Iterates over the data map represented by an inode. +pub(crate) struct MapIter<'a, 'b, FS, I> +where + FS: FileSystem<I>, + I: Inode, +{ + fs: &'a FS, + inode: &'b I, + offset: Off, + len: Off, +} + +impl<'a, 'b, FS, I> MapIter<'a, 'b, FS, I> +where + FS: FileSystem<I>, + I: Inode, +{ + pub(crate) fn new(fs: &'a FS, inode: &'b I, offset: Off) -> Self { + Self { + fs, + inode, + offset, + len: inode.info().file_size(), + } + } +} + +impl<'a, 'b, FS, I> Iterator for MapIter<'a, 'b, FS, I> +where + FS: FileSystem<I>, + I: Inode, +{ + type Item = MapResult; + fn next(&mut self) -> Option<Self::Item> { + if self.offset >= self.len { + None + } else { + let result = self.fs.map(self.inode, self.offset); + match result { + Ok(m) => { + let accessor = self.fs.superblock().blk_access(m.physical.start); + let len = m.physical.len.min(accessor.len); + self.offset += len; + Some(Ok(m)) + } + Err(e) => Some(Err(e)), + } + } + } +} diff --git a/fs/erofs/rust/erofs_sys/superblock.rs b/fs/erofs/rust/erofs_sys/superblock.rs index 940ab0b03a26..fc6b3cb00b18 100644 --- a/fs/erofs/rust/erofs_sys/superblock.rs +++ b/fs/erofs/rust/erofs_sys/superblock.rs @@ -8,8 +8,11 @@ use super::data::*; use super::devices::*; use super::inode::*; +use super::map::*; use super::*; +use crate::round; + /// The ondisk superblock structure. #[derive(Debug, Clone, Copy, Default)] #[repr(C)] @@ -135,6 +138,10 @@ pub(crate) fn blk_round_up(&self, addr: Off) -> Blk { pub(crate) fn iloc(&self, nid: Nid) -> Off { self.blkpos(self.meta_blkaddr) + ((nid as Off) << (5 as Off)) } + pub(crate) fn chunk_access(&self, format: ChunkFormat, address: Off) -> Accessor { + let chunkbits = format.chunkbits() + self.blkszbits as u16; + Accessor::new(address, chunkbits as Off) + } } pub(crate) trait FileSystem<I> @@ -145,6 +152,128 @@ pub(crate) trait FileSystem<I> fn backend(&self) -> &dyn Backend; fn as_filesystem(&self) -> &dyn FileSystem<I>; fn device_info(&self) -> &DeviceInfo; + fn flatmap(&self, inode: &I, offset: Off, inline: bool) -> MapResult { + let sb = self.superblock(); + let nblocks = sb.blk_round_up(inode.info().file_size()); + let blkaddr = match inode.info().spec() { + Spec::RawBlk(blkaddr) => Ok(blkaddr), + _ => Err(EUCLEAN), + }?; + + let lastblk = if inline { nblocks - 1 } else { nblocks }; + if offset < sb.blkpos(lastblk) { + let len = inode.info().file_size().min(sb.blkpos(lastblk)) - offset; + Ok(Map { + logical: Segment { start: offset, len }, + physical: Segment { + start: sb.blkpos(blkaddr) + offset, + len, + }, + algorithm_format: 0, + device_id: 0, + map_type: MapType::Normal, + }) + } else if inline { + let len = inode.info().file_size() - offset; + let accessor = sb.blk_access(offset); + Ok(Map { + logical: Segment { start: offset, len }, + physical: Segment { + start: sb.iloc(inode.nid()) + + inode.info().inode_size() + + inode.info().xattr_size() + + accessor.off, + len, + }, + algorithm_format: 0, + device_id: 0, + map_type: MapType::Meta, + }) + } else { + Err(EUCLEAN) + } + } + + fn chunk_map(&self, inode: &I, offset: Off) -> MapResult { + let sb = self.superblock(); + let chunkformat = match inode.info().spec() { + Spec::Chunk(chunkformat) => Ok(chunkformat), + _ => Err(EUCLEAN), + }?; + let accessor = sb.chunk_access(chunkformat, offset); + + if chunkformat.is_chunkindex() { + let unit = size_of::<ChunkIndex>() as Off; + let pos = round!( + UP, + self.superblock().iloc(inode.nid()) + + inode.info().inode_size() + + inode.info().xattr_size() + + unit * accessor.nr, + unit + ); + let mut buf = [0u8; size_of::<ChunkIndex>()]; + self.backend().fill(&mut buf, pos)?; + let chunk_index = ChunkIndex::from(buf); + if chunk_index.blkaddr == u32::MAX { + Err(EUCLEAN) + } else { + Ok(Map { + logical: Segment { + start: accessor.base + accessor.off, + len: accessor.len, + }, + physical: Segment { + start: sb.blkpos(chunk_index.blkaddr) + accessor.off, + len: accessor.len, + }, + algorithm_format: 0, + device_id: chunk_index.device_id & self.device_info().mask, + map_type: MapType::Normal, + }) + } + } else { + let unit = 4; + let pos = round!( + UP, + sb.iloc(inode.nid()) + + inode.info().inode_size() + + inode.info().xattr_size() + + unit * accessor.nr, + unit + ); + let mut buf = [0u8; 4]; + self.backend().fill(&mut buf, pos)?; + let blkaddr = u32::from_le_bytes(buf); + let len = accessor.len.min(inode.info().file_size() - offset); + if blkaddr == u32::MAX { + Err(EUCLEAN) + } else { + Ok(Map { + logical: Segment { + start: accessor.base + accessor.off, + len, + }, + physical: Segment { + start: sb.blkpos(blkaddr) + accessor.off, + len, + }, + algorithm_format: 0, + device_id: 0, + map_type: MapType::Normal, + }) + } + } + } + + fn map(&self, inode: &I, offset: Off) -> MapResult { + match inode.info().format().layout() { + Layout::FlatInline => self.flatmap(inode, offset, true), + Layout::FlatPlain => self.flatmap(inode, offset, false), + Layout::Chunk => self.chunk_map(inode, offset), + _ => todo!(), + } + } } pub(crate) struct SuperblockInfo<I, C, T> -- 2.46.0