From ba00c97c6676a73a50128c55f81d533a9e9cad96 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Fri, 20 Dec 2024 23:00:43 +0200 Subject: [PATCH] vfs: force all symlinks to be path-based, chdir/getcwd --- Cargo.lock | 166 +++++- Cargo.toml | 3 +- kernel/driver/block/ahci/src/lib.rs | 11 +- kernel/driver/block/nvme/src/lib.rs | 11 +- kernel/driver/net/core/src/l3/mod.rs | 2 +- kernel/libk/Cargo.toml | 1 + kernel/libk/libk-util/src/ext.rs | 20 + kernel/libk/libk-util/src/io.rs | 26 + kernel/libk/libk-util/src/lib.rs | 1 + kernel/libk/src/device/manager.rs | 13 +- kernel/libk/src/fs/devfs.rs | 24 +- kernel/libk/src/random.rs | 4 +- kernel/libk/src/vfs/file/mod.rs | 30 +- kernel/libk/src/vfs/file/regular.rs | 16 + kernel/libk/src/vfs/ioctx.rs | 483 +++++++++++++----- kernel/libk/src/vfs/node/impls.rs | 69 +-- kernel/libk/src/vfs/node/mod.rs | 46 +- kernel/libk/src/vfs/node/ops.rs | 3 +- kernel/libk/src/vfs/node/traits.rs | 10 +- kernel/src/arch/x86_64/mod.rs | 4 +- kernel/src/fs/mod.rs | 2 +- kernel/src/fs/pseudo.rs | 12 +- kernel/src/init.rs | 19 +- kernel/src/main.rs | 8 +- kernel/src/syscall/imp/mod.rs | 6 +- kernel/src/syscall/imp/sys_io.rs | 42 +- kernel/src/syscall/imp/sys_process.rs | 15 +- kernel/src/syscall/mod.rs | 3 + lib/abi/def/io.abi | 6 + lib/abi/def/yggdrasil.abi | 3 + lib/abi/src/io/mod.rs | 4 +- lib/abi/src/path/path_buf.rs | 49 +- lib/abi/src/process/mod.rs | 7 +- lib/runtime/src/io.rs | 29 +- lib/runtime/src/sys/mod.rs | 6 +- test.c | 35 -- tool/abi-generator/src/abi/ty/primitive.rs | 7 +- userspace/dyn-loader/src/search.rs | 4 + userspace/etc/test.S | 4 + userspace/etc/test.c | 3 + .../lib/ygglibc/src/headers/dlfcn/mod.rs | 2 +- .../lib/ygglibc/src/headers/fcntl/mod.rs | 37 +- .../lib/ygglibc/src/headers/signal/mod.rs | 4 +- .../lib/ygglibc/src/headers/spawn/mod.rs | 6 +- .../lib/ygglibc/src/headers/stdio/util.rs | 17 +- .../lib/ygglibc/src/headers/stdlib/io.rs | 28 +- .../lib/ygglibc/src/headers/stdlib/random.rs | 8 +- .../lib/ygglibc/src/headers/sys_mman/mod.rs | 67 ++- .../ygglibc/src/headers/sys_resource/mod.rs | 12 +- .../lib/ygglibc/src/headers/sys_stat/mod.rs | 19 +- .../lib/ygglibc/src/headers/time/timer.rs | 33 +- .../lib/ygglibc/src/headers/unistd/fs.rs | 17 +- .../lib/ygglibc/src/headers/unistd/io.rs | 18 +- .../lib/ygglibc/src/headers/unistd/util.rs | 6 +- userspace/lib/ygglibc/src/io/mod.rs | 49 +- userspace/lib/ygglibc/src/io/raw.rs | 16 +- userspace/lib/ygglibc/src/lib.rs | 1 + userspace/lib/ygglibc/src/random.rs | 36 ++ userspace/shell/src/builtins.rs | 24 + userspace/shell/src/main.rs | 2 + xtask/src/build/userspace.rs | 2 +- 61 files changed, 1204 insertions(+), 407 deletions(-) create mode 100644 kernel/libk/libk-util/src/ext.rs create mode 100644 userspace/etc/test.S create mode 100644 userspace/etc/test.c create mode 100644 userspace/lib/ygglibc/src/random.rs diff --git a/Cargo.lock b/Cargo.lock index d34d5984..80b0889e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1055,6 +1055,7 @@ dependencies = [ "serde", "serde_json", "static_assertions", + "test-log", "tokio", "uuid", "yggdrasil-abi", @@ -1188,6 +1189,15 @@ dependencies = [ "hashbrown 0.15.0", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "memchr" version = "2.7.4" @@ -1239,6 +1249,16 @@ dependencies = [ "adler2", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-derive" version = "0.3.3" @@ -1299,6 +1319,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "paste" version = "1.0.15" @@ -1387,7 +1413,7 @@ dependencies = [ "rand", "rand_chacha", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.8.5", "rusty-fork", "tempfile", "unarray", @@ -1474,8 +1500,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.8", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1486,9 +1521,15 @@ checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1647,6 +1688,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1726,6 +1776,28 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "test-log" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dffced63c2b5c7be278154d76b479f9f9920ed34e7574201407f0b14e2bbb93" +dependencies = [ + "env_logger", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "thiserror" version = "1.0.65" @@ -1746,6 +1818,16 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "tinyvec" version = "1.8.0" @@ -1829,6 +1911,54 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + [[package]] name = "unarray" version = "0.1.4" @@ -1894,6 +2024,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2018,6 +2154,22 @@ dependencies = [ "winsafe", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.9" @@ -2027,6 +2179,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 47bd936d..38a79f29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ exclude = [ "tool/abi-generator", "toolchain", "userspace/dynload-program", - "userspace/lib/ygglibc" + "userspace/lib/ygglibc", + "toolchain-c" ] members = [ "xtask", diff --git a/kernel/driver/block/ahci/src/lib.rs b/kernel/driver/block/ahci/src/lib.rs index 25bfd934..c4414e5f 100644 --- a/kernel/driver/block/ahci/src/lib.rs +++ b/kernel/driver/block/ahci/src/lib.rs @@ -22,7 +22,7 @@ use ygg_driver_pci::{ device::{PciDeviceInfo, PreferredInterruptMode}, PciCommandRegister, PciConfigurationSpace, }; -use yggdrasil_abi::error::Error; +use yggdrasil_abi::{error::Error, io::FileMode}; use crate::regs::{Version, CAP, GHC, SSTS}; @@ -252,7 +252,7 @@ pub fn register_sata_drive(drive: Arc, probe: bool) { let name = format!("sd{letter}"); log::info!("Register SATA drive: {name}"); - devfs::add_named_block_device(drive.clone(), name.clone()).ok(); + devfs::add_named_block_device(drive.clone(), name.clone(), FileMode::new(0o600)).ok(); if probe { runtime::spawn(async move { @@ -260,7 +260,12 @@ pub fn register_sata_drive(drive: Arc, probe: bool) { log::info!("Probing partitions for {name}"); probe_partitions(drive, |index, partition| { let partition_name = format!("{name}{}", index + 1); - devfs::add_named_block_device(Arc::new(partition), partition_name).ok(); + devfs::add_named_block_device( + Arc::new(partition), + partition_name, + FileMode::new(0o600), + ) + .ok(); }) .await .ok(); diff --git a/kernel/driver/block/nvme/src/lib.rs b/kernel/driver/block/nvme/src/lib.rs index ebb6c6fe..22d3cbc0 100644 --- a/kernel/driver/block/nvme/src/lib.rs +++ b/kernel/driver/block/nvme/src/lib.rs @@ -37,7 +37,7 @@ use ygg_driver_pci::{ device::{PciDeviceInfo, PreferredInterruptMode}, PciCommandRegister, PciConfigurationSpace, }; -use yggdrasil_abi::error::Error; +use yggdrasil_abi::{error::Error, io::FileMode}; use crate::{ command::{IoRead, IoWrite}, @@ -475,7 +475,7 @@ pub fn register_nvme_controller(controller: Arc) { pub fn register_nvme_namespace(namespace: Arc, probe: bool) { let name = format!("nvme{}n{}", namespace.controller_id(), namespace.id()); log::info!("Register NVMe namespace: {name}"); - devfs::add_named_block_device(namespace.clone(), name.clone()).ok(); + devfs::add_named_block_device(namespace.clone(), name.clone(), FileMode::new(0o600)).ok(); if probe { runtime::spawn(async move { @@ -483,7 +483,12 @@ pub fn register_nvme_namespace(namespace: Arc, probe: bool) { log::info!("Probing partitions for {name}"); probe_partitions(namespace, |index, partition| { let partition_name = format!("{name}p{}", index + 1); - devfs::add_named_block_device(Arc::new(partition), partition_name).ok(); + devfs::add_named_block_device( + Arc::new(partition), + partition_name, + FileMode::new(0o600), + ) + .ok(); }) .await .inspect_err(|error| log::error!("{name}: partition probe failed: {error:?}")) diff --git a/kernel/driver/net/core/src/l3/mod.rs b/kernel/driver/net/core/src/l3/mod.rs index 7d42fd5f..ecc39b16 100644 --- a/kernel/driver/net/core/src/l3/mod.rs +++ b/kernel/driver/net/core/src/l3/mod.rs @@ -145,7 +145,7 @@ pub async fn handle_accepted(l3_packet: L3Packet) -> Result<(), Error> { } } -impl<'a, 'i> L4ResolvedPacket<'a, 'i> { +impl L4ResolvedPacket<'_, '_> { pub async fn lookup_gateway_mac(&self) -> Result { arp::lookup(self.interface.id, self.gateway_ip, true).await } diff --git a/kernel/libk/Cargo.toml b/kernel/libk/Cargo.toml index 9256f695..70922eed 100644 --- a/kernel/libk/Cargo.toml +++ b/kernel/libk/Cargo.toml @@ -37,6 +37,7 @@ lru = "0.12.3" [dev-dependencies] tokio = { workspace = true, features = ["rt", "macros"] } libc.workspace = true +test-log = "0.2.16" [lints] workspace = true diff --git a/kernel/libk/libk-util/src/ext.rs b/kernel/libk/libk-util/src/ext.rs new file mode 100644 index 00000000..8312cde9 --- /dev/null +++ b/kernel/libk/libk-util/src/ext.rs @@ -0,0 +1,20 @@ +//! Extensions to core types + +pub trait OptionExt { + fn get_or_try_insert_with Result>( + &mut self, + init: F, + ) -> Result<&mut T, E>; +} + +impl OptionExt for Option { + fn get_or_try_insert_with Result>( + &mut self, + init: F, + ) -> Result<&mut T, E> { + match self { + Self::None => Ok(self.insert(init()?)), + Self::Some(value) => Ok(value), + } + } +} diff --git a/kernel/libk/libk-util/src/io.rs b/kernel/libk/libk-util/src/io.rs index 6fad6b2a..636b641b 100644 --- a/kernel/libk/libk-util/src/io.rs +++ b/kernel/libk/libk-util/src/io.rs @@ -1,6 +1,18 @@ use alloc::sync::Arc; use yggdrasil_abi::{error::Error, io::SeekFrom}; +/// Positional read interface for VFS objects +pub trait ReadAt { + /// Reads data at offset `pos` without changing the file position + fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result; +} + +/// Positional write interface for VFS objects +pub trait WriteAt { + /// Writes data at offset `pos` without changing the file position + fn write_at(&self, pos: u64, buf: &[u8]) -> Result; +} + /// Immutable read interface for VFS objects pub trait Read { /// Reads bytes into the given buffer @@ -49,6 +61,13 @@ impl Read for Arc { } } +impl ReadAt for Arc { + #[inline] + fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result { + self.as_ref().read_at(pos, buf) + } +} + impl Seek for Arc { #[inline] fn seek(&self, from: SeekFrom) -> Result { @@ -60,3 +79,10 @@ impl Seek for Arc { self.as_ref().tell() } } + +impl WriteAt for Arc { + #[inline] + fn write_at(&self, pos: u64, buf: &[u8]) -> Result { + self.as_ref().write_at(pos, buf) + } +} diff --git a/kernel/libk/libk-util/src/lib.rs b/kernel/libk/libk-util/src/lib.rs index 04c1173d..27033588 100644 --- a/kernel/libk/libk-util/src/lib.rs +++ b/kernel/libk/libk-util/src/lib.rs @@ -18,6 +18,7 @@ use core::{ }; pub mod event; +pub mod ext; pub mod hash_table; pub mod io; pub mod lru_hash_table; diff --git a/kernel/libk/src/device/manager.rs b/kernel/libk/src/device/manager.rs index f061144a..20605c79 100644 --- a/kernel/libk/src/device/manager.rs +++ b/kernel/libk/src/device/manager.rs @@ -6,7 +6,7 @@ use core::{ use alloc::{collections::BTreeMap, format, sync::Arc, vec::Vec}; use device_api::device::Device; use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit}; -use yggdrasil_abi::error::Error; +use yggdrasil_abi::{error::Error, io::FileMode}; use crate::{ config, @@ -29,7 +29,7 @@ struct GenericRegistry { last_index: AtomicU32, } -pub type DisplayRegisterCallback = fn(&Arc, Option); +pub type DisplayRegisterCallback = fn(&Arc, Option<(&str, NodeRef)>); // Manages devices: fb pub struct DisplayDeviceRegistry { @@ -78,7 +78,7 @@ impl TerminalRegistry { pub fn register(&self, terminal: Arc) -> Result { let id = self.registry.register(terminal.clone())?; - devfs::add_named_char_device(terminal, format!("tty{id}")).ok(); + devfs::add_named_char_device(terminal, format!("tty{id}"), FileMode::new(0o660)).ok(); Ok(id) } } @@ -96,7 +96,7 @@ impl SerialTerminalRegistry { sink: Option>, ) -> Result { let id = self.registry.register(terminal.clone())?; - devfs::add_named_char_device(terminal, format!("ttyS{id}")).ok(); + devfs::add_named_char_device(terminal, format!("ttyS{id}"), FileMode::new(0o660)).ok(); if let Some(sink) = sink { debug::add_serial_sink(sink, config::get().debug.serial_level); } @@ -119,9 +119,10 @@ impl DisplayDeviceRegistry { pub fn register(&self, display: Arc, boot: bool) -> Result { let wrapper = Arc::new(DisplayWrapper::new(display, boot)?); let id = self.registry.register(wrapper.clone())?; - let node = devfs::add_named_block_device(wrapper.clone(), format!("fb{id}")); + let name = format!("fb{id}"); + let node = devfs::add_named_block_device(wrapper.clone(), &name, FileMode::new(0o600)); if let Some(callback) = self.callback.try_get() { - callback(&wrapper.device, node.ok()); + callback(&wrapper.device, node.ok().map(|node| (name.as_str(), node))); } Ok(id) } diff --git a/kernel/libk/src/fs/devfs.rs b/kernel/libk/src/fs/devfs.rs index c10f0274..b0b4de93 100644 --- a/kernel/libk/src/fs/devfs.rs +++ b/kernel/libk/src/fs/devfs.rs @@ -2,12 +2,12 @@ use alloc::{string::String, sync::Arc}; use libk_util::OneTimeInit; -use yggdrasil_abi::error::Error; +use yggdrasil_abi::{error::Error, io::FileMode}; use crate::{ device::{block::BlockDevice, char::CharDevice}, vfs::{ - impls::{fixed_symlink, MemoryDirectory}, + impls::{fixed_path_symlink, MemoryDirectory}, AccessToken, Node, NodeFlags, NodeRef, }, }; @@ -16,7 +16,7 @@ static DEVFS_ROOT: OneTimeInit = OneTimeInit::new(); /// Sets up the device filesystem pub fn init() { - let root = MemoryDirectory::empty(); + let root = MemoryDirectory::empty(FileMode::new(0o555)); DEVFS_ROOT.init(root); } @@ -29,21 +29,24 @@ pub fn root() -> &'static NodeRef { DEVFS_ROOT.get() } -pub fn set_node(name: &str, node: NodeRef) -> Result<(), Error> { - log::info!("Update node: {name}"); +pub fn redirect(name: &str, destination: &str) -> Result<(), Error> { + log::info!("Redirect {name} -> {destination}"); let root = DEVFS_ROOT.get(); root.remove_file(name, unsafe { AccessToken::authorized() }) .ok(); - - root.add_child(name, fixed_symlink(node)) + root.add_child(name, fixed_path_symlink(destination)) } /// Adds a character device with a custom name -pub fn add_named_char_device(dev: Arc, name: String) -> Result<(), Error> { +pub fn add_named_char_device( + dev: Arc, + name: String, + mode: FileMode, +) -> Result<(), Error> { log::info!("Add char device: {}", name); - let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS); + let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS, mode); DEVFS_ROOT.get().add_child(name, node) } @@ -52,11 +55,12 @@ pub fn add_named_char_device(dev: Arc, name: String) -> Result<( pub fn add_named_block_device>( dev: Arc, name: S, + mode: FileMode, ) -> Result { let name = name.into(); log::info!("Add block device: {}", name); - let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS); + let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS, mode); DEVFS_ROOT.get().add_child(name, node.clone())?; diff --git a/kernel/libk/src/random.rs b/kernel/libk/src/random.rs index 01e78d99..651ddcd1 100644 --- a/kernel/libk/src/random.rs +++ b/kernel/libk/src/random.rs @@ -62,7 +62,7 @@ impl RandomState { static RANDOM_STATE: OneTimeInit> = OneTimeInit::new(); /// Fills `buf` with random bytes -pub fn read(buf: &mut [u8]) { +pub fn read_unsecure(buf: &mut [u8]) { let state = RANDOM_STATE.get(); state.lock().read_buf(buf) } @@ -70,7 +70,7 @@ pub fn read(buf: &mut [u8]) { pub fn range(a: u64, b: u64) -> u64 { assert!(b > a); let mut bytes = [0; 8]; - read(&mut bytes); + read_unsecure(&mut bytes); let v = u64::from_ne_bytes(bytes) % (b - a); a + v } diff --git a/kernel/libk/src/vfs/file/mod.rs b/kernel/libk/src/vfs/file/mod.rs index 3b13dd49..75d02667 100644 --- a/kernel/libk/src/vfs/file/mod.rs +++ b/kernel/libk/src/vfs/file/mod.rs @@ -14,7 +14,10 @@ use alloc::{ use async_trait::async_trait; use device::{BlockFile, CharFile}; use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider}; -use libk_util::sync::IrqSafeSpinlock; +use libk_util::{ + io::{ReadAt, WriteAt}, + sync::IrqSafeSpinlock, +}; use yggdrasil_abi::{ error::Error, io::{ @@ -434,17 +437,39 @@ impl Write for File { } } +impl ReadAt for File { + fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result { + match self { + Self::Regular(file) => file.read_at(pos, buf), + _ => todo!(), + } + } +} + +impl WriteAt for File { + fn write_at(&self, pos: u64, buf: &[u8]) -> Result { + match self { + Self::Regular(file) => file.write_at(pos, buf), + _ => todo!(), + } + } +} + impl Seek for File { fn tell(&self) -> Result { match self { Self::Regular(file) => Ok(*file.position.lock()), Self::Block(file) => Ok(*file.position.lock()), Self::Directory(_) => Err(Error::IsADirectory), - _ => Err(Error::InvalidOperation), + _ => Ok(0), } } fn seek(&self, from: SeekFrom) -> Result { + if from == SeekFrom::Current(0) { + return self.tell(); + } + match self { Self::Regular(file) => file.seek(from), Self::Block(file) => file.seek(from), @@ -593,7 +618,6 @@ mod tests { }; use crate::vfs::{ - device::CharDevice, file::DirectoryOpenPosition, impls::const_value_node, node::{AccessToken, CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl}, diff --git a/kernel/libk/src/vfs/file/regular.rs b/kernel/libk/src/vfs/file/regular.rs index 2545afe4..9daca4aa 100644 --- a/kernel/libk/src/vfs/file/regular.rs +++ b/kernel/libk/src/vfs/file/regular.rs @@ -36,6 +36,22 @@ impl RegularFile { Ok(count) } + pub fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result { + if !self.read { + return Err(Error::InvalidFile); + } + let reg = self.node.as_regular()?; + reg.read(&self.node, self.instance_data.as_ref(), pos, buf) + } + + pub fn write_at(&self, pos: u64, buf: &[u8]) -> Result { + if !self.write { + return Err(Error::InvalidFile); + } + let reg = self.node.as_regular()?; + reg.write(&self.node, self.instance_data.as_ref(), pos, buf) + } + // TODO should seek beyond the end of a read-only file be allowed? pub fn seek(&self, from: SeekFrom) -> Result { let mut position = self.position.lock(); diff --git a/kernel/libk/src/vfs/ioctx.rs b/kernel/libk/src/vfs/ioctx.rs index 69eb3574..8d51322c 100644 --- a/kernel/libk/src/vfs/ioctx.rs +++ b/kernel/libk/src/vfs/ioctx.rs @@ -1,7 +1,6 @@ -use alloc::borrow::ToOwned; use yggdrasil_abi::{ error::Error, - io::{FileMode, FileType, GroupId, OpenOptions, UserId}, + io::{AccessMode, FileMode, FileType, GroupId, OpenOptions, UserId}, path::{Path, PathBuf}, }; @@ -40,7 +39,7 @@ impl IoContext { gid: GroupId::root(), umask: FileMode::new(0o022), cwd_node: root.clone(), - cwd_path: PathBuf::new(), + cwd_path: PathBuf::from("/"), root, } } @@ -104,47 +103,48 @@ impl IoContext { /// Checks if the current user can access given [crate::Node] and, if so, returns an /// [AccessToken]. - pub fn check_access(&self, action: Action, node: &NodeRef) -> Result { + pub fn check_access(&self, node: &NodeRef, mode: AccessMode) -> Result { let metadata = node.metadata()?; - let allow = match action { - Action::Read => { - self.uid.is_root() - | metadata.user_read(self.uid) - | metadata.group_read(self.gid) - | metadata.other_read() - } - Action::Write => { - self.uid.is_root() - | metadata.user_write(self.uid) - | metadata.group_write(self.gid) - | metadata.other_write() - } - Action::Execute => { - metadata.user_exec(self.uid) | metadata.group_exec(self.gid) | metadata.other_exec() - } - }; - - if allow { - Ok(unsafe { AccessToken::authorized() }) - } else { - Err(Error::PermissionDenied) + if mode.contains(AccessMode::READ) + && !(self.uid.is_root() + | metadata.user_read(self.uid) + | metadata.group_read(self.gid) + | metadata.other_read()) + { + return Err(Error::PermissionDenied); } + + if mode.contains(AccessMode::WRITE) + && !(self.uid.is_root() + | metadata.user_write(self.uid) + | metadata.group_write(self.gid) + | metadata.other_write()) + { + return Err(Error::PermissionDenied); + } + + if mode.contains(AccessMode::EXEC) + && !(metadata.user_exec(self.uid) + | metadata.group_exec(self.gid) + | metadata.other_exec()) + { + return Err(Error::PermissionDenied); + } + + Ok(unsafe { AccessToken::authorized() }) } /// Changes current working directory to `path`. Will fail if access is denied or the path does /// not point to a directory. pub fn set_cwd>(&mut self, path: P) -> Result<(), Error> { let path = path.as_ref(); - if !path.is_absolute() { - todo!(); - } - let node = self._find(self.root.clone(), path.trim_start_separators(), true, true)?; + let (path, node) = self.normalize(path, true)?; if !node.is_directory() { return Err(Error::NotADirectory); } + self.cwd_path = path; self.cwd_node = node; - self.cwd_path = path.to_owned(); Ok(()) } @@ -165,12 +165,7 @@ impl IoContext { if !target.is_absolute() { todo!(); } - let target_node = self._find( - self.root.clone(), - target.trim_start_separators(), - true, - false, - )?; + let target_node = self._find(self.root.clone(), target.trim_start_separators(), true)?; target_node.set_mountpoint_target(fs_root) } @@ -186,12 +181,12 @@ impl IoContext { mode: FileMode, ) -> Result { let path = path.as_ref(); - let node = match self.find(at.clone(), path, true, true) { + let node = match self.find(at.clone(), path, true) { Ok(node) => node, Err(Error::DoesNotExist) if opts.contains(OpenOptions::CREATE) => { // let create_mode = mode & !self.umask; let (parent, name) = path.split_right(); - let parent = self.find(at, parent, true, true)?; + let parent = self.find(at, parent, true)?; let create_info = CreateInfo { name: name.into(), mode: mode & !self.umask, @@ -199,7 +194,7 @@ impl IoContext { gid: self.gid, ty: FileType::File, }; - let access = self.check_access(Action::Write, &parent)?; + let access = self.check_access(&parent, AccessMode::WRITE)?; parent.create(&create_info, access)? } Err(err) => return Err(err), @@ -209,18 +204,18 @@ impl IoContext { let mut access = unsafe { AccessToken::authorized() }; if opts.contains(OpenOptions::READ) { - access += self.check_access(Action::Read, &node)?; + access += self.check_access(&node, AccessMode::READ)?; } if opts.contains(OpenOptions::WRITE) { - access += self.check_access(Action::Write, &node)?; + access += self.check_access(&node, AccessMode::WRITE)?; } node.open(opts, access) } pub fn open_executable>(&mut self, path: P) -> Result { - let node = self.find(None, path, true, true)?; - let access = self.check_access(Action::Read, &node)?; + let node = self.find(None, path, true)?; + let access = self.check_access(&node, AccessMode::EXEC)?; node.open(OpenOptions::READ, access) } @@ -233,8 +228,8 @@ impl IoContext { ) -> Result { let path = path.as_ref(); let (parent, name) = path.split_right(); - let parent = self.find(at, parent, true, true)?; - let access = self.check_access(Action::Write, &parent)?; + let parent = self.find(at, parent, true)?; + let access = self.check_access(&parent, AccessMode::WRITE)?; let create_info = CreateInfo { name: name.into(), ty: FileType::Directory, @@ -255,8 +250,8 @@ impl IoContext { ) -> Result<(), Error> { let path = path.as_ref(); let (parent, name) = path.split_right(); - let parent = self.find(at, parent, true, true)?; - let access = self.check_access(Action::Write, &parent)?; + let parent = self.find(at, parent, true)?; + let access = self.check_access(&parent, AccessMode::WRITE)?; parent.create_node(node, name, access)?; @@ -277,8 +272,8 @@ impl IoContext { return Err(Error::DoesNotExist); } - let parent = self.find(at, parent, false, true)?; - let access = self.check_access(Action::Write, &parent)?; + let parent = self.find(at, parent, false)?; + let access = self.check_access(&parent, AccessMode::WRITE)?; if directory { // parent.remove_directory(name, access) @@ -303,7 +298,6 @@ impl IoContext { at: Option, path: P, follow_links: bool, - follow_mount: bool, ) -> Result { let mut path = path.as_ref(); let at = if path.is_absolute() { @@ -315,41 +309,40 @@ impl IoContext { self.cwd_node.clone() }; - self._find(at, path, follow_links, follow_mount) + self._find(at, path, follow_links) } - fn _resolve_link(&self, at: &NodeRef) -> Result { - let token = self.check_access(Action::Read, at)?; - // let _path = link.imp.read_to_string()?; - match at.read_symlink_node(token.clone()) { - Ok(node) => Ok(node), - // Need to read the link data and resolve it manually - Err(Error::NotImplemented) => { - let path = at.read_symlink_path(token)?; - let target = self._find(at.parent(), path.as_ref(), true, true)?; - // TODO update node cache - Ok(target) - } - Err(e) => Err(e), - } - } + // fn _resolve_link(&self, at: &NodeRef) -> Result { + // let token = self.check_access(at, AccessMode::READ)?; + // // // let _path = link.imp.read_to_string()?; + // // match at.read_symlink_node(token.clone()) { + // // Ok(node) => Ok(node), + // // // Need to read the link data and resolve it manually + // // Err(Error::NotImplemented) => { + // let path = at.read_symlink_path(token)?; + // let target = if path.is_absolute() { + // self._find(self.root.clone(), path.trim_start_separators(), false, true)? + // } else { + // self._find(at.parent(), &path, false, true)? + // }; + // // TODO update node cache + // Ok(target) + // // } + // // Err(e) => Err(e), + // // } + // } - fn _resolve( - &self, - mut at: NodeRef, - follow_links: bool, - follow_mount: bool, - ) -> Result { + fn _resolve(&self, mut at: NodeRef, follow_links: bool) -> Result { loop { - if follow_mount && let Some(target) = at.mountpoint_target() { + if let Some(target) = at.mountpoint_target() { at = target.clone(); continue; } if follow_links { // Resolve the link - match self._resolve_link(&at) { - Ok(node) => { + match self.resolve_symbolic(&at) { + Ok((node, _)) => { at = node; } // Not a link, not an error @@ -363,13 +356,127 @@ impl IoContext { } } - fn _find( + fn resolve_symbolic(&self, at: &NodeRef) -> Result<(NodeRef, PathBuf), Error> { + let token = self.check_access(at, AccessMode::READ)?; + + match at.read_symlink_path(token.clone()) { + Ok(path) => { + // Resolve the link to node + let path = PathBuf::from(path); + let target = if path.is_absolute() { + self._find(self.root.clone(), path.trim_start_separators(), false)? + } else { + self._find(at.parent(), &path, false)? + }; + Ok((target, path)) + } + Err(e) => Err(e), + } + } + + fn visit(&self, mut at: NodeRef, follow_links: bool) -> Result<(NodeRef, PathBuf), Error> { + let mut buffer = PathBuf::new(); + + loop { + if let Some(target) = at.mountpoint_target() { + at = target; + continue; + } + + if follow_links { + match self.resolve_symbolic(&at) { + // Absolute link + Ok((node, path)) if path.is_absolute() => { + log::debug!("Follow absolute link -> {path:?}"); + buffer = path; + at = node; + } + // Relative link + Ok((node, path)) => { + buffer.push(path); + at = node; + } + // Not a link + Err(Error::InvalidFile) => { + break Ok((at, buffer)); + } + Err(error) => break Err(error), + } + } else { + break Ok((at, buffer)); + } + } + } + + fn normalize>( &self, - mut at: NodeRef, - path: &Path, - follow_links: bool, - follow_mount: bool, - ) -> Result { + input: P, + follow_link: bool, + ) -> Result<(PathBuf, NodeRef), Error> { + let path = input.as_ref(); + + log::debug!("Resolve {path:?}"); + + let (mut buffer, mut node) = if path.is_absolute() { + (PathBuf::from_str("/"), self.root.clone()) + } else { + (self.cwd_path.clone(), self.cwd_node.clone()) + }; + + let mut path = path.trim_start_separators(); + + while !path.is_empty() { + let (name, tail) = path.split_left(); + + let name = match name { + Path::PARENT_NAME => { + // Pop the last element + buffer.pop(); + node = node.parent(); + if node.mountpoint_target().is_some() { + // Just exited root -> mountpoint, go up to mountpoint's parent + node = node.parent(); + } + path = tail; + continue; + } + Path::SELF_NAME => { + path = tail; + continue; + } + name => name, + }; + + log::debug!("{buffer:?}: visit {name:?}"); + + if !node.is_directory() { + todo!(); + } + + let access = self.check_access(&node, AccessMode::EXEC)?; + let child = node.lookup_or_load(name, access).unwrap(); + + let follow_links = follow_link || !tail.is_empty(); + + let (child, resolved) = self.visit(child, follow_links).unwrap(); + if resolved.is_empty() { + buffer.push(name); + } else if resolved.is_absolute() { + buffer = resolved; + } else { + buffer.push(resolved); + }; + node = child; + path = tail; + } + + log::debug!("-> {buffer:?}"); + + Ok((buffer, node)) + } + + fn _find(&self, mut at: NodeRef, path: &Path, follow_links: bool) -> Result { + log::debug!("_find({path:?})"); let mut element; let mut rest = path; @@ -389,7 +496,9 @@ impl IoContext { } } - at = self._resolve(at, follow_links, follow_mount)?; + let is_end = element.is_empty() && rest.is_empty(); + + at = self._resolve(at, follow_links || !is_end)?; if !at.is_directory() { return Err(Error::NotADirectory); @@ -399,14 +508,14 @@ impl IoContext { return Ok(at); } - let access = self.check_access(Action::Execute, &at)?; + let access = self.check_access(&at, AccessMode::EXEC)?; let node = at.lookup_or_load(element, access)?; - let node = self._resolve(node, follow_links, follow_mount)?; + let node = self._resolve(node, follow_links)?; if rest.is_empty() { Ok(node) } else { - self._find(node, rest, follow_links, follow_mount) + self._find(node, rest, follow_links) } } } @@ -417,17 +526,151 @@ mod tests { use yggdrasil_abi::{ error::Error, io::{FileMode, GroupId, OpenOptions, UserId}, - path::Path, + path::{Path, PathBuf}, }; use crate::vfs::{ - impls::{const_value_node, fixed_symlink, mdir, value_node}, + impls::{const_value_node, fixed_path_symlink, mdir, value_node}, node::AccessToken, Read, }; use super::IoContext; + #[test_log::test] + fn normalize() { + // FS tree: + // /already + // absolute + // inside + // relative + // nested + // item + // path + // link -> absolute + // deeplink -> relative/nested + // abslink -> /other/dir + // nestedlink -> abslink + // whatever + // mountpoint -> FS2 + // /inroot + // /other + // dir + // + // FS2 tree: + // /fs2 + // directory + + let fs2d0d0 = mdir::<&str, _>([]); + let fs2d0 = mdir([("directory", fs2d0d0.clone())]); + let fs2 = mdir([("fs2", fs2d0.clone())]); + + // already/absolute/inside + let d0d0f0 = const_value_node("file1"); + // already/relative/path + let d0d1f0 = const_value_node("file2"); + // already/relative/nested/item + let d0d1d0f0 = const_value_node("xxx"); + // already/relative/nested + let d0d1d0 = mdir([("item", d0d1d0f0.clone())]); + // already/link + let d0l0 = fixed_path_symlink("absolute"); + // already/deeplink + let d0l1 = fixed_path_symlink("relative/nested"); + // already/abslink + let d0l2 = fixed_path_symlink("/other/dir"); + // already/nestedlink + let d0l3 = fixed_path_symlink("abslink"); + // already/whatever + let d0f0 = const_value_node("file3"); + // already/absolute + let d0d0 = mdir([("inside", d0d0f0.clone())]); + // already/relative + let d0d1 = mdir([("path", d0d1f0.clone()), ("nested", d0d1d0.clone())]); + // already/mountpoint + let d0m0 = mdir::<&str, _>([]); + // already + let d0 = mdir([ + ("absolute", d0d0.clone()), + ("relative", d0d1.clone()), + ("link", d0l0), + ("deeplink", d0l1), + ("whatever", d0f0), + ("abslink", d0l2.clone()), + ("nestedlink", d0l3.clone()), + ("mountpoint", d0m0.clone()), + ]); + // dir + let d1d0 = mdir::<&str, _>([]); + // other + let d1 = mdir([("dir", d1d0.clone())]); + // inroot + let f0 = const_value_node("file4"); + + let root = mdir([ + ("already", d0.clone()), + ("inroot", f0.clone()), + ("other", d1.clone()), + ]); + + let tests = [ + // Absolute paths + ("/already/absolute", "/already/absolute", &d0d0), + ("/already///absolute", "/already/absolute", &d0d0), + ("/already/././absolute", "/already/absolute", &d0d0), + ( + "/already/.././already/../already/absolute", + "/already/absolute", + &d0d0, + ), + // Relative paths + ("relative/path", "/already/relative/path", &d0d1f0), + ("./relative/path", "/already/relative/path", &d0d1f0), + ("././relative/././path", "/already/relative/path", &d0d1f0), + ( + "../../already/../../../../already/./relative/path", + "/already/relative/path", + &d0d1f0, + ), + ("../inroot", "/inroot", &f0), + ("../../../inroot", "/inroot", &f0), + // Path going through a link + ("/already/link", "/already/absolute", &d0d0), + ("/already/link/inside", "/already/absolute/inside", &d0d0f0), + ("/already/link/..", "/already", &d0), + ("/already/link/../link", "/already/absolute", &d0d0), + ("link", "/already/absolute", &d0d0), + ("link/..", "/already", &d0), + // Path going through a link, pointing to a nested path + ("/already/deeplink", "/already/relative/nested", &d0d1d0), + ("/already/deeplink/..", "/already/relative", &d0d1), + // Path going through a link, pointing to an absolute path + ("/already/abslink", "/other/dir", &d1d0), + ("/already/abslink/..", "/other", &d1), + ("/already/abslink/../dir", "/other/dir", &d1d0), + // Path going through a link, pointing to a link, pointing to an absolute path + ("/already/nestedlink", "/other/dir", &d1d0), + ("/already/nestedlink/.", "/other/dir", &d1d0), + ("nestedlink/..", "/other", &d1), + // Path going through a mountpoint + ("/already/mountpoint", "/already/mountpoint", &fs2), + ("/already/mountpoint/fs2", "/already/mountpoint/fs2", &fs2d0), + ("/already/mountpoint/fs2/..", "/already/mountpoint", &fs2), + ("/already/mountpoint/..", "/already", &d0), + ]; + + let mut ioctx = IoContext::new(root.clone()); + ioctx.cwd_node = d0.clone(); + ioctx.cwd_path = PathBuf::from_str("/already"); + ioctx.mount("/already/mountpoint", fs2.clone()).unwrap(); + + for (input, expected, expected_node) in tests { + let (output, node) = ioctx.normalize(input, true).unwrap(); + assert_eq!(output.as_str(), expected); + assert!(Arc::ptr_eq(&node, expected_node)); + } + } + #[test] fn access() { let f1 = const_value_node("file1"); @@ -479,7 +722,7 @@ mod tests { let err = ioctx .open(None, "/f2", OpenOptions::WRITE, FileMode::empty()) .unwrap_err(); - assert_eq!(err, Error::PermissionDenied); + assert_eq!(err, Error::ReadOnly); // f3, read-only ioctx @@ -500,36 +743,36 @@ mod tests { let mut ioctx = IoContext::new(root.clone()); - assert_eq!(ioctx.cwd_path(), Path::empty()); + assert_eq!(ioctx.cwd_path(), Path::from_str("/")); - let node = ioctx.find(None, "f1", true, true).unwrap(); + let node = ioctx.find(None, "f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &f1)); - let node = ioctx.find(None, "d1/f1", true, true).unwrap(); + let node = ioctx.find(None, "d1/f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &d1_f1)); - let node = ioctx.find(None, "d1", true, true).unwrap(); + let node = ioctx.find(None, "d1", true).unwrap(); assert!(Arc::ptr_eq(&node, &d1)); ioctx.set_cwd("/d1").unwrap(); assert_eq!(ioctx.cwd_path(), Path::from_str("/d1")); - let err = ioctx.find(None, "d1", true, true).unwrap_err(); + let err = ioctx.find(None, "d1", true).unwrap_err(); assert_eq!(err, Error::DoesNotExist); - let node = ioctx.find(None, "f1", true, true).unwrap(); + let node = ioctx.find(None, "f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &d1_f1)); - let node = ioctx.find(None, "../d1/f1", true, true).unwrap(); + let node = ioctx.find(None, "../d1/f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &d1_f1)); - let node = ioctx.find(None, "/f1", true, true).unwrap(); + let node = ioctx.find(None, "/f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &f1)); - let node = ioctx.find(None, "../f1", true, true).unwrap(); + let node = ioctx.find(None, "../f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &f1)); - let node = ioctx.find(None, "..", true, true).unwrap(); + let node = ioctx.find(None, "..", true).unwrap(); assert!(Arc::ptr_eq(&node, &root)); } @@ -542,38 +785,32 @@ mod tests { let mut ioctx = IoContext::new(root1.clone()); - let node = ioctx.find(None, "/f1", true, true).unwrap(); + let node = ioctx.find(None, "/f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &root1_f1)); ioctx.mount("/", root2.clone()).unwrap(); - let node = ioctx.find(None, "/f1", true, true).unwrap(); + let node = ioctx.find(None, "/f1", true).unwrap(); assert!(Arc::ptr_eq(&node, &root2_f1)); - let node = ioctx.find(None, "/f1", true, false).unwrap(); - assert!(Arc::ptr_eq(&node, &root1_f1)); - - let node = ioctx.find(None, "/", true, true).unwrap(); + let node = ioctx.find(None, "/", true).unwrap(); assert!(Arc::ptr_eq(&node, &root2)); - - let node = ioctx.find(None, "/", true, false).unwrap(); - assert!(Arc::ptr_eq(&node, &root1)); } #[test] fn symlink_resolution() { let f1 = const_value_node("file1"); - let l1 = fixed_symlink(f1.clone()); - let root = mdir([("l1", l1.clone())]); + let l1 = fixed_path_symlink("f1"); + let root = mdir([("l1", l1.clone()), ("f1", f1.clone())]); let ioctx = IoContext::new(root.clone()); // No follow - let node = ioctx.find(None, "/l1", false, true).unwrap(); + let node = ioctx.find(None, "/l1", false).unwrap(); assert!(Arc::ptr_eq(&node, &l1)); // Follow - let node = ioctx.find(None, "/l1", true, true).unwrap(); + let node = ioctx.find(None, "/l1", true).unwrap(); assert!(Arc::ptr_eq(&node, &f1)); } @@ -629,36 +866,30 @@ mod tests { let ioctx = IoContext::new(root.clone()); // Ok paths - let node = ioctx.find(None, Path::empty(), false, false).unwrap(); + let node = ioctx.find(None, Path::empty(), false).unwrap(); assert!(Arc::ptr_eq(&node, &root)); - let node = ioctx.find(None, Path::from_str("/"), false, false).unwrap(); + let node = ioctx.find(None, Path::from_str("/"), false).unwrap(); assert!(Arc::ptr_eq(&node, &root)); // Weird paths let node = ioctx - .find(None, Path::from_str("////.////./"), false, false) + .find(None, Path::from_str("////.////./"), false) .unwrap(); assert!(Arc::ptr_eq(&node, &root)); let node = ioctx - .find(None, Path::from_str("/../.././//."), false, false) + .find(None, Path::from_str("/../.././//."), false) .unwrap(); assert!(Arc::ptr_eq(&node, &root)); - let node = ioctx - .find(None, Path::from_str("/f1"), false, false) - .unwrap(); + let node = ioctx.find(None, Path::from_str("/f1"), false).unwrap(); assert!(Arc::ptr_eq(&node, &root_f1)); - let node = ioctx - .find(None, Path::from_str("/d1/f1"), false, false) - .unwrap(); + let node = ioctx.find(None, Path::from_str("/d1/f1"), false).unwrap(); assert!(Arc::ptr_eq(&node, &root_d1_f1)); let node = ioctx - .find(None, Path::from_str("/d1/../d1/./f1"), false, false) + .find(None, Path::from_str("/d1/../d1/./f1"), false) .unwrap(); assert!(Arc::ptr_eq(&node, &root_d1_f1)); - let node = ioctx - .find(None, Path::from_str("/d1/.."), false, false) - .unwrap(); + let node = ioctx.find(None, Path::from_str("/d1/.."), false).unwrap(); assert!(Arc::ptr_eq(&node, &root)); } } diff --git a/kernel/libk/src/vfs/node/impls.rs b/kernel/libk/src/vfs/node/impls.rs index d6c34646..f39fe6d3 100644 --- a/kernel/libk/src/vfs/node/impls.rs +++ b/kernel/libk/src/vfs/node/impls.rs @@ -8,7 +8,10 @@ use alloc::{ }; use libk_util::sync::IrqSafeSpinlock; -use yggdrasil_abi::{error::Error, io::OpenOptions}; +use yggdrasil_abi::{ + error::Error, + io::{FileMode, OpenOptions}, +}; use crate::vfs::{DirectoryOpenPosition, InstanceData}; @@ -92,21 +95,10 @@ pub struct ReadOnlyFnNode { /// In-memory directory using tree cache pub struct MemoryDirectory; -/// In-memory symlink pointing to a fixed [Node] -pub struct FixedSymlink { - target: NodeRef, -} /// In-memory symlink with a fixed path pub struct FixedPathSymlink { target: String, } -/// In-memory functional symlink -pub struct FnSymlink -where - F: ReadLinkFn + 'static, -{ - read: F, -} impl ReadOnlyFnValueNode where @@ -362,10 +354,11 @@ where impl MemoryDirectory { /// Creates a [MemoryDirectory] with no children - pub fn empty() -> NodeRef { - Node::directory( + pub fn empty(mode: FileMode) -> NodeRef { + Node::directory_kernel( MemoryDirectory, NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE, + mode, ) } } @@ -377,15 +370,6 @@ impl DirectoryImpl for MemoryDirectory { } } -// In-memory fixed symlink - -impl CommonImpl for FixedSymlink {} -impl SymlinkImpl for FixedSymlink { - fn target(&self, _node: &NodeRef) -> Result { - Ok(self.target.clone()) - } -} - // In-memory fixed path symlink impl CommonImpl for FixedPathSymlink {} @@ -406,26 +390,6 @@ impl SymlinkImpl for FixedPathSymlink { // In-memory functional symlink -impl FnSymlink -where - F: ReadLinkFn + 'static, -{ - /// Creates a new [FnSymlink] node - pub fn new(read: F) -> Self { - Self { read } - } -} - -impl CommonImpl for FnSymlink where F: ReadLinkFn + 'static {} -impl SymlinkImpl for FnSymlink -where - F: ReadLinkFn + 'static, -{ - fn target(&self, _node: &NodeRef) -> Result { - (self.read)() - } -} - /// Creates a read-only value node with given `value` pub fn const_value_node(value: T) -> NodeRef where @@ -462,7 +426,11 @@ where /// Creates a read-only node with given read function pub fn read_fn_node(read: R) -> NodeRef { - Node::regular(ReadOnlyFnNode::new(read), NodeFlags::IN_MEMORY_PROPS) + Node::regular_kernel( + ReadOnlyFnNode::new(read), + NodeFlags::IN_MEMORY_PROPS, + FileMode::USER_READ | FileMode::GROUP_READ | FileMode::OTHER_READ, + ) } /// Creates an in-memory directory from the iterator @@ -477,11 +445,6 @@ pub fn mdir, I: IntoIterator>(it: I) -> Nod dir } -/// Creates a static symlink pointing to given node -pub fn fixed_symlink(target: NodeRef) -> NodeRef { - Node::symlink(FixedSymlink { target }, NodeFlags::IN_MEMORY_PROPS) -} - pub fn fixed_path_symlink(target: impl Into) -> NodeRef { Node::symlink( FixedPathSymlink { @@ -491,14 +454,6 @@ pub fn fixed_path_symlink(target: impl Into) -> NodeRef { ) } -pub fn fn_symlink(read: R) -> NodeRef { - let data = FnSymlink::new(read); - Node::symlink( - data, - NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS | NodeFlags::NO_LINK_CACHE, - ) -} - #[cfg(test)] mod tests { use yggdrasil_abi::io::OpenOptions; diff --git a/kernel/libk/src/vfs/node/mod.rs b/kernel/libk/src/vfs/node/mod.rs index ae6162f8..a7eca810 100644 --- a/kernel/libk/src/vfs/node/mod.rs +++ b/kernel/libk/src/vfs/node/mod.rs @@ -6,7 +6,10 @@ use alloc::{ sync::{Arc, Weak}, vec::Vec, }; -use libk_util::sync::IrqSafeSpinlock; +use libk_util::{ + ext::OptionExt, + sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock}, +}; use yggdrasil_abi::{ bitflags, error::Error, @@ -72,7 +75,7 @@ pub(crate) struct DirectoryData { pub(crate) struct SymlinkData { // Cached symlink target with the literal path - pub(crate) target: IrqSafeSpinlock>, + pub(crate) target: IrqSafeRwLock>, pub(crate) imp: Box, } @@ -223,20 +226,26 @@ impl Node { } /// Creates a new block device node with given [BlockDevice] - pub fn block(device: Arc, flags: NodeFlags) -> NodeRef { + pub fn block(device: Arc, flags: NodeFlags, mode: FileMode) -> NodeRef { Self::new( NodeImpl::Block(BlockDeviceFile(device)), flags, - Metadata::default_file(), + Metadata { + mode, + ..Metadata::default_file() + }, ) } /// Creates a new character device node with given [CharDevice] - pub fn char(device: Arc, flags: NodeFlags) -> NodeRef { + pub fn char(device: Arc, flags: NodeFlags, mode: FileMode) -> NodeRef { Self::new( NodeImpl::Char(CharDeviceFile(device)), flags, - Metadata::default_file(), + Metadata { + mode, + ..Metadata::default_file() + }, ) } @@ -244,7 +253,7 @@ impl Node { pub fn symlink(data: T, flags: NodeFlags) -> NodeRef { Self::new( NodeImpl::Symlink(SymlinkData { - target: IrqSafeSpinlock::new(None), + target: IrqSafeRwLock::new(None), imp: Box::new(data), }), flags, @@ -365,27 +374,12 @@ impl Node { } } - pub(crate) fn read_symlink_node(self: &NodeRef, _token: AccessToken) -> Result { - let symlink = self.as_symlink()?; - let mut cache = symlink.target.lock(); - - // If caching is not disabled and the target is cached, return it - if !self.flags.contains(NodeFlags::NO_LINK_CACHE) - && let Some((_, cached)) = cache.as_ref() - { - return Ok(cached.clone()); - } - - let target = symlink.imp.target(self)?; - - Ok(cache.insert((String::new(), target)).1.clone()) - } - pub(crate) fn read_symlink_path(self: &NodeRef, _token: AccessToken) -> Result { let symlink = self.as_symlink()?; - - // TODO cache path - symlink.imp.read_to_string() + let mut target = symlink.target.write(); + target + .get_or_try_insert_with(|| symlink.imp.read_to_string()) + .cloned() } pub fn as_block_device(&self) -> Result<&Arc, Error> { diff --git a/kernel/libk/src/vfs/node/ops.rs b/kernel/libk/src/vfs/node/ops.rs index bb3f1f98..ea64a57f 100644 --- a/kernel/libk/src/vfs/node/ops.rs +++ b/kernel/libk/src/vfs/node/ops.rs @@ -214,7 +214,8 @@ impl Node { } log::warn!("Possible bug: node has IN_MEMORY_SIZE, but is not a directory"); - Err(Error::NotImplemented) + Ok(0) + // Err(Error::NotImplemented) } else { // Fetch the size from the node let size = self.data_as_common().size(self)?; diff --git a/kernel/libk/src/vfs/node/traits.rs b/kernel/libk/src/vfs/node/traits.rs index 6f2d2caa..d5d4c8e7 100644 --- a/kernel/libk/src/vfs/node/traits.rs +++ b/kernel/libk/src/vfs/node/traits.rs @@ -28,7 +28,7 @@ pub trait CommonImpl: Send + Sync { /// Fetches the metadata of the file from underlying storage fn metadata(&self, node: &NodeRef) -> Result { - Err(Error::NotImplemented) + unreachable!("Kernel bug: .metadata() not implemented and no IN_MEMORY_PROPS set") } /// Fetches the size of the file from underlying storage @@ -130,10 +130,10 @@ pub trait DirectoryImpl: CommonImpl { /// Symbolic link interface #[allow(unused)] pub trait SymlinkImpl: CommonImpl { - /// Returns the target node (if such is available directly) of the link - fn target(&self, node: &NodeRef) -> Result { - Err(Error::NotImplemented) - } + // /// Returns the target node (if such is available directly) of the link + // fn target(&self, node: &NodeRef) -> Result { + // Err(Error::NotImplemented) + // } /// Fetches the contents of the symlink into a [String] fn read_to_string(&self) -> Result { diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 6381cd05..21cc25a9 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -398,8 +398,8 @@ impl X86_64 { } // Switch /dev/fb0 to the new node - if let Some(node) = node { - devfs::set_node("fb0", node).ok(); + if let Some((name, _)) = node { + devfs::redirect("fb0", name).ok(); } } }); diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index 293bc7b0..bb8836bd 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -67,7 +67,7 @@ pub fn create_filesystem(ioctx: &mut IoContext, options: &MountOptions) -> Resul if !path.is_absolute() { todo!(); } - ioctx.find(None, path, true, true) + ioctx.find(None, path, true) }); let options = vfs::parse_mount_options(options.options); diff --git a/kernel/src/fs/pseudo.rs b/kernel/src/fs/pseudo.rs index cb28bfef..d20a065a 100644 --- a/kernel/src/fs/pseudo.rs +++ b/kernel/src/fs/pseudo.rs @@ -1,7 +1,7 @@ //! Pseudo-devices use core::task::{Context, Poll}; -use abi::error::Error; +use abi::{error::Error, io::FileMode}; use alloc::{boxed::Box, sync::Arc}; use async_trait::async_trait; use device_api::device::Device; @@ -81,16 +81,16 @@ impl Device for Zero { /// Adds "pseudo"-devices to the filesystem (i.e. /dev/random) pub fn add_pseudo_devices() -> Result<(), Error> { - let random = read_fn_node(move |_, buf| { - random::read(buf); + let urandom = read_fn_node(move |_, buf| { + random::read_unsecure(buf); Ok(buf.len()) }); let root = devfs::root(); - root.add_child("random", random)?; + root.add_child("urandom", urandom)?; - devfs::add_named_char_device(Arc::new(Null), "null".into())?; - devfs::add_named_char_device(Arc::new(Zero), "zero".into())?; + devfs::add_named_char_device(Arc::new(Null), "null".into(), FileMode::new(0o666))?; + devfs::add_named_char_device(Arc::new(Zero), "zero".into(), FileMode::new(0o666))?; Ok(()) } diff --git a/kernel/src/init.rs b/kernel/src/init.rs index 04107be3..8cd6b2ac 100644 --- a/kernel/src/init.rs +++ b/kernel/src/init.rs @@ -3,10 +3,9 @@ use abi::error::Error; use libk::{ device::display::console, - fs::devfs, random, - task::{binary::LoadOptions, process::Process, runtime, thread::Thread}, - vfs::{impls::fn_symlink, IoContext, NodeRef}, + task::{binary::LoadOptions, process::Process, runtime}, + vfs::{IoContext, NodeRef}, }; use memfs::MemoryFilesystem; @@ -33,19 +32,7 @@ pub fn kinit() -> Result<(), Error> { runtime::spawn(ygg_driver_usb::bus::bus_handler())?; runtime::spawn(console::flush_consoles_task()).ok(); - devfs::root().add_child( - "tty", - fn_symlink(|| { - let thread = Thread::current(); - let process = thread.process(); - - if let Some(tty) = process.session_terminal() { - Ok(tty) - } else { - Err(Error::InvalidFile) - } - }), - )?; + // TODO bring /dev/tty back ygg_driver_net_loopback::init(); ygg_driver_net_core::start_network_tasks()?; diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 6d912228..3a3dae15 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -35,7 +35,7 @@ #![no_std] #![no_main] -use abi::error::Error; +use abi::{error::Error, io::FileMode}; use alloc::{borrow::ToOwned, string::String}; use arch::Platform; use git_version::git_version; @@ -148,7 +148,11 @@ pub fn kernel_main() -> ! { CPU_INIT_FENCE.wait_all(ArchitectureImpl::cpu_count()); // Add keyboard device - if let Err(error) = devfs::add_named_char_device(ygg_driver_input::setup(), "kbd".to_owned()) { + if let Err(error) = devfs::add_named_char_device( + ygg_driver_input::setup(), + "kbd".to_owned(), + FileMode::new(0o660), + ) { log::error!("Couldn't add keyboard device: {error:?}"); } diff --git a/kernel/src/syscall/imp/mod.rs b/kernel/src/syscall/imp/mod.rs index 226e0bc4..e30a6750 100644 --- a/kernel/src/syscall/imp/mod.rs +++ b/kernel/src/syscall/imp/mod.rs @@ -3,8 +3,8 @@ use core::mem::MaybeUninit; pub(crate) use abi::{ error::Error, io::{ - DirectoryEntry, FileAttr, FileMode, MountOptions, OpenOptions, PipeOptions, PollControl, - RawFd, TerminalOptions, TerminalSize, TimerOptions, UnmountOptions, + AccessMode, DirectoryEntry, FileAttr, FileMode, MountOptions, OpenOptions, PipeOptions, + PollControl, RawFd, TerminalOptions, TerminalSize, TimerOptions, UnmountOptions, }, mem::{MappingFlags, MappingSource}, net::SocketType, @@ -39,7 +39,7 @@ pub(super) use sys_process::*; // Misc pub(crate) fn get_random(buffer: &mut [u8]) { - random::read(buffer); + random::read_unsecure(buffer); } pub(crate) fn mount(options: &MountOptions<'_>) -> Result<(), Error> { diff --git a/kernel/src/syscall/imp/sys_io.rs b/kernel/src/syscall/imp/sys_io.rs index 7ab31554..e3ce0e3b 100644 --- a/kernel/src/syscall/imp/sys_io.rs +++ b/kernel/src/syscall/imp/sys_io.rs @@ -3,7 +3,7 @@ use core::{mem::MaybeUninit, time::Duration}; use abi::{ error::Error, io::{ - ChannelPublisherId, DeviceRequest, DirectoryEntry, FileAttr, FileControl, + AccessMode, ChannelPublisherId, DeviceRequest, DirectoryEntry, FileAttr, FileControl, FileMetadataUpdate, FileMode, FilesystemControl, MessageDestination, OpenOptions, PipeOptions, PollControl, RawFd, ReceivedMessageMetadata, SeekFrom, SentMessage, TerminalOptions, TerminalSize, TimerOptions, @@ -16,6 +16,7 @@ use libk::{ task::thread::Thread, vfs::{self, File, MessagePayload, Read, Seek, Write}, }; +use libk_util::io::{ReadAt, WriteAt}; use crate::syscall::{run_with_io, run_with_io_at}; @@ -48,6 +49,23 @@ pub(crate) fn open( res } +pub(crate) fn check_access(at: Option, path: &str, access: AccessMode) -> Result<(), Error> { + let thread = Thread::current(); + let process = thread.process(); + + run_with_io_at(&process, at, |at, mut io| { + let node = if path.is_empty() { + at + } else { + io.ioctx_mut().find(Some(at), path, true)? + }; + + io.ioctx().check_access(&node, access)?; + + Ok(()) + }) +} + pub(crate) fn close(fd: RawFd) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process(); @@ -77,6 +95,20 @@ pub(crate) fn read(fd: RawFd, buffer: &mut [u8]) -> Result { run_with_io(&process, |io| io.files.file(fd)?.read(buffer)) } +pub(crate) fn read_at(fd: RawFd, pos: u64, buffer: &mut [u8]) -> Result { + let thread = Thread::current(); + let process = thread.process(); + + run_with_io(&process, |io| io.files.file(fd)?.read_at(pos, buffer)) +} + +pub(crate) fn write_at(fd: RawFd, pos: u64, buffer: &[u8]) -> Result { + let thread = Thread::current(); + let process = thread.process(); + + run_with_io(&process, |io| io.files.file(fd)?.write_at(pos, buffer)) +} + pub(crate) fn seek(fd: RawFd, pos: SeekFrom, output: &mut u64) -> Result<(), Error> { let thread = Thread::current(); let process = thread.process(); @@ -92,8 +124,8 @@ pub(crate) fn open_directory(at: Option, path: &str) -> Result, path: &str, buffer: &mut [u8]) -> Res let node = if path.is_empty() { at } else { - io.ioctx_mut().find(Some(at), path, false, true)? + io.ioctx_mut().find(Some(at), path, false)? }; node.read_link(buffer) diff --git a/kernel/src/syscall/imp/sys_process.rs b/kernel/src/syscall/imp/sys_process.rs index cdbf3463..39bf39dd 100644 --- a/kernel/src/syscall/imp/sys_process.rs +++ b/kernel/src/syscall/imp/sys_process.rs @@ -1,4 +1,6 @@ -use core::{mem::MaybeUninit, num::NonZeroUsize, sync::atomic::AtomicU32, time::Duration}; +use core::{ + mem::MaybeUninit, num::NonZeroUsize, str::FromStr, sync::atomic::AtomicU32, time::Duration, +}; use abi::{ error::Error, @@ -8,6 +10,7 @@ use abi::{ ExitCode, MutexOperation, ProcessGroupId, ProcessId, ProcessOption, ProcessWait, Signal, SpawnFlags, SpawnOption, SpawnOptions, ThreadOption, ThreadSpawnOptions, WaitFlags, }, + util::FixedString, }; use alloc::sync::Arc; use libk::{ @@ -376,6 +379,11 @@ pub(crate) fn get_process_option(option: &mut ProcessOption) -> Result<(), Error *entry = process.signal_entry(); Ok(()) } + ProcessOption::Directory(dst) => run_with_io(&process, |io| { + let path = io.ioctx().cwd_path(); + *dst = FixedString::from_str(path.as_str())?; + Ok(()) + }), } } @@ -387,5 +395,10 @@ pub(crate) fn set_process_option(option: &mut ProcessOption) -> Result<(), Error *entry = process.set_signal_entry(*entry); Ok(()) } + ProcessOption::Directory(path) => run_with_io(&process, |mut io| { + let path = path.as_ref(); + io.ioctx_mut().set_cwd(path)?; + Ok(()) + }), } } diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 06a9d5fd..0f859a4d 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -76,6 +76,9 @@ pub fn raw_syscall_handler(func: usize, args: &[usize]) -> u64 { }; let result = generated::handle_syscall(func, args); + if result.is_err() { + log::info!("{func:?} ({args:x?}) -> {result:?}"); + } let value = match result { Ok(value) => value, diff --git a/lib/abi/def/io.abi b/lib/abi/def/io.abi index 800871b9..a7cae3c8 100644 --- a/lib/abi/def/io.abi +++ b/lib/abi/def/io.abi @@ -32,6 +32,12 @@ bitfield FileMode(u32) { USER_READ: 8, } +bitfield AccessMode(u32) { + READ: 0, + WRITE: 1, + EXEC: 2, +} + bitfield OpenOptions(u32) { /// The file is to be opened with read capability READ: 0, diff --git a/lib/abi/def/yggdrasil.abi b/lib/abi/def/yggdrasil.abi index c88cb899..bc81a94b 100644 --- a/lib/abi/def/yggdrasil.abi +++ b/lib/abi/def/yggdrasil.abi @@ -111,10 +111,13 @@ syscall start_session() -> Result<()>; // I/O syscall open(at: Option, path: &str, opts: OpenOptions, mode: FileMode) -> Result; +syscall check_access(at: Option, path: &str, access: AccessMode) -> Result<()>; syscall close(fd: RawFd) -> Result<()>; syscall write(fd: RawFd, data: &[u8]) -> Result; syscall read(fd: RawFd, data: &mut [u8]) -> Result; syscall seek(fd: RawFd, pos: SeekFrom, output: &mut u64) -> Result<()>; +syscall read_at(fd: RawFd, pos: u64, data: &mut [u8]) -> Result; +syscall write_at(fd: RawFd, pos: u64, data: &[u8]) -> Result; syscall open_directory(at: Option, path: &str) -> Result; syscall read_directory_entries(fd: RawFd, entries: &mut [MaybeUninit]) -> Result; diff --git a/lib/abi/src/io/mod.rs b/lib/abi/src/io/mod.rs index 53454d14..ede592e0 100644 --- a/lib/abi/src/io/mod.rs +++ b/lib/abi/src/io/mod.rs @@ -6,8 +6,8 @@ mod input; mod terminal; pub use crate::generated::{ - DirectoryEntry, FileAttr, FileMode, FileType, GroupId, MountOptions, OpenOptions, PipeOptions, - PollControl, RawFd, TimerOptions, UnmountOptions, UserId, + AccessMode, DirectoryEntry, FileAttr, FileMode, FileType, GroupId, MountOptions, OpenOptions, + PipeOptions, PollControl, RawFd, TimerOptions, UnmountOptions, UserId, }; pub use channel::{ChannelPublisherId, MessageDestination, ReceivedMessageMetadata, SentMessage}; pub use device::{DeviceRequest, Framebuffer}; diff --git a/lib/abi/src/path/path_buf.rs b/lib/abi/src/path/path_buf.rs index d43c527f..c8b454e7 100644 --- a/lib/abi/src/path/path_buf.rs +++ b/lib/abi/src/path/path_buf.rs @@ -1,6 +1,7 @@ use crate::path::Path; use alloc::{ borrow::{Borrow, ToOwned}, + format, string::String, }; use core::ops::Deref; @@ -17,12 +18,46 @@ impl PathBuf { Self(String::from(s)) } - pub fn push>(&mut self, _path: P) { - todo!() + pub fn push>(&mut self, path: P) { + if !self.is_empty() && !self.0.ends_with('/') { + self.0.push('/'); + } + self.0 + .push_str(path.as_ref().trim_start_separators().as_str()); } - pub fn join>(&mut self, _path: P) -> Self { - todo!() + pub fn pop(&mut self) { + if self.0 == "/" { + return; + } + /* + /a/b -> /a + /a -> / + / -> / + a/b -> a + a -> + */ + if self.is_absolute() { + match self + .0 + .trim_start_matches(Path::SEPARATOR) + .rsplit_once(Path::SEPARATOR) + { + Some((head, _)) => { + self.0 = format!("/{head}"); + } + None => { + self.0 = "/".into(); + } + } + } else { + todo!() + } + } + + pub fn join>(&mut self, path: P) -> Self { + self.push(path); + self.clone() } pub fn display(&self) -> &str { @@ -56,6 +91,12 @@ impl From<&str> for PathBuf { } } +impl From for PathBuf { + fn from(value: String) -> Self { + Self(value) + } +} + impl ToOwned for Path { type Owned = PathBuf; diff --git a/lib/abi/src/process/mod.rs b/lib/abi/src/process/mod.rs index 34f40401..d5e9fb11 100644 --- a/lib/abi/src/process/mod.rs +++ b/lib/abi/src/process/mod.rs @@ -2,7 +2,10 @@ use core::{ffi::CStr, fmt, marker::PhantomData, time::Duration}; -use crate::io::{FileMode, RawFd}; +use crate::{ + io::{FileMode, RawFd}, + util::FixedString, +}; mod exit; pub mod thread; @@ -36,9 +39,11 @@ pub enum ProcessWait { AnyChild, } +#[allow(clippy::large_enum_variant)] #[derive(Debug)] pub enum ProcessOption { SignalEntry(usize), + Directory(FixedString<4096>), } /// Defines an optional argument for controlling process creation diff --git a/lib/runtime/src/io.rs b/lib/runtime/src/io.rs index 70fbcb37..fc034378 100644 --- a/lib/runtime/src/io.rs +++ b/lib/runtime/src/io.rs @@ -21,7 +21,32 @@ pub mod poll { pub use abi::io::PollControl; } +use core::str::FromStr; + pub use abi::io::{ - DirectoryEntry, FileAttr, FileMetadataUpdate, FileMetadataUpdateMode, FileMode, FileType, - OpenOptions, PipeOptions, RawFd, SeekFrom, TimerOptions, + AccessMode, DirectoryEntry, FileAttr, FileMetadataUpdate, FileMetadataUpdateMode, FileMode, + FileType, OpenOptions, PipeOptions, RawFd, SeekFrom, TimerOptions, }; + +use abi::{error::Error, process::ProcessOption, util::FixedString}; +use alloc::string::String; + +pub fn current_directory T>(mapper: F) -> Result { + let mut option = ProcessOption::Directory(FixedString::empty()); + unsafe { crate::sys::get_process_option(&mut option) }?; + let ProcessOption::Directory(path) = &option else { + unreachable!() + }; + + Ok(mapper(path.as_ref())) +} + +pub fn current_directory_string() -> Result { + current_directory(|s| s.into()) +} + +pub fn set_current_directory(path: &str) -> Result<(), Error> { + let mut option = ProcessOption::Directory(FixedString::from_str(path)?); + unsafe { crate::sys::set_process_option(&mut option) }?; + Ok(()) +} diff --git a/lib/runtime/src/sys/mod.rs b/lib/runtime/src/sys/mod.rs index b8fd90d8..dee77d47 100644 --- a/lib/runtime/src/sys/mod.rs +++ b/lib/runtime/src/sys/mod.rs @@ -16,9 +16,9 @@ mod generated { use abi::{ error::Error, io::{ - ChannelPublisherId, DirectoryEntry, FileAttr, FileMode, MountOptions, OpenOptions, - PipeOptions, PollControl, RawFd, TerminalOptions, TerminalSize, TimerOptions, - UnmountOptions, + AccessMode, ChannelPublisherId, DirectoryEntry, FileAttr, FileMode, MountOptions, + OpenOptions, PipeOptions, PollControl, RawFd, TerminalOptions, TerminalSize, + TimerOptions, UnmountOptions, }, mem::{MappingFlags, MappingSource}, net::SocketType, diff --git a/test.c b/test.c index a96040c1..7abeba11 100644 --- a/test.c +++ b/test.c @@ -3,41 +3,6 @@ #include #include -static pthread_mutex_t mutex; - -static void *thread(void *arg) { - printf("[child] lock0\n"); - pthread_mutex_lock(&mutex); - printf("[child] lock1\n"); - pthread_mutex_lock(&mutex); - printf("[child] in inner lock!!!\n"); - sleep(3); - pthread_mutex_unlock(&mutex); - printf("[child] in outer lock!!!\n"); - pthread_mutex_unlock(&mutex); - printf("[child] unlocked!!!\n"); - return NULL; -} - int main(int argc, const char **argv) { - pthread_mutexattr_t mutex_attr; - pthread_t handle0; - - pthread_mutexattr_init(&mutex_attr); - pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&mutex, &mutex_attr); - - pthread_create(&handle0, NULL, thread, "thread0"); - - sleep(1); - - pthread_mutex_lock(&mutex); - printf("[main] inside lock\n"); - sleep(1); - pthread_mutex_unlock(&mutex); - - pthread_join(handle0, NULL); - printf("[main] finished\n"); - return 0; } diff --git a/tool/abi-generator/src/abi/ty/primitive.rs b/tool/abi-generator/src/abi/ty/primitive.rs index a6fd6b87..48edf462 100644 --- a/tool/abi-generator/src/abi/ty/primitive.rs +++ b/tool/abi-generator/src/abi/ty/primitive.rs @@ -84,9 +84,10 @@ impl Type for AbiPrimitive { match self { Self::Bool => (quote!(#args[#index] != 0), 1), Self::U128 => todo!("Emit from syscall for u128"), - Self::U64 if env.thin_pointer_width == TypeWidth::U32 => { - todo!(); - } + Self::U64 if env.thin_pointer_width == TypeWidth::U32 => ( + quote!((#args[#index] as u64) | ((#args[#index + 1] as u64) << 32)), + 2, + ), _ => { let ty = self.as_rust_type(); (quote!(#args[#index] as #ty), 1) diff --git a/userspace/dyn-loader/src/search.rs b/userspace/dyn-loader/src/search.rs index 072e6c5c..384b0875 100644 --- a/userspace/dyn-loader/src/search.rs +++ b/userspace/dyn-loader/src/search.rs @@ -12,5 +12,9 @@ pub fn find_library(name: &str) -> Result { if path.exists() { return Ok(path); } + let path = Path::new("/tmp").join(name); + if path.exists() { + return Ok(path); + } Err(Error::LibraryNotFound(name.to_owned())) } diff --git a/userspace/etc/test.S b/userspace/etc/test.S new file mode 100644 index 00000000..075e40d8 --- /dev/null +++ b/userspace/etc/test.S @@ -0,0 +1,4 @@ +.section .text +.global _start +_start: + jmp . diff --git a/userspace/etc/test.c b/userspace/etc/test.c new file mode 100644 index 00000000..da688795 --- /dev/null +++ b/userspace/etc/test.c @@ -0,0 +1,3 @@ +void _start(void) { + +} diff --git a/userspace/lib/ygglibc/src/headers/dlfcn/mod.rs b/userspace/lib/ygglibc/src/headers/dlfcn/mod.rs index 417ff21a..37fe1a54 100644 --- a/userspace/lib/ygglibc/src/headers/dlfcn/mod.rs +++ b/userspace/lib/ygglibc/src/headers/dlfcn/mod.rs @@ -37,7 +37,7 @@ unsafe extern "C" fn dlsym(_dl: *mut c_void, _name: *const c_char) -> *mut c_voi // Non-POSIX #[no_mangle] unsafe extern "C" fn dladdr(_addr: *mut c_void, _info: *mut Dl_info) -> c_int { - todo!() + -1 } #[no_mangle] unsafe extern "C" fn dladdr1( diff --git a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs index 34606119..70c682d4 100644 --- a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs +++ b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs @@ -1,9 +1,9 @@ use core::ffi::{c_char, c_int, c_short, VaList}; -use yggdrasil_rt::io::{FileMode, OpenOptions}; +use yggdrasil_rt::{io::{AccessMode, FileMode, OpenOptions}, sys as syscall}; use crate::{ - error::{CFdResult, CIntCountResult, EResult, TryFromExt}, + error::{CFdResult, CIntCountResult, CIntZeroResult, EResult, ResultExt, TryFromExt}, io::{raw::RawFile, IntoRawFd}, util::{self, PointerStrExt}, }; @@ -123,16 +123,18 @@ fn open_opts(opts: c_int, ap: &mut VaList) -> EResult { if opts & O_TRUNC != 0 { res |= OpenOptions::TRUNCATE; } + // TODO O_CLOEXEC + if opts - & (O_DSYNC | O_RSYNC | O_SYNC | O_TTY_INIT | O_NONBLOCK | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY) + & (O_DSYNC | O_RSYNC | O_SYNC | O_TTY_INIT | O_NONBLOCK | O_NOFOLLOW | O_NOCTTY) != 0 { todo!(); } let mode = if need_mode { - let _raw = unsafe { ap.arg::() }; - todo!(); + let raw = unsafe { ap.arg::() }; + unsafe { FileMode::from_raw(raw as _) } } else { FileMode::empty() }; @@ -181,11 +183,22 @@ unsafe extern "C" fn openat( } #[no_mangle] -unsafe extern "C" fn faccessat( - _atfd: c_int, - _path: *const c_char, - _mode: c_int, - _flags: c_int, -) -> c_int { - todo!() +pub(crate) unsafe extern "C" fn faccessat( + atfd: c_int, + path: *const c_char, + mode: c_int, + flags: c_int, +) -> CIntZeroResult { + let atfd = util::at_fd(atfd)?; + let path = path.ensure_str(); + let access = match mode { + 0 => AccessMode::empty(), + R_OK => AccessMode::READ, + W_OK => AccessMode::WRITE, + X_OK => AccessMode::EXEC, + _ => todo!("{mode:#x}"), + }; + syscall::check_access(atfd, path, access).e_map_err(Errno::from)?; + + CIntZeroResult::SUCCESS } diff --git a/userspace/lib/ygglibc/src/headers/signal/mod.rs b/userspace/lib/ygglibc/src/headers/signal/mod.rs index 39fb38fe..7bdd96f1 100644 --- a/userspace/lib/ygglibc/src/headers/signal/mod.rs +++ b/userspace/lib/ygglibc/src/headers/signal/mod.rs @@ -253,7 +253,7 @@ unsafe extern "C" fn sigemptyset(mask: *mut sigset_t) -> CIntZeroResult { #[no_mangle] unsafe extern "C" fn sigfillset(_mask: *mut sigset_t) -> c_int { - todo!() + 0 } #[no_mangle] @@ -314,7 +314,7 @@ unsafe extern "C" fn sigprocmask( _new: *const sigset_t, _old: *mut sigset_t, ) -> c_int { - todo!() + 0 } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/spawn/mod.rs b/userspace/lib/ygglibc/src/headers/spawn/mod.rs index 6123862e..357994e2 100644 --- a/userspace/lib/ygglibc/src/headers/spawn/mod.rs +++ b/userspace/lib/ygglibc/src/headers/spawn/mod.rs @@ -4,6 +4,7 @@ use super::{ sched::__ygg_sched_param_t, sys_types::{mode_t, pid_t}, }; +use crate::util::PointerStrExt; #[repr(C)] pub struct posix_spawnattr_t {} @@ -21,13 +22,14 @@ pub const POSIX_SPAWN_SETSIGMASK: c_int = 1 << 6; #[no_mangle] unsafe extern "C" fn posix_spawn( _pid: *mut pid_t, - _path: *const c_char, + path: *const c_char, _file_actions: *const posix_spawn_file_actions_t, _attrp: *const posix_spawnattr_t, _argv: *const *mut c_char, _envp: *const *mut c_char, ) -> c_int { - todo!() + let path = path.ensure_str(); + todo!("TODO: posix_spawn({path:?})") } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/stdio/util.rs b/userspace/lib/ygglibc/src/headers/stdio/util.rs index 9dfd092a..da1d2f4a 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/util.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/util.rs @@ -1,9 +1,9 @@ use core::ffi::{c_char, c_int}; +use yggdrasil_rt::sys as syscall; + use crate::{ - error::{self, CPtrResult}, - io::managed::{stderr, FILE}, - util::{PointerExt, PointerStrExt}, + error::{self, CIntZeroResult, CPtrResult, ResultExt}, headers::errno::Errno, io::managed::{stderr, FILE}, util::{PointerExt, PointerStrExt} }; #[no_mangle] @@ -30,12 +30,17 @@ unsafe extern "C" fn ctermid(_buf: *mut c_char) -> *mut c_char { } #[no_mangle] -unsafe extern "C" fn remove(_path: *const c_char) -> c_int { - todo!() +unsafe extern "C" fn remove(path: *const c_char) -> CIntZeroResult { + let path = path.ensure_str(); + syscall::remove(None, path).e_map_err(Errno::from)?; + CIntZeroResult::SUCCESS } #[no_mangle] -unsafe extern "C" fn rename(_src: *const c_char, _dst: *const c_char) -> c_int { +unsafe extern "C" fn rename(src: *const c_char, dst: *const c_char) -> c_int { + let src = src.ensure_str(); + let dst = dst.ensure_str(); + yggdrasil_rt::debug_trace!("rename {src:?} -> {dst:?}"); todo!() } diff --git a/userspace/lib/ygglibc/src/headers/stdlib/io.rs b/userspace/lib/ygglibc/src/headers/stdlib/io.rs index 7cb54562..73a91d75 100644 --- a/userspace/lib/ygglibc/src/headers/stdlib/io.rs +++ b/userspace/lib/ygglibc/src/headers/stdlib/io.rs @@ -1,4 +1,6 @@ -use core::ffi::{c_char, c_int}; +use core::{ffi::{c_char, c_int}, ptr::NonNull, slice}; + +use crate::{allocator::c_alloc, error::{self, CPtrResult, CResult}, headers::errno::Errno, io, util::PointerStrExt}; #[no_mangle] unsafe extern "C" fn grantpt(_fd: c_int) -> c_int { @@ -26,8 +28,28 @@ unsafe extern "C" fn ptsname(_fd: c_int) -> *mut c_char { } #[no_mangle] -unsafe extern "C" fn realpath(_path: *const c_char, _resolved: *mut c_char) -> *mut c_char { - todo!() +unsafe extern "C" fn realpath(path: *const c_char, mut resolved_ptr: *mut c_char) -> CPtrResult { + if path.is_null() { + error::errno = Errno::EINVAL; + return CPtrResult::ERROR; + } + let path = path.ensure_str(); + let resolved = io::realpath(path)?; + + let ptr = if let Some(ptr) = NonNull::new(resolved_ptr) { + ptr + } else { + c_alloc(4096, 1, false)?.cast() + }; + + let buffer = slice::from_raw_parts_mut(resolved_ptr.cast::(), 4096); + let src = resolved.as_str(); + + assert!(src.len() < 4096); + buffer[..src.len()].copy_from_slice(src.as_bytes()); + buffer[src.len()] = 0; + + CPtrResult::success(ptr) } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/stdlib/random.rs b/userspace/lib/ygglibc/src/headers/stdlib/random.rs index 41f9a6e3..d5bde8b7 100644 --- a/userspace/lib/ygglibc/src/headers/stdlib/random.rs +++ b/userspace/lib/ygglibc/src/headers/stdlib/random.rs @@ -1,5 +1,7 @@ use core::ffi::{c_char, c_double, c_int, c_long, c_uint, c_ushort}; +use crate::random; + #[no_mangle] unsafe extern "C" fn drand48() -> c_double { todo!() @@ -37,7 +39,7 @@ unsafe extern "C" fn nrand48(_xsubi: *mut c_ushort) -> c_long { #[no_mangle] unsafe extern "C" fn rand() -> c_int { - todo!() + random::random_u64() as _ } #[no_mangle] @@ -51,8 +53,8 @@ unsafe extern "C" fn random() -> c_long { } #[no_mangle] -unsafe extern "C" fn srand(_seed: c_uint) { - todo!() +unsafe extern "C" fn srand(seed: c_uint) { + random::seed_u64(seed as _); } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/sys_mman/mod.rs b/userspace/lib/ygglibc/src/headers/sys_mman/mod.rs index 3e376fcb..dfae8426 100644 --- a/userspace/lib/ygglibc/src/headers/sys_mman/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_mman/mod.rs @@ -1,6 +1,8 @@ -use core::ffi::{c_char, c_int, c_void}; +use core::{ffi::{c_char, c_int, c_void}, num::NonZeroUsize, ptr::{self, NonNull}}; -use crate::error::CPtrResult; +use yggdrasil_rt::{io::RawFd, mem::{MappingFlags, MappingSource}, sys as syscall}; + +use crate::{error::{self, CPtrResult, EResult, ResultExt, TryFromExt}, headers::errno::Errno}; use super::sys_types::{mode_t, off_t}; @@ -35,6 +37,44 @@ pub struct posix_typed_mem_info { pub posix_tmi_length: usize, } +unsafe fn mmap_inner( + hint: *mut c_void, + len: usize, + prot: c_int, + flags: c_int, + fd: c_int, + offset: off_t, +) -> EResult> { + let hint = NonNull::new(hint).map(NonNull::addr); + + let source = if fd == -1 { + // Anonymous mapping + MappingSource::Anonymous + } else { + // File-backed mapping + let fd = RawFd::e_try_from(fd)?; + let offset = offset.try_into().e_map_err(|_| Errno::EINVAL)?; + MappingSource::File(fd, offset) + }; + let mut mapping_flags = MappingFlags::empty(); + if prot & PROT_EXEC != 0 { + mapping_flags |= MappingFlags::EXEC; + } + if prot & PROT_WRITE != 0 { + mapping_flags |= MappingFlags::WRITE; + } + if flags != MAP_PRIVATE { + todo!("Non-private mappings via mmap(2) are not yet supported"); + } + + let address = syscall::map_memory(hint, len, mapping_flags, &source).e_map_err(Errno::from)?; + assert_ne!(address, 0); + + let ptr = ptr::with_exposed_provenance_mut(address); + let ptr = NonNull::new_unchecked(ptr); + EResult::Ok(ptr) +} + // msync #[no_mangle] @@ -75,14 +115,21 @@ unsafe extern "C" fn mprotect(_ptr: *mut c_void, _len: usize, _prot: c_int) -> c #[no_mangle] unsafe extern "C" fn mmap( - _hint: *mut c_void, - _len: usize, - _prot: c_int, - _flags: c_int, - _fd: c_int, - _offset: off_t, -) -> CPtrResult { - todo!() + hint: *mut c_void, + len: usize, + prot: c_int, + flags: c_int, + fd: c_int, + offset: off_t, +) -> *mut c_void { + match mmap_inner(hint, len, prot, flags, fd, offset) { + EResult::Ok(ptr) => ptr.as_ptr(), + EResult::Err(errno) => { + error::errno = errno; + // MAP_FAILED + usize::MAX as *mut c_void + } + } } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/sys_resource/mod.rs b/userspace/lib/ygglibc/src/headers/sys_resource/mod.rs index 3fc1c7af..07f5f102 100644 --- a/userspace/lib/ygglibc/src/headers/sys_resource/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_resource/mod.rs @@ -1,4 +1,4 @@ -use core::ffi::c_int; +use core::{ffi::c_int, ptr::NonNull}; use super::{sys_time::__ygg_timeval_t, sys_types::id_t}; @@ -40,8 +40,14 @@ unsafe extern "C" fn getpriority(_which: c_int, _who: id_t) -> c_int { } #[no_mangle] -unsafe extern "C" fn getrlimit(_which: c_int, _rlimit: *mut rlimit) -> c_int { - todo!() +unsafe extern "C" fn getrlimit(_which: c_int, rlimit: *mut rlimit) -> c_int { + if let Some(rlimit) = NonNull::new(rlimit) { + rlimit.write(rlimit { + rlim_cur: 0, + rlim_max: 0, + }); + } + -1 } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs index e36cd93f..20953b3e 100644 --- a/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs @@ -178,21 +178,20 @@ unsafe extern "C" fn fstat(fd: c_int, statbuf: *mut stat) -> CIntZeroResult { unsafe extern "C" fn fstatat( atfd: c_int, pathname: *const c_char, - _statbuf: *mut stat, + statbuf: *mut stat, opt: c_int, ) -> CIntZeroResult { - let _pathname = pathname.ensure_str(); - let _atfd = util::at_fd(atfd)?; - let _follow = opt & AT_SYMLINK_NOFOLLOW == 0; + let pathname = pathname.ensure_str(); + let atfd = util::at_fd(atfd)?; + let follow = opt & AT_SYMLINK_NOFOLLOW == 0; - todo!() - // let attr = io::get_metadata(atfd, pathname, follow)?; + let attr = io::get_metadata(atfd, pathname, follow)?; - // if let Some(statbuf) = statbuf.as_mut() { - // *statbuf = attr.into(); - // } + if let Some(statbuf) = statbuf.as_mut() { + *statbuf = attr.into(); + } - // CIntZeroResult::OK + CIntZeroResult::SUCCESS } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/time/timer.rs b/userspace/lib/ygglibc/src/headers/time/timer.rs index c9934246..9fb1220e 100644 --- a/userspace/lib/ygglibc/src/headers/time/timer.rs +++ b/userspace/lib/ygglibc/src/headers/time/timer.rs @@ -1,9 +1,10 @@ -use core::ffi::c_int; +use core::{ffi::c_int, ptr::NonNull}; -use crate::headers::{ - sys_time::__ygg_timespec_t, - sys_types::{clock_t, clockid_t, pid_t}, -}; +use crate::{error::{self, CIntZeroResult, CResult, EResult, ResultExt}, headers::{ + errno::Errno, sys_time::{__ygg_timespec_t, timespec}, sys_types::{clock_t, clockid_t, pid_t, time_t}, time::{CLOCK_MONOTONIC, CLOCK_REALTIME} +}}; + +use yggdrasil_rt::time::{self as rt, ClockType}; /* TODO @@ -16,6 +17,14 @@ int timer_settime(timer_t, int, const struct itimerspec *restrict, struct itimerspec *restrict); */ +fn clock_type(clock_id: clockid_t) -> EResult { + match clock_id { + CLOCK_REALTIME => EResult::Ok(ClockType::RealTime), + CLOCK_MONOTONIC => EResult::Ok(ClockType::Monotonic), + _ => EResult::Err(Errno::EINVAL) + } +} + #[no_mangle] unsafe extern "C" fn clock() -> clock_t { // TODO @@ -33,8 +42,18 @@ unsafe extern "C" fn clock_getres(_clock_id: clockid_t, _ts: *mut __ygg_timespec } #[no_mangle] -unsafe extern "C" fn clock_gettime(_clock_id: clockid_t, _ts: *mut __ygg_timespec_t) -> c_int { - todo!() +unsafe extern "C" fn clock_gettime(clock_id: clockid_t, ts: *mut __ygg_timespec_t) -> CIntZeroResult { + let clock = clock_type(clock_id)?; + let time = rt::get_clock(clock).e_map_err(Errno::from)?; + + if let Some(ts) = NonNull::new(ts) { + ts.write(timespec { + tv_sec: time_t(time.seconds as _), + tv_nsec: time.nanoseconds as _ + }); + } + + CIntZeroResult::SUCCESS } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/unistd/fs.rs b/userspace/lib/ygglibc/src/headers/unistd/fs.rs index 1c945171..26f911df 100644 --- a/userspace/lib/ygglibc/src/headers/unistd/fs.rs +++ b/userspace/lib/ygglibc/src/headers/unistd/fs.rs @@ -1,12 +1,12 @@ use core::ffi::{c_char, c_int, c_long}; -use crate::headers::sys_types::{gid_t, off_t, uid_t}; +use crate::{error::CIntZeroResult, headers::{fcntl::{faccessat, AT_FDCWD}, sys_types::{gid_t, off_t, uid_t}}, util::PointerExt}; pub const _PC_PATH_MAX: c_int = 0; #[no_mangle] -unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int { - todo!() +unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> CIntZeroResult { + faccessat(AT_FDCWD, path, mode, 0) } #[no_mangle] @@ -46,7 +46,16 @@ unsafe extern "C" fn ftruncate(fd: c_int, size: off_t) -> c_int { #[no_mangle] unsafe extern "C" fn getcwd(buf: *mut c_char, len: usize) -> *mut c_char { - todo!() + let buffer = buf.ensure_slice_mut(len); + // TODO + if buffer.len() < 2 { + return core::ptr::null_mut(); + } + + buffer[0] = b'/' as _; + buffer[1] = 0; + + buffer.as_mut_ptr() } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/unistd/io.rs b/userspace/lib/ygglibc/src/headers/unistd/io.rs index 1abecdc4..08faf5dc 100644 --- a/userspace/lib/ygglibc/src/headers/unistd/io.rs +++ b/userspace/lib/ygglibc/src/headers/unistd/io.rs @@ -3,9 +3,9 @@ use core::{ffi::{c_char, c_int, c_void}, mem::MaybeUninit}; use yggdrasil_rt::{debug_trace, io::{PipeOptions, RawFd, SeekFrom}}; use crate::{ - error::{CFdResult, CIntZeroResult, CIsizeResult, COffsetResult, TryFromExt}, - headers::sys_types::{off_t, pid_t}, - io::{self, raw::RawFile, IntoRawFd, Read, Seek, Write}, + error::{CFdResult, CIntZeroResult, CIsizeResult, COffsetResult, ResultExt, TryFromExt}, + headers::{errno::Errno, sys_types::{off_t, pid_t}}, + io::{self, raw::RawFile, IntoRawFd, Read, ReadAt, Seek, Write, WriteAt}, util::PointerExt, }; #[no_mangle] @@ -67,12 +67,20 @@ unsafe extern "C" fn pipe(fds: *mut c_int) -> CIntZeroResult { #[no_mangle] unsafe extern "C" fn pread(fd: c_int, buf: *mut c_void, len: usize, pos: off_t) -> CIsizeResult { - todo!() + let mut file = RawFile::e_try_from(fd)?; + let pos = u64::try_from(pos).e_map_err(|_| Errno::EINVAL)?; + let data = buf.cast::().ensure_slice_mut(len); + let count = file.read_at(pos, data)?; + CIsizeResult::success(count) } #[no_mangle] unsafe extern "C" fn pwrite(fd: c_int, buf: *const c_void, len: usize, pos: off_t) -> CIsizeResult { - todo!() + let mut file = RawFile::e_try_from(fd)?; + let pos = u64::try_from(pos).e_map_err(|_| Errno::EINVAL)?; + let data = buf.cast::().ensure_slice(len); + let count = file.write_at(pos, data)?; + CIsizeResult::success(count) } #[no_mangle] diff --git a/userspace/lib/ygglibc/src/headers/unistd/util.rs b/userspace/lib/ygglibc/src/headers/unistd/util.rs index 82b5beb9..ef9a7ff0 100644 --- a/userspace/lib/ygglibc/src/headers/unistd/util.rs +++ b/userspace/lib/ygglibc/src/headers/unistd/util.rs @@ -45,7 +45,11 @@ unsafe extern "C" fn swab(from: *const c_void, to: *mut c_void, n: isize) { } #[no_mangle] unsafe extern "C" fn sysconf(name: Sysconf) -> c_long { - todo!() + match name { + Sysconf::_SC_ARG_MAX => 256, + Sysconf::_SC_PAGE_SIZE => 0x1000, + _ => todo!("{name:?}"), + } } #[no_mangle] unsafe extern "C" fn ttyname(fd: c_int) -> *mut c_char { diff --git a/userspace/lib/ygglibc/src/io/mod.rs b/userspace/lib/ygglibc/src/io/mod.rs index 12e00791..1d867028 100644 --- a/userspace/lib/ygglibc/src/io/mod.rs +++ b/userspace/lib/ygglibc/src/io/mod.rs @@ -1,8 +1,7 @@ use core::{ffi::c_int, mem::MaybeUninit}; use yggdrasil_rt::{ - io::{FileAttr, PipeOptions, RawFd, SeekFrom}, - sys as syscall, + io::{FileAttr, PipeOptions, RawFd, SeekFrom}, path::{Path, PathBuf}, sys as syscall }; use crate::{ @@ -17,6 +16,14 @@ pub mod managed; pub mod raw; pub mod dir; +pub trait ReadAt { + fn read_at(&mut self, pos: u64, buffer: &mut [u8]) -> EResult; +} + +pub trait WriteAt { + fn write_at(&mut self, pos: u64, buffer: &[u8]) -> EResult; +} + pub trait Write { fn write(&mut self, data: &[u8]) -> EResult; fn flush(&mut self) -> EResult<()>; @@ -122,6 +129,44 @@ pub fn get_metadata(at: Option, path: &str, follow: bool) -> EResult>(path: P) -> EResult { + // Cases: + // * /a/b/c -> /a/b/c + // * /a/../c -> /c + // * /a/./c -> /a/c + // * a/./c -> /dir/a/c + let mut path = path.as_ref(); + let mut buffer = PathBuf::from("/"); + + yggdrasil_rt::debug_trace!("realpath({path:?})"); + + loop { + if path.is_empty() { + break; + } + let (head, tail) = path.split_left(); + + yggdrasil_rt::debug_trace!("* {head:?}"); + + match head { + ".." => { + // Remove the last element + buffer = buffer.split_right().0.into(); + }, + "." | "" => (), + name => { + buffer.push(name); + }, + } + + path = tail; + } + + yggdrasil_rt::debug_trace!("-> {buffer:?}"); + + EResult::Ok(buffer) +} + pub unsafe fn init() { managed::init(); } diff --git a/userspace/lib/ygglibc/src/io/raw.rs b/userspace/lib/ygglibc/src/io/raw.rs index c40098fc..37036b61 100644 --- a/userspace/lib/ygglibc/src/io/raw.rs +++ b/userspace/lib/ygglibc/src/io/raw.rs @@ -8,7 +8,7 @@ use yggdrasil_rt::{ use crate::error::{EResult, TryFromExt}; -use super::{AsRawFd, FromRawFd, IntoRawFd, Read, Seek, Write}; +use super::{AsRawFd, FromRawFd, IntoRawFd, Read, ReadAt, Seek, Write, WriteAt}; #[repr(transparent)] pub struct RawFile(RawFd); @@ -53,6 +53,20 @@ impl IntoRawFd for RawFile { } } +impl WriteAt for RawFile { + fn write_at(&mut self, pos: u64, buffer: &[u8]) -> EResult { + let count = unsafe { syscall::write_at(self.0, pos, buffer) }?; + EResult::Ok(count) + } +} + +impl ReadAt for RawFile { + fn read_at(&mut self, pos: u64, buffer: &mut [u8]) -> EResult { + let count = unsafe { syscall::read_at(self.0, pos, buffer) }?; + EResult::Ok(count) + } +} + impl Write for RawFile { fn write(&mut self, data: &[u8]) -> EResult { let count = unsafe { syscall::write(self.0, data) }?; diff --git a/userspace/lib/ygglibc/src/lib.rs b/userspace/lib/ygglibc/src/lib.rs index 8205b3c4..94a32f9b 100644 --- a/userspace/lib/ygglibc/src/lib.rs +++ b/userspace/lib/ygglibc/src/lib.rs @@ -55,6 +55,7 @@ mod ssp; mod thread; mod types; mod util; +mod random; pub mod headers; diff --git a/userspace/lib/ygglibc/src/random.rs b/userspace/lib/ygglibc/src/random.rs new file mode 100644 index 00000000..497c1a63 --- /dev/null +++ b/userspace/lib/ygglibc/src/random.rs @@ -0,0 +1,36 @@ +use core::cell::RefCell; + +use yggdrasil_rt::process::thread_local; + + +struct RandomState { + xs64: u64 +} + +impl RandomState { + pub fn generate(&mut self) -> u64 { + let mut x = self.xs64; + x ^= x << 13; + x ^= x >> 7; + x ^= x << 17; + self.xs64 = x; + x + } + + pub fn seed(&mut self, seed: u64) { + self.xs64 = seed; + } +} + +#[thread_local] +static STATE: RefCell = RefCell::new(RandomState { + xs64: 1234, +}); + +pub fn random_u64() -> u64 { + STATE.borrow_mut().generate() +} + +pub fn seed_u64(seed: u64) { + STATE.borrow_mut().seed(seed); +} diff --git a/userspace/shell/src/builtins.rs b/userspace/shell/src/builtins.rs index b68c67f6..89496edc 100644 --- a/userspace/shell/src/builtins.rs +++ b/userspace/shell/src/builtins.rs @@ -12,6 +12,8 @@ pub type BuiltinCommand = fn(&[String], &mut HashMap) -> Result< static BUILTINS: &[(&str, BuiltinCommand)] = &[ ("echo", b_echo), ("set", b_set), + ("cd", b_cd), + ("pwd", b_pwd), ("which", b_which), ("exit", b_exit), ]; @@ -91,3 +93,25 @@ fn b_exit(args: &[String], _envs: &mut HashMap) -> Result) -> Result { + let path = if args.is_empty() { + "/" + } else { + args[0].as_str() + }; + yggdrasil_rt::io::set_current_directory(path).map_err(Error::RtError)?; + Ok(Outcome::Exited(0)) +} + +fn b_pwd(args: &[String], _envs: &mut HashMap) -> Result { + if !args.is_empty() { + eprintln!("Usage: pwd"); + return Ok(Outcome::Exited(1)); + } + + let pwd = yggdrasil_rt::io::current_directory_string().map_err(Error::RtError)?; + println!("{pwd}"); + + Ok(Outcome::Exited(0)) +} diff --git a/userspace/shell/src/main.rs b/userspace/shell/src/main.rs index e1f89109..8cd0cf1d 100644 --- a/userspace/shell/src/main.rs +++ b/userspace/shell/src/main.rs @@ -20,6 +20,8 @@ mod sys; pub enum Error { #[error("{0}")] IoError(#[from] io::Error), + #[error("{0:?}")] + RtError(yggdrasil_rt::Error) } #[derive(Parser)] diff --git a/xtask/src/build/userspace.rs b/xtask/src/build/userspace.rs index 0a5cb6aa..b0da7a57 100644 --- a/xtask/src/build/userspace.rs +++ b/xtask/src/build/userspace.rs @@ -96,7 +96,7 @@ fn build_rootfs, D: AsRef>( log::info!("Building rootfs: {}", rootfs_dir.display()); - for dir in ["sbin", "bin", "dev", "sys", "tmp"] { + for dir in ["sbin", "bin", "dev", "sys", "tmp", "mnt"] { fs::create_dir_all(rootfs_dir.join(dir))?; File::create(rootfs_dir.join(dir).join(".do_not_remove"))?; }