Change io crate & add a small shell

This commit is contained in:
2026-03-25 20:45:11 +01:00
parent f966a1239e
commit ae0593c972
98 changed files with 11102 additions and 810 deletions

View File

@@ -14,12 +14,13 @@ REAL_CP_FILES := "\
sys/random/mod.rs \
sys/thread_local/mod.rs"
STD_FILES := "\
../tests ../benches \
alloc.rs ascii.rs backtrace.rs bstr.rs env.rs error.rs fs.rs \
keyword_docs.rs lib.rs macros.rs panic.rs panicking.rs pat.rs \
path.rs process.rs random.rs rt.rs tests_helpers.rs time.rs \
backtrace collections ffi fs hash io net num os/raw/mod.rs \
os/raw/tests.rs os/mod.rs prelude process sync sys/alloc/mod.rs \
sys/args/unsupported.rs sys/env/mod.rs sys/env/common.rs \
sys/args/unsupported.rs sys/args/common.rs sys/env/mod.rs sys/env/common.rs \
sys/env/unsupported.rs sys/fd/mod.rs sys/fs/mod.rs \
sys/fs/common.rs sys/fs/unsupported.rs sys/helpers/mod.rs \
sys/helpers/small_c_string.rs sys/helpers/tests.rs sys/helpers/wstr.rs \
@@ -44,7 +45,7 @@ STD_FILES := "\
sys/thread_local/no_threads.rs sys/thread_local/os.rs sys/time/mod.rs \
sys/time/unsupported.rs sys/backtrace.rs sys/cmath.rs \
sys/configure_builtins.rs sys/env_consts.rs sys/exit.rs sys/mod.rs thread"
KEEP_FILES := "survos.rs"
KEEP_FILES := "survos.rs ffi.rs"
setup-std:
@echo "🔗 Linking root directories..."
@@ -74,7 +75,8 @@ patch-std:
done
build-sysroot:
cargo clean
rm -rf target/riscv64/debug/deps/
rm -rf ../target/riscv64/
RUSTFLAGS="-Zforce-unstable-if-unmarked -C relocation-model=pic -C link-arg=-pie" \
cargo build --package std --target ../riscv64.json --features compiler-builtins-mem
mkdir -p ../sysroot/lib/rustlib/riscv64/lib

View File

@@ -2,6 +2,8 @@ Cargo.lock
Cargo.toml
build.rs
target
tests
benches
src/random.rs
src/num
src/prelude
@@ -18,7 +20,6 @@ src/tests_helpers.rs
src/error.rs
src/net
src/rt.rs
src/sys/args
src/sys/args/mod.rs
src/sys/args/unsupported.rs
src/sys/thread_local
@@ -56,9 +57,7 @@ src/sys/sync/thread_parking/unsupported.rs
src/sys/sync/once
src/sys/sync/once/no_threads.rs
src/sys/sync/once/mod.rs
src/sys/fd
src/sys/fd/mod.rs
src/sys/process
src/sys/process/mod.rs
src/sys/process/env.rs
src/sys/process/unsupported.rs
@@ -100,7 +99,6 @@ src/sys/helpers/mod.rs
src/sys/helpers/wstr.rs
src/sys/helpers/tests.rs
src/sys/helpers/small_c_string.rs
src/sys/fs
src/sys/fs/mod.rs
src/sys/fs/common.rs
src/sys/fs/unsupported.rs
@@ -124,7 +122,6 @@ src/sys/os_str/bytes
src/sys/os_str/bytes/tests.rs
src/sys/os_str/bytes.rs
src/macros.rs
src/os
src/os/mod.rs
src/os/raw
src/os/raw/mod.rs

View File

@@ -0,0 +1,5 @@
185a \#[cfg(target_os = "survos")]\
pub mod survos;\
#[cfg(target_os = "survos")]\
#[unstable(feature = "survos_std", issue = "none")]\
pub use survos::fd;

View File

@@ -1,4 +1,5 @@
# 55a \ target_os = "survos" => { \
# mod survos; \
# pub use survos::*; \
# }
55a \ target_os = "survos" => { \
mod survos; \
pub use survos::*; \
}
12a \ target_os = "survos",

