fix: storage tables grow (handles, open files, encrypted opens, grants: Slots/Vec, no 8/16 caps; release follows all narrowed grants); storage says FULL, init spawn STORAGE_BUSY (was NOT_FOUND), pm BUSY; cap slots per process 64 -> 255
This commit is contained in:
@@ -12,9 +12,7 @@
|
||||
use filecrypt::{CHUNK, Drbg, HEADER, Header, Key, OVERHEAD, PLAIN_CHUNK, area_key, chunk_offset, chunks, plain_len};
|
||||
|
||||
use crate::filesystem::{DirEntry, FileHandle, FileSystem};
|
||||
|
||||
/// The inner file system's handles this layer can track.
|
||||
const MAX_HANDLES: usize = 16;
|
||||
use crate::slots::Slots;
|
||||
|
||||
/// Where a path falls.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
@@ -66,7 +64,8 @@ pub struct Encrypted<F: FileSystem> {
|
||||
user: [u8; USER_NAME],
|
||||
user_len: usize,
|
||||
rng: Option<Drbg>,
|
||||
open: [Option<Open>; MAX_HANDLES],
|
||||
/// Sealed files open, by the inner file system's handle.
|
||||
open: Slots<Open>,
|
||||
}
|
||||
|
||||
/// A user folder's name: 16 hex digits, or `guest`.
|
||||
@@ -74,7 +73,7 @@ pub const USER_NAME: usize = 16;
|
||||
|
||||
impl<F: FileSystem> Encrypted<F> {
|
||||
pub fn new(inner: F) -> Self {
|
||||
Self { inner, master: None, user: [0; USER_NAME], user_len: 0, rng: None, open: [const { None }; MAX_HANDLES] }
|
||||
Self { inner, master: None, user: [0; USER_NAME], user_len: 0, rng: None, open: Slots::new() }
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &F {
|
||||
@@ -114,10 +113,6 @@ impl<F: FileSystem> Encrypted<F> {
|
||||
}
|
||||
}
|
||||
|
||||
fn slot(&mut self, h: FileHandle) -> Option<&mut Option<Open>> {
|
||||
self.open.get_mut(h.raw() as usize)
|
||||
}
|
||||
|
||||
/// Chunk `index` of a file of `size` bytes, opened into `out`; its length.
|
||||
fn read_chunk(inner: &mut F, h: FileHandle, key: &Key, size: usize, index: usize, out: &mut [u8; PLAIN_CHUNK]) -> Option<usize> {
|
||||
let last = index + 1 == chunks(size);
|
||||
@@ -153,10 +148,10 @@ impl<F: FileSystem> FileSystem for Encrypted<F> {
|
||||
}
|
||||
|
||||
fn lock(&mut self) {
|
||||
for (i, slot) in self.open.iter_mut().enumerate() {
|
||||
if slot.take().is_some() {
|
||||
self.inner.close(FileHandle::new(i as u16));
|
||||
}
|
||||
let open: alloc::vec::Vec<usize> = self.open.iter().map(|(i, _)| i).collect();
|
||||
for i in open {
|
||||
self.open.take(i);
|
||||
self.inner.close(FileHandle::new(i as u16));
|
||||
}
|
||||
self.master = None;
|
||||
self.rng = None;
|
||||
@@ -183,7 +178,7 @@ impl<F: FileSystem> FileSystem for Encrypted<F> {
|
||||
return None;
|
||||
};
|
||||
let key = header.file_key(&area);
|
||||
*self.slot(h)? = Some(Open { key, size, pos: 0 });
|
||||
self.open.put(h.raw() as usize, Open { key, size, pos: 0 });
|
||||
Some(h)
|
||||
}
|
||||
|
||||
@@ -203,12 +198,12 @@ impl<F: FileSystem> FileSystem for Encrypted<F> {
|
||||
let _ = self.inner.delete(path);
|
||||
return None;
|
||||
}
|
||||
*self.slot(h)? = Some(Open { key: file_key, size: 0, pos: 0 });
|
||||
self.open.put(h.raw() as usize, Open { key: file_key, size: 0, pos: 0 });
|
||||
Some(h)
|
||||
}
|
||||
|
||||
fn read(&mut self, h: FileHandle, buf: &mut [u8]) -> Option<usize> {
|
||||
let Some(Some(f)) = self.open.get(h.raw() as usize) else { return self.inner.read(h, buf) };
|
||||
let Some(f) = self.open.get(h.raw() as usize) else { return self.inner.read(h, buf) };
|
||||
let (key, size, mut pos) = (f.key.clone(), f.size, f.pos);
|
||||
let mut done = 0;
|
||||
let mut chunk = [0u8; PLAIN_CHUNK];
|
||||
@@ -222,12 +217,12 @@ impl<F: FileSystem> FileSystem for Encrypted<F> {
|
||||
pos += take;
|
||||
}
|
||||
chunk.fill(0);
|
||||
self.slot(h)?.as_mut()?.pos = pos;
|
||||
self.open.get_mut(h.raw() as usize)?.pos = pos;
|
||||
Some(done)
|
||||
}
|
||||
|
||||
fn write(&mut self, h: FileHandle, buf: &[u8]) -> Option<usize> {
|
||||
let Some(Some(f)) = self.open.get(h.raw() as usize) else { return self.inner.write(h, buf) };
|
||||
let Some(f) = self.open.get(h.raw() as usize) else { return self.inner.write(h, buf) };
|
||||
let (key, old_size, pos) = (f.key.clone(), f.size, f.pos);
|
||||
let new_size = old_size.max(pos + buf.len());
|
||||
let (old_last, new_last) = (chunks(old_size) - 1, chunks(new_size) - 1);
|
||||
@@ -253,34 +248,32 @@ impl<F: FileSystem> FileSystem for Encrypted<F> {
|
||||
}
|
||||
}
|
||||
chunk.fill(0);
|
||||
let f = self.slot(h)?.as_mut()?;
|
||||
let f = self.open.get_mut(h.raw() as usize)?;
|
||||
f.size = new_size;
|
||||
f.pos = pos + buf.len();
|
||||
Some(buf.len())
|
||||
}
|
||||
|
||||
fn seek(&mut self, h: FileHandle, offset: usize) -> bool {
|
||||
match self.slot(h) {
|
||||
Some(Some(f)) if offset <= f.size => {
|
||||
match self.open.get_mut(h.raw() as usize) {
|
||||
Some(f) if offset <= f.size => {
|
||||
f.pos = offset;
|
||||
true
|
||||
}
|
||||
Some(Some(_)) => false,
|
||||
_ => self.inner.seek(h, offset),
|
||||
Some(_) => false,
|
||||
None => self.inner.seek(h, offset),
|
||||
}
|
||||
}
|
||||
|
||||
fn close(&mut self, h: FileHandle) {
|
||||
if let Some(slot) = self.slot(h) {
|
||||
*slot = None;
|
||||
}
|
||||
self.open.take(h.raw() as usize);
|
||||
self.inner.close(h);
|
||||
}
|
||||
|
||||
fn file_size(&mut self, h: FileHandle) -> Option<usize> {
|
||||
match self.open.get(h.raw() as usize) {
|
||||
Some(Some(f)) => Some(f.size),
|
||||
_ => self.inner.file_size(h),
|
||||
Some(f) => Some(f.size),
|
||||
None => self.inner.file_size(h),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ mod path;
|
||||
|
||||
use port_block_device::BlockDevice;
|
||||
use crate::filesystem::{DirEntry, FileHandle, FileSystem};
|
||||
use crate::slots::Slots;
|
||||
use io::read_block;
|
||||
use superblock::Superblock;
|
||||
|
||||
const ROOT_INODE: u32 = 2;
|
||||
const MAX_OPEN_FILES: usize = 8;
|
||||
const EXTENT_MAGIC: u16 = 0xF30A;
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ pub struct Ext4<B: BlockDevice> {
|
||||
device: B,
|
||||
sb: Superblock,
|
||||
gdt_block: u32,
|
||||
open_files: [Option<OpenFile>; MAX_OPEN_FILES],
|
||||
/// Files open, by handle: as many as storage's memory holds.
|
||||
open_files: Slots<OpenFile>,
|
||||
}
|
||||
|
||||
impl<B: BlockDevice> Ext4<B> {
|
||||
@@ -47,7 +48,7 @@ impl<B: BlockDevice> Ext4<B> {
|
||||
device,
|
||||
sb,
|
||||
gdt_block,
|
||||
open_files: [const { None }; MAX_OPEN_FILES],
|
||||
open_files: Slots::new(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
if !inode.is_file() { return None; }
|
||||
|
||||
let handle = self.alloc_handle()?;
|
||||
self.open_files[handle.raw() as usize] = Some(OpenFile {
|
||||
self.open_files.put(handle.raw() as usize, OpenFile {
|
||||
inode_num,
|
||||
size: inode.size as usize,
|
||||
offset: 0,
|
||||
@@ -96,8 +97,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
}
|
||||
|
||||
fn read(&mut self, handle: FileHandle, buf: &mut [u8]) -> Option<usize> {
|
||||
if handle.raw() as usize >= MAX_OPEN_FILES { return None; }
|
||||
let file = *self.open_files[handle.raw() as usize].as_ref()?;
|
||||
let file = *self.open_files.get(handle.raw() as usize)?;
|
||||
if file.offset >= file.size { return Some(0); }
|
||||
|
||||
// As much as `buf` holds, across blocks: a short read means the end.
|
||||
@@ -131,15 +131,14 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
done += chunk;
|
||||
}
|
||||
|
||||
if let Some(f) = self.open_files[handle.raw() as usize].as_mut() {
|
||||
if let Some(f) = self.open_files.get_mut(handle.raw() as usize) {
|
||||
f.offset += done;
|
||||
}
|
||||
Some(done)
|
||||
}
|
||||
|
||||
fn write(&mut self, handle: FileHandle, buf: &[u8]) -> Option<usize> {
|
||||
if handle.raw() as usize >= MAX_OPEN_FILES { return None; }
|
||||
let file = *self.open_files[handle.raw() as usize].as_ref()?;
|
||||
let file = *self.open_files.get(handle.raw() as usize)?;
|
||||
let bs = self.sb.block_size;
|
||||
let mut written = 0;
|
||||
let mut offset = file.offset;
|
||||
@@ -186,7 +185,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
}
|
||||
|
||||
if written > 0 {
|
||||
if let Some(f) = self.open_files[handle.raw() as usize].as_mut() {
|
||||
if let Some(f) = self.open_files.get_mut(handle.raw() as usize) {
|
||||
f.offset = offset;
|
||||
f.size = size;
|
||||
f.block_data = block_data;
|
||||
@@ -205,8 +204,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
}
|
||||
|
||||
fn close(&mut self, handle: FileHandle) {
|
||||
if handle.raw() as usize >= MAX_OPEN_FILES { return; }
|
||||
if let Some(file) = self.open_files[handle.raw() as usize].take()
|
||||
if let Some(file) = self.open_files.take(handle.raw() as usize)
|
||||
&& let Some(mut inode) = self.read_inode(file.inode_num) {
|
||||
inode.size = file.size as u64;
|
||||
inode.block_data = file.block_data;
|
||||
@@ -255,7 +253,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
}
|
||||
|
||||
let handle = self.alloc_handle()?;
|
||||
self.open_files[handle.raw() as usize] = Some(OpenFile {
|
||||
self.open_files.put(handle.raw() as usize, OpenFile {
|
||||
inode_num: new_inode_num,
|
||||
size: 0,
|
||||
offset: 0,
|
||||
@@ -297,18 +295,15 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
}
|
||||
self.clear_inode(child_inode_num);
|
||||
self.free_inode(child_inode_num);
|
||||
for slot in self.open_files.iter_mut() {
|
||||
if let Some(f) = slot
|
||||
&& f.inode_num == child_inode_num {
|
||||
*slot = None;
|
||||
}
|
||||
let gone: alloc::vec::Vec<usize> = self.open_files.iter().filter(|(_, f)| f.inode_num == child_inode_num).map(|(i, _)| i).collect();
|
||||
for i in gone {
|
||||
self.open_files.take(i);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn file_size(&mut self, handle: FileHandle) -> Option<usize> {
|
||||
if handle.raw() as usize >= MAX_OPEN_FILES { return None; }
|
||||
self.open_files[handle.raw() as usize].as_ref().map(|f| f.size)
|
||||
self.open_files.get(handle.raw() as usize).map(|f| f.size)
|
||||
}
|
||||
|
||||
fn mkdir(&mut self, path: &[u8]) -> bool {
|
||||
@@ -370,7 +365,7 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
}
|
||||
|
||||
fn seek(&mut self, handle: FileHandle, offset: usize) -> bool {
|
||||
match self.open_files.get_mut(handle.raw() as usize).and_then(Option::as_mut) {
|
||||
match self.open_files.get_mut(handle.raw() as usize) {
|
||||
Some(f) if offset <= f.size => {
|
||||
f.offset = offset;
|
||||
true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Path resolution, directory entry operations and allocation wrappers.
|
||||
|
||||
use super::{bitmap, dir, Ext4, MAX_OPEN_FILES, ROOT_INODE};
|
||||
use super::{bitmap, dir, Ext4, ROOT_INODE};
|
||||
use crate::filesystem::FileHandle;
|
||||
use port_block_device::BlockDevice;
|
||||
|
||||
@@ -58,12 +58,7 @@ impl<B: BlockDevice> Ext4<B> {
|
||||
}
|
||||
|
||||
pub(super) fn alloc_handle(&self) -> Option<FileHandle> {
|
||||
for i in 0..MAX_OPEN_FILES {
|
||||
if self.open_files[i].is_none() {
|
||||
return Some(FileHandle::new(i as u16));
|
||||
}
|
||||
}
|
||||
None
|
||||
self.open_files.free().map(|i| FileHandle::new(i as u16))
|
||||
}
|
||||
|
||||
pub(super) fn resolve_parent<'a>(&mut self, path: &'a [u8]) -> Option<(u32, &'a [u8])> {
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
//! endpoint, so a badge it does not know (never issued, or released) is
|
||||
//! no authority at all.
|
||||
|
||||
use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use types::bounded::CapSlot;
|
||||
|
||||
use crate::scope::Scope;
|
||||
|
||||
pub const MAX_GRANTS: usize = 16;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Grant {
|
||||
badge: usize,
|
||||
@@ -20,7 +20,8 @@ struct Grant {
|
||||
}
|
||||
|
||||
pub struct Grants {
|
||||
grants: [Option<Grant>; MAX_GRANTS],
|
||||
/// As many as storage's memory holds.
|
||||
grants: Vec<Grant>,
|
||||
/// Badges are never reused, so a released one stays dead.
|
||||
next_badge: usize,
|
||||
}
|
||||
@@ -28,7 +29,7 @@ pub struct Grants {
|
||||
impl Grants {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
grants: [None; MAX_GRANTS],
|
||||
grants: Vec::new(),
|
||||
next_badge: 1,
|
||||
}
|
||||
}
|
||||
@@ -39,7 +40,6 @@ impl Grants {
|
||||
b => self
|
||||
.grants
|
||||
.iter()
|
||||
.flatten()
|
||||
.find(|g| g.badge == b)
|
||||
.map(|g| g.scope),
|
||||
}
|
||||
@@ -53,11 +53,10 @@ impl Grants {
|
||||
scope: Scope,
|
||||
mint: impl FnOnce(usize) -> Option<CapSlot>,
|
||||
) -> Option<CapSlot> {
|
||||
let free = self.grants.iter_mut().find(|g| g.is_none())?;
|
||||
let badge = self.next_badge;
|
||||
let cap = mint(badge)?;
|
||||
self.next_badge += 1;
|
||||
*free = Some(Grant {
|
||||
self.grants.push(Grant {
|
||||
badge,
|
||||
scope,
|
||||
parent,
|
||||
@@ -66,14 +65,9 @@ impl Grants {
|
||||
Some(cap)
|
||||
}
|
||||
|
||||
/// The grants init made directly, into `out`; how many.
|
||||
pub fn roots(&self, out: &mut [usize; MAX_GRANTS]) -> usize {
|
||||
let mut n = 0;
|
||||
for g in self.grants.iter().flatten().filter(|g| g.parent == 0) {
|
||||
out[n] = g.badge;
|
||||
n += 1;
|
||||
}
|
||||
n
|
||||
/// The grants init made directly.
|
||||
pub fn roots(&self) -> Vec<usize> {
|
||||
self.grants.iter().filter(|g| g.parent == 0).map(|g| g.badge).collect()
|
||||
}
|
||||
|
||||
/// Forgets `badge` and every grant narrowed from it, transitively,
|
||||
@@ -82,24 +76,19 @@ impl Grants {
|
||||
if badge == 0 {
|
||||
return;
|
||||
}
|
||||
let mut doomed = [0usize; MAX_GRANTS];
|
||||
doomed[0] = badge;
|
||||
let mut n = 1;
|
||||
let mut doomed = vec![badge];
|
||||
let mut i = 0;
|
||||
while i < n {
|
||||
let b = doomed[i];
|
||||
while let Some(&b) = doomed.get(i) {
|
||||
i += 1;
|
||||
for g in &mut self.grants {
|
||||
let Some(grant) = g else { continue };
|
||||
if grant.badge == b {
|
||||
gone(grant.badge, grant.cap);
|
||||
*g = None;
|
||||
} else if grant.parent == b && n < MAX_GRANTS {
|
||||
doomed[n] = grant.badge;
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
doomed.extend(self.grants.iter().filter(|g| g.parent == b).map(|g| g.badge));
|
||||
}
|
||||
self.grants.retain(|g| {
|
||||
let dies = doomed.contains(&g.badge);
|
||||
if dies {
|
||||
gone(g.badge, g.cap);
|
||||
}
|
||||
!dies
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ pub mod grants;
|
||||
pub mod protocol;
|
||||
pub mod scope;
|
||||
pub mod server;
|
||||
mod slots;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "tests/mod.rs"]
|
||||
|
||||
@@ -62,6 +62,8 @@ pub enum StorageResponse {
|
||||
/// Carries the new capability.
|
||||
Granted,
|
||||
Denied,
|
||||
/// No connection free.
|
||||
Full,
|
||||
}
|
||||
|
||||
fn handle(word: usize) -> Option<FileHandle> {
|
||||
@@ -143,6 +145,7 @@ impl Encode for StorageResponse {
|
||||
NotAttached => [reply::NOT_ATTACHED, 0, 0, 0],
|
||||
Granted => [reply::GRANTED, 0, 0, 0],
|
||||
Denied => [reply::DENIED, 0, 0, 0],
|
||||
Full => [reply::FULL, 0, 0, 0],
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -164,6 +167,7 @@ impl Decode for StorageResponse {
|
||||
reply::NOT_ATTACHED => NotAttached,
|
||||
reply::GRANTED => Granted,
|
||||
reply::DENIED => Denied,
|
||||
reply::FULL => Full,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,9 +10,8 @@ use crate::scope::Scope;
|
||||
mod session;
|
||||
|
||||
use types::storage::MAX_CLIENTS;
|
||||
/// Handles at or past this are never valid (the file systems hand out
|
||||
/// fewer), so ownership fits a small table.
|
||||
const MAX_HANDLES: usize = 16;
|
||||
|
||||
use crate::slots::Slots;
|
||||
const MAX_LISTED: usize = 64;
|
||||
|
||||
/// What the server needs from the kernel: regions for client buffers
|
||||
@@ -50,7 +49,8 @@ pub struct StorageServer<F: FileSystem, H: Host> {
|
||||
host: H,
|
||||
grants: Grants,
|
||||
clients: [Option<Client>; MAX_CLIENTS],
|
||||
owners: [Option<Owner>; MAX_HANDLES],
|
||||
/// Who opened each handle, by handle.
|
||||
owners: Slots<Owner>,
|
||||
log: Option<CapSlot>,
|
||||
/// The master key's and the seed's halves, from the gatekeeper through
|
||||
/// init; handed to the file system (and forgotten here) once all in.
|
||||
@@ -68,7 +68,7 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
host,
|
||||
grants: Grants::new(),
|
||||
clients: [const { None }; MAX_CLIENTS],
|
||||
owners: [None; MAX_HANDLES],
|
||||
owners: Slots::new(),
|
||||
log: None,
|
||||
key: [None; 2],
|
||||
seed: [None; 2],
|
||||
@@ -144,7 +144,8 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
);
|
||||
}
|
||||
let Some(index) = self.clients.iter().position(Option::is_none) else {
|
||||
return (StorageResponse::Error, None);
|
||||
libkos::println!("storage: every connection ({}) is in use", MAX_CLIENTS);
|
||||
return (StorageResponse::Full, None);
|
||||
};
|
||||
let Some((buf, cap)) = self.host.create(index) else {
|
||||
return (StorageResponse::Error, None);
|
||||
@@ -176,11 +177,10 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
} = self;
|
||||
grants.release(badge, |gone, cap| {
|
||||
host.revoke(cap);
|
||||
for (raw, owner) in owners.iter_mut().enumerate() {
|
||||
if owner.is_some_and(|o| o.badge == gone) {
|
||||
fs.close(FileHandle::new(raw as u16));
|
||||
*owner = None;
|
||||
}
|
||||
let theirs: alloc::vec::Vec<usize> = owners.iter().filter(|(_, o)| o.badge == gone).map(|(raw, _)| raw).collect();
|
||||
for raw in theirs {
|
||||
fs.close(FileHandle::new(raw as u16));
|
||||
owners.take(raw);
|
||||
}
|
||||
for c in clients
|
||||
.iter_mut()
|
||||
@@ -198,7 +198,7 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
}
|
||||
|
||||
fn owner(&self, handle: FileHandle) -> Option<Owner> {
|
||||
self.owners.get(handle.raw() as usize).copied().flatten()
|
||||
self.owners.get(handle.raw() as usize).copied()
|
||||
}
|
||||
|
||||
/// Serves one request from `badge`, which holds `scope`.
|
||||
@@ -269,18 +269,11 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
resolved => resolved.flatten(),
|
||||
};
|
||||
let path = disk.as_ref().map_or(&[][..], |p| p.as_bytes());
|
||||
let opened = |owners: &mut [Option<Owner>; MAX_HANDLES], h: Option<FileHandle>| match h {
|
||||
Some(h) => match owners.get_mut(h.raw() as usize) {
|
||||
Some(owner) => {
|
||||
*owner = Some(Owner {
|
||||
badge,
|
||||
write: scope.write,
|
||||
});
|
||||
R::Handle(h)
|
||||
}
|
||||
// A handle the table cannot track is one we cannot police.
|
||||
None => R::Error,
|
||||
},
|
||||
let opened = |owners: &mut Slots<Owner>, h: Option<FileHandle>| match h {
|
||||
Some(h) => {
|
||||
owners.put(h.raw() as usize, Owner { badge, write: scope.write });
|
||||
R::Handle(h)
|
||||
}
|
||||
None => R::NotFound,
|
||||
};
|
||||
let response = match request {
|
||||
@@ -311,9 +304,7 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
},
|
||||
Close { handle } => {
|
||||
fs.close(handle);
|
||||
if let Some(owner) = owners.get_mut(handle.raw() as usize) {
|
||||
*owner = None;
|
||||
}
|
||||
owners.take(handle.raw() as usize);
|
||||
R::Ok
|
||||
}
|
||||
FileSize { handle } => match fs.file_size(handle) {
|
||||
|
||||
@@ -6,7 +6,6 @@ use types::user::UserFolder;
|
||||
|
||||
use super::{Host, StorageServer};
|
||||
use crate::filesystem::{FileHandle, FileSystem};
|
||||
use crate::grants::MAX_GRANTS;
|
||||
use crate::protocol::{MAX_PATH, StorageResponse};
|
||||
|
||||
impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
@@ -46,15 +45,13 @@ impl<F: FileSystem, H: Host> StorageServer<F, H> {
|
||||
if badge != 0 {
|
||||
return StorageResponse::Error;
|
||||
}
|
||||
let mut roots = [0usize; MAX_GRANTS];
|
||||
let n = self.grants.roots(&mut roots);
|
||||
for &root in &roots[..n] {
|
||||
for root in self.grants.roots() {
|
||||
self.release(root);
|
||||
}
|
||||
for (raw, owner) in self.owners.iter_mut().enumerate() {
|
||||
if owner.take().is_some() {
|
||||
self.fs.close(FileHandle::new(raw as u16));
|
||||
}
|
||||
let open: alloc::vec::Vec<usize> = self.owners.iter().map(|(raw, _)| raw).collect();
|
||||
for raw in open {
|
||||
self.owners.take(raw);
|
||||
self.fs.close(FileHandle::new(raw as u16));
|
||||
}
|
||||
self.fs.lock();
|
||||
self.key = [None; 2];
|
||||
|
||||
55
crates/servers/storage-server/src/slots.rs
Normal file
55
crates/servers/storage-server/src/slots.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
//! A table that grows as it fills, bounded by storage's memory rather than
|
||||
//! a count: an entry keeps its index for life (a handle names it), and a
|
||||
//! freed index is reused first. At most what a `FileHandle` can name.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub const MAX: usize = u16::MAX as usize + 1;
|
||||
|
||||
pub struct Slots<T>(Vec<Option<T>>);
|
||||
|
||||
impl<T> Slots<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
|
||||
/// The index the next entry would get, if there is room for one.
|
||||
pub fn free(&self) -> Option<usize> {
|
||||
let i = self.0.iter().position(Option::is_none).unwrap_or(self.0.len());
|
||||
(i < MAX).then_some(i)
|
||||
}
|
||||
|
||||
/// Puts `value` at `index`, growing to it.
|
||||
pub fn put(&mut self, index: usize, value: T) {
|
||||
if index >= MAX {
|
||||
return;
|
||||
}
|
||||
if self.0.len() <= index {
|
||||
self.0.resize_with(index + 1, || None);
|
||||
}
|
||||
self.0[index] = Some(value);
|
||||
}
|
||||
|
||||
pub fn get(&self, index: usize) -> Option<&T> {
|
||||
self.0.get(index)?.as_ref()
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
|
||||
self.0.get_mut(index)?.as_mut()
|
||||
}
|
||||
|
||||
pub fn take(&mut self, index: usize) -> Option<T> {
|
||||
self.0.get_mut(index)?.take()
|
||||
}
|
||||
|
||||
/// Every entry, with its index.
|
||||
pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
|
||||
self.0.iter().enumerate().filter_map(|(i, v)| v.as_ref().map(|v| (i, v)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for Slots<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
@@ -595,3 +595,18 @@ fn delete_nonempty_dir_returns_false() {
|
||||
let count = fs.list_dir(b"parent", &mut entries).unwrap();
|
||||
assert_eq!(count, 1);
|
||||
}
|
||||
|
||||
/// Open files were a table of 8: now as many as memory holds.
|
||||
#[test]
|
||||
fn many_files_open_at_once() {
|
||||
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
||||
let handles: alloc::vec::Vec<_> = (0..40).map(|_| fs.open(b"hello").expect("opens")).collect();
|
||||
let mut buf = [0u8; 32];
|
||||
assert_eq!(fs.read(handles[39], &mut buf), Some(13));
|
||||
for h in &handles[..20] {
|
||||
fs.close(*h);
|
||||
}
|
||||
// Closed handles are used again.
|
||||
let again = fs.open(b"hello").unwrap();
|
||||
assert!(handles[..20].contains(&again));
|
||||
}
|
||||
|
||||
@@ -532,3 +532,32 @@ fn only_init_purges_only_users_and_only_while_locked() {
|
||||
assert_eq!(purge(&mut s, 0, b"users/x"), StorageResponse::Ok);
|
||||
assert_eq!(purge(&mut s, 0, b"users"), StorageResponse::Ok);
|
||||
}
|
||||
|
||||
/// Grants were a table of 16, and releasing followed at most 16 narrowed
|
||||
/// from one: neither is a limit any more.
|
||||
#[test]
|
||||
fn many_grants_and_all_narrowed_from_one_go_on_release() {
|
||||
let mut s = server();
|
||||
let root = Client::attach(&mut s, 0);
|
||||
let siblings: Vec<usize> = (0..20).map(|_| root.grant(&mut s, b"", true)).collect();
|
||||
let parent = Client::attach(&mut s, siblings[0]);
|
||||
let children: Vec<usize> = (0..20).map(|_| parent.grant(&mut s, b"", false)).collect();
|
||||
assert_eq!(call(&mut s, parent.badge, StorageRequest::Release).0, StorageResponse::Ok);
|
||||
for badge in children {
|
||||
assert_eq!(call(&mut s, badge, StorageRequest::Attach).0, StorageResponse::Denied, "child {badge}");
|
||||
}
|
||||
assert_eq!(call(&mut s, siblings[19], StorageRequest::Attach).0, StorageResponse::Attached { size: 4096 }, "a sibling stays");
|
||||
}
|
||||
|
||||
/// Past the last connection, storage says it is full, not that it failed.
|
||||
#[test]
|
||||
fn a_connection_past_the_last_is_told_storage_is_full() {
|
||||
let mut s = server();
|
||||
let root = Client::attach(&mut s, 0);
|
||||
let grants: Vec<usize> = (1..types::storage::MAX_CLIENTS).map(|_| root.grant(&mut s, b"", false)).collect();
|
||||
for g in &grants {
|
||||
Client::attach(&mut s, *g);
|
||||
}
|
||||
let one_more = root.grant(&mut s, b"", false);
|
||||
assert_eq!(call(&mut s, one_more, StorageRequest::Attach).0, StorageResponse::Full);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user