From 86a2958aec88b8e9542097c857cad477710aaa5e Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 16 Mar 2018 01:29:09 +0100 Subject: [PATCH 01/33] Make alloc_system and libc optional --- src/libpanic_abort/Cargo.toml | 2 ++ src/libpanic_abort/lib.rs | 3 +++ src/libstd/Cargo.toml | 6 ++++-- src/libstd/lib.rs | 2 ++ src/libunwind/Cargo.toml | 2 ++ src/libunwind/lib.rs | 2 ++ 6 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libpanic_abort/Cargo.toml b/src/libpanic_abort/Cargo.toml index 633d273b3b93c..a33b66c46a765 100644 --- a/src/libpanic_abort/Cargo.toml +++ b/src/libpanic_abort/Cargo.toml @@ -11,5 +11,7 @@ doc = false [dependencies] core = { path = "../libcore" } + +[target.'cfg(not(target_os = "switch"))'.dependencies] libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index 392bf17968fbd..d9b010958e947 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -61,7 +61,10 @@ pub unsafe extern fn __rust_start_panic(_payload: usize) -> u32 { libc::abort(); } + // TODO: We don't want to use the abort intrinsic, as that will cause a + // 2168-0000 on the switch. Instead, we should "properly" exit. #[cfg(any(target_os = "redox", + target_os = "switch", windows, all(target_arch = "wasm32", not(target_os = "emscripten"))))] unsafe fn abort() -> ! { diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 5348c9a0f3498..86ef892804ba9 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -15,15 +15,17 @@ crate-type = ["dylib", "rlib"] [dependencies] alloc = { path = "../liballoc" } alloc_jemalloc = { path = "../liballoc_jemalloc", optional = true } -alloc_system = { path = "../liballoc_system" } panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } core = { path = "../libcore" } -libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } unwind = { path = "../libunwind" } +[target.'cfg(not(target_os = "switch"))'.dependencies] +libc = { path = "../rustc/libc_shim" } +alloc_system = { path = "../liballoc_system" } + [dev-dependencies] rand = "0.4" diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3c01de2e997c9..9e474dd061e7d 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -357,7 +357,9 @@ pub use core::{unreachable, unimplemented, write, writeln, try}; #[allow(unused_imports)] // macros from `alloc` are not used on all platforms #[macro_use] extern crate alloc as alloc_crate; +#[cfg(not(target_os = "switch"))] extern crate alloc_system; +#[cfg(not(target_os = "switch"))] #[doc(masked)] extern crate libc; diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml index 4760461df64e3..d30e84ed6bc81 100644 --- a/src/libunwind/Cargo.toml +++ b/src/libunwind/Cargo.toml @@ -13,5 +13,7 @@ doc = false [dependencies] core = { path = "../libcore" } + +[target.'cfg(not(target_os = "switch"))'.dependencies] libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs index 2b3c19c067ed4..7631fd1871464 100644 --- a/src/libunwind/lib.rs +++ b/src/libunwind/lib.rs @@ -27,6 +27,8 @@ cfg_if! { // no extra unwinder support needed } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { // no unwinder on the system! + } else if #[cfg(target_os = "switch")] { + // no unwinder support for now } else { extern crate libc; mod libunwind; From 7feb3525852c5fe8ac7408620c6774ee0c7aa8d8 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 16 Mar 2018 15:27:04 +0100 Subject: [PATCH 02/33] Don't use dependencies. We're going to be using xargo's multi-stage thing instead --- src/liballoc/Cargo.toml | 6 +++--- src/libpanic_abort/Cargo.toml | 4 ++-- src/libstd/Cargo.toml | 6 +++--- src/libunwind/Cargo.toml | 4 ++-- src/rustc/compiler_builtins_shim/Cargo.toml | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index ada21e04b306c..ac2b1a321476c 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -9,9 +9,9 @@ autobenches = false name = "alloc" path = "lib.rs" -[dependencies] -core = { path = "../libcore" } -compiler_builtins = { path = "../rustc/compiler_builtins_shim" } +#[dependencies] +#core = { path = "../libcore" } +#compiler_builtins = { path = "../rustc/compiler_builtins_shim" } [dev-dependencies] rand = "0.4" diff --git a/src/libpanic_abort/Cargo.toml b/src/libpanic_abort/Cargo.toml index a33b66c46a765..46095d0155380 100644 --- a/src/libpanic_abort/Cargo.toml +++ b/src/libpanic_abort/Cargo.toml @@ -9,8 +9,8 @@ test = false bench = false doc = false -[dependencies] -core = { path = "../libcore" } +#[dependencies] +#core = { path = "../libcore" } [target.'cfg(not(target_os = "switch"))'.dependencies] libc = { path = "../rustc/libc_shim" } diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 86ef892804ba9..a58ea413b8707 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -13,12 +13,12 @@ path = "lib.rs" crate-type = ["dylib", "rlib"] [dependencies] -alloc = { path = "../liballoc" } +#alloc = { path = "../liballoc" } alloc_jemalloc = { path = "../liballoc_jemalloc", optional = true } panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } -core = { path = "../libcore" } -compiler_builtins = { path = "../rustc/compiler_builtins_shim" } +#core = { path = "../libcore" } +#compiler_builtins = { path = "../rustc/compiler_builtins_shim" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } unwind = { path = "../libunwind" } diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml index d30e84ed6bc81..9cdf7b9b635aa 100644 --- a/src/libunwind/Cargo.toml +++ b/src/libunwind/Cargo.toml @@ -11,8 +11,8 @@ test = false bench = false doc = false -[dependencies] -core = { path = "../libcore" } +#[dependencies] +#core = { path = "../libcore" } [target.'cfg(not(target_os = "switch"))'.dependencies] libc = { path = "../rustc/libc_shim" } diff --git a/src/rustc/compiler_builtins_shim/Cargo.toml b/src/rustc/compiler_builtins_shim/Cargo.toml index 7d8423ca84eb4..c54ffe156954a 100644 --- a/src/rustc/compiler_builtins_shim/Cargo.toml +++ b/src/rustc/compiler_builtins_shim/Cargo.toml @@ -27,7 +27,7 @@ doctest = false # expected success, got: exit code: 101', src/bootstrap/compile.rs:883:8 # # See https://github.com/rust-lang/rfcs/pull/1133. -core = { path = "../../libcore" } +#core = { path = "../../libcore" } [build-dependencies] cc = "1.0.1" From ac6ba789e59722a2a235a7b5923e73925108a12a Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 16 Mar 2018 15:30:37 +0100 Subject: [PATCH 03/33] Add stubbed switch target --- src/libstd/Cargo.toml | 3 + src/libstd/lib.rs | 9 +- src/libstd/sys/mod.rs | 3 + src/libstd/sys/switch/args.rs | 56 ++++ src/libstd/sys/switch/cmath.rs | 115 ++++++++ src/libstd/sys/switch/condvar.rs | 51 ++++ src/libstd/sys/switch/env.rs | 19 ++ src/libstd/sys/switch/fs.rs | 304 +++++++++++++++++++++ src/libstd/sys/switch/memchr.rs | 14 + src/libstd/sys/switch/mod.rs | 88 +++++++ src/libstd/sys/switch/mutex.rs | 96 +++++++ src/libstd/sys/switch/net.rs | 336 ++++++++++++++++++++++++ src/libstd/sys/switch/os.rs | 116 ++++++++ src/libstd/sys/switch/os_str.rs | 184 +++++++++++++ src/libstd/sys/switch/path.rs | 29 ++ src/libstd/sys/switch/pipe.rs | 35 +++ src/libstd/sys/switch/process.rs | 150 +++++++++++ src/libstd/sys/switch/rwlock.rs | 61 +++++ src/libstd/sys/switch/stack_overflow.rs | 23 ++ src/libstd/sys/switch/stdio.rs | 69 +++++ src/libstd/sys/switch/thread.rs | 49 ++++ src/libstd/sys/switch/thread_local.rs | 71 +++++ src/libstd/sys/switch/time.rs | 56 ++++ src/libstd/sys_common/mod.rs | 2 +- 24 files changed, 1937 insertions(+), 2 deletions(-) create mode 100644 src/libstd/sys/switch/args.rs create mode 100644 src/libstd/sys/switch/cmath.rs create mode 100644 src/libstd/sys/switch/condvar.rs create mode 100644 src/libstd/sys/switch/env.rs create mode 100644 src/libstd/sys/switch/fs.rs create mode 100644 src/libstd/sys/switch/memchr.rs create mode 100644 src/libstd/sys/switch/mod.rs create mode 100644 src/libstd/sys/switch/mutex.rs create mode 100644 src/libstd/sys/switch/net.rs create mode 100644 src/libstd/sys/switch/os.rs create mode 100644 src/libstd/sys/switch/os_str.rs create mode 100644 src/libstd/sys/switch/path.rs create mode 100644 src/libstd/sys/switch/pipe.rs create mode 100644 src/libstd/sys/switch/process.rs create mode 100644 src/libstd/sys/switch/rwlock.rs create mode 100644 src/libstd/sys/switch/stack_overflow.rs create mode 100644 src/libstd/sys/switch/stdio.rs create mode 100644 src/libstd/sys/switch/thread.rs create mode 100644 src/libstd/sys/switch/thread_local.rs create mode 100644 src/libstd/sys/switch/time.rs diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index a58ea413b8707..a31494f48b9e9 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -26,6 +26,9 @@ unwind = { path = "../libunwind" } libc = { path = "../rustc/libc_shim" } alloc_system = { path = "../liballoc_system" } +[target.'cfg(target_os = "switch")'.dependencies] +ralloc = { git = "https://github.com/megatonhammer/ralloc" } + [dev-dependencies] rand = "0.4" diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9e474dd061e7d..e89b6b5ddc386 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -333,11 +333,15 @@ // `force_alloc_system` is *only* intended as a workaround for local rebuilds // with a rustc without jemalloc. // FIXME(#44236) shouldn't need MSVC logic -#[cfg(all(not(target_env = "msvc"), +#[cfg(all(not(target_env = "msvc"), not(target_os = "switch"), any(all(stage0, not(test)), feature = "force_alloc_system")))] #[global_allocator] static ALLOC: alloc_system::System = alloc_system::System; +#[cfg(target_os = "switch")] +#[global_allocator] +static ALLOC: ralloc::Allocator = ralloc::Allocator; + // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. #[prelude_import] @@ -359,6 +363,9 @@ pub use core::{unreachable, unimplemented, write, writeln, try}; extern crate alloc as alloc_crate; #[cfg(not(target_os = "switch"))] extern crate alloc_system; +#[cfg(target_os = "switch")] +extern crate ralloc; + #[cfg(not(target_os = "switch"))] #[doc(masked)] extern crate libc; diff --git a/src/libstd/sys/mod.rs b/src/libstd/sys/mod.rs index c44db3b107224..dfba50771acfd 100644 --- a/src/libstd/sys/mod.rs +++ b/src/libstd/sys/mod.rs @@ -45,6 +45,9 @@ cfg_if! { } else if #[cfg(target_os = "redox")] { mod redox; pub use self::redox::*; + } else if #[cfg(target_os = "switch")] { + mod switch; + pub use self::switch::*; } else if #[cfg(target_arch = "wasm32")] { mod wasm; pub use self::wasm::*; diff --git a/src/libstd/sys/switch/args.rs b/src/libstd/sys/switch/args.rs new file mode 100644 index 0000000000000..124cc500b67b5 --- /dev/null +++ b/src/libstd/sys/switch/args.rs @@ -0,0 +1,56 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Global initialization and retrieval of command line arguments. +//! +//! On some platforms these are stored during runtime startup, +//! and on some they are retrieved from the system on demand. +//! +//! In our case, they're in the megaton_hammer Loader. But I don't need them +//! yet. + +#![allow(dead_code)] // runtime init functions not used during testing + +use ffi::OsString; + +/// One-time global initialization. +pub unsafe fn init(_argc: isize, _argv: *const *const u8) { } + +/// One-time global cleanup. +pub unsafe fn cleanup() { } + +/// Returns the command line arguments +pub fn args() -> Args { + Args { empty_array: [] } +} + +pub struct Args { + empty_array: [OsString; 0], +} + +impl Args { + pub fn inner_debug(&self) -> &[OsString] { + &self.empty_array + } +} + +impl Iterator for Args { + type Item = OsString; + fn next(&mut self) -> Option { None } + fn size_hint(&self) -> (usize, Option) { (0, Some(0)) } +} + +impl ExactSizeIterator for Args { + fn len(&self) -> usize { 0 } +} + +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option { None } +} diff --git a/src/libstd/sys/switch/cmath.rs b/src/libstd/sys/switch/cmath.rs new file mode 100644 index 0000000000000..c6b7b96ab613a --- /dev/null +++ b/src/libstd/sys/switch/cmath.rs @@ -0,0 +1,115 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(test))] + +pub unsafe fn acos(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn acosf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn asin(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn asinf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn atan(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn atan2(_a: f64, _b: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn atan2f(_a: f32, _b: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn atanf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn cbrt(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn cbrtf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn cosh(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn coshf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn expm1(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn expm1f(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn fdim(_a: f64, _b: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn fdimf(_a: f32, _b: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn hypot(_x: f64, _y: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn hypotf(_x: f32, _y: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn log1p(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn log1pf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn sinh(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn sinhf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn tan(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn tanf(_n: f32) -> f32 { + unimplemented!(); +} + +pub unsafe fn tanh(_n: f64) -> f64 { + unimplemented!(); +} + +pub unsafe fn tanhf(_n: f32) -> f32 { + unimplemented!(); +} diff --git a/src/libstd/sys/switch/condvar.rs b/src/libstd/sys/switch/condvar.rs new file mode 100644 index 0000000000000..d5c80c4375ae1 --- /dev/null +++ b/src/libstd/sys/switch/condvar.rs @@ -0,0 +1,51 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use sys::mutex::Mutex; +use time::Duration; + +pub struct Condvar; + +impl Condvar { + pub const fn new() -> Condvar { + // TODO: Sadly can't panic in const fn yet. + Condvar + } + + #[inline] + pub unsafe fn init(&self) { + unimplemented!() + } + + #[inline] + pub fn notify_one(&self) { + unimplemented!() + } + + #[inline] + pub fn notify_all(&self) { + unimplemented!() + } + + #[inline] + pub fn wait(&self, _mutex: &Mutex) { + unimplemented!() + } + + #[inline] + pub fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + unimplemented!(); + } + + #[inline] + pub unsafe fn destroy(&self) { + unimplemented!() + } +} diff --git a/src/libstd/sys/switch/env.rs b/src/libstd/sys/switch/env.rs new file mode 100644 index 0000000000000..21485f7f68e20 --- /dev/null +++ b/src/libstd/sys/switch/env.rs @@ -0,0 +1,19 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub mod os { + pub const FAMILY: &'static str = "switch"; + pub const OS: &'static str = "switch"; + pub const DLL_PREFIX: &'static str = ""; + pub const DLL_SUFFIX: &'static str = ".nso"; + pub const DLL_EXTENSION: &'static str = "nso"; + pub const EXE_SUFFIX: &'static str = ".nso"; + pub const EXE_EXTENSION: &'static str = "nso"; +} diff --git a/src/libstd/sys/switch/fs.rs b/src/libstd/sys/switch/fs.rs new file mode 100644 index 0000000000000..f679355dbee75 --- /dev/null +++ b/src/libstd/sys/switch/fs.rs @@ -0,0 +1,304 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsString; +use fmt; +use hash::{Hash, Hasher}; +use io::{self, SeekFrom}; +use path::{Path, PathBuf}; +use sys::time::SystemTime; +use sys::{unsupported, Void}; + +pub struct File(Void); + +pub struct FileAttr(Void); + +pub struct ReadDir(Void); + +pub struct DirEntry(Void); + +#[derive(Clone, Debug)] +pub struct OpenOptions { } + +pub struct FilePermissions(Void); + +pub struct FileType(Void); + +#[derive(Debug)] +pub struct DirBuilder { } + +impl FileAttr { + pub fn size(&self) -> u64 { + match self.0 {} + } + + pub fn perm(&self) -> FilePermissions { + match self.0 {} + } + + pub fn file_type(&self) -> FileType { + match self.0 {} + } + + pub fn modified(&self) -> io::Result { + match self.0 {} + } + + pub fn accessed(&self) -> io::Result { + match self.0 {} + } + + pub fn created(&self) -> io::Result { + match self.0 {} + } +} + +impl Clone for FileAttr { + fn clone(&self) -> FileAttr { + match self.0 {} + } +} + +impl FilePermissions { + pub fn readonly(&self) -> bool { + match self.0 {} + } + + pub fn set_readonly(&mut self, _readonly: bool) { + match self.0 {} + } +} + +impl Clone for FilePermissions { + fn clone(&self) -> FilePermissions { + match self.0 {} + } +} + +impl PartialEq for FilePermissions { + fn eq(&self, _other: &FilePermissions) -> bool { + match self.0 {} + } +} + +impl Eq for FilePermissions { +} + +impl fmt::Debug for FilePermissions { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl FileType { + pub fn is_dir(&self) -> bool { + match self.0 {} + } + + pub fn is_file(&self) -> bool { + match self.0 {} + } + + pub fn is_symlink(&self) -> bool { + match self.0 {} + } +} + +impl Clone for FileType { + fn clone(&self) -> FileType { + match self.0 {} + } +} + +impl Copy for FileType {} + +impl PartialEq for FileType { + fn eq(&self, _other: &FileType) -> bool { + match self.0 {} + } +} + +impl Eq for FileType { +} + +impl Hash for FileType { + fn hash(&self, _h: &mut H) { + match self.0 {} + } +} + +impl fmt::Debug for FileType { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Debug for ReadDir { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl Iterator for ReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + match self.0 {} + } +} + +impl DirEntry { + pub fn path(&self) -> PathBuf { + match self.0 {} + } + + pub fn file_name(&self) -> OsString { + match self.0 {} + } + + pub fn metadata(&self) -> io::Result { + match self.0 {} + } + + pub fn file_type(&self) -> io::Result { + match self.0 {} + } +} + +impl OpenOptions { + pub fn new() -> OpenOptions { + OpenOptions { } + } + + pub fn read(&mut self, _read: bool) { } + pub fn write(&mut self, _write: bool) { } + pub fn append(&mut self, _append: bool) { } + pub fn truncate(&mut self, _truncate: bool) { } + pub fn create(&mut self, _create: bool) { } + pub fn create_new(&mut self, _create_new: bool) { } +} + +impl File { + pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { + unsupported() + } + + pub fn file_attr(&self) -> io::Result { + match self.0 {} + } + + pub fn fsync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn datasync(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn truncate(&self, _size: u64) -> io::Result<()> { + match self.0 {} + } + + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn flush(&self) -> io::Result<()> { + match self.0 {} + } + + pub fn seek(&self, _pos: SeekFrom) -> io::Result { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +impl DirBuilder { + pub fn new() -> DirBuilder { + DirBuilder { } + } + + pub fn mkdir(&self, _p: &Path) -> io::Result<()> { + unsupported() + } +} + +impl fmt::Debug for File { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub fn readdir(_p: &Path) -> io::Result { + unsupported() +} + +pub fn unlink(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { + unsupported() +} + +pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { + match perm.0 {} +} + +pub fn rmdir(_p: &Path) -> io::Result<()> { + unsupported() +} + +pub fn remove_dir_all(_path: &Path) -> io::Result<()> { + unsupported() +} + +pub fn readlink(_p: &Path) -> io::Result { + unsupported() +} + +pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { + unsupported() +} + +pub fn stat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn lstat(_p: &Path) -> io::Result { + unsupported() +} + +pub fn canonicalize(_p: &Path) -> io::Result { + unsupported() +} + +pub fn copy(_from: &Path, _to: &Path) -> io::Result { + unsupported() +} diff --git a/src/libstd/sys/switch/memchr.rs b/src/libstd/sys/switch/memchr.rs new file mode 100644 index 0000000000000..873b33535025b --- /dev/null +++ b/src/libstd/sys/switch/memchr.rs @@ -0,0 +1,14 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +// +// Original implementation taken from rust-memchr +// Copyright 2015 Andrew Gallant, bluss and Nicolas Koch + +pub use core::slice::memchr::{memchr, memrchr}; diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs new file mode 100644 index 0000000000000..a3ff3f89b8e90 --- /dev/null +++ b/src/libstd/sys/switch/mod.rs @@ -0,0 +1,88 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! System bindings for the Nintendo Switch platform +//! +//! This module contains the facade (aka platform-specific) implementations of +//! OS level functionality for the Nintendo Switch. +//! +//! Currently all functions here are basically stubs that immediately return +//! errors. + +#![allow(dead_code, missing_docs, bad_style)] + +use io::{self, ErrorKind}; + +pub mod args; +// TODO +#[cfg(feature = "backtrace")] +pub mod backtrace; +pub mod cmath; +pub mod condvar; +pub mod env; +pub mod fs; +pub mod memchr; +pub mod mutex; +pub mod net; +pub mod os; +pub mod os_str; +pub mod path; +pub mod pipe; +pub mod process; +pub mod rwlock; +pub mod stack_overflow; +pub mod stdio; +pub mod thread; +pub mod thread_local; +pub mod time; + +#[cfg(not(test))] +pub fn init() {} + +pub fn unsupported() -> io::Result { + Err(unsupported_err()) +} + +pub fn unsupported_err() -> io::Error { + io::Error::new(io::ErrorKind::Other, + "operation not supported on wasm yet") +} + +pub fn decode_error_kind(_errno: i32) -> ErrorKind { + io::ErrorKind::Other +} + +/// TODO: Do a proper abort using the exit stuff. +pub unsafe fn abort_internal() -> ! { + ::core::intrinsics::abort(); +} + +// This enum is used as the storage for a bunch of types which can't actually +// exist. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub enum Void {} + +pub unsafe fn strlen(mut s: *const i8) -> usize { + let mut n = 0; + while *s != 0 { + n += 1; + s = s.offset(1); + } + return n +} + +// We don't have randomness yet, but I totally used a random number generator to +// generate these numbers. +// +// More seriously though this is just for DOS protection in hash maps. It's ok +// if we don't do that on wasm just yet. +pub fn hashmap_random_keys() -> (u64, u64) { + (1, 2) +} diff --git a/src/libstd/sys/switch/mutex.rs b/src/libstd/sys/switch/mutex.rs new file mode 100644 index 0000000000000..349377133f2bd --- /dev/null +++ b/src/libstd/sys/switch/mutex.rs @@ -0,0 +1,96 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub unsafe fn mutex_try_lock(_m: *mut i32) -> bool { + unimplemented!() +} + +pub unsafe fn mutex_lock(_m: *mut i32) { + unimplemented!() +} + +pub unsafe fn mutex_unlock(_m: *mut i32) { + unimplemented!() +} + +pub struct Mutex; + +impl Mutex { + /// Create a new mutex. + pub const fn new() -> Self { + // TODO: Sadly can't panic in constfn yet + Mutex + } + + #[inline] + pub unsafe fn init(&self) { + unimplemented!() + } + + /// Try to lock the mutex + #[inline] + pub unsafe fn try_lock(&self) -> bool { + unimplemented!() + } + + /// Lock the mutex + #[inline] + pub unsafe fn lock(&self) { + unimplemented!() + } + + /// Unlock the mutex + #[inline] + pub unsafe fn unlock(&self) { + unimplemented!() + } + + #[inline] + pub unsafe fn destroy(&self) { + unimplemented!() + } +} + +pub struct ReentrantMutex; + +impl ReentrantMutex { + pub const fn uninitialized() -> Self { + // TODO: Sadly can't panic in constfn yet + ReentrantMutex + } + + #[inline] + pub unsafe fn init(&mut self) { + unimplemented!() + } + + /// Try to lock the mutex + #[inline] + pub unsafe fn try_lock(&self) -> bool { + unimplemented!() + } + + /// Lock the mutex + #[inline] + pub unsafe fn lock(&self) { + unimplemented!() + } + + /// Unlock the mutex + #[inline] + pub unsafe fn unlock(&self) { + unimplemented!() + } + + #[inline] + pub unsafe fn destroy(&self) { + unimplemented!() + } +} diff --git a/src/libstd/sys/switch/net.rs b/src/libstd/sys/switch/net.rs new file mode 100644 index 0000000000000..e0c1e9e9ac793 --- /dev/null +++ b/src/libstd/sys/switch/net.rs @@ -0,0 +1,336 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use fmt; +use io; +use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; +use time::Duration; +use sys::{unsupported, Void}; + +pub struct TcpStream(Void); + +impl TcpStream { pub fn connect(_: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + unsupported() + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn read_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn write_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn read(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn peer_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn socket_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_nodelay(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn nodelay(&self) -> io::Result { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for TcpStream { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct TcpListener(Void); + +impl TcpListener { + pub fn bind(_: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result { + match self.0 {} + } + + pub fn set_only_v6(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn only_v6(&self) -> io::Result { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for TcpListener { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct UdpSocket(Void); + +impl UdpSocket { + pub fn bind(_: &SocketAddr) -> io::Result { + unsupported() + } + + pub fn socket_addr(&self) -> io::Result { + match self.0 {} + } + + pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + match self.0 {} + } + + pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result { + match self.0 {} + } + + pub fn duplicate(&self) -> io::Result { + match self.0 {} + } + + pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { + match self.0 {} + } + + pub fn read_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn write_timeout(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_broadcast(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn broadcast(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v4(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_ttl_v4(&self) -> io::Result { + match self.0 {} + } + + pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn multicast_loop_v6(&self) -> io::Result { + match self.0 {} + } + + pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) + -> io::Result<()> { + match self.0 {} + } + + pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) + -> io::Result<()> { + match self.0 {} + } + + pub fn set_ttl(&self, _: u32) -> io::Result<()> { + match self.0 {} + } + + pub fn ttl(&self) -> io::Result { + match self.0 {} + } + + pub fn take_error(&self) -> io::Result> { + match self.0 {} + } + + pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { + match self.0 {} + } + + pub fn recv(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn peek(&self, _: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn send(&self, _: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn connect(&self, _: &SocketAddr) -> io::Result<()> { + match self.0 {} + } +} + +impl fmt::Debug for UdpSocket { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct LookupHost(Void); + +impl Iterator for LookupHost { + type Item = SocketAddr; + fn next(&mut self) -> Option { + match self.0 {} + } +} + +pub fn lookup_host(_: &str) -> io::Result { + unsupported() +} + +#[allow(bad_style)] +pub mod netc { + pub const AF_INET: u8 = 0; + pub const AF_INET6: u8 = 1; + pub type sa_family_t = u8; + + #[derive(Copy, Clone)] + pub struct in_addr { + pub s_addr: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in { + pub sin_family: sa_family_t, + pub sin_port: u16, + pub sin_addr: in_addr, + } + + #[derive(Copy, Clone)] + pub struct in6_addr { + pub s6_addr: [u8; 16], + } + + #[derive(Copy, Clone)] + pub struct sockaddr_in6 { + pub sin6_family: sa_family_t, + pub sin6_port: u16, + pub sin6_addr: in6_addr, + pub sin6_flowinfo: u32, + pub sin6_scope_id: u32, + } + + #[derive(Copy, Clone)] + pub struct sockaddr { + } + + pub type socklen_t = usize; +} diff --git a/src/libstd/sys/switch/os.rs b/src/libstd/sys/switch/os.rs new file mode 100644 index 0000000000000..1c41e34e52119 --- /dev/null +++ b/src/libstd/sys/switch/os.rs @@ -0,0 +1,116 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Implementation of `std::os` functionality for unix systems + +#![allow(unused_imports)] // lots of cfg code here + +use error::Error as StdError; +use ffi::{OsString, OsStr}; +use fmt; +use io; +use path::{self, PathBuf}; +use sys::unsupported; + +/// Returns the platform-specific value of errno +pub fn errno() -> i32 { + // There's never an error... + 0 +} + +/// Gets a detailed string description for the given error number. +pub fn error_string(_errno: i32) -> String { + "unknown error".to_string() +} + +pub fn getcwd() -> io::Result { + unsupported() +} + +pub fn chdir(_p: &path::Path) -> io::Result<()> { + unsupported() +} + +pub struct SplitPaths<'a>(&'a !); + +pub fn split_paths(_unparsed: &OsStr) -> SplitPaths { + unimplemented!() +} + +impl<'a> Iterator for SplitPaths<'a> { + type Item = PathBuf; + fn next(&mut self) -> Option { match self.0 {} } + fn size_hint(&self) -> (usize, Option) { match self.0 {} } +} + +#[derive(Debug)] +pub struct JoinPathsError; + +pub fn join_paths(_paths: I) -> Result + where I: Iterator, T: AsRef +{ + Err(JoinPathsError) +} + +impl fmt::Display for JoinPathsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + "not supported on the switch yet".fmt(f) + } +} + +impl StdError for JoinPathsError { + fn description(&self) -> &str { "not supported on the switch yet" } +} + +pub fn current_exe() -> io::Result { + unsupported() +} + +pub struct Env; + +impl Iterator for Env { + type Item = (OsString, OsString); + fn next(&mut self) -> Option<(OsString, OsString)> { None } + fn size_hint(&self) -> (usize, Option) { (0, None) } +} + +/// Returns a vector of (variable, value) byte-vector pairs for all the +/// environment variables of the current process. +pub fn env() -> Env { + unimplemented!() +} + +pub fn getenv(_key: &OsStr) -> io::Result> { + unsupported() +} + +pub fn setenv(_key: &OsStr, _value: &OsStr) -> io::Result<()> { + unsupported() +} + +pub fn unsetenv(_key: &OsStr) -> io::Result<()> { + unsupported() +} + +pub fn temp_dir() -> PathBuf { + unimplemented!() +} + +pub fn home_dir() -> Option { + None +} + +pub fn exit(_code: i32) -> ! { + loop {} +} + +pub fn getpid() -> u32 { + unimplemented!() +} diff --git a/src/libstd/sys/switch/os_str.rs b/src/libstd/sys/switch/os_str.rs new file mode 100644 index 0000000000000..655bfdb916707 --- /dev/null +++ b/src/libstd/sys/switch/os_str.rs @@ -0,0 +1,184 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// The underlying OsString/OsStr implementation on Unix systems: just +/// a `Vec`/`[u8]`. + +use borrow::Cow; +use fmt; +use str; +use mem; +use rc::Rc; +use sync::Arc; +use sys_common::{AsInner, IntoInner}; +use sys_common::bytestring::debug_fmt_bytestring; +use std_unicode::lossy::Utf8Lossy; + +#[derive(Clone, Hash)] +pub struct Buf { + pub inner: Vec +} + +pub struct Slice { + pub inner: [u8] +} + +impl fmt::Debug for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + debug_fmt_bytestring(&self.inner, formatter) + } +} + +impl fmt::Display for Slice { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&Utf8Lossy::from_bytes(&self.inner), formatter) + } +} + +impl fmt::Debug for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(self.as_slice(), formatter) + } +} + +impl fmt::Display for Buf { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self.as_slice(), formatter) + } +} + +impl IntoInner> for Buf { + fn into_inner(self) -> Vec { + self.inner + } +} + +impl AsInner<[u8]> for Buf { + fn as_inner(&self) -> &[u8] { + &self.inner + } +} + + +impl Buf { + pub fn from_string(s: String) -> Buf { + Buf { inner: s.into_bytes() } + } + + #[inline] + pub fn with_capacity(capacity: usize) -> Buf { + Buf { + inner: Vec::with_capacity(capacity) + } + } + + #[inline] + pub fn clear(&mut self) { + self.inner.clear() + } + + #[inline] + pub fn capacity(&self) -> usize { + self.inner.capacity() + } + + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.inner.reserve(additional) + } + + #[inline] + pub fn reserve_exact(&mut self, additional: usize) { + self.inner.reserve_exact(additional) + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.inner.shrink_to_fit() + } + + pub fn as_slice(&self) -> &Slice { + unsafe { mem::transmute(&*self.inner) } + } + + pub fn into_string(self) -> Result { + String::from_utf8(self.inner).map_err(|p| Buf { inner: p.into_bytes() } ) + } + + pub fn push_slice(&mut self, s: &Slice) { + self.inner.extend_from_slice(&s.inner) + } + + #[inline] + pub fn into_box(self) -> Box { + unsafe { mem::transmute(self.inner.into_boxed_slice()) } + } + + #[inline] + pub fn from_box(boxed: Box) -> Buf { + let inner: Box<[u8]> = unsafe { mem::transmute(boxed) }; + Buf { inner: inner.into_vec() } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + self.as_slice().into_arc() + } + + #[inline] + pub fn into_rc(&self) -> Rc { + self.as_slice().into_rc() + } +} + +impl Slice { + fn from_u8_slice(s: &[u8]) -> &Slice { + unsafe { mem::transmute(s) } + } + + pub fn from_str(s: &str) -> &Slice { + Slice::from_u8_slice(s.as_bytes()) + } + + pub fn to_str(&self) -> Option<&str> { + str::from_utf8(&self.inner).ok() + } + + pub fn to_string_lossy(&self) -> Cow { + String::from_utf8_lossy(&self.inner) + } + + pub fn to_owned(&self) -> Buf { + Buf { inner: self.inner.to_vec() } + } + + #[inline] + pub fn into_box(&self) -> Box { + let boxed: Box<[u8]> = self.inner.into(); + unsafe { mem::transmute(boxed) } + } + + pub fn empty_box() -> Box { + let boxed: Box<[u8]> = Default::default(); + unsafe { mem::transmute(boxed) } + } + + #[inline] + pub fn into_arc(&self) -> Arc { + let arc: Arc<[u8]> = Arc::from(&self.inner); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const Slice) } + } + + #[inline] + pub fn into_rc(&self) -> Rc { + let rc: Rc<[u8]> = Rc::from(&self.inner); + unsafe { Rc::from_raw(Rc::into_raw(rc) as *const Slice) } + } +} diff --git a/src/libstd/sys/switch/path.rs b/src/libstd/sys/switch/path.rs new file mode 100644 index 0000000000000..7ffb3d59216c7 --- /dev/null +++ b/src/libstd/sys/switch/path.rs @@ -0,0 +1,29 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsStr; +use path::Prefix; + +#[inline] +pub fn is_sep_byte(b: u8) -> bool { + b == b'/' +} + +#[inline] +pub fn is_verbatim_sep(b: u8) -> bool { + b == b'/' +} + +pub fn parse_prefix(_path: &OsStr) -> Option { + None +} + +pub const MAIN_SEP_STR: &'static str = "/"; +pub const MAIN_SEP: char = '/'; diff --git a/src/libstd/sys/switch/pipe.rs b/src/libstd/sys/switch/pipe.rs new file mode 100644 index 0000000000000..992e1ac409cfb --- /dev/null +++ b/src/libstd/sys/switch/pipe.rs @@ -0,0 +1,35 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::Void; + +pub struct AnonPipe(Void); + +impl AnonPipe { + pub fn read(&self, _buf: &mut [u8]) -> io::Result { + match self.0 {} + } + + pub fn write(&self, _buf: &[u8]) -> io::Result { + match self.0 {} + } + + pub fn diverge(&self) -> ! { + match self.0 {} + } +} + +pub fn read2(p1: AnonPipe, + _v1: &mut Vec, + _p2: AnonPipe, + _v2: &mut Vec) -> io::Result<()> { + match p1.0 {} +} diff --git a/src/libstd/sys/switch/process.rs b/src/libstd/sys/switch/process.rs new file mode 100644 index 0000000000000..f3f5de350f176 --- /dev/null +++ b/src/libstd/sys/switch/process.rs @@ -0,0 +1,150 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use ffi::OsStr; +use fmt; +use io; +use sys::fs::File; +use sys::pipe::AnonPipe; +use sys::{unsupported, Void}; +use sys_common::process::{CommandEnv, DefaultEnvKey}; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +pub struct Command { + env: CommandEnv +} + +// passed back to std::process with the pipes connected to the child, if any +// were requested +pub struct StdioPipes { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option, +} + +pub enum Stdio { + Inherit, + Null, + MakePipe, +} + +impl Command { + pub fn new(_program: &OsStr) -> Command { + Command { + env: Default::default() + } + } + + pub fn arg(&mut self, _arg: &OsStr) { + } + + pub fn env_mut(&mut self) -> &mut CommandEnv { + &mut self.env + } + + pub fn cwd(&mut self, _dir: &OsStr) { + } + + pub fn stdin(&mut self, _stdin: Stdio) { + } + + pub fn stdout(&mut self, _stdout: Stdio) { + } + + pub fn stderr(&mut self, _stderr: Stdio) { + } + + pub fn spawn(&mut self, _default: Stdio, _needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + unsupported() + } +} + +impl From for Stdio { + fn from(pipe: AnonPipe) -> Stdio { + pipe.diverge() + } +} + +impl From for Stdio { + fn from(file: File) -> Stdio { + file.diverge() + } +} + +impl fmt::Debug for Command { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + Ok(()) + } +} + +pub struct ExitStatus(Void); + +impl ExitStatus { + pub fn success(&self) -> bool { + match self.0 {} + } + + pub fn code(&self) -> Option { + match self.0 {} + } +} + +impl Clone for ExitStatus { + fn clone(&self) -> ExitStatus { + match self.0 {} + } +} + +impl Copy for ExitStatus {} + +impl PartialEq for ExitStatus { + fn eq(&self, _other: &ExitStatus) -> bool { + match self.0 {} + } +} + +impl Eq for ExitStatus { +} + +impl fmt::Debug for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +impl fmt::Display for ExitStatus { + fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { + match self.0 {} + } +} + +pub struct Process(Void); + +impl Process { + pub fn id(&self) -> u32 { + match self.0 {} + } + + pub fn kill(&mut self) -> io::Result<()> { + match self.0 {} + } + + pub fn wait(&mut self) -> io::Result { + match self.0 {} + } + + pub fn try_wait(&mut self) -> io::Result> { + match self.0 {} + } +} diff --git a/src/libstd/sys/switch/rwlock.rs b/src/libstd/sys/switch/rwlock.rs new file mode 100644 index 0000000000000..d74b614ba47de --- /dev/null +++ b/src/libstd/sys/switch/rwlock.rs @@ -0,0 +1,61 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::mutex::Mutex; + +pub struct RWLock { + mutex: Mutex +} + +unsafe impl Send for RWLock {} +unsafe impl Sync for RWLock {} + +impl RWLock { + pub const fn new() -> RWLock { + RWLock { + mutex: Mutex::new() + } + } + + #[inline] + pub unsafe fn read(&self) { + self.mutex.lock(); + } + + #[inline] + pub unsafe fn try_read(&self) -> bool { + self.mutex.try_lock() + } + + #[inline] + pub unsafe fn write(&self) { + self.mutex.lock(); + } + + #[inline] + pub unsafe fn try_write(&self) -> bool { + self.mutex.try_lock() + } + + #[inline] + pub unsafe fn read_unlock(&self) { + self.mutex.unlock(); + } + + #[inline] + pub unsafe fn write_unlock(&self) { + self.mutex.unlock(); + } + + #[inline] + pub unsafe fn destroy(&self) { + self.mutex.destroy(); + } +} diff --git a/src/libstd/sys/switch/stack_overflow.rs b/src/libstd/sys/switch/stack_overflow.rs new file mode 100644 index 0000000000000..bed274142f1ce --- /dev/null +++ b/src/libstd/sys/switch/stack_overflow.rs @@ -0,0 +1,23 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub struct Handler; + +impl Handler { + pub unsafe fn new() -> Handler { + Handler + } +} + +pub unsafe fn init() { +} + +pub unsafe fn cleanup() { +} diff --git a/src/libstd/sys/switch/stdio.rs b/src/libstd/sys/switch/stdio.rs new file mode 100644 index 0000000000000..af50df5520cb8 --- /dev/null +++ b/src/libstd/sys/switch/stdio.rs @@ -0,0 +1,69 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use sys::unsupported; + +pub struct Stdin; +pub struct Stdout; +pub struct Stderr; + +impl Stdin { + pub fn new() -> io::Result { + unsupported() + } + + pub fn read(&self, _data: &mut [u8]) -> io::Result { + unsupported() + } +} + +impl Stdout { + pub fn new() -> io::Result { + unsupported() + } + + pub fn write(&self, _data: &[u8]) -> io::Result { + unsupported() + } + + pub fn flush(&self) -> io::Result<()> { + unsupported() + } +} + +impl Stderr { + pub fn new() -> io::Result { + unsupported() + } + + pub fn write(&self, _data: &[u8]) -> io::Result { + unsupported() + } + + pub fn flush(&self) -> io::Result<()> { + unsupported() + } +} + +impl io::Write for Stderr { + fn write(&mut self, _data: &[u8]) -> io::Result { + unsupported() + } + fn flush(&mut self) -> io::Result<()> { + unsupported() + } +} + +pub const STDIN_BUF_SIZE: usize = 0; + +pub fn is_ebadf(_err: &io::Error) -> bool { + true +} diff --git a/src/libstd/sys/switch/thread.rs b/src/libstd/sys/switch/thread.rs new file mode 100644 index 0000000000000..6a066509b492a --- /dev/null +++ b/src/libstd/sys/switch/thread.rs @@ -0,0 +1,49 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use alloc::boxed::FnBox; +use ffi::CStr; +use io; +use sys::{unsupported, Void}; +use time::Duration; + +pub struct Thread(Void); + +pub const DEFAULT_MIN_STACK_SIZE: usize = 4096; + +impl Thread { + pub unsafe fn new<'a>(_stack: usize, _p: Box) + -> io::Result + { + unsupported() + } + + pub fn yield_now() { + // do nothing + } + + pub fn set_name(_name: &CStr) { + // nope + } + + pub fn sleep(_dur: Duration) { + panic!("can't sleep"); + } + + pub fn join(self) { + match self.0 {} + } +} + +pub mod guard { + pub type Guard = !; + pub unsafe fn current() -> Option { None } + pub unsafe fn init() -> Option { None } +} diff --git a/src/libstd/sys/switch/thread_local.rs b/src/libstd/sys/switch/thread_local.rs new file mode 100644 index 0000000000000..cacd84e21025f --- /dev/null +++ b/src/libstd/sys/switch/thread_local.rs @@ -0,0 +1,71 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // not used on all platforms + +use collections::BTreeMap; +use ptr; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +pub type Key = usize; + +type Dtor = unsafe extern fn(*mut u8); + +static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT; + +static mut KEYS: *mut BTreeMap> = ptr::null_mut(); + +#[thread_local] +static mut LOCALS: *mut BTreeMap = ptr::null_mut(); + +unsafe fn keys() -> &'static mut BTreeMap> { + if KEYS == ptr::null_mut() { + KEYS = Box::into_raw(Box::new(BTreeMap::new())); + } + &mut *KEYS +} + +unsafe fn locals() -> &'static mut BTreeMap { + if LOCALS == ptr::null_mut() { + LOCALS = Box::into_raw(Box::new(BTreeMap::new())); + } + &mut *LOCALS +} + +#[inline] +pub unsafe fn create(dtor: Option) -> Key { + let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); + keys().insert(key, dtor); + key +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + if let Some(&entry) = locals().get(&key) { + entry + } else { + ptr::null_mut() + } +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + locals().insert(key, value); +} + +#[inline] +pub unsafe fn destroy(key: Key) { + keys().remove(&key); +} + +#[inline] +pub fn requires_synchronized_create() -> bool { + false +} diff --git a/src/libstd/sys/switch/time.rs b/src/libstd/sys/switch/time.rs new file mode 100644 index 0000000000000..b7ebc1e9c48e5 --- /dev/null +++ b/src/libstd/sys/switch/time.rs @@ -0,0 +1,56 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use time::Duration; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct Instant(Duration); + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] +pub struct SystemTime(Duration); + +pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0)); + +impl Instant { + pub fn now() -> Instant { + unimplemented!() + } + + pub fn sub_instant(&self, other: &Instant) -> Duration { + self.0 - other.0 + } + + pub fn add_duration(&self, other: &Duration) -> Instant { + Instant(self.0 + *other) + } + + pub fn sub_duration(&self, other: &Duration) -> Instant { + Instant(self.0 - *other) + } +} + +impl SystemTime { + pub fn now() -> SystemTime { + unimplemented!() + } + + pub fn sub_time(&self, other: &SystemTime) + -> Result { + self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0) + } + + pub fn add_duration(&self, other: &Duration) -> SystemTime { + SystemTime(self.0 + *other) + } + + pub fn sub_duration(&self, other: &Duration) -> SystemTime { + SystemTime(self.0 - *other) + } +} diff --git a/src/libstd/sys_common/mod.rs b/src/libstd/sys_common/mod.rs index d0c4d6a773746..20a22634d3e2a 100644 --- a/src/libstd/sys_common/mod.rs +++ b/src/libstd/sys_common/mod.rs @@ -56,7 +56,7 @@ pub mod bytestring; pub mod process; cfg_if! { - if #[cfg(any(target_os = "cloudabi", target_os = "l4re", target_os = "redox"))] { + if #[cfg(any(target_os = "cloudabi", target_os = "l4re", target_os = "redox", target_os = "switch"))] { pub use sys::net; } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { pub use sys::net; From e37fc847fffd3f932feb3978ad4a617f7a5f19d4 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 16 Mar 2018 19:41:06 +0100 Subject: [PATCH 04/33] Disable tls feature of ralloc --- src/libstd/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index a31494f48b9e9..afec8a84891b1 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -27,7 +27,7 @@ libc = { path = "../rustc/libc_shim" } alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] -ralloc = { git = "https://github.com/megatonhammer/ralloc" } +ralloc = { git = "https://github.com/megatonhammer/ralloc", default-features = false } [dev-dependencies] rand = "0.4" From 1478e394203463a0b3a09b799ef5fe3d84921091 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 16 Mar 2018 19:41:37 +0100 Subject: [PATCH 05/33] Stub out TLS implementation on switch --- src/libstd/sys/switch/thread_local.rs | 51 +++++---------------------- 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/src/libstd/sys/switch/thread_local.rs b/src/libstd/sys/switch/thread_local.rs index cacd84e21025f..48ae1fc7abc13 100644 --- a/src/libstd/sys/switch/thread_local.rs +++ b/src/libstd/sys/switch/thread_local.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,59 +10,26 @@ #![allow(dead_code)] // not used on all platforms -use collections::BTreeMap; -use ptr; -use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; - pub type Key = usize; -type Dtor = unsafe extern fn(*mut u8); - -static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT; - -static mut KEYS: *mut BTreeMap> = ptr::null_mut(); - -#[thread_local] -static mut LOCALS: *mut BTreeMap = ptr::null_mut(); - -unsafe fn keys() -> &'static mut BTreeMap> { - if KEYS == ptr::null_mut() { - KEYS = Box::into_raw(Box::new(BTreeMap::new())); - } - &mut *KEYS -} - -unsafe fn locals() -> &'static mut BTreeMap { - if LOCALS == ptr::null_mut() { - LOCALS = Box::into_raw(Box::new(BTreeMap::new())); - } - &mut *LOCALS -} - #[inline] -pub unsafe fn create(dtor: Option) -> Key { - let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); - keys().insert(key, dtor); - key +pub unsafe fn create(_dtor: Option) -> Key { + unimplemented!() } #[inline] -pub unsafe fn get(key: Key) -> *mut u8 { - if let Some(&entry) = locals().get(&key) { - entry - } else { - ptr::null_mut() - } +pub unsafe fn set(_key: Key, _value: *mut u8) { + unimplemented!() } #[inline] -pub unsafe fn set(key: Key, value: *mut u8) { - locals().insert(key, value); +pub unsafe fn get(_key: Key) -> *mut u8 { + unimplemented!() } #[inline] -pub unsafe fn destroy(key: Key) { - keys().remove(&key); +pub unsafe fn destroy(_key: Key) { + unimplemented!() } #[inline] From 4bc08e7404c25f3215a7b283bde03a5bda18c24d Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 16 Mar 2018 19:41:58 +0100 Subject: [PATCH 06/33] We don't link against a libc, we'll need the memcpy from compiler_builtins --- src/rustc/compiler_builtins_shim/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rustc/compiler_builtins_shim/Cargo.toml b/src/rustc/compiler_builtins_shim/Cargo.toml index c54ffe156954a..7b162cf695f77 100644 --- a/src/rustc/compiler_builtins_shim/Cargo.toml +++ b/src/rustc/compiler_builtins_shim/Cargo.toml @@ -34,7 +34,7 @@ cc = "1.0.1" [features] c = [] -default = ["c", "rustbuild", "compiler-builtins"] +default = ["c", "rustbuild", "compiler-builtins", "mem"] mem = [] rustbuild = [] compiler-builtins = [] From 2dd54485ba23f82c0fcbd666e66ec1c195cdec8b Mon Sep 17 00:00:00 2001 From: roblabla Date: Sat, 17 Mar 2018 04:12:12 +0100 Subject: [PATCH 07/33] Add proper thread_local storage --- src/libstd/Cargo.toml | 1 + src/libstd/lib.rs | 2 ++ src/libstd/sys/switch/thread_local.rs | 47 ++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 9 deletions(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index afec8a84891b1..1e39a0697aba9 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -28,6 +28,7 @@ alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] ralloc = { git = "https://github.com/megatonhammer/ralloc", default-features = false } +megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer" } [dev-dependencies] rand = "0.4" diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e89b6b5ddc386..e1749575f5dfe 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -366,6 +366,8 @@ extern crate alloc_system; #[cfg(target_os = "switch")] extern crate ralloc; +#[cfg(target_os = "switch")] +extern crate megaton_hammer; #[cfg(not(target_os = "switch"))] #[doc(masked)] extern crate libc; diff --git a/src/libstd/sys/switch/thread_local.rs b/src/libstd/sys/switch/thread_local.rs index 48ae1fc7abc13..28a2e245e7a8d 100644 --- a/src/libstd/sys/switch/thread_local.rs +++ b/src/libstd/sys/switch/thread_local.rs @@ -1,4 +1,4 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,26 +10,55 @@ #![allow(dead_code)] // not used on all platforms +use collections::BTreeMap; +use ptr; +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; +use megaton_hammer; + pub type Key = usize; +type Dtor = unsafe extern fn(*mut u8); + +static NEXT_KEY: AtomicUsize = ATOMIC_USIZE_INIT; + +static mut KEYS: Option>> = None; + +unsafe fn keys() -> &'static mut BTreeMap> { + if KEYS.is_none() { + KEYS = Some(BTreeMap::new()); + } + KEYS.as_mut().unwrap() +} + +unsafe fn locals() -> &'static mut BTreeMap { + let ctx = megaton_hammer::tls::TlsStruct::get_thread_ctx(); + &mut ctx.locals +} + #[inline] -pub unsafe fn create(_dtor: Option) -> Key { - unimplemented!() +pub unsafe fn create(dtor: Option) -> Key { + let key = NEXT_KEY.fetch_add(1, Ordering::SeqCst); + keys().insert(key, dtor); + key } #[inline] -pub unsafe fn set(_key: Key, _value: *mut u8) { - unimplemented!() +pub unsafe fn get(key: Key) -> *mut u8 { + if let Some(&entry) = locals().get(&key) { + entry + } else { + ptr::null_mut() + } } #[inline] -pub unsafe fn get(_key: Key) -> *mut u8 { - unimplemented!() +pub unsafe fn set(key: Key, value: *mut u8) { + locals().insert(key, value); } #[inline] -pub unsafe fn destroy(_key: Key) { - unimplemented!() +pub unsafe fn destroy(key: Key) { + keys().remove(&key); } #[inline] From 9cd010f973b867a9821ba57b0c9e6c4434a8a07f Mon Sep 17 00:00:00 2001 From: roblabla Date: Sat, 17 Mar 2018 18:13:58 +0100 Subject: [PATCH 08/33] Use WASM's stubbed mutex implementation --- src/libstd/sys/switch/mutex.rs | 86 ++++++++++++++-------------------- 1 file changed, 35 insertions(+), 51 deletions(-) diff --git a/src/libstd/sys/switch/mutex.rs b/src/libstd/sys/switch/mutex.rs index 349377133f2bd..d1a9e06781ca8 100644 --- a/src/libstd/sys/switch/mutex.rs +++ b/src/libstd/sys/switch/mutex.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -8,89 +8,73 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub unsafe fn mutex_try_lock(_m: *mut i32) -> bool { - unimplemented!() -} +// TODO: Properly implement mutex. The switch has multiple syscalls to implement +// mutexes properly. -pub unsafe fn mutex_lock(_m: *mut i32) { - unimplemented!() -} +use cell::UnsafeCell; -pub unsafe fn mutex_unlock(_m: *mut i32) { - unimplemented!() +pub struct Mutex { + locked: UnsafeCell, } -pub struct Mutex; +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} // no threads on wasm impl Mutex { - /// Create a new mutex. - pub const fn new() -> Self { - // TODO: Sadly can't panic in constfn yet - Mutex + pub const fn new() -> Mutex { + Mutex { locked: UnsafeCell::new(false) } } #[inline] - pub unsafe fn init(&self) { - unimplemented!() + pub unsafe fn init(&mut self) { } - /// Try to lock the mutex #[inline] - pub unsafe fn try_lock(&self) -> bool { - unimplemented!() + pub unsafe fn lock(&self) { + let locked = self.locked.get(); + assert!(!*locked, "cannot recursively acquire mutex"); + *locked = true; } - /// Lock the mutex #[inline] - pub unsafe fn lock(&self) { - unimplemented!() + pub unsafe fn unlock(&self) { + *self.locked.get() = false; } - /// Unlock the mutex #[inline] - pub unsafe fn unlock(&self) { - unimplemented!() + pub unsafe fn try_lock(&self) -> bool { + let locked = self.locked.get(); + if *locked { + false + } else { + *locked = true; + true + } } #[inline] pub unsafe fn destroy(&self) { - unimplemented!() } } -pub struct ReentrantMutex; +pub struct ReentrantMutex { +} impl ReentrantMutex { - pub const fn uninitialized() -> Self { - // TODO: Sadly can't panic in constfn yet - ReentrantMutex + pub unsafe fn uninitialized() -> ReentrantMutex { + ReentrantMutex { } } - #[inline] - pub unsafe fn init(&mut self) { - unimplemented!() - } + pub unsafe fn init(&mut self) {} - /// Try to lock the mutex - #[inline] - pub unsafe fn try_lock(&self) -> bool { - unimplemented!() - } + pub unsafe fn lock(&self) {} - /// Lock the mutex #[inline] - pub unsafe fn lock(&self) { - unimplemented!() + pub unsafe fn try_lock(&self) -> bool { + true } - /// Unlock the mutex - #[inline] - pub unsafe fn unlock(&self) { - unimplemented!() - } + pub unsafe fn unlock(&self) {} - #[inline] - pub unsafe fn destroy(&self) { - unimplemented!() - } + pub unsafe fn destroy(&self) {} } From 78f7cb4d12f0aa428e8dc14002cd1914d9159775 Mon Sep 17 00:00:00 2001 From: roblabla Date: Sat, 17 Mar 2018 18:20:48 +0100 Subject: [PATCH 09/33] Also steal wasm's condvar --- src/libstd/sys/switch/condvar.rs | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/libstd/sys/switch/condvar.rs b/src/libstd/sys/switch/condvar.rs index d5c80c4375ae1..69b9ff153be4c 100644 --- a/src/libstd/sys/switch/condvar.rs +++ b/src/libstd/sys/switch/condvar.rs @@ -1,4 +1,4 @@ -// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -11,41 +11,35 @@ use sys::mutex::Mutex; use time::Duration; -pub struct Condvar; +// TODO: The switch has proper condvars too. + +pub struct Condvar { } impl Condvar { pub const fn new() -> Condvar { - // TODO: Sadly can't panic in const fn yet. - Condvar + Condvar { } } #[inline] - pub unsafe fn init(&self) { - unimplemented!() - } + pub unsafe fn init(&mut self) {} #[inline] - pub fn notify_one(&self) { - unimplemented!() + pub unsafe fn notify_one(&self) { } #[inline] - pub fn notify_all(&self) { - unimplemented!() + pub unsafe fn notify_all(&self) { } - #[inline] - pub fn wait(&self, _mutex: &Mutex) { - unimplemented!() + pub unsafe fn wait(&self, _mutex: &Mutex) { + panic!("can't block with web assembly") } - #[inline] - pub fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { - unimplemented!(); + pub unsafe fn wait_timeout(&self, _mutex: &Mutex, _dur: Duration) -> bool { + panic!("can't block with web assembly"); } #[inline] pub unsafe fn destroy(&self) { - unimplemented!() } } From 205ec67f08b4f625f1b2add27310f5cfbe85840c Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 21 Mar 2018 23:19:39 +0100 Subject: [PATCH 10/33] Implement sys::switch::stdio::{Stdout, Stderr} --- src/libstd/Cargo.toml | 3 +- src/libstd/lib.rs | 2 ++ src/libstd/sys/switch/stdio.rs | 57 +++++++++++++++++++++++++++------- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 1e39a0697aba9..c8eede178885e 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -28,7 +28,8 @@ alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] ralloc = { git = "https://github.com/megatonhammer/ralloc", default-features = false } -megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer" } +megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } +megaton-ipc = { git = "https://github.com/megatonhammer/megaton-hammer" } [dev-dependencies] rand = "0.4" diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e1749575f5dfe..082c6363c5307 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -368,6 +368,8 @@ extern crate ralloc; #[cfg(target_os = "switch")] extern crate megaton_hammer; +#[cfg(target_os = "switch")] +extern crate megaton_ipc; #[cfg(not(target_os = "switch"))] #[doc(masked)] extern crate libc; diff --git a/src/libstd/sys/switch/stdio.rs b/src/libstd/sys/switch/stdio.rs index af50df5520cb8..67687bb8e0d81 100644 --- a/src/libstd/sys/switch/stdio.rs +++ b/src/libstd/sys/switch/stdio.rs @@ -10,6 +10,9 @@ use io; use sys::unsupported; +use slice; + +use megaton_hammer::loader::{self, Logger, SocketKind}; pub struct Stdin; pub struct Stdout; @@ -27,38 +30,70 @@ impl Stdin { impl Stdout { pub fn new() -> io::Result { - unsupported() + Ok(Stdout) } - pub fn write(&self, _data: &[u8]) -> io::Result { - unsupported() + pub fn write(&self, data: &[u8]) -> io::Result { + use megaton_ipc::nn::socket::sf::IClient; + + Logger.write(&data[..data.len()]); + let msg_len = if let Some((kind, stdout)) = loader::get_stdout_socket() { + let client = match kind { + SocketKind::BsdU => IClient::new_bsd_u(), + SocketKind::BsdS => IClient::new_bsd_s() + }; + // Should be already initialized. + match client.and_then(|client| client.write(stdout, unsafe { slice::from_raw_parts(data.as_ptr() as *const i8, data.len()) })) { + Ok((ret, _bsd_errno)) if ret >= 0 => ret as usize, + _ => data.len() + } + } else { + data.len() + }; + Ok(msg_len) } pub fn flush(&self) -> io::Result<()> { - unsupported() + Ok(()) } } impl Stderr { pub fn new() -> io::Result { - unsupported() + Ok(Stderr) } - pub fn write(&self, _data: &[u8]) -> io::Result { - unsupported() + pub fn write(&self, data: &[u8]) -> io::Result { + use megaton_ipc::nn::socket::sf::IClient; + + let msg_len = if let Some((kind, stderr)) = loader::get_stderr_socket() { + let client = match kind { + SocketKind::BsdU => IClient::new_bsd_u(), + SocketKind::BsdS => IClient::new_bsd_s() + }; + // Should be already initialized. + match client.and_then(|client| client.write(stderr, unsafe { slice::from_raw_parts(data.as_ptr() as *const i8, data.len()) } )) { + Ok((ret, _bsd_errno)) if ret >= 0 => ret as usize, + _ => data.len() + } + } else { + data.len() + }; + Logger.write(&data[..msg_len]); + Ok(msg_len) } pub fn flush(&self) -> io::Result<()> { - unsupported() + Ok(()) } } impl io::Write for Stderr { - fn write(&mut self, _data: &[u8]) -> io::Result { - unsupported() + fn write(&mut self, data: &[u8]) -> io::Result { + Stderr::write(self, data) } fn flush(&mut self) -> io::Result<()> { - unsupported() + Stderr::flush(self) } } From def9e0cc43bb18914f6815d5e2cb33052e3f6a51 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 25 Apr 2018 19:56:38 +0200 Subject: [PATCH 11/33] Stop using ralloc, it's too buggy. Use our simple allocator --- src/libstd/Cargo.toml | 1 - src/libstd/lib.rs | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index c8eede178885e..8bcc65f241fa3 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -27,7 +27,6 @@ libc = { path = "../rustc/libc_shim" } alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] -ralloc = { git = "https://github.com/megatonhammer/ralloc", default-features = false } megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } megaton-ipc = { git = "https://github.com/megatonhammer/megaton-hammer" } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 082c6363c5307..ae8979703b378 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -340,7 +340,7 @@ static ALLOC: alloc_system::System = alloc_system::System; #[cfg(target_os = "switch")] #[global_allocator] -static ALLOC: ralloc::Allocator = ralloc::Allocator; +static ALLOC: megaton_hammer::allocator::Allocator = megaton_hammer::allocator::Allocator::new(); // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. @@ -363,8 +363,6 @@ pub use core::{unreachable, unimplemented, write, writeln, try}; extern crate alloc as alloc_crate; #[cfg(not(target_os = "switch"))] extern crate alloc_system; -#[cfg(target_os = "switch")] -extern crate ralloc; #[cfg(target_os = "switch")] extern crate megaton_hammer; From 89b3214d47dc56f51cdc67c29ede6bda8d5908bd Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 25 Apr 2018 19:57:34 +0200 Subject: [PATCH 12/33] Cleanly exit in panic_abort on switch --- src/libpanic_abort/Cargo.toml | 3 +++ src/libpanic_abort/lib.rs | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libpanic_abort/Cargo.toml b/src/libpanic_abort/Cargo.toml index 46095d0155380..e9ba8b81a75ec 100644 --- a/src/libpanic_abort/Cargo.toml +++ b/src/libpanic_abort/Cargo.toml @@ -15,3 +15,6 @@ doc = false [target.'cfg(not(target_os = "switch"))'.dependencies] libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } + +[target.'cfg(target_os = "switch")'.dependencies] +megaton-hammer = { path = "../../../megatonhammer/megaton-hammer" } #git = "https://github.com/megatonhammer/megaton-hammer" } diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index d9b010958e947..83f2317b9f8a3 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -61,15 +61,19 @@ pub unsafe extern fn __rust_start_panic(_payload: usize) -> u32 { libc::abort(); } - // TODO: We don't want to use the abort intrinsic, as that will cause a - // 2168-0000 on the switch. Instead, we should "properly" exit. #[cfg(any(target_os = "redox", - target_os = "switch", windows, all(target_arch = "wasm32", not(target_os = "emscripten"))))] unsafe fn abort() -> ! { core::intrinsics::abort(); } + + #[cfg(target_os = "switch")] + unsafe fn abort() -> ! { + extern crate megaton_hammer; + + megaton_hammer::loader::exit(1) + } } // This... is a bit of an oddity. The tl;dr; is that this is required to link From c51b2f72ddc30a51809dc861de48df38601556c3 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 4 May 2018 00:13:43 +0200 Subject: [PATCH 13/33] Add TCP network impl --- src/libstd/error.rs | 9 + src/libstd/sys/switch/mod.rs | 46 ++++- src/libstd/sys/switch/net.rs | 374 +++++++++++++++++++++++++++++++---- 3 files changed, 391 insertions(+), 38 deletions(-) diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 29534696abc5b..28db9c1b96cb2 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -367,6 +367,15 @@ impl Error for char::ParseCharError { } } +// TODO: I am a terrible human being. This doesn't belong here, but I have to... +#[stable(feature = "megaton_hammer", since = "1.0.0")] +impl Error for ::megaton_hammer::error::Error { + fn description(&self) -> &str { + "MegatonHammer error occured" + } +} + + // copied from any.rs impl dyn Error + 'static { /// Returns true if the boxed type is the same as `T` diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index a3ff3f89b8e90..9b591abed0104 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -55,8 +55,50 @@ pub fn unsupported_err() -> io::Error { "operation not supported on wasm yet") } -pub fn decode_error_kind(_errno: i32) -> ErrorKind { - io::ErrorKind::Other +pub fn decode_error_kind(errno: i32) -> ErrorKind { + // Taken from linux, which is what net seems to use. + mod linux { + pub const ECONNREFUSED: i32 = 111; + pub const ECONNRESET: i32 = 104; + pub const EPERM: i32 = 1; + pub const EACCES: i32 = 13; + pub const EPIPE: i32 = 32; + pub const ENOTCONN: i32 = 107; + pub const ECONNABORTED: i32 = 103; + pub const EADDRNOTAVAIL: i32 = 99; + pub const EADDRINUSE: i32 = 98; + pub const ENOENT: i32 = 2; + pub const EINTR: i32 = 4; + pub const EINVAL: i32 = 22; + pub const ETIMEDOUT: i32 = 110; + pub const EEXIST: i32 = 17; + pub const EAGAIN: i32 = 11; + pub const EWOULDBLOCK: i32 = EAGAIN; + } + + match errno { + linux::ECONNREFUSED => ErrorKind::ConnectionRefused, + linux::ECONNRESET => ErrorKind::ConnectionReset, + linux::EPERM | linux::EACCES => ErrorKind::PermissionDenied, + linux::EPIPE => ErrorKind::BrokenPipe, + linux::ENOTCONN => ErrorKind::NotConnected, + linux::ECONNABORTED => ErrorKind::ConnectionAborted, + linux::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, + linux::EADDRINUSE => ErrorKind::AddrInUse, + linux::ENOENT => ErrorKind::NotFound, + linux::EINTR => ErrorKind::Interrupted, + linux::EINVAL => ErrorKind::InvalidInput, + linux::ETIMEDOUT => ErrorKind::TimedOut, + linux::EEXIST => ErrorKind::AlreadyExists, + + // These two constants can have the same value on some systems, + // but different values on others, so we can't use a match + // clause + x if x == linux::EAGAIN || x == linux::EWOULDBLOCK => + ErrorKind::WouldBlock, + + _ => ErrorKind::Other, + } } /// TODO: Do a proper abort using the exit stuff. diff --git a/src/libstd/sys/switch/net.rs b/src/libstd/sys/switch/net.rs index e0c1e9e9ac793..0dca6c45c2738 100644 --- a/src/libstd/sys/switch/net.rs +++ b/src/libstd/sys/switch/net.rs @@ -9,93 +9,216 @@ // except according to those terms. use fmt; -use io; +use io::{self, Write}; +use ffi::{CStr, CString}; use net::{SocketAddr, Shutdown, Ipv4Addr, Ipv6Addr}; use time::Duration; use sys::{unsupported, Void}; +use sys_common::{FromInner, IntoInner}; +use self::netc as c; +use mem; +use slice; +use sync::Arc; +use megaton_hammer::kernel::Session; +use megaton_ipc::nn::socket::sf::IClient; +use megaton_ipc::nn::socket::resolver::IResolver; + +pub struct TcpStream(Arc>, u32); + +macro_rules! handle_err { + ($x: expr) => {{ + let val = try_mth!($x); + if val.0 == -1 { + return Err(io::Error::from_raw_os_error(val.1 as i32)); + } else { + val + } + }} +} -pub struct TcpStream(Void); +macro_rules! try_mth { + ($x: expr) => { + match $x { + Ok(val) => val, + Err(err) => { + return Err(io::Error::new(io::ErrorKind::Other, Box::new(err))) + } + } + } +} -impl TcpStream { pub fn connect(_: &SocketAddr) -> io::Result { - unsupported() +fn sockname(f: F) -> io::Result + where F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> io::Result<()> +{ + unsafe { + let mut storage: c::sockaddr_storage = mem::zeroed(); + let mut len = mem::size_of_val(&storage) as c::socklen_t; + f(&mut storage as *mut _ as *mut _, &mut len)?; + sockaddr_to_addr(&storage, len as usize) + } +} + +pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, + len: usize) -> io::Result { + match storage.sa_family { + c::AF_INET => { + assert!(len as usize >= mem::size_of::()); + Ok(SocketAddr::V4(FromInner::from_inner(unsafe { + *(storage as *const _ as *const c::sockaddr_in) + }))) + } + c::AF_INET6 => { + assert!(len as usize >= mem::size_of::()); + Ok(SocketAddr::V6(FromInner::from_inner(unsafe { + *(storage as *const _ as *const c::sockaddr_in6) + }))) + } + _ => { + Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid argument")) + } + } +} + +impl TcpStream { + pub fn connect(addr: &SocketAddr) -> io::Result { + let bsd = if let Ok(bsd) = IClient::new_bsd_u() { + bsd + } else { + try_mth!(IClient::new_bsd_s()) + }; + + if let &SocketAddr::V6(_) = addr { + return unsupported() + } + + let (socket, _) = handle_err!(bsd.socket(netc::AF_INET as u32, 1, 0)); // SOCK_STREAM + + let (addrp, _) = addr.into_inner(); + let _ = handle_err!(bsd.connect(socket as u32, unsafe { &*addrp })); + + Ok(TcpStream(bsd, socket as u32)) } pub fn connect_timeout(_: &SocketAddr, _: Duration) -> io::Result { + // TODO: Use set_nonblocking, which itself does some magic... unsupported() } pub fn set_read_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + // TODO: setsockopt magic stuff + unsupported() } pub fn set_write_timeout(&self, _: Option) -> io::Result<()> { - match self.0 {} + // TODO: setsockopt magic stuff + unsupported() } pub fn read_timeout(&self) -> io::Result> { - match self.0 {} + // TODO: setsockopt magic stuff + unsupported() } pub fn write_timeout(&self) -> io::Result> { - match self.0 {} + // TODO: setsockopt magic stuff + unsupported() } - pub fn peek(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + fn recv_with_flags(&self, buf: &mut [u8], flags: u32) -> io::Result { + let (ret, _) = handle_err!(self.0.recv(self.1, flags, unsafe { + slice::from_raw_parts_mut(buf as *mut [u8] as *mut u8 as *mut i8, buf.len()) + })); + Ok(ret as usize) } - pub fn read(&self, _: &mut [u8]) -> io::Result { - match self.0 {} + pub fn peek(&self, buf: &mut [u8]) -> io::Result { + self.recv_with_flags(buf, 2) // MSG_PEEK, as defined by linux and freebsd. } - pub fn write(&self, _: &[u8]) -> io::Result { - match self.0 {} + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.recv_with_flags(buf, 0) + } + + pub fn write(&self, buf: &[u8]) -> io::Result { + // TODO: let len = cmp::min(buf.len(), ::max_value() as usize) as wrlen_t; + // TODO: The standard implementation uses MSG_NOSIGNAL, which avoids + // generating SIGPIPE on EOF. The switch has no notion of signals + // however, so this seems to be a NOOP. + let (ret, _) = handle_err!(self.0.send(self.1, 0, unsafe { + slice::from_raw_parts(buf as *const [u8] as *const u8 as *const i8, buf.len()) + })); + Ok(ret as usize) } pub fn peer_addr(&self) -> io::Result { - match self.0 {} + sockname(|buf, _len| unsafe { + handle_err!(self.0.get_peer_name(self.1, &mut *buf)); + Ok(()) + }) } pub fn socket_addr(&self) -> io::Result { - match self.0 {} + sockname(|buf, _len| unsafe { + handle_err!(self.0.get_sock_name(self.1, &mut *buf)); + Ok(()) + }) } - pub fn shutdown(&self, _: Shutdown) -> io::Result<()> { - match self.0 {} + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + // True in both freebsd and linux + let how = match how { + Shutdown::Write => 1, // SHUT_WR + Shutdown::Read => 0, + Shutdown::Both => 2, + }; + handle_err!(self.0.shutdown(self.1, how)); + Ok(()) } pub fn duplicate(&self) -> io::Result { - match self.0 {} + let (socket, _) = handle_err!(self.0.duplicate_socket(self.1, 0)); + Ok(TcpStream(self.0.clone(), socket as u32)) } pub fn set_nodelay(&self, _: bool) -> io::Result<()> { - match self.0 {} + unsupported() } pub fn nodelay(&self) -> io::Result { - match self.0 {} + unsupported() } pub fn set_ttl(&self, _: u32) -> io::Result<()> { - match self.0 {} + unsupported() } pub fn ttl(&self) -> io::Result { - match self.0 {} + unsupported() } pub fn take_error(&self) -> io::Result> { - match self.0 {} + unsupported() } pub fn set_nonblocking(&self, _: bool) -> io::Result<()> { - match self.0 {} + unsupported() } } impl fmt::Debug for TcpStream { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - match self.0 {} + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut res = f.debug_struct("TcpStream"); + + if let Ok(addr) = self.socket_addr() { + res.field("addr", &addr); + } + + if let Ok(peer) = self.peer_addr() { + res.field("peer", &peer); + } + + res.field("fd", &self.1) + .finish() } } @@ -283,44 +406,223 @@ impl fmt::Debug for UdpSocket { } } -pub struct LookupHost(Void); +pub struct LookupHost { + data: io::Cursor>, + done: bool +} + +unsafe fn read_struct(s: &mut T, f: &mut io::Read) -> io::Result<()> { + let size = mem::size_of::(); + let slice = slice::from_raw_parts_mut( + s as *mut T as *mut u8, + size + ); + // `read_exact()` comes from `Read` impl for `&[u8]` + f.read_exact(slice) +} + +unsafe fn write_struct(s: &T, f: &mut io::Write) -> io::Result<()> { + let size = mem::size_of::(); + let slice = slice::from_raw_parts( + s as *const T as *const u8, + size + ); + // `read_exact()` comes from `Read` impl for `&[u8]` + f.write_all(slice) +} + impl Iterator for LookupHost { type Item = SocketAddr; fn next(&mut self) -> Option { - match self.0 {} + if self.done { + return None + } + loop { + let mut magic : u32 = 0; + unsafe { read_struct(&mut magic, &mut self.data).ok()?; } + + if u32::from_be(magic) != 0xBEEFCAFE { + self.done = true; + return None; + } + + let mut hdr : c::PackedAddrInfoHdr = unsafe { mem::zeroed() }; + unsafe { read_struct(&mut hdr, &mut self.data).expect("Can't fail"); } + hdr.ai_flags = u32::from_be(hdr.ai_flags); + hdr.family = u32::from_be(hdr.family); + hdr.socktype = u32::from_be(hdr.socktype); + hdr.protocol = u32::from_be(hdr.protocol); + hdr.addrlen = u32::from_be(hdr.addrlen); + + let ret = if hdr.addrlen != 0 { + let pos = self.data.position(); + let ret = if hdr.family == c::AF_INET as u32 { + if hdr.addrlen < mem::size_of::() as u32 { + self.done = true; + panic!("Wrong addrlen: {}", hdr.addrlen); + } + let sockaddr = unsafe { + let mut sockaddr : c::sockaddr_in = mem::zeroed(); + read_struct(&mut sockaddr, &mut self.data).expect("Can't fail"); + let addr = &mut sockaddr as *mut c::sockaddr_in; + (*addr).sin_port = u16::from_be((*addr).sin_port); + (*addr).sin_addr.s_addr = u32::from_be((*addr).sin_addr.s_addr); + sockaddr + }; + Some(SocketAddr::V4(FromInner::from_inner(sockaddr))) + } else { + None + }; + self.data.set_position(pos + hdr.addrlen as u64); + ret + } else { + let pos = self.data.position(); + self.data.set_position(pos + 4); + None + }; + + // skip over canonname. + let eos_pos = self.data.get_ref()[self.data.position() as usize..].iter().position(|x| *x == b'\0').expect("Can't fail"); + let pos = self.data.position(); + self.data.set_position(pos + eos_pos as u64 + 1); + + if ret.is_some() { + return ret; + } + } } } -pub fn lookup_host(_: &str) -> io::Result { - unsupported() +fn pack_ai(ai: &c::addrinfo, buf: &mut [u8]) -> io::Result<()> { + let mut cursor = io::Cursor::new(buf); + let mut cur_ai = ai as *const c::addrinfo; + unsafe { + while !cur_ai.is_null() { + write_struct(&0xBEEFCAFEu32.to_be(), &mut cursor)?; + write_struct(&c::PackedAddrInfoHdr { + ai_flags: (*cur_ai).ai_flags.to_be(), + family: (*cur_ai).ai_family.to_be(), + socktype: (*cur_ai).ai_socktype.to_be(), + protocol: (*cur_ai).ai_protocol.to_be(), + addrlen: (*cur_ai).ai_addrlen.to_be(), + }, &mut cursor)?; + if (*cur_ai).ai_addrlen == 0 { + write_struct(&0u32, &mut cursor)?; + } else if (*cur_ai).ai_family == c::AF_INET as u32 { + // Write sockaddr + let addr = (*cur_ai).ai_addr as *const c::sockaddr_in; + write_struct(&0u32, &mut cursor)?; // len + write_struct(&((*addr).sin_family as u32), &mut cursor)?; // family + write_struct(&((*addr).sin_port as u32).to_be(), &mut cursor)?; // port + write_struct(&((*addr).sin_addr.s_addr).to_be(), &mut cursor)?; // addr + } else { + let slice = slice::from_raw_parts( + (*cur_ai).ai_addr as *const u8, + (*cur_ai).ai_addrlen as usize + ); + cursor.write_all(slice)?; + } + if (*cur_ai).ai_canonname.is_null() { + write_struct(&0u8, &mut cursor)?; + } else { + cursor.write_all(CStr::from_ptr((*cur_ai).ai_canonname as _).to_bytes_with_nul())?; + } + cur_ai = (*cur_ai).ai_next; + } + write_struct(&0u32, &mut cursor)?; + } + Ok(()) +} + +pub fn lookup_host(host: &str) -> io::Result { + let sfdnsres = try_mth!(IResolver::new()); + + let c_host = CString::new(host)?; + // TODO + let mut hints: c::addrinfo = unsafe { mem::zeroed() }; + hints.ai_socktype = c::SOCK_STREAM; + + let mut hints_packed = [0; 0x400]; + + pack_ai(&hints, &mut hints_packed[..])?; + + // Box the result as it's a bit big (stack size can be as small as 0x4000 + // on the switch. + let mut res = Box::new([0; 0x1000]); + //TODO: Use gethostbyname instead. + let (ret, _errno, _size) = match sfdnsres.get_addr_info(1, 0, 0, c_host.as_bytes_with_nul(), &b"0\0"[..], &hints_packed, &mut *res) { + Ok(x) => x, + Err(err) => return Err(io::Error::new(io::ErrorKind::Other, Box::new(err))) + }; + + if ret == 0 { + Ok(LookupHost { + data: io::Cursor::new(res), + // TODO: size: size, + done: false + }) + } else { + // TODO: ret or errno? + Err(io::Error::from_raw_os_error(ret as i32)) + } } + #[allow(bad_style)] pub mod netc { - pub const AF_INET: u8 = 0; - pub const AF_INET6: u8 = 1; + #[repr(C)] + #[derive(Copy, Clone)] + pub struct PackedAddrInfoHdr { + pub ai_flags: u32, + pub family: u32, + pub socktype: u32, + pub protocol: u32, + pub addrlen: u32, + } + + pub struct addrinfo { + pub ai_flags: u32, + pub ai_family: u32, + pub ai_socktype: u32, + pub ai_protocol: u32, + pub ai_addrlen: u32, + pub ai_addr: *const sockaddr, + pub ai_canonname: *const u8, + pub ai_next: *const addrinfo, + } + + pub const AF_INET: u8 = 2; + pub const AF_INET6: u8 = 10; pub type sa_family_t = u8; + pub const SOCK_STREAM: u32 = 1; + + #[repr(C)] #[derive(Copy, Clone)] pub struct in_addr { pub s_addr: u32, } + #[repr(C)] #[derive(Copy, Clone)] pub struct sockaddr_in { + pub sin_len: u8, pub sin_family: sa_family_t, pub sin_port: u16, pub sin_addr: in_addr, } + #[repr(C)] #[derive(Copy, Clone)] pub struct in6_addr { pub s6_addr: [u8; 16], } + #[repr(C)] #[derive(Copy, Clone)] pub struct sockaddr_in6 { + pub sin6_len: u8, pub sin6_family: sa_family_t, pub sin6_port: u16, pub sin6_addr: in6_addr, @@ -328,9 +630,9 @@ pub mod netc { pub sin6_scope_id: u32, } - #[derive(Copy, Clone)] - pub struct sockaddr { - } + pub type sockaddr = ::megaton_ipc::nn::socket::Sockaddr; + + pub type sockaddr_storage = sockaddr; pub type socklen_t = usize; } From f3b7beb41e615d0ed8317f877ddf2a30191b7eaf Mon Sep 17 00:00:00 2001 From: roblabla Date: Sun, 13 May 2018 01:23:54 +0200 Subject: [PATCH 14/33] Initialize bsd IPC client --- src/libstd/sys/switch/net.rs | 41 ++++++++++++++++++++++++++-------- src/libstd/sys/switch/stdio.rs | 37 +++++++++++++++++++++++++----- 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/libstd/sys/switch/net.rs b/src/libstd/sys/switch/net.rs index 0dca6c45c2738..748edd17b78a6 100644 --- a/src/libstd/sys/switch/net.rs +++ b/src/libstd/sys/switch/net.rs @@ -19,7 +19,8 @@ use self::netc as c; use mem; use slice; use sync::Arc; -use megaton_hammer::kernel::Session; +use megaton_hammer::kernel::{Session, TransferMemory, KObject}; +use megaton_ipc::nn; use megaton_ipc::nn::socket::sf::IClient; use megaton_ipc::nn::socket::resolver::IResolver; @@ -60,7 +61,7 @@ fn sockname(f: F) -> io::Result pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, len: usize) -> io::Result { - match storage.sa_family { + match storage.sin_family { c::AF_INET => { assert!(len as usize >= mem::size_of::()); Ok(SocketAddr::V4(FromInner::from_inner(unsafe { @@ -79,18 +80,40 @@ pub fn sockaddr_to_addr(storage: &c::sockaddr_storage, } } +fn init() -> io::Result>> { + fn init_args(cb: fn(nn::socket::BsdBufferConfig, u64, u64, &KObject) -> ::megaton_hammer::error::Result>) -> ::megaton_hammer::error::Result> { + let transfer_mem = TransferMemory::new(4 * 256 * 2 * 1024).expect("TransferMem creation to succeed"); + cb(nn::socket::BsdBufferConfig { + version: 1, + tcp_tx_buf_size: 0x8000, + tcp_rx_buf_size: 0x10_000, + tcp_tx_buf_max_size: 0x40_000, + tcp_rx_buf_max_size: 0x40_000, + udp_tx_buf_size: 0x2400, + udp_rx_buf_size: 0xA500, + sb_efficiency: 4, + }, 0, 4 * 256 * 2 * 1024, transfer_mem.as_ref()) + } + let bsd = if let Ok(bsd) = IClient::new_bsd_u(init_args) { + bsd + } else { + try_mth!(IClient::new_bsd_s(init_args)) + }; + + // Leak the handle. + // TODO: We should clean it up at exit! + ::core::mem::forget(bsd.clone()); + Ok(bsd) +} + impl TcpStream { pub fn connect(addr: &SocketAddr) -> io::Result { - let bsd = if let Ok(bsd) = IClient::new_bsd_u() { - bsd - } else { - try_mth!(IClient::new_bsd_s()) - }; - if let &SocketAddr::V6(_) = addr { return unsupported() } + let bsd = init()?; + let (socket, _) = handle_err!(bsd.socket(netc::AF_INET as u32, 1, 0)); // SOCK_STREAM let (addrp, _) = addr.into_inner(); @@ -630,7 +653,7 @@ pub mod netc { pub sin6_scope_id: u32, } - pub type sockaddr = ::megaton_ipc::nn::socket::Sockaddr; + pub type sockaddr = ::megaton_ipc::nn::socket::SockaddrIn; pub type sockaddr_storage = sockaddr; diff --git a/src/libstd/sys/switch/stdio.rs b/src/libstd/sys/switch/stdio.rs index 67687bb8e0d81..f89c413983b2d 100644 --- a/src/libstd/sys/switch/stdio.rs +++ b/src/libstd/sys/switch/stdio.rs @@ -13,6 +13,8 @@ use sys::unsupported; use slice; use megaton_hammer::loader::{self, Logger, SocketKind}; +use megaton_hammer::kernel::TransferMemory; +use megaton_ipc::nn; pub struct Stdin; pub struct Stdout; @@ -38,11 +40,23 @@ impl Stdout { Logger.write(&data[..data.len()]); let msg_len = if let Some((kind, stdout)) = loader::get_stdout_socket() { + let init_args = |cb: fn(_, _, _, &_) -> _| { + let transfer_mem = TransferMemory::new(4 * 256 * 2 * 1024).expect("TransferMem creation to succeed"); + cb(nn::socket::BsdBufferConfig { + version: 1, + tcp_tx_buf_size: 0x8000, + tcp_rx_buf_size: 0x10_000, + tcp_tx_buf_max_size: 0x40_000, + tcp_rx_buf_max_size: 0x40_000, + udp_tx_buf_size: 0x2400, + udp_rx_buf_size: 0xA500, + sb_efficiency: 4, + }, 0, 4 * 256 * 2 * 1024, transfer_mem.as_ref()) + }; let client = match kind { - SocketKind::BsdU => IClient::new_bsd_u(), - SocketKind::BsdS => IClient::new_bsd_s() + SocketKind::BsdU => IClient::new_bsd_u(init_args), + SocketKind::BsdS => IClient::new_bsd_s(init_args) }; - // Should be already initialized. match client.and_then(|client| client.write(stdout, unsafe { slice::from_raw_parts(data.as_ptr() as *const i8, data.len()) })) { Ok((ret, _bsd_errno)) if ret >= 0 => ret as usize, _ => data.len() @@ -67,9 +81,22 @@ impl Stderr { use megaton_ipc::nn::socket::sf::IClient; let msg_len = if let Some((kind, stderr)) = loader::get_stderr_socket() { + let init_args = |cb: fn(_, _, _, &_) -> _| { + let transfer_mem = TransferMemory::new(4 * 256 * 2 * 1024).expect("TransferMem creation to succeed"); + cb(nn::socket::BsdBufferConfig { + version: 1, + tcp_tx_buf_size: 0x8000, + tcp_rx_buf_size: 0x10_000, + tcp_tx_buf_max_size: 0x40_000, + tcp_rx_buf_max_size: 0x40_000, + udp_tx_buf_size: 0x2400, + udp_rx_buf_size: 0xA500, + sb_efficiency: 4, + }, 0, 4 * 256 * 2 * 1024, transfer_mem.as_ref()) + }; let client = match kind { - SocketKind::BsdU => IClient::new_bsd_u(), - SocketKind::BsdS => IClient::new_bsd_s() + SocketKind::BsdU => IClient::new_bsd_u(init_args), + SocketKind::BsdS => IClient::new_bsd_s(init_args) }; // Should be already initialized. match client.and_then(|client| client.write(stderr, unsafe { slice::from_raw_parts(data.as_ptr() as *const i8, data.len()) } )) { From 936ffb831748b2c60ce395dd279d05df52f2e352 Mon Sep 17 00:00:00 2001 From: roblabla Date: Sun, 13 May 2018 01:24:42 +0200 Subject: [PATCH 15/33] Use mth exit in os::exit --- src/libstd/sys/switch/os.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/switch/os.rs b/src/libstd/sys/switch/os.rs index 1c41e34e52119..0c11e73ca7cf0 100644 --- a/src/libstd/sys/switch/os.rs +++ b/src/libstd/sys/switch/os.rs @@ -107,8 +107,8 @@ pub fn home_dir() -> Option { None } -pub fn exit(_code: i32) -> ! { - loop {} +pub fn exit(code: i32) -> ! { + ::megaton_hammer::loader::exit(code as u64); } pub fn getpid() -> u32 { From b8463b2cda6ff800d8c61ba33246e9cc9bb76b26 Mon Sep 17 00:00:00 2001 From: roblabla Date: Sun, 13 May 2018 01:25:42 +0200 Subject: [PATCH 16/33] Use megaton-allocator --- src/libstd/Cargo.toml | 1 + src/libstd/lib.rs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 8bcc65f241fa3..d3548f4096d91 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -27,6 +27,7 @@ libc = { path = "../rustc/libc_shim" } alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] +megaton-allocator = { git = "https://github.com/megatonhammer/megaton-hammer" } megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } megaton-ipc = { git = "https://github.com/megatonhammer/megaton-hammer" } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index ae8979703b378..b4f07dcfc4413 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -340,7 +340,7 @@ static ALLOC: alloc_system::System = alloc_system::System; #[cfg(target_os = "switch")] #[global_allocator] -static ALLOC: megaton_hammer::allocator::Allocator = megaton_hammer::allocator::Allocator::new(); +static ALLOC: megaton_allocator::Allocator = megaton_allocator::Allocator::new(); // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. @@ -367,6 +367,8 @@ extern crate alloc_system; #[cfg(target_os = "switch")] extern crate megaton_hammer; #[cfg(target_os = "switch")] +extern crate megaton_allocator; +#[cfg(target_os = "switch")] extern crate megaton_ipc; #[cfg(not(target_os = "switch"))] #[doc(masked)] From 1d2c3215236722e39cf8d4168a1c8353abcd9a2c Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 15 May 2018 02:05:35 +0200 Subject: [PATCH 17/33] Add liblibc switch typedef --- .gitmodules | 2 +- src/liblibc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index f3eb902709ca7..1e1c1a7ee1258 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,7 +10,7 @@ url = https://github.com/rust-lang/rust-installer.git [submodule "src/liblibc"] path = src/liblibc - url = https://github.com/rust-lang/libc.git + url = https://github.com/MegatonHammer/libc.git [submodule "src/doc/nomicon"] path = src/doc/nomicon url = https://github.com/rust-lang-nursery/nomicon.git diff --git a/src/liblibc b/src/liblibc index b6d23ed45d729..8ff70b6a4c8fe 160000 --- a/src/liblibc +++ b/src/liblibc @@ -1 +1 @@ -Subproject commit b6d23ed45d72918239c0bfac11dc547895e59b81 +Subproject commit 8ff70b6a4c8feeba9e9c00bb4d1a0af4b94d4bb1 From 2f88dfaee5f02c9561cb41e06e805da0e20fbc95 Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 15 May 2018 04:32:18 +0200 Subject: [PATCH 18/33] Add missing impls --- src/libstd/sys/switch/os_str.rs | 7 ++++++- src/libstd/sys/switch/process.rs | 12 ++++++++++++ src/libstd/sys/switch/stdio.rs | 4 ++++ src/libstd/sys/switch/thread.rs | 3 ++- 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/switch/os_str.rs b/src/libstd/sys/switch/os_str.rs index 655bfdb916707..eb3a1ead58c94 100644 --- a/src/libstd/sys/switch/os_str.rs +++ b/src/libstd/sys/switch/os_str.rs @@ -19,7 +19,7 @@ use rc::Rc; use sync::Arc; use sys_common::{AsInner, IntoInner}; use sys_common::bytestring::debug_fmt_bytestring; -use std_unicode::lossy::Utf8Lossy; +use core::str::lossy::Utf8Lossy; #[derive(Clone, Hash)] pub struct Buf { @@ -104,6 +104,11 @@ impl Buf { self.inner.shrink_to_fit() } + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + pub fn as_slice(&self) -> &Slice { unsafe { mem::transmute(&*self.inner) } } diff --git a/src/libstd/sys/switch/process.rs b/src/libstd/sys/switch/process.rs index f3f5de350f176..5aaa27840a794 100644 --- a/src/libstd/sys/switch/process.rs +++ b/src/libstd/sys/switch/process.rs @@ -88,6 +88,18 @@ impl fmt::Debug for Command { } } +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct ExitCode(i32); + +impl ExitCode { + pub const SUCCESS: ExitCode = ExitCode(0); + pub const FAILURE: ExitCode = ExitCode(1); + + pub fn as_i32(&self) -> i32 { + self.0 as i32 + } +} + pub struct ExitStatus(Void); impl ExitStatus { diff --git a/src/libstd/sys/switch/stdio.rs b/src/libstd/sys/switch/stdio.rs index f89c413983b2d..6816f9b293857 100644 --- a/src/libstd/sys/switch/stdio.rs +++ b/src/libstd/sys/switch/stdio.rs @@ -129,3 +129,7 @@ pub const STDIN_BUF_SIZE: usize = 0; pub fn is_ebadf(_err: &io::Error) -> bool { true } + +pub fn stderr_prints_nothing() -> bool { + false +} diff --git a/src/libstd/sys/switch/thread.rs b/src/libstd/sys/switch/thread.rs index 6a066509b492a..728e678a2e8c1 100644 --- a/src/libstd/sys/switch/thread.rs +++ b/src/libstd/sys/switch/thread.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::boxed::FnBox; +use boxed::FnBox; use ffi::CStr; use io; use sys::{unsupported, Void}; @@ -46,4 +46,5 @@ pub mod guard { pub type Guard = !; pub unsafe fn current() -> Option { None } pub unsafe fn init() -> Option { None } + pub unsafe fn deinit() {} } From 082f16c60deb25a6ba6e02cf50c10bc5c9d23daa Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 15 May 2018 04:33:08 +0200 Subject: [PATCH 19/33] FIXUP --- src/libstd/alloc.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 8db365cd21d67..b959a2bf2af17 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -82,10 +82,14 @@ use sys_common::util::dumb_print; #[doc(inline)] pub use alloc_crate::alloc::*; +#[cfg(not(target_os = "switch"))] #[stable(feature = "alloc_system_type", since = "1.28.0")] #[doc(inline)] pub use alloc_system::System; +#[cfg(target_os = "switch")] +use ALLOC as System; + static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. @@ -155,7 +159,7 @@ pub mod __default_lib_allocator { #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc(layout) + (&System).alloc(layout) } #[no_mangle] @@ -163,7 +167,7 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) + (&System).dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } #[no_mangle] @@ -173,13 +177,13 @@ pub mod __default_lib_allocator { align: usize, new_size: usize) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr, old_layout, new_size) + (&System).realloc(ptr, old_layout, new_size) } #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc_zeroed(layout) + (&System).alloc_zeroed(layout) } } From 39594c7560552e36ad26f2d4f3e19f8ad85a3ebd Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 6 Jul 2018 00:23:39 +0200 Subject: [PATCH 20/33] Use ipc in megaton-hammer --- src/libstd/Cargo.toml | 1 - src/libstd/lib.rs | 2 -- src/libstd/sys/switch/net.rs | 8 ++++---- src/libstd/sys/switch/stdio.rs | 6 +++--- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index d3548f4096d91..59804b4cf0e1b 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -29,7 +29,6 @@ alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] megaton-allocator = { git = "https://github.com/megatonhammer/megaton-hammer" } megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } -megaton-ipc = { git = "https://github.com/megatonhammer/megaton-hammer" } [dev-dependencies] rand = "0.4" diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index b4f07dcfc4413..dbb2d440cbfab 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -368,8 +368,6 @@ extern crate alloc_system; extern crate megaton_hammer; #[cfg(target_os = "switch")] extern crate megaton_allocator; -#[cfg(target_os = "switch")] -extern crate megaton_ipc; #[cfg(not(target_os = "switch"))] #[doc(masked)] extern crate libc; diff --git a/src/libstd/sys/switch/net.rs b/src/libstd/sys/switch/net.rs index 748edd17b78a6..ca4931c629c40 100644 --- a/src/libstd/sys/switch/net.rs +++ b/src/libstd/sys/switch/net.rs @@ -20,9 +20,9 @@ use mem; use slice; use sync::Arc; use megaton_hammer::kernel::{Session, TransferMemory, KObject}; -use megaton_ipc::nn; -use megaton_ipc::nn::socket::sf::IClient; -use megaton_ipc::nn::socket::resolver::IResolver; +use megaton_hammer::ipcdefs::nn; +use megaton_hammer::ipcdefs::nn::socket::sf::IClient; +use megaton_hammer::ipcdefs::nn::socket::resolver::IResolver; pub struct TcpStream(Arc>, u32); @@ -653,7 +653,7 @@ pub mod netc { pub sin6_scope_id: u32, } - pub type sockaddr = ::megaton_ipc::nn::socket::SockaddrIn; + pub type sockaddr = ::megaton_hammer::ipcdefs::nn::socket::SockaddrIn; pub type sockaddr_storage = sockaddr; diff --git a/src/libstd/sys/switch/stdio.rs b/src/libstd/sys/switch/stdio.rs index 6816f9b293857..a312de39632db 100644 --- a/src/libstd/sys/switch/stdio.rs +++ b/src/libstd/sys/switch/stdio.rs @@ -14,7 +14,7 @@ use slice; use megaton_hammer::loader::{self, Logger, SocketKind}; use megaton_hammer::kernel::TransferMemory; -use megaton_ipc::nn; +use megaton_hammer::ipcdefs::nn; pub struct Stdin; pub struct Stdout; @@ -36,7 +36,7 @@ impl Stdout { } pub fn write(&self, data: &[u8]) -> io::Result { - use megaton_ipc::nn::socket::sf::IClient; + use megaton_hammer::ipcdefs::nn::socket::sf::IClient; Logger.write(&data[..data.len()]); let msg_len = if let Some((kind, stdout)) = loader::get_stdout_socket() { @@ -78,7 +78,7 @@ impl Stderr { } pub fn write(&self, data: &[u8]) -> io::Result { - use megaton_ipc::nn::socket::sf::IClient; + use megaton_hammer::ipcdefs::nn::socket::sf::IClient; let msg_len = if let Some((kind, stderr)) = loader::get_stderr_socket() { let init_args = |cb: fn(_, _, _, &_) -> _| { From c53db9779f5c8199a4650de6163ef9e1ca850204 Mon Sep 17 00:00:00 2001 From: roblabla Date: Sat, 7 Jul 2018 23:09:56 +0200 Subject: [PATCH 21/33] Depend on megaton-hammer from git --- src/libpanic_abort/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libpanic_abort/Cargo.toml b/src/libpanic_abort/Cargo.toml index e9ba8b81a75ec..57d59d70ff402 100644 --- a/src/libpanic_abort/Cargo.toml +++ b/src/libpanic_abort/Cargo.toml @@ -17,4 +17,4 @@ libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } [target.'cfg(target_os = "switch")'.dependencies] -megaton-hammer = { path = "../../../megatonhammer/megaton-hammer" } #git = "https://github.com/megatonhammer/megaton-hammer" } +megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer" } From 963b9759b1ee447d275ea74ff5a4d7c9c412fd06 Mon Sep 17 00:00:00 2001 From: roblabla Date: Sat, 28 Jul 2018 01:30:11 +0200 Subject: [PATCH 22/33] Add switch unwind support --- src/libpanic_unwind/Cargo.toml | 6 +++--- src/libpanic_unwind/lib.rs | 1 + src/libunwind/Cargo.toml | 6 ++---- src/libunwind/lib.rs | 2 -- src/rustc/libc_shim/Cargo.toml | 4 ++-- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/libpanic_unwind/Cargo.toml b/src/libpanic_unwind/Cargo.toml index 74aaa4d5ae3db..a732578cd50cb 100644 --- a/src/libpanic_unwind/Cargo.toml +++ b/src/libpanic_unwind/Cargo.toml @@ -10,8 +10,8 @@ bench = false doc = false [dependencies] -alloc = { path = "../liballoc" } -core = { path = "../libcore" } +#alloc = { path = "../liballoc" } +#core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } unwind = { path = "../libunwind" } -compiler_builtins = { path = "../rustc/compiler_builtins_shim" } +#compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 5c320bb369e70..3eedb339256e2 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -71,6 +71,7 @@ mod imp; // i686-pc-windows-gnu and all others #[cfg(any(all(unix, not(target_os = "emscripten")), + target_os = "switch", target_os = "cloudabi", target_os = "redox", all(windows, target_arch = "x86", target_env = "gnu")))] diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml index 9cdf7b9b635aa..fffe0403c1dbb 100644 --- a/src/libunwind/Cargo.toml +++ b/src/libunwind/Cargo.toml @@ -11,9 +11,7 @@ test = false bench = false doc = false -#[dependencies] +[dependencies] #core = { path = "../libcore" } - -[target.'cfg(not(target_os = "switch"))'.dependencies] libc = { path = "../rustc/libc_shim" } -compiler_builtins = { path = "../rustc/compiler_builtins_shim" } +#compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs index 7631fd1871464..2b3c19c067ed4 100644 --- a/src/libunwind/lib.rs +++ b/src/libunwind/lib.rs @@ -27,8 +27,6 @@ cfg_if! { // no extra unwinder support needed } else if #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] { // no unwinder on the system! - } else if #[cfg(target_os = "switch")] { - // no unwinder support for now } else { extern crate libc; mod libunwind; diff --git a/src/rustc/libc_shim/Cargo.toml b/src/rustc/libc_shim/Cargo.toml index e77897d643313..d0703ead08647 100644 --- a/src/rustc/libc_shim/Cargo.toml +++ b/src/rustc/libc_shim/Cargo.toml @@ -28,8 +28,8 @@ doc = false # expected success, got: exit code: 101', src/bootstrap/compile.rs:883:8 # # See https://github.com/rust-lang/rfcs/pull/1133. -core = { path = "../../libcore" } -compiler_builtins = { path = "../compiler_builtins_shim" } +#core = { path = "../../libcore" } +#compiler_builtins = { path = "../compiler_builtins_shim" } [features] From d13dcf27c428dd1f8ba1c3b4641047574ba3afe5 Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 31 Jul 2018 03:01:58 +0200 Subject: [PATCH 23/33] Implement symbolication on switch target --- src/libstd/Cargo.toml | 5 +- src/libstd/build.rs | 1 + src/libstd/lib.rs | 1 - src/libstd/sys/switch/backtrace/mod.rs | 22 +++++ src/libstd/sys/switch/backtrace/printing.rs | 85 +++++++++++++++++++ src/libstd/sys/switch/backtrace/tracing.rs | 93 +++++++++++++++++++++ src/libstd/sys/switch/mod.rs | 1 - 7 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 src/libstd/sys/switch/backtrace/mod.rs create mode 100644 src/libstd/sys/switch/backtrace/printing.rs create mode 100644 src/libstd/sys/switch/backtrace/tracing.rs diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 59804b4cf0e1b..bafb27d23bcf3 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -21,14 +21,17 @@ panic_abort = { path = "../libpanic_abort" } #compiler_builtins = { path = "../rustc/compiler_builtins_shim" } profiler_builtins = { path = "../libprofiler_builtins", optional = true } unwind = { path = "../libunwind" } +libc = { path = "../rustc/libc_shim" } [target.'cfg(not(target_os = "switch"))'.dependencies] -libc = { path = "../rustc/libc_shim" } alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] megaton-allocator = { git = "https://github.com/megatonhammer/megaton-hammer" } megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } +gimli = { git = "https://github.com/gimli-rs/gimli", rev = "8a7743d3ff7d677dfb40e45dbca2a31b7ff13051", default-features = false, features = ["alloc"] } +fallible-iterator = { version = "=0.1.4", default-features = false, features = ["alloc"] } +addr2line = { git = "https://github.com/roblabla/addr2line", default-features = false } [dev-dependencies] rand = "0.4" diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 26d93f97e69f3..e885fe14a9141 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -23,6 +23,7 @@ fn main() { !target.contains("cloudabi") && !target.contains("emscripten") && !target.contains("msvc") && + !target.contains("switch") && !target.contains("wasm32") { let _ = build_libbacktrace(&target); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index dbb2d440cbfab..419ad382e0e0a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -368,7 +368,6 @@ extern crate alloc_system; extern crate megaton_hammer; #[cfg(target_os = "switch")] extern crate megaton_allocator; -#[cfg(not(target_os = "switch"))] #[doc(masked)] extern crate libc; diff --git a/src/libstd/sys/switch/backtrace/mod.rs b/src/libstd/sys/switch/backtrace/mod.rs new file mode 100644 index 0000000000000..72390ea50fcb6 --- /dev/null +++ b/src/libstd/sys/switch/backtrace/mod.rs @@ -0,0 +1,22 @@ +/// See sys/unix/backtrace/mod.rs for an explanation of the method used here. + +pub use self::tracing::unwind_backtrace; +pub use self::printing::{foreach_symbol_fileline, resolve_symname}; + +// tracing impls: +mod tracing; +// symbol resolvers: +mod printing; + +pub mod gnu { + use io; + use fs; + use libc::c_char; + use vec::Vec; + + pub fn get_executable_filename() -> io::Result<(Vec, fs::File)> { + Err(io::Error::new(io::ErrorKind::Other, "Unimplemented")) + } +} + +pub struct BacktraceContext; diff --git a/src/libstd/sys/switch/backtrace/printing.rs b/src/libstd/sys/switch/backtrace/printing.rs new file mode 100644 index 0000000000000..5850fe4b7b366 --- /dev/null +++ b/src/libstd/sys/switch/backtrace/printing.rs @@ -0,0 +1,85 @@ +extern crate addr2line; +extern crate gimli; + +use self::addr2line::Context; +use self::gimli::{DebugAbbrev, DebugInfo, DebugLine, DebugRanges, DebugRngLists, DebugStr, LittleEndian}; + +use io; +use slice; +use sys::backtrace::BacktraceContext; +use sys_common::backtrace::Frame; + +pub fn resolve_symname(frame: Frame, + callback: F, + _ctx: &BacktraceContext) -> io::Result<()> +where + F: FnOnce(Option<&str>) -> io::Result<()> +{ + let mut cb: Option = Some(callback); + // TODO: Use .dynamic. + from_debuginfo(frame, |name, file, line| cb.take().map_or(Ok(()), |cb| cb(name))) +} + +pub fn foreach_symbol_fileline(frame: Frame, mut f: F, _ctx: &BacktraceContext) -> io::Result +where + F: FnMut(&[u8], u32) -> io::Result<()> +{ + from_debuginfo(frame, |name, file, line| f(file, line))?; + Ok(false) +} + +fn from_debuginfo(frame: Frame, mut cb: F) -> io::Result<()> +where + F: FnMut(Option<&str>, &[u8], u32) -> io::Result<()> +{ + extern { + static __debug_abbrev_start: u8; + static __debug_abbrev_end: u8; + static __debug_info_start: u8; + static __debug_info_end: u8; + static __debug_line_start: u8; + static __debug_line_end: u8; + static __debug_ranges_start: u8; + static __debug_ranges_end: u8; + static __debug_rnglists_start: u8; + static __debug_rnglists_end: u8; + static __debug_str_start: u8; + static __debug_str_end: u8; + } + + unsafe fn from_ptrs(start: &u8, end: &u8) -> &'static [u8] { + slice::from_raw_parts(start, end as *const u8 as usize - start as *const u8 as usize) + } + + let ctx = unsafe { + // SAFETY: The addresses are set by the linker script, and should be correct. + let abbrev = DebugAbbrev::new(from_ptrs(&__debug_abbrev_start, &__debug_abbrev_end), LittleEndian); + let info = DebugInfo::new(from_ptrs(&__debug_info_start, &__debug_info_end), LittleEndian); + let line = DebugLine::new(from_ptrs(&__debug_line_start, &__debug_line_end), LittleEndian); + let ranges = DebugRanges::new(from_ptrs(&__debug_ranges_start, &__debug_ranges_end), LittleEndian); + let rnglists = DebugRngLists::new(from_ptrs(&__debug_rnglists_start, &__debug_rnglists_end), LittleEndian); + let s = DebugStr::new(from_ptrs(&__debug_str_start, &__debug_str_end), LittleEndian); + + match Context::from_sections(abbrev, info, line, ranges, rnglists, s) { + Ok(k) => k, + Err(err) => panic!("LOL"), // TODO: Propagate error + } + }; + + if let Ok(mut frames) = ctx.find_frames(frame.symbol_addr as u64) { + while let Ok(Some(mut frame)) = frames.next() { + let (file, line) = frame + .location + .map(|l| (l.file, l.line)) + .unwrap_or((None, None)); + let name = frame + .function + .as_ref() + .and_then(|f| f.raw_name().ok()); + if let (Some(f), Some(l)) = (file, line) { + cb(name.as_ref().map(|v| v.as_ref()), f.as_bytes(), l as u32)?; + } + } + } + Ok(()) +} diff --git a/src/libstd/sys/switch/backtrace/tracing.rs b/src/libstd/sys/switch/backtrace/tracing.rs new file mode 100644 index 0000000000000..4852313020547 --- /dev/null +++ b/src/libstd/sys/switch/backtrace/tracing.rs @@ -0,0 +1,93 @@ +// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use error::Error; +use io; +use libc; +use sys::backtrace::BacktraceContext; +use sys_common::backtrace::Frame; + +use unwind as uw; + +struct Context<'a> { + idx: usize, + frames: &'a mut [Frame], +} + +#[derive(Debug)] +struct UnwindError(uw::_Unwind_Reason_Code); + +impl Error for UnwindError { + fn description(&self) -> &'static str { + "unexpected return value while unwinding" + } +} + +impl ::fmt::Display for UnwindError { + fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result { + write!(f, "{}: {:?}", self.description(), self.0) + } +} + +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn unwind_backtrace(frames: &mut [Frame]) + -> io::Result<(usize, BacktraceContext)> +{ + let mut cx = Context { + idx: 0, + frames, + }; + let result_unwind = unsafe { + uw::_Unwind_Backtrace(trace_fn, + &mut cx as *mut Context + as *mut libc::c_void) + }; + // See libunwind:src/unwind/Backtrace.c for the return values. + // No, there is no doc. + match result_unwind { + // These return codes seem to be benign and need to be ignored for backtraces + // to show up properly on all tested platforms. + uw::_URC_END_OF_STACK | uw::_URC_FATAL_PHASE1_ERROR | uw::_URC_FAILURE => { + Ok((cx.idx, BacktraceContext)) + } + _ => { + Err(io::Error::new(io::ErrorKind::Other, + UnwindError(result_unwind))) + } + } +} + +extern fn trace_fn(ctx: *mut uw::_Unwind_Context, + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { + let cx = unsafe { &mut *(arg as *mut Context) }; + let mut ip_before_insn = 0; + let mut ip = unsafe { + uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void + }; + if !ip.is_null() && ip_before_insn == 0 { + // this is a non-signaling frame, so `ip` refers to the address + // after the calling instruction. account for that. + ip = (ip as usize - 1) as *mut _; + } + + let symaddr = unsafe { uw::_Unwind_FindEnclosingFunction(ip) }; + + if cx.idx < cx.frames.len() { + cx.frames[cx.idx] = Frame { + symbol_addr: symaddr as *mut u8, + exact_position: ip as *mut u8, + inline_context: 0, + }; + cx.idx += 1; + } + + uw::_URC_NO_REASON +} diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index 9b591abed0104..e75ea4ca24388 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -21,7 +21,6 @@ use io::{self, ErrorKind}; pub mod args; -// TODO #[cfg(feature = "backtrace")] pub mod backtrace; pub mod cmath; From 10106e6bcb47c1ed56b479461db74fc7d527ade3 Mon Sep 17 00:00:00 2001 From: roblabla Date: Thu, 2 Aug 2018 14:40:42 +0200 Subject: [PATCH 24/33] Use megaton branch for gimli and addr2line --- src/libstd/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index bafb27d23bcf3..6bc66e1cc6e68 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -29,9 +29,9 @@ alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] megaton-allocator = { git = "https://github.com/megatonhammer/megaton-hammer" } megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } -gimli = { git = "https://github.com/gimli-rs/gimli", rev = "8a7743d3ff7d677dfb40e45dbca2a31b7ff13051", default-features = false, features = ["alloc"] } -fallible-iterator = { version = "=0.1.4", default-features = false, features = ["alloc"] } -addr2line = { git = "https://github.com/roblabla/addr2line", default-features = false } +gimli = { git = "https://github.com/roblabla/gimli", branch = "megaton", default-features = false, features = ["alloc"] } +fallible-iterator = { version = "0.1", default-features = false, features = ["alloc"] } +addr2line = { git = "https://github.com/roblabla/addr2line", branch = "megaton", default-features = false, features = ["alloc"] } [dev-dependencies] rand = "0.4" From 90a6e7ffccd462630530fca1b5b1fc9b6db09913 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 3 Aug 2018 15:35:40 +0200 Subject: [PATCH 25/33] Add runwind --- src/libstd/Cargo.toml | 1 + src/libstd/sys/switch/mod.rs | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 6bc66e1cc6e68..e1eade338bb76 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -32,6 +32,7 @@ megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", feat gimli = { git = "https://github.com/roblabla/gimli", branch = "megaton", default-features = false, features = ["alloc"] } fallible-iterator = { version = "0.1", default-features = false, features = ["alloc"] } addr2line = { git = "https://github.com/roblabla/addr2line", branch = "megaton", default-features = false, features = ["alloc"] } +runwind = { git = "https://github.com/roblabla/unwind-rs", branch = "megaton", default-features = false, features = ["nightly"] } [dev-dependencies] rand = "0.4" diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index e75ea4ca24388..240aa5f2f0ac3 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -18,6 +18,9 @@ #![allow(dead_code, missing_docs, bad_style)] +// Link against runwind here to avoid future conflicts +extern crate runwind; + use io::{self, ErrorKind}; pub mod args; From b233e90bdd0b42c75427191832150876ce952c17 Mon Sep 17 00:00:00 2001 From: Thog Date: Tue, 7 Aug 2018 21:37:57 +0200 Subject: [PATCH 26/33] Fix webasm -> switch in some error code --- src/libstd/sys/switch/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index 240aa5f2f0ac3..042558c115e9a 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -54,7 +54,7 @@ pub fn unsupported() -> io::Result { pub fn unsupported_err() -> io::Error { io::Error::new(io::ErrorKind::Other, - "operation not supported on wasm yet") + "operation not supported on switch yet") } pub fn decode_error_kind(errno: i32) -> ErrorKind { @@ -126,7 +126,7 @@ pub unsafe fn strlen(mut s: *const i8) -> usize { // generate these numbers. // // More seriously though this is just for DOS protection in hash maps. It's ok -// if we don't do that on wasm just yet. +// if we don't do that on switch just yet. pub fn hashmap_random_keys() -> (u64, u64) { (1, 2) } From 747f3fa9a65a228c4497f1599f597f826879bb78 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 3 Aug 2018 22:29:56 +0200 Subject: [PATCH 27/33] Implement redox-style path handling --- src/libstd/path.rs | 55 ++++++++++++++-------------------- src/libstd/sys/redox/path.rs | 10 ++----- src/libstd/sys/windows/path.rs | 8 ++++- 3 files changed, 32 insertions(+), 41 deletions(-) diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 688a7e99f10ed..471c235ed9f46 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -195,7 +195,7 @@ pub enum Prefix<'a> { impl<'a> Prefix<'a> { #[inline] - fn len(&self) -> usize { + pub(crate) fn len(&self) -> usize { use self::Prefix::*; fn os_str_len(s: &OsStr) -> usize { os_str_as_u8_slice(s).len() @@ -322,19 +322,14 @@ unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr { &*(s as *const [u8] as *const OsStr) } -// Detect scheme on Redox -fn has_redox_scheme(s: &[u8]) -> bool { - cfg!(target_os = "redox") && s.split(|b| *b == b'/').next().unwrap_or(b"").contains(&b':') -} - //////////////////////////////////////////////////////////////////////////////// // Cross-platform, iterator-independent parsing //////////////////////////////////////////////////////////////////////////////// /// Says whether the first byte after the prefix is a separator. -fn has_physical_root(s: &[u8], prefix: Option) -> bool { +fn has_physical_root(s: &[u8], prefix: Option) -> bool { let path = if let Some(p) = prefix { - &s[p.len()..] + &s[os_str_as_u8_slice(p.as_os_str()).len()..] } else { s }; @@ -448,6 +443,13 @@ impl<'a> PrefixComponent<'a> { pub fn as_os_str(&self) -> &'a OsStr { self.raw } + + pub(crate) fn from_os_str_kind<'raw>(raw: &'raw OsStr, prefix: Prefix<'raw>) -> PrefixComponent<'raw> { + PrefixComponent { + raw, + parsed: prefix + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -609,7 +611,7 @@ pub struct Components<'a> { path: &'a [u8], // The prefix as it was originally parsed, if any - prefix: Option>, + prefix: Option>, // true if path *physically* has a root separator; for most Windows // prefixes, it may have a "logical" rootseparator for the purposes of @@ -660,12 +662,12 @@ impl<'a> Components<'a> { // how long is the prefix, if any? #[inline] fn prefix_len(&self) -> usize { - self.prefix.as_ref().map(Prefix::len).unwrap_or(0) + self.prefix.map(|v| v.as_os_str().len()).unwrap_or(0) } #[inline] fn prefix_verbatim(&self) -> bool { - self.prefix.as_ref().map(Prefix::is_verbatim).unwrap_or(false) + self.prefix.as_ref().map(|v| v.kind().is_verbatim()).unwrap_or(false) } /// how much of the prefix is left from the point of view of iteration? @@ -740,7 +742,7 @@ impl<'a> Components<'a> { return true; } if let Some(p) = self.prefix { - if p.has_implicit_root() { + if p.kind().has_implicit_root() { return true; } } @@ -917,12 +919,8 @@ impl<'a> Iterator for Components<'a> { State::Prefix if self.prefix_len() > 0 => { self.front = State::StartDir; debug_assert!(self.prefix_len() <= self.path.len()); - let raw = &self.path[..self.prefix_len()]; self.path = &self.path[self.prefix_len()..]; - return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(raw) }, - parsed: self.prefix.unwrap(), - })); + return Some(Component::Prefix(self.prefix.unwrap())); } State::Prefix => { self.front = State::StartDir; @@ -934,7 +932,7 @@ impl<'a> Iterator for Components<'a> { self.path = &self.path[1..]; return Some(Component::RootDir); } else if let Some(p) = self.prefix { - if p.has_implicit_root() && !p.is_verbatim() { + if p.kind().has_implicit_root() && !p.kind().is_verbatim() { return Some(Component::RootDir); } } else if self.include_cur_dir() { @@ -981,7 +979,7 @@ impl<'a> DoubleEndedIterator for Components<'a> { self.path = &self.path[..self.path.len() - 1]; return Some(Component::RootDir); } else if let Some(p) = self.prefix { - if p.has_implicit_root() && !p.is_verbatim() { + if p.kind().has_implicit_root() && !p.kind().is_verbatim() { return Some(Component::RootDir); } } else if self.include_cur_dir() { @@ -991,10 +989,7 @@ impl<'a> DoubleEndedIterator for Components<'a> { } State::Prefix if self.prefix_len() > 0 => { self.back = State::Done; - return Some(Component::Prefix(PrefixComponent { - raw: unsafe { u8_slice_as_os_str(self.path) }, - parsed: self.prefix.unwrap(), - })); + return Some(Component::Prefix(self.prefix.unwrap())); } State::Prefix => { self.back = State::Done; @@ -1214,7 +1209,7 @@ impl PathBuf { { let comps = self.components(); if comps.prefix_len() > 0 && comps.prefix_len() == comps.path.len() && - comps.prefix.unwrap().is_drive() { + comps.prefix.unwrap().kind().is_drive() { need_sep = false } } @@ -1798,12 +1793,7 @@ impl Path { #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] pub fn is_absolute(&self) -> bool { - if cfg!(target_os = "redox") { - // FIXME: Allow Redox prefixes - self.has_root() || has_redox_scheme(self.as_u8_slice()) - } else { - self.has_root() && (cfg!(unix) || self.prefix().is_some()) - } + self.has_root() && (cfg!(all(unix, not(target_os = "redox"))) || self.prefix().is_some()) } /// Returns `true` if the `Path` is relative, i.e. not absolute. @@ -1824,7 +1814,7 @@ impl Path { !self.is_absolute() } - fn prefix(&self) -> Option { + fn prefix(&self) -> Option { self.components().prefix } @@ -2206,8 +2196,7 @@ impl Path { Components { path: self.as_u8_slice(), prefix, - has_physical_root: has_physical_root(self.as_u8_slice(), prefix) || - has_redox_scheme(self.as_u8_slice()), + has_physical_root: has_physical_root(self.as_u8_slice(), prefix), front: State::Prefix, back: State::Body, } diff --git a/src/libstd/sys/redox/path.rs b/src/libstd/sys/redox/path.rs index e6a267dd5d913..aca6729b668e4 100644 --- a/src/libstd/sys/redox/path.rs +++ b/src/libstd/sys/redox/path.rs @@ -23,13 +23,9 @@ pub fn is_verbatim_sep(b: u8) -> bool { pub fn parse_prefix(path: &OsStr) -> Option { if let Some(path_str) = path.to_str() { - if let Some(_i) = path_str.find(':') { - // FIXME: Redox specific prefix - // Some(Prefix::Verbatim(OsStr::new(&path_str[..i]))) - None - } else { - None - } + path_str.split('/').next() + .and_then(|s| s.bytes().position(|v| v == b':')) + .map(|idx| PrefixComponent::from_os_str_kind(OsStr::new(&path_str[..idx + 1]), Prefix::Disk(0))) } else { None } diff --git a/src/libstd/sys/windows/path.rs b/src/libstd/sys/windows/path.rs index 98d62a0c953a6..767e5c33d9ca2 100644 --- a/src/libstd/sys/windows/path.rs +++ b/src/libstd/sys/windows/path.rs @@ -29,7 +29,13 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'\\' } -pub fn parse_prefix<'a>(path: &'a OsStr) -> Option { +pub fn parse_prefix<'a>(path: &'a OsStr) -> Option { + let pathu8 = os_str_as_u8_slice(path); + parse_prefix_simple(path) + .map(|v| PrefixComponent::from_os_str_kind(u8_slice_as_os_str(&pathu8[..v.len()]), v)) +} + +fn parse_prefix_simple<'a>(path: &'a OsStr) -> Option { use path::Prefix::*; unsafe { // The unsafety here stems from converting between &OsStr and &[u8] From 33c88164d96a785362c59978c82dea46d37bea48 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 8 Aug 2018 19:55:38 +0200 Subject: [PATCH 28/33] Implement Mutex and RecursiveMutex --- src/libstd/sys/switch/mutex.rs | 39 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/libstd/sys/switch/mutex.rs b/src/libstd/sys/switch/mutex.rs index d1a9e06781ca8..701dfd70d1846 100644 --- a/src/libstd/sys/switch/mutex.rs +++ b/src/libstd/sys/switch/mutex.rs @@ -11,10 +11,11 @@ // TODO: Properly implement mutex. The switch has multiple syscalls to implement // mutexes properly. -use cell::UnsafeCell; +use megaton_hammer::kernel::sync::Mutex as InternalMutex; +use megaton_hammer::kernel::sync::RMutex as InternalRMutex; pub struct Mutex { - locked: UnsafeCell, + internal: InternalMutex } unsafe impl Send for Mutex {} @@ -22,7 +23,7 @@ unsafe impl Sync for Mutex {} // no threads on wasm impl Mutex { pub const fn new() -> Mutex { - Mutex { locked: UnsafeCell::new(false) } + Mutex { internal: InternalMutex::new() } } #[inline] @@ -31,25 +32,17 @@ impl Mutex { #[inline] pub unsafe fn lock(&self) { - let locked = self.locked.get(); - assert!(!*locked, "cannot recursively acquire mutex"); - *locked = true; + self.internal.lock() } #[inline] pub unsafe fn unlock(&self) { - *self.locked.get() = false; + self.internal.unlock() } #[inline] pub unsafe fn try_lock(&self) -> bool { - let locked = self.locked.get(); - if *locked { - false - } else { - *locked = true; - true - } + self.internal.try_lock() } #[inline] @@ -58,23 +51,33 @@ impl Mutex { } pub struct ReentrantMutex { + internal: InternalRMutex } impl ReentrantMutex { pub unsafe fn uninitialized() -> ReentrantMutex { - ReentrantMutex { } + ReentrantMutex { + internal: InternalRMutex::new() + } } pub unsafe fn init(&mut self) {} - pub unsafe fn lock(&self) {} + pub unsafe fn lock(&self) { + self.internal.lock() + } #[inline] pub unsafe fn try_lock(&self) -> bool { - true + self.internal.try_lock() } - pub unsafe fn unlock(&self) {} + pub unsafe fn unlock(&self) { + self.internal.unlock() + } pub unsafe fn destroy(&self) {} } + +unsafe impl Send for ReentrantMutex {} +unsafe impl Sync for ReentrantMutex {} From cd730e91d8ce34c66610dc967a906ebf879b38d1 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 8 Aug 2018 19:57:20 +0200 Subject: [PATCH 29/33] parse prefix like redox --- src/libstd/sys/switch/path.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/libstd/sys/switch/path.rs b/src/libstd/sys/switch/path.rs index 7ffb3d59216c7..3c15ca5347811 100644 --- a/src/libstd/sys/switch/path.rs +++ b/src/libstd/sys/switch/path.rs @@ -9,7 +9,7 @@ // except according to those terms. use ffi::OsStr; -use path::Prefix; +use path::{PrefixComponent, Prefix}; #[inline] pub fn is_sep_byte(b: u8) -> bool { @@ -21,8 +21,14 @@ pub fn is_verbatim_sep(b: u8) -> bool { b == b'/' } -pub fn parse_prefix(_path: &OsStr) -> Option { - None +pub fn parse_prefix(path: &OsStr) -> Option { + if let Some(path_str) = path.to_str() { + path_str.split('/').next() + .and_then(|s| s.bytes().position(|v| v == b':')) + .map(|idx| PrefixComponent::from_os_str_kind(OsStr::new(&path_str[..idx + 1]), Prefix::Disk(0))) + } else { + None + } } pub const MAIN_SEP_STR: &'static str = "/"; From fd82cd5589647bacaf00fd388715709737d41734 Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 10 Aug 2018 03:33:05 +0200 Subject: [PATCH 30/33] Implement OsStrExt --- src/libstd/os/mod.rs | 2 +- src/libstd/sys/switch/ext/ffi.rs | 65 ++++++++++++++++++++++++++++++++ src/libstd/sys/switch/ext/mod.rs | 15 ++++++++ src/libstd/sys/switch/mod.rs | 1 + 4 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 src/libstd/sys/switch/ext/ffi.rs create mode 100644 src/libstd/sys/switch/ext/mod.rs diff --git a/src/libstd/os/mod.rs b/src/libstd/os/mod.rs index c384ec9168ac4..226851180f259 100644 --- a/src/libstd/os/mod.rs +++ b/src/libstd/os/mod.rs @@ -49,7 +49,7 @@ cfg_if! { #[cfg(target_os = "fuchsia")] pub mod fuchsia; #[cfg(target_os = "hermit")] pub mod hermit; - #[cfg(any(target_os = "redox", unix))] + #[cfg(any(target_os = "switch", target_os = "redox", unix))] #[stable(feature = "rust1", since = "1.0.0")] pub use sys::ext as unix; diff --git a/src/libstd/sys/switch/ext/ffi.rs b/src/libstd/sys/switch/ext/ffi.rs new file mode 100644 index 0000000000000..6f8004e757157 --- /dev/null +++ b/src/libstd/sys/switch/ext/ffi.rs @@ -0,0 +1,65 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Switch-specific extension to the primitives in the `std::ffi` module. + +#![stable(feature = "rust1", since = "1.0.0")] + +use ffi::{OsStr, OsString}; +use mem; +use sys::os_str::Buf; +use sys_common::{FromInner, IntoInner, AsInner}; + +/// Switch-specific extensions to [`OsString`]. +/// +/// [`OsString`]: ../../../../std/ffi/struct.OsString.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStringExt { + /// Creates an `OsString` from a byte vector. + #[stable(feature = "rust1", since = "1.0.0")] + fn from_vec(vec: Vec) -> Self; + + /// Yields the underlying byte vector of this `OsString`. + #[stable(feature = "rust1", since = "1.0.0")] + fn into_vec(self) -> Vec; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStringExt for OsString { + fn from_vec(vec: Vec) -> OsString { + FromInner::from_inner(Buf { inner: vec }) + } + fn into_vec(self) -> Vec { + self.into_inner().inner + } +} + +/// Switch-specific extensions to [`OsStr`]. +/// +/// [`OsStr`]: ../../../../std/ffi/struct.OsStr.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait OsStrExt { + #[stable(feature = "rust1", since = "1.0.0")] + fn from_bytes(slice: &[u8]) -> &Self; + + /// Gets the underlying byte view of the `OsStr` slice. + #[stable(feature = "rust1", since = "1.0.0")] + fn as_bytes(&self) -> &[u8]; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl OsStrExt for OsStr { + fn from_bytes(slice: &[u8]) -> &OsStr { + unsafe { mem::transmute(slice) } + } + fn as_bytes(&self) -> &[u8] { + &self.as_inner().inner + } +} diff --git a/src/libstd/sys/switch/ext/mod.rs b/src/libstd/sys/switch/ext/mod.rs new file mode 100644 index 0000000000000..fb0ae6df8d331 --- /dev/null +++ b/src/libstd/sys/switch/ext/mod.rs @@ -0,0 +1,15 @@ +//! Experimental extensions to `std` for the Switch. + +#![stable(feature = "rust1", since = "1.0.0")] +#![doc(cfg(target_os = "switch"))] + +pub mod ffi; + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +#[stable(feature = "rust1", since = "1.0.0")] +pub mod prelude { + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::ffi::{OsStrExt, OsStringExt}; +} diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index 042558c115e9a..46affe3612a45 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -29,6 +29,7 @@ pub mod backtrace; pub mod cmath; pub mod condvar; pub mod env; +pub mod ext; pub mod fs; pub mod memchr; pub mod mutex; From 935b7faca673e03206da8faa7e221ab46e5ead4f Mon Sep 17 00:00:00 2001 From: roblabla Date: Fri, 10 Aug 2018 03:35:04 +0200 Subject: [PATCH 31/33] First implementation of FS --- src/libstd/Cargo.toml | 1 + src/libstd/lib.rs | 5 + src/libstd/sys/switch/fs.rs | 542 +++++++++++++++++++++++-------- src/libstd/sys/switch/mod.rs | 13 + src/libstd/sys/switch/os.rs | 3 +- src/libstd/sys/switch/process.rs | 2 +- 6 files changed, 424 insertions(+), 142 deletions(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index e1eade338bb76..e763c671400b9 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -29,6 +29,7 @@ alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] megaton-allocator = { git = "https://github.com/megatonhammer/megaton-hammer" } megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } +lazy_static = { version = "1.1.0", features = ["spin_no_std"] } gimli = { git = "https://github.com/roblabla/gimli", branch = "megaton", default-features = false, features = ["alloc"] } fallible-iterator = { version = "0.1", default-features = false, features = ["alloc"] } addr2line = { git = "https://github.com/roblabla/addr2line", branch = "megaton", default-features = false, features = ["alloc"] } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 419ad382e0e0a..4baeca7d94585 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -276,6 +276,7 @@ #![feature(macro_vis_matcher)] #![feature(needs_panic_runtime)] #![feature(never_type)] +#![feature(no_more_cas)] #![feature(exhaustive_patterns)] #![feature(num_bits_bytes)] #![feature(old_wrapping)] @@ -368,6 +369,10 @@ extern crate alloc_system; extern crate megaton_hammer; #[cfg(target_os = "switch")] extern crate megaton_allocator; +#[cfg(target_os = "switch")] +#[macro_use] +extern crate lazy_static; + #[doc(masked)] extern crate libc; diff --git a/src/libstd/sys/switch/fs.rs b/src/libstd/sys/switch/fs.rs index f679355dbee75..584077ad66d00 100644 --- a/src/libstd/sys/switch/fs.rs +++ b/src/libstd/sys/switch/fs.rs @@ -12,137 +12,105 @@ use ffi::OsString; use fmt; use hash::{Hash, Hasher}; use io::{self, SeekFrom}; -use path::{Path, PathBuf}; +use path::{Path, PathBuf, Component}; use sys::time::SystemTime; use sys::{unsupported, Void}; +use sys::os::getcwd; +//use sync::Mutex; +use megaton_hammer::ipcdefs::nn::fssrv::sf::IFileSystemProxy; +use megaton_hammer::kernel::Session; +use megaton_hammer::error::Result as MTHResult; -pub struct File(Void); +pub struct File(Box); -pub struct FileAttr(Void); +#[derive(Debug, Clone)] +pub struct FileAttr { + size: u64, + perm: FilePermissions, + file_type: FileType +} + +#[derive(Debug)] +pub struct ReadDir(Box>>); -pub struct ReadDir(Void); +trait ReadDirOps : Iterator + fmt::Debug {} -pub struct DirEntry(Void); +pub struct DirEntry { + path: PathBuf, + file_name: OsString, + metadata: FileAttr, + file_type: FileType +} #[derive(Clone, Debug)] -pub struct OpenOptions { } +pub struct OpenOptions { + read: bool, + write: bool, + append: bool, + truncate: bool, + create: bool, + create_new: bool +} -pub struct FilePermissions(Void); +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct FilePermissions; -pub struct FileType(Void); +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum FileType { + File, + Directory +} #[derive(Debug)] pub struct DirBuilder { } impl FileAttr { pub fn size(&self) -> u64 { - match self.0 {} + self.size } pub fn perm(&self) -> FilePermissions { - match self.0 {} + self.perm } pub fn file_type(&self) -> FileType { - match self.0 {} + self.file_type } pub fn modified(&self) -> io::Result { - match self.0 {} + unsupported() } pub fn accessed(&self) -> io::Result { - match self.0 {} + unsupported() } pub fn created(&self) -> io::Result { - match self.0 {} - } -} - -impl Clone for FileAttr { - fn clone(&self) -> FileAttr { - match self.0 {} + unsupported() } } impl FilePermissions { pub fn readonly(&self) -> bool { - match self.0 {} + false } pub fn set_readonly(&mut self, _readonly: bool) { - match self.0 {} - } -} - -impl Clone for FilePermissions { - fn clone(&self) -> FilePermissions { - match self.0 {} - } -} - -impl PartialEq for FilePermissions { - fn eq(&self, _other: &FilePermissions) -> bool { - match self.0 {} - } -} - -impl Eq for FilePermissions { -} - -impl fmt::Debug for FilePermissions { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - match self.0 {} + // TODO } } impl FileType { pub fn is_dir(&self) -> bool { - match self.0 {} + *self == FileType::Directory } pub fn is_file(&self) -> bool { - match self.0 {} + *self == FileType::File } pub fn is_symlink(&self) -> bool { - match self.0 {} - } -} - -impl Clone for FileType { - fn clone(&self) -> FileType { - match self.0 {} - } -} - -impl Copy for FileType {} - -impl PartialEq for FileType { - fn eq(&self, _other: &FileType) -> bool { - match self.0 {} - } -} - -impl Eq for FileType { -} - -impl Hash for FileType { - fn hash(&self, _h: &mut H) { - match self.0 {} - } -} - -impl fmt::Debug for FileType { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - match self.0 {} - } -} - -impl fmt::Debug for ReadDir { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - match self.0 {} + false } } @@ -150,88 +118,332 @@ impl Iterator for ReadDir { type Item = io::Result; fn next(&mut self) -> Option> { - match self.0 {} + self.0.next() } } impl DirEntry { pub fn path(&self) -> PathBuf { - match self.0 {} + self.path.clone() } pub fn file_name(&self) -> OsString { - match self.0 {} + self.file_name.clone() } pub fn metadata(&self) -> io::Result { - match self.0 {} + Ok(self.metadata.clone()) } pub fn file_type(&self) -> io::Result { - match self.0 {} + Ok(self.file_type.clone()) } } impl OpenOptions { pub fn new() -> OpenOptions { - OpenOptions { } + OpenOptions { + read: false, + write: false, + append: false, + truncate: false, + create: false, + create_new: false + } + } + + pub fn read(&mut self, read: bool) { + self.read = read; + } + pub fn write(&mut self, write: bool) { + self.write = write; + } + pub fn append(&mut self, append: bool) { + self.append = append; + } + pub fn truncate(&mut self, truncate: bool) { + self.truncate = truncate; + } + pub fn create(&mut self, create: bool) { + self.create = create; + } + pub fn create_new(&mut self, create_new: bool) { + self.create_new = create_new; + } +} + +trait FilesystemOps { + fn open(&self, path: &Path, opts: &OpenOptions) -> io::Result>; + fn readdir(&self, p: &Path) -> io::Result; + fn unlink(&self, p: &Path) -> io::Result<()>; + fn rename(&self, old: &Path, _new: &Path) -> io::Result<()>; + fn set_perm(&self, p: &Path, perm: FilePermissions) -> io::Result<()>; + fn rmdir(&self, p: &Path) -> io::Result<()>; + fn remove_dir_all(&self, path: &Path) -> io::Result<()>; + fn readlink(&self, p: &Path) -> io::Result; + fn symlink(&self, src: &Path, dst: &Path) -> io::Result<()>; + fn link(&self, src: &Path, dst: &Path) -> io::Result<()>; + fn stat(&self, p: &Path) -> io::Result; + fn lstat(&self, p: &Path) -> io::Result; + fn canonicalize(&self, p: &Path) -> io::Result; +} + +trait FileOps { + fn file_attr(&self) -> io::Result; + fn fsync(&self) -> io::Result<()>; + fn datasync(&self) -> io::Result<()>; + fn truncate(&self, size: u64) -> io::Result<()>; + fn read(&self, buf: &mut [u8]) -> io::Result; + fn write(&self, buf: &[u8]) -> io::Result; + fn flush(&self) -> io::Result<()>; + fn seek(&self, pos: SeekFrom) -> io::Result; + // Trait objects: HOW? + //fn duplicate(&self) -> io::Result; + fn set_permissions(&self, perm: FilePermissions) -> io::Result<()>; +} + +mod fspsrv { + use io::{self, ErrorKind}; + use super::{FilesystemOps, FileOps, OpenOptions, FileAttr, SeekFrom, FilePermissions, ReadDir}; + use path::{Path, PathBuf}; + use megaton_hammer::ipcdefs::nn::fssrv::sf::{IFile, IFileSystem}; + use megaton_hammer::kernel::Object; + use sync::atomic::{AtomicU64, Ordering}; + use sys::switch::unsupported; + use sys::ext::ffi::OsStrExt; + use core::slice; + + pub struct FspSrvFs(IFileSystem); + + pub struct FspSrvFile { + internal: IFile, + offset: AtomicU64 + } + + impl FspSrvFs { + pub fn new(fs: IFileSystem) -> FspSrvFs { + FspSrvFs(fs) + } + } + + impl FilesystemOps for FspSrvFs { + fn open(&self, path: &Path, opts: &OpenOptions) -> io::Result> { + let mut arr = [0u8; 0x301]; + let path_as_bytes = path.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + let mut mode = 0; + if opts.read { + mode |= 1; + } + if opts.write { + mode |= 1 << 1 | 1 << 2; + } + if opts.create || opts.create_new { + let err = self.0.create_file(0, 0, &arr); + match (opts.create_new, err) { + (false, Err(err)) if io::Error::from(err).kind() == ErrorKind::AlreadyExists => (), + (_, err) => err? + } + } + let file = self.0.open_file(mode, &arr)?; + if opts.truncate { + file.set_size(0)?; + } + let offset = if opts.append { + file.get_size()? + } else { + 0 + }; + Ok(Box::new(FspSrvFile { + internal: file, + offset: AtomicU64::new(offset) + })) + } + fn readdir(&self, p: &Path) -> io::Result { + unsupported() + } + fn unlink(&self, p: &Path) -> io::Result<()> { + unsupported() + } + fn rename(&self, old: &Path, _new: &Path) -> io::Result<()> { + unsupported() + } + fn set_perm(&self, p: &Path, perm: FilePermissions) -> io::Result<()> { + unsupported() + } + fn rmdir(&self, p: &Path) -> io::Result<()> { + unsupported() + } + fn remove_dir_all(&self, path: &Path) -> io::Result<()> { + unsupported() + } + fn readlink(&self, p: &Path) -> io::Result { + unsupported() + } + fn symlink(&self, src: &Path, dst: &Path) -> io::Result<()> { + unsupported() + } + fn link(&self, src: &Path, dst: &Path) -> io::Result<()> { + unsupported() + } + fn stat(&self, p: &Path) -> io::Result { + unsupported() + } + fn lstat(&self, p: &Path) -> io::Result { + unsupported() + } + fn canonicalize(&self, p: &Path) -> io::Result { + unsupported() + } + } + + impl FileOps for FspSrvFile { + fn file_attr(&self) -> io::Result { + unsupported() + } + fn fsync(&self) -> io::Result<()> { + unsupported() + } + fn datasync(&self) -> io::Result<()> { + unsupported() + } + fn truncate(&self, size: u64) -> io::Result<()> { + self.internal.set_size(size)?; + Ok(()) + } + fn read(&self, buf: &mut [u8]) -> io::Result { + // TODO: Maybe I should lock the file on read/write? + let buf = unsafe { + // Safety: Do I really need to explain why &[u8] to &[i8] is legal? + slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut i8, buf.len()) + }; + let read_size = self.internal.read(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; + self.offset.fetch_add(read_size, Ordering::SeqCst); + Ok(read_size as usize) + } + fn write(&self, buf: &[u8]) -> io::Result { + // TODO: Maybe I should lock the file on read/write? + // TODO: In append mode, should I ignore offset and just write from + // the end? + let buf = unsafe { + // Safety: Do I really need to explain why &[u8] to &[i8] is legal? + slice::from_raw_parts(buf.as_ptr() as *const i8, buf.len()) + }; + self.internal.write(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; + self.offset.fetch_add(buf.len() as u64, Ordering::SeqCst); + Ok(buf.len()) + } + fn flush(&self) -> io::Result<()> { + unsupported() + } + fn seek(&self, pos: SeekFrom) -> io::Result { + let newpos = match pos { + SeekFrom::Current(pos) => { + self.offset.fetch_update(|v| { + let newval = v as i64 + pos; + if newval < 0 { + None + } else { + Some(newval as u64) + } + }, Ordering::SeqCst, Ordering::SeqCst).map_err(|_| io::Error::from(ErrorKind::InvalidInput))? + } + SeekFrom::Start(pos) => { + self.offset.store(pos, Ordering::SeqCst); + pos + }, + SeekFrom::End(pos) => { + let size = self.internal.get_size()?; + let newpos = size as i64 + pos; + if newpos < 0 { + Err(io::Error::from(ErrorKind::InvalidInput))? + } + self.offset.store(newpos as u64, Ordering::SeqCst); + newpos as u64 + } + }; + Ok(newpos) + } + /*fn duplicate(&self) -> io::Result> { + unsupported() + }*/ + fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + unsupported() + } + } +} + +use self::fspsrv::FspSrvFs; + +lazy_static! { + static ref SDMC: MTHResult> = { + let ifs = IFileSystemProxy::new(|init| init(0))?; + let sdcard = ifs.mount_sd_card()?; + Ok(FspSrvFs::new(sdcard)) + }; +} + +fn get_filesystem(path: &Path) -> io::Result<(&'static FilesystemOps, &Path)> { + assert!(path.is_absolute(), "CWD is not absolute ?"); + let mut iter = path.components(); + let prefix = match iter.next() { + Some(Component::Prefix(prefix)) => prefix.as_os_str(), + _ => panic!("If path is absolute, it should start with prefix") + }; + if prefix == "sdmc:" { + Ok(((&*SDMC).as_ref().map_err(|v| *v)?, &iter.as_path())) + } else { + unsupported() } - - pub fn read(&mut self, _read: bool) { } - pub fn write(&mut self, _write: bool) { } - pub fn append(&mut self, _append: bool) { } - pub fn truncate(&mut self, _truncate: bool) { } - pub fn create(&mut self, _create: bool) { } - pub fn create_new(&mut self, _create_new: bool) { } } impl File { - pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result { - unsupported() + pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { + let path = getcwd()?.join(path); + let (fs, path) = get_filesystem(&path)?; + Ok(File(fs.open(path, opts)?)) } pub fn file_attr(&self) -> io::Result { - match self.0 {} + self.0.file_attr() } pub fn fsync(&self) -> io::Result<()> { - match self.0 {} + self.0.fsync() } pub fn datasync(&self) -> io::Result<()> { - match self.0 {} + self.0.datasync() } - pub fn truncate(&self, _size: u64) -> io::Result<()> { - match self.0 {} + pub fn truncate(&self, size: u64) -> io::Result<()> { + self.0.truncate(size) } - pub fn read(&self, _buf: &mut [u8]) -> io::Result { - match self.0 {} + pub fn read(&self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) } - pub fn write(&self, _buf: &[u8]) -> io::Result { - match self.0 {} + pub fn write(&self, buf: &[u8]) -> io::Result { + self.0.write(buf) } pub fn flush(&self) -> io::Result<()> { - match self.0 {} + self.0.flush() } - pub fn seek(&self, _pos: SeekFrom) -> io::Result { - match self.0 {} + pub fn seek(&self, pos: SeekFrom) -> io::Result { + self.0.seek(pos) } pub fn duplicate(&self) -> io::Result { - match self.0 {} - } - - pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> { - match self.0 {} + // Trait objects are not fun + // Ok(File(self.0.duplicate()?)) + unsupported() } - pub fn diverge(&self) -> ! { - match self.0 {} + pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + self.0.set_permissions(perm) } } @@ -246,40 +458,72 @@ impl DirBuilder { } impl fmt::Debug for File { - fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result { - match self.0 {} + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "File") } } -pub fn readdir(_p: &Path) -> io::Result { - unsupported() +pub fn readdir(p: &Path) -> io::Result { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.readdir(path) } -pub fn unlink(_p: &Path) -> io::Result<()> { - unsupported() +pub fn unlink(p: &Path) -> io::Result<()> { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.unlink(path) } -pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> { - unsupported() +pub fn rename(old: &Path, new: &Path) -> io::Result<()> { + // Guarantee old and new have the same prefix + let cwd = getcwd()?; + let old = cwd.join(old); + let new = cwd.join(new); + let prefix_old = match old.components().next() { + Some(Component::Prefix(prefix)) => prefix.as_os_str(), + _ => panic!("If path is absolute, it should start with prefix") + }; + let mut newpath_iter = new.components(); + let prefix_new = match newpath_iter.next() { + Some(Component::Prefix(prefix)) => prefix.as_os_str(), + _ => panic!("If path is absolute, it should start with prefix") + }; + + if prefix_old != prefix_new { + // TODO: MTH error + return Err(io::Error::from(io::ErrorKind::Other)); + } + let (fs, oldpath) = get_filesystem(&old)?; + let newpath = newpath_iter.as_path(); + fs.rename(&old, newpath) } -pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> { - match perm.0 {} +pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.set_perm(path, perm) } -pub fn rmdir(_p: &Path) -> io::Result<()> { - unsupported() +pub fn rmdir(p: &Path) -> io::Result<()> { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.rmdir(path) } -pub fn remove_dir_all(_path: &Path) -> io::Result<()> { - unsupported() +pub fn remove_dir_all(p: &Path) -> io::Result<()> { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.remove_dir_all(path) } -pub fn readlink(_p: &Path) -> io::Result { - unsupported() +pub fn readlink(p: &Path) -> io::Result { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.readlink(path) } -pub fn symlink(_src: &Path, _dst: &Path) -> io::Result<()> { +pub fn symlink(src: &Path, dst: &Path) -> io::Result<()> { unsupported() } @@ -287,18 +531,36 @@ pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> { unsupported() } -pub fn stat(_p: &Path) -> io::Result { - unsupported() +pub fn stat(p: &Path) -> io::Result { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.stat(path) } -pub fn lstat(_p: &Path) -> io::Result { - unsupported() +pub fn lstat(p: &Path) -> io::Result { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.lstat(path) } -pub fn canonicalize(_p: &Path) -> io::Result { - unsupported() +pub fn canonicalize(p: &Path) -> io::Result { + let path = getcwd()?.join(p); + let (fs, path) = get_filesystem(&path)?; + fs.canonicalize(path) } -pub fn copy(_from: &Path, _to: &Path) -> io::Result { - unsupported() +pub fn copy(from: &Path, to: &Path) -> io::Result { + use fs::File; + if !from.is_file() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "the source path is not an existing regular file")) + } + + let mut reader = File::open(from)?; + let mut writer = File::create(to)?; + let perm = reader.metadata()?.permissions(); + + let ret = io::copy(&mut reader, &mut writer)?; + writer.set_permissions(perm)?; + Ok(ret) } diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index 46affe3612a45..7cd8b256121df 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -22,6 +22,7 @@ extern crate runwind; use io::{self, ErrorKind}; +use megaton_hammer::error::Module; pub mod args; #[cfg(feature = "backtrace")] @@ -30,6 +31,7 @@ pub mod cmath; pub mod condvar; pub mod env; pub mod ext; +pub mod fast_thread_local; pub mod fs; pub mod memchr; pub mod mutex; @@ -131,3 +133,14 @@ pub unsafe fn strlen(mut s: *const i8) -> usize { pub fn hashmap_random_keys() -> (u64, u64) { (1, 2) } + +#[stable(feature = "rust1", since = "1.0.0")] +impl From<::megaton_hammer::error::Error> for io::Error { + fn from(err: ::megaton_hammer::error::Error) -> io::Error { + match (err.module(), err.description_id()) { + (Ok(Module::FS), 2) => io::Error::from(io::ErrorKind::AlreadyExists), + //(Ok(Module::FS), 1002) => io::Error::from(io::ErrorKind::NotFound), + _ => io::Error::new(io::ErrorKind::Other, Box::new(err)) + } + } +} diff --git a/src/libstd/sys/switch/os.rs b/src/libstd/sys/switch/os.rs index 0c11e73ca7cf0..762de1f51dbd3 100644 --- a/src/libstd/sys/switch/os.rs +++ b/src/libstd/sys/switch/os.rs @@ -30,8 +30,9 @@ pub fn error_string(_errno: i32) -> String { "unknown error".to_string() } +//static CWD: Mutex = Mutex::new(PathBuf::new("romfs:/")); pub fn getcwd() -> io::Result { - unsupported() + Ok(PathBuf::from("romfs:/")) } pub fn chdir(_p: &path::Path) -> io::Result<()> { diff --git a/src/libstd/sys/switch/process.rs b/src/libstd/sys/switch/process.rs index 5aaa27840a794..8ea425a97f946 100644 --- a/src/libstd/sys/switch/process.rs +++ b/src/libstd/sys/switch/process.rs @@ -78,7 +78,7 @@ impl From for Stdio { impl From for Stdio { fn from(file: File) -> Stdio { - file.diverge() + unimplemented!() } } From 16a6a22a30e16c556db06751b854d4507a420297 Mon Sep 17 00:00:00 2001 From: roblabla Date: Mon, 3 Sep 2018 13:41:56 +0000 Subject: [PATCH 32/33] Fix for newer megatonhammer, and various fs impls --- src/libstd/Cargo.toml | 2 +- src/libstd/sys/switch/fs.rs | 169 +++++++++++++++++++++++++++-------- src/libstd/sys/switch/mod.rs | 74 ++++++++------- src/libstd/sys/switch/net.rs | 22 ++--- 4 files changed, 175 insertions(+), 92 deletions(-) diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index e763c671400b9..0d60740006277 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -28,7 +28,7 @@ alloc_system = { path = "../liballoc_system" } [target.'cfg(target_os = "switch")'.dependencies] megaton-allocator = { git = "https://github.com/megatonhammer/megaton-hammer" } -megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", features = ["std"] } +megaton-hammer = { git = "https://github.com/megatonhammer/megaton-hammer", default-features = false, features = ["instd"] } lazy_static = { version = "1.1.0", features = ["spin_no_std"] } gimli = { git = "https://github.com/roblabla/gimli", branch = "megaton", default-features = false, features = ["alloc"] } fallible-iterator = { version = "0.1", default-features = false, features = ["alloc"] } diff --git a/src/libstd/sys/switch/fs.rs b/src/libstd/sys/switch/fs.rs index 584077ad66d00..7c464502c578b 100644 --- a/src/libstd/sys/switch/fs.rs +++ b/src/libstd/sys/switch/fs.rs @@ -39,7 +39,6 @@ pub struct DirEntry { path: PathBuf, file_name: OsString, metadata: FileAttr, - file_type: FileType } #[derive(Clone, Debug)] @@ -136,7 +135,7 @@ impl DirEntry { } pub fn file_type(&self) -> io::Result { - Ok(self.file_type.clone()) + Ok(self.metadata.file_type) } } @@ -181,8 +180,6 @@ trait FilesystemOps { fn rmdir(&self, p: &Path) -> io::Result<()>; fn remove_dir_all(&self, path: &Path) -> io::Result<()>; fn readlink(&self, p: &Path) -> io::Result; - fn symlink(&self, src: &Path, dst: &Path) -> io::Result<()>; - fn link(&self, src: &Path, dst: &Path) -> io::Result<()>; fn stat(&self, p: &Path) -> io::Result; fn lstat(&self, p: &Path) -> io::Result; fn canonicalize(&self, p: &Path) -> io::Result; @@ -204,14 +201,16 @@ trait FileOps { mod fspsrv { use io::{self, ErrorKind}; - use super::{FilesystemOps, FileOps, OpenOptions, FileAttr, SeekFrom, FilePermissions, ReadDir}; - use path::{Path, PathBuf}; - use megaton_hammer::ipcdefs::nn::fssrv::sf::{IFile, IFileSystem}; + use super::{FilesystemOps, FileOps, OpenOptions, FileAttr, SeekFrom, FilePermissions, ReadDir, FileType,DirEntry, ReadDirOps}; + use path::{Path, PathBuf, Component}; + use ffi::OsStr; + use megaton_hammer::ipcdefs::nn::fssrv::sf::{IFile, IDirectory, IFileSystem, IDirectoryEntry, DirectoryEntryType}; use megaton_hammer::kernel::Object; use sync::atomic::{AtomicU64, Ordering}; use sys::switch::unsupported; use sys::ext::ffi::OsStrExt; use core::slice; + use core::fmt::Debug; pub struct FspSrvFs(IFileSystem); @@ -226,7 +225,52 @@ mod fspsrv { } } - impl FilesystemOps for FspSrvFs { + #[derive(Debug)] + pub struct FspReadDir { + internal: IDirectory, + parent: PathBuf + } + + impl ReadDirOps for FspReadDir {} + impl Iterator for FspReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + let mut entry: [IDirectoryEntry; 1] = [IDirectoryEntry { + path: [0; 0x300], + unk1: 0, + directory_entry_type: DirectoryEntryType::File, + filesize: 0, + }]; + match self.internal.read(&mut entry) { + Ok(0) => return None, + Err(err) => return Some(Err(err.into())), + Ok(n) => () + } + let size = entry[0].path.iter().position(|c| *c == b'\0').unwrap_or(0x300); + let file_name = OsStr::from_bytes(&entry[0].path[..size]); + Some(Ok(DirEntry { + path: self.parent.join(file_name), + file_name: file_name.into(), + metadata: FileAttr { + size: entry[0].filesize, + perm: FilePermissions, + file_type: entry[0].directory_entry_type.into() + }, + })) + } + } + + impl From for FileType { + fn from(ty: DirectoryEntryType) -> FileType { + match ty { + DirectoryEntryType::File => FileType::File, + DirectoryEntryType::Directory => FileType::Directory + } + } + } + + impl FilesystemOps for FspSrvFs { fn open(&self, path: &Path, opts: &OpenOptions) -> io::Result> { let mut arr = [0u8; 0x301]; let path_as_bytes = path.as_os_str().as_bytes(); @@ -260,46 +304,96 @@ mod fspsrv { })) } fn readdir(&self, p: &Path) -> io::Result { - unsupported() + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + Ok(ReadDir(Box::new(FspReadDir { + internal: self.0.open_directory(3, &arr)?, + parent: p.into() + }))) } fn unlink(&self, p: &Path) -> io::Result<()> { - unsupported() + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.delete_file(&arr)?; + Ok(()) } - fn rename(&self, old: &Path, _new: &Path) -> io::Result<()> { - unsupported() + fn rename(&self, old: &Path, new: &Path) -> io::Result<()> { + let mut oldarr = [0u8; 0x301]; + let path_as_bytes = old.as_os_str().as_bytes(); + (&mut oldarr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + + let mut newarr = [0u8; 0x301]; + let path_as_bytes = new.as_os_str().as_bytes(); + (&mut newarr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.rename_file(&oldarr, &newarr)?; + Ok(()) } fn set_perm(&self, p: &Path, perm: FilePermissions) -> io::Result<()> { unsupported() } fn rmdir(&self, p: &Path) -> io::Result<()> { - unsupported() + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.delete_directory(&arr)?; + Ok(()) } - fn remove_dir_all(&self, path: &Path) -> io::Result<()> { - unsupported() + fn remove_dir_all(&self, p: &Path) -> io::Result<()> { + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.delete_directory_recursively(&arr)?; + Ok(()) } fn readlink(&self, p: &Path) -> io::Result { unsupported() } - fn symlink(&self, src: &Path, dst: &Path) -> io::Result<()> { - unsupported() - } - fn link(&self, src: &Path, dst: &Path) -> io::Result<()> { - unsupported() - } fn stat(&self, p: &Path) -> io::Result { - unsupported() + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + + let entry_type = self.0.get_entry_type(&arr)?; + + let size = match entry_type { + DirectoryEntryType::File => { + self.0.open_file(0, &arr)?.get_size()? + }, + DirectoryEntryType::Directory => 0 + }; + Ok(FileAttr { + size, + perm: FilePermissions, + file_type: entry_type.into() + }) } fn lstat(&self, p: &Path) -> io::Result { unsupported() } fn canonicalize(&self, p: &Path) -> io::Result { - unsupported() + let mut fullpath = PathBuf::from("/"); + for component in p.components() { + match component { + Component::Prefix(p) => panic!("Shouldn't obtain a prefix in inner fs impl"), + Component::RootDir => fullpath.push("/"), + Component::CurDir => (), + Component::ParentDir => { fullpath.pop(); }, + Component::Normal(p) => fullpath.push(p) + } + } + Ok(p.into()) } } - impl FileOps for FspSrvFile { + impl FileOps for FspSrvFile { fn file_attr(&self) -> io::Result { - unsupported() + Ok(FileAttr { + size: self.internal.get_size()?, + perm: FilePermissions, + file_type: FileType::File + }) } fn fsync(&self) -> io::Result<()> { unsupported() @@ -313,10 +407,6 @@ mod fspsrv { } fn read(&self, buf: &mut [u8]) -> io::Result { // TODO: Maybe I should lock the file on read/write? - let buf = unsafe { - // Safety: Do I really need to explain why &[u8] to &[i8] is legal? - slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut i8, buf.len()) - }; let read_size = self.internal.read(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; self.offset.fetch_add(read_size, Ordering::SeqCst); Ok(read_size as usize) @@ -325,10 +415,6 @@ mod fspsrv { // TODO: Maybe I should lock the file on read/write? // TODO: In append mode, should I ignore offset and just write from // the end? - let buf = unsafe { - // Safety: Do I really need to explain why &[u8] to &[i8] is legal? - slice::from_raw_parts(buf.as_ptr() as *const i8, buf.len()) - }; self.internal.write(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; self.offset.fetch_add(buf.len() as u64, Ordering::SeqCst); Ok(buf.len()) @@ -378,7 +464,7 @@ use self::fspsrv::FspSrvFs; lazy_static! { static ref SDMC: MTHResult> = { let ifs = IFileSystemProxy::new(|init| init(0))?; - let sdcard = ifs.mount_sd_card()?; + let sdcard = ifs.open_sd_card_file_system()?; Ok(FspSrvFs::new(sdcard)) }; } @@ -399,7 +485,7 @@ fn get_filesystem(path: &Path) -> io::Result<(&'static FilesystemOps, &Path)> { impl File { pub fn open(path: &Path, opts: &OpenOptions) -> io::Result { - let path = getcwd()?.join(path); + let path = getcwd()?.join(path); let (fs, path) = get_filesystem(&path)?; Ok(File(fs.open(path, opts)?)) } @@ -545,8 +631,17 @@ pub fn lstat(p: &Path) -> io::Result { pub fn canonicalize(p: &Path) -> io::Result { let path = getcwd()?.join(p); - let (fs, path) = get_filesystem(&path)?; - fs.canonicalize(path) + let (fs, fs_path) = get_filesystem(&path)?; + let canonicalized = fs.canonicalize(fs_path)?; + + let mut iter = path.components(); + let prefix = match iter.next() { + Some(Component::Prefix(prefix)) => prefix.as_os_str(), + _ => panic!("If path is absolute, it should start with prefix") + }; + let mut ret = PathBuf::from(prefix); + ret.push(canonicalized); + Ok(ret) } pub fn copy(from: &Path, to: &Path) -> io::Result { diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index 7cd8b256121df..6dd7aa3860c35 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -22,7 +22,7 @@ extern crate runwind; use io::{self, ErrorKind}; -use megaton_hammer::error::Module; +use megaton_hammer::error::{Error, Module}; pub mod args; #[cfg(feature = "backtrace")] @@ -61,45 +61,47 @@ pub fn unsupported_err() -> io::Error { } pub fn decode_error_kind(errno: i32) -> ErrorKind { - // Taken from linux, which is what net seems to use. mod linux { - pub const ECONNREFUSED: i32 = 111; - pub const ECONNRESET: i32 = 104; - pub const EPERM: i32 = 1; - pub const EACCES: i32 = 13; - pub const EPIPE: i32 = 32; - pub const ENOTCONN: i32 = 107; - pub const ECONNABORTED: i32 = 103; - pub const EADDRNOTAVAIL: i32 = 99; - pub const EADDRINUSE: i32 = 98; - pub const ENOENT: i32 = 2; - pub const EINTR: i32 = 4; - pub const EINVAL: i32 = 22; - pub const ETIMEDOUT: i32 = 110; - pub const EEXIST: i32 = 17; - pub const EAGAIN: i32 = 11; - pub const EWOULDBLOCK: i32 = EAGAIN; + pub const ECONNREFUSED: u32 = 111; + pub const ECONNRESET: u32 = 104; + pub const EPERM: u32 = 1; + pub const EACCES: u32 = 13; + pub const EPIPE: u32 = 32; + pub const ENOTCONN: u32 = 107; + pub const ECONNABORTED: u32 = 103; + pub const EADDRNOTAVAIL: u32 = 99; + pub const EADDRINUSE: u32 = 98; + pub const ENOENT: u32 = 2; + pub const EINTR: u32 = 4; + pub const EINVAL: u32 = 22; + pub const ETIMEDOUT: u32 = 110; + pub const EEXIST: u32 = 17; + pub const EAGAIN: u32 = 11; + pub const EWOULDBLOCK: u32 = EAGAIN; } - match errno { - linux::ECONNREFUSED => ErrorKind::ConnectionRefused, - linux::ECONNRESET => ErrorKind::ConnectionReset, - linux::EPERM | linux::EACCES => ErrorKind::PermissionDenied, - linux::EPIPE => ErrorKind::BrokenPipe, - linux::ENOTCONN => ErrorKind::NotConnected, - linux::ECONNABORTED => ErrorKind::ConnectionAborted, - linux::EADDRNOTAVAIL => ErrorKind::AddrNotAvailable, - linux::EADDRINUSE => ErrorKind::AddrInUse, - linux::ENOENT => ErrorKind::NotFound, - linux::EINTR => ErrorKind::Interrupted, - linux::EINVAL => ErrorKind::InvalidInput, - linux::ETIMEDOUT => ErrorKind::TimedOut, - linux::EEXIST => ErrorKind::AlreadyExists, + // Taken from linux, which is what net seems to use. + let err = Error(errno as u32); + match (err.module(), err.description_id()) { + (Ok(Module::MegatonHammerLinux), linux::ECONNREFUSED) => ErrorKind::ConnectionRefused, + (Ok(Module::MegatonHammerLinux), linux::ECONNRESET) => ErrorKind::ConnectionReset, + (Ok(Module::MegatonHammerLinux), linux::EPERM) | + (Ok(Module::MegatonHammerLinux), linux::EACCES) => ErrorKind::PermissionDenied, + (Ok(Module::MegatonHammerLinux), linux::EPIPE) => ErrorKind::BrokenPipe, + (Ok(Module::MegatonHammerLinux), linux::ENOTCONN) => ErrorKind::NotConnected, + (Ok(Module::MegatonHammerLinux), linux::ECONNABORTED) => ErrorKind::ConnectionAborted, + (Ok(Module::MegatonHammerLinux), linux::EADDRNOTAVAIL) => ErrorKind::AddrNotAvailable, + (Ok(Module::MegatonHammerLinux), linux::EADDRINUSE) => ErrorKind::AddrInUse, + (Ok(Module::MegatonHammerLinux), linux::ENOENT) => ErrorKind::NotFound, + (Ok(Module::MegatonHammerLinux), linux::EINTR) => ErrorKind::Interrupted, + (Ok(Module::MegatonHammerLinux), linux::EINVAL) => ErrorKind::InvalidInput, + (Ok(Module::MegatonHammerLinux), linux::ETIMEDOUT) => ErrorKind::TimedOut, + (Ok(Module::MegatonHammerLinux), linux::EEXIST) => ErrorKind::AlreadyExists, // These two constants can have the same value on some systems, // but different values on others, so we can't use a match // clause - x if x == linux::EAGAIN || x == linux::EWOULDBLOCK => + (Ok(Module::MegatonHammerLinux), x) if x == linux::EAGAIN || x == linux::EWOULDBLOCK => ErrorKind::WouldBlock, _ => ErrorKind::Other, @@ -137,10 +139,6 @@ pub fn hashmap_random_keys() -> (u64, u64) { #[stable(feature = "rust1", since = "1.0.0")] impl From<::megaton_hammer::error::Error> for io::Error { fn from(err: ::megaton_hammer::error::Error) -> io::Error { - match (err.module(), err.description_id()) { - (Ok(Module::FS), 2) => io::Error::from(io::ErrorKind::AlreadyExists), - //(Ok(Module::FS), 1002) => io::Error::from(io::ErrorKind::NotFound), - _ => io::Error::new(io::ErrorKind::Other, Box::new(err)) - } + io::Error::from_raw_os_error(err.0 as i32) } } diff --git a/src/libstd/sys/switch/net.rs b/src/libstd/sys/switch/net.rs index ca4931c629c40..74cc994db3475 100644 --- a/src/libstd/sys/switch/net.rs +++ b/src/libstd/sys/switch/net.rs @@ -23,31 +23,21 @@ use megaton_hammer::kernel::{Session, TransferMemory, KObject}; use megaton_hammer::ipcdefs::nn; use megaton_hammer::ipcdefs::nn::socket::sf::IClient; use megaton_hammer::ipcdefs::nn::socket::resolver::IResolver; +use megaton_hammer::error::{Module, Error}; pub struct TcpStream(Arc>, u32); macro_rules! handle_err { ($x: expr) => {{ - let val = try_mth!($x); + let val = $x?; if val.0 == -1 { - return Err(io::Error::from_raw_os_error(val.1 as i32)); + Err(Error::from_module_description(Module::MegatonHammerLinux, val.1))? } else { val } }} } -macro_rules! try_mth { - ($x: expr) => { - match $x { - Ok(val) => val, - Err(err) => { - return Err(io::Error::new(io::ErrorKind::Other, Box::new(err))) - } - } - } -} - fn sockname(f: F) -> io::Result where F: FnOnce(*mut c::sockaddr, *mut c::socklen_t) -> io::Result<()> { @@ -97,7 +87,7 @@ fn init() -> io::Result>> { let bsd = if let Ok(bsd) = IClient::new_bsd_u(init_args) { bsd } else { - try_mth!(IClient::new_bsd_s(init_args)) + IClient::new_bsd_s(init_args)? }; // Leak the handle. @@ -559,7 +549,7 @@ fn pack_ai(ai: &c::addrinfo, buf: &mut [u8]) -> io::Result<()> { } pub fn lookup_host(host: &str) -> io::Result { - let sfdnsres = try_mth!(IResolver::new()); + let sfdnsres = IResolver::new()?; let c_host = CString::new(host)?; // TODO @@ -574,7 +564,7 @@ pub fn lookup_host(host: &str) -> io::Result { // on the switch. let mut res = Box::new([0; 0x1000]); //TODO: Use gethostbyname instead. - let (ret, _errno, _size) = match sfdnsres.get_addr_info(1, 0, 0, c_host.as_bytes_with_nul(), &b"0\0"[..], &hints_packed, &mut *res) { + let (ret, _errno, _size) = match sfdnsres.get_addr_info(true, 0, 0, c_host.as_bytes_with_nul(), &b"0\0"[..], &hints_packed, &mut *res) { Ok(x) => x, Err(err) => return Err(io::Error::new(io::ErrorKind::Other, Box::new(err))) }; From 4b6f5921b34caf6af3574cb3d29d34ea6619120c Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 18 Sep 2018 02:16:23 +0000 Subject: [PATCH 33/33] Implement RomFs. --- src/libstd/Cargo.toml | 1 + src/libstd/sys/switch/ext/fs.rs | 24 + src/libstd/sys/switch/ext/mod.rs | 3 + src/libstd/sys/switch/fs/fspsrv.rs | 270 ++++++++++ src/libstd/sys/switch/{fs.rs => fs/mod.rs} | 279 +--------- src/libstd/sys/switch/fs/romfs.rs | 571 +++++++++++++++++++++ src/libstd/sys/switch/mod.rs | 8 + 7 files changed, 890 insertions(+), 266 deletions(-) create mode 100644 src/libstd/sys/switch/ext/fs.rs create mode 100644 src/libstd/sys/switch/fs/fspsrv.rs rename src/libstd/sys/switch/{fs.rs => fs/mod.rs} (50%) create mode 100644 src/libstd/sys/switch/fs/romfs.rs diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 0d60740006277..eeb9f1a52aadc 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -34,6 +34,7 @@ gimli = { git = "https://github.com/roblabla/gimli", branch = "megaton", default fallible-iterator = { version = "0.1", default-features = false, features = ["alloc"] } addr2line = { git = "https://github.com/roblabla/addr2line", branch = "megaton", default-features = false, features = ["alloc"] } runwind = { git = "https://github.com/roblabla/unwind-rs", branch = "megaton", default-features = false, features = ["nightly"] } +plain = { version = "0.2" } [dev-dependencies] rand = "0.4" diff --git a/src/libstd/sys/switch/ext/fs.rs b/src/libstd/sys/switch/ext/fs.rs new file mode 100644 index 0000000000000..c9849bd4f8bef --- /dev/null +++ b/src/libstd/sys/switch/ext/fs.rs @@ -0,0 +1,24 @@ +//! Switch-specific extensions to the filesystem ops. + +#![stable(feature = "rust1", since = "1.0.0")] + +use io; +use fs::File; +use sys_common::{FromInner, AsInner}; + +/// Switch-specitic extension to [`File`] +/// +/// [`File`]: ../../../../std/fs/struct.File.html +#[stable(feature = "rust1", since = "1.0.0")] +pub trait FileExt { + /// Reopens the underlying file, with its own cursor. + #[stable(feature = "rust1", since = "1.0.0")] + fn reopen(&self) -> io::Result; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FileExt for File { + fn reopen(&self) -> io::Result { + Ok(File::from_inner(self.as_inner().reopen()?)) + } +} diff --git a/src/libstd/sys/switch/ext/mod.rs b/src/libstd/sys/switch/ext/mod.rs index fb0ae6df8d331..daa6ba9883f12 100644 --- a/src/libstd/sys/switch/ext/mod.rs +++ b/src/libstd/sys/switch/ext/mod.rs @@ -4,6 +4,7 @@ #![doc(cfg(target_os = "switch"))] pub mod ffi; +pub mod fs; /// A prelude for conveniently writing platform-specific code. /// @@ -12,4 +13,6 @@ pub mod ffi; pub mod prelude { #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] pub use super::ffi::{OsStrExt, OsStringExt}; + #[doc(no_inline)] #[stable(feature = "rust1", since = "1.0.0")] + pub use super::fs::{FileExt}; } diff --git a/src/libstd/sys/switch/fs/fspsrv.rs b/src/libstd/sys/switch/fs/fspsrv.rs new file mode 100644 index 0000000000000..d254d5ab62a12 --- /dev/null +++ b/src/libstd/sys/switch/fs/fspsrv.rs @@ -0,0 +1,270 @@ +use io::{self, ErrorKind}; +use super::{FilesystemOps, FileOps, OpenOptions, FileAttr, SeekFrom, FilePermissions, ReadDir, FileType,DirEntry, ReadDirOps}; +use path::{Path, PathBuf, Component}; +use ffi::OsStr; +use megaton_hammer::ipcdefs::nn::fssrv::sf::{IFile, IDirectory, IFileSystem, IDirectoryEntry, DirectoryEntryType}; +use megaton_hammer::kernel::Object; +use sync::atomic::{AtomicU64, Ordering}; +use sync::Arc; +use sys::switch::unsupported; +use sys::ext::ffi::OsStrExt; +use sys_common::AsInner; +use core::slice; +use core::fmt::Debug; + +pub struct FspSrvFs(IFileSystem); + +#[derive(Debug)] +pub struct FspSrvFile { + internal: Arc>, + offset: AtomicU64 +} + +impl FspSrvFs { + pub fn new(fs: IFileSystem) -> FspSrvFs { + FspSrvFs(fs) + } +} + +#[derive(Debug)] +pub struct FspReadDir { + internal: IDirectory, + parent: PathBuf +} + +impl ReadDirOps for FspReadDir {} +impl Iterator for FspReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + let mut entry: [IDirectoryEntry; 1] = [IDirectoryEntry { + path: [0; 0x300], + unk1: 0, + directory_entry_type: DirectoryEntryType::File, + filesize: 0, + }]; + match self.internal.read(&mut entry) { + Ok(0) => return None, + Err(err) => return Some(Err(err.into())), + Ok(n) => () + } + let size = entry[0].path.iter().position(|c| *c == b'\0').unwrap_or(0x300); + let file_name = OsStr::from_bytes(&entry[0].path[..size]); + Some(Ok(DirEntry { + path: self.parent.join(file_name), + file_name: file_name.into(), + metadata: FileAttr { + size: entry[0].filesize, + perm: FilePermissions, + file_type: entry[0].directory_entry_type.into() + }, + })) + } +} + +impl From for FileType { + fn from(ty: DirectoryEntryType) -> FileType { + match ty { + DirectoryEntryType::File => FileType::File, + DirectoryEntryType::Directory => FileType::Directory + } + } +} + +impl FilesystemOps for FspSrvFs { + fn open(&self, path: &Path, opts: &OpenOptions) -> io::Result> { + let mut arr = [0u8; 0x301]; + let path_as_bytes = path.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + let mut mode = 0; + if opts.read { + mode |= 1; + } + if opts.write { + mode |= 1 << 1 | 1 << 2; + } + if opts.create || opts.create_new { + let err = self.0.create_file(0, 0, &arr); + match (opts.create_new, err) { + (false, Err(err)) if io::Error::from(err).kind() == ErrorKind::AlreadyExists => (), + (_, err) => err? + } + } + let file = self.0.open_file(mode, &arr)?; + if opts.truncate { + file.set_size(0)?; + } + let offset = if opts.append { + file.get_size()? + } else { + 0 + }; + Ok(Box::new(FspSrvFile { + internal: Arc::new(file), + offset: AtomicU64::new(offset) + })) + } + fn readdir(&self, p: &Path) -> io::Result { + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + Ok(ReadDir(Box::new(FspReadDir { + internal: self.0.open_directory(3, &arr)?, + parent: p.into() + }))) + } + fn unlink(&self, p: &Path) -> io::Result<()> { + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.delete_file(&arr)?; + Ok(()) + } + fn rename(&self, old: &Path, new: &Path) -> io::Result<()> { + let mut oldarr = [0u8; 0x301]; + let path_as_bytes = old.as_os_str().as_bytes(); + (&mut oldarr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + + let mut newarr = [0u8; 0x301]; + let path_as_bytes = new.as_os_str().as_bytes(); + (&mut newarr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.rename_file(&oldarr, &newarr)?; + Ok(()) + } + fn set_perm(&self, p: &Path, perm: FilePermissions) -> io::Result<()> { + unsupported() + } + fn rmdir(&self, p: &Path) -> io::Result<()> { + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.delete_directory(&arr)?; + Ok(()) + } + fn remove_dir_all(&self, p: &Path) -> io::Result<()> { + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + self.0.delete_directory_recursively(&arr)?; + Ok(()) + } + fn readlink(&self, p: &Path) -> io::Result { + unsupported() + } + fn stat(&self, p: &Path) -> io::Result { + let mut arr = [0u8; 0x301]; + let path_as_bytes = p.as_os_str().as_bytes(); + (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); + + let entry_type = self.0.get_entry_type(&arr)?; + + let size = match entry_type { + DirectoryEntryType::File => { + self.0.open_file(0, &arr)?.get_size()? + }, + DirectoryEntryType::Directory => 0 + }; + Ok(FileAttr { + size, + perm: FilePermissions, + file_type: entry_type.into() + }) + } + fn lstat(&self, p: &Path) -> io::Result { + unsupported() + } + fn canonicalize(&self, p: &Path) -> io::Result { + let mut fullpath = PathBuf::from("/"); + for component in p.components() { + match component { + Component::Prefix(p) => panic!("Shouldn't obtain a prefix in inner fs impl"), + Component::RootDir => fullpath.push("/"), + Component::CurDir => (), + Component::ParentDir => { fullpath.pop(); }, + Component::Normal(p) => fullpath.push(p) + } + } + Ok(p.into()) + } +} + +impl FileOps for FspSrvFile { + fn file_attr(&self) -> io::Result { + Ok(FileAttr { + size: self.internal.get_size()?, + perm: FilePermissions, + file_type: FileType::File + }) + } + fn fsync(&self) -> io::Result<()> { + unsupported() + } + fn datasync(&self) -> io::Result<()> { + unsupported() + } + fn truncate(&self, size: u64) -> io::Result<()> { + self.internal.set_size(size)?; + Ok(()) + } + fn read(&self, buf: &mut [u8]) -> io::Result { + // TODO: Maybe I should lock the file on read/write? + let read_size = self.internal.read(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; + self.offset.fetch_add(read_size, Ordering::SeqCst); + Ok(read_size as usize) + } + fn write(&self, buf: &[u8]) -> io::Result { + // TODO: Maybe I should lock the file on read/write? + // TODO: In append mode, should I ignore offset and just write from + // the end? + self.internal.write(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; + self.offset.fetch_add(buf.len() as u64, Ordering::SeqCst); + Ok(buf.len()) + } + fn flush(&self) -> io::Result<()> { + unsupported() + } + fn seek(&self, pos: SeekFrom) -> io::Result { + let newpos = match pos { + SeekFrom::Current(pos) => { + self.offset.fetch_update(|v| { + let newval = v as i64 + pos; + if newval < 0 { + None + } else { + Some(newval as u64) + } + }, Ordering::SeqCst, Ordering::SeqCst).map_err(|_| io::Error::from(ErrorKind::InvalidInput))? + } + SeekFrom::Start(pos) => { + self.offset.store(pos, Ordering::SeqCst); + pos + }, + SeekFrom::End(pos) => { + let size = self.internal.get_size()?; + let newpos = size as i64 + pos; + if newpos < 0 { + Err(io::Error::from(ErrorKind::InvalidInput))? + } + self.offset.store(newpos as u64, Ordering::SeqCst); + newpos as u64 + } + }; + Ok(newpos) + } + fn duplicate(&self) -> io::Result> { + // TODO: This requires sharing the cursor between both FDs. That's a huge pain, + // so let's just not support it for now. We have a custom function called reopen + // that is basically equivalent to duplicate, but doesn't share the cursor. + unsupported() + } + // Custom extension + fn reopen(&self) -> io::Result> { + Ok(Box::new(FspSrvFile { + internal: self.internal.clone(), + offset: AtomicU64::new(0) + })) + } + fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + unsupported() + } +} diff --git a/src/libstd/sys/switch/fs.rs b/src/libstd/sys/switch/fs/mod.rs similarity index 50% rename from src/libstd/sys/switch/fs.rs rename to src/libstd/sys/switch/fs/mod.rs index 7c464502c578b..0a2856b5a0932 100644 --- a/src/libstd/sys/switch/fs.rs +++ b/src/libstd/sys/switch/fs/mod.rs @@ -9,7 +9,7 @@ // except according to those terms. use ffi::OsString; -use fmt; +use fmt::{self, Debug}; use hash::{Hash, Hasher}; use io::{self, SeekFrom}; use path::{Path, PathBuf, Component}; @@ -185,7 +185,7 @@ trait FilesystemOps { fn canonicalize(&self, p: &Path) -> io::Result; } -trait FileOps { +trait FileOps: Debug { fn file_attr(&self) -> io::Result; fn fsync(&self) -> io::Result<()>; fn datasync(&self) -> io::Result<()>; @@ -194,270 +194,15 @@ trait FileOps { fn write(&self, buf: &[u8]) -> io::Result; fn flush(&self) -> io::Result<()>; fn seek(&self, pos: SeekFrom) -> io::Result; - // Trait objects: HOW? - //fn duplicate(&self) -> io::Result; + fn duplicate(&self) -> io::Result>; + // Switch-specific extension. Reopens the file. The file cursor will be + // different. + fn reopen(&self) -> io::Result>; fn set_permissions(&self, perm: FilePermissions) -> io::Result<()>; } -mod fspsrv { - use io::{self, ErrorKind}; - use super::{FilesystemOps, FileOps, OpenOptions, FileAttr, SeekFrom, FilePermissions, ReadDir, FileType,DirEntry, ReadDirOps}; - use path::{Path, PathBuf, Component}; - use ffi::OsStr; - use megaton_hammer::ipcdefs::nn::fssrv::sf::{IFile, IDirectory, IFileSystem, IDirectoryEntry, DirectoryEntryType}; - use megaton_hammer::kernel::Object; - use sync::atomic::{AtomicU64, Ordering}; - use sys::switch::unsupported; - use sys::ext::ffi::OsStrExt; - use core::slice; - use core::fmt::Debug; - - pub struct FspSrvFs(IFileSystem); - - pub struct FspSrvFile { - internal: IFile, - offset: AtomicU64 - } - - impl FspSrvFs { - pub fn new(fs: IFileSystem) -> FspSrvFs { - FspSrvFs(fs) - } - } - - #[derive(Debug)] - pub struct FspReadDir { - internal: IDirectory, - parent: PathBuf - } - - impl ReadDirOps for FspReadDir {} - impl Iterator for FspReadDir { - type Item = io::Result; - - fn next(&mut self) -> Option> { - let mut entry: [IDirectoryEntry; 1] = [IDirectoryEntry { - path: [0; 0x300], - unk1: 0, - directory_entry_type: DirectoryEntryType::File, - filesize: 0, - }]; - match self.internal.read(&mut entry) { - Ok(0) => return None, - Err(err) => return Some(Err(err.into())), - Ok(n) => () - } - let size = entry[0].path.iter().position(|c| *c == b'\0').unwrap_or(0x300); - let file_name = OsStr::from_bytes(&entry[0].path[..size]); - Some(Ok(DirEntry { - path: self.parent.join(file_name), - file_name: file_name.into(), - metadata: FileAttr { - size: entry[0].filesize, - perm: FilePermissions, - file_type: entry[0].directory_entry_type.into() - }, - })) - } - } - - impl From for FileType { - fn from(ty: DirectoryEntryType) -> FileType { - match ty { - DirectoryEntryType::File => FileType::File, - DirectoryEntryType::Directory => FileType::Directory - } - } - } - - impl FilesystemOps for FspSrvFs { - fn open(&self, path: &Path, opts: &OpenOptions) -> io::Result> { - let mut arr = [0u8; 0x301]; - let path_as_bytes = path.as_os_str().as_bytes(); - (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - let mut mode = 0; - if opts.read { - mode |= 1; - } - if opts.write { - mode |= 1 << 1 | 1 << 2; - } - if opts.create || opts.create_new { - let err = self.0.create_file(0, 0, &arr); - match (opts.create_new, err) { - (false, Err(err)) if io::Error::from(err).kind() == ErrorKind::AlreadyExists => (), - (_, err) => err? - } - } - let file = self.0.open_file(mode, &arr)?; - if opts.truncate { - file.set_size(0)?; - } - let offset = if opts.append { - file.get_size()? - } else { - 0 - }; - Ok(Box::new(FspSrvFile { - internal: file, - offset: AtomicU64::new(offset) - })) - } - fn readdir(&self, p: &Path) -> io::Result { - let mut arr = [0u8; 0x301]; - let path_as_bytes = p.as_os_str().as_bytes(); - (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - Ok(ReadDir(Box::new(FspReadDir { - internal: self.0.open_directory(3, &arr)?, - parent: p.into() - }))) - } - fn unlink(&self, p: &Path) -> io::Result<()> { - let mut arr = [0u8; 0x301]; - let path_as_bytes = p.as_os_str().as_bytes(); - (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - self.0.delete_file(&arr)?; - Ok(()) - } - fn rename(&self, old: &Path, new: &Path) -> io::Result<()> { - let mut oldarr = [0u8; 0x301]; - let path_as_bytes = old.as_os_str().as_bytes(); - (&mut oldarr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - - let mut newarr = [0u8; 0x301]; - let path_as_bytes = new.as_os_str().as_bytes(); - (&mut newarr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - self.0.rename_file(&oldarr, &newarr)?; - Ok(()) - } - fn set_perm(&self, p: &Path, perm: FilePermissions) -> io::Result<()> { - unsupported() - } - fn rmdir(&self, p: &Path) -> io::Result<()> { - let mut arr = [0u8; 0x301]; - let path_as_bytes = p.as_os_str().as_bytes(); - (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - self.0.delete_directory(&arr)?; - Ok(()) - } - fn remove_dir_all(&self, p: &Path) -> io::Result<()> { - let mut arr = [0u8; 0x301]; - let path_as_bytes = p.as_os_str().as_bytes(); - (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - self.0.delete_directory_recursively(&arr)?; - Ok(()) - } - fn readlink(&self, p: &Path) -> io::Result { - unsupported() - } - fn stat(&self, p: &Path) -> io::Result { - let mut arr = [0u8; 0x301]; - let path_as_bytes = p.as_os_str().as_bytes(); - (&mut arr[..path_as_bytes.len()]).copy_from_slice(path_as_bytes); - - let entry_type = self.0.get_entry_type(&arr)?; - - let size = match entry_type { - DirectoryEntryType::File => { - self.0.open_file(0, &arr)?.get_size()? - }, - DirectoryEntryType::Directory => 0 - }; - Ok(FileAttr { - size, - perm: FilePermissions, - file_type: entry_type.into() - }) - } - fn lstat(&self, p: &Path) -> io::Result { - unsupported() - } - fn canonicalize(&self, p: &Path) -> io::Result { - let mut fullpath = PathBuf::from("/"); - for component in p.components() { - match component { - Component::Prefix(p) => panic!("Shouldn't obtain a prefix in inner fs impl"), - Component::RootDir => fullpath.push("/"), - Component::CurDir => (), - Component::ParentDir => { fullpath.pop(); }, - Component::Normal(p) => fullpath.push(p) - } - } - Ok(p.into()) - } - } - - impl FileOps for FspSrvFile { - fn file_attr(&self) -> io::Result { - Ok(FileAttr { - size: self.internal.get_size()?, - perm: FilePermissions, - file_type: FileType::File - }) - } - fn fsync(&self) -> io::Result<()> { - unsupported() - } - fn datasync(&self) -> io::Result<()> { - unsupported() - } - fn truncate(&self, size: u64) -> io::Result<()> { - self.internal.set_size(size)?; - Ok(()) - } - fn read(&self, buf: &mut [u8]) -> io::Result { - // TODO: Maybe I should lock the file on read/write? - let read_size = self.internal.read(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; - self.offset.fetch_add(read_size, Ordering::SeqCst); - Ok(read_size as usize) - } - fn write(&self, buf: &[u8]) -> io::Result { - // TODO: Maybe I should lock the file on read/write? - // TODO: In append mode, should I ignore offset and just write from - // the end? - self.internal.write(0, self.offset.load(Ordering::SeqCst), buf.len() as u64, buf)?; - self.offset.fetch_add(buf.len() as u64, Ordering::SeqCst); - Ok(buf.len()) - } - fn flush(&self) -> io::Result<()> { - unsupported() - } - fn seek(&self, pos: SeekFrom) -> io::Result { - let newpos = match pos { - SeekFrom::Current(pos) => { - self.offset.fetch_update(|v| { - let newval = v as i64 + pos; - if newval < 0 { - None - } else { - Some(newval as u64) - } - }, Ordering::SeqCst, Ordering::SeqCst).map_err(|_| io::Error::from(ErrorKind::InvalidInput))? - } - SeekFrom::Start(pos) => { - self.offset.store(pos, Ordering::SeqCst); - pos - }, - SeekFrom::End(pos) => { - let size = self.internal.get_size()?; - let newpos = size as i64 + pos; - if newpos < 0 { - Err(io::Error::from(ErrorKind::InvalidInput))? - } - self.offset.store(newpos as u64, Ordering::SeqCst); - newpos as u64 - } - }; - Ok(newpos) - } - /*fn duplicate(&self) -> io::Result> { - unsupported() - }*/ - fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { - unsupported() - } - } -} +mod fspsrv; +mod romfs; use self::fspsrv::FspSrvFs; @@ -490,6 +235,10 @@ impl File { Ok(File(fs.open(path, opts)?)) } + pub fn reopen(&self) -> io::Result { + Ok(File(self.0.reopen()?)) + } + pub fn file_attr(&self) -> io::Result { self.0.file_attr() } @@ -523,9 +272,7 @@ impl File { } pub fn duplicate(&self) -> io::Result { - // Trait objects are not fun - // Ok(File(self.0.duplicate()?)) - unsupported() + Ok(File(self.0.duplicate()?)) } pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { diff --git a/src/libstd/sys/switch/fs/romfs.rs b/src/libstd/sys/switch/fs/romfs.rs new file mode 100644 index 0000000000000..e6d4874b43f53 --- /dev/null +++ b/src/libstd/sys/switch/fs/romfs.rs @@ -0,0 +1,571 @@ +use super::fspsrv::FspSrvFile; +use super::{FilePermissions, FileAttr, DirEntry, ReadDirOps, ReadDir, FileType, FilesystemOps, FileOps, OpenOptions, unsupported}; +use io::{self, SeekFrom}; +use path::{Path, PathBuf, Component}; +use ffi::OsStr; +use sys::ext::ffi::OsStrExt; +use self::util::*; +use sync::Arc; +use sync::atomic::{AtomicU64, Ordering}; + +use megaton_hammer::error::MegatonHammerDescription; + +extern crate plain; +use self::plain::Plain; + +const ROMFS_NONE: u32 = ::max_value(); + +#[derive(Debug)] +enum RomFsType { + File { + f: Box, + offset: u64 + }, + // TODO: Write a storage backend for romfs. + Storage(!) +} + +impl RomFsType { + fn read(&self, at: u64, buf: &mut [u8]) -> io::Result { + match self { + &RomFsType::File { ref f, offset } => { + f.seek(SeekFrom::Start(offset + at))?; + f.read(buf) + } + } + } + + fn read_exact(&self, at: u64, mut buf: &mut [u8]) -> io::Result<()> { + while !buf.is_empty() { + match self.read(at, buf) { + Ok(0) => break, + Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new(io::ErrorKind::UnexpectedEof, + "failed to fill whole buffer")) + } else { + Ok(()) + } + } + + fn read_plain(&self, at: u64, t: &mut T) -> io::Result<()> { + let data = unsafe { + // Safety: According to plain, writing to this reference is safe, reading is not. + // We are only going to write to it, so it should be fine. + plain::as_mut_bytes(t) + }; + self.read_exact(at, data) + } + + fn read_plain_slice(&self, at: u64, t: &mut [T]) -> io::Result<()> { + let data = unsafe { + // Safety: According to plain, writing to this reference is safe, reading is not. + // We are only going to write to it, so it should be fine. + plain::as_mut_bytes(t) + }; + self.read_exact(at, data) + } + + fn reopen(&self) -> io::Result { + match self { + RomFsType::File { f, offset } => Ok(RomFsType::File { + f: f.reopen()?, offset: *offset + }) + } + } +} + + +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +struct RomFsHeader { + header_size: u64, + dir_hash_table_off: u64, + dir_hash_table_size: u64, + dir_table_off: u64, + dir_table_size: u64, + file_hash_table_off: u64, + file_hash_table_size: u64, + file_table_off: u64, + file_table_size: u64, + file_data_off: u64, +} +unsafe impl Plain for RomFsHeader {} + +trait NextHash { + fn get_next_hash(&self) -> u32; +} + +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +struct RomFsDirTableEntryHeader { + parent: u32, + sibling: u32, + child_dir: u32, + child_file: u32, + next_hash: u32, + name_len: u32, +} + +impl UnsizedHeader for RomFsDirTableEntryHeader { + fn get_len(&self) -> usize { + self.name_len as usize + } +} + +#[repr(C)] +#[derive(Debug)] +struct RomFsDirTableEntry { + parent: u32, + sibling: u32, + child_dir: u32, + child_file: u32, + next_hash: u32, + name_len: u32, + name: [u8] +} + +impl NextHash for RomFsDirTableEntry { + fn get_next_hash(&self) -> u32 { + self.next_hash + } +} + +impl Unsized for RomFsDirTableEntry { + type Header = RomFsDirTableEntryHeader; +} + +#[repr(C)] +#[derive(Debug, Default, Clone, Copy)] +struct RomFsFileTableEntryHeader { + parent: u32, + sibling: u32, + data_off: u64, + data_size: u64, + next_hash: u32, + name_len: u32 +} + +impl UnsizedHeader for RomFsFileTableEntryHeader { + fn get_len(&self) -> usize { + self.name_len as usize + } +} + + +#[repr(C)] +#[derive(Debug)] +struct RomFsFileTableEntry { + parent: u32, + sibling: u32, + data_off: u64, + data_size: u64, + next_hash: u32, + name_len: u32, + name: [u8] +} + +impl NextHash for RomFsFileTableEntry { + fn get_next_hash(&self) -> u32 { + self.next_hash + } +} + +impl Unsized for RomFsFileTableEntry { + type Header = RomFsFileTableEntryHeader; +} + +struct IterateHash<'a, T: 'a + Unsized + ?Sized> { + ctx: &'a EntityTable, cur_off: u32 +} + +impl<'a, T: 'a + Unsized + ?Sized> IterateHash<'a, T> { + fn new(ctx: &'a EntityTable, cur_off: u32) -> IterateHash<'a, T> { + IterateHash { ctx, cur_off } + } +} + +impl<'a, T: 'a + NextHash + Unsized + ?Sized> Iterator for IterateHash<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option<&'a T> { + if self.cur_off == ROMFS_NONE { + None + } else { + let cur_ent = self.ctx.get(self.cur_off); + self.cur_off = cur_ent.get_next_hash(); + Some(cur_ent) + } + } +} + + + +#[derive(Debug)] +pub struct RomFsFile { + internal: RomFsType, + start: u64, + size: u64, + offset: AtomicU64 +} + +fn calc_hash(parent: u32, name: &OsStr, hash_table_size: u32) -> u32 { + let mut hash = parent ^ 123456789; + for c in name.as_bytes() { + hash = (hash >> 5) | (hash << 27); + hash ^= *c as u32; + } + return hash % hash_table_size; +} + +struct RomFs { + ty: RomFsType, + header: RomFsHeader, + dir_hash_table: Vec, + dir_table: Arc>, + file_hash_table: Vec, + file_table: Arc> +} + +impl RomFs { + pub fn from_file(file: Box, romfs_start_offset: u64) -> io::Result { + let romfs_type = RomFsType::File { + f: file, + offset: romfs_start_offset + }; + + let mut header: RomFsHeader = RomFsHeader::default(); + romfs_type.read_plain(0, &mut header)?; + + let mut dir_hash_table = vec![0u32; (header.dir_hash_table_size / 4) as usize]; + romfs_type.read_plain_slice(header.dir_hash_table_off, &mut dir_hash_table)?; + + let mut dir_table = vec![0; header.dir_table_size as usize]; + romfs_type.read_exact(header.dir_table_off, &mut dir_table)?; + + let mut file_hash_table = vec![0u32; (header.file_hash_table_size / 4) as usize]; + romfs_type.read_plain_slice(header.file_hash_table_off, &mut file_hash_table)?; + + let mut file_table = vec![0; header.file_table_size as usize]; + romfs_type.read_exact(header.file_table_off, &mut file_table)?; + + Ok(RomFs { + ty: romfs_type, + header, + dir_hash_table, + dir_table: Arc::new(EntityTable::new(dir_table)), + file_hash_table, + file_table: Arc::new(EntityTable::new(file_table)) + }) + } + + pub fn get_dir(&self, idx: u32) -> &RomFsDirTableEntry { + self.dir_table.get(idx) + } + + pub fn get_file(&self, idx: u32) -> &RomFsFileTableEntry { + self.file_table.get(idx) + } + + fn root_dir(&self) -> &RomFsDirTableEntry { + self.get_dir(0) + } + + fn search_for_dir(&self, parent: &RomFsDirTableEntry, path_component: &OsStr) -> Option<&RomFsDirTableEntry> { + let parent_off = parent as *const RomFsDirTableEntry as *const u8 as u32 - self.dir_table.as_ptr() as u32; + let hash = calc_hash(parent_off, path_component, self.dir_hash_table.len() as u32); + + for cur_dir in IterateHash::new(&*self.dir_table, self.dir_hash_table[hash as usize]) { + if cur_dir.parent != parent_off { continue } + if &cur_dir.name != path_component.as_bytes() { continue } + return Some(cur_dir) + } + + None + } + + fn search_for_file(&self, parent: &RomFsDirTableEntry, path_component: &OsStr) -> Option<&RomFsFileTableEntry> { + let parent_off = parent as *const RomFsDirTableEntry as *const u8 as u32 - self.dir_table.as_ptr() as u32; + let hash = calc_hash(parent_off, path_component, self.dir_hash_table.len() as u32); + + for cur_file in IterateHash::new(&*self.file_table, self.file_hash_table[hash as usize]) { + if cur_file.parent != parent_off { continue } + if &cur_file.name != path_component.as_bytes() { continue } + return Some(cur_file) + } + + None + } + + fn navigate_to_dir<'a>(&'a self, mut parent: &'a RomFsDirTableEntry, path: &Path) -> io::Result<&'a RomFsDirTableEntry> { + let mut components = path.components(); + assert_eq!(Some(Component::RootDir), components.next(), "Expected first component of path to be root dir"); + for component in components { + match component { + Component::Prefix(_) | Component::RootDir => panic!("Didn't expect component here"), + Component::CurDir => continue, + Component::ParentDir => parent = self.get_dir(parent.parent), + Component::Normal(p) => { + if let Some(new_parent) = self.search_for_dir(parent, p) { + parent = new_parent + } else { + Err(MegatonHammerDescription::RomFsEntityDoesNotExist)? + } + } + } + } + Ok(parent) + } +} + +impl FilesystemOps for RomFs { + fn open(&self, path: &Path, opts: &OpenOptions) -> io::Result> { + if opts.write || opts.append || opts.truncate { + Err(MegatonHammerDescription::RomFsReadOnly)? + } + let path_parent = path.parent().ok_or(MegatonHammerDescription::RomFsEntityDoesNotExist)?; + let parent = self.navigate_to_dir(self.root_dir(), path_parent)?; + + let path_filename = path.file_name().ok_or(MegatonHammerDescription::RomFsEntityDoesNotExist)?; + if let Some(file) = self.search_for_file(parent, path_filename) { + if opts.create && opts.create_new { + Err(MegatonHammerDescription::RomFsEntityExists)? + } else { + Ok(Box::new(RomFsFile { + internal: self.ty.reopen()?, + start: self.header.file_data_off + file.data_off, + size: file.data_size, + offset: AtomicU64::new(0) + })) + } + } else if opts.create { + Err(MegatonHammerDescription::RomFsReadOnly)? + } else { + Err(MegatonHammerDescription::RomFsEntityDoesNotExist)? + } + } + + fn readdir(&self, p: &Path) -> io::Result { + let dir = self.navigate_to_dir(self.root_dir(), p)?; + + Ok(ReadDir(Box::new(RomFsReadDir { + parent_path: PathBuf::from(p), + dir_table: self.dir_table.clone(), + file_table: self.file_table.clone(), + cur_dir: dir.child_dir, + cur_file: dir.child_file + }))) + } + fn unlink(&self, p: &Path) -> io::Result<()> { + unsupported() + } + fn rename(&self, old: &Path, _new: &Path) -> io::Result<()> { + unsupported() + } + fn set_perm(&self, p: &Path, perm: FilePermissions) -> io::Result<()> { + unsupported() + } + fn rmdir(&self, p: &Path) -> io::Result<()> { + unsupported() + } + fn remove_dir_all(&self, path: &Path) -> io::Result<()> { + unsupported() + } + fn readlink(&self, p: &Path) -> io::Result { + unsupported() + } + fn stat(&self, p: &Path) -> io::Result { + unsupported() + } + fn lstat(&self, p: &Path) -> io::Result { + unsupported() + } + fn canonicalize(&self, p: &Path) -> io::Result { + unsupported() + } +} + +impl FileOps for RomFsFile { + fn file_attr(&self) -> io::Result { + Ok(FileAttr { + size: self.size, + perm: FilePermissions, + file_type: FileType::File + }) + } + fn fsync(&self) -> io::Result<()> { + unsupported() + } + fn datasync(&self) -> io::Result<()> { + unsupported() + } + fn truncate(&self, size: u64) -> io::Result<()> { + unsupported() + } + fn read(&self, mut buf: &mut [u8]) -> io::Result { + let pos = self.offset.load(Ordering::SeqCst); + if self.size < pos + buf.len() as u64 { + let tmp = buf; + buf = &mut tmp[..(self.size - pos) as usize]; + } + let out = self.internal.read(self.start + pos, buf)?; + self.offset.store(pos + out as u64, Ordering::SeqCst); + Ok(out) + } + fn write(&self, buf: &[u8]) -> io::Result { + unsupported() + } + fn flush(&self) -> io::Result<()> { + unsupported() + } + fn seek(&self, pos: SeekFrom) -> io::Result { + let newpos = match pos { + SeekFrom::Current(pos) => { + self.offset.fetch_update(|v| { + let newval = v as i64 + pos; + if newval < 0 { + None + } else { + Some(newval as u64) + } + }, Ordering::SeqCst, Ordering::SeqCst).map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))? + } + SeekFrom::Start(pos) => { + self.offset.store(pos, Ordering::SeqCst); + pos + }, + SeekFrom::End(pos) => { + let size = self.size; + let newpos = size as i64 + pos; + if newpos < 0 { + Err(io::Error::from(io::ErrorKind::InvalidInput))? + } + self.offset.store(newpos as u64, Ordering::SeqCst); + newpos as u64 + } + }; + Ok(newpos) + } + fn duplicate(&self) -> io::Result> { + unsupported() + } + fn reopen(&self) -> io::Result> { + unsupported() + } + fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> { + unsupported() + } +} + +#[derive(Debug)] +struct RomFsReadDir { + file_table: Arc>, + dir_table: Arc>, + parent_path: PathBuf, + cur_dir: u32, + cur_file: u32 +} + +impl ReadDirOps for RomFsReadDir {} +impl Iterator for RomFsReadDir { + type Item = io::Result; + + fn next(&mut self) -> Option> { + match (self.cur_dir, self.cur_file) { + (ROMFS_NONE, ROMFS_NONE) => None, + (ROMFS_NONE, cur_file) => { + let cur_file = self.file_table.get(cur_file); + self.cur_file = cur_file.sibling; + Some(Ok(DirEntry { + path: self.parent_path.join(OsStr::from_bytes(&cur_file.name)), + file_name: OsStr::from_bytes(&cur_file.name).into(), + metadata: FileAttr { + size: cur_file.data_size, + perm: FilePermissions, + file_type: FileType::File + } + })) + }, + (cur_dir, _) => { + let cur_dir = self.dir_table.get(cur_dir); + self.cur_dir = cur_dir.sibling; + Some(Ok(DirEntry { + path: self.parent_path.join(OsStr::from_bytes(&cur_dir.name)), + file_name: OsStr::from_bytes(&cur_dir.name).into(), + metadata: FileAttr { + size: 0, + perm: FilePermissions, + file_type: FileType::Directory + }, + })) + }, + } + } +} + +mod util { + //! Stop here you fool! This is a place of madness... + //! + //! Look at https://play.rust-lang.org/?gist=e3cc82ffe60c1eef1401e6e15c8875e8&version=stable&mode=debug&edition=2015 + //! if you want a chance at understanding the madness going on here. + #![allow(dead_code)] + + use core::marker::PhantomData; + + struct FatPtr { + data: *const u8, + len: usize + } + + pub trait Unsized { + type Header: UnsizedHeader; + } + + pub trait UnsizedHeader { + fn get_len(&self) -> usize; + } + + #[derive(Debug)] + pub struct EntityTable { + data: Vec, + phantom: PhantomData + } + + impl EntityTable { + pub fn new(data: Vec) -> EntityTable { + EntityTable { + data: data, + phantom: PhantomData + } + } + + pub fn get(&self, idx: u32) -> &T { + unsafe { + // Safety: + // This method is unsafe. It creates a reference with utter + // disregard for alignment constraints. This function will only be + // used on AArch64, where unaligned accesses don't cause too much + // trouble. Still, this is technically UB... + + // Alternative: I could use read_unaligned() and return an owned + // struct. + let dir_ptr = (&self.data[idx as usize..]).as_ptr() as *const u8 as *const T::Header; + let len = (*dir_ptr).get_len(); + let ptr = FatPtr { + data: dir_ptr as *const u8, + len + }; + use mem::{self, size_of}; + assert_eq!(size_of::<&T>(), size_of::(), "&T is not a fat pointer"); + mem::transmute_copy::(&ptr) + } + } + + pub fn as_ptr(&self) -> *const u8 { + self.data.as_ptr() + } + } +} diff --git a/src/libstd/sys/switch/mod.rs b/src/libstd/sys/switch/mod.rs index 6dd7aa3860c35..01ee65902c0fd 100644 --- a/src/libstd/sys/switch/mod.rs +++ b/src/libstd/sys/switch/mod.rs @@ -142,3 +142,11 @@ impl From<::megaton_hammer::error::Error> for io::Error { io::Error::from_raw_os_error(err.0 as i32) } } + +// TODO: Figure out a better way to do this. +#[stable(feature = "rust1", since = "1.0.0")] +impl From<::megaton_hammer::error::MegatonHammerDescription> for io::Error { + fn from(err: ::megaton_hammer::error::MegatonHammerDescription) -> io::Error { + io::Error::from(::megaton_hammer::error::Error::from(err)) + } +}