Fix ext2 fast symlink inode detection logic

This commit is contained in:
Qingsong Chen 2026-01-27 07:44:54 +00:00 committed by Tate, Hongliang Tian
parent 01cf00a4c6
commit 28fe11e98d
1 changed files with 58 additions and 17 deletions

View File

@ -1062,7 +1062,7 @@ impl InodeInner {
pub fn write_link(&mut self, target: &str) -> Result<()> {
if target.len() <= MAX_FAST_SYMLINK_LEN {
return self.inode_impl.write_link(target);
return self.inode_impl.write_fast_link(target);
}
self.page_cache.resize(target.len())?;
@ -1076,8 +1076,8 @@ impl InodeInner {
pub fn read_link(&self) -> Result<String> {
let file_size = self.inode_impl.file_size();
if file_size <= MAX_FAST_SYMLINK_LEN {
return self.inode_impl.read_link();
if self.inode_impl.desc.is_fast_symlink() {
return self.inode_impl.read_fast_link();
}
let mut symlink = vec![0u8; file_size];
@ -1293,7 +1293,7 @@ impl InodeImpl {
}
pub fn set_acl(&mut self, bid: Bid) {
self.desc.acl = Some(bid);
self.desc.set_acl_block(bid);
}
pub fn atime(&self) -> Duration {
@ -1335,16 +1335,14 @@ impl InodeImpl {
device_id
}
pub fn read_link(&self) -> Result<String> {
pub fn read_fast_link(&self) -> Result<String> {
let symlink_str = core::str::from_utf8(&self.desc.block_ptrs.as_bytes()[..self.desc.size])?;
Ok(symlink_str.to_owned())
}
pub fn write_link(&mut self, target: &str) -> Result<()> {
pub fn write_fast_link(&mut self, target: &str) -> Result<()> {
let target_len = target.len();
self.desc.block_ptrs.as_bytes_mut()[..target_len].copy_from_slice(target.as_bytes());
self.block_manager.block_ptrs.write().as_bytes_mut()[..target_len]
.copy_from_slice(target.as_bytes());
if self.desc.size != target_len {
self.resize(target_len)?;
}
@ -1473,9 +1471,11 @@ impl InodeImpl {
self.fs().free_blocks(device_range).unwrap();
return Err(e);
}
self.desc.sector_count = blocks_to_sectors(range.start + device_range.len() as u32);
self.last_alloc_device_bid = Some(device_range.end - 1);
return Ok(device_range.len() as Ext2Bid);
let nr_blocks = device_range.len() as Ext2Bid;
self.desc.set_data_blocks(range.start + nr_blocks);
return Ok(nr_blocks);
}
// Allocates the required additional indirect blocks and at least one block.
@ -1519,9 +1519,10 @@ impl InodeImpl {
return Err(e);
}
self.desc.sector_count = blocks_to_sectors(range.start + device_range.len() as u32);
self.last_alloc_device_bid = Some(device_range.end - 1);
Ok(device_range.len() as Ext2Bid)
let nr_blocks = device_range.len() as Ext2Bid;
self.desc.set_data_blocks(range.start + nr_blocks);
Ok(nr_blocks)
}
/// Sets the device block IDs for a specified range.
@ -1681,7 +1682,7 @@ impl InodeImpl {
current_range.end -= free_cnt;
}
self.desc.sector_count = blocks_to_sectors(range.start);
self.desc.set_data_blocks(range.start);
self.last_alloc_device_bid = if range.start == 0 {
None
} else {
@ -2200,13 +2201,15 @@ impl InodeDesc {
(self.blocks_count() as usize) * BLOCK_SIZE
}
/// Returns the actual number of blocks utilized.
/// Returns the actual number of blocks utilized, excluding the indirect blocks.
///
/// Ext2 allows the `block_count` to exceed the actual number of blocks utilized.
pub fn blocks_count(&self) -> Ext2Bid {
let blocks = self.size_to_blocks(self.size);
debug_assert!(blocks <= sectors_to_blocks(self.sector_count));
blocks
if self.is_fast_symlink() {
return 0;
}
self.size.div_ceil(BLOCK_SIZE) as Ext2Bid
}
fn size_to_blocks(&self, size: usize) -> Ext2Bid {
@ -2215,6 +2218,44 @@ impl InodeDesc {
}
size.div_ceil(BLOCK_SIZE) as Ext2Bid
}
/// Returns the number of sectors used for ACL.
fn acl_sectors(&self) -> u32 {
let Some(bid) = self.acl else {
return 0;
};
if bid.to_raw() == 0 {
return 0;
}
blocks_to_sectors(1)
}
/// Sets the extended attribute block for ACL.
fn set_acl_block(&mut self, bid: Bid) {
let old_acl_sectors = self.acl_sectors();
self.acl = Some(bid);
let new_acl_sectors = self.acl_sectors();
self.sector_count = self.sector_count - old_acl_sectors + new_acl_sectors;
}
/// Returns the number of data sectors, including the indirect blocks.
fn data_sectors(&self) -> u32 {
self.sector_count - self.acl_sectors()
}
/// Sets the number of data blocks, including the indirect blocks.
fn set_data_blocks(&mut self, block_count: u32) {
self.sector_count = blocks_to_sectors(block_count) + self.acl_sectors();
}
/// Returns whether the inode is a fast symlink.
///
/// Reference: <https://elixir.bootlin.com/linux/v6.18/source/fs/ext2/inode.c#L48-L55>.
fn is_fast_symlink(&self) -> bool {
self.type_ == InodeType::SymLink && self.data_sectors() == 0
}
}
fn sectors_to_blocks(sector_count: u32) -> Ext2Bid {