fix(clippy): make clippy = host libs + riscv programs (shared PROGRAMS); libkos alloc spinlock (was unlocked across threads); keystore Account; gpu Rect; factory Entry; Default impls, let-chains, div_ceil, iter loops

This commit is contained in:
2026-10-28 01:17:21 +01:00
parent 91f7d28834
commit c4871320a0
20 changed files with 188 additions and 134 deletions

View File

@@ -28,8 +28,11 @@ check:
fmt: fmt:
cargo fmt --all -- --check cargo fmt --all -- --check
# Libraries on the host; programs (no_std, they link only for RISC-V) for
# their target, as `build` builds them. The adapter is `kernel-clippy`'s.
clippy: clippy:
cargo clippy --workspace -- -D warnings cargo clippy --workspace --exclude adapter-riscv --lib -- -D warnings
cargo clippy --target $(RISCV_TARGET) --release $(PROGRAMS) -- -D warnings
test: test:
cargo test --workspace cargo test --workspace
@@ -48,14 +51,15 @@ kani-kernel:
kani: kani-types kani-domain kani-kernel kani: kani-types kani-domain kani-kernel
# Programs first: the kernel image packs init and storage (see the PROGRAMS = -p init -p echo -p sleep -p snake \
# adapter's build.rs).
build:
cargo build --target $(RISCV_TARGET) --release \
-p init -p echo -p sleep -p snake \
-p display-qemu -p input-qemu -p storage-qemu \ -p display-qemu -p input-qemu -p storage-qemu \
-p log-server -p permission-manager -p session-server -p smptest -p spawntest -p hello \ -p log-server -p permission-manager -p session-server -p smptest -p spawntest -p hello \
-p terminal-server -p shell -p cat -p ls -p counter -p gatekeeper -p terminal-server -p shell -p cat -p ls -p counter -p gatekeeper
# Programs first: the kernel image packs init and storage (see the
# adapter's build.rs).
build:
cargo build --target $(RISCV_TARGET) --release $(PROGRAMS)
cargo build --target $(RISCV_TARGET) --release -p adapter-riscv cargo build --target $(RISCV_TARGET) --release -p adapter-riscv
# The keystore holds the owner's sealed keys: made once, never rebuilt. # The keystore holds the owner's sealed keys: made once, never rebuilt.

View File

