Skip to content
Open
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
1 change: 1 addition & 0 deletions library/std/src/os/xous/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub mod ffi;
#[stable(feature = "rust1", since = "1.0.0")]
pub mod services;

pub mod path;
/// A prelude for conveniently writing platform-specific code.
///
/// Includes all extension traits, and some important type definitions.
Expand Down
40 changes: 40 additions & 0 deletions library/std/src/os/xous/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#![stable(feature = "rust1", since = "1.0.0")]

use crate::path;

#[stable(feature = "file_type_ext", since = "1.5.0")]
pub trait PathExt {
/// Returns `true` if this file type is a basis.
///
/// # Examples
///
/// ```no_run
/// use std::fs;
/// use std::os::xous::fs::FileTypeExt;
/// use std::io;
///
/// fn main() -> io::Result<()> {
/// let meta = fs::metadata(":.System")?;
/// let file_type = meta.file_type();
/// assert!(file_type.is_basis());
/// Ok(())
/// }
/// ```
#[stable(feature = "file_type_ext", since = "1.5.0")]
fn is_basis(&self) -> bool;
}

#[stable(feature = "file_type_ext", since = "1.5.0")]
impl PathExt for path::Path {
fn is_basis(&self) -> bool {
let Some(as_str) = self.as_os_str().to_str() else {
return false;
};

let Ok(path) = crate::sys::path::split_basis_dict_key(as_str) else {
return false;
};

path.basis.is_some() && path.dict.is_none() && path.key.is_none()
}
}
6 changes: 0 additions & 6 deletions library/std/src/sys/pal/xous/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#![forbid(unsafe_op_in_unsafe_fn)]

use crate::os::xous::ffi::exit;

pub mod os;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
Expand All @@ -10,7 +8,3 @@ pub mod time;
#[path = "../unsupported/common.rs"]
mod common;
pub use common::*;

pub fn abort_internal() -> ! {
exit(101);
}
4 changes: 2 additions & 2 deletions library/std/src/sys/pal/xous/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub(crate) mod params;
static PARAMS_ADDRESS: Atomic<*mut u8> = AtomicPtr::new(core::ptr::null_mut());

