Skip to content

Commit

Permalink
Support more OpenOptions in File::open: create, create_new, a…
Browse files Browse the repository at this point in the history
…ppend` (theseus-os#511)

Not all options have been thoroughly tested, but any unsupported ones will return `io::Error` as expected.
  • Loading branch information
kevinaboos authored May 2, 2022
1 parent fa85796 commit 7bd703c
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 22 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions applications/test_std_fs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "test_std_fs"
version = "0.1.0"
authors = ["Kevin Boos <[email protected]>"]
build = "../../build.rs"

[dependencies]
log = "0.4.8"
core2 = { version = "0.4.0", default-features = false, features = ["alloc", "nightly"] }

[dependencies.theseus_std]
path = "../../ports/theseus_std"

[dependencies.terminal_print]
path = "../../kernel/terminal_print"
35 changes: 35 additions & 0 deletions applications/test_std_fs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Tests the basic features of Theseus's port of `std::fs` modules from Rust `std`.
//!
#![no_std]

extern crate alloc;
extern crate log;
extern crate theseus_std;
extern crate core2;
#[macro_use] extern crate terminal_print;

use alloc::{string::String, vec::Vec};
use core2::io::{self, Result, Error, Read, Write};
use theseus_std::fs::File;


pub fn main(_args: Vec<String>) -> isize {
match rmain(_args) {
Ok(_) => {
println!("test_std_fs complete!");
0
}
Err(e) => {
println!("test_std_fs error: {:?}", e);
-1
}
}
}


fn rmain(_args: Vec<String>) -> io::Result<()> {
let mut out = theseus_std::fs::File::create("test.txt")?;
out.write(b"yo what's up\nhey there!")?;
out.write_all(b"take 2: yo what's up, hey there!")?;
Ok(())
}
2 changes: 1 addition & 1 deletion libs/core2
Submodule core2 updated 1 files
+0 −1 src/lib.rs
1 change: 1 addition & 0 deletions ports/theseus_std/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ theseus_task = { path = "../../kernel/task", package = "task" }
theseus_path = { path = "../../kernel/path", package = "path" }
theseus_fs_node = { path = "../../kernel/fs_node", package = "fs_node" }
theseus_io = { path = "../../kernel/io", package = "io" }
theseus_memfs = { path = "../../kernel/memfs", package = "memfs" }
spin = "0.9.0"
core2 = { version = "0.4.0", default-features = false, features = ["alloc", "nightly"] }
116 changes: 95 additions & 21 deletions ports/theseus_std/src/fs_imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use crate::os_str::OsString;
use core::fmt;
use core::hash::Hash;
use core2::io::{self, /*IoSlice, IoSliceMut, ReadBuf,*/ SeekFrom, Read, Write, Seek};
use alloc::sync::Arc;
use crate::path::{Path, PathBuf};
#[cfg(feature = "time")]
use crate::sys::time::SystemTime;
Expand Down Expand Up @@ -72,21 +71,25 @@ type LockableFileRef = LockableIo<
pub struct File(FileOrDirectory);

enum FileOrDirectory {
OpenFile(OpenFileRef),
OpenFile {
file: OpenFileRef,
opts: OpenOptions,
},
Directory(theseus_fs_node::DirRef),
}

impl fmt::Debug for FileOrDirectory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Self::OpenFile(file) => write!(
Self::OpenFile { file, opts} => write!(
f,
"OpenFile({})",
"OpenFile({}, {:?})",
file.try_lock()
.and_then(|rw| rw.try_lock()
.map(|fr| fr.get_absolute_path())
)
.unwrap_or_else(|| "<Locked>".into())
.unwrap_or_else(|| "<Locked>".into()),
opts,
),
Self::Directory(dir) => write!(
f,
Expand All @@ -112,7 +115,7 @@ pub struct ReadDir();

pub struct DirEntry();

#[derive(Clone, Debug)]
#[derive(Clone, Copy, Debug)]
pub struct OpenOptions {
// Includes only the system-generic flags for now.
read: bool,
Expand Down Expand Up @@ -291,22 +294,78 @@ impl OpenOptions {
}
}

/// Convenience function for converting a Theseus `FileRef` into a `File`.
fn theseus_file_ref_to_file(f: FileRef, opts: OpenOptions) -> File {
File(FileOrDirectory::OpenFile {
file: LockableIo::from(Mutex::new(
ReaderWriter::new(LockableIo::from(f))
)),
opts,
})
}

impl File {
pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result<File> {
let working_dir = crate::env::current_dir()?;
theseus_path::Path::new(path.to_string_lossy().into()).get(&working_dir)
.ok_or(io::ErrorKind::NotFound.into())
.map(|theseus_file_or_dir| match theseus_file_or_dir {
theseus_fs_node::FileOrDir::File(f) => FileOrDirectory::OpenFile(
LockableIo::from(Mutex::new(ReaderWriter::new(LockableIo::from(f))))
),
theseus_fs_node::FileOrDir::Dir(d) => FileOrDirectory::Directory(d),
}).map(File) // wrap it in the main `File` struct
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
// Handle file creation
if opts.create_new || opts.create {
// `create` and `create_new` both require either the `write` or `append` option.
if !(opts.write || opts.append) {
return Err(io::ErrorKind::InvalidInput.into());
}

let curr_dir = crate::env::current_dir()?;
let parent_dir_of_file = path.parent()
.ok_or(io::Error::from(io::ErrorKind::NotFound))?;

let theseus_file_path = theseus_path::Path::new(path.to_string_lossy().into());
let theseus_dir_path = theseus_path::Path::new(parent_dir_of_file.to_string_lossy().into());

// `create_new` requires that the file must not previously exist at all.
if opts.create_new && theseus_file_path.get(&curr_dir).is_some() {
return Err(io::ErrorKind::AlreadyExists.into());
}

let containing_dir = theseus_dir_path.get_dir(&curr_dir)
.ok_or(io::Error::from(io::ErrorKind::NotFound))?;

let file_name = path.file_name().ok_or(io::Error::from(io::ErrorKind::NotFound))?;
let new_file = theseus_memfs::MemFile::new(
file_name.to_string_lossy().into(),
&containing_dir,
).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;

Ok(theseus_file_ref_to_file(new_file, opts.clone()))
}

// Handle truncate (TODO)
else if opts.truncate {
if opts.write {
// TODO: support truncate
Err(io::Error::new(io::ErrorKind::Uncategorized, "Theseus filesystem doesn't yet support truncate"))
} else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "`OpenOptions::truncate` requires `OpenOptions::write`"))
}
}

// Handle accessing a file that must exist (in any mode)
else if opts.read || opts.write || opts.append {
let working_dir = crate::env::current_dir()?;
theseus_path::Path::new(path.to_string_lossy().into()).get(&working_dir)
.ok_or(io::ErrorKind::NotFound.into())
.map(|theseus_file_or_dir| match theseus_file_or_dir {
theseus_fs_node::FileOrDir::File(f) => theseus_file_ref_to_file(f, opts.clone()),
theseus_fs_node::FileOrDir::Dir(d) => File(FileOrDirectory::Directory(d)),
})
}

else {
Err(io::Error::new(io::ErrorKind::InvalidInput, "no `OpenOptions` were specified"))
}
}

pub fn file_attr(&self) -> io::Result<FileAttr> {
let (size, is_file) = match &self.0 {
FileOrDirectory::OpenFile(f) => (f.lock().len(), true),
FileOrDirectory::OpenFile { file, ..} => (file.lock().len(), true),
FileOrDirectory::Directory(_) => (0, false),
};
Ok(FileAttr {
Expand Down Expand Up @@ -335,7 +394,13 @@ impl File {
io::ErrorKind::Other,
"Is A Directory (TODO: use IsADirectory)"
)),
FileOrDirectory::OpenFile(f) => f.lock().read(buf),
FileOrDirectory::OpenFile { file, opts } => {
if opts.read {
file.lock().read(buf)
} else {
Err(io::Error::from(io::ErrorKind::PermissionDenied))
}
}
}
}

Expand All @@ -360,7 +425,16 @@ impl File {
io::ErrorKind::Other,
"Is A Directory (TODO: use IsADirectory)"
)),
FileOrDirectory::OpenFile(f) => f.lock().write(buf),
FileOrDirectory::OpenFile { file, opts } => {
if opts.append {
file.lock().seek(SeekFrom::End(0))?;
}
if opts.write || opts.append {
file.lock().write(buf)
} else {
Err(io::Error::from(io::ErrorKind::PermissionDenied))
}
}
}
}

Expand All @@ -380,7 +454,7 @@ impl File {
io::ErrorKind::Other,
"Is A Directory (TODO: use IsADirectory)"
)),
FileOrDirectory::OpenFile(f) => f.lock().flush(),
FileOrDirectory::OpenFile { file, .. } => file.lock().flush(),
}
}

Expand All @@ -390,7 +464,7 @@ impl File {
io::ErrorKind::Other,
"Is A Directory (TODO: use IsADirectory)"
)),
FileOrDirectory::OpenFile(f) => f.lock().seek(pos),
FileOrDirectory::OpenFile { file, .. } => file.lock().seek(pos),
}
}

Expand Down

0 comments on commit 7bd703c

Please sign in to comment.