feat(storage-server): add StorageServer<F: FileSystem> with BlockDevice trait, mock adapters, and tests

This commit is contained in:
2026-10-18 03:37:40 +02:00
parent b7bc223a79
commit 7a4600b3a2
13 changed files with 591 additions and 12 deletions

View File

@@ -0,0 +1,46 @@
use crate::block_device::BlockDevice;
const SECTOR_SIZE: usize = 512;
const MAX_SECTORS: usize = 64;
pub struct MockBlockDevice {
data: [[u8; SECTOR_SIZE]; MAX_SECTORS],
sectors: usize,
}
impl MockBlockDevice {
pub fn new(sectors: usize) -> Self {
Self {
data: [[0; SECTOR_SIZE]; MAX_SECTORS],
sectors: sectors.min(MAX_SECTORS),
}
}
}
impl BlockDevice for MockBlockDevice {
fn sector_size(&self) -> usize {
SECTOR_SIZE
}
fn sector_count(&self) -> usize {
self.sectors
}
fn read_sector(&mut self, sector: u32, buf: &mut [u8]) -> bool {
let idx = sector as usize;
if idx >= self.sectors || buf.len() < SECTOR_SIZE {
return false;
}
buf[..SECTOR_SIZE].copy_from_slice(&self.data[idx]);
true
}
fn write_sector(&mut self, sector: u32, buf: &[u8]) -> bool {
let idx = sector as usize;
if idx >= self.sectors || buf.len() < SECTOR_SIZE {
return false;
}
self.data[idx].copy_from_slice(&buf[..SECTOR_SIZE]);
true
}
}

View File

@@ -0,0 +1,127 @@
use crate::filesystem::{DirEntry, FileHandle, FileSystem};
const MAX_FILES: usize = 16;
const MAX_FILE_SIZE: usize = 256;
struct FileEntry {
name: [u8; 32],
name_len: u8,
data: [u8; MAX_FILE_SIZE],
size: usize,
open: bool,
}
impl FileEntry {
fn empty() -> Self {
Self {
name: [0; 32],
name_len: 0,
data: [0; MAX_FILE_SIZE],
size: 0,
open: false,
}
}
}
pub struct MockFileSystem {
files: [Option<FileEntry>; MAX_FILES],
count: usize,
}
impl MockFileSystem {
pub fn new() -> Self {
Self {
files: [const { None }; MAX_FILES],
count: 0,
}
}
fn find_by_name(&self, path: &[u8]) -> Option<usize> {
for i in 0..self.count {
if let Some(entry) = &self.files[i] {
if &entry.name[..entry.name_len as usize] == path {
return Some(i);
}
}
}
None
}
}
impl FileSystem for MockFileSystem {
fn open(&mut self, path: &[u8]) -> Option<FileHandle> {
let idx = self.find_by_name(path)?;
if let Some(entry) = &mut self.files[idx] {
entry.open = true;
Some(FileHandle::new(idx as u16))
} else {
None
}
}
fn read(&mut self, handle: FileHandle, buf: &mut [u8]) -> Option<usize> {
let entry = self.files[handle.raw() as usize].as_ref()?;
let len = buf.len().min(entry.size);
buf[..len].copy_from_slice(&entry.data[..len]);
Some(len)
}
fn write(&mut self, handle: FileHandle, buf: &[u8]) -> Option<usize> {
let entry = self.files[handle.raw() as usize].as_mut()?;
let len = buf.len().min(MAX_FILE_SIZE - entry.size);
entry.data[entry.size..entry.size + len].copy_from_slice(&buf[..len]);
entry.size += len;
Some(len)
}
fn close(&mut self, handle: FileHandle) {
if let Some(entry) = &mut self.files[handle.raw() as usize] {
entry.open = false;
}
}
fn list_dir(&mut self, _path: &[u8], entries: &mut [DirEntry]) -> Option<usize> {
let mut count = 0;
for i in 0..self.count {
if count >= entries.len() { break; }
if let Some(file) = &self.files[i] {
let mut entry = DirEntry::empty();
let len = file.name_len as usize;
entry.name[..len].copy_from_slice(&file.name[..len]);
entry.name_len = file.name_len;
entry.size = file.size as u32;
entry.is_dir = false;
entries[count] = entry;
count += 1;
}
}
Some(count)
}
fn create(&mut self, path: &[u8]) -> Option<FileHandle> {
if self.count >= MAX_FILES || path.len() > 32 {
return None;
}
let idx = self.count;
let mut entry = FileEntry::empty();
entry.name[..path.len()].copy_from_slice(path);
entry.name_len = path.len() as u8;
entry.open = true;
self.files[idx] = Some(entry);
self.count += 1;
Some(FileHandle::new(idx as u16))
}
fn delete(&mut self, path: &[u8]) -> bool {
if let Some(idx) = self.find_by_name(path) {
self.files[idx] = None;
true
} else {
false
}
}
fn file_size(&mut self, handle: FileHandle) -> Option<usize> {
self.files[handle.raw() as usize].as_ref().map(|e| e.size)
}
}

View File

@@ -0,0 +1,2 @@
pub mod mock_block;
pub mod mock_fs;