#[cfg(not(test))]
#[cfg(feature = "panic_unwind")]
#[cfg(feature = "panic-unwind")]
mod eh_unwinding {
pub(crate) struct EhFrameFinder;
pub(crate) static mut EH_FRAME_ADDRESS: usize = 0;
Expand Down Expand Up @@ -45,7 +45,7 @@ mod c_compat {

#[unsafe(no_mangle)]
pub extern "C" fn _start(eh_frame: usize, params_address: usize) {
#[cfg(feature = "panic_unwind")]
#[cfg(feature = "panic-unwind")]
{
unsafe { super::eh_unwinding::EH_FRAME_ADDRESS = eh_frame };
unwind::set_custom_eh_frame_finder(&super::eh_unwinding::EH_FRAME_SETTINGS).ok();
Expand Down
4 changes: 4 additions & 0 deletions library/std/src/sys/path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ cfg_select! {
mod uefi;
pub use uefi::*;
}
target_os = "xous" => {
mod xous;
pub use xous::*;
}
target_os = "cygwin" => {
mod cygwin;
mod windows_prefix;
Expand Down
248 changes: 248 additions & 0 deletions library/std/src/sys/path/xous.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
use crate::ffi::OsStr;
use crate::io;
use crate::path::{Path, PathBuf, Prefix};

#[inline]
pub fn is_sep_byte(b: u8) -> bool {
b == b'/' || b == b':'
}

#[inline]
pub fn is_verbatim_sep(b: u8) -> bool {
b == b'/'
}

#[inline]
pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
None
}

pub const HAS_PREFIXES: bool = false;
pub const MAIN_SEP_STR: &str = "/";
pub const MAIN_SEP: char = '/';
pub const ALLOWED_SEP: &[char] = &['/', ':'];

/// Make a POSIX path absolute without changing its semantics.
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
Ok(path.to_owned())
}

pub(crate) fn is_absolute(path: &Path) -> bool {
path.has_root() && path.prefix().is_some()
}

#[derive(Debug, PartialEq)]
pub enum BasisSplitError {
/// This basis couldn't be split because it's the root element
Root,
/// This basis couldn't be split because it's listing all bases
All,
/// A path component was empty
EmptyComponent,
}

#[derive(Default, Debug, PartialEq)]
pub struct SplitPath<'a> {
pub basis: Option<&'a str>,
pub dict: Option<&'a str>,
pub key: Option<&'a str>,
}

/// Split a path into its constituent Basis, Dict, and Key, if the path is legal.
/// A Basis of `None` indicates the default Basis.
pub fn split_basis_dict_key<'a>(src: &'a str) -> Result<SplitPath<'a>, BasisSplitError> {
let mut current_string = src;
let mut path = SplitPath::default();

// Special case for root
if src == ":" || src == "/" {
return Err(BasisSplitError::Root);
}
// Special case for listing all bases
if src == "::" || src == "//" || src == "/:" || src == ":/" {
return Err(BasisSplitError::All);
}

// If the string is an absolute path, then the first component is a basis
if let Some(src) = src.strip_prefix(ALLOWED_SEP) {
// See if we can split off a second separator which will get us the end
// of the Basis name
if let Some((maybe_basis, remainder)) = src.split_once(ALLOWED_SEP) {
// If the basis is empty, pull it from the default Basis function.
// Otherwise, use the specified basis.
if !maybe_basis.is_empty() {
path.basis = Some(maybe_basis);
}
current_string = remainder;
}
}

// Now that we have a fully-specified Basis, pull out the rest of the string.
if let Some(components) = current_string.rsplit_once(ALLOWED_SEP) {
let (mut dict, key) = components;
if let Some(stripped_dict) = dict.strip_prefix(ALLOWED_SEP) {
dict = stripped_dict;
}
// Don't allow empty dicts
if components.0.is_empty() {
return Err(BasisSplitError::EmptyComponent);
}
for path_component in dict.split(ALLOWED_SEP) {
if path_component.is_empty() {
return Err(BasisSplitError::EmptyComponent);
}
}

path.dict = Some(dict);
// Don't allow empty keys
if !key.is_empty() {
path.key = Some(key);
}
} else if !current_string.is_empty() {
path.dict = Some(current_string)
}

Ok(path)
}

#[cfg(test)]
fn assert_basis_dict_key(test: &str, basis: Option<&str>, dict: Option<&str>, key: Option<&str>) {
let path = split_basis_dict_key(test).unwrap();
assert_eq!(path.basis, basis);
assert_eq!(path.dict, dict);
assert_eq!(path.key, key);
}

#[test]
fn basis() {
// A basis named "Home Wifi"
assert_basis_dict_key(":Home Wifi:", Some("Home Wifi"), None, None);

// :.System: -- A basis named ".System"
assert_basis_dict_key(":.System:", Some(".System"), None, None);
}

#[test]
fn dict() {
// wlan.networks -- A dict named "wlan.networks" in the default basis
assert_basis_dict_key("wlan.networks", None, Some("wlan.networks"), None);
}

#[test]
fn key() {
// wlan.networks/recent -- A dict named "wlan.networks/recent", which may be considered a path, in the default basis. This also describes a key called "recent" in the dict "wlan.networks", depending on whether
assert_basis_dict_key("wlan.networks/recent", None, Some("wlan.networks"), Some("recent"));
}

#[test]
fn key_legacy() {
// wlan.networks/recent -- A dict named "wlan.networks:recent", which may be considered a path, in the default basis. This also describes a key called "recent" in the dict "wlan.networks", depending on whether
assert_basis_dict_key("wlan.networks:recent", None, Some("wlan.networks"), Some("recent"));
}

#[test]
fn dict_in_key() {
// :.System:wlan.networks -- A dict named "wlan.networks" in the basis ".System"
assert_basis_dict_key(":.System:wlan.networks", Some(".System"), Some("wlan.networks"), None);
}

