This patch adds xattrs for erofs_sys crate and will later be used to implement xattr handler in Rust. Signed-off-by: Yiyang Wu <toolmanp@xxxxxxx> --- fs/erofs/inode_rs.rs | 7 +- fs/erofs/rust/erofs_sys/inode.rs | 1 + fs/erofs/rust/erofs_sys/operations.rs | 27 ++++ fs/erofs/rust/erofs_sys/superblock.rs | 141 +++++++++++++++++++++ fs/erofs/rust/erofs_sys/superblock/mem.rs | 13 +- fs/erofs/rust/erofs_sys/xattrs.rs | 148 ++++++++++++++++++++++ fs/erofs/rust/kinode.rs | 6 + 7 files changed, 341 insertions(+), 2 deletions(-) diff --git a/fs/erofs/inode_rs.rs b/fs/erofs/inode_rs.rs index 5cca2ae581ac..a79d1157b910 100644 --- a/fs/erofs/inode_rs.rs +++ b/fs/erofs/inode_rs.rs @@ -48,8 +48,13 @@ fn try_fill_inode(k_inode: NonNull<inode>, nid: Nid) -> PosixResult<()> { let erofs_inode: &mut KernelInode = unsafe { &mut *(container_of!(k_inode.as_ptr(), KernelInode, k_inode) as *mut KernelInode) }; - erofs_inode.info.write(sbi.filesystem.read_inode_info(nid)?); + let info = sbi.filesystem.read_inode_info(nid)?; erofs_inode.nid.write(nid); + erofs_inode.shared_entries.write( + sbi.filesystem + .read_inode_xattrs_shared_entries(nid, &info)?, + ); + erofs_inode.info.write(info); Ok(()) } /// Exported as fill_inode additional fill inode diff --git a/fs/erofs/rust/erofs_sys/inode.rs b/fs/erofs/rust/erofs_sys/inode.rs index 1ecd6147a126..eb3c2144cad8 100644 --- a/fs/erofs/rust/erofs_sys/inode.rs +++ b/fs/erofs/rust/erofs_sys/inode.rs @@ -299,6 +299,7 @@ pub(crate) trait Inode: Sized { fn new(_sb: &SuperBlock, info: InodeInfo, nid: Nid) -> Self; fn info(&self) -> &InodeInfo; fn nid(&self) -> Nid; + fn xattrs_shared_entries(&self) -> &XAttrSharedEntries; } /// Represents the error which occurs when trying to convert the inode. diff --git a/fs/erofs/rust/erofs_sys/operations.rs b/fs/erofs/rust/erofs_sys/operations.rs index 070ba20908a2..292bfbc7b72c 100644 --- a/fs/erofs/rust/erofs_sys/operations.rs +++ b/fs/erofs/rust/erofs_sys/operations.rs @@ -1,9 +1,16 @@ // Copyright 2024 Yiyang Wu // SPDX-License-Identifier: MIT or GPL-2.0-or-later +use super::alloc_helper::*; +use super::data::raw_iters::*; +use super::data::*; use super::inode::*; use super::superblock::*; +use super::xattrs::*; use super::*; +use alloc::vec::Vec; + +use crate::round; pub(crate) fn read_inode<'a, I, C>( filesystem: &'a dyn FileSystem<I>, @@ -33,3 +40,23 @@ pub(crate) fn dir_lookup<'a, I, C>( read_inode(filesystem, collection, nid) }) } + +pub(crate) fn get_xattr_infixes<'a>( + iter: &mut (dyn ContinuousBufferIter<'a> + 'a), +) -> PosixResult<Vec<XAttrInfix>> { + let mut result: Vec<XAttrInfix> = Vec::new(); + for data in iter { + let buffer = data?; + let buf = buffer.content(); + let len = buf.len(); + let mut cur: usize = 0; + while cur <= len { + let mut infix: Vec<u8> = Vec::new(); + let size = u16::from_le_bytes([buf[cur], buf[cur + 1]]) as usize; + extend_from_slice(&mut infix, &buf[cur + 2..cur + 2 + size])?; + push_vec(&mut result, XAttrInfix(infix))?; + cur = round!(UP, cur + 2 + size, 4); + } + } + Ok(result) +} diff --git a/fs/erofs/rust/erofs_sys/superblock.rs b/fs/erofs/rust/erofs_sys/superblock.rs index 403ffdeb4573..6ea59058446e 100644 --- a/fs/erofs/rust/erofs_sys/superblock.rs +++ b/fs/erofs/rust/erofs_sys/superblock.rs @@ -3,14 +3,17 @@ pub(crate) mod mem; use alloc::boxed::Box; +use alloc::vec::Vec; use core::mem::size_of; +use super::alloc_helper::*; use super::data::raw_iters::*; use super::data::*; use super::devices::*; use super::dir::*; use super::inode::*; use super::map::*; +use super::xattrs::*; use super::*; use crate::round; @@ -346,6 +349,144 @@ fn fill_dentries( } Ok(()) } + // Extended attributes goes here. + fn xattr_infixes(&self) -> &Vec<XAttrInfix>; + // Currently we eagerly initialized all xattrs; + fn read_inode_xattrs_shared_entries( + &self, + nid: Nid, + info: &InodeInfo, + ) -> PosixResult<XAttrSharedEntries> { + let sb = self.superblock(); + let mut offset = sb.iloc(nid) + info.inode_size(); + let mut buf = XATTR_ENTRY_SUMMARY_BUF; + let mut indexes: Vec<u32> = Vec::new(); + self.backend().fill(&mut buf, offset)?; + + let header: XAttrSharedEntrySummary = XAttrSharedEntrySummary::from(buf); + offset += size_of::<XAttrSharedEntrySummary>() as Off; + for buf in self.continuous_iter(offset, (header.shared_count << 2) as Off)? { + let data = buf?; + extend_from_slice(&mut indexes, unsafe { + core::slice::from_raw_parts( + data.content().as_ptr().cast(), + data.content().len() >> 2, + ) + })?; + } + + Ok(XAttrSharedEntries { + name_filter: header.name_filter, + shared_indexes: indexes, + }) + } + fn get_xattr( + &self, + inode: &I, + index: u32, + name: &[u8], + buffer: &mut Option<&mut [u8]>, + ) -> PosixResult<XAttrValue> { + let sb = self.superblock(); + let shared_count = inode.xattrs_shared_entries().shared_indexes.len(); + let inline_offset = sb.iloc(inode.nid()) + + inode.info().inode_size() as Off + + size_of::<XAttrSharedEntrySummary>() as Off + + 4 * shared_count as Off; + + let inline_len = inode.info().xattr_size() + - size_of::<XAttrSharedEntrySummary>() as Off + - shared_count as Off * 4; + + if let Some(mut inline_provider) = + SkippableContinuousIter::try_new(self.continuous_iter(inline_offset, inline_len)?)? + { + while !inline_provider.eof() { + let header = inline_provider.get_entry_header()?; + match inline_provider.query_xattr_value( + self.xattr_infixes(), + &header, + name, + index, + buffer, + ) { + Ok(value) => return Ok(value), + Err(e) => { + if e != ENODATA { + return Err(e); + } + } + } + } + } + + for entry_index in inode.xattrs_shared_entries().shared_indexes.iter() { + let mut shared_provider = SkippableContinuousIter::try_new(self.continuous_iter( + sb.blkpos(self.superblock().xattr_blkaddr) + (*entry_index as Off) * 4, + u64::MAX, + )?)? + .unwrap(); + let header = shared_provider.get_entry_header()?; + match shared_provider.query_xattr_value( + self.xattr_infixes(), + &header, + name, + index, + buffer, + ) { + Ok(value) => return Ok(value), + Err(e) => { + if e != ENODATA { + return Err(e); + } + } + } + } + + Err(ENODATA) + } + + fn list_xattrs(&self, inode: &I, buffer: &mut [u8]) -> PosixResult<usize> { + let sb = self.superblock(); + let shared_count = inode.xattrs_shared_entries().shared_indexes.len(); + let inline_offset = sb.iloc(inode.nid()) + + inode.info().inode_size() as Off + + size_of::<XAttrSharedEntrySummary>() as Off + + shared_count as Off * 4; + let mut offset = 0; + let inline_len = inode.info().xattr_size() + - size_of::<XAttrSharedEntrySummary>() as Off + - shared_count as Off * 4; + + if let Some(mut inline_provider) = + SkippableContinuousIter::try_new(self.continuous_iter(inline_offset, inline_len)?)? + { + while !inline_provider.eof() { + let header = inline_provider.get_entry_header()?; + offset += inline_provider.get_xattr_key( + self.xattr_infixes(), + &header, + &mut buffer[offset..], + )?; + inline_provider.skip_xattr_value(&header)?; + } + } + + for index in inode.xattrs_shared_entries().shared_indexes.iter() { + let mut shared_provider = SkippableContinuousIter::try_new(self.continuous_iter( + sb.blkpos(self.superblock().xattr_blkaddr) + (*index as Off) * 4, + u64::MAX, + )?)? + .unwrap(); + let header = shared_provider.get_entry_header()?; + offset += shared_provider.get_xattr_key( + self.xattr_infixes(), + &header, + &mut buffer[offset..], + )?; + } + Ok(offset) + } } pub(crate) struct SuperblockInfo<I, C, T> diff --git a/fs/erofs/rust/erofs_sys/superblock/mem.rs b/fs/erofs/rust/erofs_sys/superblock/mem.rs index 5756dc08744c..c8af3cb5e56e 100644 --- a/fs/erofs/rust/erofs_sys/superblock/mem.rs +++ b/fs/erofs/rust/erofs_sys/superblock/mem.rs @@ -1,8 +1,8 @@ // Copyright 2024 Yiyang Wu // SPDX-License-Identifier: MIT or GPL-2.0-or-later -use super::alloc_helper::*; use super::data::raw_iters::ref_iter::*; +use super::operations::*; use super::*; // Memory Mapped Device/File so we need to have some external lifetime on the backend trait. @@ -16,6 +16,7 @@ pub(crate) struct KernelFileSystem<B> backend: B, sb: SuperBlock, device_info: DeviceInfo, + infixes: Vec<XAttrInfix>, } impl<I, B> FileSystem<I> for KernelFileSystem<B> @@ -58,6 +59,9 @@ fn continuous_iter<'a>( fn device_info(&self) -> &DeviceInfo { &self.device_info } + fn xattr_infixes(&self) -> &Vec<XAttrInfix> { + &self.infixes + } } impl<B> KernelFileSystem<B> @@ -68,6 +72,12 @@ pub(crate) fn try_new(backend: B) -> PosixResult<Self> { let mut buf = SUPERBLOCK_EMPTY_BUF; backend.fill(&mut buf, EROFS_SUPER_OFFSET)?; let sb: SuperBlock = buf.into(); + let infixes = get_xattr_infixes(&mut ContinuousRefIter::new( + &sb, + &backend, + sb.xattr_prefix_start as Off, + sb.xattr_prefix_count as Off * 4, + ))?; let device_info = get_device_infos(&mut ContinuousRefIter::new( &sb, &backend, @@ -78,6 +88,7 @@ pub(crate) fn try_new(backend: B) -> PosixResult<Self> { backend, sb, device_info, + infixes, }) } } diff --git a/fs/erofs/rust/erofs_sys/xattrs.rs b/fs/erofs/rust/erofs_sys/xattrs.rs index d1a110ef10dd..c97640731562 100644 --- a/fs/erofs/rust/erofs_sys/xattrs.rs +++ b/fs/erofs/rust/erofs_sys/xattrs.rs @@ -1,7 +1,13 @@ // Copyright 2024 Yiyang Wu // SPDX-License-Identifier: MIT or GPL-2.0-or-later +use super::alloc_helper::*; +use super::data::raw_iters::*; +use super::*; +use crate::round; + use alloc::vec::Vec; +use core::mem::size_of; /// The header of the xattr entry index. /// This is used to describe the superblock's xattrs collection. @@ -122,3 +128,145 @@ pub(crate) enum XAttrValue { Buffer(usize), Vec(Vec<u8>), } + +/// An iterator to read xattrs by comparing the entry's name one by one and reads its value +/// correspondingly. +pub(crate) trait XAttrEntriesProvider { + fn get_entry_header(&mut self) -> PosixResult<XAttrEntryHeader>; + fn get_xattr_key( + &mut self, + pfs: &[XAttrInfix], + header: &XAttrEntryHeader, + buffer: &mut [u8], + ) -> PosixResult<usize>; + fn query_xattr_value( + &mut self, + pfs: &[XAttrInfix], + header: &XAttrEntryHeader, + name: &[u8], + index: u32, + buffer: &mut Option<&mut [u8]>, + ) -> PosixResult<XAttrValue>; + fn skip_xattr_value(&mut self, header: &XAttrEntryHeader) -> PosixResult<()>; +} +impl<'a> XAttrEntriesProvider for SkippableContinuousIter<'a> { + fn get_entry_header(&mut self) -> PosixResult<XAttrEntryHeader> { + let mut buf: [u8; 4] = [0; 4]; + self.read(&mut buf).map(|_| XAttrEntryHeader::from(buf)) + } + + fn get_xattr_key( + &mut self, + ifs: &[XAttrInfix], + header: &XAttrEntryHeader, + buffer: &mut [u8], + ) -> PosixResult<usize> { + let mut cur = if header.name_index.is_long() { + let if_index: usize = header.name_index.into(); + let infix: &XAttrInfix = ifs.get(if_index).unwrap(); + + let pf_index = infix.prefix_index(); + let prefix = EROFS_XATTRS_PREFIXS[pf_index as usize]; + let plen = prefix.len(); + + buffer[..plen].copy_from_slice(&prefix[..plen]); + buffer[plen..infix.name().len() + plen].copy_from_slice(infix.name()); + + plen + infix.name().len() + } else { + let pf_index: usize = header.name_index.into(); + let prefix = EROFS_XATTRS_PREFIXS[pf_index]; + let plen = prefix.len(); + buffer[..plen].copy_from_slice(&prefix[..plen]); + plen + }; + + self.read(&mut buffer[cur..cur + header.suffix_len as usize])?; + cur += header.suffix_len as usize; + buffer[cur] = b'\0'; + Ok(cur + 1) + } + + fn query_xattr_value( + &mut self, + ifs: &[XAttrInfix], + header: &XAttrEntryHeader, + name: &[u8], + index: u32, + buffer: &mut Option<&mut [u8]>, + ) -> PosixResult<XAttrValue> { + let xattr_size = round!( + UP, + header.suffix_len as Off + header.value_len as Off, + size_of::<XAttrEntryHeader>() as Off + ); + + let cur = if header.name_index.is_long() { + let if_index: usize = header.name_index.into(); + + if if_index >= ifs.len() { + return Err(ENODATA); + } + + let infix = ifs.get(if_index).unwrap(); + let ilen = infix.name().len(); + + let pf_index = infix.prefix_index(); + + if pf_index >= EROFS_XATTRS_PREFIXS.len() as u8 { + return Err(ENODATA); + } + + if index != pf_index as u32 + || name.len() != ilen + header.suffix_len as usize + || name[..ilen] != *infix.name() + { + return Err(ENODATA); + } + ilen + } else { + let pf_index: usize = header.name_index.into(); + if pf_index >= EROFS_XATTRS_PREFIXS.len() { + return Err(ENODATA); + } + + if pf_index != index as usize || header.suffix_len as usize != name.len() { + return Err(ENODATA); + } + 0 + }; + + match self.try_cmp(&name[cur..]) { + Ok(()) => match buffer.as_mut() { + Some(b) => { + if b.len() < header.value_len as usize { + return Err(ERANGE); + } + self.read(&mut b[..header.value_len as usize])?; + Ok(XAttrValue::Buffer(header.value_len as usize)) + } + None => { + let mut b: Vec<u8> = vec_with_capacity(header.value_len as usize)?; + self.read(&mut b)?; + Ok(XAttrValue::Vec(b)) + } + }, + Err(skip_err) => match skip_err { + SkipCmpError::NotEqual(nvalue) => { + self.skip(xattr_size - nvalue)?; + Err(ENODATA) + } + SkipCmpError::PosixError(e) => Err(e), + }, + } + } + fn skip_xattr_value(&mut self, header: &XAttrEntryHeader) -> PosixResult<()> { + self.skip( + round!( + UP, + header.suffix_len as Off + header.value_len as Off, + size_of::<XAttrEntryHeader>() as Off + ) - header.suffix_len as Off, + ) + } +} diff --git a/fs/erofs/rust/kinode.rs b/fs/erofs/rust/kinode.rs index fac72bd8b6b3..a4bea228ddc0 100644 --- a/fs/erofs/rust/kinode.rs +++ b/fs/erofs/rust/kinode.rs @@ -11,6 +11,7 @@ use super::erofs_sys::errnos::*; use super::erofs_sys::inode::*; use super::erofs_sys::superblock::*; +use super::erofs_sys::xattrs::*; use super::erofs_sys::*; extern "C" { @@ -22,6 +23,7 @@ pub(crate) struct KernelInode { pub(crate) info: MaybeUninit<InodeInfo>, pub(crate) nid: MaybeUninit<Nid>, + pub(crate) shared_entries: MaybeUninit<XAttrSharedEntries>, pub(crate) k_inode: MaybeUninit<inode>, pub(crate) k_opaque: MaybeUninit<*mut c_void>, } @@ -31,6 +33,7 @@ fn new(_sb: &SuperBlock, _info: InodeInfo, _nid: Nid) -> Self { Self { info: MaybeUninit::uninit(), nid: MaybeUninit::uninit(), + shared_entries: MaybeUninit::uninit(), k_inode: MaybeUninit::uninit(), k_opaque: MaybeUninit::uninit(), } @@ -41,6 +44,9 @@ fn nid(&self) -> Nid { fn info(&self) -> &InodeInfo { unsafe { self.info.assume_init_ref() } } + fn xattrs_shared_entries(&self) -> &XAttrSharedEntries { + unsafe { self.shared_entries.assume_init_ref() } + } } pub(crate) struct KernelInodeCollection { -- 2.46.0