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:
@@ -1,126 +1,129 @@
|
||||
//! The storage protocol. A client first `Attach`es: the server makes it a
|
||||
//! buffer region and replies with a capability to it, which the client
|
||||
//! maps. Paths, file data and directory listings then travel through that
|
||||
//! buffer; the message words carry only lengths and handles, and the
|
||||
//! server clamps every length to the buffer. Clients are told apart by
|
||||
//! their endpoint badge, and a handle only works for the client that
|
||||
//! opened it.
|
||||
|
||||
use server_framework::{Decode, Encode};
|
||||
use crate::filesystem::FileHandle;
|
||||
|
||||
pub use types::storage::{BUFFER_PAGES, BUFFER_SIZE, DIR_ENTRY_SIZE, DIR_NAME_SIZE, MAX_PATH};
|
||||
use types::storage::{op, reply};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct PackedPath {
|
||||
word0: usize,
|
||||
word1: usize,
|
||||
}
|
||||
|
||||
impl PackedPath {
|
||||
pub fn from_bytes(src: &[u8]) -> Self {
|
||||
let mut w0 = [0u8; 8];
|
||||
let mut w1 = [0u8; 8];
|
||||
for (i, &b) in src.iter().enumerate() {
|
||||
if i < 8 { w0[i] = b; }
|
||||
else if i < 16 { w1[i - 8] = b; }
|
||||
}
|
||||
Self {
|
||||
word0: usize::from_le_bytes(w0),
|
||||
word1: usize::from_le_bytes(w1),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_words(word0: usize, word1: usize) -> Self {
|
||||
Self { word0, word1 }
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> [u8; 16] {
|
||||
let mut buf = [0u8; 16];
|
||||
buf[..8].copy_from_slice(&self.word0.to_le_bytes());
|
||||
buf[8..16].copy_from_slice(&self.word1.to_le_bytes());
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn word0(&self) -> usize { self.word0 }
|
||||
pub fn word1(&self) -> usize { self.word1 }
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum StorageRequest {
|
||||
Open { path: PackedPath },
|
||||
Read { handle: FileHandle, max_bytes: usize },
|
||||
Write { handle: FileHandle, data_word: usize },
|
||||
Attach,
|
||||
/// The path is the first `path_len` bytes of the buffer.
|
||||
Open { path_len: usize },
|
||||
/// Reads up to `len` bytes into the buffer.
|
||||
Read { handle: FileHandle, len: usize },
|
||||
/// Writes the first `len` bytes of the buffer.
|
||||
Write { handle: FileHandle, len: usize },
|
||||
Close { handle: FileHandle },
|
||||
ListDir { path: PackedPath },
|
||||
Create { path: PackedPath },
|
||||
Delete { path: PackedPath },
|
||||
FileSize { handle: FileHandle },
|
||||
Mkdir { path: PackedPath },
|
||||
/// Fills the buffer with `DIR_ENTRY_SIZE` records.
|
||||
ListDir { path_len: usize },
|
||||
Create { path_len: usize },
|
||||
Delete { path_len: usize },
|
||||
Mkdir { path_len: usize },
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum StorageResponse {
|
||||
/// Carries the buffer capability; `size` bytes are usable.
|
||||
Attached { size: usize },
|
||||
Handle(FileHandle),
|
||||
Data { bytes_read: usize },
|
||||
Written { bytes_written: usize },
|
||||
Data { bytes: usize },
|
||||
Written { bytes: usize },
|
||||
Ok,
|
||||
DirEntries { count: usize },
|
||||
Size(usize),
|
||||
NotFound,
|
||||
Error,
|
||||
/// The disk cannot be written.
|
||||
ReadOnly,
|
||||
/// The client has not attached a buffer yet.
|
||||
NotAttached,
|
||||
}
|
||||
|
||||
fn handle(word: usize) -> Option<FileHandle> {
|
||||
u16::try_from(word).ok().map(FileHandle::new)
|
||||
}
|
||||
|
||||
impl Encode for StorageRequest {
|
||||
fn encode(&self) -> [usize; 4] {
|
||||
match self {
|
||||
StorageRequest::Open { path } => [0, path.word0(), path.word1(), 0],
|
||||
StorageRequest::Read { handle, max_bytes } => [1, handle.raw() as usize, *max_bytes, 0],
|
||||
StorageRequest::Write { handle, data_word } => [2, handle.raw() as usize, *data_word, 0],
|
||||
StorageRequest::Close { handle } => [3, handle.raw() as usize, 0, 0],
|
||||
StorageRequest::ListDir { path } => [4, path.word0(), path.word1(), 0],
|
||||
StorageRequest::Create { path } => [5, path.word0(), path.word1(), 0],
|
||||
StorageRequest::Delete { path } => [6, path.word0(), path.word1(), 0],
|
||||
StorageRequest::FileSize { handle } => [7, handle.raw() as usize, 0, 0],
|
||||
StorageRequest::Mkdir { path } => [8, path.word0(), path.word1(), 0],
|
||||
use StorageRequest::*;
|
||||
match *self {
|
||||
Attach => [op::ATTACH, 0, 0, 0],
|
||||
Open { path_len } => [op::OPEN, path_len, 0, 0],
|
||||
Read { handle, len } => [op::READ, handle.raw() as usize, len, 0],
|
||||
Write { handle, len } => [op::WRITE, handle.raw() as usize, len, 0],
|
||||
Close { handle } => [op::CLOSE, handle.raw() as usize, 0, 0],
|
||||
FileSize { handle } => [op::FILE_SIZE, handle.raw() as usize, 0, 0],
|
||||
ListDir { path_len } => [op::LIST_DIR, path_len, 0, 0],
|
||||
Create { path_len } => [op::CREATE, path_len, 0, 0],
|
||||
Delete { path_len } => [op::DELETE, path_len, 0, 0],
|
||||
Mkdir { path_len } => [op::MKDIR, path_len, 0, 0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for StorageRequest {
|
||||
fn decode(words: [usize; 4]) -> Option<Self> {
|
||||
match words[0] {
|
||||
0 => Some(StorageRequest::Open { path: PackedPath::from_words(words[1], words[2]) }),
|
||||
1 => Some(StorageRequest::Read { handle: FileHandle::new(words[1] as u16), max_bytes: words[2] }),
|
||||
2 => Some(StorageRequest::Write { handle: FileHandle::new(words[1] as u16), data_word: words[2] }),
|
||||
3 => Some(StorageRequest::Close { handle: FileHandle::new(words[1] as u16) }),
|
||||
4 => Some(StorageRequest::ListDir { path: PackedPath::from_words(words[1], words[2]) }),
|
||||
5 => Some(StorageRequest::Create { path: PackedPath::from_words(words[1], words[2]) }),
|
||||
6 => Some(StorageRequest::Delete { path: PackedPath::from_words(words[1], words[2]) }),
|
||||
7 => Some(StorageRequest::FileSize { handle: FileHandle::new(words[1] as u16) }),
|
||||
8 => Some(StorageRequest::Mkdir { path: PackedPath::from_words(words[1], words[2]) }),
|
||||
_ => None,
|
||||
}
|
||||
fn decode(w: [usize; 4]) -> Option<Self> {
|
||||
use StorageRequest::*;
|
||||
Some(match w[0] {
|
||||
op::ATTACH => Attach,
|
||||
op::OPEN => Open { path_len: w[1] },
|
||||
op::READ => Read { handle: handle(w[1])?, len: w[2] },
|
||||
op::WRITE => Write { handle: handle(w[1])?, len: w[2] },
|
||||
op::CLOSE => Close { handle: handle(w[1])? },
|
||||
op::FILE_SIZE => FileSize { handle: handle(w[1])? },
|
||||
op::LIST_DIR => ListDir { path_len: w[1] },
|
||||
op::CREATE => Create { path_len: w[1] },
|
||||
op::DELETE => Delete { path_len: w[1] },
|
||||
op::MKDIR => Mkdir { path_len: w[1] },
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Encode for StorageResponse {
|
||||
fn encode(&self) -> [usize; 4] {
|
||||
match self {
|
||||
StorageResponse::Handle(h) => [0, h.raw() as usize, 0, 0],
|
||||
StorageResponse::Data { bytes_read } => [1, *bytes_read, 0, 0],
|
||||
StorageResponse::Written { bytes_written } => [2, *bytes_written, 0, 0],
|
||||
StorageResponse::Ok => [3, 0, 0, 0],
|
||||
StorageResponse::DirEntries { count } => [4, *count, 0, 0],
|
||||
StorageResponse::Size(s) => [5, *s, 0, 0],
|
||||
StorageResponse::NotFound => [6, 0, 0, 0],
|
||||
StorageResponse::Error => [7, 0, 0, 0],
|
||||
use StorageResponse::*;
|
||||
match *self {
|
||||
Attached { size } => [reply::ATTACHED, size, 0, 0],
|
||||
Handle(h) => [reply::HANDLE, h.raw() as usize, 0, 0],
|
||||
Data { bytes } => [reply::DATA, bytes, 0, 0],
|
||||
Written { bytes } => [reply::WRITTEN, bytes, 0, 0],
|
||||
Ok => [reply::OK, 0, 0, 0],
|
||||
DirEntries { count } => [reply::DIR_ENTRIES, count, 0, 0],
|
||||
Size(s) => [reply::SIZE, s, 0, 0],
|
||||
NotFound => [reply::NOT_FOUND, 0, 0, 0],
|
||||
Error => [reply::ERROR, 0, 0, 0],
|
||||
ReadOnly => [reply::READ_ONLY, 0, 0, 0],
|
||||
NotAttached => [reply::NOT_ATTACHED, 0, 0, 0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Decode for StorageResponse {
|
||||
fn decode(words: [usize; 4]) -> Option<Self> {
|
||||
match words[0] {
|
||||
0 => Some(StorageResponse::Handle(FileHandle::new(words[1] as u16))),
|
||||
1 => Some(StorageResponse::Data { bytes_read: words[1] }),
|
||||
2 => Some(StorageResponse::Written { bytes_written: words[1] }),
|
||||
3 => Some(StorageResponse::Ok),
|
||||
4 => Some(StorageResponse::DirEntries { count: words[1] }),
|
||||
5 => Some(StorageResponse::Size(words[1])),
|
||||
6 => Some(StorageResponse::NotFound),
|
||||
7 => Some(StorageResponse::Error),
|
||||
_ => None,
|
||||
}
|
||||
fn decode(w: [usize; 4]) -> Option<Self> {
|
||||
use StorageResponse::*;
|
||||
Some(match w[0] {
|
||||
reply::ATTACHED => Attached { size: w[1] },
|
||||
reply::HANDLE => Handle(handle(w[1])?),
|
||||
reply::DATA => Data { bytes: w[1] },
|
||||
reply::WRITTEN => Written { bytes: w[1] },
|
||||
reply::OK => Ok,
|
||||
reply::DIR_ENTRIES => DirEntries { count: w[1] },
|
||||
reply::SIZE => Size(w[1]),
|
||||
reply::NOT_FOUND => NotFound,
|
||||
reply::ERROR => Error,
|
||||
reply::READ_ONLY => ReadOnly,
|
||||
reply::NOT_ATTACHED => NotAttached,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user