244 lines
7.6 KiB
Rust
Raw Normal View History

2023-12-02 20:28:43 +02:00
use core::mem::MaybeUninit;
use yggdrasil_abi::{
error::Error,
io::{DeviceRequest, DirectoryEntry, FileMode, GroupId, OpenOptions, UserId},
2023-12-02 20:28:43 +02:00
};
2024-03-13 19:01:59 +02:00
use crate::vfs::file::{File, FileRef};
2023-12-02 20:28:43 +02:00
use super::{AccessToken, CreateInfo, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
2023-12-02 20:28:43 +02:00
impl Node {
// Devices
/// Performs a device-specific function on a the device node
pub fn device_request(self: &NodeRef, req: &mut DeviceRequest) -> Result<(), Error> {
match &self.data {
NodeImpl::Block(dev) => dev.device_request(req),
NodeImpl::Char(dev) => dev.device_request(req),
_ => Err(Error::InvalidOperation),
}
}
// Devices + files
/// Opens the node with given [OpenOptions]. Only works for regular files and devices. For
/// directories, use [Node::open_directory].
pub fn open(self: &NodeRef, opts: OpenOptions, _check: AccessToken) -> Result<FileRef, Error> {
2023-12-02 20:28:43 +02:00
match &self.data {
NodeImpl::Regular(imp) => {
let (pos, instance) = imp.open(self, opts)?;
if opts.contains(OpenOptions::TRUNCATE) {
imp.truncate(self, 0)?;
}
2023-12-02 20:28:43 +02:00
Ok(File::regular(self.clone(), pos, instance, opts))
}
NodeImpl::Block(dev) => File::block(dev.clone(), self.clone(), opts),
NodeImpl::Char(dev) => File::char(dev.clone(), self.clone(), opts),
2024-12-21 00:00:00 +02:00
NodeImpl::Hardlink(_) => unreachable!("BUG: Hard links cannot be referenced directly"),
2023-12-02 20:28:43 +02:00
// TODO: maybe merge open_directory and open?
NodeImpl::Directory(_) => Err(Error::IsADirectory),
NodeImpl::Symlink(_) => todo!(),
2024-12-21 00:04:31 +02:00
NodeImpl::PseudoTerminalSlave(pty) => {
let pty = pty.upgrade().ok_or(Error::DoesNotExist)?;
Ok(File::open_pty_slave(pty, self.clone()))
}
NodeImpl::PseudoTerminalMaster(pty) => {
let pty = pty.upgrade().ok_or(Error::DoesNotExist)?;
Ok(File::open_pty_master(pty, self.clone()))
}
2023-12-02 20:28:43 +02:00
}
}
// Directory
/// Opens the node as a directory for reading its entries
pub fn open_directory(self: &NodeRef, _check: AccessToken) -> Result<FileRef, Error> {
2023-12-02 20:28:43 +02:00
let dir = self.as_directory()?;
let pos = dir.imp.open(self)?;
Ok(File::directory(self.clone(), pos))
}
/// Reads entries from the directory
2023-12-02 20:28:43 +02:00
pub fn read_directory(
self: &NodeRef,
pos: u64,
entries: &mut [MaybeUninit<DirectoryEntry>],
) -> Result<(usize, u64), Error> {
self.as_directory()?.imp.read_entries(self, pos, entries)
}
/// Attempts to look up a child node with given name inside the directory node in the tree
/// cache. If no such node is present there, will attempt to fetch it from the underlying
/// filesystem.
pub fn lookup_or_load(
self: &NodeRef,
name: &str,
_check: AccessToken,
) -> Result<NodeRef, Error> {
2023-12-03 14:52:42 +02:00
let dir = self.as_directory()?;
2024-08-02 17:04:47 +03:00
{
let children = dir.children.lock();
if let Some((_, node)) = children.iter().find(|(name_, _)| name_ == name) {
return Ok(node.clone());
}
2023-12-03 14:52:42 +02:00
}
2024-08-02 17:04:47 +03:00
let node = match dir.imp.lookup(self, name) {
2024-07-30 17:46:50 +03:00
Err(Error::NotImplemented) => Err(Error::DoesNotExist),
res => res,
2024-08-02 17:04:47 +03:00
}?;
self.add_child(name, node.clone())?;
Ok(node)
2023-12-03 14:52:42 +02:00
}
/// Creates an entry within a directory with given [CreateInfo].
pub fn create(self: &NodeRef, info: &CreateInfo, check: AccessToken) -> Result<NodeRef, Error> {
2023-12-03 14:52:42 +02:00
let directory = self.as_directory()?;
let node = directory.imp.create_node(self, info.ty)?;
// Fill out the node info
node.set_access(
Some(info.uid),
Some(info.gid),
Some(info.mode),
check.clone(),
)?;
self.create_node(node, &info.name, check)
}
/// Attaches a pre-created node to its parent
pub fn create_node(
self: &NodeRef,
node: NodeRef,
name: &str,
_check: AccessToken,
) -> Result<NodeRef, Error> {
let directory = self.as_directory()?;
match directory.imp.attach_node(self, &node, name) {
Ok(_) | Err(Error::NotImplemented) => (),
Err(err) => return Err(err),
}
2023-12-03 14:52:42 +02:00
// Attach the created node to the directory in memory cache
self.add_child(name, node.clone())?;
2023-12-03 14:52:42 +02:00
Ok(node)
}
/// Removes a regular file, device or symlink from the directory
pub fn remove_file(self: &NodeRef, name: &str, check: AccessToken) -> Result<(), Error> {
let directory = self.as_directory()?;
let child = self.lookup_or_load(name, check)?;
if child.is_directory() {
return Err(Error::IsADirectory);
}
// Detach the node in the real filesystem
match directory.imp.unlink_node(self, name) {
Ok(_) | Err(Error::NotImplemented) => (),
Err(err) => return Err(err),
}
// Detach the node in the tree cache
{
let mut children = directory.children.lock();
children.retain(|(name_, _)| name != name_);
}
// TODO child.destroy() or something?
Ok(())
}
2023-12-02 20:28:43 +02:00
// Common
/// Changes user/group ID or access mode of the node
pub fn set_access(
self: &NodeRef,
uid: Option<UserId>,
gid: Option<GroupId>,
mode: Option<FileMode>,
_check: AccessToken,
) -> Result<(), Error> {
if uid.is_none() && gid.is_none() && mode.is_none() {
return Err(Error::InvalidOperation);
}
let mut metadata = self.metadata()?;
if let Some(uid) = uid {
metadata.uid = uid;
}
if let Some(gid) = gid {
metadata.gid = gid;
}
if let Some(mode) = mode {
metadata.mode = mode;
}
// Update cached props
self.props.lock().metadata = metadata;
if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
// Update permissions in the real node
2024-12-02 19:02:18 +02:00
// todo!();
log::error!("TODO: update real node metadata");
}
Ok(())
}
/// Returns the "metadata" of the file: uid, gid, access mode
2023-12-03 14:52:42 +02:00
pub fn metadata(self: &NodeRef) -> Result<Metadata, Error> {
if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) {
let props = self.props.lock();
return Ok(props.metadata);
}
self.data_as_common().metadata(self)
}
// TODO clarify directory size
/// Returns the size in bytes of the node
2023-12-03 14:52:42 +02:00
pub fn size(self: &NodeRef) -> Result<u64, Error> {
// Try to fetch the size from the cache
let mut props = self.props.lock();
if let Some(size) = props.size {
return Ok(size);
}
if self.flags.contains(NodeFlags::IN_MEMORY_SIZE) {
if let Ok(dir) = self.as_directory() {
return Ok(dir.children.lock().len() as _);
}
2024-12-21 00:00:00 +02:00
log::warn!(
"Possible bug: node has IN_MEMORY_SIZE, but is not a directory: {:?}",
self.ty()
);
Ok(0)
// Err(Error::NotImplemented)
2023-12-03 14:52:42 +02:00
} else {
// Fetch the size from the node
let size = self.data_as_common().size(self)?;
props.size = Some(size);
Ok(size)
}
2023-12-02 20:28:43 +02:00
}
2024-08-02 17:04:47 +03:00
pub fn read_link(self: &NodeRef, buffer: &mut [u8]) -> Result<usize, Error> {
let symlink = self.as_symlink()?;
symlink.imp.read_link(buffer)
}
2023-12-02 20:28:43 +02:00
}