From 3fd2e4c63c19bdb6747023995083e9b59e92ba22 Mon Sep 17 00:00:00 2001 From: Klim Tsoutsman Date: Tue, 24 Oct 2023 13:15:24 +1100 Subject: [PATCH] Redesign `path` crate to follow `std::path` (#1061) * The `path` crate now exports a `Path` and `PathBuf` struct, akin to `std::path`. This makes it easier to support `std`, but also removes unnecessary allocations throughout Theseus. * Notably, `Path::file_stem()` now returns an `Option<&str>`. * This causes `crate_name_from_path()` to return a `Result`, which is responsible for most of the logic changes required. Signed-off-by: Klimenty Tsoutsman --- Cargo.lock | 4 - applications/bm/src/lib.rs | 14 +- applications/cat/src/lib.rs | 4 +- applications/cd/src/lib.rs | 6 +- applications/hull/src/builtin.rs | 14 +- applications/hull/src/lib.rs | 6 +- applications/loadc/src/lib.rs | 5 +- applications/ls/src/lib.rs | 3 +- applications/ns/src/lib.rs | 4 +- applications/qemu_test/src/lib.rs | 8 +- applications/rm/src/lib.rs | 4 +- applications/shell/src/lib.rs | 6 +- applications/swap/src/lib.rs | 4 +- applications/test_wasmtime/src/lib.rs | 2 +- applications/upd/src/lib.rs | 10 +- applications/wasm/src/lib.rs | 4 +- kernel/console/src/lib.rs | 4 +- kernel/crate_name_utils/src/lib.rs | 8 +- kernel/crate_swap/src/lib.rs | 34 +- kernel/environment/src/lib.rs | 3 +- kernel/fault_crate_swap/src/lib.rs | 4 +- kernel/first_application/src/lib.rs | 5 +- kernel/mod_mgmt/src/lib.rs | 15 +- kernel/mod_mgmt/src/parse_nano_core.rs | 4 +- kernel/path/Cargo.toml | 28 +- kernel/path/src/component.rs | 339 ++++++++ kernel/path/src/lib.rs | 763 +++++++++++++----- kernel/spawn/src/lib.rs | 6 +- kernel/task_fs/src/lib.rs | 24 +- .../wasi_interpreter/src/posix_file_system.rs | 19 +- kernel/window_manager/src/lib.rs | 9 +- ports/theseus_std/src/env.rs | 2 +- ports/theseus_std/src/fs_imp.rs | 9 +- 33 files changed, 1021 insertions(+), 353 deletions(-) create mode 100644 kernel/path/src/component.rs diff --git a/Cargo.lock b/Cargo.lock index c3b9f24bad..6baff26288 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2593,11 +2593,7 @@ name = "path" version = "0.1.0" dependencies = [ "fs_node", - "lazy_static", - "log", "root", - "spin 0.9.4", - "vfs_node", ] [[package]] diff --git a/applications/bm/src/lib.rs b/applications/bm/src/lib.rs index fab5fd45cf..a75331dd34 100644 --- a/applications/bm/src/lib.rs +++ b/applications/bm/src/lib.rs @@ -35,7 +35,7 @@ use alloc::string::{String, ToString}; use alloc::sync::Arc; use hpet::get_hpet; use heapfile::HeapFile; -use path::Path; +use path::{Path, PathBuf}; use fs_node::{DirRef, FileOrDir, FileRef}; use libtest::*; use memory::{create_mapping, PteFlags}; @@ -324,9 +324,9 @@ fn do_spawn_inner(overhead_ct: u64, th: usize, nr: usize, on_cpu: CpuId) -> Resu .map_err(|_| "could not find the application namespace")?; let namespace_dir = namespace.dir(); let app_path = namespace_dir.get_file_starting_with("hello-") - .map(|f| Path::new(f.lock().get_absolute_path())) + .map(|f| PathBuf::from(f.lock().get_absolute_path())) .ok_or("Could not find the application 'hello'")?; - let crate_name = crate_name_from_path(&app_path).to_string(); + let crate_name = crate_name_from_path(&app_path).ok_or("invalid app path")?.to_string(); // here we are taking the time at every iteration. // otherwise the crate is not fully unloaded from the namespace before the next iteration starts @@ -336,7 +336,7 @@ fn do_spawn_inner(overhead_ct: u64, th: usize, nr: usize, on_cpu: CpuId) -> Resu while namespace.get_crate(&crate_name).is_some() { } start_hpet = hpet.get_counter(); - let child = spawn::new_application_task_builder(app_path.clone(), None)? + let child = spawn::new_application_task_builder(&app_path, None)? .pin_on_cpu(on_cpu) .spawn()?; @@ -1237,7 +1237,7 @@ fn do_fs_read_with_open_inner(filename: &str, overhead_ct: u64, th: usize, nr: u let hpet = get_hpet().ok_or("Could not retrieve hpet counter")?; - let path = Path::new(filename.to_string()); + let path = PathBuf::from(filename.to_string()); let mut _dummy_sum: u64 = 0; let mut buf = vec![0; READ_BUF_SIZE]; let size = match get_file(filename) { @@ -1302,7 +1302,7 @@ fn do_fs_read_only_inner(filename: &str, overhead_ct: u64, th: usize, nr: usize) let hpet = get_hpet().ok_or("Could not retrieve hpet counter")?; - let path = Path::new(filename.to_string()); + let path = PathBuf::from(filename.to_string()); let _dummy_sum: u64 = 0; let mut buf = vec![0; READ_BUF_SIZE]; let size = match get_file(filename) { @@ -1640,7 +1640,7 @@ fn test_file_inner(fileref: FileRef) { /// Wrapper function to get a file provided a string. /// Not used in measurements fn get_file(filename: &str) -> Option { - let path = Path::new(filename.to_string()); + let path: &Path = filename.as_ref(); match path.get(&get_cwd().unwrap()) { Some(file_dir_enum) => { match file_dir_enum { diff --git a/applications/cat/src/lib.rs b/applications/cat/src/lib.rs index a387f9766a..ba4de221b3 100644 --- a/applications/cat/src/lib.rs +++ b/applications/cat/src/lib.rs @@ -11,7 +11,7 @@ extern crate core2; use core::str; use alloc::{ - string::{String, ToString}, + string::String, vec::Vec, }; use getopts::Options; @@ -47,7 +47,7 @@ pub fn main(args: Vec) -> isize { println!("failed to get current task"); return -1; }; - let path = Path::new(matches.free[0].to_string()); + let path: &Path = matches.free[0].as_ref(); // navigate to the filepath specified by first argument match path.get(&cwd) { diff --git a/applications/cd/src/lib.rs b/applications/cd/src/lib.rs index 47e57e5063..c2d9647ccd 100644 --- a/applications/cd/src/lib.rs +++ b/applications/cd/src/lib.rs @@ -10,11 +10,9 @@ extern crate root; extern crate task; use alloc::string::String; -use alloc::string::ToString; use alloc::sync::Arc; use alloc::vec::Vec; use getopts::Options; -use path::Path; pub fn main(args: Vec) -> isize { let mut opts = Options::new(); @@ -38,8 +36,8 @@ pub fn main(args: Vec) -> isize { if matches.free.is_empty() { curr_env.lock().working_dir = Arc::clone(root::get_root()); } else { - let path = Path::new(matches.free[0].to_string()); - match curr_env.lock().chdir(&path) { + let path = matches.free[0].as_ref(); + match curr_env.lock().chdir(path) { Err(environment::Error::NotADirectory) => { println!("not a directory: {}", path); return -1; diff --git a/applications/hull/src/builtin.rs b/applications/hull/src/builtin.rs index 436d6a6c7b..9b0ddf9f76 100644 --- a/applications/hull/src/builtin.rs +++ b/applications/hull/src/builtin.rs @@ -1,9 +1,8 @@ //! Builtin shell commands. use crate::{Error, Result, Shell}; -use alloc::{borrow::ToOwned, string::ToString}; +use alloc::string::ToString; use app_io::println; -use path::Path; // TODO: Decide which builtins we don't need. @@ -64,15 +63,16 @@ impl Shell { return Err(Error::Command(1)); } - let path = Path::new(if let Some(arg) = args.first() { - (*arg).to_owned() + let path = if let Some(arg) = args.first() { + *arg } else { - "/".to_owned() - }); + "/" + } + .as_ref(); let task = task::get_my_current_task().ok_or(Error::CurrentTaskUnavailable)?; - match task.get_env().lock().chdir(&path) { + match task.get_env().lock().chdir(path) { Ok(()) => Ok(()), Err(_) => { println!("no such file or directory: {path}"); diff --git a/applications/hull/src/lib.rs b/applications/hull/src/lib.rs index 49d1a3a32e..7e994937b4 100644 --- a/applications/hull/src/lib.rs +++ b/applications/hull/src/lib.rs @@ -39,7 +39,7 @@ use hashbrown::HashMap; use job::Job; use log::{error, warn}; use noline::{builder::EditorBuilder, sync::embedded::IO as Io}; -use path::Path; +use path::PathBuf; use stdio::Stdio; use sync_block::Mutex; use task::{ExitValue, KillReason}; @@ -306,7 +306,7 @@ impl Shell { .into_iter(); let app_path = match matching_files.next() { - Some(f) => Path::new(f.lock().get_absolute_path()), + Some(f) => PathBuf::from(f.lock().get_absolute_path()), None => return Err(Error::CommandNotFound(cmd.to_owned())), }; @@ -314,7 +314,7 @@ impl Shell { println!("multiple matching files found, running: {app_path}"); } - let task = spawn::new_application_task_builder(app_path, None) + let task = spawn::new_application_task_builder(&app_path, None) .map_err(Error::SpawnFailed)? .argument(args.into_iter().map(ToOwned::to_owned).collect::>()) .block() diff --git a/applications/loadc/src/lib.rs b/applications/loadc/src/lib.rs index c9ee6b1dde..4fdc62f406 100644 --- a/applications/loadc/src/lib.rs +++ b/applications/loadc/src/lib.rs @@ -26,8 +26,8 @@ use alloc::{collections::BTreeSet, string::{String, ToString}, sync::Arc, vec::V use getopts::{Matches, Options}; use memory::{Page, MappedPages, VirtualAddress, PteFlagsArch, PteFlags}; use mod_mgmt::{CrateNamespace, StrongDependency, find_symbol_table, RelocationEntry, write_relocation}; -use rustc_demangle::demangle; use path::Path; +use rustc_demangle::demangle; use xmas_elf::{ ElfFile, program::SegmentData, @@ -72,8 +72,7 @@ fn rmain(matches: Matches) -> Result { ).map_err(|_| String::from("failed to get current task"))?; let path = matches.free.get(0).ok_or_else(|| "Missing path to ELF executable".to_string())?; - let path = Path::new(path.clone()); - let file_ref = path.get_file(&curr_wd) + let file_ref = Path::new(path).get_file(&curr_wd) .ok_or_else(|| format!("Failed to access file at {path:?}"))?; let file = file_ref.lock(); diff --git a/applications/ls/src/lib.rs b/applications/ls/src/lib.rs index 0e885956ca..dd6cb40bef 100644 --- a/applications/ls/src/lib.rs +++ b/applications/ls/src/lib.rs @@ -9,7 +9,6 @@ extern crate path; use alloc::{ string::String, - string::ToString, vec::Vec, }; use core::fmt::Write; @@ -45,7 +44,7 @@ pub fn main(args: Vec) -> isize { return 0; } - let path = Path::new(matches.free[0].to_string()); + let path: &Path = matches.free[0].as_ref(); // Navigate to the path specified by first argument match path.get(&curr_wd) { diff --git a/applications/ns/src/lib.rs b/applications/ns/src/lib.rs index 1334ed5df4..c49baad568 100644 --- a/applications/ns/src/lib.rs +++ b/applications/ns/src/lib.rs @@ -25,7 +25,7 @@ use alloc::{ use getopts::{Options, Matches}; use mod_mgmt::CrateNamespace; use fs_node::FileRef; -use path::Path; +use path::PathBuf; pub fn main(args: Vec) -> isize { @@ -68,7 +68,7 @@ fn rmain(matches: Matches) -> Result<(), String> { let mut output = String::new(); if let Some(crate_obj_file_path) = matches.opt_str("load") { - let path = Path::new(crate_obj_file_path); + let path = PathBuf::from(crate_obj_file_path); let file = path.get_file(&curr_wd).ok_or_else(|| format!("Couldn't resolve path to crate object file at {path:?}") )?; diff --git a/applications/qemu_test/src/lib.rs b/applications/qemu_test/src/lib.rs index e6a46f726f..c79d2fc778 100644 --- a/applications/qemu_test/src/lib.rs +++ b/applications/qemu_test/src/lib.rs @@ -9,9 +9,9 @@ use alloc::{boxed::Box, string::String, vec::Vec}; use app_io::{print, println}; -use path::Path; use qemu_exit::{QEMUExit, X86}; use task::{ExitValue, KillReason}; +use path::{Path, PathBuf}; extern crate alloc; @@ -37,7 +37,7 @@ pub fn main(_: Vec) -> isize { // deadlock. let file = dir.lock().get_file(file_name.as_ref()).unwrap(); let path = file.lock().get_absolute_path(); - Some((file_name, Path::new(path))) + Some((file_name, PathBuf::from(path))) } else { None } @@ -56,7 +56,7 @@ pub fn main(_: Vec) -> isize { num_ignored += 1; println!("ignored"); } else { - match run_test(path) { + match run_test(&path) { Ok(_) => println!("ok"), Err(_) => { num_failed += 1; @@ -81,7 +81,7 @@ pub fn main(_: Vec) -> isize { } #[allow(clippy::result_unit_err)] -pub fn run_test(path: Path) -> Result<(), ()> { +pub fn run_test(path: &Path) -> Result<(), ()> { match spawn::new_application_task_builder(path, None) .unwrap() .argument(Vec::new()) diff --git a/applications/rm/src/lib.rs b/applications/rm/src/lib.rs index b2c440688e..3ad41784bd 100644 --- a/applications/rm/src/lib.rs +++ b/applications/rm/src/lib.rs @@ -13,7 +13,7 @@ use alloc::vec::Vec; use alloc::string::String; use alloc::string::ToString; use getopts::Options; -use path::Path; +use path::PathBuf; use fs_node::{FsNode, FileOrDir}; @@ -56,7 +56,7 @@ pub fn remove_node(args: Vec) -> Result<(), String> { } for path_string in &matches.free { - let path = Path::new(path_string.clone()); + let path = PathBuf::from(path_string.clone()); let node_to_delete = match path.get(&working_dir) { Some(node) => node, _ => return Err(format!("Couldn't find path {path}")), diff --git a/applications/shell/src/lib.rs b/applications/shell/src/lib.rs index 748f7ec303..6080e8a25c 100644 --- a/applications/shell/src/lib.rs +++ b/applications/shell/src/lib.rs @@ -638,10 +638,10 @@ impl Shell { let app_file = matching_apps.next(); let second_match = matching_apps.next(); // return an error if there are multiple matching apps let app_path = app_file.xor(second_match) - .map(|f| Path::new(f.lock().get_absolute_path())) + .map(|f| f.lock().get_absolute_path()) .ok_or(AppErr::NotFound(cmd))?; - let taskref = spawn::new_application_task_builder(app_path, None) + let taskref = spawn::new_application_task_builder(app_path.as_ref(), None) .map_err(|e| AppErr::SpawnErr(e.to_string()))? .argument(args) .block() @@ -860,7 +860,7 @@ impl Shell { // Walk through nodes existing in the command. for node in &nodes { - let path = Path::new(node.to_string()); + let path: &Path = node.as_ref(); match path.get(&curr_wd) { Some(file_dir_enum) => { match file_dir_enum { diff --git a/applications/swap/src/lib.rs b/applications/swap/src/lib.rs index 16c3867997..675340a0ba 100644 --- a/applications/swap/src/lib.rs +++ b/applications/swap/src/lib.rs @@ -72,7 +72,7 @@ fn rmain(matches: Matches) -> Result<(), String> { return Err("failed to get current task".to_string()); }; let override_namespace_crate_dir = if let Some(path) = matches.opt_str("d") { - let path = Path::new(path); + let path: &Path = path.as_ref(); let dir = match path.get(&curr_dir) { Some(FileOrDir::Dir(dir)) => dir, _ => return Err(format!("Error: could not find specified namespace crate directory: {path}.")), @@ -166,7 +166,7 @@ fn do_swap( let (into_new_crate_file, new_namespace) = { if let Some(f) = override_namespace_crate_dir.as_ref().and_then(|ns_dir| ns_dir.get_file_starting_with(new_crate_str)) { (IntoCrateObjectFile::File(f), None) - } else if let Some(FileOrDir::File(f)) = Path::new(String::from(new_crate_str)).get(curr_dir) { + } else if let Some(FileOrDir::File(f)) = Path::new(new_crate_str).get(curr_dir) { (IntoCrateObjectFile::File(f), None) } else { (IntoCrateObjectFile::Prefix(String::from(new_crate_str)), None) diff --git a/applications/test_wasmtime/src/lib.rs b/applications/test_wasmtime/src/lib.rs index 2893b36d5a..858d7c204f 100644 --- a/applications/test_wasmtime/src/lib.rs +++ b/applications/test_wasmtime/src/lib.rs @@ -29,7 +29,7 @@ pub fn main(args: Vec) -> isize { fn rmain(args: Vec) -> Result<(), String> { - let path_to_hello_cwasm = Path::new(args.get(0).cloned().unwrap_or("/extra_files/wasm/hello.cwasm".to_string())); + let path_to_hello_cwasm: &Path = args.get(0).map(|arg| &arg[..]).unwrap_or("/extra_files/wasm/hello.cwasm").as_ref(); let Ok(curr_wd) = task::with_current_task(|t| t.get_env().lock().working_dir.clone()) else { return Err("failed to get current task".to_string()); }; diff --git a/applications/upd/src/lib.rs b/applications/upd/src/lib.rs index d82fdce0c8..562f6af8cc 100644 --- a/applications/upd/src/lib.rs +++ b/applications/upd/src/lib.rs @@ -123,7 +123,7 @@ fn rmain(matches: Matches) -> Result<(), String> { } "apply" | "ap" => { let base_dir_path = matches.free.get(1).ok_or_else(|| String::from("missing BASE_DIR path argument"))?; - apply(&Path::new(base_dir_path.clone())) + apply(base_dir_path.as_ref()) } other => { Err(format!("unrecognized command {other:?}")) @@ -197,8 +197,8 @@ fn download(remote_endpoint: IpEndpoint, update_build: &str, crate_list: Option< let size = content.len(); // The name of the crate file that we downloaded is something like: "/keyboard_log/k#keyboard-36be916209949cef.o". // We need to get just the basename of the file, then remove the crate type prefix ("k#"). - let df_path = Path::new(df.name); - let cfile = new_namespace_dir.write_crate_object_file(df_path.basename(), content)?; + let file_name = Path::new(&df.name).file_name().ok_or("crate file path did not have file name")?; + let cfile = new_namespace_dir.write_crate_object_file(file_name, content)?; println!("Downloaded crate: {:?}, size {}", cfile.lock().get_absolute_path(), size); } @@ -257,7 +257,9 @@ fn apply(base_dir_path: &Path) -> Result<(), String> { // An empty old_crate_name indicates that there is no old crate or object file to remove, we are just loading a new crate (or inserting its object file) None } else { - let old_crate_name = mod_mgmt::crate_name_from_path(&Path::new(old_crate_module_file_name)).to_string(); + let old_crate_name = mod_mgmt::crate_name_from_path(old_crate_module_file_name.as_ref()) + .ok_or("invalid old crate module file name")? + .to_string(); if curr_namespace.get_crate(&old_crate_name).is_none() { println!("\t Note: old crate {:?} was not currently loaded into namespace {:?}.", old_crate_name, curr_namespace.name()); } diff --git a/applications/wasm/src/lib.rs b/applications/wasm/src/lib.rs index 15acd8fdfa..d52d82c0da 100644 --- a/applications/wasm/src/lib.rs +++ b/applications/wasm/src/lib.rs @@ -66,7 +66,7 @@ pub fn main(args: Vec) -> isize { // Verify passed preopened directories are real directories. for dir in preopened_dirs.iter() { - let dir_path = Path::new(dir.clone()); + let dir_path: &Path = dir.as_ref(); match dir_path.get(&curr_wd) { Some(file_dir_enum) => match file_dir_enum { @@ -92,7 +92,7 @@ pub fn main(args: Vec) -> isize { return -1; } - let wasm_binary_path = Path::new(args[0].clone()); + let wasm_binary_path: &Path = args[0].as_ref(); // Parse inputted WebAssembly binary path into byte array. let wasm_binary: Vec = match wasm_binary_path.get(&curr_wd) { diff --git a/kernel/console/src/lib.rs b/kernel/console/src/lib.rs index 38c2bf1ab8..723e628fca 100644 --- a/kernel/console/src/lib.rs +++ b/kernel/console/src/lib.rs @@ -116,8 +116,8 @@ fn shell_loop( mod_mgmt::CrateNamespace::get_crate_object_file_starting_with(&new_app_ns, "hull-") .expect("Couldn't find hull in default app namespace"); - let path = path::Path::new(app_file.lock().get_absolute_path()); - let task = spawn::new_application_task_builder(path, Some(new_app_ns))? + let path = app_file.lock().get_absolute_path(); + let task = spawn::new_application_task_builder(path.as_ref(), Some(new_app_ns))? .name(format!("{address:?}_hull")) .block() .spawn()?; diff --git a/kernel/crate_name_utils/src/lib.rs b/kernel/crate_name_utils/src/lib.rs index 25c2c75256..6de325cd40 100644 --- a/kernel/crate_name_utils/src/lib.rs +++ b/kernel/crate_name_utils/src/lib.rs @@ -25,12 +25,12 @@ use crate_metadata::CrateType; /// * be absolute or relative, /// * optionally end with an extension, e.g., `".o"`, optionally start /// * optionally start with a module file prefix, e.g., `"k#my_crate-.o"`. -pub fn crate_name_from_path(object_file_path: &Path) -> &str { - let stem = object_file_path.file_stem(); +pub fn crate_name_from_path(object_file_path: &Path) -> Option<&str> { + let stem = object_file_path.file_stem()?; if let Ok((_crate_type, _prefix, name)) = CrateType::from_module_name(stem) { - name + Some(name) } else { - stem + Some(stem) } } diff --git a/kernel/crate_swap/src/lib.rs b/kernel/crate_swap/src/lib.rs index db629c0d13..c9f943e87d 100644 --- a/kernel/crate_swap/src/lib.rs +++ b/kernel/crate_swap/src/lib.rs @@ -24,7 +24,7 @@ use core::{ ops::Deref, }; use alloc::{ - borrow::Cow, + borrow::{Cow, ToOwned}, collections::BTreeSet, string::{String, ToString}, sync::Arc, @@ -44,7 +44,7 @@ use mod_mgmt::{ StrongSectionRef, WeakDependent, StrRef, }; -use path::Path; +use path::{Path, PathBuf, Component}; use by_address::ByAddress; @@ -231,7 +231,7 @@ pub fn swap_crates( let reexport_new_symbols_as_old = *reexport_new_symbols_as_old; // Populate the list of new crate names for future usage. - let new_crate_name = crate_name_from_path(&Path::new(new_crate_object_file.lock().get_name())).to_string(); + let new_crate_name = crate_name_from_path(&PathBuf::from(new_crate_object_file.lock().get_name())).ok_or("invalid crate path")?.to_owned(); new_crate_names.push(new_crate_name.clone()); // Get a reference to the old crate that is currently loaded into the `old_namespace`. @@ -651,8 +651,8 @@ pub fn swap_crates( // FIXME: currently we use a hack to determine which namespace this freshly-loaded crate should be added to, // based on which directory its object file { - let objfile_path = Path::new(new_crate_ref.lock_as_ref().object_file.lock().get_absolute_path()); - if objfile_path.components().nth(1) == Some(mod_mgmt::CrateType::Kernel.default_namespace_name()) { + let objfile_path = PathBuf::from(new_crate_ref.lock_as_ref().object_file.lock().get_absolute_path()); + if objfile_path.components().nth(1) == Some(Component::Normal(mod_mgmt::CrateType::Kernel.default_namespace_name())) { let new_target_ns = this_namespace.recursive_namespace().unwrap_or(this_namespace); #[cfg(not(loscd_eval))] warn!("temp fix: changing target_ns from {} to {}, for crate {:?}", this_namespace.name(), new_target_ns.name(), new_crate_ref); @@ -919,7 +919,7 @@ impl SwapRequest { /// as the `this_namespace` argument that `swap_crates()` is invoked with. /// /// * `new_crate_object_file`: a type that can be converted into a crate object file. - /// This can either be a direct reference to the file, an absolute `Path` that points to the file, + /// This can either be a direct reference to the file, an absolute `PathBuf` that points to the file, /// or a prefix string used to find the file in the new namespace's directory of crate object files. /// /// * `new_namespace`: the `CrateNamespace` to which the new crate will be loaded and its symbols added. @@ -957,8 +957,10 @@ impl SwapRequest { let mut matching_files = CrateNamespace::get_crate_object_files_starting_with(&old_namespace, ocn); if matching_files.len() == 1 { let (old_crate_file, real_old_namespace) = matching_files.remove(0); - let old_crate_file_path = Path::new(old_crate_file.lock().get_name()); - let old_crate_full_name = crate_name_from_path(&old_crate_file_path).to_string(); + let old_crate_file_path = PathBuf::from(old_crate_file.lock().get_name()); + let old_crate_full_name = crate_name_from_path(&old_crate_file_path) + .ok_or(InvalidSwapRequest::OldCrateNotFound(old_crate_name.map(|name| name.to_owned()), old_namespace.clone(), Vec::new()))? + .into(); (Some(old_crate_full_name), real_old_namespace) } else { // Here, we couldn't find a single matching loaded crate or crate object file, so we return an error. @@ -1004,7 +1006,7 @@ impl SwapRequest { _ => if path.is_absolute() { return Err(InvalidSwapRequest::NewCrateAbsolutePathNotFound(path)); } else { - return Err(InvalidSwapRequest::NewCratePathNotAbsolute(path)); + return Err(InvalidSwapRequest::NewCratePathBufNotAbsolute(path)); }, } IntoCrateObjectFile::Prefix(prefix) => { @@ -1045,10 +1047,10 @@ pub enum InvalidSwapRequest { /// The enclosed vector is the list of matching crate names or crate object file names /// along with the `CrateNamespace` in which they were found. OldCrateNotFound(Option, Arc, Vec<(String, Arc)>), - /// The given absolute `Path` for the new crate object file could not be resolved. - NewCrateAbsolutePathNotFound(Path), - /// The given `Path` for the new crate object file was not an absolute path, as expected. - NewCratePathNotAbsolute(Path), + /// The given absolute `PathBuf` for the new crate object file could not be resolved. + NewCrateAbsolutePathNotFound(PathBuf), + /// The given `PathBuf` for the new crate object file was not an absolute path, as expected. + NewCratePathBufNotAbsolute(PathBuf), /// A single crate object file could not be found by matching the given prefix `String` /// within the given new `CrateNamespace` (which was searched recursively). /// Either zero or multiple crate object files matched the prefix, @@ -1072,11 +1074,11 @@ impl fmt::Debug for InvalidSwapRequest { } } Self::NewCrateAbsolutePathNotFound(path) => { - dbg.field("reason", &"New Crate Absolute Path Not Found") + dbg.field("reason", &"New Crate Absolute PathBuf Not Found") .field("path", &path); } - Self::NewCratePathNotAbsolute(path) => { - dbg.field("reason", &"New Crate Path Not Absolute") + Self::NewCratePathBufNotAbsolute(path) => { + dbg.field("reason", &"New Crate PathBuf Not Absolute") .field("path", &path); } Self::NewCratePrefixNotFound(prefix, new_namespace, matches) => { diff --git a/kernel/environment/src/lib.rs b/kernel/environment/src/lib.rs index b569283fd0..df3e23d1dc 100644 --- a/kernel/environment/src/lib.rs +++ b/kernel/environment/src/lib.rs @@ -31,7 +31,8 @@ impl Environment { /// Changes the current working directory. #[doc(alias("change"))] pub fn chdir(&mut self, path: &Path) -> Result<()> { - match path.get(&self.working_dir) { + let new_dir = self.working_dir.lock().get(path.as_ref()); + match new_dir { Some(FileOrDir::Dir(dir_ref)) => { self.working_dir = dir_ref; Ok(()) diff --git a/kernel/fault_crate_swap/src/lib.rs b/kernel/fault_crate_swap/src/lib.rs index a05a48156d..775b24a269 100644 --- a/kernel/fault_crate_swap/src/lib.rs +++ b/kernel/fault_crate_swap/src/lib.rs @@ -29,7 +29,7 @@ use mod_mgmt::{ NamespaceDir, IntoCrateObjectFile, }; -use path::Path; +use path::PathBuf; use crate_swap::{SwapRequest, swap_crates}; use fault_log::{RecoveryAction, FaultEntry, remove_unhandled_exceptions, log_handled_fault}; @@ -73,7 +73,7 @@ pub fn do_self_swap( let (into_new_crate_file, new_namespace) = { if let Some(f) = override_namespace_crate_dir.as_ref().and_then(|ns_dir| ns_dir.get_file_starting_with(crate_name)) { (IntoCrateObjectFile::File(f), None) - } else if let Some(FileOrDir::File(f)) = Path::new(String::from(crate_name)).get(curr_dir) { + } else if let Some(FileOrDir::File(f)) = PathBuf::from(String::from(crate_name)).get(curr_dir) { (IntoCrateObjectFile::File(f), None) } else { (IntoCrateObjectFile::Prefix(String::from(crate_name)), None) diff --git a/kernel/first_application/src/lib.rs b/kernel/first_application/src/lib.rs index 817e7f0352..ef2c402355 100644 --- a/kernel/first_application/src/lib.rs +++ b/kernel/first_application/src/lib.rs @@ -24,7 +24,6 @@ extern crate path; use alloc::format; use mod_mgmt::CrateNamespace; -use path::Path; /// See the crate-level docs and this crate's `Cargo.toml` for more. const FIRST_APPLICATION_CRATE_NAME: &str = { @@ -50,9 +49,9 @@ pub fn start() -> Result<(), &'static str> { FIRST_APPLICATION_CRATE_NAME, ).ok_or("Couldn't find first application in default app namespace")?; - let path = Path::new(app_file.lock().get_absolute_path()); + let path = app_file.lock().get_absolute_path(); info!("Starting first application: crate at {:?}", path); - spawn::new_application_task_builder(path, Some(new_app_ns))? + spawn::new_application_task_builder(path.as_ref(), Some(new_app_ns))? .name(format!("first_{}", &FIRST_APPLICATION_CRATE_NAME[.. FIRST_APPLICATION_CRATE_NAME.len() - 1])) .spawn()?; diff --git a/kernel/mod_mgmt/src/lib.rs b/kernel/mod_mgmt/src/lib.rs index 737e1c3e3d..a06cbff74e 100644 --- a/kernel/mod_mgmt/src/lib.rs +++ b/kernel/mod_mgmt/src/lib.rs @@ -21,7 +21,7 @@ use rustc_demangle::demangle; use qp_trie::Trie; use fs_node::{FileOrDir, File, FileRef, DirRef}; use vfs_node::VFSDirectory; -use path::Path; +use path::{Path, PathBuf}; use memfs::MemFile; use hashbrown::HashMap; use crate_metadata_serde::{CLS_SECTION_FLAG, CLS_SYMBOL_TYPE}; @@ -387,7 +387,7 @@ pub enum IntoCrateObjectFile { /// A direct reference to the crate object file. This will be used as-is. File(FileRef), /// An absolute path that points to the crate object file. - AbsolutePath(Path), + AbsolutePath(PathBuf), /// A string prefix that will be used to search for the crate object file in the namespace. /// This must be able to uniquely identify a single crate object file in the namespace directory (recursively searched). Prefix(String), @@ -1052,8 +1052,11 @@ impl CrateNamespace { ) -> Result<(StrongCrateRef, ElfFile<'f>), &'static str> { let mapped_pages = crate_file.as_mapping()?; let size_in_bytes = crate_file.len(); - let abs_path = Path::new(crate_file.get_absolute_path()); - let crate_name = StrRef::from(crate_name_from_path(&abs_path)); + let abs_path = PathBuf::from(crate_file.get_absolute_path()); + let crate_name = StrRef::from( + crate_name_from_path(&abs_path) + .ok_or("failed to get crate name from path")? + ); // First, check to make sure this crate hasn't already been loaded. // Application crates are now added to the CrateNamespace just like kernel crates, @@ -2839,9 +2842,9 @@ impl CrateNamespace { // The object files from the recursive namespace(s) are appended after the files in the initial namespace, // so they'll only be searched if the symbol isn't found in the current namespace. for (potential_crate_file, ns_of_crate_file) in self.method_get_crate_object_files_starting_with(&potential_crate_name) { - let potential_crate_file_path = Path::new(potential_crate_file.lock().get_absolute_path()); + let potential_crate_file_path = PathBuf::from(potential_crate_file.lock().get_absolute_path()); // Check to make sure this crate is not already loaded into this namespace (or its recursive namespace). - if self.get_crate(crate_name_from_path(&potential_crate_file_path)).is_some() { + if self.get_crate(crate_name_from_path(&potential_crate_file_path)?).is_some() { trace!(" (skipping already-loaded crate {:?})", potential_crate_file_path); continue; } diff --git a/kernel/mod_mgmt/src/parse_nano_core.rs b/kernel/mod_mgmt/src/parse_nano_core.rs index 604ea2549e..f3bf0eee7f 100644 --- a/kernel/mod_mgmt/src/parse_nano_core.rs +++ b/kernel/mod_mgmt/src/parse_nano_core.rs @@ -8,7 +8,7 @@ use alloc::{collections::{BTreeMap, BTreeSet}, string::{String, ToString}, sync::Arc}; use crate::{CrateNamespace, mp_range, CLS_SECTION_FLAG}; use fs_node::FileRef; -use path::Path; +use path::PathBuf; use rustc_demangle::demangle; use spin::Mutex; use cow_arc::{CowArc, CowWeak}; @@ -78,7 +78,7 @@ pub fn parse_nano_core( CrateNamespace::get_crate_object_file_starting_with(namespace, NANO_CORE_FILENAME_PREFIX) .ok_or("couldn't find the expected \"nano_core\" kernel file") ); - let nano_core_file_path = Path::new(nano_core_file.lock().get_absolute_path()); + let nano_core_file_path = PathBuf::from(nano_core_file.lock().get_absolute_path()); debug!( "parse_nano_core(): trying to load and parse the nano_core file: {:?}", nano_core_file_path diff --git a/kernel/path/Cargo.toml b/kernel/path/Cargo.toml index 6f23768517..c4119e1d5b 100644 --- a/kernel/path/Cargo.toml +++ b/kernel/path/Cargo.toml @@ -1,28 +1,10 @@ [package] name = "path" version = "0.1.0" -authors = ["Andrew Pham , Christine Wang , Christine Wang "] +description = "File system path manipulation" +edition = "2021" [dependencies] -spin = "0.9.4" - -[dependencies.lazy_static] -features = ["spin_no_std"] -version = "1.4.0" - -[dependencies.fs_node] -path = "../fs_node" - -[dependencies.vfs_node] -path = "../vfs_node" - -[dependencies.root] -path = "../root" - -[dependencies.log] -version = "0.4.8" - -[lib] -crate-type = ["rlib"] +fs_node = { path = "../fs_node" } +root = { path = "../root" } diff --git a/kernel/path/src/component.rs b/kernel/path/src/component.rs new file mode 100644 index 0000000000..0c5afad754 --- /dev/null +++ b/kernel/path/src/component.rs @@ -0,0 +1,339 @@ +/// The implementation is heavily based on `std`. +use crate::Path; + +pub const SEPARATOR: char = '/'; +pub const SEPARATOR_STR: &str = "/"; +pub const CURRENT_DIR_WITH_SEPARATOR: &str = "./"; + +/// An iterator over the components of a path. +/// +/// This struct is created by the [`components`] method on Path. See its +/// documentation for more details. +/// +/// [`components`]: Path::components +#[derive(Clone, PartialEq, PartialOrd, Debug)] +pub struct Components<'a> { + path: &'a Path, + front: State, + back: State, +} + +#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +enum State { + StartDir = 0, + Body = 1, + Done = 2, +} + +impl<'a> Iterator for Components<'a> { + type Item = Component<'a>; + + #[inline] + fn next(&mut self) -> Option { + while !self.finished() { + match self.front { + State::StartDir => { + self.front = State::Body; + if self.path.inner.starts_with(SEPARATOR) { + // Trim the starting slash. Even if there are subsequent slashes, they will + // be ignored as we change our state to State::Body. + self.path = Path::new(&self.path.inner[1..]); + return Some(Component::RootDir); + } else if self.include_cur_dir() { + // Trim the dot. + self.path = Path::new(&self.path.inner[1..]); + return Some(Component::CurDir); + } + } + State::Body if !self.path.inner.is_empty() => { + let (rest, component) = self.peek(); + self.path = rest; + if component.is_some() { + return component; + } + } + State::Body => { + self.front = State::Done; + } + State::Done => unreachable!(), + } + } + None + } +} + +impl<'a> DoubleEndedIterator for Components<'a> { + #[inline] + fn next_back(&mut self) -> Option { + while !self.finished() { + match self.back { + State::Body if self.path.inner.len() > self.len_before_body() => { + let (rest, component) = self.peek_back(); + self.path = rest; + if component.is_some() { + return component; + } + } + State::Body => { + self.back = State::StartDir; + } + State::StartDir => { + self.back = State::Done; + if self.has_root() { + self.path = Path::new(&self.path.inner[..self.path.inner.len() - 1]); + return Some(Component::RootDir); + } else if self.include_cur_dir() { + self.path = Path::new(&self.path.inner[..self.path.inner.len() - 1]); + return Some(Component::CurDir); + } + } + State::Done => unreachable!(), + } + } + None + } +} + +impl<'a> Components<'a> { + pub(crate) fn new(path: &'a Path) -> Self { + Self { + path, + front: State::StartDir, + back: State::Body, + } + } + + /// Extracts a slice corresponding to the portion of the path remaining for + /// iteration. + #[inline] + pub fn as_path(&self) -> &'a Path { + let mut components = self.clone(); + if components.front == State::Body { + components.trim_left(); + } + if components.back == State::Body { + components.trim_right(); + } + components.path + } + + fn include_cur_dir(&self) -> bool { + self.path == ".".as_ref() || self.path.inner.starts_with(CURRENT_DIR_WITH_SEPARATOR) + } + + fn has_root(&self) -> bool { + self.path.inner.starts_with(SEPARATOR) + } + + fn len_before_body(&self) -> usize { + let root = if self.front == State::StartDir && self.has_root() { + 1 + } else { + 0 + }; + let cur_dir = if self.front == State::StartDir && self.include_cur_dir() { + 1 + } else { + 0 + }; + root + cur_dir + } + + fn trim_left(&mut self) { + while !self.path.inner.is_empty() { + let (rest, comp) = self.peek(); + if comp.is_some() { + return; + } else { + self.path = rest; + } + } + } + + fn peek(&self) -> (&'a Path, Option>) { + match self.path.inner.split_once(SEPARATOR) { + Some((next, rest)) => (Path::new(rest), component(next)), + None => (Path::new(""), component(self.path.as_ref())), + } + } + + fn trim_right(&mut self) { + while self.path.inner.len() > self.len_before_body() { + let (rest, comp) = self.peek_back(); + if comp.is_some() { + return; + } else { + self.path = rest; + } + } + } + + fn peek_back(&self) -> (&'a Path, Option>) { + match self.path.inner[self.len_before_body()..].rsplit_once(SEPARATOR) { + Some((rest, next)) => ( + Path::new(&self.path.inner[..(self.len_before_body() + rest.len())]), + component(next), + ), + None => ( + Path::new(&self.path.inner[..self.len_before_body()]), + component(&self.path.inner[self.len_before_body()..]), + ), + } + } + + fn finished(&self) -> bool { + self.front == State::Done || self.back == State::Done || self.front > self.back + } +} + +fn component(component: &str) -> Option> { + match component { + "." | "" => None, + ".." => Some(Component::ParentDir), + _ => Some(Component::Normal(component)), + } +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum Component<'a> { + RootDir, + CurDir, + ParentDir, + Normal(&'a str), +} + +impl<'a> AsRef for Component<'a> { + #[inline] + fn as_ref(&self) -> &'a Path { + // TODO: Why is this a lifetime error? + // Path::new(AsRef::::as_ref(self)) + match self { + Component::RootDir => Path::new(SEPARATOR_STR), + Component::CurDir => Path::new("."), + Component::ParentDir => Path::new(".."), + Component::Normal(path) => Path::new(*path), + } + } +} + +impl<'a> AsRef for Component<'a> { + #[inline] + fn as_ref(&self) -> &'a str { + match self { + Component::RootDir => SEPARATOR_STR, + Component::CurDir => ".", + Component::ParentDir => "..", + Component::Normal(path) => path, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_components_iter_front() { + let mut components = Path::new("/tmp/foo/bar.txt").components(); + assert_eq!(components.as_path(), "/tmp/foo/bar.txt".as_ref()); + assert_eq!(components.next(), Some(Component::RootDir)); + assert_eq!(components.as_path(), "tmp/foo/bar.txt".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("tmp"))); + assert_eq!(components.as_path(), "foo/bar.txt".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "bar.txt".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("bar.txt"))); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next(), None); + + let mut components = Path::new("//tmp//../foo/./").components(); + assert_eq!(components.as_path(), "//tmp//../foo".as_ref()); + assert_eq!(components.next(), Some(Component::RootDir)); + assert_eq!(components.as_path(), "tmp//../foo".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("tmp"))); + assert_eq!(components.as_path(), "../foo".as_ref()); + assert_eq!(components.next(), Some(Component::ParentDir)); + assert_eq!(components.as_path(), "foo".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next(), None); + + let mut components = Path::new("..//./foo").components(); + assert_eq!(components.as_path(), "..//./foo".as_ref()); + assert_eq!(components.next(), Some(Component::ParentDir)); + assert_eq!(components.as_path(), "foo".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next(), None); + } + + #[test] + fn test_components_iter_back() { + let mut components = Path::new("/tmp/foo/bar.txt").components(); + assert_eq!(components.as_path(), "/tmp/foo/bar.txt".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("bar.txt"))); + assert_eq!(components.as_path(), "/tmp/foo".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "/tmp".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("tmp"))); + assert_eq!(components.as_path(), "/".as_ref()); + assert_eq!(components.next_back(), Some(Component::RootDir)); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next_back(), None); + + let mut components = Path::new("//tmp//../foo/./").components(); + assert_eq!(components.as_path(), "//tmp//../foo".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "//tmp//..".as_ref()); + assert_eq!(components.next_back(), Some(Component::ParentDir)); + assert_eq!(components.as_path(), "//tmp".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("tmp"))); + assert_eq!(components.as_path(), "/".as_ref()); + assert_eq!(components.next_back(), Some(Component::RootDir)); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next_back(), None); + + let mut components = Path::new("..//./foo").components(); + assert_eq!(components.as_path(), "..//./foo".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "..".as_ref()); + assert_eq!(components.next_back(), Some(Component::ParentDir)); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next_back(), None); + } + + #[test] + fn test_components_iter_front_back() { + let mut components = Path::new("/tmp/foo/bar.txt").components(); + assert_eq!(components.as_path(), "/tmp/foo/bar.txt".as_ref()); + assert_eq!(components.next(), Some(Component::RootDir)); + assert_eq!(components.as_path(), "tmp/foo/bar.txt".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("bar.txt"))); + assert_eq!(components.as_path(), "tmp/foo".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("tmp"))); + assert_eq!(components.as_path(), "foo".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next_back(), None); + + let mut components = Path::new("//tmp//../foo/./").components(); + assert_eq!(components.as_path(), "//tmp//../foo".as_ref()); + assert_eq!(components.next(), Some(Component::RootDir)); + assert_eq!(components.as_path(), "tmp//../foo".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "tmp//..".as_ref()); + assert_eq!(components.next_back(), Some(Component::ParentDir)); + assert_eq!(components.as_path(), "tmp".as_ref()); + assert_eq!(components.next(), Some(Component::Normal("tmp"))); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next_back(), None); + + let mut components = Path::new("..//./foo").components(); + assert_eq!(components.as_path(), "..//./foo".as_ref()); + assert_eq!(components.next_back(), Some(Component::Normal("foo"))); + assert_eq!(components.as_path(), "..".as_ref()); + assert_eq!(components.next(), Some(Component::ParentDir)); + assert_eq!(components.as_path(), "".as_ref()); + assert_eq!(components.next_back(), None); + } +} diff --git a/kernel/path/src/lib.rs b/kernel/path/src/lib.rs index dfc9a431d0..3791ee497a 100644 --- a/kernel/path/src/lib.rs +++ b/kernel/path/src/lib.rs @@ -1,279 +1,628 @@ +//! File system paths. +//! +//! This crate is designed to mimic `std::path` and as such, much of the +//! documentation and implementation is the same. + #![no_std] -/// This crate contains all the necessary functions for navigating the virtual filesystem / obtaining specific -/// directories via the Path struct -// #[macro_use] extern crate log; + extern crate alloc; -extern crate spin; -extern crate fs_node; -extern crate root; +mod component; + +use alloc::{borrow::ToOwned, string::String, vec, vec::Vec}; use core::{ - fmt, - fmt::Write, + borrow::Borrow, + fmt::{self, Display}, ops::{Deref, DerefMut}, }; -use alloc::{ - string::{String, ToString}, - vec::Vec, - sync::Arc, -}; -use fs_node::{FileOrDir, FileRef, DirRef}; -pub const PATH_DELIMITER: &str = "/"; -pub const EXTENSION_DELIMITER: &str = "."; +pub use component::{Component, Components}; - -/// A structure that represents a relative or absolute path -/// to a file or directory. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +/// A slice of a path. +/// +/// This type is just a wrapper around a [`str`]. +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +#[repr(transparent)] pub struct Path { - path: String + inner: str, +} + +impl AsRef for Path { + #[inline] + fn as_ref(&self) -> &Path { + self + } +} + +impl AsMut for Path { + #[inline] + fn as_mut(&mut self) -> &mut Path { + self + } } -impl Deref for Path { - type Target = String; +impl AsRef for Path { + #[inline] + fn as_ref(&self) -> &str { + &self.inner + } +} - fn deref(&self) -> &String { - &self.path +impl AsMut for Path { + #[inline] + fn as_mut(&mut self) -> &mut str { + &mut self.inner } } -impl DerefMut for Path { - fn deref_mut(&mut self) -> &mut String { - &mut self.path + +impl AsRef for str { + #[inline] + fn as_ref(&self) -> &Path { + Path::new(self) } } -impl fmt::Display for Path { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - write!(f, "{}", self.path) +impl AsMut for str { + #[inline] + fn as_mut(&mut self) -> &mut Path { + // SAFETY: Path has the same type layout as str. This is the same + // implementation as std: https://github.com/rust-lang/rust/blob/f654229c27267334023a22233795b88b75fc340e/library/std/src/path.rs#L2047 + unsafe { &mut *(self as *mut str as *mut Path) } } } -impl From for Path { +impl AsRef for String { #[inline] - fn from(path: String) -> Self { - Path { path } + fn as_ref(&self) -> &Path { + self[..].as_ref() } } -impl From for String { +impl AsMut for String { #[inline] - fn from(path: Path) -> String { - path.path + fn as_mut(&mut self) -> &mut Path { + self[..].as_mut() + } +} + +impl Display for Path { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl ToOwned for Path { + type Owned = PathBuf; + + #[inline] + fn to_owned(&self) -> Self::Owned { + PathBuf { + inner: self.inner.to_owned(), + } } } impl Path { - /// Creates a new `Path` from the given String. - pub fn new(path: String) -> Self { - Path { path } + /// Wraps a string slice as a path slice. + /// + /// This is a cost-free conversion. + #[inline] + pub fn new(s: &S) -> &Self + where + S: AsRef + ?Sized, + { + // SAFETY: Path has the same type layout as str. This is the same + // implementation as std: https://github.com/rust-lang/rust/blob/f654229c27267334023a22233795b88b75fc340e/library/std/src/path.rs#L2041 + unsafe { &*(s.as_ref() as *const str as *const Path) } } - - /// Returns an iterator over the components of this `Path`, - /// split by the path delimiter `"/"`. - pub fn components(&self) -> impl Iterator { - self.path.split(PATH_DELIMITER) - .filter(|&x| !x.is_empty()) + + /// Produces an iterator over the [`Component`]s of the path. + /// + /// When parsing the path there is a small amount of normalization: + /// - Repeated separators are ignored, so `a/b` and `a//b` both have `a` and + /// `b` as components. + /// - Occurrences of `.` are normalized away, except if they are at the + /// beginning of the path. For example, `a/./b`, `a/b/`, `a/b/.` and `a/b` + /// all have `a` and `b` as components, but `./a/b` starts with an + /// additional [`CurDir`] component. + /// - A trailing slash is normalized away, `/a/b` and `/a/b/` are + /// equivalent. + /// + /// # Examples + /// + /// ``` + /// # use path::{Component, Path}; + /// let mut components = Path::new("/tmp/foo.txt").components(); + /// + /// assert_eq!(components.next(), Some(Component::RootDir)); + /// assert_eq!(components.next(), Some(Component::Normal("tmp"))); + /// assert_eq!(components.next(), Some(Component::Normal("foo.txt"))); + /// assert_eq!(components.next(), None) + /// ``` + /// + /// [`CurDir`]: Component::CurDir + #[inline] + pub fn components(&self) -> Components<'_> { + Components::new(self) } - /// Returns a reverse iterator over the components of this `Path`, - /// split by the path delimiter `"/"`. - pub fn rcomponents(&self) -> impl Iterator { - self.path.rsplit(PATH_DELIMITER) - .filter(|&x| !x.is_empty()) + /// Returns true if the path starts with the root. + /// + /// # Examples + /// + /// ``` + /// # use path::Path; + /// assert!(Path::new("/foo.txt").is_absolute()); + /// assert!(!Path::new("foo.txt").is_absolute()); + /// ``` + #[inline] + pub fn is_absolute(&self) -> bool { + self.inner.starts_with('/') } - /// Returns just the file name, i.e., the trailling component of the path. + /// Creates an owned [`PathBuf`] with `path` adjoined to `self`. + /// + /// If `path` is absolute, it replaces the current path. + /// /// # Examples - /// `"/path/to/my/file.a"` -> "file.a" - /// `"my/file.a"` -> "file.a" - /// `"file.a"` -> "file.a" - pub fn basename(&self) -> &str { - self.rcomponents() - .next() - .unwrap_or(&self.path) - } - - /// Like [`basename()`](#method.basename), but excludes the file extension, if present. - pub fn file_stem(&self) -> &str { - self.basename() - .split(EXTENSION_DELIMITER) - .find(|&x| !x.is_empty()) - .unwrap_or(&self.path) - } - - /// Returns the file extension, if present. - /// If there are multiple extensions as defined by the extension delimiter, `'.'`, - /// then the last one will be treated as the extension. - pub fn extension(&self) -> Option<&str> { - self.basename() - .rsplit(EXTENSION_DELIMITER) - .find(|&x| !x.is_empty()) - } - - /// Returns a canonical and absolute form of the current path (i.e. the path of the working directory) - /// TODO: FIXME: this doesn't work if the `current_path` is absolute. - #[allow(dead_code)] - fn canonicalize(&self, current_path: &Path) -> Path { - let mut new_components = Vec::new(); - // Push the components of the working directory to the components of the new path - new_components.extend(current_path.components()); - // Push components of the path to the components of the new path - for component in self.components() { - if component == "." { - continue; - } else if component == ".." { - new_components.pop(); - } else { - new_components.push(component); - } - } - // Create the new path from its components - let mut new_path = String::new(); - let mut first_cmpnt = true; - for component in new_components { - if first_cmpnt { - new_path.push_str(component); - first_cmpnt = false; - } - else { - write!(new_path, "/{component}").expect("Failed to create new path from its components"); - } - } - Path::new(new_path) - } - - /// Returns a `Path` that expresses a relative path from this `Path` (`self`) - /// to the given `other` `Path`. - // An example algorithm: https://docs.rs/pathdiff/0.1.0/src/pathdiff/lib.rs.html#32-74 - pub fn relative(&self, other: &Path) -> Option { - let mut ita_iter = self.components(); - let mut itb_iter = other.components(); - let mut comps: Vec = Vec::new(); - loop { - match (ita_iter.next(), itb_iter.next()) { - (None, None) => break, - (Some(a), None) => { - comps.push(a.to_string()); - for remaining_a in ita_iter { - comps.push(remaining_a.to_string()); - } - break; - } - (None, _) => comps.push("..".to_string()), - (Some(ref a), Some(ref b)) if comps.is_empty() && a == b => continue, - (Some(_a), Some(ref b)) if b == &".".to_string() => comps.push("..".to_string()), - (Some(_), Some(ref b)) if b == &"..".to_string() => return None, - (Some(a), Some(_)) => { - comps.push("..".to_string()); - for _ in itb_iter { - comps.push("..".to_string()); - } - comps.push(a.to_string()); - for remaining_a in ita_iter { - comps.push(remaining_a.to_string()); - } - break; - } + /// + /// ``` + /// # use path::{Path, PathBuf}; + /// assert_eq!( + /// Path::new("/etc").join("passwd"), + /// PathBuf::from("/etc/passwd") + /// ); + /// assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh")); + /// ``` + #[inline] + pub fn join

(&self, path: P) -> PathBuf + where + P: AsRef, + { + let mut buf = self.to_owned(); + buf.push(path); + buf + } + + /// Returns the path without its final component, if there is one. + /// + /// # Examples + /// + /// ``` + /// # use path::Path; + /// let path = Path::new("/foo/bar"); + /// let parent = path.parent().unwrap(); + /// assert_eq!(parent, Path::new("/foo")); + /// + /// let grand_parent = parent.parent().unwrap(); + /// assert_eq!(grand_parent, Path::new("/")); + /// assert_eq!(grand_parent.parent(), None); + /// + /// let relative_path = Path::new("foo/bar"); + /// let parent = relative_path.parent(); + /// assert_eq!(parent, Some(Path::new("foo"))); + /// let grand_parent = parent.and_then(Path::parent); + /// assert_eq!(grand_parent, Some(Path::new(""))); + /// assert_eq!(grand_parent, Some(Path::new(""))); + /// let great_grand_parent = grand_parent.and_then(Path::parent); + /// assert_eq!(great_grand_parent, None); + /// ``` + #[inline] + pub fn parent(&self) -> Option<&Self> { + let mut components = self.components(); + + let component = components.next_back(); + component.and_then(|p| match p { + Component::Normal(_) | Component::CurDir | Component::ParentDir => { + Some(components.as_path()) } - } - // Create the new path from its components - let mut new_path = String::new(); - for component in comps.iter() { - write!(new_path, "{component}/").expect("Failed to create new path from its components"); - } - // Remove the trailing slash after the final path component - new_path.pop(); - Some(Path::new(new_path)) + _ => None, + }) } - - /// Returns a boolean indicating whether this Path is absolute, - /// i.e., whether it starts with the root directory. - pub fn is_absolute(&self) -> bool { - self.path.starts_with(PATH_DELIMITER) + + /// Returns the final component of the `Path`, if there is one. + /// + /// If the path is a normal file, this is the file name. If it's the path of + /// a directory, this is the directory name. + /// + /// Returns [`None`] if the path terminates in `..`. + /// + /// # Examples + /// + /// ``` + /// # use path::Path; + /// assert_eq!(Some("bin"), Path::new("/usr/bin/").file_name()); + /// assert_eq!(Some("foo.txt"), Path::new("tmp/foo.txt").file_name()); + /// assert_eq!(Some("foo.txt"), Path::new("foo.txt/.").file_name()); + /// assert_eq!(Some("foo.txt"), Path::new("foo.txt/.//").file_name()); + /// assert_eq!(None, Path::new("foo.txt/..").file_name()); + /// assert_eq!(None, Path::new("/").file_name()); + /// ``` + #[inline] + pub fn file_name(&self) -> Option<&str> { + self.components().next_back().and_then(|p| match p { + Component::Normal(p) => Some(p), + _ => None, + }) } - /// Returns the file or directory specified by the given path, - /// which can either be absolute or relative from the given starting directory. - pub fn get(&self, starting_dir: &DirRef) -> Option { - // let current_path = { Path::new(starting_dir.lock().get_absolute_path()) }; - let mut curr_dir = { - if self.is_absolute() { - Arc::clone(root::get_root()) + /// Extracts the stem (non-extension) portion of [`self.file_name`]. + /// + /// [`self.file_name`]: Path::file_name + /// + /// The stem is: + /// + /// - [`None`], if there is no file name; + /// - The entire file name if there is no embedded `.`; + /// - The entire file name if the file name begins with `.` and has no other + /// `.`s within; + /// - Otherwise, the portion of the file name before the final `.` + /// + /// # Examples + /// + /// ``` + /// # use path::Path; + /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap()); + /// assert_eq!(".foo", Path::new(".foo").file_stem().unwrap()); + /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap()); + /// ``` + #[inline] + pub fn file_stem(&self) -> Option<&str> { + self.file_name().map(|name| match name.rsplit_once('.') { + Some((before, _)) => { + if before.is_empty() { + // The file starts with a `.` and has no other `.`s within. + name + } else { + before + } } - else { - Arc::clone(starting_dir) + None => name, + }) + } + + // TODO: Move out of path crate. + + /// Returns the file or directory at the given path. + /// + /// The path can be relative or absolute. + /// + /// If the path does not point to a file system object, `None` is returned. + #[inline] + pub fn get(&self, cwd: &fs_node::DirRef) -> Option { + let mut iter = self.components().peekable(); + let mut current = match iter.peek() { + Some(Component::RootDir) => { + iter.next(); + root::get_root().clone() } + _ => cwd.clone(), }; - for component in self.components() { + while let Some(component) = iter.next() { match component { - "." => { - // stay in the current directory, do nothing. + Component::RootDir => current = root::get_root().clone(), + Component::CurDir => {} + Component::ParentDir => { + let temp = current.lock().get_parent_dir()?; + current = temp; } - ".." => { - // navigate to parent directory - let parent_dir = curr_dir.lock().get_parent_dir()?; - curr_dir = parent_dir; - } - cmpnt => { - // navigate to child directory, or return the child file - let child_dir = match curr_dir.lock().get(cmpnt) { - Some(FileOrDir::File(f)) => return Some(FileOrDir::File(f)), - Some(FileOrDir::Dir(d)) => d, - None => return None, - }; - curr_dir = child_dir; + Component::Normal(name) => { + if iter.peek().is_none() { + return current.lock().get(name); + } else { + let temp = match current.lock().get(name) { + Some(fs_node::FileOrDir::Dir(directory)) => directory, + // Path didn't exist or had a file in the middle e.g. /dir/file/dir + _ => return None, + }; + current = temp; + } } } } - Some(FileOrDir::Dir(curr_dir)) + + Some(fs_node::FileOrDir::Dir(current)) } - /// Returns the file specified by the given path, which can be either absolute, - /// or relative from the given starting directory. + // TODO: Move out of path crate. + /// Returns the file at the given path. /// - /// If the path is invalid or points to a directory, then `None` is returned. - pub fn get_file(&self, starting_dir: &DirRef) -> Option { - match self.get(starting_dir) { - Some(FileOrDir::File(file)) => Some(file), + /// The path can be relative or absolute. + /// + /// If the path does not point to a file, `None` is returned. + #[inline] + pub fn get_file(&self, cwd: &fs_node::DirRef) -> Option { + match self.get(cwd) { + Some(fs_node::FileOrDir::File(file)) => Some(file), _ => None, } } - /// Returns the file specified by the given path, which can be either absolute, - /// or relative from the given starting directory. + // TODO: Move out of path crate. + /// Returns the directory at the given path. + /// + /// The path can be relative or absolute. /// - /// If the path is invalid or points to a directory, then `None` is returned. - pub fn get_dir(&self, starting_dir: &DirRef) -> Option { - match self.get(starting_dir) { - Some(FileOrDir::Dir(dir)) => Some(dir), + /// If the path does not point to a directory, `None` is returned. + #[inline] + pub fn get_dir(&self, cwd: &fs_node::DirRef) -> Option { + match self.get(cwd) { + Some(fs_node::FileOrDir::Dir(dir)) => Some(dir), _ => None, } } - /// Returns the file or directory specified by the given absolute path - pub fn get_absolute(path: &Path) -> Option { + // TODO: Move out of path crate. + /// Returns the file or directory at the given absolute path. + /// + /// If the path does not point to a file system object or the path is + /// relative, `None` is returned. + #[inline] + pub fn get_absolute(path: &Path) -> Option { if path.is_absolute() { path.get(root::get_root()) } else { None } } + + /// Construct a relative path from a provided base directory path to the + /// provided path. + #[inline] + pub fn relative

(&self, base: P) -> Option + where + P: AsRef, + { + let base = base.as_ref(); + + if self.is_absolute() != base.is_absolute() { + if self.is_absolute() { + Some(self.to_owned()) + } else { + None + } + } else { + let mut ita = self.components(); + let mut itb = base.components(); + let mut comps: Vec = vec![]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + (None, _) => comps.push(Component::ParentDir), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), + (Some(_), Some(b)) if b == Component::ParentDir => return None, + (Some(a), Some(_)) => { + comps.push(Component::ParentDir); + for _ in itb { + comps.push(Component::ParentDir); + } + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + } + } + Some(comps.iter().map(|c| -> &Path { c.as_ref() }).collect()) + } + } + + /// Extracts the extension (without the leading dot) of [`self.file_name`], + /// if possible. + /// + /// The extension is: + /// + /// - [`None`], if there is no file name; + /// - [`None`], if there is no embedded `.`; + /// - [`None`], if the file name begins with `.` and has no other `.`s + /// within; + /// - Otherwise, the portion of the file name after the final `.` + /// + /// [`self.file_name`]: Path::file_name + /// + /// # Examples + /// + /// ``` + /// # use path::Path; + /// assert_eq!(None, Path::new("foo").extension()); + /// assert_eq!(None, Path::new(".foo").extension()); + /// assert_eq!("rs", Path::new("foo.rs").extension().unwrap()); + /// assert_eq!("gz", Path::new("foo.tar.gz").extension().unwrap()); + /// ``` + #[inline] + pub fn extension(&self) -> Option<&str> { + self.file_name() + .and_then(|file_name| file_name.rsplit_once('.')) + .and_then(|(before, after)| if before.is_empty() { None } else { Some(after) }) + } } -pub enum PathComponent { - RootDir, - ParentDir, - CurrentDir, +/// An owned, mutable path. +/// +/// This type is just a wrapper around a [`String`]. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PathBuf { + inner: String, +} + +impl AsRef for PathBuf { + #[inline] + fn as_ref(&self) -> &str { + AsRef::::as_ref(self).as_ref() + } +} + +impl AsRef for PathBuf { + #[inline] + fn as_ref(&self) -> &Path { + self.deref() + } +} + +impl Borrow for PathBuf { + #[inline] + fn borrow(&self) -> &Path { + self.deref() + } +} + +impl Default for PathBuf { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Deref for PathBuf { + type Target = Path; + + #[inline] + fn deref(&self) -> &Self::Target { + self.inner.deref().as_ref() + } +} + +impl DerefMut for PathBuf { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.deref_mut().as_mut() + } } -impl PathComponent { - pub fn as_string(self) -> String { - match self { - PathComponent::RootDir => String::from(root::ROOT_DIRECTORY_NAME), - PathComponent::CurrentDir => String::from("."), - PathComponent::ParentDir => String::from(".."), +impl Display for PathBuf { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } +} + +impl From for PathBuf { + #[inline] + fn from(value: String) -> Self { + Self { inner: value } + } +} + +impl From for String { + #[inline] + fn from(value: PathBuf) -> Self { + value.inner + } +} + +impl From<&T> for PathBuf +where + T: ?Sized + AsRef, +{ + fn from(value: &T) -> Self { + Self { + inner: value.as_ref().to_owned(), + } + } +} + +impl

