fs/libc: implement some libc functions, fix file times

This commit is contained in:
Mark Poliakov 2025-03-08 01:22:19 +02:00
parent fd0e2cc229
commit 8ffc223a2b
44 changed files with 629 additions and 305 deletions

View File

@ -5,6 +5,7 @@ use libk::vfs::Metadata;
use yggdrasil_abi::{
bitflags,
io::{FileMode, FileType, GroupId, UserId},
time::SystemTime,
};
use crate::Ext2Fs;
@ -253,8 +254,9 @@ impl Inode {
inode: Some(ino),
block_count: self.blocks(fs) as _,
block_size: fs.block_size as _,
ctime: self.ctime as _,
mtime: self.mtime as _,
ctime: SystemTime::from_seconds(self.ctime as u64),
mtime: SystemTime::from_seconds(self.mtime as u64),
atime: SystemTime::from_seconds(self.atime as u64),
}
}
}

View File

@ -176,9 +176,9 @@ impl InodeAccess {
.map_err(|_| Error::InvalidArgument)?;
self.map_mut(|inode| {
inode.mtime = metadata.mtime as _;
inode.atime = metadata.mtime as _;
inode.ctime = metadata.ctime as _;
inode.mtime = metadata.mtime.seconds() as u32;
inode.atime = metadata.mtime.seconds() as u32;
inode.ctime = metadata.ctime.seconds() as u32;
inode.mode.update_permissions(metadata.mode);
inode.uid = uid;

View File

@ -15,6 +15,7 @@ use libk_util::{
};
use yggdrasil_abi::{
io::{DirectoryEntry, FileMode, GroupId, UserId},
time::SystemTime,
util::FixedString,
};
@ -254,8 +255,10 @@ impl DirectoryNode {
FileMode::default_file()
},
inode: Some(cluster.0),
ctime: 0,
mtime: 0,
// TODO
ctime: SystemTime::ZERO,
mtime: SystemTime::ZERO,
atime: SystemTime::ZERO,
block_count: (size.div_ceil(self.fs.layout.bytes_per_sector as u32)) as u64,
block_size: self.fs.layout.bytes_per_sector as u64,
};

View File

@ -10,7 +10,10 @@ use libk::{
vfs::{Filesystem, FilesystemMountOption, Metadata, Node, NodeFlags, NodeRef},
};
use libk_util::get_le_u32;
use yggdrasil_abi::io::{FileMode, GroupId, UserId};
use yggdrasil_abi::{
io::{FileMode, GroupId, UserId},
time::SystemTime,
};
extern crate alloc;
@ -64,8 +67,10 @@ impl Fat32Fs {
uid: UserId::root(),
gid: GroupId::root(),
mode: FileMode::default_dir(),
ctime: 0,
mtime: 0,
// TODO
ctime: SystemTime::ZERO,
mtime: SystemTime::ZERO,
atime: SystemTime::ZERO,
inode: Some(fs.layout.root_directory_cluster.0),
block_size: fs.layout.bytes_per_sector as u64,
block_count: 0,

View File

@ -2,13 +2,14 @@ use alloc::sync::Arc;
use core::any::Any;
use libk::vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl};
use libk_util::sync::IrqSafeSpinlock;
use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock};
use yggdrasil_abi::{error::Error, io::OpenOptions};
use crate::{block::BlockAllocator, bvec::BVec, MemoryFilesystem};
pub(crate) struct FileNode<A: BlockAllocator> {
pub(crate) data: IrqSafeSpinlock<BVec<'static, A>>,
pub(crate) metadata: IrqSafeRwLock<Metadata>,
}
impl<A: BlockAllocator> FileNode<A> {
@ -16,9 +17,10 @@ impl<A: BlockAllocator> FileNode<A> {
Node::regular(
Self {
data: IrqSafeSpinlock::new(BVec::new()),
metadata: IrqSafeRwLock::new(metadata),
},
NodeFlags::IN_MEMORY_PROPS,
Some(metadata),
NodeFlags::empty(),
None,
Some(fs),
)
}
@ -32,6 +34,15 @@ impl<A: BlockAllocator> CommonImpl for FileNode<A> {
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
Ok(self.data.lock().size() as u64)
}
fn metadata(&self, _node: &NodeRef) -> Result<Metadata, Error> {
Ok(*self.metadata.read())
}
fn set_metadata(&self, _node: &NodeRef, metadata: &Metadata) -> Result<(), Error> {
*self.metadata.write() = *metadata;
Ok(())
}
}
impl<A: BlockAllocator> RegularImpl for FileNode<A> {
@ -55,6 +66,7 @@ impl<A: BlockAllocator> RegularImpl for FileNode<A> {
pos: u64,
buf: &mut [u8],
) -> Result<usize, Error> {
self.metadata.write().set_atime_now();
self.data.lock().read(pos, buf)
}
@ -65,10 +77,12 @@ impl<A: BlockAllocator> RegularImpl for FileNode<A> {
pos: u64,
buf: &[u8],
) -> Result<usize, Error> {
self.metadata.write().set_mtime_now();
self.data.lock().write(pos, buf)
}
fn truncate(&self, _node: &NodeRef, new_size: u64) -> Result<(), Error> {
self.metadata.write().set_mtime_now();
self.data.lock().truncate(new_size)
}

View File

@ -20,7 +20,7 @@ use libk_mm::{phys::GlobalPhysicalAllocator, process::ProcessAddressSpace};
use libk_util::{
event::{BoolEvent, OneTimeEvent},
sync::{
spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockWriteGuard},
spin_rwlock::{IrqSafeRwLock, IrqSafeRwLockReadGuard, IrqSafeRwLockWriteGuard},
IrqSafeSpinlock,
},
};
@ -443,7 +443,7 @@ impl Process {
if let Some(status) = child.get_exit_status() {
log::debug!("Child {id} exited with status {status:?} (didn't block)");
// TODO remove child
self.inner.write().remove_child(id);
return Ok(status);
}
@ -455,7 +455,7 @@ impl Process {
loop {
if let Some(status) = child.get_exit_status() {
log::debug!("Child {id} exited with status {status:?}", );
// TODO remove child
self.inner.write().remove_child(id);
break status;
}
@ -464,13 +464,23 @@ impl Process {
}
}
fn poll_any_child_exit(&self) -> Option<(Arc<Process>, IrqSafeRwLockWriteGuard<ProcessInner>)> {
let read = self.inner.read();
if let Some(child) = read.any_exited_child() {
let write = IrqSafeRwLockReadGuard::upgrade(read);
Some((child, write))
} else {
None
}
}
pub fn wait_for_any_child(&self, flags: WaitFlags) -> Result<(ProcessId, ExitCode), Error> {
if let Some(child) = self.inner.read().any_exited_child() {
if let Some((child, mut guard)) = self.poll_any_child_exit() {
let id = child.id;
// unwrap ok: ProcessInner tells the child already exited
let status = child.get_exit_status().unwrap();
log::debug!("Child {id} exited with status {status:?} (didn't block)");
// TODO remove child
log::info!("Child {id} exited with status {status:?} (didn't block)");
guard.remove_child(id);
return Ok((id, status));
}
@ -480,12 +490,12 @@ impl Process {
block! {
loop {
if let Some(child) = self.inner.read().any_exited_child() {
if let Some((child, mut guard)) = self.poll_any_child_exit() {
let id = child.id;
// unwrap ok: ProcessInner tells the child already exited
let status = child.get_exit_status().unwrap();
log::debug!("Child {id} exited with status {status:?}", );
// TODO remove child
log::info!("Child {id} exited with status {status:?}", );
guard.remove_child(id);
break (id, status);
}
@ -705,6 +715,10 @@ impl ProcessInner {
.find(|child| child.has_exited())
}
pub fn remove_child(&mut self, id: ProcessId) -> bool {
self.children.remove(&id).is_some()
}
pub fn remove_thread(&mut self, id: ThreadId) -> bool {
let n = self.threads.len();
self.threads.retain(|t| t.id != id);

View File

@ -14,8 +14,9 @@ use traits::HardlinkImpl;
use yggdrasil_abi::{
bitflags,
error::Error,
io::{FileMode, FileType, GroupId, UserId},
io::{FileMetadataUpdate, FileMetadataUpdateMode, FileMode, FileType, GroupId, UserId},
path::Path,
time::SystemTime,
};
mod access;
@ -126,10 +127,12 @@ pub struct Metadata {
pub block_size: u64,
/// Size of the node (without metadata) in units of `block_size`
pub block_count: u64,
/// Creation time (in seconds)
pub ctime: u64,
/// Modification time (in seconds)
pub mtime: u64,
/// Creation time
pub ctime: SystemTime,
/// Modification time
pub mtime: SystemTime,
/// Last access time
pub atime: SystemTime,
}
struct PropertyCache {
@ -147,14 +150,17 @@ pub struct Node {
}
impl Metadata {
pub const MODE_MASK: FileMode = FileMode::new(0o777);
pub fn now(uid: UserId, gid: GroupId, mode: FileMode, ino: u32) -> Metadata {
let now = real_time().seconds();
let now = real_time();
Metadata {
mode,
uid,
gid,
ctime: now,
mtime: now,
atime: now,
inode: Some(ino),
block_size: 4096,
block_count: 0,
@ -164,6 +170,33 @@ impl Metadata {
pub fn now_root(mode: FileMode, ino: u32) -> Metadata {
Self::now(UserId::root(), GroupId::root(), mode, ino)
}
pub fn set_atime_now(&mut self) {
self.atime = real_time();
}
pub fn set_mtime_now(&mut self) {
let t = real_time();
log::info!("set_mtime_now = {t:?}");
self.mtime = t;
self.atime = t;
}
pub fn update(&mut self, update: &FileMetadataUpdate) {
match update {
FileMetadataUpdate::Times(_) => todo!(),
FileMetadataUpdate::Permissions(mode) => match *mode {
FileMetadataUpdateMode::Set(mode) => {
self.mode &= !Self::MODE_MASK;
self.mode |= mode & Self::MODE_MASK;
}
FileMetadataUpdateMode::Modify { set, clear } => {
self.mode &= !(clear & Self::MODE_MASK);
self.mode |= set & Self::MODE_MASK;
}
},
}
}
}
impl Node {

View File

@ -3,10 +3,7 @@ use core::mem::MaybeUninit;
use libk_util::ext::OptionExt;
use yggdrasil_abi::{
error::Error,
io::{
DirectoryEntry, FileMetadataUpdate, FileMetadataUpdateMode, FileMode, GroupId, OpenOptions,
UserId,
},
io::{DirectoryEntry, FileMetadataUpdate, FileMode, GroupId, OpenOptions, UserId},
};
use crate::vfs::{
@ -228,70 +225,74 @@ impl Node {
return Err(Error::InvalidOperation);
}
let mut cache = self.props.lock();
let common = self.data_as_common();
if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
let mut cache = self.props.lock();
let metadata = cache
.metadata
.get_or_try_insert_with(|| common.metadata(self))?;
let metadata = cache
.metadata
.get_or_try_insert_with(|| common.metadata(self))?;
// let mut metadata = self.metadata()?;
if let Some(uid) = uid {
metadata.uid = uid;
}
if let Some(gid) = gid {
metadata.gid = gid;
}
if let Some(mode) = mode {
metadata.mode = mode;
}
if let Some(uid) = uid {
metadata.uid = uid;
}
if let Some(gid) = gid {
metadata.gid = gid;
}
if let Some(mode) = mode {
metadata.mode = mode;
}
if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
Ok(())
} else {
// Update permissions in the real node
common.set_metadata(self, metadata)?;
let mut metadata = common.metadata(self)?;
if let Some(uid) = uid {
metadata.uid = uid;
}
if let Some(gid) = gid {
metadata.gid = gid;
}
if let Some(mode) = mode {
metadata.mode &= !Metadata::MODE_MASK;
metadata.mode |= mode & Metadata::MODE_MASK;
}
common.set_metadata(self, &metadata)
}
Ok(())
}
/// Returns the "metadata" of the file: uid, gid, access mode
pub fn metadata(self: &NodeRef) -> Result<Metadata, Error> {
let mut cache = self.props.lock();
if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
let mut cache = self.props.lock();
let metadata = cache
.metadata
.get_or_try_insert_with(|| self.data_as_common().metadata(self))?;
let metadata = cache
.metadata
.get_or_try_insert_with(|| self.data_as_common().metadata(self))?;
Ok(*metadata)
Ok(*metadata)
} else {
self.data_as_common().metadata(self)
}
}
pub fn update_metadata(self: &NodeRef, update: &FileMetadataUpdate) -> Result<(), Error> {
let FileMetadataUpdate::Permissions(mode) = update else {
return Err(Error::NotImplemented);
};
let mut cache = self.props.lock();
let common = self.data_as_common();
let metadata = cache
.metadata
.get_or_try_insert_with(|| common.metadata(self))?;
if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
let mut cache = self.props.lock();
match *mode {
FileMetadataUpdateMode::Set(value) => metadata.mode |= value,
FileMetadataUpdateMode::Modify { set, clear } => {
metadata.mode &= !clear;
metadata.mode |= set;
}
}
if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
let metadata = cache
.metadata
.get_or_try_insert_with(|| common.metadata(self))?;
metadata.update(update);
Ok(())
} else {
let mut metadata = common.metadata(self)?;
metadata.update(update);
// Update permissions in the real node
common.set_metadata(self, metadata)?;
common.set_metadata(self, &metadata)
}
Ok(())
}
// TODO clarify directory size

View File

@ -273,7 +273,7 @@ pub(crate) fn get_metadata(
let metadata = node.metadata()?;
let size = node.size()?;
buffer.write(FileAttr {
let metadata = FileAttr {
size,
ty: node.ty(),
mode: metadata.mode,
@ -284,9 +284,9 @@ pub(crate) fn get_metadata(
block_size: metadata.block_size,
ctime: metadata.ctime,
mtime: metadata.mtime,
// TODO atime?
atime: metadata.mtime,
});
};
buffer.write(metadata);
Ok(())
})

View File

@ -103,11 +103,11 @@ struct FileAttr {
pub gid: GroupId,
/// Creation time
pub ctime: u64,
pub ctime: SystemTime,
/// Last modification time
pub mtime: u64,
pub mtime: SystemTime,
/// Last access time
pub atime: u64,
pub atime: SystemTime,
}
/// Raw directory entry representation

View File

@ -26,7 +26,7 @@ pub use abi_serde;
mod generated {
#![allow(missing_docs)]
use crate::{arch::SavedFrame, process::SpawnOption, util::FixedString};
use crate::{arch::SavedFrame, process::SpawnOption, time::SystemTime, util::FixedString};
include!(concat!(env!("OUT_DIR"), "/generated_types.rs"));
}

View File

@ -60,17 +60,21 @@ fn terminate_by_signal(signal: Signal) -> ! {
}
/// Updates the handler for a particular signal. Returns the old handler used for that signal.
///
/// # Safety
///
/// Marked as unsafe due to being thread-unsafe. Will be lifted once I port RwLock into the runtime
/// crate.
pub unsafe fn set_handler(signal: Signal, handler: SignalHandler) -> SignalHandler {
pub fn set_handler(signal: Signal, handler: SignalHandler) -> SignalHandler {
let mut table = TABLE.write();
let entry = &mut table[signal.into_raw() as usize];
core::mem::replace(entry, handler)
}
/// Updates all signal handlers to point to a single handler. Intended to be used with custom
/// signal "routers".
pub fn set_all_handlers(handler: SignalHandler) {
let mut table = TABLE.write();
for entry in table.iter_mut() {
*entry = handler;
}
}
/// Sets the stack that will be used to handle signals **on this thread**.
///
/// # Safety

4
ports/c-tests/compile.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
mkdir -p $3
clang -target $Y_TRIPLE --sysroot $Y_SYSROOT -fPIC -o $3/c-test $1/test.c

4
ports/c-tests/install.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
mkdir -p $Y_SYSROOT/bin
install -m0755 $3/c-test $Y_SYSROOT/bin/c-test

View File

@ -2,4 +2,4 @@ description = "Test C/C++ programs"
version = "0.0.1"
[dependencies]
runtime = ["meta-libc", "libc++"]
runtime = ["meta-libc", "compiler-rt"]

15
ports/c-tests/test.c Normal file
View File

@ -0,0 +1,15 @@
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <sys/stat.h>
int main(int argc, char **argv) {
struct stat s;
if (stat("/etc/test.c", &s) != 0) {
return 1;
}
printf("t = %lds, %ldns\n", s.st_mtim.tv_sec, s.st_mtim.tv_nsec);
return 0;
}

View File

@ -2,5 +2,11 @@
build_dir=$3
RT_LIB_DIR=$Y_SYSROOT/lib/clang/19/lib/$Y_TRIPLE
cd $build_dir
cmake --build . -t install -j >/dev/null
mkdir -p $RT_LIB_DIR
cd $RT_LIB_DIR
ln -sf ../../../../yggdrasil/libclang_rt.builtins-$Y_ARCH.a libclang_rt.builtins.a

View File

@ -5,3 +5,7 @@ set -e
build_dir=$3
cd $build_dir
cmake --build . -t install -j >/dev/null
ln -sf clang $Y_SYSROOT/bin/cc
ln -sf clang++ $Y_SYSROOT/bin/cxx
ln -sf lld $Y_SYSROOT/bin/ld

View File

@ -1,2 +1,3 @@
/target
/dynload-program/target
/etc/rc.d/*.ext

View File

@ -79,14 +79,19 @@ pub fn build_argument(args: &[String], auxv: &[AuxValue]) -> Result<usize, Error
let mut placer = ArgPlacer::new(&mut buffer[..]);
let mut argv = vec![];
let mut envp = vec![];
for arg in args {
argv.push(placer.put_str(arg)? + arg_base);
}
for (key, value) in std::env::vars() {
let env_string = format!("{key}={value}");
envp.push(placer.put_str(&env_string)? + arg_base);
}
// TODO env
let argv = placer.put_ptr_array(&argv)? + arg_base;
let envp = placer.put_ptr_array(&[])? + arg_base;
let envp = placer.put_ptr_array(&envp)? + arg_base;
let auxv = placer.put_aux_array(auxv)? + arg_base;
let argument = placer.position + arg_base;

View File

@ -1,6 +0,0 @@
.section .text
.global _start
_start:
mov $12, %rax
mov $123, %rdi
syscall

View File

@ -1,10 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv) {
printf("Argument count: %d\n", argc);
for (int i = 0; i < argc; ++i) {
printf("Argument %d is %s\n", i, argv[i]);
}
return EXIT_SUCCESS;
}

View File

@ -1,6 +0,0 @@
#include <iostream>
int main(int argc, char **argv) {
std::cout << "Test!!!" << std::endl;
return 0;
}

View File

@ -1,36 +0,0 @@
1. List item one.
List item one continued with a second paragraph followed by an
Indented block.
$ ls *.sh
$ mv *.sh ~/tmp
List item continued with a third paragraph.
2. List item two continued with an open block.
This paragraph is part of the preceding list item.
1. This list is nested and does not require explicit item continuation.
This paragraph is part of the preceding list item.
2. List item b.
This paragraph belongs to item two of the outer list.
> This is a blockquote
>> This is a quote of a quote
>> * This is an item of quote-quote list
>>
>> This is a continuation of quote-quote-list
>> * This is another item of a quote-quote list
Another paragraph
* Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean eget congue risus. Aenean facilisis quis augue ac accumsan. Praesent felis odio, sagittis ut pulvinar a, pharetra non ante. Cras accumsan varius auctor. Suspendisse pharetra mauris eget eros congue, ut scelerisque enim pharetra. Quisque pellentesque ante quis porttitor condimentum. Nullam nisi purus, interdum a dui vitae, hendrerit eleifend leo. Integer tempus neque ut neque faucibus vulputate. Ut orci tellus, interdum et sagittis eu, interdum ut ex. Donec ac consectetur sem. Aenean eget mauris rutrum, condimentum nisi nec, tempor nulla. Nullam ullamcorper nibh vel ligula pellentesque blandit. Curabitur suscipit placerat gravida. Nam id consectetur urna. Morbi viverra lorem vel nulla varius, at placerat nulla posuere. Donec in bibendum ex, ut tincidunt sapien.
* In ut quam tellus. Nunc ac sem vestibulum, sollicitudin ligula id, facilisis tortor. Pellentesque quam ex, ornare id diam ac, sagittis volutpat quam. Etiam faucibus, eros non tristique venenatis, odio risus interdum dolor, porttitor volutpat nulla erat in ex. Nullam venenatis leo justo. Integer ullamcorper auctor orci, non pulvinar nisi volutpat molestie. Phasellus tristique, leo id convallis cursus, diam dolor pretium dui, nec suscipit nisl ligula sit amet magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Maecenas sit amet nibh vel mauris sagittis semper quis efficitur mauris. Aenean iaculis, lectus sit amet placerat scelerisque, dui libero maximus orci, at convallis justo urna eget quam. Aenean luctus felis tristique enim suscipit, non porta eros gravida. Aliquam erat volutpat. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum eleifend iaculis fringilla.
* Fusce eleifend mauris vel urna pretium, non suscipit massa accumsan. Phasellus pretium ultricies accumsan. Suspendisse accumsan bibendum erat, sit amet eleifend ipsum maximus in. Curabitur eleifend, ipsum ut sollicitudin varius, felis lectus elementum nibh, eget bibendum mi ex eget lacus. Nam erat sapien, sodales nec bibendum cursus, accumsan eget magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Ut quis aliquam est. Nunc et erat lacus. Proin consequat eleifend fringilla. Phasellus eget nulla orci. Proin fermentum mi eu nisi posuere blandit. Interdum et malesuada fames ac ante ipsum primis in faucibus.
* In eget facilisis nisi. Donec purus dolor, fringilla nec efficitur nec, elementum id metus. Aenean a scelerisque augue. Nullam tempor porttitor eros tempus imperdiet. Sed euismod felis sed neque venenatis, quis lobortis odio sagittis. Phasellus tristique auctor massa eget vulputate. Etiam nulla tellus, congue ut euismod a, posuere quis sapien. Morbi pellentesque orci sit amet commodo interdum. Nunc eleifend, velit consectetur tempor dictum, sem turpis rutrum erat, eget vehicula justo felis id erat. Morbi condimentum pulvinar sem, sit amet molestie enim suscipit quis. Aliquam convallis ante lectus, at lacinia ex gravida semper. Morbi vel metus aliquam, vulputate neque ac, sodales arcu. Ut quis bibendum sem.
* Vivamus elementum augue eget ligula laoreet, et feugiat turpis efficitur. Mauris eleifend lacus id felis condimentum, vitae volutpat tellus volutpat. Vestibulum justo diam, bibendum sit amet neque quis, commodo luctus nunc. Donec luctus, libero vel viverra venenatis, nisl libero cursus massa, dapibus tempus libero erat facilisis lacus. Donec vitae finibus metus, porta tempor velit. Proin velit odio, facilisis sit amet elementum at, laoreet at tellus. Phasellus convallis, neque sit amet imperdiet hendrerit, nisi quam laoreet odio, molestie venenatis nisl ligula in erat.

View File

@ -1,7 +0,0 @@
#!/bin/sh
/sbin/mount -t ext2 /dev/vb0p1 /mnt
/mnt/bin/clang -cc1as -filetype obj -main-file-name test.S -target-cpu x86-64 -mrelocation-model pic -triple x86_64-unknown-yggdrasil -o /test.o /etc/test.S
/mnt/bin/ld.lld -nostdlib -o /test /test.o
/bin/ls -lh /

View File

@ -147,7 +147,13 @@ unsafe extern "C" fn fcntl(fd: c_int, cmd: c_int, _args: ...) -> CIntCountResult
// TODO kernel support for fcntl
let _file = RawFile::e_try_from(fd)?;
todo!("fcntl({}, {}, ...)", fd, cmd);
match cmd {
F_GETFD => CIntCountResult::success(0),
F_SETFD => CIntCountResult::success(0),
_ => {
todo!("fcntl({}, {}, ...)", fd, cmd);
}
}
}
unsafe fn vopenat(atfd: c_int, pathname: *const c_char, opts: c_int, mut ap: VaList) -> CFdResult {

View File

@ -94,7 +94,7 @@ unsafe extern "C" fn newlocale(
#[no_mangle]
unsafe extern "C" fn setlocale(_category: c_int, _locale: *const c_char) -> *mut c_char {
// TODO
todo!()
c"C".as_ptr().cast_mut()
}
#[no_mangle]

View File

@ -43,7 +43,8 @@ unsafe extern "C" fn pthread_create(
#[no_mangle]
unsafe extern "C" fn pthread_detach(_thread: pthread_t) -> c_int {
todo!()
// TODO
0
}
#[no_mangle]

View File

@ -1,12 +1,12 @@
use core::{
ffi::{c_char, c_int, c_long, c_void},
ffi::{c_char, c_int, c_long, c_void, CStr},
ptr::NonNull,
};
use yggdrasil_rt::process::{signal as rt, Signal};
use crate::{
error::{self, CIntZeroResult, CResult, EResult, TryFromExt},
error::{self, CIntZeroResult, CResult, EResult},
headers::errno::Errno,
signal,
util::PointerExt,
@ -17,7 +17,7 @@ use super::{
sys_types::{pid_t, uid_t},
};
pub type sig_handler_t = unsafe extern "C" fn(SigNumber);
pub type sig_handler_t = unsafe extern "C" fn(c_int);
pub type sigset_t = u64;
@ -72,45 +72,41 @@ pub union sigval {
// SIG_DFL, SIG_ERR, SIG_HOLD, SIG_IGN are in <bits/signal.h>
extern "C" {
fn __sig_terminate(_: SigNumber);
fn __sig_ignore(_: SigNumber);
fn __sig_terminate(_: c_int);
fn __sig_ignore(_: c_int);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
#[repr(C)]
pub enum SigNumber {
SIGHUP = 1,
SIGINT = 2,
SIGQUIT = 3,
SIGILL = 4,
SIGTRAP = 5,
SIGABRT = 6,
SIGBUS = 7,
SIGFPE = 8,
SIGKILL = 9,
SIGUSR1 = 10,
SIGSEGV = 11,
SIGUSR2 = 12,
SIGPIPE = 13,
SIGALRM = 14,
SIGTERM = 15,
SIGCHLD = 17,
SIGCONT = 18,
SIGSTOP = 19,
SIGTSTP = 20,
SIGTTIN = 21,
SIGTTOU = 22,
SIGURG = 23,
SIGXCPU = 24,
SIGXFSZ = 25,
SIGVTALRM = 26,
SIGPROF = 27,
SIGWINCH = 28,
SIGPOLL = 29,
SIGPWR = 30,
SIGSYS = 31,
}
pub const SIGHUP: c_int = 1;
pub const SIGINT: c_int = 2;
pub const SIGQUIT: c_int = 3;
pub const SIGILL: c_int = 4;
pub const SIGTRAP: c_int = 5;
pub const SIGABRT: c_int = 6;
pub const SIGBUS: c_int = 7;
pub const SIGFPE: c_int = 8;
pub const SIGKILL: c_int = 9;
pub const SIGUSR1: c_int = 10;
pub const SIGSEGV: c_int = 11;
pub const SIGUSR2: c_int = 12;
pub const SIGPIPE: c_int = 13;
pub const SIGALRM: c_int = 14;
pub const SIGTERM: c_int = 15;
pub const SIGCHLD: c_int = 17;
pub const SIGCONT: c_int = 18;
pub const SIGSTOP: c_int = 19;
pub const SIGTSTP: c_int = 20;
pub const SIGTTIN: c_int = 21;
pub const SIGTTOU: c_int = 22;
pub const SIGURG: c_int = 23;
pub const SIGXCPU: c_int = 24;
pub const SIGXFSZ: c_int = 25;
pub const SIGVTALRM: c_int = 26;
pub const SIGPROF: c_int = 27;
pub const SIGWINCH: c_int = 28;
pub const SIGPOLL: c_int = 29;
pub const SIGPWR: c_int = 30;
pub const SIGSYS: c_int = 31;
pub const SIGNAL_MAX: c_int = SIGSYS + 1;
pub const SIGEV_NONE: c_int = 0;
pub const SIGEV_SIGNAL: c_int = 1;
@ -134,32 +130,64 @@ pub const SS_DISABLE: c_int = 1 << 9;
pub const MINSIGSTKSZ: usize = 2 * 4096;
pub const SIGSTKSZ: usize = 8 * 4096;
impl TryFromExt<SigNumber> for Signal {
fn e_try_from(value: SigNumber) -> EResult<Self> {
match value {
SigNumber::SIGABRT => EResult::Ok(Signal::Aborted),
SigNumber::SIGSEGV => EResult::Ok(Signal::MemoryAccessViolation),
SigNumber::SIGINT => EResult::Ok(Signal::Interrupted),
SigNumber::SIGILL => EResult::Ok(Signal::InvalidInstruction),
SigNumber::SIGKILL => EResult::Ok(Signal::Killed),
SigNumber::SIGTERM => EResult::Ok(Signal::Terminated),
_ => EResult::Err(Errno::EINVAL),
}
pub fn int_to_signum(value: c_int) -> EResult<Signal> {
match value {
SIGABRT => EResult::Ok(Signal::Aborted),
SIGSEGV => EResult::Ok(Signal::MemoryAccessViolation),
SIGINT => EResult::Ok(Signal::Interrupted),
SIGILL => EResult::Ok(Signal::InvalidInstruction),
SIGKILL => EResult::Ok(Signal::Killed),
SIGTERM => EResult::Ok(Signal::Terminated),
_ => EResult::Err(Errno::EINVAL),
}
}
impl From<Signal> for SigNumber {
fn from(value: Signal) -> Self {
match value {
Signal::Aborted => Self::SIGABRT,
Signal::MemoryAccessViolation => Self::SIGSEGV,
Signal::Interrupted => Self::SIGINT,
Signal::InvalidInstruction => Self::SIGILL,
Signal::Killed => Self::SIGKILL,
Signal::Terminated => Self::SIGTERM,
// Never issued/handled
Signal::Debug => unreachable!(),
}
pub fn signum_to_int(value: Signal) -> c_int {
match value {
Signal::Aborted => SIGABRT,
Signal::MemoryAccessViolation => SIGSEGV,
Signal::Interrupted => SIGINT,
Signal::InvalidInstruction => SIGILL,
Signal::Killed => SIGKILL,
Signal::Terminated => SIGTERM,
// Never issued/handled
Signal::Debug => unreachable!(),
}
}
pub fn signal_string(value: c_int) -> Option<&'static CStr> {
match value {
SIGHUP => Some(c"Hangup"),
SIGINT => Some(c"Interrupted"),
SIGQUIT => Some(c"Quit"),
SIGILL => Some(c"Illegal instruction"),
SIGTRAP => Some(c"Trap/breakpoint"),
SIGABRT => Some(c"Aborted"),
SIGBUS => Some(c"Bus error"),
SIGFPE => Some(c"Floating-point exception"),
SIGKILL => Some(c"Killed"),
SIGUSR1 => Some(c"User signal 1"),
SIGSEGV => Some(c"Segmentation fault"),
SIGUSR2 => Some(c"User signal 2"),
SIGPIPE => Some(c"Broken pipe"),
SIGALRM => Some(c"Timer alarm"),
SIGTERM => Some(c"Terminated"),
SIGCHLD => Some(c"Child quit"),
SIGCONT => Some(c"Continued"),
SIGSTOP => Some(c"Stopped"),
SIGTSTP => Some(c"Terminal stopped"),
SIGTTIN => Some(c"Background process terminal input"),
SIGTTOU => Some(c"Background process terminal output"),
SIGURG => Some(c"Urgent"),
SIGXCPU => Some(c"CPU time limit exceeded"),
SIGXFSZ => Some(c"File size limit exceeded"),
SIGVTALRM => Some(c"Virtual alarm"),
SIGPROF => Some(c"Profiling timer expired"),
SIGWINCH => Some(c"Window size changed"),
SIGPOLL => Some(c"Pollable event"),
SIGPWR => Some(c"Power failure"),
SIGSYS => Some(c"Bad system call"),
_ => None,
}
}
@ -205,7 +233,7 @@ unsafe extern "C" fn raise(_signum: c_int) -> c_int {
#[no_mangle]
unsafe extern "C" fn sigaction(
signum: SigNumber,
signum: c_int,
_new: *const sigaction,
_old: *mut sigaction,
) -> CIntZeroResult {
@ -214,8 +242,13 @@ unsafe extern "C" fn sigaction(
}
#[no_mangle]
unsafe extern "C" fn sigaddset(_mask: *mut sigset_t, _signum: c_int) -> c_int {
todo!()
unsafe extern "C" fn sigaddset(mask: *mut sigset_t, signum: c_int) -> CIntZeroResult {
if signum > 63 || signum <= 0 {
error::errno = Errno::EINVAL;
return CIntZeroResult::ERROR;
}
*mask |= 1 << signum;
CIntZeroResult::SUCCESS
}
#[no_mangle]
@ -277,7 +310,7 @@ unsafe extern "C" fn sigismember(_mask: *const sigset_t, _signum: c_int) -> c_in
}
#[no_mangle]
unsafe extern "C" fn signal(handler: sig_handler_t, signum: SigNumber) -> sig_handler_t {
unsafe extern "C" fn signal(signum: c_int, handler: sig_handler_t) -> sig_handler_t {
// Validate handler
let address = handler as usize;
// NULL or SIG_ERR

View File

@ -37,20 +37,24 @@ unsafe extern "C" fn posix_spawn(
_file_actions: *const posix_spawn_file_actions_t,
_attrp: *const posix_spawnattr_t,
argv: *const *mut c_char,
_envp: *const *mut c_char,
envp: *const *mut c_char,
) -> CIntZeroResult {
let path = path.ensure_str();
let argv = NullTerminatedArrayIter::new(argv);
let envp = NullTerminatedArrayIter::new(envp);
let args = argv
.map(|arg| arg.cast_const().ensure_str())
.collect::<Vec<_>>();
let envs = envp
.map(|env| env.cast_const().ensure_str())
.collect::<Vec<_>>();
log::info!("posix_spawn({path:?}, {args:?})");
log::info!("posix_spawn({path:?}, {args:?}, {envs:?})");
let options = SpawnOptions {
program: path,
arguments: &args,
environment: &[],
environment: &envs,
directory: None,
optional: &[
SpawnOption::CopyFile {
@ -72,6 +76,7 @@ unsafe extern "C" fn posix_spawn(
if let Some(pid) = pid.as_mut() {
*pid = id.bits() as i32;
}
log::info!(" -> {id:?}");
CIntZeroResult::SUCCESS
}
@ -120,19 +125,23 @@ unsafe extern "C" fn posix_spawn_file_actions_addopen(
unsafe extern "C" fn posix_spawn_file_actions_destroy(
_file_actions: *mut posix_spawn_file_actions_t,
) -> c_int {
todo!()
log::warn!("TODO: posix_spawn_file_actions_destroy");
0
}
#[no_mangle]
unsafe extern "C" fn posix_spawn_file_actions_init(
_file_actions: *mut posix_spawn_file_actions_t,
) -> c_int {
todo!()
log::warn!("TODO: posix_spawn_file_actions_init");
0
// todo!()
}
#[no_mangle]
unsafe extern "C" fn posix_spawnattr_destroy(_attrp: *mut posix_spawnattr_t) -> c_int {
todo!()
log::warn!("TODO: posix_spawnattr_destroy");
0
}
#[no_mangle]
@ -186,7 +195,8 @@ unsafe extern "C" fn posix_spawnattr_getsigmask(
#[no_mangle]
unsafe extern "C" fn posix_spawnattr_init(_attrp: *mut posix_spawnattr_t) -> c_int {
todo!()
log::warn!("TODO: posix_spawnattr_init");
0
}
#[no_mangle]
@ -194,7 +204,8 @@ unsafe extern "C" fn posix_spawnattr_setflags(
_attrp: *mut posix_spawnattr_t,
_flags: c_short,
) -> c_int {
todo!()
log::warn!("TODO: posix_spawnattr_setflags");
0
}
#[no_mangle]
@ -234,5 +245,7 @@ unsafe extern "C" fn posix_spawnattr_setsigmask(
_attrp: *mut posix_spawnattr_t,
_sigset: *const c_void,
) -> c_int {
todo!()
log::warn!("TODO: posix_spawnattr_setsigmask");
0
// todo!()
}

View File

@ -7,7 +7,7 @@ use core::{
use crate::{
allocator,
error::CPtrResult,
headers::{errno::Errno, locale::locale_t},
headers::{errno::Errno, locale::locale_t, signal::signal_string},
};
use super::mem::{memcpy, mempcpy, memset};
@ -69,7 +69,15 @@ unsafe extern "C" fn strcmp(a: *const c_char, b: *const c_char) -> c_int {
#[no_mangle]
pub unsafe extern "C" fn strcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char {
stpcpy(dst, src);
let mut i = 0;
loop {
let c = src.add(i).read();
dst.add(i).write(c);
if c == 0 {
break;
}
i += 1;
}
dst
}
@ -246,8 +254,11 @@ unsafe extern "C" fn strrchr(a: *const c_char, c: c_int) -> *mut c_char {
}
#[no_mangle]
unsafe extern "C" fn strsignal(_signum: c_int) -> *mut c_char {
todo!()
unsafe extern "C" fn strsignal(signum: c_int) -> *mut c_char {
match signal_string(signum) {
Some(name) => name.as_ptr().cast_mut(),
None => null_mut(),
}
}
#[no_mangle]

View File

@ -13,7 +13,7 @@ use crate::{
use super::{
fcntl::{AT_FDCWD, AT_SYMLINK_NOFOLLOW},
sys_time::__ygg_timespec_t,
sys_time::{__ygg_timespec_t, timespec},
sys_types::{blkcnt_t, blksize_t, dev_t, gid_t, ino_t, mode_t, nlink_t, off_t, uid_t},
};
@ -77,10 +77,13 @@ impl From<FileAttr> for stat {
let st_size: off_t = value.size.try_into().unwrap();
let st_uid = u32::from(value.uid).try_into().unwrap();
let st_gid = u32::from(value.gid).try_into().unwrap();
// TODO
let st_blksize = value.block_size as _;
let st_blocks = st_size.div_ceil(st_blksize as _).try_into().unwrap();
let st_ino = value.inode.unwrap_or(0) as u64;
let st_ctim = timespec::from(value.ctime);
let st_mtim = timespec::from(value.mtime);
let st_atim = timespec::from(value.atime);
log::info!("{st_mtim:?}, {:?}", value.mtime);
Self {
st_mode,
@ -90,7 +93,12 @@ impl From<FileAttr> for stat {
st_blksize,
st_blocks,
st_ino,
..Default::default()
st_mtim,
st_atim,
st_ctim,
st_nlink: 1,
st_dev: 0,
st_rdev: 0,
}
}
}

View File

@ -4,6 +4,7 @@ use core::{
};
use yggdrasil_abi::time::MICROSECONDS_IN_SECOND;
use yggdrasil_rt::time::SystemTime;
use super::sys_types::{suseconds_t, time_t};
@ -97,6 +98,15 @@ impl From<__ygg_timespec_t> for Duration {
}
}
impl From<SystemTime> for __ygg_timespec_t {
fn from(value: SystemTime) -> Self {
Self {
tv_sec: time_t(value.seconds() as i64),
tv_nsec: value.subsec_nanos() as i64,
}
}
}
impl From<Duration> for __ygg_timespec_t {
fn from(value: Duration) -> Self {
Self {

View File

@ -39,7 +39,9 @@ fn wait_inner(what: ProcessWait, nonblocking: bool) -> EResult<WaitResult> {
if nonblocking {
flags |= WaitFlags::NON_BLOCKING;
}
log::info!("wait_inner({what:?})");
let pid = unsafe { syscall::wait_process(&what, &mut status, flags) }.e_map_err(Errno::from)?;
log::info!(" -> {pid:?}");
EResult::Ok(WaitResult { pid, status })
}
@ -68,8 +70,20 @@ fn encode_exit_status(code: ExitCode) -> c_int {
}
#[no_mangle]
unsafe extern "C" fn wait(_status: *mut c_int) -> pid_t {
todo!()
unsafe extern "C" fn wait(status: *mut c_int) -> pid_t {
let result = wait_inner(ProcessWait::AnyChild, false);
match result {
EResult::Ok(res) => {
if let Some(status) = status.as_mut() {
*status = encode_exit_status(res.status);
}
res.pid.bits() as pid_t
}
EResult::Err(error) => {
error::errno = error;
-1 as pid_t
}
}
}
// TODO siginfo_t

View File

@ -1,13 +1,17 @@
use core::ffi::{c_char, c_int, c_long};
use core::{
ffi::{c_char, c_int, c_long},
ptr::{null_mut, NonNull},
};
use crate::{
error::{CIntZeroResult, TryFromExt},
error::{self, CIntZeroResult, CPtrResult, EResult, TryFromExt},
headers::{
errno::Errno,
fcntl::{faccessat, AT_FDCWD},
sys_types::{gid_t, off_t, uid_t},
},
io::{raw::RawFile, FromRawFd},
util::PointerExt,
io::{self, raw::RawFile, FromRawFd},
util::{PointerExt, PointerStrExt},
};
pub const _PC_PATH_MAX: c_int = 0;
@ -18,8 +22,10 @@ unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> CIntZeroResult
}
#[no_mangle]
unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
todo!()
unsafe extern "C" fn chdir(path: *const c_char) -> CIntZeroResult {
let path = path.ensure_str();
io::set_current_directory(None, path)?;
CIntZeroResult::SUCCESS
}
#[no_mangle]
@ -56,17 +62,21 @@ unsafe extern "C" fn ftruncate(fd: c_int, size: off_t) -> CIntZeroResult {
}
#[no_mangle]
unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> *mut c_char {
let buffer = buf.ensure_slice_mut(len);
// TODO
if buffer.len() < 2 {
return core::ptr::null_mut();
}
unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> CPtrResult<c_char> {
let result = io::get_current_directory(|s| {
if buf.is_null() {
todo!();
}
if len < s.len() + 1 {
return EResult::Err(Errno::ERANGE);
}
let buffer = buf.cast::<u8>().ensure_slice_mut(len);
buffer[..s.len()].copy_from_slice(s.as_bytes());
buffer[s.len()] = 0;
EResult::Ok(NonNull::new_unchecked(buf))
})?;
buffer[0] = b'/' as _;
buffer[1] = 0;
buffer.as_mut_ptr()
CPtrResult::success(result)
}
#[no_mangle]
@ -126,7 +136,9 @@ unsafe extern "C" fn truncate(path: *const c_char, size: off_t) -> c_int {
}
#[no_mangle]
unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
todo!()
let path = path.ensure_str();
log::warn!("TODO: unlink({path:?})");
0
}
#[no_mangle]
unsafe extern "C" fn unlinkat(atfd: c_int, path: *const c_char, flags: c_int) -> c_int {

View File

@ -1,4 +1,11 @@
use core::ffi::{c_char, c_int, c_long, c_uint, c_void};
use core::ffi::{c_char, c_int, c_long, c_uint, c_void, CStr};
use crate::{
env::get_env,
error::{self, EResult},
headers::errno::Errno,
util::PointerExt,
};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
#[non_exhaustive]
@ -12,6 +19,8 @@ pub enum Sysconf {
_SC_ARG_MAX,
}
pub const _CS_PATH: c_int = 1;
#[no_mangle]
unsafe extern "C" fn alarm(value: c_uint) -> c_uint {
todo!()
@ -19,7 +28,33 @@ unsafe extern "C" fn alarm(value: c_uint) -> c_uint {
#[no_mangle]
unsafe extern "C" fn confstr(name: c_int, buf: *mut c_char, size: usize) -> usize {
todo!()
match name {
_CS_PATH => {
let path = match get_env("PATH".as_bytes()) {
EResult::Ok(value) => value,
EResult::Err(err) => {
error::errno = Errno::EINVAL;
return 0;
}
};
let Some(path) = path else {
return 0;
};
let cstr = CStr::from_ptr(path.as_ptr());
if !buf.is_null() && size > 0 {
let buf = buf.cast::<u8>().ensure_slice_mut(size);
let bytes = cstr.to_bytes();
let len = core::cmp::min(buf.len() - 1, bytes.len());
buf[..len].copy_from_slice(&bytes[..len]);
buf[len] = 0;
}
cstr.to_bytes().len()
}
_ => {
error::errno = Errno::EINVAL;
0
}
}
}
#[no_mangle]

View File

@ -136,6 +136,18 @@ pub fn create_directory(at: Option<RawFd>, path: &str, mode: FileMode) -> EResul
EResult::Ok(())
}
pub fn set_current_directory(at: Option<RawFd>, path: &str) -> EResult<()> {
// TODO
assert!(at.is_none());
yggdrasil_rt::io::set_current_directory(path)?;
EResult::Ok(())
}
pub fn get_current_directory<T, F: FnOnce(&str) -> EResult<T>>(mapper: F) -> EResult<T> {
let result = yggdrasil_rt::io::current_directory(mapper)??;
EResult::Ok(result)
}
pub fn realpath<P: AsRef<Path>>(path: P) -> EResult<PathBuf> {
// Cases:
// * /a/b/c -> /a/b/c

View File

@ -1,29 +1,54 @@
use yggdrasil_rt::process::{
signal::{self, SignalHandler},
Signal,
use core::ffi::c_int;
use yggdrasil_rt::{
process::{
signal::{self, SignalHandler},
ExitCode, Signal,
},
sync::rwlock::RwLock,
};
use crate::{
error::{EResult, TryFromExt},
headers::signal::{sig_handler_t, SigNumber},
error::EResult,
headers::{
errno::Errno,
signal::{sig_handler_t, signum_to_int, SIGNAL_MAX},
},
};
const SIGNAL_STACK_SIZE: usize = 4096 * 8;
// These are just stubs for addresses, which get converted into Rust handlers
static SIGNAL_TABLE: RwLock<[Option<unsafe extern "C" fn(c_int)>; SIGNAL_MAX as usize]> =
RwLock::new([None; SIGNAL_MAX as usize]);
#[no_mangle]
unsafe extern "C" fn __sig_ignore(_signum: SigNumber) {
unsafe extern "C" fn __sig_ignore(_signum: c_int) {
unreachable!()
}
#[no_mangle]
unsafe extern "C" fn __sig_terminate(_signum: SigNumber) {
unsafe extern "C" fn __sig_terminate(_signum: c_int) {
unreachable!()
}
pub unsafe fn set(sig: SigNumber, handler: sig_handler_t) -> EResult<sig_handler_t> {
let signal = Signal::e_try_from(sig)?;
fn signal_handler(signal: Signal) {
let signum = signum_to_int(signal);
if let Some(entry) = SIGNAL_TABLE.read()[signum as usize] {
unsafe { entry(signum) };
} else {
let pid = unsafe { yggdrasil_rt::sys::get_pid() }.into_raw();
log::error!("{pid}: terminated by signal {signal:?}");
unsafe { yggdrasil_rt::sys::exit_process(ExitCode::BySignal(Ok(signal))) };
}
}
pub unsafe fn set(sig: c_int, handler: sig_handler_t) -> EResult<sig_handler_t> {
if sig < 0 || sig >= SIGNAL_MAX {
return EResult::Err(Errno::EINVAL);
}
let mut table = SIGNAL_TABLE.write();
// let signal = int_to_signum(sig)?;
let address = handler as usize;
let handler = match handler {
// Transform special cases into Rust signal handlers instead
@ -32,14 +57,11 @@ pub unsafe fn set(sig: SigNumber, handler: sig_handler_t) -> EResult<sig_handler
// This is safe: handler essentially has the same type, just in a wrapper
_ => core::mem::transmute(handler),
};
let old = signal::set_handler(signal, SignalHandler::C(handler));
let old = table[sig as usize].replace(handler);
let old = match old {
// Transform Rust special cases into C "handlers"
SignalHandler::Ignore => __sig_ignore,
SignalHandler::Terminate => __sig_terminate,
// libc doesn't set Rust signal handlers, return terminate just in case
SignalHandler::Rust(_) => __sig_terminate,
SignalHandler::C(handler) => core::mem::transmute(handler),
Some(old) => core::mem::transmute(old),
None => __sig_terminate as sig_handler_t,
};
EResult::Ok(old)
}
@ -52,4 +74,6 @@ pub fn init(main: bool) {
} else {
signal::setup_signal_stack(SIGNAL_STACK_SIZE).expect("Couldn't setup thread signal stack");
}
signal::set_all_handlers(SignalHandler::Rust(signal_handler));
}

View File

@ -58,6 +58,10 @@ name = "kmod"
path = "src/kmod.rs"
# /bin
[[bin]]
name = "echo"
path = "src/echo.rs"
[[bin]]
name = "ls"
path = "src/ls.rs"

View File

@ -0,0 +1,10 @@
fn main() {
let args = std::env::args().skip(1);
for (i, arg) in args.enumerate() {
if i != 0 {
print!(" ");
}
print!("{arg}");
}
println!();
}

View File

@ -250,7 +250,8 @@ impl Entry {
#[cfg(any(target_os = "yggdrasil", rust_analyzer))]
impl Entry {
pub fn is_device(&self) -> bool {
self.ty.map_or(false, |d| d.is_block_device() || d.is_char_device())
self.ty
.map_or(false, |d| d.is_block_device() || d.is_char_device())
}
pub fn is_executable(&self) -> bool {
@ -285,6 +286,7 @@ impl MetadataImpl for Metadata {
}
fn convert_file_time(time: SystemTime) -> chrono::DateTime<chrono::Utc> {
log::info!("time = {time:?}");
let timestamp = time.duration_since(SystemTime::UNIX_EPOCH).unwrap();
chrono::DateTime::from_timestamp(timestamp.as_secs() as _, 0).unwrap()
}
@ -550,6 +552,7 @@ fn run(opts: &Args) -> Vec<Result<(), io::Error>> {
}
pub fn main() -> ExitCode {
logsink::setup_logging(false);
let mut args = Args::parse();
if !stdout().is_terminal() {

View File

@ -1,13 +1,26 @@
#![feature(rustc_private, yggdrasil_os)]
use runtime::{abi as yggdrasil_abi, rt as yggdrasil_rt};
use std::{
os::yggdrasil::io::device::{mount_raw, MountOptions},
io,
process::ExitCode,
time::{Duration, Instant},
};
use yggdrasil_abi::io::MountOptions;
use clap::Parser;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("{0}")]
Io(#[from] io::Error),
#[error("Timed out")]
TimedOut,
}
#[derive(Parser, Debug)]
struct Args {
#[arg(short, help = "Wait for the device to become present in N seconds")]
wait: Option<u64>,
#[arg(short)]
ty: Option<String>,
#[arg(short)]
@ -16,6 +29,36 @@ struct Args {
target: Option<String>,
}
fn try_mount(options: &MountOptions) -> io::Result<()> {
unsafe { yggdrasil_rt::sys::mount(options) }?;
Ok(())
}
fn run(args: &Args, options: &MountOptions) -> Result<(), Error> {
let start = Instant::now();
let timeout = args.wait.map(Duration::from_secs);
loop {
match try_mount(options) {
Ok(()) => return Ok(()),
Err(e) if e.kind() == io::ErrorKind::NotFound => {
if let Some(timeout) = timeout {
if start.elapsed() >= timeout {
return Err(Error::TimedOut);
} else {
std::thread::sleep(Duration::from_millis(100));
}
} else {
return Err(e.into());
}
}
Err(e) => {
return Err(e.into());
}
}
}
}
fn main() -> ExitCode {
let args = Args::parse();
@ -30,18 +73,14 @@ fn main() -> ExitCode {
let options = options.as_deref();
// Permissions are not yet implemented, lol
let result = unsafe {
let options = MountOptions {
source,
filesystem,
options,
target,
};
mount_raw(&options)
let options = MountOptions {
source,
filesystem,
options,
target,
};
match result {
match run(&args, &options) {
Ok(()) => ExitCode::SUCCESS,
Err(err) => {
eprintln!("mount: {:?}", err);

View File

@ -30,10 +30,14 @@ pub mod syntax;
pub enum Error {
#[error("{0}")]
Io(#[from] io::Error),
#[error("Invalid usage")]
InvalidUsage,
}
#[derive(Debug, Parser)]
pub struct ShellArgs {
#[arg(short)]
command: Option<String>,
#[arg(short)]
login: bool,
script: Option<String>,
@ -67,6 +71,38 @@ impl ShellInput {
}
}
fn run_single(env: &Env, command: &str) -> Outcome {
let line = command.trim();
let command = match parse_interactive(line) {
Ok(c) => c,
Err(e) => {
eprintln!("Syntax error: {e}");
return Outcome::err();
}
};
let command = match command.expand(env) {
Ok(c) => c,
Err(e) => {
eprintln!("{e}");
return Outcome::err();
}
};
let (outcome, exit) = match exec::eval(command) {
Ok(res) => res,
Err(error) => {
eprintln!("{error}");
return Outcome::err();
}
};
if let Some(exit) = exit {
exit.exit_process();
}
outcome
}
fn run(mut input: ShellInput, env: &Env) -> Result<(), Error> {
let mut line = String::new();
loop {
@ -140,16 +176,27 @@ fn find_script<P: AsRef<Path>>(arg: &P) -> &Path {
}
fn run_wrapper(args: ShellArgs, env: &mut Env) -> Result<(), Error> {
match args.script {
Some(script) => {
let shell_name = std::env::args().next().unwrap();
match (args.command, args.script) {
(Some(_), Some(_)) => {
eprintln!("{shell_name}: cannot mix '-c' and regular arguments");
Err(Error::InvalidUsage)
}
(Some(command), None) => match run_single(env, &command) {
Outcome::ExitShell(_) => unreachable!(),
Outcome::Process(status) if status.success() => ExitCode::SUCCESS.exit_process(),
Outcome::Process(status) if let Some(code) = status.code() => std::process::exit(code),
Outcome::Process(_) => todo!(),
Outcome::Builtin(code) => code.exit_process(),
},
(None, Some(script)) => {
let script_path = find_script(&script);
let script_path_str = script_path.to_str().unwrap();
env.put_var("0", script_path_str.into());
let script = BufReader::new(File::open(script_path)?);
run(ShellInput::File(script), env)
}
None => {
let shell_name = std::env::args().next().unwrap();
(None, None) => {
env.put_var("0", shell_name.into());
run(ShellInput::Interactive, env)
}

View File

@ -29,6 +29,7 @@ const PROGRAMS: &[(&str, &str)] = &[
// shell
("shell", "bin/sh"),
// sysutils
("echo", "bin/echo"),
("mount", "sbin/mount"),
("login", "sbin/login"),
("strace", "bin/strace"),
@ -150,8 +151,6 @@ fn build_rootfs<S: AsRef<Path>, D: AsRef<Path>>(
}
}
// TODO this is a temporary hack
fs::create_dir_all(rootfs_dir.join("lib"))?;
// TODO other architectures
util::copy_file(
env.workspace_root.join(format!(
@ -162,13 +161,6 @@ fn build_rootfs<S: AsRef<Path>, D: AsRef<Path>>(
rootfs_dir.join("dynload-program"),
)?;
let libstd_so = env.workspace_root.join(format!(
"toolchain/build/host/stage1-std/{}-unknown-yggdrasil/release/libstd.so",
env.arch.name()
));
util::copy_file(libstd_so, rootfs_dir.join("lib/libstd.so"))?;
log::info!("Installing extras");
for (src, dst) in install_extra {
util::copy_file(src, rootfs_dir.join(dst))?;