rsext4
注意!!在开启 USE_MULTILEVEL_CACHE(使用多级缓存,默认开启!)feature的情况rsext4 使用多级缓存来提升读写性能,写操作只修改内存缓存,不会立即写入磁盘。在需要确保所有数据已经实际写入磁盘的时机前,请调用
self.sync_filesystem(block_dev)?;
不需要多级缓存的特殊情况可以在Cargo.toml关闭掉USE_MULTILEVEL_CACHE feature来禁用多级缓存
如何使用rsext4。
1. 接入块设备(实现 BlockDevice trait)
你需要提供一个块设备实现,并实现 rsext4::BlockDevice:
read(&mut self, buffer, block_id, count):从 block_id 开始读取 count 个块到 buffer
write(&mut self, buffer, block_id, count):从 buffer 写入 count 个块到设备
open/close:可选的设备打开/关闭(可为空实现)
total_blocks():设备总块数
block_size():块大小(通常为 BLOCK_SIZE)
下面是一个参考实现:使用宿主机文件模拟块设备(来自 src/main.rs)。
use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::Path;
use rsext4::{BlockDevError, BlockDevResult, BlockDevice, BLOCK_SIZE};
struct FileBlockDev {
file: File,
total_blocks: u64,
}
impl FileBlockDev {
fn open_or_create<P: AsRef<Path>>(path: P, total_blocks: u64) -> std::io::Result<Self> {
let path = path.as_ref();
let block_size = BLOCK_SIZE as u64;
let size_bytes = total_blocks * block_size;
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)?;
let metadata = file.metadata()?;
if metadata.len() < size_bytes {
file.set_len(size_bytes)?;
}
Ok(Self { file, total_blocks })
}
}
impl BlockDevice for FileBlockDev {
fn write(&mut self, buffer: &[u8], block_id: u32, count: u32) -> BlockDevResult<()> {
let block_size = self.block_size() as usize;
let required = block_size * count as usize;
if buffer.len() < required {
return Err(BlockDevError::BufferTooSmall { provided: buffer.len(), required });
}
let offset = block_id as u64 * block_size as u64;
let bytes = &buffer[..required];
self.file.seek(SeekFrom::Start(offset)).map_err(|_| BlockDevError::IoError)?;
self.file.write_all(bytes).map_err(|_| BlockDevError::IoError)?;
self.file.flush().map_err(|_| BlockDevError::IoError)?;
Ok(())
}
fn read(&mut self, buffer: &mut [u8], block_id: u32, count: u32) -> BlockDevResult<()> {
let block_size = self.block_size() as usize;
let required = block_size * count as usize;
if buffer.len() < required {
return Err(BlockDevError::BufferTooSmall { provided: buffer.len(), required });
}
let offset = block_id as u64 * block_size as u64;
let mut f = &self.file;
f.seek(SeekFrom::Start(offset)).map_err(|_| BlockDevError::IoError)?;
f.read_exact(&mut buffer[..required]).map_err(|_| BlockDevError::IoError)?;
Ok(())
}
fn open(&mut self) -> BlockDevResult<()> { Ok(()) }
fn close(&mut self) -> BlockDevResult<()> {
self.file.flush().map_err(|_| BlockDevError::IoError)?;
Ok(())
}
fn total_blocks(&self) -> u64 { self.total_blocks }
fn block_size(&self) -> u32 { BLOCK_SIZE as u32 }
}
2. 用 Jbd2Dev 包装块设备,目前只支持ordered模式,ordered会储存完整元数据内容然后写主盘,如果对性能有较高要求请关闭
rsext4 的所有读写都通过 Jbd2Dev<B> 进行:
use rsext4::Jbd2Dev;
let mut dev = Jbd2Dev::initial_jbd2dev(0, block_dev, true);
运行时可以切换 journal 开关:
如果在mount之后启用需要手动读取日志超级块并且注入!
dev.set_journal_use(true);
dev.set_journal_use(false);
如果你需要手动重放日志(注意:会有性能影响):
dev.journal_replay();
注意:mkfs() 内部会临时关闭 journal,避免在 journal superblock 尚未注入时触发 JBD2 逻辑;mkfs() 结束前会恢复原先的开关状态(见 src/ext4_backend/ext4.rs)。
3. 创建文件系统(mkfs)
use rsext4::mkfs;
mkfs(&mut dev)?;
4. 挂载与卸载
你可以直接用 mount/umount,也可以用 fs_mount/fs_umount(它们只是转发到 ext4::mount/umount)。
use rsext4::{mount, umount};
let mut fs = mount(&mut dev)?;
umount(fs, &mut dev)?;
5. 常用 API 使用
下面这些调用方式来自 src/testfs/test_example.rs(建议直接看该文件作为更完整的用例集合)。
5.1 目录与文件创建
use rsext4::{mkdir, mkfile};
mkdir(&mut dev, &mut fs, "/test_dir/");
let data = vec![b'a'; 4096];
mkfile(&mut dev, &mut fs, "/test_dir/hello", Some(&data),None);mkfile(&mut dev, &mut fs, "/test_dir/empty", None,None);
5.2 读取整个文件
use rsext4::read_file;
let content = read_file(&mut dev, &mut fs, "/test_dir/hello")?;
if let Some(bytes) = content {
}
5.3 打开文件句柄 + 基于 offset 的写入/读取
open() 返回 OpenFile { path, inode, offset },并维护 offset。
use rsext4::{open, append, read_at, lseek};
let mut f = open(&mut dev, &mut fs, "/test_dir/f", true)?;
append(&mut dev, &mut fs, &mut f, b"hello")?;
append(&mut dev, &mut fs, &mut f, b" world")?;
let ok = lseek(&mut f, 0);
assert!(ok);
let buf = read_at(&mut dev, &mut fs, &mut f, 5)?;
5.4 rename / mv
use rsext4::{rename, mv};
rename(&mut dev, &mut fs, "/renametest/a", "/renametest/c")?;
mv(&mut fs, &mut dev, "/mvtest/a/f1", "/mvtest/b/f1_moved")?;
5.5 link / unlink
use rsext4::{link, unlink};
link(&mut fs, &mut dev, "/linktest/l1", "/linktest/target");
unlink(&mut fs, &mut dev, "/linktest/l1");
5.6 符号链接
use rsext4::create_symbol_link;
create_symbol_link(&mut dev, &mut fs, "/symlinktest/target", "/symlinktest/l1")?;
5.7 truncate
use rsext4::truncate;
truncate(&mut dev, &mut fs, "/truncatetest/f1", 0)?;
truncate(&mut dev, &mut fs, "/truncatetest/f1", 128 * 1024)?;
5.8 删除
use rsext4::{delete_file, delete_dir};
delete_file(&mut fs, &mut dev, "/path/to/file");
delete_dir(&mut fs, &mut dev, "/path/to/dir");