文件系统仿真 — Rust+egui
用 Rust 模拟类 FAT 文件系统,结合 egui 构建跨平台 GUI,位图可视化一目了然。
📋 项目概览
技术栈
Rusteguibit-vec
功能特性
- 位图磁盘块分配与回收
- 多级目录树(创建/删除/重命名)
- 文件 CRUD,动态长度增长
- egui 实时可视化磁盘状态
- 交互式 Shell 命令行界面
🚀 在线演示新窗口打开 ↗
💻 核心代码Rust · egui · bit-vec
rust磁盘块管理(Disk)
使用位图管理 512 个磁盘块的分配与回收。磁盘以 1024 字节为块大小,通过位图标记空闲/占用状态。
use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
pub const BLOCK_SIZE: usize = 1024;
pub const NUM_BLOCKS: usize = 512;
pub const BOOT_BLOCK: usize = 0; // 引导块
pub const SUPER_BLOCK: usize = 1; // 超级块
pub const MFD_BLOCK: usize = 2; // 主文件目录
pub const UFD_START: usize = 3; // 用户文件目录
pub const UFD_END: usize = 34;
pub const DATA_START: usize = 35; // 数据区起点
pub struct Disk {
file: File,
pub num_blocks: usize,
pub block_size: usize,
}
impl Disk {
pub fn new(path: &str) -> Result<Self, String> {
let file = OpenOptions::new()
.read(true).write(true).create(true)
.open(path)
.map_err(|e| format!("Failed to open disk file: {}", e))?;
file.set_len((NUM_BLOCKS * BLOCK_SIZE) as u64)
.map_err(|e| format!("Failed to set disk size: {}", e))?;
Ok(Disk { file, num_blocks: NUM_BLOCKS, block_size: BLOCK_SIZE })
}
pub fn read_block(&mut self, block_num: usize) -> Result<Vec<u8>, String> {
if block_num >= self.num_blocks {
return Err(format!("Block {} out of range (max {})", block_num, self.num_blocks));
}
let offset = (block_num * self.block_size) as u64;
self.file.seek(SeekFrom::Start(offset))
.map_err(|e| format!("Seek error: {}", e))?;
let mut buf = vec![0u8; self.block_size];
self.file.read_exact(&mut buf)
.map_err(|e| format!("Read error: {}", e))?;
Ok(buf)
}
pub fn write_block(&mut self, block_num: usize, data: &[u8]) -> Result<(), String> {
if block_num >= self.num_blocks {
return Err(format!("Block {} out of range (max {})", block_num, self.num_blocks));
}
if data.len() != self.block_size {
return Err(format!("Data size {} != block size {}", data.len(), self.block_size));
}
let offset = (block_num * self.block_size) as u64;
self.file.seek(SeekFrom::Start(offset))
.map_err(|e| format!("Seek error: {}", e))?;
self.file.write_all(data)
.map_err(|e| format!("Write error: {}", e))?;
self.file.flush().map_err(|e| format!("Flush error: {}", e))?;
Ok(())
}
}rust文件系统核心(FS)
文件系统核心逻辑,包括目录项管理、主文件目录(MFD)、用户文件目录(UFD)。支持登录验证、文件操作。
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct DirEntry {
pub name: [u8; MAX_FILENAME], // 28 字节文件名
pub type_: i32, // 0=文件, 1=目录
pub start_block: i32, // 起始块号
pub size: i32, // 文件大小
pub password: [u8; MAX_PASSWORD],// 16 字节密码
pub create_time: i64, // 创建时间戳
}
impl DirEntry {
pub fn new(name: &str, type_: i32, password: &str) -> Self {
let mut entry = DirEntry {
name: [0u8; MAX_FILENAME], type_, start_block: -1,
size: 0, password: [0u8; MAX_PASSWORD],
create_time: SystemTime::now()
.duration_since(UNIX_EPOCH).unwrap().as_secs() as i64,
};
let name_bytes = name.as_bytes();
let len = name_bytes.len().min(MAX_FILENAME - 1);
entry.name[..len].copy_from_slice(&name_bytes[..len]);
// ... password similarly ...
entry
}
pub fn name_str(&self) -> String {
let end = self.name.iter().position(|&b| b == 0)
.unwrap_or(self.name.len());
String::from_utf8_lossy(&self.name[..end]).to_string()
}
pub fn is_empty(&self) -> bool {
self.name[0] == 0 || self.name_str().is_empty()
}
}
pub struct Session {
pub current_user: Option<String>,
}
impl Session {
pub fn new() -> Self { Session { current_user: None } }
pub fn login(&mut self, disk: &mut Disk, username: &str, password: &str) -> Result<(), String> {
// 读取 MFD 块,验证用户名密码
let data = disk.read_block(MFD_BLOCK)?;
let mfd: MasterDirectory = unsafe { std::mem::transmute(data) };
for user in &mfd.users[..mfd.user_count as usize] {
if user.name_str() == username && user.password_str() == password {
self.current_user = Some(username.to_string());
return Ok(());
}
}
Err("用户名或密码错误".to_string())
}
}rustegui 图形界面(App)
基于 egui 构建的交互式 GUI,包含命令终端、磁盘状态可视化和实时反馈。
use eframe::egui;
use egui::Color32;
pub struct FileSystemApp {
pub session: Session,
pub disk: Option<Disk>,
pub disk_state: DiskState,
pub output_lines: Vec<(String, Color32)>,
pub input_buffer: String,
pub selected_block: Option<usize>,
}
impl Default for FileSystemApp {
fn default() -> Self {
Self {
session: Session::new(),
disk: None,
disk_state: DiskState::default(),
output_lines: vec![
("💾 二级文件系统仿真 — Rust + egui".to_string(), Color32::from_rgb(86, 156, 214)),
("输入 help 查看可用命令,format <文件名> 开始使用".to_string(), Color32::from_rgb(128, 128, 128)),
],
input_buffer: String::new(),
selected_block: None,
}
}
}
impl eframe::App for FileSystemApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::TopBottomPanel::top("terminal").show(ctx, |ui| {
ui.horizontal(|ui| {
let input = egui::TextEdit::singleline(&mut self.input_buffer)
.desired_width(f32::INFINITY)
.hint_text("> 输入命令...")
.show(ui);
if ui.button("⏎").clicked() || input.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
self.execute_current_command();
}
});
});
egui::CentralPanel::default().show(ctx, |ui| {
// Left: terminal output
egui::SidePanel::left("output")
.resizable(true)
.default_width(400.0)
.show_inside(ui, |ui| {
egui::ScrollArea::vertical().stick_to_bottom(true).show(ui, |ui| {
for (text, color) in &self.output_lines {
ui.colored_label(*color, text);
}
});
});
// Right: disk visualization
self.render_disk_map(ui);
});
}
}📁 源文件清单
| 路径 | 说明 | 行数 |
|---|---|---|
src/disk.rs | 磁盘块设备模拟(512块×1024字节) | 72 |
src/fs.rs | 文件系统核心(MFD/UFD/目录项) | 210 |
src/app.rs | egui 图形界面应用 | 180 |
src/shell.rs | 命令解析与执行 | 150 |
src/visual.rs | 磁盘状态可视化渲染 | 85 |
src/main.rs | 程序入口 | 40 |
tests/integration_test.rs | 集成测试 | 95 |