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:
16
Makefile
16
Makefile
@@ -28,8 +28,11 @@ check:
|
||||
fmt:
|
||||
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:
|
||||
cargo clippy --workspace -- -D warnings
|
||||
cargo clippy --workspace --exclude adapter-riscv --lib -- -D warnings
|
||||
cargo clippy --target $(RISCV_TARGET) --release $(PROGRAMS) -- -D warnings
|
||||
|
||||
test:
|
||||
cargo test --workspace
|
||||
@@ -48,14 +51,15 @@ kani-kernel:
|
||||
|
||||
kani: kani-types kani-domain kani-kernel
|
||||
|
||||
PROGRAMS = -p init -p echo -p sleep -p snake \
|
||||
-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 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 \
|
||||
-p init -p echo -p sleep -p snake \
|
||||
-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 terminal-server -p shell -p cat -p ls -p counter -p gatekeeper
|
||||
cargo build --target $(RISCV_TARGET) --release $(PROGRAMS)
|
||||
cargo build --target $(RISCV_TARGET) --release -p adapter-riscv
|
||||
|
||||
# The keystore holds the owner's sealed keys: made once, never rebuilt.
|
||||
|
||||
@@ -44,6 +44,12 @@ pub struct Snake {
|
||||
rng_state: u32,
|
||||
}
|
||||
|
||||
impl Default for Snake {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Snake {
|
||||
pub fn new() -> Self {
|
||||
let start = Position { x: GRID_WIDTH / 2, y: GRID_HEIGHT / 2 };
|
||||
|
||||
@@ -61,8 +61,7 @@ fn main() {
|
||||
|
||||
loop {
|
||||
let count = renderer::render(&snake, &mut commands);
|
||||
for i in 0..count {
|
||||
let cmd = &commands[i];
|
||||
for cmd in &commands[..count] {
|
||||
draw_rect(cmd.x, cmd.y, cmd.w, cmd.h, cmd.color);
|
||||
}
|
||||
flush();
|
||||
|
||||
@@ -148,7 +148,8 @@ impl Framebuffer for VirtioFramebuffer {
|
||||
fn flush(&mut self) {
|
||||
let w = self.width.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);
|
||||
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, RESOURCE_ID, 0, 0, w, h);
|
||||
let whole = gpu::Rect { x: 0, y: 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,18 +56,27 @@ pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddr
|
||||
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);
|
||||
put_rect(&dma.cmd, x, y, w, h);
|
||||
put_rect(&dma.cmd, rect);
|
||||
// Offset into the resource's memory, then the resource id.
|
||||
dma.cmd.write(BODY + 16, 0u64);
|
||||
dma.cmd.write(BODY + 24, resource_id);
|
||||
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);
|
||||
put_rect(&dma.cmd, x, y, w, h);
|
||||
put_rect(&dma.cmd, rect);
|
||||
dma.cmd.write(BODY + 16, resource_id);
|
||||
submit_cmd(mmio, queue, dma, 48)
|
||||
}
|
||||
@@ -77,11 +86,11 @@ fn write_cmd_header(cmd: &Window, cmd_type: u32) {
|
||||
cmd.write(TYPE, cmd_type);
|
||||
}
|
||||
|
||||
fn put_rect(cmd: &Window, x: u32, y: u32, w: u32, h: u32) {
|
||||
cmd.write(RECT_X, x);
|
||||
cmd.write(RECT_Y, y);
|
||||
cmd.write(RECT_W, w);
|
||||
cmd.write(RECT_H, h);
|
||||
fn put_rect(cmd: &Window, r: Rect) {
|
||||
cmd.write(RECT_X, r.x);
|
||||
cmd.write(RECT_Y, r.y);
|
||||
cmd.write(RECT_W, r.w);
|
||||
cmd.write(RECT_H, r.h);
|
||||
}
|
||||
|
||||
pub fn get_display_info(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs) -> Option<(u32, u32)> {
|
||||
|
||||
@@ -43,16 +43,30 @@ impl Path {
|
||||
}
|
||||
}
|
||||
|
||||
/// Up to `BATCH` entries of `dir`: names and whether each is a folder.
|
||||
fn entries(fs: &mut Storage, dir: &Path) -> Option<([([u8; NAME], usize, bool); BATCH], usize)> {
|
||||
let mut out = [([0u8; NAME], 0, false); BATCH];
|
||||
/// One thing in a folder.
|
||||
#[derive(Clone, Copy)]
|
||||
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;
|
||||
fs.list_dir(dir.as_bytes(), |e| {
|
||||
let name = e.name();
|
||||
if name != b"." && name != b".." && n < BATCH {
|
||||
out[n].0[..name.len()].copy_from_slice(name);
|
||||
out[n].1 = name.len();
|
||||
out[n].2 = e.is_dir;
|
||||
out[n].name[..name.len()].copy_from_slice(name);
|
||||
out[n].len = name.len();
|
||||
out[n].is_dir = e.is_dir;
|
||||
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.
|
||||
None => return Ok(()),
|
||||
};
|
||||
for (name, len, is_dir) in &list[..n] {
|
||||
let (src, dst) = (from.join(&name[..*len]).ok_or("path too long")?, to.join(&name[..*len]).ok_or("path too long")?);
|
||||
if *is_dir {
|
||||
for entry in &list[..n] {
|
||||
let (src, dst) = (from.join(entry.name()).ok_or("path too long")?, to.join(entry.name()).ok_or("path too long")?);
|
||||
if entry.is_dir {
|
||||
fs.mkdir(dst.as_bytes()).map_err(|_| "cannot make a folder")?;
|
||||
copy_tree(fs, &src, &dst, buf)?;
|
||||
} else {
|
||||
|
||||
@@ -54,7 +54,7 @@ pub(crate) fn load_binary(
|
||||
let page_start = segment.vaddr & !0xFFF;
|
||||
let offset_in_page = segment.vaddr & 0xFFF;
|
||||
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 local = libkos::window::request(page_count)?;
|
||||
|
||||
@@ -21,6 +21,7 @@ mod user;
|
||||
mod tests;
|
||||
|
||||
pub use record::{
|
||||
Account,
|
||||
Copies, Entry, KdfParams, Outcome, RECORD_SIZE, Record, Refused, SLOTS, Session, State,
|
||||
};
|
||||
pub use user::{Name, Role, User, UserId};
|
||||
|
||||
@@ -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 {
|
||||
fn name_tag(&self, name: &Name) -> [u8; NAME_TAG] {
|
||||
let mut m = <Blake2sMac256 as KeyInit>::new_from_slice(&self.pepper)
|
||||
@@ -273,7 +282,7 @@ impl Record {
|
||||
admin: Some(admin),
|
||||
};
|
||||
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()?;
|
||||
let session = record.unlock(name.as_bytes(), passcode, memory);
|
||||
let _ = user;
|
||||
@@ -371,16 +380,8 @@ impl Record {
|
||||
}
|
||||
|
||||
/// An owner adds a user; the new record and the user.
|
||||
pub fn add_user(
|
||||
&self,
|
||||
by: &Session,
|
||||
name: Name,
|
||||
role: Role,
|
||||
passcode: &[u8],
|
||||
duress: Option<&[u8]>,
|
||||
memory: &mut [Block],
|
||||
rng: &mut impl Random,
|
||||
) -> Result<(Self, User), Refused> {
|
||||
pub fn add_user(&self, by: &Session, account: Account, memory: &mut [Block], rng: &mut impl Random) -> Result<(Self, User), Refused> {
|
||||
let Account { name, role, passcode, duress } = account;
|
||||
let admin = by.admin.clone().ok_or(Refused::NotOwner)?;
|
||||
let mut entries = self.entries(&admin).ok_or(Refused::Failed)?;
|
||||
if entries.iter().flatten().any(|e| e.user.name == name) {
|
||||
@@ -467,10 +468,10 @@ impl Record {
|
||||
return Err(Refused::LastOwner);
|
||||
}
|
||||
let mut r = self.next(|_| {});
|
||||
for i in 0..SLOTS {
|
||||
if entries[i].is_some_and(|e| e.user.id == gone.id) {
|
||||
r.slots[i] = Slot(noise(rng).ok_or(Refused::Failed)?);
|
||||
entries[i] = None;
|
||||
for (entry, slot) in entries.iter_mut().zip(r.slots.iter_mut()) {
|
||||
if entry.is_some_and(|e| e.user.id == gone.id) {
|
||||
*slot = Slot(noise(rng).ok_or(Refused::Failed)?);
|
||||
*entry = None;
|
||||
}
|
||||
}
|
||||
if !r.seal_directory(&admin, &entries, rng) {
|
||||
|
||||
@@ -79,15 +79,7 @@ fn users_have_their_own_keys_and_may_even_share_a_passcode() {
|
||||
let (r, owner) = setup(None, 2);
|
||||
let mut rng = Counter(40);
|
||||
let (r, work) = r
|
||||
.add_user(
|
||||
&owner,
|
||||
name("work"),
|
||||
Role::Standard,
|
||||
b"1234",
|
||||
None,
|
||||
&mut memory(),
|
||||
&mut rng,
|
||||
)
|
||||
.add_user(&owner, Account { name: name("work"), role: Role::Standard, passcode: b"1234", duress: None }, &mut memory(), &mut rng)
|
||||
.unwrap();
|
||||
let w = signed_in(&r, "work", 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"
|
||||
);
|
||||
assert_eq!(
|
||||
r.add_user(
|
||||
&w,
|
||||
name("x"),
|
||||
Role::Standard,
|
||||
b"9",
|
||||
None,
|
||||
&mut memory(),
|
||||
&mut rng
|
||||
)
|
||||
r.add_user(&w, Account { name: name("x"), role: Role::Standard, passcode: b"9", duress: None }, &mut memory(), &mut rng)
|
||||
.err(),
|
||||
Some(Refused::NotOwner)
|
||||
);
|
||||
@@ -125,26 +109,10 @@ fn owners_see_and_manage_the_users() {
|
||||
let (r, owner) = setup(Some(b"0000"), 3);
|
||||
let mut rng = Counter(50);
|
||||
let (r, _) = r
|
||||
.add_user(
|
||||
&owner,
|
||||
name("work"),
|
||||
Role::Standard,
|
||||
b"5678",
|
||||
Some(b"8765"),
|
||||
&mut memory(),
|
||||
&mut rng,
|
||||
)
|
||||
.add_user(&owner, Account { name: name("work"), role: Role::Standard, passcode: b"5678", duress: Some(b"8765") }, &mut memory(), &mut rng)
|
||||
.unwrap();
|
||||
let (r, second) = r
|
||||
.add_user(
|
||||
&owner,
|
||||
name("anna"),
|
||||
Role::Owner,
|
||||
b"4321",
|
||||
None,
|
||||
&mut memory(),
|
||||
&mut rng,
|
||||
)
|
||||
.add_user(&owner, Account { name: name("anna"), role: Role::Owner, passcode: b"4321", duress: None }, &mut memory(), &mut rng)
|
||||
.unwrap();
|
||||
let names: Vec<_> = r
|
||||
.users(&owner)
|
||||
@@ -155,15 +123,7 @@ fn owners_see_and_manage_the_users() {
|
||||
.collect();
|
||||
assert_eq!(names.len(), 3, "duress slots are not listed: {names:?}");
|
||||
assert_eq!(
|
||||
r.add_user(
|
||||
&owner,
|
||||
name("work"),
|
||||
Role::Standard,
|
||||
b"x",
|
||||
None,
|
||||
&mut memory(),
|
||||
&mut rng
|
||||
)
|
||||
r.add_user(&owner, Account { name: name("work"), role: Role::Standard, passcode: b"x", duress: None }, &mut memory(), &mut rng)
|
||||
.err(),
|
||||
Some(Refused::NameTaken)
|
||||
);
|
||||
@@ -205,15 +165,7 @@ fn slots_run_out() {
|
||||
let mut added = 0;
|
||||
for i in 0..SLOTS {
|
||||
let n = std::format!("u{i}");
|
||||
match r.add_user(
|
||||
&owner,
|
||||
name(&n),
|
||||
Role::Standard,
|
||||
b"pass",
|
||||
None,
|
||||
&mut memory(),
|
||||
&mut rng,
|
||||
) {
|
||||
match r.add_user(&owner, Account { name: name(&n), role: Role::Standard, passcode: b"pass", duress: None }, &mut memory(), &mut rng) {
|
||||
Ok((next, _)) => {
|
||||
r = next;
|
||||
added += 1;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use core::alloc::{GlobalAlloc, Layout};
|
||||
use core::cell::UnsafeCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::ptr;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
|
||||
@@ -10,6 +12,8 @@ struct FreeBlock {
|
||||
}
|
||||
|
||||
pub struct KosAllocator {
|
||||
/// A program's threads share the heap: one at a time in the free list.
|
||||
locked: AtomicBool,
|
||||
inner: UnsafeCell<AllocatorInner>,
|
||||
}
|
||||
|
||||
@@ -24,12 +28,45 @@ unsafe impl Sync for KosAllocator {}
|
||||
impl KosAllocator {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
locked: AtomicBool::new(false),
|
||||
inner: UnsafeCell::new(AllocatorInner { free_list: ptr::null_mut() }),
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_mut(&self) -> &mut AllocatorInner {
|
||||
unsafe { &mut *self.inner.get() }
|
||||
fn lock(&self) -> Locked<'_> {
|
||||
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 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 align = layout.align();
|
||||
|
||||
@@ -113,7 +150,7 @@ unsafe impl GlobalAlloc for KosAllocator {
|
||||
}
|
||||
|
||||
// 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);
|
||||
if block.is_null() {
|
||||
return ptr::null_mut();
|
||||
@@ -124,7 +161,7 @@ unsafe impl GlobalAlloc for KosAllocator {
|
||||
}
|
||||
|
||||
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>());
|
||||
inner.add_to_free_list(ptr, size);
|
||||
}
|
||||
|
||||
@@ -12,12 +12,17 @@ use types::time::Ticks;
|
||||
|
||||
pub fn exit() -> ! {
|
||||
let _ = call0(SYS_EXIT);
|
||||
loop {}
|
||||
// The kernel does not return from an exit.
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exit_thread() -> ! {
|
||||
let _ = call0(SYS_EXIT_THREAD);
|
||||
loop {}
|
||||
loop {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn yield_now() {
|
||||
@@ -28,7 +33,9 @@ pub fn yield_now() {
|
||||
/// call fails and this spins, since there is nothing sensible to return.
|
||||
pub fn halt(control: CapSlot) -> ! {
|
||||
let _ = raw(SYS_HALT, [control.value() as usize, 0, 0, 0, 0, 0]);
|
||||
loop {}
|
||||
loop {
|
||||
yield_now();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_time() -> Ticks {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! randomness, and the owner (`Prompt`). No kernel, no hardware: tested on
|
||||
//! 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;
|
||||
|
||||
/// 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());
|
||||
ask_passcodes(prompt, &mut real, &mut duress)?;
|
||||
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" => {
|
||||
let name = ask_name(prompt, "Remove whom: ")?;
|
||||
|
||||
@@ -6,6 +6,12 @@ pub struct MockInputDevice {
|
||||
tail: usize,
|
||||
}
|
||||
|
||||
impl Default for MockInputDevice {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MockInputDevice {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
|
||||
@@ -18,6 +18,12 @@ pub struct LogServer<O: FnMut(&[u8]) = fn(&[u8])> {
|
||||
out: O,
|
||||
}
|
||||
|
||||
impl Default for LogServer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl LogServer {
|
||||
/// Text goes nowhere.
|
||||
pub fn new() -> Self {
|
||||
|
||||
@@ -44,16 +44,21 @@ impl MockFileSystem {
|
||||
|
||||
fn find_by_name(&self, path: &[u8]) -> Option<usize> {
|
||||
for i in 0..MAX_FILES {
|
||||
if let Some(entry) = &self.files[i] {
|
||||
if entry.name == *path {
|
||||
if let Some(entry) = &self.files[i]
|
||||
&& entry.name == *path {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MockFileSystem {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystem for MockFileSystem {
|
||||
fn writable(&self) -> bool {
|
||||
!self.read_only
|
||||
|
||||
@@ -39,6 +39,10 @@ impl FileName {
|
||||
pub fn len(&self) -> usize {
|
||||
self.len as usize
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<[u8]> for FileName {
|
||||
|
||||
@@ -37,11 +37,10 @@ pub fn find_entry<B: BlockDevice>(
|
||||
let name_len = block_buf[pos + 6] as usize;
|
||||
|
||||
if rec_len == 0 { break; }
|
||||
if entry_inode != 0 && name_len == name.len() {
|
||||
if &block_buf[pos + 8..pos + 8 + name_len] == name {
|
||||
if entry_inode != 0 && name_len == name.len()
|
||||
&& &block_buf[pos + 8..pos + 8 + name_len] == name {
|
||||
return Some(entry_inode);
|
||||
}
|
||||
}
|
||||
pos += rec_len;
|
||||
}
|
||||
}
|
||||
@@ -147,8 +146,8 @@ pub fn remove_entry<B: BlockDevice>(
|
||||
let name_len = block_buf[pos + 6] as usize;
|
||||
|
||||
if rec_len == 0 { break; }
|
||||
if entry_inode != 0 && name_len == name.len() {
|
||||
if &block_buf[pos + 8..pos + 8 + name_len] == name {
|
||||
if entry_inode != 0 && name_len == name.len()
|
||||
&& &block_buf[pos + 8..pos + 8 + name_len] == name {
|
||||
if let Some(pp) = prev_pos {
|
||||
let prev_rec = read_u16(&block_buf, pp + 4) as usize;
|
||||
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);
|
||||
return Some(entry_inode);
|
||||
}
|
||||
}
|
||||
prev_pos = Some(pos);
|
||||
pos += rec_len;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,9 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -204,13 +206,12 @@ 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(mut inode) = self.read_inode(file.inode_num) {
|
||||
if let Some(file) = self.open_files[handle.raw() as usize].take()
|
||||
&& let Some(mut inode) = self.read_inode(file.inode_num) {
|
||||
inode.size = file.size as u64;
|
||||
inode.block_data = file.block_data;
|
||||
self.write_inode(file.inode_num, &inode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn list_dir(&mut self, path: &[u8], entries: &mut [DirEntry]) -> Option<usize> {
|
||||
@@ -222,14 +223,13 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
|
||||
let inode = self.read_inode(dir_inode_num)?;
|
||||
let count = dir::list_entries(&mut self.device, &self.sb, &inode, entries)?;
|
||||
for i in 0..count {
|
||||
if !entries[i].is_dir {
|
||||
let name = entries[i].name;
|
||||
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) {
|
||||
entries[i].size = child.size as usize;
|
||||
for entry in entries.iter_mut().take(count) {
|
||||
if !entry.is_dir {
|
||||
let name = entry.name;
|
||||
if let Some(child_ino) = dir::find_entry(&mut self.device, &self.sb, &inode, name.as_bytes())
|
||||
&& let Some(child) = self.read_inode(child_ino) {
|
||||
entry.size = child.size as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(count)
|
||||
@@ -279,11 +279,10 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
||||
None => return false,
|
||||
};
|
||||
|
||||
if let Some(child_inode) = self.read_inode(child_inode_num) {
|
||||
if child_inode.is_dir() {
|
||||
if let Some(child_inode) = self.read_inode(child_inode_num)
|
||||
&& child_inode.is_dir() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
let parent_inode = match self.read_inode(parent_inode_num) {
|
||||
Some(i) => i,
|
||||
@@ -299,11 +298,10 @@ 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 {
|
||||
if f.inode_num == child_inode_num {
|
||||
if let Some(f) = slot
|
||||
&& f.inode_num == child_inode_num {
|
||||
*slot = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
@@ -102,3 +102,9 @@ impl Grants {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Grants {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user