feat(storage): full file protocol over per-client shared region (badged clients, owned handles, clamped lengths, ReadOnly on unwritable disk); init loads via libkos::fs; drop boot shared buffer

This commit is contained in:
2026-10-27 12:42:37 +01:00
parent 694c675a40
commit 909b3f0503
19 changed files with 827 additions and 506 deletions

View File

@@ -13,19 +13,33 @@ pub mod server;
mod tests;
use libkos::syscall;
use types::address::VirtualAddress;
use types::bounded::{CapSlot, PageCount};
use types::mapping::PagePerms;
const CMD_READ_FILE: usize = 1;
const SHARED_BUF_VA: usize = 0x30000;
const SHARED_BUF_SIZE: usize = 64 * 1024;
/// Client buffers sit high in storage's window, clear of its heap.
const BUFFERS_VA: usize = 0x0100_0000;
fn shared_buf() -> &'static mut [u8] {
unsafe { core::slice::from_raw_parts_mut(SHARED_BUF_VA as *mut u8, SHARED_BUF_SIZE) }
/// Each client's buffer is a fresh region, mapped here and handed to the
/// client, which maps the same pages in its own space.
struct RegionBuffers;
impl server::Buffers for RegionBuffers {
fn create(&mut self, client: usize) -> Option<(&'static mut [u8], Option<CapSlot>)> {
let va = BUFFERS_VA + client * protocol::BUFFER_SIZE;
let region = syscall::create_region(PageCount::new(protocol::BUFFER_PAGES as u16)?).ok()?;
syscall::map_region(region, VirtualAddress::from_raw(va), PagePerms::ReadWrite).ok()?;
// SAFETY: the region is mapped read-write at `va` for as long as
// the server holds `region`, which it never drops.
let buf = unsafe { core::slice::from_raw_parts_mut(va as *mut u8, protocol::BUFFER_SIZE) };
Some((buf, Some(region)))
}
}
pub fn run() -> ! {
libkos::println!("storage: started");
let ep = types::bounded::CapSlot::new(0).unwrap();
let ep = CapSlot::new(0).unwrap();
// Init hands over the disk before asking for anything.
let Some(device) = libkos::device::receive_device(ep)
.mmio
@@ -34,72 +48,11 @@ pub fn run() -> ! {
libkos::println!("storage: no block device, exiting");
syscall::exit();
};
let fat16 = fs::fat16::Fat16::mount(device);
if fat16.is_none() {
let Some(fat16) = fs::fat16::Fat16::mount(device) else {
libkos::println!("storage: FAT16 mount failed, exiting");
syscall::exit();
}
let mut fat16 = fat16.unwrap();
libkos::println!("storage: FAT16 mounted");
let mut next = syscall::receive_msg(ep);
loop {
let message = match next {
Ok(m) => m,
Err(e) => {
libkos::println!("storage: endpoint unusable ({:?}), exiting", e);
syscall::exit();
}
};
let [w0, w1, _, _] = message.words();
let reply = match w0 {
CMD_READ_FILE => {
let filename_len = w1;
let buf = shared_buf();
let filename_len = filename_len.min(buf.len());
let mut name_buf = [0u8; 32];
let name_len = filename_len.min(32);
name_buf[..name_len].copy_from_slice(&buf[..name_len]);
let name = core::str::from_utf8(&name_buf[..name_len]).unwrap_or("?");
libkos::print!("storage: read {}...", name);
let file_size = read_entire_file(&mut fat16, &name_buf[..name_len], buf);
if file_size > 0 {
libkos::println!("ok");
} else {
libkos::println!("NOT FOUND");
}
[file_size, 0, 0, 0]
}
_ => [0, 0, 0, 0],
};
next = match message {
syscall::Received::Call { .. } => syscall::reply_recv(ep, reply, None),
_ => syscall::receive_msg(ep),
};
}
}
fn read_entire_file<B: port_block_device::BlockDevice>(
fat: &mut fs::fat16::Fat16<B>,
filename: &[u8],
out: &mut [u8],
) -> usize {
use filesystem::FileSystem;
let handle = match fat.open(filename) {
Some(h) => h,
None => return 0,
};
let size = fat.file_size(handle).unwrap_or(0);
let to_read = size.min(out.len());
let read = fat.read(handle, &mut out[..to_read]).unwrap_or(0);
fat.close(handle);
read
libkos::println!("storage: FAT16 mounted");
let mut srv = server::StorageServer::new(fat16, RegionBuffers);
server_framework::serve(&mut srv, ep)
}