vfs: implement filename validation

This commit is contained in:
Mark Poliakov 2025-01-01 21:17:08 +02:00
parent 77b6403c68
commit d597197ca2
18 changed files with 290 additions and 80 deletions

@ -11,7 +11,10 @@ use libk::{
block,
error::Error,
time::real_time,
vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef},
vfs::{
CommonImpl, DirectoryImpl, DirectoryOpenPosition, Filename, Metadata, Node, NodeFlags,
NodeRef,
},
};
use walk::{DirentIter, DirentIterMut};
use yggdrasil_abi::io::{DirectoryEntry, FileMode, FileType};
@ -38,11 +41,11 @@ impl DirentName<'_> {
}
}
impl<'a> TryFrom<&'a str> for DirentName<'a> {
impl<'a> TryFrom<&'a Filename> for DirentName<'a> {
type Error = Error;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
if value.len() >= 255 || value.is_empty() || value.contains('/') {
fn try_from(value: &'a Filename) -> Result<Self, Self::Error> {
if value.len() >= 255 {
return Err(Error::InvalidArgument);
}
Ok(Self(value))
@ -359,7 +362,7 @@ impl DirectoryImpl for DirectoryNode {
Err(Error::NotImplemented)
}
fn lookup(&self, _node: &NodeRef, name: &str) -> Result<NodeRef, Error> {
fn lookup(&self, _node: &NodeRef, name: &Filename) -> Result<NodeRef, Error> {
let name = DirentName::try_from(name)?;
block!(self.lookup_entry(name).await)?
}
@ -375,7 +378,12 @@ impl DirectoryImpl for DirectoryNode {
block!(InodeAccess::allocate(&self.fs, ty, mode, Some(self.inode.ino())).await)?
}
fn attach_node(&self, _parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
fn attach_node(
&self,
_parent: &NodeRef,
child: &NodeRef,
name: &Filename,
) -> Result<(), Error> {
if self.fs.force_readonly {
return Err(Error::ReadOnly);
}
@ -391,7 +399,12 @@ impl DirectoryImpl for DirectoryNode {
Ok(())
}
fn unlink_node(&self, _parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
fn unlink_node(
&self,
_parent: &NodeRef,
child: &NodeRef,
name: &Filename,
) -> Result<(), Error> {
if self.fs.force_readonly {
return Err(Error::ReadOnly);
}

@ -10,7 +10,7 @@ use alloc::sync::Arc;
use block::BlockAllocator;
use dir::DirectoryNode;
use file::FileNode;
use libk::vfs::{impls::fixed_path_symlink, AccessToken, Filesystem, Metadata, NodeRef};
use libk::vfs::{impls::fixed_path_symlink, AccessToken, Filename, Filesystem, Metadata, NodeRef};
use libk_util::sync::IrqSafeSpinlock;
use tar::TarEntry;
use yggdrasil_abi::{
@ -80,8 +80,10 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
assert!(!element.is_empty());
assert!(!element.contains('/'));
let filename = Filename::new(element)?;
// let node = at.lookup(element);
let node = at.lookup_or_load(element, access);
let node = at.lookup_or_load(filename, access);
let node = match node {
Ok(node) => node,
Err(Error::DoesNotExist) => {
@ -90,7 +92,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
}
let node = DirectoryNode::<A>::new(self.clone(), Metadata::now_root(mode));
at.add_child(element, node.clone())?;
at.add_child(filename, node.clone())?;
node
}
@ -140,6 +142,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
let (dirname, filename) = path.split_right();
let parent = self.make_path(&root, dirname, true, FileMode::new(0o755))?;
let node = self.create_node_initial(hdr)?;
let filename = Filename::new(filename)?;
parent.add_child(filename, node)?;
}

@ -1,6 +1,6 @@
//! Device virtual file system
use alloc::{string::String, sync::Arc};
use alloc::sync::Arc;
use libk_util::OneTimeInit;
use yggdrasil_abi::{error::Error, io::FileMode};
@ -8,6 +8,7 @@ use crate::{
device::{block::BlockDevice, char::CharDevice},
vfs::{
impls::{fixed_path_symlink, MemoryDirectory},
path::OwnedFilename,
AccessToken, Metadata, Node, NodeFlags, NodeRef,
},
};
@ -29,21 +30,24 @@ pub fn root() -> &'static NodeRef {
DEVFS_ROOT.get()
}
pub fn redirect(name: &str, destination: &str) -> Result<(), Error> {
pub fn redirect<S: AsRef<str>>(name: S, destination: &str) -> Result<(), Error> {
let name = name.as_ref();
log::info!("Redirect {name} -> {destination}");
let root = DEVFS_ROOT.get();
root.remove_file(name, unsafe { AccessToken::authorized() })
let filename = OwnedFilename::new(name)?;
root.remove_file(filename.as_ref(), unsafe { AccessToken::authorized() })
.ok();
root.add_child(name, fixed_path_symlink(destination))
root.add_child(filename, fixed_path_symlink(destination))
}
/// Adds a character device with a custom name
pub fn add_named_char_device(
pub fn add_named_char_device<S: AsRef<str>>(
dev: Arc<dyn CharDevice>,
name: String,
name: S,
mode: FileMode,
) -> Result<(), Error> {
let name = name.as_ref();
log::info!("Add char device: {}", name);
let node = Node::char(
@ -53,16 +57,17 @@ pub fn add_named_char_device(
None,
);
DEVFS_ROOT.get().add_child(name, node)
let filename = OwnedFilename::new(name)?;
DEVFS_ROOT.get().add_child(filename, node)
}
/// Adds a block device with a custom name
pub fn add_named_block_device<S: Into<String>>(
pub fn add_named_block_device<S: AsRef<str>>(
dev: Arc<dyn BlockDevice>,
name: S,
mode: FileMode,
) -> Result<NodeRef, Error> {
let name = name.into();
let name = name.as_ref();
log::info!("Add block device: {}", name);
let node = Node::block(
@ -72,7 +77,8 @@ pub fn add_named_block_device<S: Into<String>>(
None,
);
DEVFS_ROOT.get().add_child(name, node.clone())?;
let filename = OwnedFilename::new(name)?;
DEVFS_ROOT.get().add_child(filename, node.clone())?;
Ok(node)
}

@ -8,7 +8,7 @@ use yggdrasil_abi::{
use crate::{
fs::sysfs::object::KObject,
vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl},
vfs::{CommonImpl, Filename, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl},
};
use super::Attribute;
@ -123,7 +123,8 @@ impl<V: BytesAttributeOps> Attribute<V::Data> for BytesAttribute<V> {
))
}
fn name(&self) -> &str {
V::NAME
// TODO implement this properly
fn name(&self) -> &Filename {
unsafe { Filename::from_str_unchecked(V::NAME) }
}
}

@ -1,7 +1,7 @@
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
use crate::vfs::NodeRef;
use crate::vfs::{Filename, NodeRef};
use super::object::KObject;
@ -10,7 +10,7 @@ mod string;
pub trait Attribute<D>: Sync + Send {
fn instantiate(&self, parent: &Arc<KObject<D>>) -> Result<NodeRef, Error>;
fn name(&self) -> &str;
fn name(&self) -> &Filename;
}
pub use bytes::{BytesAttribute, BytesAttributeOps};

@ -13,7 +13,7 @@ use yggdrasil_abi::{
use crate::{
fs::sysfs::object::KObject,
vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl},
vfs::{CommonImpl, Filename, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl},
};
use super::Attribute;
@ -191,7 +191,8 @@ impl<V: StringAttributeOps> Attribute<V::Data> for StringAttribute<V> {
))
}
fn name(&self) -> &str {
V::NAME
// TODO implement this properly
fn name(&self) -> &Filename {
unsafe { Filename::from_str_unchecked(V::NAME) }
}
}

@ -1,8 +1,8 @@
use alloc::{string::String, sync::Arc};
use alloc::sync::Arc;
use libk_util::OneTimeInit;
use yggdrasil_abi::{error::Error, io::FileMode};
use crate::vfs::{impls::MemoryDirectory, Metadata, Node, NodeFlags};
use crate::vfs::{impls::MemoryDirectory, path::OwnedFilename, Metadata, Node, NodeFlags};
use super::attribute::Attribute;
@ -28,12 +28,13 @@ impl<D> KObject<D> {
Ok(())
}
pub fn add_object<S: Into<String>, T>(
pub fn add_object<S: AsRef<str>, T>(
&self,
name: S,
child: Arc<KObject<T>>,
) -> Result<(), Error> {
self.node.add_child(name, child.node.clone())
let filename = OwnedFilename::new(name)?;
self.node.add_child(filename, child.node.clone())
}
pub(super) fn data(&self) -> &D {

@ -6,10 +6,10 @@ use yggdrasil_abi::{
use crate::vfs::{
node::{AccessToken, CreateInfo},
FileRef, NodeRef,
FileRef, Filename, NodeRef,
};
use super::Node;
use super::{path::OwnedFilename, Node};
/// Describes a general filesystem access
pub enum Action {
@ -189,15 +189,16 @@ impl IoContext {
// let create_mode = mode & !self.umask;
let (parent, name) = path.split_right();
let parent = self.find(at, parent, true)?;
let filename = OwnedFilename::new(name)?;
let create_info = CreateInfo {
name: name.into(),
name: filename,
mode: mode & !self.umask,
uid: self.uid,
gid: self.gid,
ty: FileType::File,
};
let access = self.check_access(&parent, AccessMode::WRITE)?;
parent.create(&create_info, access)?
parent.create(create_info, access)?
}
Err(err) => return Err(err),
};
@ -232,15 +233,16 @@ impl IoContext {
let (parent, name) = path.split_right();
let parent = self.find(at, parent, true)?;
let access = self.check_access(&parent, AccessMode::WRITE)?;
let filename = OwnedFilename::new(name)?;
let create_info = CreateInfo {
name: name.into(),
name: filename,
ty: FileType::Directory,
uid: self.uid,
gid: self.gid,
mode: mode & !self.umask,
};
parent.create(&create_info, access)
parent.create(create_info, access)
}
/// Creates an arbitrary node at given path
@ -255,7 +257,8 @@ impl IoContext {
let parent = self.find(at, parent, true)?;
let access = self.check_access(&parent, AccessMode::WRITE)?;
parent.create_node(node, name, access)?;
let filename = Filename::new(name)?;
parent.create_node(node, filename, access)?;
Ok(())
}
@ -281,7 +284,8 @@ impl IoContext {
// parent.remove_directory(name, access)
todo!()
} else {
parent.remove_file(name, access)
let filename = Filename::new(name)?;
parent.remove_file(filename, access)
}
}
@ -330,6 +334,9 @@ impl IoContext {
(node, dst_name)
};
let src_name = Filename::new(src_name)?;
let dst_name = Filename::new(dst_name)?;
let access = self.check_access(&src_node, AccessMode::READ | AccessMode::WRITE)?
+ self.check_access(&dst_node, AccessMode::WRITE)?;
@ -485,7 +492,8 @@ impl IoContext {
}
let access = self.check_access(&node, AccessMode::EXEC)?;
let child = node.lookup_or_load(name, access)?.flatten_hardlink()?;
let filename = Filename::new(name)?;
let child = node.lookup_or_load(filename, access)?.flatten_hardlink()?;
let follow_links = follow_link || !tail.is_empty();
@ -538,7 +546,8 @@ impl IoContext {
}
let access = self.check_access(&at, AccessMode::EXEC)?;
let node = at.lookup_or_load(element, access)?.flatten_hardlink()?;
let filename = Filename::new(element)?;
let node = at.lookup_or_load(filename, access)?.flatten_hardlink()?;
let node = self._resolve(node, follow_links)?;
if rest.is_empty() {

@ -31,6 +31,7 @@ pub use node::{
impls, AccessToken, CommonImpl, CreateInfo, DirectoryImpl, Metadata, Node, NodeFlags, NodeRef,
RegularImpl, SymlinkImpl,
};
pub use path::{Filename, OwnedFilename};
pub use poll::FdPoll;
pub use pty::{PseudoTerminalMaster, PseudoTerminalSlave};
pub use shared_memory::SharedMemory;

@ -1,5 +1,5 @@
//! Various helper node implementations for convenience
use core::{marker::PhantomData, str::FromStr};
use core::{fmt, marker::PhantomData, str::FromStr};
use alloc::{
string::{String, ToString},
@ -13,7 +13,7 @@ use yggdrasil_abi::{
io::{FileMode, FileType, OpenOptions},
};
use crate::vfs::{DirectoryOpenPosition, InstanceData};
use crate::vfs::{path::OwnedFilename, DirectoryOpenPosition, InstanceData};
use super::{
traits::HardlinkImpl, CommonImpl, DirectoryImpl, Metadata, Node, NodeFlags, NodeRef,
@ -464,7 +464,10 @@ pub fn read_fn_node<R: ReadFn + 'static>(read: R) -> NodeRef {
}
/// Creates an in-memory directory from the iterator
pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> NodeRef {
pub fn mdir<S: TryInto<OwnedFilename>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> NodeRef
where
S::Error: fmt::Debug,
{
let dir = Node::directory(
MemoryDirectory,
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
@ -472,7 +475,7 @@ pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> Nod
None,
);
for (name, node) in it {
dir.add_child(name, node).unwrap();
dir.add_child(name.try_into().unwrap(), node).unwrap();
}
dir
}

@ -37,6 +37,7 @@ use crate::{
};
use super::{
path::OwnedFilename,
pty::{PseudoTerminalMaster, PseudoTerminalSlave},
Filesystem,
};
@ -60,7 +61,7 @@ bitflags! {
#[derive(Debug, Clone)]
pub struct CreateInfo {
/// New entry name
pub name: String,
pub name: OwnedFilename,
/// User ID of the entry
pub uid: UserId,
/// Group ID of the entry
@ -74,7 +75,7 @@ pub struct CreateInfo {
pub(crate) struct DirectoryData {
pub(crate) imp: Box<dyn DirectoryImpl>,
pub(crate) mountpoint: IrqSafeSpinlock<Option<NodeRef>>,
pub(crate) children: IrqSafeSpinlock<Vec<(String, NodeRef)>>,
pub(crate) children: IrqSafeSpinlock<Vec<(OwnedFilename, NodeRef)>>,
}
pub(crate) struct SymlinkData {

@ -6,7 +6,11 @@ use yggdrasil_abi::{
io::{DeviceRequest, DirectoryEntry, FileMode, GroupId, OpenOptions, UserId},
};
use crate::vfs::file::{File, FileRef};
use crate::vfs::{
file::{File, FileRef},
path::OwnedFilename,
Filename,
};
use super::{AccessToken, CreateInfo, DirectoryData, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
@ -75,7 +79,7 @@ impl Node {
/// filesystem.
pub fn lookup_or_load(
self: &NodeRef,
name: &str,
name: &Filename,
_check: AccessToken,
) -> Result<NodeRef, Error> {
let dir = self.as_directory()?;
@ -99,7 +103,7 @@ impl Node {
}
/// Creates an entry within a directory with given [CreateInfo].
pub fn create(self: &NodeRef, info: &CreateInfo, check: AccessToken) -> Result<NodeRef, Error> {
pub fn create(self: &NodeRef, info: CreateInfo, check: AccessToken) -> Result<NodeRef, Error> {
let directory = self.as_directory()?;
let node = directory.imp.create_node(self, info.ty)?;
@ -111,31 +115,32 @@ impl Node {
check.clone(),
)?;
self.create_node(node, &info.name, check)
self.create_node(node, info.name, check)
}
/// Attaches a pre-created node to its parent
pub fn create_node(
pub fn create_node<F: Into<OwnedFilename>>(
self: &NodeRef,
node: NodeRef,
name: &str,
name: F,
_check: AccessToken,
) -> Result<NodeRef, Error> {
let filename = name.into();
let directory = self.as_directory()?;
match directory.imp.attach_node(self, &node, name) {
match directory.imp.attach_node(self, &node, filename.as_ref()) {
Ok(_) | Err(Error::NotImplemented) => (),
Err(err) => return Err(err),
}
// Attach the created node to the directory in memory cache
self.add_child(name, node.clone())?;
self.add_child(filename, node.clone())?;
Ok(node)
}
/// Removes a regular file, device or symlink from the directory
pub fn remove_file(self: &NodeRef, name: &str, check: AccessToken) -> Result<(), Error> {
pub fn remove_file(self: &NodeRef, name: &Filename, check: AccessToken) -> Result<(), Error> {
let directory = self.as_directory()?;
let child = self.lookup_or_load(name, check)?;
@ -166,12 +171,10 @@ impl Node {
pub fn move_node(
source: &NodeRef,
destination: &NodeRef,
source_name: &str,
destination_name: &str,
source_name: &Filename,
destination_name: &Filename,
access: AccessToken,
) -> Result<(), Error> {
// TODO sanity checks: source and destination is not the same
// filenames are valid
if !Self::is_same_filesystem(source, destination) {
return Err(Error::CrossDeviceLink);
}
@ -295,7 +298,7 @@ impl Node {
}
impl DirectoryData {
fn remove_child(&self, name: &str) {
fn remove_child(&self, name: &Filename) {
let mut children = self.children.lock();
children.retain(|(t, child)| {

@ -6,7 +6,10 @@ use yggdrasil_abi::{
io::{DirectoryEntry, FileType, OpenOptions},
};
use crate::vfs::file::{DirectoryOpenPosition, InstanceData};
use crate::vfs::{
file::{DirectoryOpenPosition, InstanceData},
Filename,
};
use super::{Metadata, NodeRef};
@ -124,7 +127,7 @@ pub trait DirectoryImpl: CommonImpl {
}
/// Associates the given node with the directory, creating an entry for it inside
fn attach_node(&self, parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
fn attach_node(&self, parent: &NodeRef, child: &NodeRef, name: &Filename) -> Result<(), Error> {
let _ = parent;
let _ = child;
let _ = name;
@ -132,7 +135,7 @@ pub trait DirectoryImpl: CommonImpl {
}
/// Removes an entry of the directory with given name
fn unlink_node(&self, parent: &NodeRef, child: &NodeRef, name: &str) -> Result<(), Error> {
fn unlink_node(&self, parent: &NodeRef, child: &NodeRef, name: &Filename) -> Result<(), Error> {
let _ = parent;
let _ = child;
let _ = name;
@ -140,7 +143,7 @@ pub trait DirectoryImpl: CommonImpl {
}
/// Fetches the child of the directory with given name
fn lookup(&self, node: &NodeRef, name: &str) -> Result<NodeRef, Error> {
fn lookup(&self, node: &NodeRef, name: &Filename) -> Result<NodeRef, Error> {
let _ = node;
let _ = name;
Err(Error::NotImplemented)

@ -1,6 +1,6 @@
use yggdrasil_abi::error::Error;
use alloc::string::String;
use crate::vfs::path::OwnedFilename;
use super::{Node, NodeRef};
@ -22,7 +22,7 @@ impl Node {
}
/// Adds an entry to the directory tree cache
pub fn add_child<S: Into<String>>(
pub fn add_child<S: Into<OwnedFilename>>(
self: &NodeRef,
name: S,
child: NodeRef,

@ -0,0 +1,167 @@
use core::{borrow::Borrow, fmt, mem, ops::Deref};
use alloc::{boxed::Box, string::String};
use yggdrasil_abi::error::Error;
#[derive(PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Filename(str);
#[derive(Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct OwnedFilename(Box<str>);
impl Filename {
pub const DOT: &'static Filename = unsafe { Self::from_str_unchecked(".") };
pub const DOT_DOT: &'static Filename = unsafe { Self::from_str_unchecked("..") };
pub fn is_dot_or_dotdot(&self) -> bool {
matches!(&self.0, "." | "..")
}
pub fn new(name: &str) -> Result<&Self, Error> {
if !name.is_empty() && !name.contains('/') {
Ok(unsafe { Self::from_str_unchecked(name) })
} else {
Err(Error::InvalidArgument)
}
}
/// # Safety
///
/// The function is memory safe, but is still marked unsafe because it does not check
/// the filename validity invariants.
#[inline(always)]
pub const unsafe fn from_str_unchecked(name: &str) -> &Self {
mem::transmute(name)
}
}
impl AsRef<str> for Filename {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Deref for Filename {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Display for Filename {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl OwnedFilename {
pub fn new<S: AsRef<str>>(filename: S) -> Result<Self, Error> {
Filename::new(filename.as_ref()).map(Self::from)
}
}
impl AsRef<str> for OwnedFilename {
#[inline]
fn as_ref(&self) -> &str {
&self.0
}
}
impl AsRef<Filename> for OwnedFilename {
#[inline]
fn as_ref(&self) -> &Filename {
unsafe { Filename::from_str_unchecked(&self.0) }
}
}
impl Deref for OwnedFilename {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl TryFrom<&str> for OwnedFilename {
type Error = Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl TryFrom<String> for OwnedFilename {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::new(&value)
}
}
impl From<&Filename> for OwnedFilename {
#[inline]
fn from(value: &Filename) -> Self {
Self(value.0.into())
}
}
impl Borrow<Filename> for OwnedFilename {
fn borrow(&self) -> &Filename {
unsafe { Filename::from_str_unchecked(&self.0) }
}
}
impl PartialEq<Filename> for OwnedFilename {
fn eq(&self, other: &Filename) -> bool {
*self.0 == other.0
}
}
impl fmt::Display for OwnedFilename {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&*self.0, f)
}
}
// impl<'a> TryFrom<&'a str> for Filename<'a> {
// type Error = Error;
//
// fn try_from(value: &'a str) -> Result<Self, Self::Error> {
// if !value.contains('/') && !value.is_empty() {
// Ok(Self(value))
// } else {
// Err(Error::InvalidArgument)
// }
// }
// }
//
// impl Deref for Filename<'_> {
// type Target = str;
//
// fn deref(&self) -> &Self::Target {
// self.0
// }
// }
//
// impl OwnedFilename {
// pub fn dot() -> Self {
// Self(".".into())
// }
//
// pub fn dotdot() -> Self {
// Self("..".into())
// }
// }
//
// impl Deref for OwnedFilename {
// type Target = str;
//
// fn deref(&self) -> &Self::Target {
// &*self.0
// }
// }

@ -9,7 +9,7 @@ use libk::{
device::char::CharDevice,
fs::devfs,
random,
vfs::{impls::read_fn_node, FileReadiness},
vfs::{impls::read_fn_node, FileReadiness, OwnedFilename},
};
struct Null;
@ -87,10 +87,10 @@ pub fn add_pseudo_devices() -> Result<(), Error> {
});
let root = devfs::root();
root.add_child("urandom", urandom)?;
root.add_child(OwnedFilename::new("urandom").unwrap(), urandom)?;
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))?;
devfs::add_named_char_device(Arc::new(Null), "null", FileMode::new(0o666))?;
devfs::add_named_char_device(Arc::new(Zero), "zero", FileMode::new(0o666))?;
Ok(())
}

@ -6,7 +6,7 @@ use libk::{
fs::devfs,
random,
task::{binary::LoadOptions, process::Process, runtime, thread::Thread},
vfs::{impls::fn_hardlink, IoContext, NodeRef},
vfs::{impls::fn_hardlink, IoContext, NodeRef, OwnedFilename},
};
use memfs::MemoryFilesystem;
@ -35,7 +35,7 @@ pub fn kinit() -> Result<(), Error> {
devfs::root()
.add_child(
"tty",
OwnedFilename::new("tty").unwrap(),
fn_hardlink(|| {
let thread = Thread::current();
let process = thread.process();

@ -36,7 +36,7 @@
#![no_main]
use abi::{error::Error, io::FileMode};
use alloc::{borrow::ToOwned, string::String};
use alloc::string::String;
use arch::Platform;
use git_version::git_version;
use kernel_arch::{Architecture, ArchitectureImpl};
@ -148,11 +148,9 @@ 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(),
FileMode::new(0o660),
) {
if let Err(error) =
devfs::add_named_char_device(ygg_driver_input::setup(), "kbd", FileMode::new(0o660))
{
log::error!("Couldn't add keyboard device: {error:?}");
}