ext2: properly handle max file size

This commit is contained in:
Mark Poliakov 2025-01-02 21:48:00 +02:00
parent d8f035dc69
commit a126118589
2 changed files with 29 additions and 28 deletions

View File

@ -229,6 +229,7 @@ impl Inode {
self.size_upper = (size >> 32) as u32; self.size_upper = (size >> 32) as u32;
self.size_lower = size as u32; self.size_lower = size as u32;
} else { } else {
// Unreachable: size is checked in resize()
if size > u32::MAX as u64 { if size > u32::MAX as u64 {
todo!("File too large") todo!("File too large")
} }
@ -242,8 +243,10 @@ impl Inode {
if size == self.size(fs) { if size == self.size(fs) {
return Ok(false); return Ok(false);
} }
if size > fs.max_file_size {
// TODO check max inode size // TODO FileTooBig or something
return Err(Error::InvalidArgument);
}
let is_symlink = self let is_symlink = self
.mode .mode
@ -256,11 +259,12 @@ impl Inode {
} else { } else {
size.div_ceil(fs.block_size as u64) size.div_ceil(fs.block_size as u64)
}; };
// TODO check size_upper as well?
let old_blocks = if is_symlink && self.size_lower < 60 { let old_size = self.size(fs);
let old_blocks = if is_symlink && old_size < 60 {
0 0
} else { } else {
self.size(fs).div_ceil(fs.block_size as u64) old_size.div_ceil(fs.block_size as u64)
}; };
if new_blocks as usize if new_blocks as usize
@ -268,7 +272,7 @@ impl Inode {
+ fs.pointers_per_block + fs.pointers_per_block
+ fs.pointers_per_block * fs.pointers_per_block + fs.pointers_per_block * fs.pointers_per_block
{ {
log::warn!("ext2: only L0/L1 are supported"); log::warn!("ext2: only L0/L1/L2 are supported");
return Err(Error::InvalidArgument); return Err(Error::InvalidArgument);
} }

View File

@ -9,7 +9,7 @@ use core::mem;
use alloc::{boxed::Box, sync::Arc}; use alloc::{boxed::Box, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use bytemuck::Zeroable; use bytemuck::Zeroable;
use data::{FsReadonlyFeatures, FsRequiredFeatures}; use data::{FsReadonlyFeatures, FsRequiredFeatures, DIRECT_BLOCK_COUNT};
use dir::DirectoryNode; use dir::DirectoryNode;
use file::RegularNode; use file::RegularNode;
use inode::{InodeAccess, InodeCache}; use inode::{InodeAccess, InodeCache};
@ -56,6 +56,8 @@ pub struct Ext2Fs {
inodes_per_block: usize, inodes_per_block: usize,
pointers_per_block: usize, pointers_per_block: usize,
max_file_size: u64,
required_features: FsRequiredFeatures, required_features: FsRequiredFeatures,
write_features: FsReadonlyFeatures, write_features: FsReadonlyFeatures,
@ -259,17 +261,32 @@ impl Ext2Fs {
} }
}; };
let pointers_per_block = block_size / size_of::<u32>();
let max_inode_blocks = (DIRECT_BLOCK_COUNT
+ pointers_per_block
+ pointers_per_block * pointers_per_block
+ pointers_per_block * pointers_per_block * pointers_per_block)
as u64;
let max_file_size = max_inode_blocks.saturating_mul(block_size as u64);
let max_file_size = if write_features.contains(FsReadonlyFeatures::FILE_SIZE_64_BIT) {
max_file_size
} else {
max_file_size.min(u32::MAX as u64)
};
Ok(Self { Ok(Self {
block_size, block_size,
inode_size: superblock.inode_size(), inode_size: superblock.inode_size(),
inodes_per_block: block_size / superblock.inode_size(), inodes_per_block: block_size / superblock.inode_size(),
pointers_per_block: block_size / size_of::<u32>(), pointers_per_block,
total_inodes, total_inodes,
total_blocks, total_blocks,
block_group_inode_count, block_group_inode_count,
block_group_block_count, block_group_block_count,
max_file_size,
mapper, mapper,
inode_cache: OneTimeInit::new(), inode_cache: OneTimeInit::new(),
@ -393,26 +410,6 @@ impl Ext2Fs {
self.inode_cache.get().flush().await self.inode_cache.get().flush().await
} }
// pub async fn flush_superblock(&self) -> Result<(), Error> {
// let state = self.state.read();
// if state.dirty {
// log::info!("Flushing superblock");
// log::info!(
// "inodes {} blocks {}",
// state.superblock.total_unallocated_inodes,
// state.superblock.total_unallocated_blocks
// );
// self.mapper
// .device()
// .write_exact(
// data::SUPERBLOCK_OFFSET,
// bytemuck::bytes_of(&state.superblock),
// )
// .await?;
// }
// Ok(())
// }
async fn read_index(&self, block_index: u32, index: usize) -> Result<u32, Error> { async fn read_index(&self, block_index: u32, index: usize) -> Result<u32, Error> {
self.with_block(block_index, |block| { self.with_block(block_index, |block| {
let indirect: &[u32] = unsafe { let indirect: &[u32] = unsafe {