feat(storage-server): implement FAT16 filesystem with read, write, create, delete, list, and cluster chain management

This commit is contained in:
2026-10-18 03:45:30 +02:00
parent 2dc503e07e
commit dd5c2c7268
4 changed files with 510 additions and 3 deletions

View File

@@ -0,0 +1,112 @@
use crate::adapters::mock_block::MockBlockDevice;
use crate::block_device::{BlockDevice, SectorId};
use crate::filesystem::{DirEntry, FileSystem};
use crate::fs::fat16::Fat16;
fn format_fat16_disk(sectors: usize) -> MockBlockDevice {
let mut dev = MockBlockDevice::new(sectors);
let mut boot = [0u8; 512];
// BPB
boot[11] = 0x00; boot[12] = 0x02; // bytes_per_sector = 512
boot[13] = 1; // sectors_per_cluster = 1
boot[14] = 1; boot[15] = 0; // reserved_sectors = 1
boot[16] = 1; // num_fats = 1
boot[17] = 16; boot[18] = 0; // root_entry_count = 16
boot[19] = sectors as u8; // total_sectors
boot[20] = (sectors >> 8) as u8;
boot[22] = 1; boot[23] = 0; // sectors_per_fat = 1
// boot signature
boot[510] = 0x55;
boot[511] = 0xAA;
dev.write_sector(SectorId::new(0), &boot);
// FAT sector (sector 1) — all zeros = all free
let fat = [0u8; 512];
dev.write_sector(SectorId::new(1), &fat);
dev
}
fn mount_test_disk() -> Fat16<MockBlockDevice> {
let dev = format_fat16_disk(32);
Fat16::mount(dev).expect("mount failed")
}
#[test]
fn mount_valid_disk() {
let dev = format_fat16_disk(32);
assert!(Fat16::mount(dev).is_some());
}
#[test]
fn mount_invalid_disk() {
let dev = MockBlockDevice::new(32); // no boot signature
assert!(Fat16::mount(dev).is_none());
}
#[test]
fn create_and_open_file() {
let mut fs = mount_test_disk();
let h = fs.create(b"hello.txt").expect("create");
fs.close(h);
let h2 = fs.open(b"hello.txt").expect("open");
assert_eq!(fs.file_size(h2), Some(0));
fs.close(h2);
}
#[test]
fn write_and_read_file() {
let mut fs = mount_test_disk();
let h = fs.create(b"data.bin").expect("create");
let data = b"Hello, FAT16!";
let written = fs.write(h, data).expect("write");
assert_eq!(written, data.len());
fs.close(h);
let h2 = fs.open(b"data.bin").expect("open");
assert_eq!(fs.file_size(h2), Some(data.len()));
let mut buf = [0u8; 32];
let read = fs.read(h2, &mut buf).expect("read");
assert_eq!(read, data.len());
assert_eq!(&buf[..read], data);
fs.close(h2);
}
#[test]
fn delete_file() {
let mut fs = mount_test_disk();
fs.create(b"temp.txt").map(|h| fs.close(h));
assert!(fs.delete(b"temp.txt"));
assert!(fs.open(b"temp.txt").is_none());
}
#[test]
fn list_directory() {
let mut fs = mount_test_disk();
fs.create(b"a.txt").map(|h| fs.close(h));
fs.create(b"b.txt").map(|h| fs.close(h));
let mut entries = [DirEntry::empty(); 16];
let count = fs.list_dir(b"/", &mut entries).expect("list");
assert_eq!(count, 2);
}
#[test]
fn open_nonexistent_returns_none() {
let mut fs = mount_test_disk();
assert!(fs.open(b"nope.txt").is_none());
}
#[test]
fn create_duplicate_returns_none() {
let mut fs = mount_test_disk();
fs.create(b"dup.txt").map(|h| fs.close(h));
assert!(fs.create(b"dup.txt").is_none());
}

View File

@@ -1,2 +1,3 @@
mod fat16;
mod protocol;
mod server;