vfs: force all symlinks to be path-based, chdir/getcwd

This commit is contained in:
Mark Poliakov 2024-12-20 23:00:43 +02:00
parent 55e6dae194
commit ba00c97c66
61 changed files with 1204 additions and 407 deletions

166
Cargo.lock generated
View File

@ -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"

View File

@ -6,7 +6,8 @@ exclude = [
"tool/abi-generator",
"toolchain",
"userspace/dynload-program",
"userspace/lib/ygglibc"
"userspace/lib/ygglibc",
"toolchain-c"
]
members = [
"xtask",

View File

@ -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();

View File

@ -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:?}"))

View File

@ -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
}

View File

@ -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

View 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),
}
}
}

View File

@ -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)
}
}

View File

@ -18,6 +18,7 @@ use core::{
};
pub mod event;
pub mod ext;
pub mod hash_table;
pub mod io;
pub mod lru_hash_table;

View File

@ -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)
}

View File

@ -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())?;

View File

@ -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
}

View File

@ -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},

View File

@ -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();

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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> {

View File

@ -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)?;

View File

@ -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> {

View File

@ -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();
}
}
});

View File

@ -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);

View File

@ -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(())
}

View File

@ -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()?;

View File

@ -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:?}");
}

View File

@ -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> {

View File

@ -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)

View File

@ -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(())
}),
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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>;

View File

@ -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};

View File

@ -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;

View File

@ -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

View File

@ -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(())
}

View File

@ -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
View File

@ -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;
}

View File

@ -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)

View File

@ -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
View File

@ -0,0 +1,4 @@
.section .text
.global _start
_start:
jmp .

3
userspace/etc/test.c Normal file
View File

@ -0,0 +1,3 @@
void _start(void) {
}

View File

@ -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(

View File

@ -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
}

View File

@ -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]

View File

@ -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]

View File

@ -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!()
}

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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]

View File

@ -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 {

View File

@ -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();
}

View File

@ -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) }?;

View File

@ -55,6 +55,7 @@ mod ssp;
mod thread;
mod types;
mod util;
mod random;
pub mod headers;

View 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);
}

View File

@ -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))
}

View File

@ -20,6 +20,8 @@ mod sys;
pub enum Error {
#[error("{0}")]
IoError(#[from] io::Error),
#[error("{0:?}")]
RtError(yggdrasil_rt::Error)
}
#[derive(Parser)]

View File

@ -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"))?;
}