Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add file syscalls #242

Merged
merged 26 commits into from
Sep 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions src/api/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ fn color_to_bg(name: &str) -> Option<usize> {
color_to_fg(name).map(|fg| fg + 10)
}

pub fn read_char() -> Option<char> {
Some(sys::console::get_char())
}

pub fn is_printable(c: char) -> bool {
if cfg!(feature = "video") {
c.is_ascii() && sys::vga::is_printable(c as u8)
Expand Down
148 changes: 115 additions & 33 deletions src/api/fs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,46 @@
use crate::api::syscall;
use crate::sys::fs::{OpenFlag, DeviceType};
use crate::sys;

use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use alloc::vec;

pub trait FileIO {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()>;
fn write(&mut self, buf: &[u8]) -> Result<usize, ()>;
}

pub fn dirname(pathname: &str) -> &str {
let n = pathname.len();
let i = match pathname.rfind('/') {
Some(0) => 1,
Some(i) => i,
None => n,
};
&pathname[0..i]
}

pub fn filename(pathname: &str) -> &str {
let n = pathname.len();
let i = match pathname.rfind('/') {
Some(i) => i + 1,
None => 0,
};
&pathname[i..n]
}

// Transform "foo.txt" into "/path/to/foo.txt"
pub fn realpath(pathname: &str) -> String {
if pathname.starts_with('/') {
pathname.into()
} else {
let dirname = sys::process::dir();
let sep = if dirname.ends_with('/') { "" } else { "/" };
format!("{}{}{}", dirname, sep, pathname)
}
}

pub fn canonicalize(path: &str) -> Result<String, ()> {
match sys::process::env("HOME") {
Expand All @@ -18,53 +57,96 @@ pub fn canonicalize(path: &str) -> Result<String, ()> {
}
}

pub fn read_to_string(path: &str) -> Result<String, ()> {
let path = match canonicalize(path) {
Ok(path) => path,
Err(_) => return Err(()),
};
match sys::fs::File::open(&path) {
Some(mut file) => {
Ok(file.read_to_string())
},
None => {
Err(())
}
pub fn exists(path: &str) -> bool {
syscall::stat(path).is_some()
}

pub fn open_file(path: &str) -> Option<usize> {
let flags = 0;
syscall::open(path, flags)
}

pub fn create_file(path: &str) -> Option<usize> {
let flags = OpenFlag::Create as usize;
syscall::open(path, flags)
}

pub fn open_dir(path: &str) -> Option<usize> {
let flags = OpenFlag::Dir as usize;
syscall::open(path, flags)
}

pub fn create_dir(path: &str) -> Option<usize> {
let flags = OpenFlag::Create as usize | OpenFlag::Dir as usize;
syscall::open(path, flags)
}

pub fn open_device(path: &str) -> Option<usize> {
let flags = OpenFlag::Device as usize;
syscall::open(path, flags)
}

pub fn create_device(path: &str, kind: DeviceType) -> Option<usize> {
let flags = OpenFlag::Create as usize | OpenFlag::Device as usize;
if let Some(handle) = syscall::open(path, flags) {
let buf = [kind as u8; 1];
return syscall::write(handle, &buf);
}
None
}

pub fn read_to_string(path: &str) -> Result<String, ()> {
let buf = read(path)?;
Ok(String::from_utf8_lossy(&buf).to_string())
}

pub fn read(path: &str) -> Result<Vec<u8>, ()> {
let path = match canonicalize(path) {
Ok(path) => path,
Err(_) => return Err(()),
};
match sys::fs::File::open(&path) {
Some(mut file) => {
let mut buf = vec![0; file.size()];
file.read(&mut buf);
Ok(buf)
},
None => {
Err(())
if let Some(stat) = syscall::stat(&path) {
let res = if stat.is_device() { open_device(&path) } else { open_file(&path) };
if let Some(handle) = res {
let mut buf = vec![0; stat.size() as usize];
if let Some(bytes) = syscall::read(handle, &mut buf) {
buf.resize(bytes, 0);
syscall::close(handle);
return Ok(buf)
}
}
}
Err(())
}

pub fn write(path: &str, buf: &[u8]) -> Result<(), ()> {
pub fn write(path: &str, buf: &[u8]) -> Result<usize, ()> {
let path = match canonicalize(path) {
Ok(path) => path,
Err(_) => return Err(()),
};
let mut file = match sys::fs::File::open(&path) {
None => match sys::fs::File::create(&path) {
None => return Err(()),
Some(file) => file,
},
Some(file) => file,
};
// TODO: add File::write_all to split buf if needed
match file.write(buf) {
Ok(_) => Ok(()),
Err(_) => Err(()),
if let Some(handle) = create_file(&path) {
if let Some(bytes) = syscall::write(handle, buf) {
syscall::close(handle);
return Ok(bytes)
}
}
Err(())
}

#[test_case]
fn test_file() {
use crate::sys::fs::{mount_mem, format_mem, dismount};
mount_mem();
format_mem();

assert_eq!(open_file("/test"), None);

// Write file
let input = "Hello, world!".as_bytes();
assert_eq!(write("/test", &input), Ok(input.len()));

// Read file
assert_eq!(read("/test"), Ok(input.to_vec()));

dismount();
}
51 changes: 51 additions & 0 deletions src/api/io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::api::syscall;

use alloc::vec;
use alloc::string::{String, ToString};

pub struct Stdin;
pub struct Stdout;

impl Stdin {
fn new() -> Self {
Self {}
}

pub fn read_char(&self) -> Option<char> {
let mut buf = vec![0; 1];
if let Some(bytes) = syscall::read(0, &mut buf) {
if bytes > 0 {
return Some(buf[0] as char);
}
}
None
}

pub fn read_line(&self) -> String {
let mut buf = vec![0; 256];
if let Some(bytes) = syscall::read(0, &mut buf) {
buf.resize(bytes, 0);
String::from_utf8_lossy(&buf).to_string()
} else {
String::new()
}
}
}

impl Stdout {
fn new() -> Self {
Self {}
}

pub fn write(&self, s: &str) {
syscall::write(1, s.as_bytes());
}
}

pub fn stdout() -> Stdout {
Stdout::new()
}

pub fn stdin() -> Stdin {
Stdin::new()
}
7 changes: 5 additions & 2 deletions src/api/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
// TODO: Use syscall instead
$crate::sys::console::print_fmt(format_args!($($arg)*));
use alloc::format;
let s = format!("{}", format_args!($($arg)*));
$crate::api::io::stdout().write(&s);
});
}

Expand All @@ -19,7 +20,9 @@ macro_rules! println {
pub mod console;
pub mod font;
pub mod fs;
pub mod io;
pub mod prompt;
pub mod random;
pub mod regex;
pub mod syscall;
pub mod vga;
Expand Down
5 changes: 2 additions & 3 deletions src/api/prompt.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::api::fs;
use crate::api::console;
use crate::api::{console, fs, io};
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
Expand Down Expand Up @@ -31,7 +30,7 @@ impl Prompt {
self.cursor = self.offset;
self.line = String::new();
let mut parser = Parser::new();
while let Some(c) = console::read_char() {
while let Some(c) = io::stdin().read_char() {
match c {
'\x03' => { // End of Text (^C)
println!();
Expand Down
22 changes: 22 additions & 0 deletions src/api/random.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::api::syscall;
use crate::api::fs;

pub fn get_u64() -> u64 {
let mut buf = [0; 8];
if let Some(handle) = fs::open_device("/dev/random") {
if syscall::read(handle, &mut buf).is_some() {
return u64::from_be_bytes(buf);
}
}
0
}

pub fn get_u16() -> u16 {
let mut buf = [0; 2];
if let Some(handle) = fs::open_device("/dev/random") {
if syscall::read(handle, &mut buf).is_some() {
return u16::from_be_bytes(buf);
}
}
0
}
86 changes: 86 additions & 0 deletions src/api/syscall.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::syscall;
use crate::sys::syscall::number::*;
use crate::sys::fs::FileStat;

pub fn sleep(seconds: f64) {
unsafe { syscall!(SLEEP, seconds.to_bits()) };
Expand All @@ -14,3 +15,88 @@ pub fn realtime() -> f64 {
let res = unsafe { syscall!(REALTIME) };
f64::from_bits(res as u64)
}

pub fn stat(path: &str) -> Option<FileStat> {
let path_ptr = path.as_ptr() as usize;
let path_len = path.len() as usize;
let mut stat = FileStat::new();
let stat_ptr = &mut stat as *mut FileStat as usize;
let res = unsafe { syscall!(STAT, path_ptr, path_len, stat_ptr) } as isize;
if res.is_negative() {
None
} else {
Some(stat)
}
}

pub fn open(path: &str, flags: usize) -> Option<usize> {
let ptr = path.as_ptr() as usize;
let len = path.len() as usize;
let res = unsafe { syscall!(OPEN, ptr, len, flags) } as isize;
if res.is_negative() {
None
} else {
Some(res as usize)
}
}

pub fn read(handle: usize, buf: &mut [u8]) -> Option<usize> {
let ptr = buf.as_ptr() as usize;
let len = buf.len() as usize;
let res = unsafe { syscall!(READ, handle, ptr, len) } as isize;
if res.is_negative() {
None
} else {
Some(res as usize)
}
}

pub fn write(handle: usize, buf: &[u8]) -> Option<usize> {
let ptr = buf.as_ptr() as usize;
let len = buf.len() as usize;
let res = unsafe { syscall!(WRITE, handle, ptr, len) } as isize;
if res.is_negative() {
None
} else {
Some(res as usize)
}
}

pub fn close(handle: usize) {
unsafe { syscall!(CLOSE, handle as usize) };
}

#[test_case]
fn test_file() {
use crate::sys::fs::{mount_mem, format_mem, dismount, OpenFlag};
use alloc::vec;
mount_mem();
format_mem();

let flags = 0;
assert_eq!(open("/test", flags), None);

// Write file
let flags = OpenFlag::Create as usize;
assert_eq!(open("/test", flags), Some(4));
let input = "Hello, world!".as_bytes();
assert_eq!(write(4, &input), Some(input.len()));

// Read file
let flags = 0;
assert_eq!(open("/test", flags), Some(5));
let mut output = vec![0; input.len()];
assert_eq!(read(5, &mut output), Some(input.len()));
assert_eq!(output, input);

close(4);
close(5);

assert_eq!(open("/test", flags), Some(4));

close(4);

//assert!(write(1, b"Hello, World\n").is_some());

dismount();
}
Loading