View File

@@ -0,0 +1,4 @@
21a \ target_os = "survos" => { \
mod survos; \
pub use survos::*; \
}

View File

@@ -0,0 +1,4 @@
47a \ target_os = "survos" => { \
mod survos; \
use survos as imp; \
}

View File

@@ -0,0 +1,4 @@
17a \ target_os = "survos" => { \
mod survos; \
use survos as imp; \
}

View File

@@ -0,0 +1,18 @@
#![unstable(feature = "survos_std", issue = "none")]
#[unstable(feature = "survos_std", issue = "none")]
pub mod fd {
pub type RawFd = u64;
pub trait FromRawFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self;
}
pub trait AsRawFd {
// Required method
fn as_raw_fd(&self) -> RawFd;
}
}
pub mod ffi;
pub mod syscall;

View File

@@ -0,0 +1,70 @@
use crate::ffi::{OsStr, OsString};
use crate::mem;
use crate::sealed::Sealed;
use crate::sys::os_str::Buf;
use crate::sys::{AsInner, FromInner, IntoInner};
// Note: this file is currently reused in other `std::os::{platform}::ffi` modules to reduce duplication.
// Keep this in mind when applying changes to this file that only apply to `unix`.
/// Platform-specific extensions to [`OsString`].
///
/// This trait is sealed: it cannot be implemented outside the standard library.
/// This is so that future additional methods are not breaking changes.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait OsStringExt: Sealed {
/// Creates an [`OsString`] from a byte vector.
///
/// See the module documentation for an example.
#[stable(feature = "rust1", since = "1.0.0")]
fn from_vec(vec: Vec<u8>) -> Self;
/// Yields the underlying byte vector of this [`OsString`].
///
/// See the module documentation for an example.
#[stable(feature = "rust1", since = "1.0.0")]
fn into_vec(self) -> Vec<u8>;
}
#[stable(feature = "rust1", since = "1.0.0")]
impl OsStringExt for OsString {
#[inline]
fn from_vec(vec: Vec<u8>) -> OsString {
FromInner::from_inner(Buf { inner: vec })
}
#[inline]
fn into_vec(self) -> Vec<u8> {
self.into_inner().inner
}
}
/// Platform-specific extensions to [`OsStr`].
///
/// This trait is sealed: it cannot be implemented outside the standard library.
/// This is so that future additional methods are not breaking changes.
#[stable(feature = "rust1", since = "1.0.0")]
pub trait OsStrExt: Sealed {
#[stable(feature = "rust1", since = "1.0.0")]
/// Creates an [`OsStr`] from a byte slice.
///
/// See the module documentation for an example.
fn from_bytes(slice: &[u8]) -> &Self;
/// Gets the underlying byte view of the [`OsStr`] slice.
///
/// See the module documentation for an example.
#[stable(feature = "rust1", since = "1.0.0")]
fn as_bytes(&self) -> &[u8];
}
#[stable(feature = "rust1", since = "1.0.0")]
impl OsStrExt for OsStr {
#[inline]
fn from_bytes(slice: &[u8]) -> &OsStr {
unsafe { mem::transmute(slice) }
}
#[inline]
fn as_bytes(&self) -> &[u8] {
&self.as_inner().inner
}
}

View File

@@ -0,0 +1 @@
../../../../../crates/shared/src/syscall.rs

View File

@@ -0,0 +1 @@
/home/julien/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/args/common.rs

View File

