All of lore.kernel.org
 help / color / mirror / Atom feed
From: priyena.programming@gmail.com
To: linux-erofs@lists.ozlabs.org
Cc: Transcendental-Programmer <priyena.programming@gmail.com>
Subject: [PATCH] erofs-rs: fix inline xattr size for tail offsets
Date: Thu,  9 Apr 2026 15:48:35 +0000	[thread overview]
Message-ID: <20260409154835.18533-1-priyena.programming@gmail.com> (raw)
In-Reply-To: <CAPXyYWf=J5iyZS=ZRtC-mOePzcc-nq-i7P6wQuw2tqDAr9+obw@mail.gmail.com>

From: Transcendental-Programmer <priyena.programming@gmail.com>

---
 erofs/src/async/filesystem.rs | 64 ++++++++++++++++++++++++++++++++++-
 erofs/src/filesystem.rs       | 53 +++++++++++++++++++++++++++--
 erofs/src/sync/filesystem.rs  | 34 ++++++++++++++++++-
 erofs/src/types.rs            |  7 ++++
 4 files changed, 153 insertions(+), 5 deletions(-)

diff --git a/erofs/src/async/filesystem.rs b/erofs/src/async/filesystem.rs
index 1987f09..c791b49 100644
--- a/erofs/src/async/filesystem.rs
+++ b/erofs/src/async/filesystem.rs
@@ -1,5 +1,7 @@
 use alloc::format;
 use alloc::vec::Vec;
+use binrw::{BinRead, io::Cursor};
+use core::mem::size_of;
 use typed_path::Component;
 
 use bytes::Buf;
@@ -83,6 +85,44 @@ impl<I: AsyncImage> EroFS<I> {
         self.core.block_size
     }
 
