Better virtual file system, keyboard through MMIO&VirtIO
This commit is contained in:
@@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
io = { path = "../io" }
|
||||
bitflags = "2"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{error::Error, io::{Read, ReadLeExt}};
|
||||
use crate::error::Error;
|
||||
use io::{Read, ReadLeExt};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Fat32BootSector {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
entry::{DirEntry, DirectoryIterator},
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
io::{self, IoBase, Read, Seek, Write},
|
||||
path::Path,
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
};
|
||||
use io::{self, IoBase, Read, Seek, Write};
|
||||
|
||||
pub struct Dir<'a, T> {
|
||||
raw: RawFile<'a, T>,
|
||||
@@ -13,24 +13,24 @@ pub struct Dir<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> IoBase for Dir<'a, T> {
|
||||
type Error = T::Error;
|
||||
type Error = Error<T::Error>;
|
||||
}
|
||||
impl<'a, T: ReadSeek> Seek for Dir<'a, T> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Error<Self::Error>> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
self.raw.seek(pos)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadSeek> Read for Dir<'a, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.raw.read(buf)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadWriteSeek> Write for Dir<'a, T> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.raw.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Error<Self::Error>> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.raw.flush()
|
||||
}
|
||||
}
|
||||
@@ -44,25 +44,28 @@ impl<'a, T> Dir<'a, T> {
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadSeek> Dir<'a, T> {
|
||||
pub fn open_entry<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
) -> Result<DirEntry<'a, T>, Error<T::Error>> {
|
||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'a, T>, Error<T::Error>> {
|
||||
if path.as_ref().is_absolute() {
|
||||
return self.fs.open_entry(path);
|
||||
}
|
||||
let (start, entryname) = path.as_ref().split_path();
|
||||
for file in self.iter() {
|
||||
let f = file?;
|
||||
if f.name_is(path.as_ref().as_str()) {
|
||||
return Ok(f);
|
||||
if f.name_is(start) {
|
||||
if let Some(entry_name) = entryname {
|
||||
if f.is_dir() {
|
||||
return f.to_dir().open_entry(entry_name);
|
||||
} else {
|
||||
return Err(Error::NotFound);
|
||||
}
|
||||
} else {
|
||||
return Ok(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
pub fn open_file<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
) -> Result<File<'a, T>, Error<T::Error>> {
|
||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'a, T>, Error<T::Error>> {
|
||||
if path.as_ref().is_absolute() {
|
||||
return self.fs.open_file(path);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ use core::{array::IntoIter, iter::Copied, str::Utf8Error};
|
||||
use core::{char::DecodeUtf16, marker::PhantomData, slice::Iter};
|
||||
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek,
|
||||
consts::FATAttr,
|
||||
dir::Dir,
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
io::{IoBase, Read, ReadLeExt},
|
||||
Fat32FileSystem, ReadSeek,
|
||||
};
|
||||
|
||||
use io::{IoBase, Read, ReadLeExt};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::borrow::ToOwned;
|
||||
#[cfg(feature = "alloc")]
|
||||
@@ -32,7 +33,7 @@ pub struct FatDirEntry {
|
||||
}
|
||||
|
||||
impl FatDirEntry {
|
||||
pub fn deserialize<T: Read>(reader: &mut T) -> Result<Self, Error<T::Error>> {
|
||||
pub fn deserialize<T: Read>(reader: &mut T) -> Result<Self, T::Error> {
|
||||
let mut name = [0u8; _];
|
||||
reader.read_exact(&mut name)?;
|
||||
let attr = reader.read_u8()?;
|
||||
@@ -224,7 +225,7 @@ impl<'a, T> DirectoryIterator<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ReadSeek> Iterator for DirectoryIterator<'a, T> {
|
||||
impl<'a, T: ReadSeek + 'a> Iterator for DirectoryIterator<'a, T> {
|
||||
type Item = Result<DirEntry<'a, T>, Error<T::Error>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use io::error::IoError;
|
||||
|
||||
/// Error enum with all errors that can be returned by functions from this crate
|
||||
///
|
||||
/// Generic parameter `T` is a type of external error returned by the user provided storage
|
||||
@@ -55,15 +57,6 @@ impl<T: core::fmt::Display> core::fmt::Display for Error<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait that should be implemented by errors returned from the user supplied storage.
|
||||
///
|
||||
/// Implementations for `std::io::Error` and `()` are provided by this crate.
|
||||
pub trait IoError: core::fmt::Debug {
|
||||
fn is_interrupted(&self) -> bool;
|
||||
fn new_unexpected_eof_error() -> Self;
|
||||
fn new_write_zero_error() -> Self;
|
||||
}
|
||||
|
||||
impl<T: core::fmt::Debug + IoError> IoError for Error<T> {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
match self {
|
||||
@@ -79,39 +72,8 @@ impl<T: core::fmt::Debug + IoError> IoError for Error<T> {
|
||||
fn new_write_zero_error() -> Self {
|
||||
Error::<T>::WriteZero
|
||||
}
|
||||
}
|
||||
|
||||
impl IoError for () {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn new_unexpected_eof_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
|
||||
fn new_write_zero_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl IoError for std::io::Error {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
self.kind() == std::io::ErrorKind::Interrupted
|
||||
}
|
||||
|
||||
fn new_unexpected_eof_error() -> Self {
|
||||
Self::new(
|
||||
std::io::ErrorKind::UnexpectedEof,
|
||||
"failed to fill whole buffer",
|
||||
)
|
||||
}
|
||||
|
||||
fn new_write_zero_error() -> Self {
|
||||
Self::new(
|
||||
std::io::ErrorKind::WriteZero,
|
||||
"failed to write whole buffer",
|
||||
)
|
||||
fn new_invalid_utf8_error() -> Self {
|
||||
Error::<T>::InvalidUTF8
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
use crate::{
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
consts::{FAT32_BAD_CLUSTER, FAT32_END_OF_CHAIN},
|
||||
error::Error,
|
||||
io::{self, IoBase, Read, Seek, Write},
|
||||
Fat32FileSystem, ReadSeek, ReadWriteSeek,
|
||||
};
|
||||
|
||||
use io::{self, IoBase, Read, Seek, Write};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RawFile<'a, T> {
|
||||
pub(crate) fs: &'a Fat32FileSystem<T>,
|
||||
@@ -30,11 +31,11 @@ impl<'a, T> RawFile<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> IoBase for RawFile<'a, T> {
|
||||
type Error = T::Error;
|
||||
type Error = Error<T::Error>;
|
||||
}
|
||||
|
||||
impl<'a, T: ReadSeek> Seek for RawFile<'a, T> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Error<Self::Error>> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
let new_pos = match pos {
|
||||
io::SeekFrom::Start(s) => s,
|
||||
io::SeekFrom::Current(c) => (self.pos as i64 + c) as u64,
|
||||
@@ -66,7 +67,7 @@ impl<'a, T: ReadSeek> Seek for RawFile<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T: ReadSeek> Read for RawFile<'a, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
let max_len = self
|
||||
.size
|
||||
.map(|size| core::cmp::min(buf.len(), size as usize - self.pos as usize))
|
||||
@@ -115,40 +116,41 @@ impl<'a, T: ReadSeek> Read for RawFile<'a, T> {
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadWriteSeek> Write for RawFile<'a, T> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Error<Self::Error>> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
#[derive(Debug)]
|
||||
pub struct File<'a, T> {
|
||||
raw: RawFile<'a, T>,
|
||||
fs: &'a Fat32FileSystem<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: IoBase> IoBase for File<'a, T> {
|
||||
type Error = T::Error;
|
||||
type Error = Error<T::Error>;
|
||||
}
|
||||
impl<'a, T: ReadSeek> Seek for File<'a, T> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Error<Self::Error>> {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> Result<u64, Self::Error> {
|
||||
self.raw.seek(pos)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadSeek> Read for File<'a, T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.raw.read(buf)
|
||||
}
|
||||
}
|
||||
impl<'a, T: ReadWriteSeek> Write for File<'a, T> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.raw.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Error<Self::Error>> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.raw.flush()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,11 @@ use crate::{
|
||||
entry::{DirEntry, FatEntry},
|
||||
error::Error,
|
||||
file::{File, RawFile},
|
||||
io::{Read, ReadLeExt, Seek, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use io::{Read, ReadLeExt, Seek, Write};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
@@ -24,7 +25,6 @@ pub mod dir;
|
||||
pub mod entry;
|
||||
pub mod error;
|
||||
pub mod file;
|
||||
pub mod io;
|
||||
pub mod path;
|
||||
|
||||
pub trait ReadSeek: Read + Seek {}
|
||||
@@ -136,10 +136,7 @@ impl<T> Fat32FileSystem<T> {
|
||||
}
|
||||
}
|
||||
impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||
pub fn open_entry<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
) -> Result<DirEntry<'_, T>, Error<T::Error>> {
|
||||
pub fn open_entry<P: AsRef<Path>>(&self, path: P) -> Result<DirEntry<'_, T>, Error<T::Error>> {
|
||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||
self.root_directory().open_entry(path)
|
||||
}
|
||||
@@ -147,10 +144,7 @@ impl<T: ReadSeek> Fat32FileSystem<T> {
|
||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||
self.root_directory().open_dir(path)
|
||||
}
|
||||
pub fn open_file<P: AsRef<Path>>(
|
||||
&self,
|
||||
path: P,
|
||||
) -> Result<File<'_, T>, Error<T::Error>> {
|
||||
pub fn open_file<P: AsRef<Path>>(&self, path: P) -> Result<File<'_, T>, Error<T::Error>> {
|
||||
let path = path.as_ref().as_str().trim_start_matches("/");
|
||||
self.root_directory().open_file(path)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use core::ops::Deref;
|
||||
#[cfg(feature = "alloc")]
|
||||
use core::{borrow::Borrow, ops::Deref};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::borrow::ToOwned;
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct Path {
|
||||
inner: str,
|
||||
}
|
||||
@@ -19,6 +23,13 @@ impl AsRef<Path> for str {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl AsRef<Path> for String {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.as_str().as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Path {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self
|
||||
@@ -32,9 +43,17 @@ impl AsRef<str> for Path {
|
||||
}
|
||||
|
||||
impl Path {
|
||||
pub fn new(path: &str) -> &Self {
|
||||
path.as_ref()
|
||||
}
|
||||
|
||||
pub fn split_path(&self) -> (&str, Option<&Path>) {
|
||||
if let Some((start, end)) = self.inner.split_once("/") {
|
||||
(start, Some(end.into()))
|
||||
if end.is_empty() {
|
||||
(start, None)
|
||||
} else {
|
||||
(start, Some(end.into()))
|
||||
}
|
||||
} else {
|
||||
(&self.inner, None)
|
||||
}
|
||||
@@ -48,9 +67,46 @@ impl Path {
|
||||
pub fn is_relative(&self) -> bool {
|
||||
!self.is_absolute()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
pub fn is_root(&self) -> bool {
|
||||
self.as_str() == "/"
|
||||
}
|
||||
|
||||
pub fn starts_with<P: AsRef<Self>>(&self, other: P) -> bool {
|
||||
let this = self.split_path();
|
||||
let other = other.as_ref().split_path();
|
||||
if this.0 == other.0 {
|
||||
if let Some(this) = this.1
|
||||
&& let Some(other) = other.1
|
||||
{
|
||||
this.starts_with(other)
|
||||
} else {
|
||||
other.1.is_none()
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn without<P: AsRef<Self>>(&self, other: P) -> &Path {
|
||||
let this = self.split_path();
|
||||
let other = other.as_ref().split_path();
|
||||
if this.0 == other.0 {
|
||||
match (this.1, other.1) {
|
||||
(None, None) => Path::new(""),
|
||||
(None, Some(_)) => todo!(),
|
||||
(Some(p), None) => p,
|
||||
(Some(this), Some(other)) => this.without(other),
|
||||
}
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
pub struct PathBuf {
|
||||
inner: String,
|
||||
}
|
||||
@@ -63,3 +119,26 @@ impl Deref for PathBuf {
|
||||
self.inner.as_str().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl AsRef<Path> for PathBuf {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl Borrow<str> for PathBuf {
|
||||
fn borrow(&self) -> &str {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl From<&str> for PathBuf {
|
||||
fn from(val: &str) -> Self {
|
||||
PathBuf {
|
||||
inner: val.to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
crates/io/Cargo.toml
Normal file
10
crates/io/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "io"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[features]
|
||||
alloc = []
|
||||
std = ["alloc"]
|
||||
48
crates/io/src/error.rs
Normal file
48
crates/io/src/error.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
/// Trait that should be implemented by errors returned from the user supplied storage.
|
||||
///
|
||||
/// Implementations for `std::io::Error` and `()` are provided by this crate.
|
||||
pub trait IoError: core::fmt::Debug {
|
||||
fn is_interrupted(&self) -> bool;
|
||||
fn new_unexpected_eof_error() -> Self;
|
||||
fn new_write_zero_error() -> Self;
|
||||
fn new_invalid_utf8_error() -> Self;
|
||||
}
|
||||
|
||||
impl IoError for () {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn new_unexpected_eof_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
|
||||
fn new_write_zero_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
|
||||
fn new_invalid_utf8_error() -> Self {
|
||||
// empty
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl IoError for std::io::Error {
|
||||
fn is_interrupted(&self) -> bool {
|
||||
self.kind() == std::io::ErrorKind::Interrupted
|
||||
}
|
||||
|
||||
fn new_unexpected_eof_error() -> Self {
|
||||
Self::new(
|
||||
std::io::ErrorKind::UnexpectedEof,
|
||||
"failed to fill whole buffer",
|
||||
)
|
||||
}
|
||||
|
||||
fn new_write_zero_error() -> Self {
|
||||
Self::new(
|
||||
std::io::ErrorKind::WriteZero,
|
||||
"failed to write whole buffer",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
use crate::error::{Error, IoError};
|
||||
#![cfg_attr(any(not(feature = "std"), target_arch = "riscv64"), no_std)]
|
||||
pub mod error;
|
||||
|
||||
use error::IoError;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
extern crate alloc;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::string::String;
|
||||
@@ -41,7 +47,7 @@ pub trait Read: IoBase {
|
||||
/// then it must be guaranteed that no bytes were read.
|
||||
/// An error for which `IoError::is_interrupted` returns true is non-fatal and the read operation should be retried
|
||||
/// if there is nothing else to do.
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::Error>>;
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error>;
|
||||
|
||||
/// Read the exact number of bytes required to fill `buf`.
|
||||
///
|
||||
@@ -58,7 +64,7 @@ pub trait Read: IoBase {
|
||||
///
|
||||
/// If this function returns an error, it is unspecified how many bytes it has read, but it will never read more
|
||||
/// than would be necessary to completely fill the buffer.
|
||||
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Error<Self::Error>> {
|
||||
fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<(), Self::Error> {
|
||||
while !buf.is_empty() {
|
||||
match self.read(buf) {
|
||||
Ok(0) => break,
|
||||
@@ -73,12 +79,12 @@ pub trait Read: IoBase {
|
||||
if buf.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::<Self::Error>::new_unexpected_eof_error())
|
||||
Err(Self::Error::new_unexpected_eof_error())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Error<Self::Error>> {
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize, Self::Error> {
|
||||
const CHUNK_SIZE: usize = 32;
|
||||
let start_len = buf.len();
|
||||
loop {
|
||||
@@ -93,11 +99,11 @@ pub trait Read: IoBase {
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Error<Self::Error>> {
|
||||
fn read_to_string(&mut self, buf: &mut String) -> Result<usize, Self::Error> {
|
||||
let read = self.read_to_end(unsafe { buf.as_mut_vec() })?;
|
||||
|
||||
if str::from_utf8(buf.as_bytes()).is_err() {
|
||||
Err(Error::InvalidUTF8)
|
||||
Err(Self::Error::new_invalid_utf8_error())
|
||||
} else {
|
||||
Ok(read)
|
||||
}
|
||||
@@ -115,7 +121,7 @@ pub trait Write: IoBase {
|
||||
/// Each call to write may generate an I/O error indicating that the operation could not be completed. If an error
|
||||
/// is returned then no bytes in the buffer were written to this writer.
|
||||
/// It is not considered an error if the entire buffer could not be written to this writer.
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Error<Self::Error>>;
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error>;
|
||||
|
||||
/// Attempts to write an entire buffer into this writer.
|
||||
///
|
||||
@@ -129,11 +135,11 @@ pub trait Write: IoBase {
|
||||
///
|
||||
/// This function will return the first error for which `IoError::is_interrupted` method returns false that `write`
|
||||
/// returns.
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Error<Self::Error>> {
|
||||
fn write_all(&mut self, mut buf: &[u8]) -> Result<(), Self::Error> {
|
||||
while !buf.is_empty() {
|
||||
match self.write(buf) {
|
||||
Ok(0) => {
|
||||
return Err(Error::<Self::Error>::new_write_zero_error());
|
||||
return Err(Self::Error::new_write_zero_error());
|
||||
}
|
||||
Ok(n) => buf = &buf[n..],
|
||||
Err(ref e) if e.is_interrupted() => {}
|
||||
@@ -148,7 +154,7 @@ pub trait Write: IoBase {
|
||||
/// # Errors
|
||||
///
|
||||
/// It is considered an error if not all bytes could be written due to I/O errors or EOF being reached.
|
||||
fn flush(&mut self) -> Result<(), Error<Self::Error>>;
|
||||
fn flush(&mut self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// Enumeration of possible methods to seek within an I/O object.
|
||||
@@ -177,7 +183,7 @@ pub trait Seek: IoBase {
|
||||
///
|
||||
/// # Errors
|
||||
/// Seeking to a negative offset is considered an error.
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error<Self::Error>>;
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error>;
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
@@ -209,59 +215,59 @@ impl IoBase for std::fs::File {
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl<T: std::io::Read + IoBase<Error = std::io::Error>> Read for T {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
self.read(buf).map_err(Error::Io)
|
||||
}
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error<Self::Error>> {
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read_exact(buf).map_err(Error::Io)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl<T: std::io::Write + IoBase<Error = std::io::Error>> Write for T {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Error<Self::Error>> {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
self.write(buf).map_err(Error::Io)
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Error<Self::Error>> {
|
||||
fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write_all(buf).map_err(Error::Io)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Error<Self::Error>> {
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
self.flush().map_err(Error::Io)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "std", not(target_arch = "riscv64")))]
|
||||
impl<T: std::io::Seek + IoBase<Error = std::io::Error>> Seek for T {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Error<Self::Error>> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
|
||||
self.seek(pos.into()).map_err(Error::Io)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait ReadLeExt {
|
||||
pub trait ReadLeExt {
|
||||
type Error;
|
||||
fn read_u8(&mut self) -> Result<u8, Error<Self::Error>>;
|
||||
fn read_u16_le(&mut self) -> Result<u16, Error<Self::Error>>;
|
||||
fn read_u32_le(&mut self) -> Result<u32, Error<Self::Error>>;
|
||||
fn read_u8(&mut self) -> Result<u8, Self::Error>;
|
||||
fn read_u16_le(&mut self) -> Result<u16, Self::Error>;
|
||||
fn read_u32_le(&mut self) -> Result<u32, Self::Error>;
|
||||
}
|
||||
|
||||
impl<T: Read> ReadLeExt for T {
|
||||
type Error = <Self as IoBase>::Error;
|
||||
|
||||
fn read_u8(&mut self) -> Result<u8, Error<Self::Error>> {
|
||||
fn read_u8(&mut self) -> Result<u8, Self::Error> {
|
||||
let mut buf = [0_u8; 1];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
|
||||
fn read_u16_le(&mut self) -> Result<u16, Error<Self::Error>> {
|
||||
fn read_u16_le(&mut self) -> Result<u16, Self::Error> {
|
||||
let mut buf = [0_u8; 2];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
fn read_u32_le(&mut self) -> Result<u32, Error<Self::Error>> {
|
||||
fn read_u32_le(&mut self) -> Result<u32, Self::Error> {
|
||||
let mut buf = [0_u8; 4];
|
||||
self.read_exact(&mut buf)?;
|
||||
Ok(u32::from_le_bytes(buf))
|
||||
@@ -271,23 +277,23 @@ impl<T: Read> ReadLeExt for T {
|
||||
#[allow(unused)]
|
||||
pub(crate) trait WriteLeExt {
|
||||
type Error;
|
||||
fn write_u8(&mut self, n: u8) -> Result<(), Error<Self::Error>>;
|
||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Error<Self::Error>>;
|
||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Error<Self::Error>>;
|
||||
fn write_u8(&mut self, n: u8) -> Result<(), Self::Error>;
|
||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error>;
|
||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
impl<T: Write> WriteLeExt for T {
|
||||
type Error = <Self as IoBase>::Error;
|
||||
|
||||
fn write_u8(&mut self, n: u8) -> Result<(), Error<Self::Error>> {
|
||||
fn write_u8(&mut self, n: u8) -> Result<(), Self::Error> {
|
||||
self.write_all(&[n])
|
||||
}
|
||||
|
||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Error<Self::Error>> {
|
||||
fn write_u16_le(&mut self, n: u16) -> Result<(), Self::Error> {
|
||||
self.write_all(&n.to_le_bytes())
|
||||
}
|
||||
|
||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Error<Self::Error>> {
|
||||
fn write_u32_le(&mut self, n: u32) -> Result<(), Self::Error> {
|
||||
self.write_all(&n.to_le_bytes())
|
||||
}
|
||||
}
|
||||
@@ -6,3 +6,4 @@ edition = "2024"
|
||||
[dependencies]
|
||||
os-std-macros = { path = "../os-std-macros" }
|
||||
shared = { path = "../shared", features = ["user"] }
|
||||
io = { path = "../io", features = ["alloc"] }
|
||||
|
||||
1
crates/os-std/src/io.rs
Normal file
1
crates/os-std/src/io.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub use io::SeekFrom;
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod io;
|
||||
pub mod prelude;
|
||||
|
||||
pub use shared::fs;
|
||||
|
||||
@@ -5,6 +5,7 @@ edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
bffs = { path = "../bffs" }
|
||||
io = { path = "../io" }
|
||||
|
||||
[features]
|
||||
kernel = []
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
#[derive(Debug)]
|
||||
pub struct File {
|
||||
fd: u64,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// # Safety
|
||||
/// The file descriptor must be valid
|
||||
pub unsafe fn new(fd: u64) -> Self {
|
||||
Self { fd }
|
||||
}
|
||||
|
||||
pub fn as_fd(&self) -> u64 {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
use core::{alloc::Layout, time::Duration};
|
||||
|
||||
use bffs::path::Path;
|
||||
use io::SeekFrom;
|
||||
|
||||
use crate::fs::File;
|
||||
|
||||
#[repr(u64)]
|
||||
pub enum SysCall {
|
||||
Write = 1,
|
||||
Open = 2,
|
||||
Seek = 8,
|
||||
Alloc = 40,
|
||||
Dealloc = 41,
|
||||
Exit = 60,
|
||||
@@ -17,7 +22,9 @@ pub enum SysCall {
|
||||
impl From<u64> for SysCall {
|
||||
fn from(value: u64) -> Self {
|
||||
match value {
|
||||
1 => SysCall::Write,
|
||||
2 => SysCall::Open,
|
||||
8 => SysCall::Seek,
|
||||
40 => SysCall::Alloc,
|
||||
41 => SysCall::Dealloc,
|
||||
60 => SysCall::Exit,
|
||||
@@ -128,12 +135,29 @@ pub fn dealloc(ptr: *mut u8, layout: core::alloc::Layout) {
|
||||
syscall!(SysCall::Dealloc, ptr as u64, size as u64, align as u64);
|
||||
}
|
||||
}
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> u64 {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> File {
|
||||
unsafe {
|
||||
let path_str = path.as_ref().as_str();
|
||||
let ptr = path_str.as_ptr();
|
||||
let size = path_str.len();
|
||||
let (fd, ..) = syscall!(SysCall::Open, ptr as u64, size as u64);
|
||||
fd
|
||||
File::new(fd)
|
||||
}
|
||||
}
|
||||
pub fn write(file: &mut File, buf: &[u8]) {
|
||||
unsafe {
|
||||
let ptr = buf.as_ptr();
|
||||
let size = buf.len();
|
||||
syscall!(SysCall::Write, file.as_fd(), ptr as u64, size as u64);
|
||||
}
|
||||
}
|
||||
pub fn seek(file: &mut File, seek: SeekFrom) {
|
||||
unsafe {
|
||||
let (discriminant, value) = match seek {
|
||||
SeekFrom::Start(v) => (0, v),
|
||||
SeekFrom::End(v) => (1, v as u64),
|
||||
SeekFrom::Current(v) => (2, v as u64),
|
||||
};
|
||||
syscall!(SysCall::Seek, file.as_fd(), discriminant, value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user