vfs: force all symlinks to be path-based, chdir/getcwd
This commit is contained in:
parent
55e6dae194
commit
ba00c97c66
166
Cargo.lock
generated
166
Cargo.lock
generated
@ -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"
|
||||
|
@ -6,7 +6,8 @@ exclude = [
|
||||
"tool/abi-generator",
|
||||
"toolchain",
|
||||
"userspace/dynload-program",
|
||||
"userspace/lib/ygglibc"
|
||||
"userspace/lib/ygglibc",
|
||||
"toolchain-c"
|
||||
]
|
||||
members = [
|
||||
"xtask",
|
||||
|
@ -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<AhciPort>, 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<AhciPort>, 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();
|
||||
|
@ -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<NvmeController>) {
|
||||
pub fn register_nvme_namespace(namespace: Arc<NvmeNamespace>, 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<NvmeNamespace>, 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:?}"))
|
||||
|
@ -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<MacAddress, Error> {
|
||||
arp::lookup(self.interface.id, self.gateway_ip, true).await
|
||||
}
|
||||
|
@ -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
|
||||
|
20
kernel/libk/libk-util/src/ext.rs
Normal file
20
kernel/libk/libk-util/src/ext.rs
Normal file
@ -0,0 +1,20 @@
|
||||
//! Extensions to core types
|
||||
|
||||
pub trait OptionExt<T> {
|
||||
fn get_or_try_insert_with<E, F: FnOnce() -> Result<T, E>>(
|
||||
&mut self,
|
||||
init: F,
|
||||
) -> Result<&mut T, E>;
|
||||
}
|
||||
|
||||
impl<T> OptionExt<T> for Option<T> {
|
||||
fn get_or_try_insert_with<E, F: FnOnce() -> Result<T, E>>(
|
||||
&mut self,
|
||||
init: F,
|
||||
) -> Result<&mut T, E> {
|
||||
match self {
|
||||
Self::None => Ok(self.insert(init()?)),
|
||||
Self::Some(value) => Ok(value),
|
||||
}
|
||||
}
|
||||
}
|
@ -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<usize, Error>;
|
||||
}
|
||||
|
||||
/// 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<usize, Error>;
|
||||
}
|
||||
|
||||
/// Immutable read interface for VFS objects
|
||||
pub trait Read {
|
||||
/// Reads bytes into the given buffer
|
||||
@ -49,6 +61,13 @@ impl<F: Read> Read for Arc<F> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: ReadAt> ReadAt for Arc<F> {
|
||||
#[inline]
|
||||
fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
self.as_ref().read_at(pos, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Seek> Seek for Arc<F> {
|
||||
#[inline]
|
||||
fn seek(&self, from: SeekFrom) -> Result<u64, Error> {
|
||||
@ -60,3 +79,10 @@ impl<F: Seek> Seek for Arc<F> {
|
||||
self.as_ref().tell()
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: WriteAt> WriteAt for Arc<F> {
|
||||
#[inline]
|
||||
fn write_at(&self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
|
||||
self.as_ref().write_at(pos, buf)
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ use core::{
|
||||
};
|
||||
|
||||
pub mod event;
|
||||
pub mod ext;
|
||||
pub mod hash_table;
|
||||
pub mod io;
|
||||
pub mod lru_hash_table;
|
||||
|
@ -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<T> {
|
||||
last_index: AtomicU32,
|
||||
}
|
||||
|
||||
pub type DisplayRegisterCallback = fn(&Arc<dyn DisplayDevice>, Option<NodeRef>);
|
||||
pub type DisplayRegisterCallback = fn(&Arc<dyn DisplayDevice>, Option<(&str, NodeRef)>);
|
||||
|
||||
// Manages devices: fb<N>
|
||||
pub struct DisplayDeviceRegistry {
|
||||
@ -78,7 +78,7 @@ impl TerminalRegistry {
|
||||
|
||||
pub fn register(&self, terminal: Arc<dyn CharDevice>) -> Result<u32, Error> {
|
||||
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<Arc<dyn DebugSink>>,
|
||||
) -> Result<u32, Error> {
|
||||
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<dyn DisplayDevice>, boot: bool) -> Result<u32, Error> {
|
||||
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)
|
||||
}
|
||||
|
@ -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<NodeRef> = 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<dyn CharDevice>, name: String) -> Result<(), Error> {
|
||||
pub fn add_named_char_device(
|
||||
dev: Arc<dyn CharDevice>,
|
||||
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<dyn CharDevice>, name: String) -> Result<(
|
||||
pub fn add_named_block_device<S: Into<String>>(
|
||||
dev: Arc<dyn BlockDevice>,
|
||||
name: S,
|
||||
mode: FileMode,
|
||||
) -> Result<NodeRef, Error> {
|
||||
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())?;
|
||||
|
||||
|
@ -62,7 +62,7 @@ impl RandomState {
|
||||
static RANDOM_STATE: OneTimeInit<IrqSafeSpinlock<RandomState>> = 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
|
||||
}
|
||||
|
@ -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<usize, Error> {
|
||||
match self {
|
||||
Self::Regular(file) => file.read_at(pos, buf),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteAt for File {
|
||||
fn write_at(&self, pos: u64, buf: &[u8]) -> Result<usize, Error> {
|
||||
match self {
|
||||
Self::Regular(file) => file.write_at(pos, buf),
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for File {
|
||||
fn tell(&self) -> Result<u64, Error> {
|
||||
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<u64, Error> {
|
||||
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},
|
||||
|
@ -36,6 +36,22 @@ impl RegularFile {
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
pub fn read_at(&self, pos: u64, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
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<usize, Error> {
|
||||
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<u64, Error> {
|
||||
let mut position = self.position.lock();
|
||||
|
@ -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<AccessToken, Error> {
|
||||
pub fn check_access(&self, node: &NodeRef, mode: AccessMode) -> Result<AccessToken, Error> {
|
||||
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<P: AsRef<Path>>(&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<FileRef, Error> {
|
||||
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<P: AsRef<Path>>(&mut self, path: P) -> Result<FileRef, Error> {
|
||||
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<NodeRef, 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)?;
|
||||
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<NodeRef>,
|
||||
path: P,
|
||||
follow_links: bool,
|
||||
follow_mount: bool,
|
||||
) -> Result<NodeRef, Error> {
|
||||
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<NodeRef, Error> {
|
||||
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<NodeRef, Error> {
|
||||
// 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<NodeRef, Error> {
|
||||
fn _resolve(&self, mut at: NodeRef, follow_links: bool) -> Result<NodeRef, Error> {
|
||||
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<P: AsRef<Path>>(
|
||||
&self,
|
||||
mut at: NodeRef,
|
||||
path: &Path,
|
||||
follow_links: bool,
|
||||
follow_mount: bool,
|
||||
) -> Result<NodeRef, Error> {
|
||||
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<NodeRef, Error> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -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<R: ReadFn> {
|
||||
|
||||
/// 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<F>
|
||||
where
|
||||
F: ReadLinkFn + 'static,
|
||||
{
|
||||
read: F,
|
||||
}
|
||||
|
||||
impl<T, R> ReadOnlyFnValueNode<T, R>
|
||||
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<NodeRef, Error> {
|
||||
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<F> FnSymlink<F>
|
||||
where
|
||||
F: ReadLinkFn + 'static,
|
||||
{
|
||||
/// Creates a new [FnSymlink] node
|
||||
pub fn new(read: F) -> Self {
|
||||
Self { read }
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> CommonImpl for FnSymlink<F> where F: ReadLinkFn + 'static {}
|
||||
impl<F> SymlinkImpl for FnSymlink<F>
|
||||
where
|
||||
F: ReadLinkFn + 'static,
|
||||
{
|
||||
fn target(&self, _node: &NodeRef) -> Result<NodeRef, Error> {
|
||||
(self.read)()
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a read-only value node with given `value`
|
||||
pub fn const_value_node<T>(value: T) -> NodeRef
|
||||
where
|
||||
@ -462,7 +426,11 @@ where
|
||||
|
||||
/// Creates a read-only node with given read function
|
||||
pub fn read_fn_node<R: ReadFn + 'static>(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<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(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<String>) -> NodeRef {
|
||||
Node::symlink(
|
||||
FixedPathSymlink {
|
||||
@ -491,14 +454,6 @@ pub fn fixed_path_symlink(target: impl Into<String>) -> NodeRef {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fn_symlink<R: ReadLinkFn + 'static>(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;
|
||||
|
@ -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<Option<(String, NodeRef)>>,
|
||||
pub(crate) target: IrqSafeRwLock<Option<String>>,
|
||||
pub(crate) imp: Box<dyn SymlinkImpl>,
|
||||
}
|
||||
|
||||
@ -223,20 +226,26 @@ impl Node {
|
||||
}
|
||||
|
||||
/// Creates a new block device node with given [BlockDevice]
|
||||
pub fn block(device: Arc<dyn BlockDevice>, flags: NodeFlags) -> NodeRef {
|
||||
pub fn block(device: Arc<dyn BlockDevice>, 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<dyn CharDevice>, flags: NodeFlags) -> NodeRef {
|
||||
pub fn char(device: Arc<dyn CharDevice>, 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<T: SymlinkImpl + 'static>(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<NodeRef, Error> {
|
||||
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<String, Error> {
|
||||
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<dyn BlockDevice>, Error> {
|
||||
|
@ -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)?;
|
||||
|
@ -28,7 +28,7 @@ pub trait CommonImpl: Send + Sync {
|
||||
|
||||
/// Fetches the metadata of the file from underlying storage
|
||||
fn metadata(&self, node: &NodeRef) -> Result<Metadata, Error> {
|
||||
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<NodeRef, Error> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
// /// Returns the target node (if such is available directly) of the link
|
||||
// fn target(&self, node: &NodeRef) -> Result<NodeRef, Error> {
|
||||
// Err(Error::NotImplemented)
|
||||
// }
|
||||
|
||||
/// Fetches the contents of the symlink into a [String]
|
||||
fn read_to_string(&self) -> Result<String, Error> {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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(())
|
||||
}
|
||||
|
@ -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()?;
|
||||
|
@ -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:?}");
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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<RawFd>, 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<usize, Error> {
|
||||
run_with_io(&process, |io| io.files.file(fd)?.read(buffer))
|
||||
}
|
||||
|
||||
pub(crate) fn read_at(fd: RawFd, pos: u64, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
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<usize, Error> {
|
||||
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<RawFd>, path: &str) -> Result<RawFd, Err
|
||||
let process = thread.process();
|
||||
|
||||
run_with_io_at(&process, at, |at, mut io| {
|
||||
let node = io.ioctx_mut().find(Some(at), path, true, true)?;
|
||||
let access = io.ioctx_mut().check_access(vfs::Action::Read, &node)?;
|
||||
let node = io.ioctx_mut().find(Some(at), path, true)?;
|
||||
let access = io.ioctx_mut().check_access(&node, AccessMode::READ)?;
|
||||
let file = node.open_directory(access)?;
|
||||
let fd = io.files.place_file(file, true)?;
|
||||
|
||||
@ -180,7 +212,7 @@ pub(crate) fn get_metadata(
|
||||
let node = if path.is_empty() {
|
||||
at
|
||||
} else {
|
||||
io.ioctx_mut().find(Some(at), path, follow, true)?
|
||||
io.ioctx_mut().find(Some(at), path, follow)?
|
||||
};
|
||||
|
||||
let metadata = node.metadata()?;
|
||||
@ -209,7 +241,7 @@ pub(crate) fn read_link(at: Option<RawFd>, 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)
|
||||
|
@ -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(())
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -111,10 +111,13 @@ syscall start_session() -> Result<()>;
|
||||
|
||||
// I/O
|
||||
syscall open(at: Option<RawFd>, path: &str, opts: OpenOptions, mode: FileMode) -> Result<RawFd>;
|
||||
syscall check_access(at: Option<RawFd>, path: &str, access: AccessMode) -> Result<()>;
|
||||
syscall close(fd: RawFd) -> Result<()>;
|
||||
syscall write(fd: RawFd, data: &[u8]) -> Result<usize>;
|
||||
syscall read(fd: RawFd, data: &mut [u8]) -> Result<usize>;
|
||||
syscall seek(fd: RawFd, pos: SeekFrom, output: &mut u64) -> Result<()>;
|
||||
syscall read_at(fd: RawFd, pos: u64, data: &mut [u8]) -> Result<usize>;
|
||||
syscall write_at(fd: RawFd, pos: u64, data: &[u8]) -> Result<usize>;
|
||||
|
||||
syscall open_directory(at: Option<RawFd>, path: &str) -> Result<RawFd>;
|
||||
syscall read_directory_entries(fd: RawFd, entries: &mut [MaybeUninit<DirectoryEntry>]) -> Result<usize>;
|
||||
|
@ -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};
|
||||
|
@ -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<P: AsRef<Path>>(&mut self, _path: P) {
|
||||
todo!()
|
||||
pub fn push<P: AsRef<Path>>(&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<P: AsRef<Path>>(&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<P: AsRef<Path>>(&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<String> for PathBuf {
|
||||
fn from(value: String) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for Path {
|
||||
type Owned = PathBuf;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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, F: FnOnce(&str) -> T>(mapper: F) -> Result<T, Error> {
|
||||
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<String, Error> {
|
||||
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(())
|
||||
}
|
||||
|
@ -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,
|
||||
|
35
test.c
35
test.c
@ -3,41 +3,6 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -12,5 +12,9 @@ pub fn find_library(name: &str) -> Result<PathBuf, Error> {
|
||||
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()))
|
||||
}
|
||||
|
4
userspace/etc/test.S
Normal file
4
userspace/etc/test.S
Normal file
@ -0,0 +1,4 @@
|
||||
.section .text
|
||||
.global _start
|
||||
_start:
|
||||
jmp .
|
3
userspace/etc/test.c
Normal file
3
userspace/etc/test.c
Normal file
@ -0,0 +1,3 @@
|
||||
void _start(void) {
|
||||
|
||||
}
|
@ -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(
|
||||
|
@ -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<OpenMode> {
|
||||
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::<c_int>() };
|
||||
todo!();
|
||||
let raw = unsafe { ap.arg::<c_int>() };
|
||||
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
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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!()
|
||||
}
|
||||
|
||||
|
@ -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<c_char> {
|
||||
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::<u8>(), 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]
|
||||
|
@ -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]
|
||||
|
@ -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<NonNull<c_void>> {
|
||||
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<c_void> {
|
||||
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]
|
||||
|
@ -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]
|
||||
|
@ -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]
|
||||
|
@ -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<ClockType> {
|
||||
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]
|
||||
|
@ -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]
|
||||
|
@ -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::<u8>().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::<u8>().ensure_slice(len);
|
||||
let count = file.write_at(pos, data)?;
|
||||
CIsizeResult::success(count)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -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 {
|
||||
|
@ -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<usize>;
|
||||
}
|
||||
|
||||
pub trait WriteAt {
|
||||
fn write_at(&mut self, pos: u64, buffer: &[u8]) -> EResult<usize>;
|
||||
}
|
||||
|
||||
pub trait Write {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize>;
|
||||
fn flush(&mut self) -> EResult<()>;
|
||||
@ -122,6 +129,44 @@ pub fn get_metadata(at: Option<RawFd>, path: &str, follow: bool) -> EResult<File
|
||||
EResult::Ok(unsafe { metadata.assume_init() })
|
||||
}
|
||||
|
||||
pub fn realpath<P: AsRef<Path>>(path: P) -> EResult<PathBuf> {
|
||||
// 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();
|
||||
}
|
||||
|
@ -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<usize> {
|
||||
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<usize> {
|
||||
let count = unsafe { syscall::read_at(self.0, pos, buffer) }?;
|
||||
EResult::Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for RawFile {
|
||||
fn write(&mut self, data: &[u8]) -> EResult<usize> {
|
||||
let count = unsafe { syscall::write(self.0, data) }?;
|
||||
|
@ -55,6 +55,7 @@ mod ssp;
|
||||
mod thread;
|
||||
mod types;
|
||||
mod util;
|
||||
mod random;
|
||||
|
||||
pub mod headers;
|
||||
|
||||
|
36
userspace/lib/ygglibc/src/random.rs
Normal file
36
userspace/lib/ygglibc/src/random.rs
Normal file
@ -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<RandomState> = 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);
|
||||
}
|
@ -12,6 +12,8 @@ pub type BuiltinCommand = fn(&[String], &mut HashMap<String, String>) -> 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<String, String>) -> Result<Outcom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn b_cd(args: &[String], _envs: &mut HashMap<String, String>) -> Result<Outcome, Error> {
|
||||
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<String, String>) -> Result<Outcome, Error> {
|
||||
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))
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ mod sys;
|
||||
pub enum Error {
|
||||
#[error("{0}")]
|
||||
IoError(#[from] io::Error),
|
||||
#[error("{0:?}")]
|
||||
RtError(yggdrasil_rt::Error)
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
|
@ -96,7 +96,7 @@ fn build_rootfs<S: AsRef<Path>, D: AsRef<Path>>(
|
||||
|
||||
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"))?;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user