@@ -44,6 +44,12 @@ pub struct Snake {
rng_state: u32, rng_state: u32,
} }
impl Default for Snake {
fn default() -> Self {
Self::new()
}
}
impl Snake { impl Snake {
pub fn new() -> Self { pub fn new() -> Self {
let start = Position { x: GRID_WIDTH / 2, y: GRID_HEIGHT / 2 }; let start = Position { x: GRID_WIDTH / 2, y: GRID_HEIGHT / 2 };

View File

@@ -61,8 +61,7 @@ fn main() {
loop { loop {
let count = renderer::render(&snake, &mut commands); let count = renderer::render(&snake, &mut commands);
for i in 0..count { for cmd in &commands[..count] {
let cmd = &commands[i];
draw_rect(cmd.x, cmd.y, cmd.w, cmd.h, cmd.color); draw_rect(cmd.x, cmd.y, cmd.w, cmd.h, cmd.color);
} }
flush(); flush();

View File

@@ -148,7 +148,8 @@ impl Framebuffer for VirtioFramebuffer {
fn flush(&mut self) { fn flush(&mut self) {
let w = self.width.raw() as u32; let w = self.width.raw() as u32;
let h = self.height.raw() as u32; let h = self.height.raw() as u32;
gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, 0, 0, w, h); let whole = gpu::Rect { x: 0, y: 0, w, h };
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, 0, 0, w, h); gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, whole);
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, whole);
} }
} }

View File

@@ -56,18 +56,27 @@ pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddr
submit_cmd(mmio, queue, dma, 48) submit_cmd(mmio, queue, dma, 48)
} }
pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { /// A rectangle of a resource, in pixels.
#[derive(Clone, Copy, Debug)]
pub struct Rect {
pub x: u32,
pub y: u32,
pub w: u32,
pub h: u32,
}
pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, rect: Rect) -> bool {
write_cmd_header(&dma.cmd, 0x0105); write_cmd_header(&dma.cmd, 0x0105);
put_rect(&dma.cmd, x, y, w, h); put_rect(&dma.cmd, rect);
// Offset into the resource's memory, then the resource id. // Offset into the resource's memory, then the resource id.
dma.cmd.write(BODY + 16, 0u64); dma.cmd.write(BODY + 16, 0u64);
dma.cmd.write(BODY + 24, resource_id); dma.cmd.write(BODY + 24, resource_id);
submit_cmd(mmio, queue, dma, 56) submit_cmd(mmio, queue, dma, 56)
} }
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, rect: Rect) -> bool {
write_cmd_header(&dma.cmd, 0x0104); write_cmd_header(&dma.cmd, 0x0104);
put_rect(&dma.cmd, x, y, w, h); put_rect(&dma.cmd, rect);
dma.cmd.write(BODY + 16, resource_id); dma.cmd.write(BODY + 16, resource_id);
submit_cmd(mmio, queue, dma, 48) submit_cmd(mmio, queue, dma, 48)
} }
@@ -77,11 +86,11 @@ fn write_cmd_header(cmd: &Window, cmd_type: u32) {
cmd.write(TYPE, cmd_type); cmd.write(TYPE, cmd_type);
} }
fn put_rect(cmd: &Window, x: u32, y: u32, w: u32, h: u32) { fn put_rect(cmd: &Window, r: Rect) {
cmd.write(RECT_X, x); cmd.write(RECT_X, r.x);
cmd.write(RECT_Y, y); cmd.write(RECT_Y, r.y);
cmd.write(RECT_W, w); cmd.write(RECT_W, r.w);
cmd.write(RECT_H, h); cmd.write(RECT_H, r.h);
} }
pub fn get_display_info(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs) -> Option<(u32, u32)> { pub fn get_display_info(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs) -> Option<(u32, u32)> {

View File

@@ -43,16 +43,30 @@ impl Path {
} }
} }
/// Up to `BATCH` entries of `dir`: names and whether each is a folder. /// One thing in a folder.
fn entries(fs: &mut Storage, dir: &Path) -> Option<([([u8; NAME], usize, bool); BATCH], usize)> { #[derive(Clone, Copy)]
let mut out = [([0u8; NAME], 0, false); BATCH]; struct Entry {
name: [u8; NAME],
len: usize,
is_dir: bool,
}
impl Entry {
fn name(&self) -> &[u8] {
&self.name[..self.len]
}
}
/// Up to `BATCH` entries of `dir`.
fn entries(fs: &mut Storage, dir: &Path) -> Option<([Entry; BATCH], usize)> {
let mut out = [Entry { name: [0; NAME], len: 0, is_dir: false }; BATCH];
let mut n = 0; let mut n = 0;
fs.list_dir(dir.as_bytes(), |e| { fs.list_dir(dir.as_bytes(), |e| {
let name = e.name(); let name = e.name();
if name != b"." && name != b".." && n < BATCH { if name != b"." && name != b".." && n < BATCH {
out[n].0[..name.len()].copy_from_slice(name); out[n].name[..name.len()].copy_from_slice(name);
out[n].1 = name.len(); out[n].len = name.len();
out[n].2 = e.is_dir; out[n].is_dir = e.is_dir;
n += 1; n += 1;
} }
}) })
@@ -83,9 +97,9 @@ fn copy_tree(fs: &mut Storage, from: &Path, to: &Path, buf: &mut [u8]) -> Result
// Nothing shipped for this area. // Nothing shipped for this area.
None => return Ok(()), None => return Ok(()),
}; };
for (name, len, is_dir) in &list[..n] { for entry in &list[..n] {
let (src, dst) = (from.join(&name[..*len]).ok_or("path too long")?, to.join(&name[..*len]).ok_or("path too long")?); let (src, dst) = (from.join(entry.name()).ok_or("path too long")?, to.join(entry.name()).ok_or("path too long")?);
if *is_dir { if entry.is_dir {
fs.mkdir(dst.as_bytes()).map_err(|_| "cannot make a folder")?; fs.mkdir(dst.as_bytes()).map_err(|_| "cannot make a folder")?;
copy_tree(fs, &src, &dst, buf)?; copy_tree(fs, &src, &dst, buf)?;
} else { } else {

View File

@@ -54,7 +54,7 @@ pub(crate) fn load_binary(
let page_start = segment.vaddr & !0xFFF; let page_start = segment.vaddr & !0xFFF;
let offset_in_page = segment.vaddr & 0xFFF; let offset_in_page = segment.vaddr & 0xFFF;
let total_size = offset_in_page + segment.mem_size; let total_size = offset_in_page + segment.mem_size;
let seg_pages = (total_size + PAGE_SIZE - 1) / PAGE_SIZE; let seg_pages = total_size.div_ceil(PAGE_SIZE);
let page_count = PageCount::new(u16::try_from(seg_pages).ok()?)?; let page_count = PageCount::new(u16::try_from(seg_pages).ok()?)?;
let local = libkos::window::request(page_count)?; let local = libkos::window::request(page_count)?;

View File

@@ -21,6 +21,7 @@ mod user;
mod tests; mod tests;
pub use record::{ pub use record::{
Account,
Copies, Entry, KdfParams, Outcome, RECORD_SIZE, Record, Refused, SLOTS, Session, State, Copies, Entry, KdfParams, Outcome, RECORD_SIZE, Record, Refused, SLOTS, Session, State,
}; };
pub use user::{Name, Role, User, UserId}; pub use user::{Name, Role, User, UserId};

View File

@@ -154,6 +154,15 @@ fn parse_user(role: u8, id: &[u8], name: &[u8]) -> Option<User> {
}) })
} }
/// A user about to be added: who, and the passcodes that will open their
/// slot (and, if given, their duress slot).
pub struct Account<'a> {
pub name: Name,
pub role: Role,
pub passcode: &'a [u8],
pub duress: Option<&'a [u8]>,
}
impl Record { impl Record {
fn name_tag(&self, name: &Name) -> [u8; NAME_TAG] { fn name_tag(&self, name: &Name) -> [u8; NAME_TAG] {
let mut m = <Blake2sMac256 as KeyInit>::new_from_slice(&self.pepper) let mut m = <Blake2sMac256 as KeyInit>::new_from_slice(&self.pepper)
@@ -273,7 +282,7 @@ impl Record {
admin: Some(admin), admin: Some(admin),
}; };
let (record, user) = first let (record, user) = first
.add_user(&owner, name, Role::Owner, passcode, duress, memory, rng) .add_user(&owner, Account { name, role: Role::Owner, passcode, duress }, memory, rng)
.ok()?; .ok()?;
let session = record.unlock(name.as_bytes(), passcode, memory); let session = record.unlock(name.as_bytes(), passcode, memory);
let _ = user; let _ = user;
@@ -371,16 +380,8 @@ impl Record {
} }
/// An owner adds a user; the new record and the user. /// An owner adds a user; the new record and the user.
pub fn add_user( pub fn add_user(&self, by: &Session, account: Account, memory: &mut [Block], rng: &mut impl Random) -> Result<(Self, User), Refused> {
&self, let Account { name, role, passcode, duress } = account;
by: &Session,
name: Name,
role: Role,
passcode: &[u8],
duress: Option<&[u8]>,
memory: &mut [Block],
rng: &mut impl Random,
) -> Result<(Self, User), Refused> {
let admin = by.admin.clone().ok_or(Refused::NotOwner)?; let admin = by.admin.clone().ok_or(Refused::NotOwner)?;
let mut entries = self.entries(&admin).ok_or(Refused::Failed)?; let mut entries = self.entries(&admin).ok_or(Refused::Failed)?;
if entries.iter().flatten().any(|e| e.user.name == name) { if entries.iter().flatten().any(|e| e.user.name == name) {
@@ -467,10 +468,10 @@ impl Record {
return Err(Refused::LastOwner); return Err(Refused::LastOwner);
} }
let mut r = self.next(|_| {}); let mut r = self.next(|_| {});
for i in 0..SLOTS { for (entry, slot) in entries.iter_mut().zip(r.slots.iter_mut()) {
if entries[i].is_some_and(|e| e.user.id == gone.id) { if entry.is_some_and(|e| e.user.id == gone.id) {
r.slots[i] = Slot(noise(rng).ok_or(Refused::Failed)?); *slot = Slot(noise(rng).ok_or(Refused::Failed)?);
entries[i] = None; *entry = None;
} }
} }
if !r.seal_directory(&admin, &entries, rng) { if !r.seal_directory(&admin, &entries, rng) {

View File

@@ -79,15 +79,7 @@ fn users_have_their_own_keys_and_may_even_share_a_passcode() {
let (r, owner) = setup(None, 2); let (r, owner) = setup(None, 2);
let mut rng = Counter(40); let mut rng = Counter(40);
let (r, work) = r let (r, work) = r
.add_user( .add_user(&owner, Account { name: name("work"), role: Role::Standard, passcode: b"1234", duress: None }, &mut memory(), &mut rng)
&owner,
name("work"),
Role::Standard,
b"1234",
None,
&mut memory(),
&mut rng,
)
.unwrap(); .unwrap();
let w = signed_in(&r, "work", b"1234").unwrap(); let w = signed_in(&r, "work", b"1234").unwrap();
let g = signed_in(&r, "gabriel", b"1234").unwrap(); let g = signed_in(&r, "gabriel", b"1234").unwrap();
@@ -105,15 +97,7 @@ fn users_have_their_own_keys_and_may_even_share_a_passcode() {
"not even hidden in their slot" "not even hidden in their slot"
); );
assert_eq!( assert_eq!(
r.add_user( r.add_user(&w, Account { name: name("x"), role: Role::Standard, passcode: b"9", duress: None }, &mut memory(), &mut rng)
&w,
name("x"),
Role::Standard,
b"9",
None,
&mut memory(),
&mut rng
)
.err(), .err(),
Some(Refused::NotOwner) Some(Refused::NotOwner)
); );
@@ -125,26 +109,10 @@ fn owners_see_and_manage_the_users() {
let (r, owner) = setup(Some(b"0000"), 3); let (r, owner) = setup(Some(b"0000"), 3);
let mut rng = Counter(50); let mut rng = Counter(50);
let (r, _) = r let (r, _) = r
.add_user( .add_user(&owner, Account { name: name("work"), role: Role::Standard, passcode: b"5678", duress: Some(b"8765") }, &mut memory(), &mut rng)
&owner,
name("work"),
Role::Standard,
b"5678",
Some(b"8765"),
&mut memory(),
&mut rng,
)
.unwrap(); .unwrap();
let (r, second) = r let (r, second) = r
.add_user( .add_user(&owner, Account { name: name("anna"), role: Role::Owner, passcode: b"4321", duress: None }, &mut memory(), &mut rng)
&owner,
name("anna"),
Role::Owner,
b"4321",
None,
&mut memory(),
&mut rng,
)
.unwrap(); .unwrap();
let names: Vec<_> = r let names: Vec<_> = r
.users(&owner) .users(&owner)
@@ -155,15 +123,7 @@ fn owners_see_and_manage_the_users() {
.collect(); .collect();
assert_eq!(names.len(), 3, "duress slots are not listed: {names:?}"); assert_eq!(names.len(), 3, "duress slots are not listed: {names:?}");
assert_eq!( assert_eq!(
r.add_user( r.add_user(&owner, Account { name: name("work"), role: Role::Standard, passcode: b"x", duress: None }, &mut memory(), &mut rng)
&owner,
name("work"),
Role::Standard,
b"x",
None,
&mut memory(),
&mut rng
)
.err(), .err(),
Some(Refused::NameTaken) Some(Refused::NameTaken)
); );
@@ -205,15 +165,7 @@ fn slots_run_out() {
let mut added = 0; let mut added = 0;
for i in 0..SLOTS { for i in 0..SLOTS {
let n = std::format!("u{i}"); let n = std::format!("u{i}");
match r.add_user( match r.add_user(&owner, Account { name: name(&n), role: Role::Standard, passcode: b"pass", duress: None }, &mut memory(), &mut rng) {
&owner,
name(&n),
Role::Standard,
b"pass",
None,
&mut memory(),
&mut rng,
) {
Ok((next, _)) => { Ok((next, _)) => {
r = next; r = next;
added += 1; added += 1;

View File

@@ -1,6 +1,8 @@
use core::alloc::{GlobalAlloc, Layout}; use core::alloc::{GlobalAlloc, Layout};
use core::cell::UnsafeCell; use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use core::ptr; use core::ptr;
use core::sync::atomic::{AtomicBool, Ordering};
const PAGE_SIZE: usize = 4096; const PAGE_SIZE: usize = 4096;
@@ -10,6 +12,8 @@ struct FreeBlock {
} }
pub struct KosAllocator { pub struct KosAllocator {
/// A program's threads share the heap: one at a time in the free list.
locked: AtomicBool,
inner: UnsafeCell<AllocatorInner>, inner: UnsafeCell<AllocatorInner>,
} }
@@ -24,12 +28,45 @@ unsafe impl Sync for KosAllocator {}
impl KosAllocator { impl KosAllocator {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
locked: AtomicBool::new(false),
inner: UnsafeCell::new(AllocatorInner { free_list: ptr::null_mut() }), inner: UnsafeCell::new(AllocatorInner { free_list: ptr::null_mut() }),
} }
} }
fn inner_mut(&self) -> &mut AllocatorInner { fn lock(&self) -> Locked<'_> {
unsafe { &mut *self.inner.get() } while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
core::hint::spin_loop();
}
Locked(self)
}
}
impl Default for KosAllocator {
fn default() -> Self {
Self::new()
}
}
/// The free list, held until dropped.
struct Locked<'a>(&'a KosAllocator);
impl Deref for Locked<'_> {
type Target = AllocatorInner;
fn deref(&self) -> &AllocatorInner {
// Only one `Locked` exists at a time.
unsafe { &*self.0.inner.get() }
}
}
impl DerefMut for Locked<'_> {
fn deref_mut(&mut self) -> &mut AllocatorInner {
unsafe { &mut *self.0.inner.get() }
}
}
impl Drop for Locked<'_> {
fn drop(&mut self) {
self.0.locked.store(false, Ordering::Release);
} }
} }
@@ -103,7 +140,7 @@ impl AllocatorInner {
unsafe impl GlobalAlloc for KosAllocator { unsafe impl GlobalAlloc for KosAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 { unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let inner = self.inner_mut(); let mut inner = self.lock();
let size = layout.size().max(core::mem::size_of::<FreeBlock>()); let size = layout.size().max(core::mem::size_of::<FreeBlock>());
let align = layout.align(); let align = layout.align();
@@ -113,7 +150,7 @@ unsafe impl GlobalAlloc for KosAllocator {
} }
// Alignment can cost up to `align` bytes of the block. // Alignment can cost up to `align` bytes of the block.
let pages_needed = (size + align + PAGE_SIZE - 1) / PAGE_SIZE; let pages_needed = (size + align).div_ceil(PAGE_SIZE);
let block = inner.request_pages(pages_needed); let block = inner.request_pages(pages_needed);
if block.is_null() { if block.is_null() {
return ptr::null_mut(); return ptr::null_mut();
@@ -124,7 +161,7 @@ unsafe impl GlobalAlloc for KosAllocator {
} }
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
let inner = self.inner_mut(); let mut inner = self.lock();
let size = layout.size().max(core::mem::size_of::<FreeBlock>()); let size = layout.size().max(core::mem::size_of::<FreeBlock>());
inner.add_to_free_list(ptr, size); inner.add_to_free_list(ptr, size);
} }

View File

@@ -12,12 +12,17 @@ use types::time::Ticks;
pub fn exit() -> ! { pub fn exit() -> ! {
let _ = call0(SYS_EXIT); let _ = call0(SYS_EXIT);
loop {} // The kernel does not return from an exit.
loop {
core::hint::spin_loop();
}
} }
pub fn exit_thread() -> ! { pub fn exit_thread() -> ! {
let _ = call0(SYS_EXIT_THREAD); let _ = call0(SYS_EXIT_THREAD);
loop {} loop {
core::hint::spin_loop();
}
} }
pub fn yield_now() { pub fn yield_now() {
@@ -28,7 +33,9 @@ pub fn yield_now() {
/// call fails and this spins, since there is nothing sensible to return. /// call fails and this spins, since there is nothing sensible to return.
pub fn halt(control: CapSlot) -> ! { pub fn halt(control: CapSlot) -> ! {
let _ = raw(SYS_HALT, [control.value() as usize, 0, 0, 0, 0, 0]); let _ = raw(SYS_HALT, [control.value() as usize, 0, 0, 0, 0, 0]);
loop {} loop {
yield_now();
}
} }
pub fn get_time() -> Ticks { pub fn get_time() -> Ticks {

View File

@@ -2,7 +2,7 @@
//! randomness, and the owner (`Prompt`). No kernel, no hardware: tested on //! randomness, and the owner (`Prompt`). No kernel, no hardware: tested on
//! the host. //! the host.
use keystore::{Block, Copies, KdfParams, Key, Name, Outcome, RECORD_SIZE, Random, Record, Refused, Role, SLOTS, Session, User, UserId}; use keystore::{Account, Block, Copies, KdfParams, Key, Name, Outcome, RECORD_SIZE, Random, Record, Refused, Role, SLOTS, Session, User, UserId};
use zeroize::Zeroize; use zeroize::Zeroize;
/// The keystore device: the record's two copies. /// The keystore device: the record's two copies.
@@ -228,7 +228,7 @@ fn manage(ks: &mut impl Keystore, rng: &mut impl Random, prompt: &mut impl Promp
let (mut real, mut duress) = (Secret::new(), Secret::new()); let (mut real, mut duress) = (Secret::new(), Secret::new());
ask_passcodes(prompt, &mut real, &mut duress)?; ask_passcodes(prompt, &mut real, &mut duress)?;
let duress = (duress.len > 0).then_some(duress.as_bytes()); let duress = (duress.len > 0).then_some(duress.as_bytes());
record.add_user(session, name, role, real.as_bytes(), duress, memory, rng).map(|(r, _)| r) record.add_user(session, Account { name, role, passcode: real.as_bytes(), duress }, memory, rng).map(|(r, _)| r)
} }
b"r" => { b"r" => {
let name = ask_name(prompt, "Remove whom: ")?; let name = ask_name(prompt, "Remove whom: ")?;

View File

@@ -6,6 +6,12 @@ pub struct MockInputDevice {
tail: usize, tail: usize,
} }
impl Default for MockInputDevice {
fn default() -> Self {
Self::new()
}
}
impl MockInputDevice { impl MockInputDevice {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {

View File

@@ -18,6 +18,12 @@ pub struct LogServer<O: FnMut(&[u8]) = fn(&[u8])> {
out: O, out: O,
} }
impl Default for LogServer {
fn default() -> Self {
Self::new()
}
}
impl LogServer { impl LogServer {
/// Text goes nowhere. /// Text goes nowhere.
pub fn new() -> Self { pub fn new() -> Self {

View File

@@ -44,16 +44,21 @@ impl MockFileSystem {
fn find_by_name(&self, path: &[u8]) -> Option<usize> { fn find_by_name(&self, path: &[u8]) -> Option<usize> {
for i in 0..MAX_FILES { for i in 0..MAX_FILES {
if let Some(entry) = &self.files[i] { if let Some(entry) = &self.files[i]
if entry.name == *path { && entry.name == *path {
return Some(i); return Some(i);
} }
} }
}
None None
} }
} }
impl Default for MockFileSystem {
fn default() -> Self {
Self::new()
}
}
impl FileSystem for MockFileSystem { impl FileSystem for MockFileSystem {
fn writable(&self) -> bool { fn writable(&self) -> bool {
!self.read_only !self.read_only

View File

@@ -39,6 +39,10 @@ impl FileName {
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.len as usize self.len as usize
} }
pub fn is_empty(&self) -> bool {
self.len == 0
}
} }
impl PartialEq<[u8]> for FileName { impl PartialEq<[u8]> for FileName {

View File

@@ -37,11 +37,10 @@ pub fn find_entry<B: BlockDevice>(
let name_len = block_buf[pos + 6] as usize; let name_len = block_buf[pos + 6] as usize;
if rec_len == 0 { break; } if rec_len == 0 { break; }
if entry_inode != 0 && name_len == name.len() { if entry_inode != 0 && name_len == name.len()
if &block_buf[pos + 8..pos + 8 + name_len] == name { && &block_buf[pos + 8..pos + 8 + name_len] == name {
return Some(entry_inode); return Some(entry_inode);
} }
}
pos += rec_len; pos += rec_len;
} }
} }
@@ -147,8 +146,8 @@ pub fn remove_entry<B: BlockDevice>(
let name_len = block_buf[pos + 6] as usize; let name_len = block_buf[pos + 6] as usize;
if rec_len == 0 { break; } if rec_len == 0 { break; }
if entry_inode != 0 && name_len == name.len() { if entry_inode != 0 && name_len == name.len()
if &block_buf[pos + 8..pos + 8 + name_len] == name { && &block_buf[pos + 8..pos + 8 + name_len] == name {
if let Some(pp) = prev_pos { if let Some(pp) = prev_pos {
let prev_rec = read_u16(&block_buf, pp + 4) as usize; let prev_rec = read_u16(&block_buf, pp + 4) as usize;
write_u16(&mut block_buf, pp + 4, (prev_rec + rec_len) as u16); write_u16(&mut block_buf, pp + 4, (prev_rec + rec_len) as u16);
@@ -158,7 +157,6 @@ pub fn remove_entry<B: BlockDevice>(
write_block(device, phys, spb, &block_buf); write_block(device, phys, spb, &block_buf);
return Some(entry_inode); return Some(entry_inode);
} }
}
prev_pos = Some(pos); prev_pos = Some(pos);
pos += rec_len; pos += rec_len;
} }

View File

@@ -131,7 +131,9 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
done += chunk; done += chunk;
} }
self.open_files[handle.raw() as usize].as_mut().map(|f| f.offset += done); if let Some(f) = self.open_files[handle.raw() as usize].as_mut() {
f.offset += done;
}
Some(done) Some(done)
} }
@@ -204,14 +206,13 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
fn close(&mut self, handle: FileHandle) { fn close(&mut self, handle: FileHandle) {
if handle.raw() as usize >= MAX_OPEN_FILES { return; } 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[handle.raw() as usize].take()
if let Some(mut inode) = self.read_inode(file.inode_num) { && let Some(mut inode) = self.read_inode(file.inode_num) {
inode.size = file.size as u64; inode.size = file.size as u64;
inode.block_data = file.block_data; inode.block_data = file.block_data;
self.write_inode(file.inode_num, &inode); self.write_inode(file.inode_num, &inode);
} }
} }
}
fn list_dir(&mut self, path: &[u8], entries: &mut [DirEntry]) -> Option<usize> { fn list_dir(&mut self, path: &[u8], entries: &mut [DirEntry]) -> Option<usize> {
let dir_inode_num = if path.is_empty() || path == b"/" { let dir_inode_num = if path.is_empty() || path == b"/" {
@@ -222,13 +223,12 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
let inode = self.read_inode(dir_inode_num)?; let inode = self.read_inode(dir_inode_num)?;
let count = dir::list_entries(&mut self.device, &self.sb, &inode, entries)?; let count = dir::list_entries(&mut self.device, &self.sb, &inode, entries)?;
for i in 0..count { for entry in entries.iter_mut().take(count) {
if !entries[i].is_dir { if !entry.is_dir {
let name = entries[i].name; let name = entry.name;
if let Some(child_ino) = dir::find_entry(&mut self.device, &self.sb, &inode, name.as_bytes()) { if let Some(child_ino) = dir::find_entry(&mut self.device, &self.sb, &inode, name.as_bytes())
if let Some(child) = self.read_inode(child_ino) { && let Some(child) = self.read_inode(child_ino) {
entries[i].size = child.size as usize; entry.size = child.size as usize;
}
} }
} }
} }
@@ -279,11 +279,10 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
None => return false, None => return false,
}; };
if let Some(child_inode) = self.read_inode(child_inode_num) { if let Some(child_inode) = self.read_inode(child_inode_num)
if child_inode.is_dir() { && child_inode.is_dir() {
return false; return false;
} }
}
let parent_inode = match self.read_inode(parent_inode_num) { let parent_inode = match self.read_inode(parent_inode_num) {
Some(i) => i, Some(i) => i,
@@ -299,12 +298,11 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
self.clear_inode(child_inode_num); self.clear_inode(child_inode_num);
self.free_inode(child_inode_num); self.free_inode(child_inode_num);
for slot in self.open_files.iter_mut() { for slot in self.open_files.iter_mut() {
if let Some(f) = slot { if let Some(f) = slot
if f.inode_num == child_inode_num { && f.inode_num == child_inode_num {
*slot = None; *slot = None;
} }
} }
}
true true
} }

View File

@@ -102,3 +102,9 @@ impl Grants {
} }
} }
} }
impl Default for Grants {
fn default() -> Self {
Self::new()
}
}