2023-12-02 20:28:43 +02:00
|
|
|
use core::mem::MaybeUninit;
|
|
|
|
|
|
|
|
use yggdrasil_abi::{
|
|
|
|
error::Error,
|
2023-12-05 10:27:56 +02:00
|
|
|
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
|
|
|
|
2023-12-05 10:27:56 +02:00
|
|
|
use super::{AccessToken, CreateInfo, Metadata, Node, NodeFlags, NodeImpl, NodeRef};
|
2023-12-02 20:28:43 +02:00
|
|
|
|
|
|
|
impl Node {
|
2023-12-05 10:27:56 +02:00
|
|
|
// 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 {
|
2024-12-10 11:52:26 +02:00
|
|
|
NodeImpl::Block(dev) => dev.device_request(req),
|
|
|
|
NodeImpl::Char(dev) => dev.device_request(req),
|
2023-12-05 10:27:56 +02:00
|
|
|
_ => 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)?;
|
2024-01-15 18:15:59 +02:00
|
|
|
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),
|
|
|
|
// TODO: maybe merge open_directory and open?
|
2024-01-15 18:15:59 +02:00
|
|
|
NodeImpl::Directory(_) => Err(Error::IsADirectory),
|
|
|
|
NodeImpl::Symlink(_) => todo!(),
|
2024-03-03 02:09:02 +02:00
|
|
|
NodeImpl::PseudoTerminalSlave(_) | NodeImpl::PseudoTerminalMaster(_) => todo!(),
|
2023-12-02 20:28:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Directory
|
|
|
|
|
2023-12-05 10:27:56 +02:00
|
|
|
/// 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))
|
|
|
|
}
|
|
|
|
|
2023-12-05 10:27:56 +02:00
|
|
|
/// 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)
|
|
|
|
}
|
|
|
|
|
2023-12-05 10:27:56 +02:00
|
|
|
/// 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
|
|
|
}
|
|
|
|
|
2023-12-05 10:27:56 +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()?;
|
2023-12-05 10:27:56 +02:00
|
|
|
let node = directory.imp.create_node(self, info.ty)?;
|
|
|
|
|
|
|
|
// Fill out the node info
|
2023-12-26 22:12:47 +02:00
|
|
|
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()?;
|
2023-12-05 10:27:56 +02:00
|
|
|
|
2023-12-26 22:12:47 +02:00
|
|
|
match directory.imp.attach_node(self, &node, name) {
|
2023-12-05 10:27:56 +02:00
|
|
|
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
|
2023-12-26 22:12:47 +02:00
|
|
|
self.add_child(name, node.clone())?;
|
|
|
|
|
2023-12-03 14:52:42 +02:00
|
|
|
Ok(node)
|
|
|
|
}
|
|
|
|
|
2023-12-05 10:27:56 +02:00
|
|
|
/// 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
|
|
|
|
|
2023-12-05 10:27:56 +02:00
|
|
|
/// 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");
|
2023-12-05 10:27:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
2023-12-05 10:27:56 +02:00
|
|
|
// 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-13 23:35:17 +02:00
|
|
|
log::warn!("Possible bug: node has IN_MEMORY_SIZE, but is not a directory");
|
2023-12-03 14:52:42 +02:00
|
|
|
Err(Error::NotImplemented)
|
|
|
|
} 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
|
|
|
}
|