@@ -0,0 +1,42 @@
pub use super::common::Args;
use crate::ffi::{CStr, OsStr, OsString};
use crate::ptr;
use crate::sync::atomic::{Atomic, AtomicIsize, AtomicPtr, Ordering};
pub fn args() -> Args {
let (argc, argv) = argc_argv();
let mut vec = Vec::with_capacity(argc as usize);
for i in 0..argc {
let ptr = unsafe { argv.offset(i).read() };
if ptr.is_null() {
break;
}
let cstr = unsafe { CStr::from_ptr(ptr) };
vec.push(unsafe { OsStr::from_encoded_bytes_unchecked(cstr.to_bytes()) }.to_owned());
}
Args::new(vec)
}
static ARGC: Atomic<isize> = AtomicIsize::new(0);
static ARGV: Atomic<*mut *const u8> = AtomicPtr::new(ptr::null_mut());
pub unsafe fn init(argc: isize, argv: *const *const u8) {
ARGC.store(argc, Ordering::Relaxed);
ARGV.store(argv as *mut _, Ordering::Relaxed);
}
pub fn argc_argv() -> (isize, *const *const u8) {
let argv = ARGV.load(Ordering::Relaxed);
let argc = if argv.is_null() {
0
} else {
ARGC.load(Ordering::Relaxed)
};
(argc, argv.cast())
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,145 @@
#[path = "unsupported.rs"]
mod unsupported;
use core::io::BorrowedCursor;
pub use self::unsupported::*;
use crate::fs::TryLockError;
use crate::io::{self, IoSlice, IoSliceMut, SeekFrom, prelude::*};
use crate::os::fd::{AsRawFd, FromRawFd, RawFd};
use crate::os::survos::syscall;
use crate::sys::FromInner;
use crate::sys::fs::Path;
use crate::sys::unsupported;
#[derive(Debug)]
pub struct File(RawFd);
impl File {
pub fn open(path: &Path, _opts: &OpenOptions) -> io::Result<File> {
unsafe {
Ok(File::from_raw_fd(syscall::open(
path.as_os_str().to_str().unwrap(),
)))
}
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
todo!()
}
pub fn fsync(&self) -> io::Result<()> {
todo!()
}
pub fn datasync(&self) -> io::Result<()> {
todo!()
}
pub fn lock(&self) -> io::Result<()> {
todo!()
}
pub fn lock_shared(&self) -> io::Result<()> {
todo!()
}
pub fn try_lock(&self) -> Result<(), TryLockError> {
todo!()
}
pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
todo!()
}
pub fn unlock(&self) -> io::Result<()> {
todo!()
}
pub fn truncate(&self, _size: u64) -> io::Result<()> {
todo!()
}
pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
todo!()
}
pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
todo!()
}
pub fn is_read_vectored(&self) -> bool {
todo!()
}
pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
todo!()
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
Ok(syscall::write(self.as_raw_fd(), buf) as usize)
}
pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
todo!()
}
pub fn is_write_vectored(&self) -> bool {
todo!()
}
pub fn flush(&self) -> io::Result<()> {
todo!()
}
pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
todo!()
}
pub fn size(&self) -> Option<io::Result<u64>> {
todo!()
}
pub fn tell(&self) -> io::Result<u64> {
todo!()
}
pub fn duplicate(&self) -> io::Result<File> {
todo!()
}
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
todo!()
}
pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
todo!()
}
}
impl Drop for File {
fn drop(&mut self) {
unsafe {
syscall::close(self.as_raw_fd());
}
}
}
impl FromRawFd for File {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self(fd)
}
}
#[unstable(feature = "survos_std", issue = "none")]
impl FromRawFd for crate::fs::File {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
unsafe { Self::from_inner(File::from_raw_fd(fd)) }
}
}
impl AsRawFd for File {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}

View File