FromIterator

for PathBuf +where + P: AsRef, +{ + #[inline] + fn from_iter(iter: T) -> Self + where + T: IntoIterator, + { + let mut inner = String::new(); + let mut iter = iter.into_iter().peekable(); + while let Some(path) = iter.next() { + inner.push_str(path.as_ref().as_ref()); + if iter.peek().is_some() { + inner.push('/'); + } + } + Self { inner } + } +} + +impl PathBuf { + /// Allocates an empty `PathBuf`. + #[inline] + pub fn new() -> Self { + Self { + inner: String::new(), + } + } + + /// Extends self with path. + /// + /// If path is absolute, it replaces the current path. + /// + /// # Examples + /// + /// Pushing a relative path extends the existing path: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("file.bk"); + /// assert_eq!(path, PathBuf::from("/tmp/file.bk")); + /// ``` + /// + /// Pushing an absolute path replaces the existing path: + /// + /// ``` + /// use std::path::PathBuf; + /// + /// let mut path = PathBuf::from("/tmp"); + /// path.push("/etc"); + /// assert_eq!(path, PathBuf::from("/etc")); + /// ``` + #[inline] + pub fn push

(&mut self, path: P) + where + P: AsRef, + { + if path.as_ref().is_absolute() { + *self = path.as_ref().to_owned(); + } else { + self.inner.push('/'); + self.inner.push_str(path.as_ref().as_ref()); } } -} \ No newline at end of file + + /// Truncates `self` to [`self.parent`]. + /// + /// Returns `false` and does nothing if [`self.parent`] is [`None`]. + /// Otherwise, returns `true`. + /// + /// [`self.parent`]: Path::parent + /// + /// # Examples + /// + /// ``` + /// use std::path::{Path, PathBuf}; + /// + /// let mut p = PathBuf::from("/spirited/away.rs"); + /// + /// p.pop(); + /// assert_eq!(Path::new("/spirited"), p); + /// p.pop(); + /// assert_eq!(Path::new("/"), p); + /// ``` + #[inline] + pub fn pop(&mut self) -> bool { + match self.parent().map(|p| p.inner.len()) { + Some(len) => { + self.inner.truncate(len); + true + } + None => false, + } + } +} diff --git a/kernel/spawn/src/lib.rs b/kernel/spawn/src/lib.rs index d0ff408b87..ba5ee60d77 100755 --- a/kernel/spawn/src/lib.rs +++ b/kernel/spawn/src/lib.rs @@ -33,7 +33,7 @@ use stack::Stack; use task::{Task, TaskRef, RestartInfo, RunState, JoinableTaskRef, ExitableTaskRef, FailureCleanupFunction}; use task_struct::ExposedTask; use mod_mgmt::{CrateNamespace, SectionType, SECTION_HASH_DELIMITER}; -use path::Path; +use path::{Path, PathBuf}; use fs_node::FileOrDir; use preemption::{hold_preemption, PreemptionGuard}; use no_drop::NoDrop; @@ -213,7 +213,7 @@ type MainFunc = fn(MainFuncArg) -> MainFuncRet; /// If not provided, the new Task will be spawned within the same namespace as the current task. /// pub fn new_application_task_builder( - crate_object_file: Path, // TODO FIXME: use `mod_mgmt::IntoCrateObjectFile`, + crate_object_file: &Path, // TODO FIXME: use `mod_mgmt::IntoCrateObjectFile`, new_namespace: Option>, ) -> Result, &'static str> { @@ -222,7 +222,7 @@ pub fn new_application_task_builder( .ok_or("spawn::new_application_task_builder(): couldn't get current task")?; let crate_object_file = match crate_object_file.get(namespace.dir()) - .or_else(|| Path::new(format!("{}.o", &crate_object_file)).get(namespace.dir())) // retry with ".o" extension + .or_else(|| PathBuf::from(format!("{}.o", &crate_object_file)).get(namespace.dir())) // retry with ".o" extension { Some(FileOrDir::File(f)) => f, _ => return Err("Couldn't find specified file path for new application crate"), diff --git a/kernel/task_fs/src/lib.rs b/kernel/task_fs/src/lib.rs index 80dcced539..2d76b06837 100644 --- a/kernel/task_fs/src/lib.rs +++ b/kernel/task_fs/src/lib.rs @@ -42,7 +42,7 @@ use alloc::sync::Arc; use fs_node::{DirRef, WeakDirRef, Directory, FileOrDir, File, FileRef, FsNode}; use memory::MappedPages; use task::WeakTaskRef; -use path::Path; +use path::{Path, PathBuf}; use io::{ByteReader, ByteWriter, KnownLength, IoError}; @@ -146,7 +146,7 @@ pub struct TaskDir { /// The name of the directory pub name: String, /// The absolute path of the TaskDir - path: Path, + path: PathBuf, task_id: usize, taskref: WeakTaskRef, /// We can store the parent (TaskFs) because it is a persistent directory @@ -163,7 +163,7 @@ impl TaskDir { ) -> Result { let directory = TaskDir { name, - path: Path::new(format!("{TASKS_DIRECTORY_PATH}/{task_id}")), + path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}")), task_id, taskref, parent: Arc::clone(parent), @@ -227,7 +227,7 @@ impl FsNode for TaskDir { pub struct TaskFile { taskref: WeakTaskRef, task_id: usize, - path: Path, + path: PathBuf, } impl TaskFile { @@ -235,7 +235,7 @@ impl TaskFile { TaskFile { taskref, task_id, - path: Path::new(format!("{TASKS_DIRECTORY_PATH}/{task_id}/task_info")), + path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}/task_info")), } } @@ -280,7 +280,7 @@ impl FsNode for TaskFile { } fn get_parent_dir(&self) -> Option { - let path = Path::new(format!("{}/{}", TASKS_DIRECTORY_PATH, self.task_id)); + let path = PathBuf::from(format!("{}/{}", TASKS_DIRECTORY_PATH, self.task_id)); match Path::get_absolute(&path) { Some(FileOrDir::Dir(d)) => Some(d), _ => None, @@ -333,7 +333,7 @@ impl File for TaskFile { pub struct MmiDir { taskref: WeakTaskRef, task_id: usize, - path: Path, + path: PathBuf, } impl MmiDir { @@ -342,7 +342,7 @@ impl MmiDir { MmiDir { taskref, task_id, - path: Path::new(format!("{TASKS_DIRECTORY_PATH}/{task_id}/mmi")), + path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}/mmi")), } } } @@ -383,7 +383,7 @@ impl FsNode for MmiDir { } fn get_parent_dir(&self) -> Option { - let path = Path::new(format!("{}/{}", TASKS_DIRECTORY_PATH, self.task_id)); + let path = PathBuf::from(format!("{}/{}", TASKS_DIRECTORY_PATH, self.task_id)); match Path::get_absolute(&path) { Some(FileOrDir::Dir(d)) => Some(d), _ => None, @@ -402,7 +402,7 @@ impl FsNode for MmiDir { pub struct MmiFile { taskref: WeakTaskRef, task_id: usize, - path: Path, + path: PathBuf, } impl MmiFile { @@ -410,7 +410,7 @@ impl MmiFile { MmiFile { taskref, task_id, - path: Path::new(format!("{TASKS_DIRECTORY_PATH}/{task_id}/mmi/MmiInfo")), + path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}/mmi/MmiInfo")), } } @@ -434,7 +434,7 @@ impl FsNode for MmiFile { } fn get_parent_dir(&self) -> Option { - let path = Path::new(format!("{}/{}/mmi", TASKS_DIRECTORY_PATH, self.task_id)); + let path = PathBuf::from(format!("{}/{}/mmi", TASKS_DIRECTORY_PATH, self.task_id)); match Path::get_absolute(&path) { Some(FileOrDir::Dir(d)) => Some(d), _ => None, diff --git a/kernel/wasi_interpreter/src/posix_file_system.rs b/kernel/wasi_interpreter/src/posix_file_system.rs index bcd1ea9829..8484e339b1 100644 --- a/kernel/wasi_interpreter/src/posix_file_system.rs +++ b/kernel/wasi_interpreter/src/posix_file_system.rs @@ -8,12 +8,11 @@ //! use alloc::string::String; -use alloc::vec::Vec; use core::{cmp, convert::TryFrom as _}; use fs_node::{DirRef, FileOrDir, FileRef, FsNode}; use hashbrown::HashMap; use memfs::MemFile; -use path::Path; +use path::{PathBuf, Path}; const FIRST_NONRESERVED_FD: wasi::Fd = 3; @@ -146,12 +145,12 @@ impl PosixNode { /// # Return /// Returns relative path of file descriptor as a string. pub fn get_relative_path(&self) -> String { - let absolute_path = Path::new(self.theseus_file_or_dir.get_absolute_path()); + let absolute_path = PathBuf::from(self.theseus_file_or_dir.get_absolute_path()); let wd_path = task::with_current_task(|t| - Path::new(t.get_env().lock().cwd()) + PathBuf::from(t.get_env().lock().cwd()) ).expect("couldn't get current task"); - let relative_path: Path = absolute_path.relative(&wd_path).unwrap(); + let relative_path = absolute_path.relative(wd_path).unwrap(); String::from(relative_path) } @@ -381,12 +380,10 @@ impl FileDescriptorTable { } // Split path into parent directory path and base path. - let file_path: Path = Path::new(String::from(path)); - let mut file_path_tokens: Vec<&str> = file_path.components().collect(); - file_path_tokens.truncate(file_path_tokens.len().saturating_sub(1)); - let parent_dir_path: Path = Path::new(file_path_tokens.join("/")); - let base_name: &str = file_path.basename(); - let base_path: Path = Path::new(String::from(base_name)); + let file_path: &Path = path.as_ref(); + let parent_dir_path = file_path.parent().ok_or(wasi::ERRNO_NOENT)?; + let base_name = file_path.file_name().ok_or(wasi::ERRNO_NOENT)?; + let base_path: &Path = base_name.as_ref(); // Get parent directory. let parent_dir: DirRef = match parent_dir_path.get(&starting_dir) { diff --git a/kernel/window_manager/src/lib.rs b/kernel/window_manager/src/lib.rs index 39d4ec8103..005627557b 100644 --- a/kernel/window_manager/src/lib.rs +++ b/kernel/window_manager/src/lib.rs @@ -31,18 +31,17 @@ extern crate color; use alloc::collections::VecDeque; use alloc::string::ToString; use alloc::sync::{Arc, Weak}; -use alloc::vec::{Vec}; +use alloc::vec::Vec; use compositor::{Compositor, FramebufferUpdates, CompositableRegion}; use mpmc::Queue; use event_types::{Event, MousePositionEvent}; use framebuffer::{Framebuffer, AlphaPixel}; -use color::{Color}; +use color::Color; use shapes::{Coord, Rectangle}; use framebuffer_compositor::{FRAME_COMPOSITOR}; use keycodes_ascii::{KeyAction, KeyEvent, Keycode}; use mouse_data::MouseEvent; -use path::Path; use spin::{Mutex, Once}; use window_inner::{WindowInner, WindowMovingStatus}; @@ -769,8 +768,8 @@ fn keyboard_handle_application(key_input: KeyEvent) -> Result<(), &'static str> let new_app_namespace = mod_mgmt::create_application_namespace(None)?; let shell_objfile = new_app_namespace.dir().get_file_starting_with("shell-") .ok_or("Couldn't find shell application file to run upon Ctrl+Alt+T")?; - let path = Path::new(shell_objfile.lock().get_absolute_path()); - spawn::new_application_task_builder(path, Some(new_app_namespace))? + let path = shell_objfile.lock().get_absolute_path(); + spawn::new_application_task_builder(path.as_ref(), Some(new_app_namespace))? .name("shell".to_string()) .spawn()?; diff --git a/ports/theseus_std/src/env.rs b/ports/theseus_std/src/env.rs index db9349ded6..11e7e546f8 100644 --- a/ports/theseus_std/src/env.rs +++ b/ports/theseus_std/src/env.rs @@ -12,7 +12,7 @@ pub fn current_dir() -> io::Result { ) .and_then(|task| match theseus_path::Path::get_absolute( - &task.get_env().lock().cwd().into() + &task.get_env().lock().cwd().as_ref() ) { Some(FileOrDir::File(_)) => Err(io::Error::new( io::ErrorKind::Other, diff --git a/ports/theseus_std/src/fs_imp.rs b/ports/theseus_std/src/fs_imp.rs index b61c575bbd..0d83aac78d 100644 --- a/ports/theseus_std/src/fs_imp.rs +++ b/ports/theseus_std/src/fs_imp.rs @@ -317,8 +317,11 @@ impl File { 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()); + let cow_file_path = path.to_string_lossy(); + let theseus_file_path: &theseus_path::Path = cow_file_path.as_ref().as_ref(); + + let cow_dir_path = parent_dir_of_file.to_string_lossy(); + let theseus_dir_path: &theseus_path::Path = cow_dir_path.as_ref().as_ref(); // `create_new` requires that the file must not previously exist at all. if opts.create_new && theseus_file_path.get(&curr_dir).is_some() { @@ -350,7 +353,7 @@ impl File { // 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) + theseus_path::Path::new(path.to_string_lossy().as_ref()).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()),