+    async fn xattr_ibody_size(&self, inode: &Inode) -> Result<usize> {
+        let total_count = inode.xattr_count();
+        if total_count == 0 {
+            return Ok(0);
+        }
+
+        let inode_offset = self.core.get_inode_offset(inode.id()) as usize;
+        let xattr_start = inode_offset + inode.size();
+
+        let mut header_buf = vec![0u8; size_of::<XattrHeader>()];
+        self.image.read_exact_at(&mut header_buf, xattr_start).await?;
+        let header = XattrHeader::read(&mut Cursor::new(&header_buf))?;
+
+        let shared_count = header.shared_count as usize;
+        let total = total_count as usize;
+        if shared_count > total {
+            return Err(Error::CorruptedData(
+                "xattr shared count exceeds total count".to_string(),
+            ));
+        }
+
+        let mut offset = xattr_start + size_of::<XattrHeader>() + shared_count * size_of::<u32>();
+        let inline_count = total - shared_count;
+        for _ in 0..inline_count {
+            let mut entry_buf = vec![0u8; size_of::<XattrEntry>()];
+            self.image.read_exact_at(&mut entry_buf, offset).await?;
+            let entry = XattrEntry::read(&mut Cursor::new(&entry_buf))?;
+            offset += size_of::<XattrEntry>();
+
+            let payload = entry.name_len as usize + entry.value_len as usize;
+            offset = offset
+                .checked_add(payload)
+                .ok_or_else(|| Error::CorruptedData("xattr size overflow".to_string()))?;
+        }
+
+        Ok(offset - xattr_start)
+    }
+
     pub async fn get_inode(&self, nid: u64) -> Result<Inode> {
         let offset = self.core.get_inode_offset(nid) as usize;
         let mut buf = vec![0u8; InodeExtended::size()];
@@ -91,7 +131,29 @@ impl<I: AsyncImage> EroFS<I> {
     }
 
     pub(crate) async fn read_inode_block(&self, inode: &Inode, offset: usize) -> Result<Vec<u8>> {
-        match self.core.plan_inode_block_read(inode, offset)? {
+        let layout = inode.layout()?;
+        let xattr_size = if inode.xattr_count() == 0 {
+            0
+        } else {
+            match layout {
+                Layout::FlatInline => {
+                    let block_count = inode.data_size().div_ceil(self.core.block_size);
+                    let block_index = offset / self.core.block_size;
+                    if block_count != 0 && block_index == block_count - 1 {
+                        self.xattr_ibody_size(inode).await?
+                    } else {
+                        0
+                    }
+                }
+                Layout::ChunkBased => self.xattr_ibody_size(inode).await?,
+                _ => 0,
+            }
+        };
+
+        match self
+            .core
+            .plan_inode_block_read(inode, offset, xattr_size)?
+        {
             BlockPlan::Direct { offset, size } => {
                 if size > self.core.block_size {
                     return Err(Error::CorruptedData(format!(
diff --git a/erofs/src/filesystem.rs b/erofs/src/filesystem.rs
index dfe22aa..d44a5cd 100644
--- a/erofs/src/filesystem.rs
+++ b/erofs/src/filesystem.rs
@@ -1,4 +1,5 @@
 use alloc::{format, string::ToString};
+use core::mem::size_of;
 
 use binrw::BinRead;
 use binrw::BinReaderExt;
@@ -87,7 +88,12 @@ impl EroFSCore {
     /// Returns a `BlockPlan` describing what bytes to read.
     /// For `BlockPlan::Chunked`, the caller must perform an additional
     /// read and call `resolve_chunk_read()`.
-    pub(crate) fn plan_inode_block_read(&self, inode: &Inode, offset: usize) -> Result<BlockPlan> {
+    pub(crate) fn plan_inode_block_read(
+        &self,
+        inode: &Inode,
+        offset: usize,
+        xattr_size: usize,
+    ) -> Result<BlockPlan> {
         match inode.layout()? {
             Layout::FlatPlain => {
                 let block_count = inode.data_size().div_ceil(self.block_size);
@@ -112,7 +118,7 @@ impl EroFSCore {
                     // tail block
                     let inode_offset = self.get_inode_offset(inode.id());
                     let buf_size = inode.data_size() % self.block_size;
-                    let offset = inode_offset as usize + inode.size() + inode.xattr_size();
+                    let offset = inode_offset as usize + inode.size() + xattr_size;
                     return Ok(BlockPlan::Direct {
                         offset,
                         size: buf_size,
@@ -151,7 +157,7 @@ impl EroFSCore {
 
                 let inode_offset = self.get_inode_offset(inode.id());
                 let addr_offset =
-                    inode_offset as usize + inode.size() + inode.xattr_size() + (chunk_index * 4);
+                    inode_offset as usize + inode.size() + xattr_size + (chunk_index * 4);
 
                 Ok(BlockPlan::Chunked {
                     addr_offset,
@@ -201,4 +207,45 @@ impl EroFSCore {
     pub(crate) fn block_offset(&self, block: u32) -> u64 {
         (block as u64) << self.super_block.blk_size_bits
     }
+
+    pub(crate) fn xattr_ibody_size_from_slice(
+        data: &[u8],
+        total_count: u16,
+    ) -> Result<usize> {
+        if total_count == 0 {
+            return Ok(0);
+        }
+
+        let header = XattrHeader::read(&mut Cursor::new(data))?;
+        let shared_count = header.shared_count as usize;
+        let total = total_count as usize;
+        if shared_count > total {
+            return Err(Error::CorruptedData(
+                "xattr shared count exceeds total count".to_string(),
+            ));
+        }
+
+        let mut offset = size_of::<XattrHeader>() + shared_count * size_of::<u32>();
+        let inline_count = total - shared_count;
+        for _ in 0..inline_count {
+            if data.len() < offset + size_of::<XattrEntry>() {
+                return Err(Error::CorruptedData(
+                    "xattr entry header out of range".to_string(),
+                ));
+            }
+
+            let entry = XattrEntry::read(&mut Cursor::new(&data[offset..]))?;
+            offset += size_of::<XattrEntry>();
+
+            let payload = entry.name_len as usize + entry.value_len as usize;
+            if data.len() < offset + payload {
+                return Err(Error::CorruptedData(
+                    "xattr entry payload out of range".to_string(),
+                ));
+            }
+            offset += payload;
+        }
+
+        Ok(offset)
+    }
 }
diff --git a/erofs/src/sync/filesystem.rs b/erofs/src/sync/filesystem.rs
index 2727383..7d94727 100644
--- a/erofs/src/sync/filesystem.rs
+++ b/erofs/src/sync/filesystem.rs
@@ -143,6 +143,16 @@ impl<I: Image> EroFS<I> {
         self.core.block_size
     }
 
+    fn xattr_ibody_size(&self, inode: &Inode) -> Result<usize> {
+        let inode_offset = self.core.get_inode_offset(inode.id()) as usize;
+        let xattr_start = inode_offset + inode.size();
+        let data = self
+            .image
+            .get(xattr_start..)
+            .ok_or_else(|| Error::OutOfBounds("failed to read xattr data".to_string()))?;
+        EroFSCore::xattr_ibody_size_from_slice(data, inode.xattr_count())
+    }
+
     pub fn get_inode(&self, nid: u64) -> Result<Inode> {
         let offset = self.core.get_inode_offset(nid) as usize;
         let data = self
@@ -153,7 +163,29 @@ impl<I: Image> EroFS<I> {
     }
 
     pub(crate) fn get_inode_block(&self, inode: &Inode, offset: usize) -> Result<&[u8]> {
-        match self.core.plan_inode_block_read(inode, offset)? {
+        let layout = inode.layout()?;
+        let xattr_size = if inode.xattr_count() == 0 {
+            0
+        } else {
+            match layout {
+                Layout::FlatInline => {
+                    let block_count = inode.data_size().div_ceil(self.core.block_size);
+                    let block_index = offset / self.core.block_size;
+                    if block_count != 0 && block_index == block_count - 1 {
+                        self.xattr_ibody_size(inode)?
+                    } else {
+                        0
+                    }
+                }
+                Layout::ChunkBased => self.xattr_ibody_size(inode)?,
+                _ => 0,
+            }
+        };
+
+        match self
+            .core
+            .plan_inode_block_read(inode, offset, xattr_size)?
+        {
             BlockPlan::Direct { offset, size } => self
                 .image
                 .get(offset..offset + size)
diff --git a/erofs/src/types.rs b/erofs/src/types.rs
index f4cfcf6..5bb6df1 100644
--- a/erofs/src/types.rs
+++ b/erofs/src/types.rs
@@ -185,6 +185,13 @@ impl Inode {
         }
     }
 
+    pub fn xattr_count(&self) -> u16 {
+        match self {
+            Self::Compact((_, n)) => n.xattr_count,
+            Self::Extended((_, n)) => n.xattr_count,
+        }
+    }
+
     pub fn file_type(&self) -> FileType {
         match self {
             Self::Compact((_, n)) => FileType::from_raw_mode(n.mode as _),
-- 
2.43.0



  reply	other threads:[~2026-04-09 15:51 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-31 13:31 [GSoC 2026] erofs-rs xattr POC + xattr_size() fix Priyansh Saxena
2026-04-09 15:48 ` priyena.programming [this message]
2026-04-10 15:41   ` [PATCH v2] erofs-rs: fix inline xattr size for tail offsets priyena.programming
2026-04-10 15:44     ` Gao Xiang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260409154835.18533-1-priyena.programming@gmail.com \
    --to=priyena.programming@gmail.com \
    --cc=linux-erofs@lists.ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.