@@ -1,9 +1,8 @@
#[path = "unsupported/mod.rs"]
mod unsupported;
pub use self::unsupported::*;
use crate::os::survos::syscall;
#[path = "../../../../../crates/shared/src/syscall.rs"]
mod syscall;
pub use self::unsupported::*;
/// # Safety
/// `argc` and `argv` are passed by the kernel
@@ -17,6 +16,10 @@ pub unsafe extern "C" fn _start(argc: isize, argv: *const *const u8) -> isize {
unsafe { main(argc, argv) }
}
pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
crate::sys::args::init(argc, argv);
}
pub fn abort_internal() -> ! {
// todo real abort
syscall::exit()

View File

@@ -0,0 +1,392 @@
use core::mem::ManuallyDrop;
use alloc_crate::ffi::CString;
use super::env::{CommandEnv, CommandEnvs};
pub use crate::ffi::OsString as EnvKey;
use crate::ffi::{CStr, OsStr, OsString};
use crate::num::NonZero;
use crate::os::survos::{ffi::OsStrExt, syscall};
use crate::path::Path;
use crate::process::StdioPipes;
use crate::sys::fs::File;
use crate::sys::unsupported;
use crate::{fmt, io};
////////////////////////////////////////////////////////////////////////////////
// Command
////////////////////////////////////////////////////////////////////////////////
pub struct Command {
program: CString,
args: Vec<*const u8>,
env: CommandEnv,
cwd: Option<OsString>,
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,
}
#[derive(Debug)]
pub enum Stdio {
Inherit,
Null,
MakePipe,
ParentStdout,
ParentStderr,
#[allow(dead_code)] // This variant exists only for the Debug impl
InheritFile(File),
}
impl Command {
pub fn new(program: &OsStr) -> Command {
Command {
program: CString::new(program.as_bytes()).unwrap(),
args: vec![
CString::new(program.as_bytes()).unwrap().into_raw(),
crate::ptr::null(),
],
env: Default::default(),
cwd: None,
stdin: None,
stdout: None,
stderr: None,
}
}
pub fn arg(&mut self, arg: &OsStr) {
let argc = self.args.len() - 1;
// Replace the null pointer at the end of the array...
self.args[argc] = CString::new(arg.as_bytes()).unwrap().into_raw();
// ... and recreate it to restore the data structure invariant.
self.args.push(crate::ptr::null());
}
pub fn env_mut(&mut self) -> &mut CommandEnv {
&mut self.env
}
pub fn cwd(&mut self, dir: &OsStr) {
self.cwd = Some(dir.to_owned());
}
pub fn stdin(&mut self, stdin: Stdio) {
self.stdin = Some(stdin);
}
pub fn stdout(&mut self, stdout: Stdio) {
self.stdout = Some(stdout);
}
pub fn stderr(&mut self, stderr: Stdio) {
self.stderr = Some(stderr);
}
pub fn get_program(&self) -> &OsStr {
OsStr::from_bytes(self.program.as_bytes())
}
pub fn get_args(&self) -> CommandArgs<'_> {
let mut iter = CStringIter {
iter: self.args.iter(),
};
iter.next();
CommandArgs { iter }
}
pub fn get_envs(&self) -> CommandEnvs<'_> {
self.env.iter()
}
pub fn get_env_clear(&self) -> bool {
self.env.does_clear()
}
pub fn get_current_dir(&self) -> Option<&Path> {
self.cwd.as_ref().map(|cs| Path::new(cs))
}
pub fn spawn(
&mut self,
_default: Stdio,
_needs_stdin: bool,
) -> io::Result<(Process, StdioPipes)> {
let (args, ..) = Vec::into_raw_parts(self.args.clone());
let child_pid = syscall::spawn(
self.program.to_str().unwrap(),
(self.args.len() - 1) as isize,
args,
);
Ok((
Process(child_pid as usize),
StdioPipes {
stdin: None,
stdout: None,
stderr: None,
},
))
}
}
pub fn output(_cmd: &mut Command) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
unsupported()
}
impl From<ChildPipe> for Stdio {
fn from(pipe: ChildPipe) -> Stdio {
pipe.diverge()
}
}
impl From<io::Stdout> for Stdio {
fn from(_: io::Stdout) -> Stdio {
Stdio::ParentStdout
}
}
impl From<io::Stderr> for Stdio {
fn from(_: io::Stderr) -> Stdio {
Stdio::ParentStderr
}
}
impl From<File> for Stdio {
fn from(file: File) -> Stdio {
Stdio::InheritFile(file)
}
}
impl fmt::Debug for Command {
// show all attributes
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
let mut debug_command = f.debug_struct("Command");
debug_command
.field("program", &self.program)
.field("args", &self.args);
if !self.env.is_unchanged() {
debug_command.field("env", &self.env);
}
if self.cwd.is_some() {
debug_command.field("cwd", &self.cwd);
}
if self.stdin.is_some() {
debug_command.field("stdin", &self.stdin);
}
if self.stdout.is_some() {
debug_command.field("stdout", &self.stdout);
}
if self.stderr.is_some() {
debug_command.field("stderr", &self.stderr);
}
debug_command.finish()
} else {
if let Some(ref cwd) = self.cwd {
write!(f, "cd {cwd:?} && ")?;
}
if self.env.does_clear() {
write!(f, "env -i ")?;
// Altered env vars will be printed next, that should exactly work as expected.
} else {
// Removed env vars need the command to be wrapped in `env`.
let mut any_removed = false;
for (key, value_opt) in self.get_envs() {
if value_opt.is_none() {
if !any_removed {
write!(f, "env ")?;
any_removed = true;
}
write!(f, "-u {} ", key.to_string_lossy())?;
}
}
}
// Altered env vars can just be added in front of the program.
for (key, value_opt) in self.get_envs() {
if let Some(value) = value_opt {
write!(f, "{}={value:?} ", key.to_string_lossy())?;
}
}
// if self.program != self.args[0] {
// write!(f, "[{:?}] ", self.program)?;
// }
// write!(f, "{:?}", self.args[0])?;
// for arg in &self.args[1..] {
// write!(f, " {:?}", arg)?;
// }
Ok(())
}
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
#[non_exhaustive]
pub struct ExitStatus();
impl ExitStatus {
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
Ok(())
}
pub fn code(&self) -> Option<i32> {
Some(0)
}
}
impl fmt::Display for ExitStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<dummy exit status>")
}
}
pub struct ExitStatusError(!);
impl Clone for ExitStatusError {
fn clone(&self) -> ExitStatusError {
self.0
}
}
impl Copy for ExitStatusError {}
impl PartialEq for ExitStatusError {
fn eq(&self, _other: &ExitStatusError) -> bool {
self.0
}
}
impl Eq for ExitStatusError {}
impl fmt::Debug for ExitStatusError {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
impl Into<ExitStatus> for ExitStatusError {
fn into(self) -> ExitStatus {
self.0
}
}
impl ExitStatusError {
pub fn code(self) -> Option<NonZero<i32>> {
self.0
}
}
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitCode(u8);
impl ExitCode {
pub const SUCCESS: ExitCode = ExitCode(0);
pub const FAILURE: ExitCode = ExitCode(1);
pub fn as_i32(&self) -> i32 {
self.0 as i32
}
}
impl From<u8> for ExitCode {
fn from(code: u8) -> Self {
Self(code)
}
}
pub struct Process(usize);
impl Process {
pub fn id(&self) -> u32 {
todo!()
}
pub fn kill(&mut self) -> io::Result<()> {
todo!()
}
pub fn wait(&mut self) -> io::Result<ExitStatus> {
syscall::waitpid(self.0);
Ok(ExitStatus())
}
pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
todo!()
}
}
pub struct CommandArgs<'a> {
iter: CStringIter<'a>,
}
impl<'a> Iterator for CommandArgs<'a> {
type Item = &'a OsStr;
fn next(&mut self) -> Option<&'a OsStr> {
self.iter.next().map(|os| OsStr::from_bytes(os.to_bytes()))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a> ExactSizeIterator for CommandArgs<'a> {
fn len(&self) -> usize {
self.iter.len()
}
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}
impl<'a> fmt::Debug for CommandArgs<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter.clone()).finish()
}
}
pub type ChildPipe = crate::sys::pipe::Pipe;
pub fn read_output(
out: ChildPipe,
_stdout: &mut Vec<u8>,
_err: ChildPipe,
_stderr: &mut Vec<u8>,
) -> io::Result<()> {
match out.diverge() {}
}
pub fn getpid() -> u32 {
panic!("no pids on this platform")
}
/// An iterator over all `CStr`s contained in a `CStringArray`.
#[derive(Clone)]
pub struct CStringIter<'a> {
iter: crate::slice::Iter<'a, *const u8>,
}
impl<'a> Iterator for CStringIter<'a> {
type Item = &'a CStr;
fn next(&mut self) -> Option<&'a CStr> {
// SAFETY:
// `CStringArray` owns all of its strings. Also, this is not the null
// pointer since the last element is excluded when creating `iter`.
self.iter.next().map(|&p| unsafe { CStr::from_ptr(p) })
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
impl<'a> ExactSizeIterator for CStringIter<'a> {
fn len(&self) -> usize {
self.iter.len()
}
fn is_empty(&self) -> bool {
self.iter.is_empty()
}
}