#[test]
fn basis_dict_key() {
// :.System:wlan.networks/recent -- a fully-qualified path, describing a key "recent" in the dict "wlan.networks" in the basis ".System".
assert_basis_dict_key(
":.System:wlan.networks/recent",
Some(".System"),
Some("wlan.networks"),
Some("recent"),
);
}

#[test]
fn basis_dict_key_legacy() {
// :.System:wlan.networks:recent -- a fully-qualified path, describing a key "recent" in the dict "wlan.networks" in the basis ".System".
assert_basis_dict_key(
":.System:wlan.networks:recent",
Some(".System"),
Some("wlan.networks"),
Some("recent"),
);
}

#[test]
fn root() {
// : -- The root, which lists every basis. Files cannot be created here. "Directories" can be created and destroyed, which corresponds to creating and destroying bases.
assert_eq!(split_basis_dict_key(":"), Err(BasisSplitError::Root));
assert_eq!(split_basis_dict_key("/"), Err(BasisSplitError::Root));
}

#[test]
fn all_bases() {
// :: -- An empty basis is a synonym for all bases, so this corresponds to listing all dicts in the root of the default basis.
assert_eq!(split_basis_dict_key("::"), Err(BasisSplitError::All));
assert_eq!(split_basis_dict_key("/:"), Err(BasisSplitError::All));
assert_eq!(split_basis_dict_key(":/"), Err(BasisSplitError::All));
assert_eq!(split_basis_dict_key("//"), Err(BasisSplitError::All));
}

#[test]
fn space_basis() {
// : : -- A basis named " ". Legal, but questionable
assert_basis_dict_key(": :", Some(" "), None, None);
}

#[test]
fn space_dict() {
// -- A dict named " " in the default basis. Legal, but questionable.
assert_basis_dict_key(" ", None, Some(" "), None);
}

#[test]
fn legacy_dict_key() {
// : -- A key named " " in a dict called " ". Legal.
assert_basis_dict_key(" : ", None, Some(" "), Some(" "));
}

#[test]
fn legacy_trailing_separator() {
// baz: -- A dict named "baz" in the default basis with an extra ":" following. Legal.
assert_basis_dict_key("baz:", None, Some("baz"), None);
}

#[test]
fn legacy_trailing_separator_two_dicts() {
// baz:foo: -- Currently illegal, but may become equal to baz:foo in the future.
assert_basis_dict_key("baz:foo:", None, Some("baz:foo"), None);
}

#[test]
fn empty_dict() {
// ::: -- An key named ":" in an empty dict in the default basis. Illegal.
assert_eq!(split_basis_dict_key(":::"), Err(BasisSplitError::EmptyComponent));
}

#[test]
fn empty_key_empty_dict() {
// :::: -- An key named "::" in an empty dict in the default basis. Illegal.
assert_eq!(split_basis_dict_key("::::"), Err(BasisSplitError::EmptyComponent));
}

#[test]
fn key_in_default_basis() {
// ::foo -- A dict "foo" in the default basis.
assert_basis_dict_key("::foo", None, Some("foo"), None);
}

#[test]
fn starts_with_slash() {
assert_basis_dict_key("/test/path", Some("test"), Some("path"), None);
}

#[test]
fn non_legacy_basis() {
assert_basis_dict_key(
"/.System/wlan.networks/recent",
Some(".System"),
Some("wlan.networks"),
Some("recent"),
);
}
2 changes: 1 addition & 1 deletion library/std/src/sys/thread/xous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ impl Thread {
// dealloc calls from being reordered to after the TLS has been destroyed.
// See https://github.com/rust-lang/rust/pull/144465#pullrequestreview-3289729950
// for more context.
run_main_thread_not_inlined(init);
rust_main_thread_not_inlined(init);

// Destroy TLS, which will free the TLS page and call the destructor for
// any thread local storage (if any).
Expand Down
Loading