Reorganize the codebase

This commit is contained in:
Jianfeng Jiang
2023-04-09 23:12:42 -04:00
committed by Tate, Hongliang Tian
parent 888853a6de
commit 271a16d492
416 changed files with 67 additions and 53 deletions

View File

@ -0,0 +1,43 @@
use crate::fs::utils::{IoEvents, IoctlCmd, Metadata, SeekFrom};
use crate::prelude::*;
use crate::tty::get_n_tty;
use core::any::Any;
/// The basic operations defined on a file
pub trait File: Send + Sync + Any {
fn read(&self, buf: &mut [u8]) -> Result<usize> {
panic!("read unsupported");
}
fn write(&self, buf: &[u8]) -> Result<usize> {
panic!("write unsupported");
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
match cmd {
IoctlCmd::TCGETS => {
// FIXME: only a work around
let tty = get_n_tty();
tty.ioctl(cmd, arg)
}
_ => panic!("Ioctl unsupported"),
}
}
fn poll(&self) -> IoEvents {
IoEvents::empty()
}
fn flush(&self) -> Result<()> {
Ok(())
}
fn metadata(&self) -> Metadata {
panic!("metadata unsupported");
}
fn seek(&self, seek_from: SeekFrom) -> Result<usize> {
panic!("seek unsupported");
}
}

View File

@ -0,0 +1,72 @@
use crate::prelude::*;
use crate::rights::{Rights, TRights};
use super::*;
impl InodeHandle<Rights> {
pub fn new(
dentry: Arc<Dentry>,
access_mode: AccessMode,
status_flags: StatusFlags,
) -> Result<Self> {
let vnode = dentry.vnode();
if access_mode.is_readable() && !vnode.inode_mode().is_readable() {
return_errno_with_message!(Errno::EACCES, "File is not readable");
}
if access_mode.is_writable() && !vnode.inode_mode().is_writable() {
return_errno_with_message!(Errno::EACCES, "File is not writable");
}
if access_mode.is_writable() && vnode.inode_type() == InodeType::Dir {
return_errno_with_message!(Errno::EISDIR, "Directory cannot open to write");
}
let inner = Arc::new(InodeHandle_ {
dentry,
offset: Mutex::new(0),
access_mode,
status_flags: Mutex::new(status_flags),
});
Ok(Self(inner, Rights::from(access_mode)))
}
pub fn to_static<R1: TRights>(self) -> Result<InodeHandle<R1>> {
let rights = Rights::from_bits(R1::BITS).ok_or(Error::new(Errno::EBADF))?;
if !self.1.contains(rights) {
return_errno_with_message!(Errno::EBADF, "check rights failed");
}
Ok(InodeHandle(self.0, R1::new()))
}
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
if !self.1.contains(Rights::READ) {
return_errno_with_message!(Errno::EBADF, "File is not readable");
}
self.0.read(buf)
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
if !self.1.contains(Rights::READ) {
return_errno_with_message!(Errno::EBADF, "File is not readable");
}
self.0.read_to_end(buf)
}
pub fn write(&self, buf: &[u8]) -> Result<usize> {
if !self.1.contains(Rights::WRITE) {
return_errno_with_message!(Errno::EBADF, "File is not writable");
}
self.0.write(buf)
}
pub fn readdir(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
if !self.1.contains(Rights::READ) {
return_errno_with_message!(Errno::EBADF, "File is not readable");
}
self.0.readdir(visitor)
}
}
impl Clone for InodeHandle<Rights> {
fn clone(&self) -> Self {
Self(self.0.clone(), self.1.clone())
}
}

View File

@ -0,0 +1,151 @@
//! Opend Inode-backed File Handle
mod dyn_cap;
mod static_cap;
use crate::fs::utils::{AccessMode, Dentry, DirentVisitor, InodeType, SeekFrom, StatusFlags};
use crate::prelude::*;
use crate::rights::Rights;
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
struct InodeHandle_ {
dentry: Arc<Dentry>,
offset: Mutex<usize>,
access_mode: AccessMode,
status_flags: Mutex<StatusFlags>,
}
impl InodeHandle_ {
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
let mut offset = self.offset.lock();
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
self.dentry.vnode().read_direct_at(*offset, buf)?
} else {
self.dentry.vnode().read_at(*offset, buf)?
};
*offset += len;
Ok(len)
}
pub fn write(&self, buf: &[u8]) -> Result<usize> {
let mut offset = self.offset.lock();
if self.status_flags.lock().contains(StatusFlags::O_APPEND) {
*offset = self.dentry.vnode().len();
}
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
self.dentry.vnode().write_direct_at(*offset, buf)?
} else {
self.dentry.vnode().write_at(*offset, buf)?
};
*offset += len;
Ok(len)
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
self.dentry.vnode().read_direct_to_end(buf)?
} else {
self.dentry.vnode().read_to_end(buf)?
};
Ok(len)
}
pub fn seek(&self, pos: SeekFrom) -> Result<usize> {
let mut offset = self.offset.lock();
let new_offset: isize = match pos {
SeekFrom::Start(off /* as usize */) => {
if off > isize::max_value() as usize {
return_errno_with_message!(Errno::EINVAL, "file offset is too large");
}
off as isize
}
SeekFrom::End(off /* as isize */) => {
let file_size = self.dentry.vnode().len() as isize;
assert!(file_size >= 0);
file_size
.checked_add(off)
.ok_or_else(|| Error::with_message(Errno::EOVERFLOW, "file offset overflow"))?
}
SeekFrom::Current(off /* as isize */) => (*offset as isize)
.checked_add(off)
.ok_or_else(|| Error::with_message(Errno::EOVERFLOW, "file offset overflow"))?,
};
if new_offset < 0 {
return_errno_with_message!(Errno::EINVAL, "file offset must not be negative");
}
// Invariant: 0 <= new_offset <= isize::max_value()
let new_offset = new_offset as usize;
*offset = new_offset;
Ok(new_offset)
}
pub fn offset(&self) -> usize {
let offset = self.offset.lock();
*offset
}
pub fn len(&self) -> usize {
self.dentry.vnode().len()
}
pub fn access_mode(&self) -> AccessMode {
self.access_mode
}
pub fn status_flags(&self) -> StatusFlags {
let status_flags = self.status_flags.lock();
*status_flags
}
pub fn set_status_flags(&self, new_status_flags: StatusFlags) {
let mut status_flags = self.status_flags.lock();
// Can change only the O_APPEND, O_ASYNC, O_NOATIME, and O_NONBLOCK flags
let valid_flags_mask = StatusFlags::O_APPEND
| StatusFlags::O_ASYNC
| StatusFlags::O_NOATIME
| StatusFlags::O_NONBLOCK;
status_flags.remove(valid_flags_mask);
status_flags.insert(new_status_flags & valid_flags_mask);
}
pub fn readdir(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
let mut offset = self.offset.lock();
let read_cnt = self.dentry.vnode().readdir_at(*offset, visitor)?;
*offset += read_cnt;
Ok(read_cnt)
}
}
/// Methods for both dyn and static
impl<R> InodeHandle<R> {
pub fn seek(&self, pos: SeekFrom) -> Result<usize> {
self.0.seek(pos)
}
pub fn offset(&self) -> usize {
self.0.offset()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn access_mode(&self) -> AccessMode {
self.0.access_mode()
}
pub fn status_flags(&self) -> StatusFlags {
self.0.status_flags()
}
pub fn set_status_flags(&self, new_status_flags: StatusFlags) {
self.0.set_status_flags(new_status_flags)
}
pub fn dentry(&self) -> &Arc<Dentry> {
&self.0.dentry
}
}

View File

@ -0,0 +1,27 @@
use crate::prelude::*;
use crate::rights::*;
use jinux_rights_proc::require;
use super::*;
impl<R: TRights> InodeHandle<R> {
#[require(R > Read)]
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
self.0.read(buf)
}
#[require(R > Read)]
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
self.0.read_to_end(buf)
}
#[require(R > Write)]
pub fn write(&self, buf: &[u8]) -> Result<usize> {
self.0.write(buf)
}
#[require(R > Read)]
pub fn readdir(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
self.0.readdir(visitor)
}
}

View File

@ -0,0 +1,93 @@
//! Opend File Handle
mod file;
mod inode_handle;
use crate::fs::utils::{Metadata, SeekFrom};
use crate::prelude::*;
use crate::rights::{ReadOp, WriteOp};
use alloc::sync::Arc;
pub use self::file::File;
pub use self::inode_handle::InodeHandle;
#[derive(Clone)]
pub struct FileHandle {
inner: Inner,
}
#[derive(Clone)]
enum Inner {
File(Arc<dyn File>),
Inode(InodeHandle),
}
impl FileHandle {
pub fn new_file(file: Arc<dyn File>) -> Self {
let inner = Inner::File(file);
Self { inner }
}
pub fn new_inode_handle(inode_handle: InodeHandle) -> Self {
let inner = Inner::Inode(inode_handle);
Self { inner }
}
pub fn as_file(&self) -> Option<&Arc<dyn File>> {
match &self.inner {
Inner::File(file) => Some(file),
_ => None,
}
}
pub fn as_inode_handle(&self) -> Option<&InodeHandle> {
match &self.inner {
Inner::Inode(inode_handle) => Some(inode_handle),
_ => None,
}
}
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
match &self.inner {
Inner::File(file) => file.read(buf),
Inner::Inode(inode_handle) => {
let static_handle = inode_handle.clone().to_static::<ReadOp>()?;
static_handle.read(buf)
}
}
}
pub fn write(&self, buf: &[u8]) -> Result<usize> {
match &self.inner {
Inner::File(file) => file.write(buf),
Inner::Inode(inode_handle) => {
let static_handle = inode_handle.clone().to_static::<WriteOp>()?;
static_handle.write(buf)
}
}
}
pub fn metadata(&self) -> Metadata {
match &self.inner {
Inner::File(file) => file.metadata(),
Inner::Inode(inode_handle) => inode_handle.dentry().vnode().metadata(),
}
}
pub fn seek(&self, seek_from: SeekFrom) -> Result<usize> {
match &self.inner {
Inner::File(file) => file.seek(seek_from),
Inner::Inode(inode_handle) => inode_handle.seek(seek_from),
}
}
pub fn clean_for_close(&self) -> Result<()> {
match &self.inner {
Inner::Inode(_) => {
// Close does not guarantee that the data has been successfully saved to disk.
}
Inner::File(file) => file.flush()?,
}
Ok(())
}
}

View File

@ -0,0 +1,128 @@
use crate::events::{Events, Observer, Subject};
use crate::prelude::*;
use super::{
file_handle::FileHandle,
stdio::{Stderr, Stdin, Stdout, FD_STDERR, FD_STDIN, FD_STDOUT},
};
pub type FileDescripter = i32;
pub struct FileTable {
table: BTreeMap<FileDescripter, FileHandle>,
subject: Subject<FdEvents>,
}
impl FileTable {
pub fn new() -> Self {
Self {
table: BTreeMap::new(),
subject: Subject::new(),
}
}
pub fn new_with_stdio() -> Self {
let mut table = BTreeMap::new();
let stdin = Stdin::new_with_default_console();
let stdout = Stdout::new_with_default_console();
let stderr = Stderr::new_with_default_console();
table.insert(FD_STDIN, FileHandle::new_file(Arc::new(stdin)));
table.insert(FD_STDOUT, FileHandle::new_file(Arc::new(stdout)));
table.insert(FD_STDERR, FileHandle::new_file(Arc::new(stderr)));
Self {
table,
subject: Subject::new(),
}
}
pub fn dup(&mut self, fd: FileDescripter, new_fd: Option<FileDescripter>) -> Result<()> {
let file = self.table.get(&fd).map_or_else(
|| return_errno_with_message!(Errno::ENOENT, "No such file"),
|f| Ok(f.clone()),
)?;
let new_fd = if let Some(new_fd) = new_fd {
new_fd
} else {
self.max_fd() + 1
};
if self.table.contains_key(&new_fd) {
return_errno_with_message!(Errno::EBADF, "Fd exists");
}
self.table.insert(new_fd, file);
Ok(())
}
fn max_fd(&self) -> FileDescripter {
self.table.iter().map(|(fd, _)| fd.clone()).max().unwrap()
}
pub fn insert(&mut self, item: FileHandle) -> FileDescripter {
let fd = self.max_fd() + 1;
self.table.insert(fd, item);
fd
}
pub fn insert_at(&mut self, fd: FileDescripter, item: FileHandle) -> Option<FileHandle> {
let file = self.table.insert(fd, item);
if file.is_some() {
self.notify_close_fd_event(fd);
}
file
}
pub fn close_file(&mut self, fd: FileDescripter) -> Option<FileHandle> {
let file = self.table.remove(&fd);
if file.is_some() {
self.notify_close_fd_event(fd);
}
file
}
pub fn get_file(&self, fd: FileDescripter) -> Result<&FileHandle> {
self.table
.get(&fd)
.ok_or(Error::with_message(Errno::EBADF, "fd not exits"))
}
pub fn fds_and_files(&self) -> impl Iterator<Item = (&'_ FileDescripter, &'_ FileHandle)> {
self.table.iter()
}
pub fn register_observer(&self, observer: Weak<dyn Observer<FdEvents>>) {
self.subject.register_observer(observer);
}
pub fn unregister_observer(&self, observer: Weak<dyn Observer<FdEvents>>) {
self.subject.unregister_observer(observer);
}
fn notify_close_fd_event(&self, fd: FileDescripter) {
let events = FdEvents::Close(fd);
self.subject.notify_observers(&events);
}
}
impl Clone for FileTable {
fn clone(&self) -> Self {
Self {
table: self.table.clone(),
subject: Subject::new(),
}
}
}
impl Drop for FileTable {
fn drop(&mut self) {
let events = FdEvents::DropFileTable;
self.subject.notify_observers(&events);
}
}
#[derive(Copy, Clone)]
pub enum FdEvents {
Close(FileDescripter),
DropFileTable,
}
impl Events for FdEvents {}

View File

@ -0,0 +1,423 @@
use crate::prelude::*;
use alloc::str;
use alloc::string::String;
use super::file_handle::InodeHandle;
use super::file_table::FileDescripter;
use super::procfs::ProcFS;
use super::ramfs::RamFS;
use super::utils::{
AccessMode, CreationFlags, Dentry, FileSystem, InodeMode, InodeType, StatusFlags, Vnode,
PATH_MAX, SYMLINKS_MAX,
};
lazy_static! {
static ref RAM_FS: Arc<dyn FileSystem> = RamFS::new();
static ref ROOT_DENTRY: Arc<Dentry> = {
fn init() -> Result<Arc<Dentry>> {
let root_vnode = Vnode::new(RAM_FS.root_inode())?;
Ok(Dentry::new_root(root_vnode))
}
init().unwrap()
};
static ref PROC_FS: Arc<dyn FileSystem> = ProcFS::new();
static ref PROC_DENTRY: Arc<Dentry> = {
let vnode = Vnode::new(PROC_FS.root_inode()).unwrap();
Dentry::new_root(vnode)
};
}
pub struct FsResolver {
root: Arc<Dentry>,
cwd: Arc<Dentry>,
}
impl Clone for FsResolver {
fn clone(&self) -> Self {
Self {
root: self.root.clone(),
cwd: self.cwd.clone(),
}
}
}
impl FsResolver {
pub fn new() -> Self {
Self {
root: ROOT_DENTRY.clone(),
cwd: ROOT_DENTRY.clone(),
}
}
/// Get the root directory
pub fn root(&self) -> &Arc<Dentry> {
&self.root
}
/// Get the current working directory
pub fn cwd(&self) -> &Arc<Dentry> {
&self.cwd
}
/// Set the current working directory.
pub fn set_cwd(&mut self, dentry: Arc<Dentry>) {
self.cwd = dentry;
}
/// Open or create a file inode handler.
pub fn open(&self, path: &FsPath, flags: u32, mode: u16) -> Result<InodeHandle> {
let creation_flags = CreationFlags::from_bits_truncate(flags);
let status_flags = StatusFlags::from_bits_truncate(flags);
let access_mode = AccessMode::from_u32(flags)?;
let inode_mode = InodeMode::from_bits_truncate(mode);
let follow_tail_link = !creation_flags.contains(CreationFlags::O_NOFOLLOW);
let dentry = match self.lookup_inner(path, follow_tail_link) {
Ok(dentry) => {
let vnode = dentry.vnode();
if vnode.inode_type() == InodeType::SymLink
&& !status_flags.contains(StatusFlags::O_PATH)
{
return_errno_with_message!(Errno::ELOOP, "file is a symlink");
}
if creation_flags.contains(CreationFlags::O_CREAT)
&& creation_flags.contains(CreationFlags::O_EXCL)
{
return_errno_with_message!(Errno::EEXIST, "file exists");
}
if creation_flags.contains(CreationFlags::O_DIRECTORY)
&& vnode.inode_type() != InodeType::Dir
{
return_errno_with_message!(
Errno::ENOTDIR,
"O_DIRECTORY is specified but file is not a directory"
);
}
dentry
}
Err(e)
if e.error() == Errno::ENOENT
&& creation_flags.contains(CreationFlags::O_CREAT) =>
{
if creation_flags.contains(CreationFlags::O_DIRECTORY) {
return_errno_with_message!(Errno::ENOTDIR, "cannot create directory");
}
let (dir_dentry, file_name) =
self.lookup_dir_and_base_name_inner(path, follow_tail_link)?;
if file_name.ends_with("/") {
return_errno_with_message!(Errno::EISDIR, "path refers to a directory");
}
if !dir_dentry.vnode().inode_mode().is_writable() {
return_errno_with_message!(Errno::EPERM, "file cannot be created");
}
let new_dentry = dir_dentry.create(&file_name, InodeType::File, inode_mode)?;
new_dentry
}
Err(e) => return Err(e),
};
let inode_handle = InodeHandle::new(dentry, access_mode, status_flags)?;
Ok(inode_handle)
}
/// Lookup dentry according to FsPath, always follow symlinks
pub fn lookup(&self, path: &FsPath) -> Result<Arc<Dentry>> {
self.lookup_inner(path, true)
}
/// Lookup dentry according to FsPath, do not follow it if last component is a symlink
pub fn lookup_no_follow(&self, path: &FsPath) -> Result<Arc<Dentry>> {
self.lookup_inner(path, false)
}
fn lookup_inner(&self, path: &FsPath, follow_tail_link: bool) -> Result<Arc<Dentry>> {
let dentry = match path.inner {
FsPathInner::Absolute(path) => {
// TODO: Mount procfs at "/proc" if mount feature is ready
if path.starts_with("/proc") {
let path = path.strip_prefix("/proc").unwrap();
self.lookup_from_parent(
&PROC_DENTRY,
path.trim_start_matches('/'),
follow_tail_link,
)?
} else {
self.lookup_from_parent(
&self.root,
path.trim_start_matches('/'),
follow_tail_link,
)?
}
}
FsPathInner::CwdRelative(path) => {
self.lookup_from_parent(&self.cwd, path, follow_tail_link)?
}
FsPathInner::Cwd => self.cwd.clone(),
FsPathInner::FdRelative(fd, path) => {
let parent = self.lookup_from_fd(fd)?;
self.lookup_from_parent(&parent, path, follow_tail_link)?
}
FsPathInner::Fd(fd) => self.lookup_from_fd(fd)?,
};
Ok(dentry)
}
/// Lookup dentry from parent
///
/// The length of `path` cannot exceed PATH_MAX.
/// If `path` ends with `/`, then the returned inode must be a directory inode.
///
/// While looking up the dentry, symbolic links will be followed for
/// at most `SYMLINKS_MAX` times.
///
/// If `follow_tail_link` is true and the trailing component is a symlink,
/// it will be followed.
/// Symlinks in earlier components of the path will always be followed.
fn lookup_from_parent(
&self,
parent: &Arc<Dentry>,
relative_path: &str,
follow_tail_link: bool,
) -> Result<Arc<Dentry>> {
debug_assert!(!relative_path.starts_with("/"));
if relative_path.len() > PATH_MAX {
return_errno_with_message!(Errno::ENAMETOOLONG, "path is too long");
}
// To handle symlinks
let mut link_path = String::new();
let mut follows = 0;
// Initialize the first dentry and the relative path
let (mut dentry, mut relative_path) = (parent.clone(), relative_path);
while !relative_path.is_empty() {
let (next_name, path_remain, must_be_dir) =
if let Some((prefix, suffix)) = relative_path.split_once('/') {
let suffix = suffix.trim_start_matches('/');
(prefix, suffix, true)
} else {
(relative_path, "", false)
};
// Iterate next dentry
let next_dentry = dentry.lookup(next_name)?;
let next_type = next_dentry.vnode().inode_type();
let next_is_tail = path_remain.is_empty();
// If next inode is a symlink, follow symlinks at most `SYMLINKS_MAX` times.
if next_type == InodeType::SymLink && (follow_tail_link || !next_is_tail) {
if follows >= SYMLINKS_MAX {
return_errno_with_message!(Errno::ELOOP, "too many symlinks");
}
let link_path_remain = {
let mut tmp_link_path = next_dentry.vnode().read_link()?;
if tmp_link_path.is_empty() {
return_errno_with_message!(Errno::ENOENT, "empty symlink");
}
if !path_remain.is_empty() {
tmp_link_path += "/";
tmp_link_path += path_remain;
} else if must_be_dir {
tmp_link_path += "/";
}
tmp_link_path
};
// Change the dentry and relative path according to symlink
if link_path_remain.starts_with("/") {
dentry = self.root.clone();
}
link_path.clear();
link_path.push_str(&link_path_remain.trim_start_matches('/'));
relative_path = &link_path;
follows += 1;
} else {
// If path ends with `/`, the inode must be a directory
if must_be_dir && next_type != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "inode is not dir");
}
dentry = next_dentry;
relative_path = path_remain;
}
}
Ok(dentry)
}
/// Lookup dentry from the giving fd
pub fn lookup_from_fd(&self, fd: FileDescripter) -> Result<Arc<Dentry>> {
let current = current!();
let file_table = current.file_table().lock();
let inode_handle = file_table
.get_file(fd)?
.as_inode_handle()
.ok_or(Error::with_message(Errno::EBADE, "not inode"))?;
Ok(inode_handle.dentry().clone())
}
/// Lookup the dir dentry and base file name of the giving path.
///
/// If the last component is a symlink, do not deference it
pub fn lookup_dir_and_base_name(&self, path: &FsPath) -> Result<(Arc<Dentry>, String)> {
self.lookup_dir_and_base_name_inner(path, false)
}
fn lookup_dir_and_base_name_inner(
&self,
path: &FsPath,
follow_tail_link: bool,
) -> Result<(Arc<Dentry>, String)> {
let (mut dir_dentry, mut base_name) = match path.inner {
FsPathInner::Absolute(path) => {
let (dir, file_name) = split_path(path);
(
self.lookup_from_parent(&self.root, dir.trim_start_matches('/'), true)?,
String::from(file_name),
)
}
FsPathInner::CwdRelative(path) => {
let (dir, file_name) = split_path(path);
(
self.lookup_from_parent(&self.cwd, dir, true)?,
String::from(file_name),
)
}
FsPathInner::FdRelative(fd, path) => {
let (dir, file_name) = split_path(path);
let parent = self.lookup_from_fd(fd)?;
(
self.lookup_from_parent(&parent, dir, true)?,
String::from(file_name),
)
}
_ => return_errno!(Errno::ENOENT),
};
if !follow_tail_link {
return Ok((dir_dentry, base_name));
}
// Dereference the tail symlinks if needed
loop {
match dir_dentry.lookup(&base_name.trim_end_matches('/')) {
Ok(dentry) if dentry.vnode().inode_type() == InodeType::SymLink => {
let link = {
let mut link = dentry.vnode().read_link()?;
if link.is_empty() {
return_errno_with_message!(Errno::ENOENT, "invalid symlink");
}
if base_name.ends_with("/") && !link.ends_with("/") {
link += "/";
}
link
};
let (dir, file_name) = split_path(&link);
if dir.starts_with("/") {
dir_dentry =
self.lookup_from_parent(&self.root, dir.trim_start_matches('/'), true)?;
base_name = String::from(file_name);
} else {
dir_dentry = self.lookup_from_parent(&dir_dentry, dir, true)?;
base_name = String::from(file_name);
}
}
_ => break,
}
}
Ok((dir_dentry, base_name))
}
}
pub const AT_FDCWD: FileDescripter = -100;
pub struct FsPath<'a> {
inner: FsPathInner<'a>,
}
#[derive(Debug)]
enum FsPathInner<'a> {
// absolute path
Absolute(&'a str),
// path is relative to Cwd
CwdRelative(&'a str),
// Cwd
Cwd,
// path is relative to DirFd
FdRelative(FileDescripter, &'a str),
// Fd
Fd(FileDescripter),
}
impl<'a> FsPath<'a> {
pub fn new(dirfd: FileDescripter, path: &'a str) -> Result<Self> {
if path.len() > PATH_MAX {
return_errno_with_message!(Errno::ENAMETOOLONG, "path name too long");
}
let fs_path_inner = if path.starts_with("/") {
FsPathInner::Absolute(path)
} else if dirfd >= 0 {
if path.is_empty() {
FsPathInner::Fd(dirfd)
} else {
FsPathInner::FdRelative(dirfd, path)
}
} else if dirfd == AT_FDCWD {
if path.is_empty() {
FsPathInner::Cwd
} else {
FsPathInner::CwdRelative(path)
}
} else {
return_errno_with_message!(Errno::EINVAL, "invalid dirfd number");
};
Ok(Self {
inner: fs_path_inner,
})
}
}
impl<'a> TryFrom<&'a str> for FsPath<'a> {
type Error = crate::error::Error;
fn try_from(path: &'a str) -> Result<FsPath> {
if path.is_empty() {
return_errno_with_message!(Errno::ENOENT, "path is an empty string");
}
FsPath::new(AT_FDCWD, path)
}
}
/// Split a `path` to (`dir_path`, `file_name`).
///
/// The `dir_path` must be a directory.
///
/// The `file_name` is the last component. It can be suffixed by "/".
///
/// Example:
///
/// The path "/dir/file/" will be split to ("/dir", "file/").
fn split_path(path: &str) -> (&str, &str) {
let file_name = path
.split_inclusive('/')
.filter(|&x| x != "/")
.last()
.unwrap_or(".");
let mut split = path.trim_end_matches('/').rsplitn(2, '/');
let dir_path = if split.next().unwrap().is_empty() {
"/"
} else {
let mut dir = split.next().unwrap_or(".").trim_end_matches('/');
if dir.is_empty() {
dir = "/";
}
dir
};
(dir_path, file_name)
}

View File

@ -0,0 +1,55 @@
use crate::prelude::*;
use super::fs_resolver::{FsPath, FsResolver};
use super::utils::{InodeMode, InodeType};
use cpio_decoder::{CpioDecoder, FileType};
/// Unpack and prepare the fs from the ramdisk CPIO buffer.
pub fn init(ramdisk_buf: &[u8]) -> Result<()> {
let decoder = CpioDecoder::new(ramdisk_buf);
let fs = FsResolver::new();
for entry_result in decoder.decode_entries() {
let entry = entry_result?;
// Make sure the name is a relative path, and is not end with "/".
let entry_name = entry.name().trim_start_matches('/').trim_end_matches('/');
if entry_name.is_empty() {
return_errno_with_message!(Errno::EINVAL, "invalid entry name");
}
if entry_name == "." {
continue;
}
// Here we assume that the directory referred by "prefix" must has been created.
// The basis of this assumption is
// The mkinitramfs script uses `find` command to ensure that the entries are
// sorted that a directory always appears before its child directories and files.
let (parent, name) = if let Some((prefix, last)) = entry_name.rsplit_once('/') {
(fs.lookup(&FsPath::try_from(prefix)?)?, last)
} else {
(fs.root().clone(), entry_name)
};
let metadata = entry.metadata();
let mode = InodeMode::from_bits_truncate(metadata.permission_mode());
match metadata.file_type() {
FileType::File => {
let dentry = parent.create(name, InodeType::File, mode)?;
dentry.vnode().write_at(0, entry.data())?;
}
FileType::Dir => {
let _ = parent.create(name, InodeType::Dir, mode)?;
}
FileType::Link => {
let dentry = parent.create(name, InodeType::SymLink, mode)?;
let link_content = core::str::from_utf8(entry.data())?;
dentry.vnode().write_link(link_content)?;
}
type_ => {
warn!("unsupported file type = {:?} in initramfs", type_);
}
}
}
Ok(())
}

View File

@ -0,0 +1,8 @@
pub mod file_handle;
pub mod file_table;
pub mod fs_resolver;
pub mod initramfs;
pub mod procfs;
pub mod ramfs;
pub mod stdio;
pub mod utils;

View File

@ -0,0 +1,125 @@
use alloc::string::{String, ToString};
use core::any::Any;
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::events::Observer;
use crate::fs::utils::{DirEntryVecExt, FileSystem, FsFlags, Inode, SuperBlock, NAME_MAX};
use crate::prelude::*;
use crate::process::{process_table, process_table::PidEvent, Pid};
use self::pid::PidDirOps;
use self::self_::SelfSymOps;
use self::template::{DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps};
mod pid;
mod self_;
mod template;
/// Magic number.
const PROC_MAGIC: usize = 0x9fa0;
/// Root Inode ID.
const PROC_ROOT_INO: usize = 1;
/// Block size.
const BLOCK_SIZE: usize = 1024;
pub struct ProcFS {
sb: RwLock<SuperBlock>,
root: RwLock<Option<Arc<dyn Inode>>>,
inode_allocator: AtomicUsize,
}
impl ProcFS {
pub fn new() -> Arc<Self> {
let procfs = {
let sb = SuperBlock::new(PROC_MAGIC, BLOCK_SIZE, NAME_MAX);
Arc::new(Self {
sb: RwLock::new(sb),
root: RwLock::new(None),
inode_allocator: AtomicUsize::new(PROC_ROOT_INO),
})
};
let root = RootDirOps::new_inode(&procfs);
*procfs.root.write() = Some(root);
procfs
}
pub(in crate::fs::procfs) fn alloc_id(&self) -> usize {
let next_id = self.inode_allocator.fetch_add(1, Ordering::SeqCst);
next_id
}
}
impl FileSystem for ProcFS {
fn sync(&self) -> Result<()> {
Ok(())
}
fn root_inode(&self) -> Arc<dyn Inode> {
self.root.read().as_ref().unwrap().clone()
}
fn sb(&self) -> SuperBlock {
self.sb.read().clone()
}
fn flags(&self) -> FsFlags {
FsFlags::NO_PAGECACHE
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
/// Represents the inode at `/proc`.
struct RootDirOps;
impl RootDirOps {
pub fn new_inode(fs: &Arc<ProcFS>) -> Arc<dyn Inode> {
let root_inode = ProcDirBuilder::new(Self).fs(fs.clone()).build().unwrap();
let weak_ptr = Arc::downgrade(&root_inode);
process_table::register_observer(weak_ptr);
root_inode
}
}
impl Observer<PidEvent> for ProcDir<RootDirOps> {
fn on_events(&self, events: &PidEvent) {
let PidEvent::Exit(pid) = events;
let mut cached_children = self.cached_children().write();
cached_children.remove_entry_by_name(&pid.to_string());
}
}
impl DirOps for RootDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let child = if name == "self" {
SelfSymOps::new_inode(this_ptr.clone())
} else if let Ok(pid) = name.parse::<Pid>() {
let process_ref =
process_table::pid_to_process(pid).ok_or_else(|| Error::new(Errno::ENOENT))?;
PidDirOps::new_inode(process_ref, this_ptr.clone())
} else {
return_errno!(Errno::ENOENT);
};
Ok(child)
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<RootDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
cached_children.put_entry_if_not_found("self", || SelfSymOps::new_inode(this_ptr.clone()));
let processes = process_table::get_all_processes();
for process in processes {
let pid = process.pid().to_string();
cached_children.put_entry_if_not_found(&pid, || {
PidDirOps::new_inode(process.clone(), this_ptr.clone())
});
}
}
}

View File

@ -0,0 +1,30 @@
use super::*;
/// Represents the inode at `/proc/[pid]/comm`.
pub struct CommFileOps(Arc<Process>);
impl CommFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self(process_ref))
.parent(parent)
.build()
.unwrap()
}
}
impl FileOps for CommFileOps {
fn data(&self) -> Result<Vec<u8>> {
let mut comm_output = {
let exe_path = self.0.executable_path().read();
let last_component = exe_path.rsplit('/').next().unwrap_or(&exe_path);
let mut comm = last_component.as_bytes().to_vec();
comm.push(b'\0');
comm.truncate(TASK_COMM_LEN);
comm
};
comm_output.push(b'\n');
Ok(comm_output)
}
}
const TASK_COMM_LEN: usize = 16;

View File

@ -0,0 +1,19 @@
use super::*;
/// Represents the inode at `/proc/[pid]/exe`.
pub struct ExeSymOps(Arc<Process>);
impl ExeSymOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcSymBuilder::new(Self(process_ref))
.parent(parent)
.build()
.unwrap()
}
}
impl SymOps for ExeSymOps {
fn read_link(&self) -> Result<String> {
Ok(self.0.executable_path().read().clone())
}
}

View File

@ -0,0 +1,88 @@
use super::*;
use crate::fs::file_handle::FileHandle;
use crate::fs::file_table::FileDescripter;
/// Represents the inode at `/proc/[pid]/fd`.
pub struct FdDirOps(Arc<Process>);
impl FdDirOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let fd_inode = ProcDirBuilder::new(Self(process_ref.clone()))
.parent(parent)
.build()
.unwrap();
let file_table = process_ref.file_table().lock();
let weak_ptr = Arc::downgrade(&fd_inode);
file_table.register_observer(weak_ptr);
fd_inode
}
}
impl Observer<FdEvents> for ProcDir<FdDirOps> {
fn on_events(&self, events: &FdEvents) {
let fd_string = if let FdEvents::Close(fd) = events {
fd.to_string()
} else {
return;
};
let mut cached_children = self.cached_children().write();
cached_children.remove_entry_by_name(&fd_string);
}
}
impl DirOps for FdDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let file = {
let fd = name
.parse::<FileDescripter>()
.map_err(|_| Error::new(Errno::ENOENT))?;
let file_table = self.0.file_table().lock();
file_table
.get_file(fd)
.map_err(|_| Error::new(Errno::ENOENT))?
.clone()
};
Ok(FileSymOps::new_inode(file, this_ptr.clone()))
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<FdDirOps>>().unwrap().this()
};
let file_table = self.0.file_table().lock();
let mut cached_children = this.cached_children().write();
for (fd, file) in file_table.fds_and_files() {
cached_children.put_entry_if_not_found(&fd.to_string(), || {
FileSymOps::new_inode(file.clone(), this_ptr.clone())
});
}
}
}
/// Represents the inode at `/proc/[pid]/fd/N`.
struct FileSymOps(FileHandle);
impl FileSymOps {
pub fn new_inode(file: FileHandle, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcSymBuilder::new(Self(file))
.parent(parent)
.build()
.unwrap()
}
}
impl SymOps for FileSymOps {
fn read_link(&self) -> Result<String> {
let path = if let Some(inode_handle) = self.0.as_inode_handle() {
inode_handle.dentry().abs_path()
} else if let Some(file) = self.0.as_file() {
// TODO: get the real path for stdio
String::from("/dev/tty")
} else {
unreachable!()
};
Ok(path)
}
}

View File

@ -0,0 +1,72 @@
use crate::events::Observer;
use crate::fs::file_table::FdEvents;
use crate::fs::utils::{DirEntryVecExt, Inode};
use crate::prelude::*;
use crate::process::Process;
use self::comm::CommFileOps;
use self::exe::ExeSymOps;
use self::fd::FdDirOps;
use super::template::{
DirOps, FileOps, ProcDir, ProcDirBuilder, ProcFileBuilder, ProcSymBuilder, SymOps,
};
mod comm;
mod exe;
mod fd;
/// Represents the inode at `/proc/[pid]`.
pub struct PidDirOps(Arc<Process>);
impl PidDirOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let pid_inode = ProcDirBuilder::new(Self(process_ref.clone()))
.parent(parent)
// The pid directories must be volatile, because it is just associated with one process.
.volatile()
.build()
.unwrap();
let file_table = process_ref.file_table().lock();
let weak_ptr = Arc::downgrade(&pid_inode);
file_table.register_observer(weak_ptr);
pid_inode
}
}
impl Observer<FdEvents> for ProcDir<PidDirOps> {
fn on_events(&self, events: &FdEvents) {
if let FdEvents::DropFileTable = events {
let mut cached_children = self.cached_children().write();
cached_children.remove_entry_by_name("fd");
}
}
}
impl DirOps for PidDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"exe" => ExeSymOps::new_inode(self.0.clone(), this_ptr.clone()),
"comm" => CommFileOps::new_inode(self.0.clone(), this_ptr.clone()),
"fd" => FdDirOps::new_inode(self.0.clone(), this_ptr.clone()),
_ => return_errno!(Errno::ENOENT),
};
Ok(inode)
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<PidDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
cached_children.put_entry_if_not_found("exe", || {
ExeSymOps::new_inode(self.0.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("comm", || {
CommFileOps::new_inode(self.0.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("fd", || {
FdDirOps::new_inode(self.0.clone(), this_ptr.clone())
})
}
}

View File

@ -0,0 +1,16 @@
use super::*;
/// Represents the inode at `/proc/self`.
pub struct SelfSymOps;
impl SelfSymOps {
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcSymBuilder::new(Self).parent(parent).build().unwrap()
}
}
impl SymOps for SelfSymOps {
fn read_link(&self) -> Result<String> {
Ok(current!().pid().to_string())
}
}

View File

@ -0,0 +1,175 @@
use crate::fs::utils::{FileSystem, Inode};
use crate::prelude::*;
use super::{
dir::{DirOps, ProcDir},
file::{FileOps, ProcFile},
sym::{ProcSym, SymOps},
};
pub struct ProcDirBuilder<O: DirOps> {
// Mandatory field
dir: O,
// Optional fields
optional_builder: Option<OptionalBuilder>,
}
impl<O: DirOps> ProcDirBuilder<O> {
pub fn new(dir: O) -> Self {
let optional_builder: OptionalBuilder = Default::default();
Self {
dir,
optional_builder: Some(optional_builder),
}
}
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
self.optional_builder(|ob| ob.parent(parent))
}
pub fn fs(self, fs: Arc<dyn FileSystem>) -> Self {
self.optional_builder(|ob| ob.fs(fs))
}
pub fn volatile(self) -> Self {
self.optional_builder(|ob| ob.volatile())
}
pub fn build(mut self) -> Result<Arc<ProcDir<O>>> {
let (fs, parent, is_volatile) = self.optional_builder.take().unwrap().build()?;
Ok(ProcDir::new(self.dir, fs, parent, is_volatile))
}
fn optional_builder<F>(mut self, f: F) -> Self
where
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
{
let optional_builder = self.optional_builder.take().unwrap();
self.optional_builder = Some(f(optional_builder));
self
}
}
pub struct ProcFileBuilder<O: FileOps> {
// Mandatory field
file: O,
// Optional fields
optional_builder: Option<OptionalBuilder>,
}
impl<O: FileOps> ProcFileBuilder<O> {
pub fn new(file: O) -> Self {
let optional_builder: OptionalBuilder = Default::default();
Self {
file,
optional_builder: Some(optional_builder),
}
}
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
self.optional_builder(|ob| ob.parent(parent))
}
pub fn volatile(self) -> Self {
self.optional_builder(|ob| ob.volatile())
}
pub fn build(mut self) -> Result<Arc<ProcFile<O>>> {
let (fs, _, is_volatile) = self.optional_builder.take().unwrap().build()?;
Ok(ProcFile::new(self.file, fs, is_volatile))
}
fn optional_builder<F>(mut self, f: F) -> Self
where
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
{
let optional_builder = self.optional_builder.take().unwrap();
self.optional_builder = Some(f(optional_builder));
self
}
}
pub struct ProcSymBuilder<O: SymOps> {
// Mandatory field
sym: O,
// Optional fields
optional_builder: Option<OptionalBuilder>,
}
impl<O: SymOps> ProcSymBuilder<O> {
pub fn new(sym: O) -> Self {
let optional_builder: OptionalBuilder = Default::default();
Self {
sym,
optional_builder: Some(optional_builder),
}
}
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
self.optional_builder(|ob| ob.parent(parent))
}
pub fn volatile(self) -> Self {
self.optional_builder(|ob| ob.volatile())
}
pub fn build(mut self) -> Result<Arc<ProcSym<O>>> {
let (fs, _, is_volatile) = self.optional_builder.take().unwrap().build()?;
Ok(ProcSym::new(self.sym, fs, is_volatile))
}
fn optional_builder<F>(mut self, f: F) -> Self
where
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
{
let optional_builder = self.optional_builder.take().unwrap();
self.optional_builder = Some(f(optional_builder));
self
}
}
#[derive(Default)]
struct OptionalBuilder {
parent: Option<Weak<dyn Inode>>,
fs: Option<Arc<dyn FileSystem>>,
is_volatile: bool,
}
impl OptionalBuilder {
pub fn parent(mut self, parent: Weak<dyn Inode>) -> Self {
self.parent = Some(parent);
self
}
pub fn fs(mut self, fs: Arc<dyn FileSystem>) -> Self {
self.fs = Some(fs);
self
}
pub fn volatile(mut self) -> Self {
self.is_volatile = true;
self
}
pub fn build(self) -> Result<(Arc<dyn FileSystem>, Option<Weak<dyn Inode>>, bool)> {
if self.parent.is_none() && self.fs.is_none() {
return_errno_with_message!(Errno::EINVAL, "must have parent or fs");
}
let fs = self
.fs
.unwrap_or_else(|| self.parent.as_ref().unwrap().upgrade().unwrap().fs());
// The volatile property is inherited from parent.
let is_volatile = {
let mut is_volatile = self.is_volatile;
if let Some(parent) = self.parent.as_ref() {
if !parent.upgrade().unwrap().is_dentry_cacheable() {
is_volatile = true;
}
}
is_volatile
};
Ok((fs, self.parent, is_volatile))
}
}

View File

@ -0,0 +1,225 @@
use alloc::string::String;
use core::any::Any;
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{
DirEntryVec, DirentVisitor, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
pub struct ProcDir<D: DirOps> {
inner: D,
this: Weak<ProcDir<D>>,
parent: Option<Weak<dyn Inode>>,
cached_children: RwLock<DirEntryVec<(String, Arc<dyn Inode>)>>,
info: ProcInodeInfo,
}
impl<D: DirOps> ProcDir<D> {
pub fn new(
dir: D,
fs: Arc<dyn FileSystem>,
parent: Option<Weak<dyn Inode>>,
is_volatile: bool,
) -> Arc<Self> {
let info = {
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_dir(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o555),
&fs.sb(),
);
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
};
Arc::new_cyclic(|weak_self| Self {
inner: dir,
this: weak_self.clone(),
parent,
cached_children: RwLock::new(DirEntryVec::new()),
info,
})
}
pub fn this(&self) -> Arc<ProcDir<D>> {
self.this.upgrade().unwrap()
}
pub fn parent(&self) -> Option<Arc<dyn Inode>> {
self.parent.as_ref().and_then(|p| p.upgrade())
}
pub fn cached_children(&self) -> &RwLock<DirEntryVec<(String, Arc<dyn Inode>)>> {
&self.cached_children
}
}
impl<D: DirOps + 'static> Inode for ProcDir<D> {
fn len(&self) -> usize {
self.info.metadata().size
}
fn resize(&self, _new_size: usize) {}
fn metadata(&self) -> Metadata {
self.info.metadata().clone()
}
fn atime(&self) -> Duration {
self.info.metadata().atime
}
fn set_atime(&self, _time: Duration) {}
fn mtime(&self) -> Duration {
self.info.metadata().mtime
}
fn set_mtime(&self, _time: Duration) {}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn mknod(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::EPERM))
}
fn readdir_at(&self, mut offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
let try_readdir = |offset: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
// Read the two special entries.
if *offset == 0 {
let this_inode = self.this();
visitor.visit(
".",
this_inode.info.metadata().ino as u64,
this_inode.info.metadata().type_,
*offset,
)?;
*offset += 1;
}
if *offset == 1 {
let parent_inode = self.parent().unwrap_or(self.this());
visitor.visit(
"..",
parent_inode.metadata().ino as u64,
parent_inode.metadata().type_,
*offset,
)?;
*offset += 1;
}
// Read the normal child entries.
self.inner.populate_children(self.this.clone());
let cached_children = self.cached_children.read();
for (idx, (name, child)) in cached_children
.idxes_and_entries()
.map(|(idx, (name, child))| (idx + 2, (name, child)))
{
if idx < *offset {
continue;
}
visitor.visit(
name.as_ref(),
child.metadata().ino as u64,
child.metadata().type_,
idx,
)?;
*offset = idx + 1;
}
Ok(())
};
let initial_offset = offset;
match try_readdir(&mut offset, visitor) {
Err(e) if initial_offset == offset => Err(e),
_ => Ok(offset - initial_offset),
}
}
fn link(&self, _old: &Arc<dyn Inode>, _name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn unlink(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn rmdir(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"." => self.this(),
".." => self.parent().unwrap_or(self.this()),
name => {
let mut cached_children = self.cached_children.write();
if let Some((_, inode)) = cached_children
.iter()
.find(|(child_name, inode)| child_name.as_str() == name)
{
return Ok(inode.clone());
}
let inode = self.inner.lookup_child(self.this.clone(), name)?;
cached_children.put((String::from(name), inode.clone()));
inode
}
};
Ok(inode)
}
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn read_link(&self) -> Result<String> {
Err(Error::new(Errno::EISDIR))
}
fn write_link(&self, _target: &str) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn ioctl(&self, _cmd: &IoctlCmd) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn sync(&self) -> Result<()> {
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.info.fs().upgrade().unwrap()
}
fn is_dentry_cacheable(&self) -> bool {
!self.info.is_volatile()
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
pub trait DirOps: Sync + Send {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOENT))
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {}
}

View File

@ -0,0 +1,136 @@
use alloc::string::String;
use core::any::Any;
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{
DirentVisitor, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
pub struct ProcFile<F: FileOps> {
inner: F,
info: ProcInodeInfo,
}
impl<F: FileOps> ProcFile<F> {
pub fn new(file: F, fs: Arc<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
let info = {
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_file(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o444),
&fs.sb(),
);
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
};
Arc::new(Self { inner: file, info })
}
}
impl<F: FileOps + 'static> Inode for ProcFile<F> {
fn len(&self) -> usize {
self.info.metadata().size
}
fn resize(&self, _new_size: usize) {}
fn metadata(&self) -> Metadata {
self.info.metadata().clone()
}
fn atime(&self) -> Duration {
self.info.metadata().atime
}
fn set_atime(&self, _time: Duration) {}
fn mtime(&self) -> Duration {
self.info.metadata().mtime
}
fn set_mtime(&self, _time: Duration) {}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
unreachable!()
}
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
unreachable!()
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let data = self.inner.data()?;
let start = data.len().min(offset);
let end = data.len().min(offset + buf.len());
let len = end - start;
buf[0..len].copy_from_slice(&data[start..end]);
Ok(len)
}
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn mknod(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn readdir_at(&self, _offset: usize, _visitor: &mut dyn DirentVisitor) -> Result<usize> {
Err(Error::new(Errno::ENOTDIR))
}
fn link(&self, _old: &Arc<dyn Inode>, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn unlink(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn rmdir(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn lookup(&self, _name: &str) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn read_link(&self) -> Result<String> {
Err(Error::new(Errno::EINVAL))
}
fn write_link(&self, _target: &str) -> Result<()> {
Err(Error::new(Errno::EINVAL))
}
fn ioctl(&self, _cmd: &IoctlCmd) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn sync(&self) -> Result<()> {
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.info.fs().upgrade().unwrap()
}
fn is_dentry_cacheable(&self) -> bool {
!self.info.is_volatile()
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
pub trait FileOps: Sync + Send {
fn data(&self) -> Result<Vec<u8>>;
}

View File

@ -0,0 +1,42 @@
use crate::fs::utils::{FileSystem, Metadata};
use crate::prelude::*;
use super::ProcFS;
pub use self::builder::{ProcDirBuilder, ProcFileBuilder, ProcSymBuilder};
pub use self::dir::{DirOps, ProcDir};
pub use self::file::FileOps;
pub use self::sym::SymOps;
mod builder;
mod dir;
mod file;
mod sym;
struct ProcInodeInfo {
metadata: Metadata,
fs: Weak<dyn FileSystem>,
is_volatile: bool,
}
impl ProcInodeInfo {
pub fn new(metadata: Metadata, fs: Weak<dyn FileSystem>, is_volatile: bool) -> Self {
Self {
metadata,
fs,
is_volatile,
}
}
pub fn fs(&self) -> &Weak<dyn FileSystem> {
&self.fs
}
pub fn metadata(&self) -> &Metadata {
&self.metadata
}
pub fn is_volatile(&self) -> bool {
self.is_volatile
}
}

View File

@ -0,0 +1,131 @@
use alloc::string::String;
use core::any::Any;
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{
DirentVisitor, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
pub struct ProcSym<S: SymOps> {
inner: S,
info: ProcInodeInfo,
}
impl<S: SymOps> ProcSym<S> {
pub fn new(sym: S, fs: Arc<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
let info = {
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_symlink(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o777),
&fs.sb(),
);
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
};
Arc::new(Self { inner: sym, info })
}
}
impl<S: SymOps + 'static> Inode for ProcSym<S> {
fn len(&self) -> usize {
self.info.metadata().size
}
fn resize(&self, _new_size: usize) {}
fn metadata(&self) -> Metadata {
self.info.metadata().clone()
}
fn atime(&self) -> Duration {
self.info.metadata().atime
}
fn set_atime(&self, _time: Duration) {}
fn mtime(&self) -> Duration {
self.info.metadata().mtime
}
fn set_mtime(&self, _time: Duration) {}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn mknod(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn readdir_at(&self, _offset: usize, _visitor: &mut dyn DirentVisitor) -> Result<usize> {
Err(Error::new(Errno::ENOTDIR))
}
fn link(&self, _old: &Arc<dyn Inode>, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn unlink(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn rmdir(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn lookup(&self, _name: &str) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn read_link(&self) -> Result<String> {
self.inner.read_link()
}
fn write_link(&self, _target: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn ioctl(&self, _cmd: &IoctlCmd) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn sync(&self) -> Result<()> {
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.info.fs().upgrade().unwrap()
}
fn is_dentry_cacheable(&self) -> bool {
!self.info.is_volatile()
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
pub trait SymOps: Sync + Send {
fn read_link(&self) -> Result<String>;
}

View File

@ -0,0 +1,665 @@
use crate::prelude::*;
use alloc::str;
use alloc::string::String;
use core::any::Any;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use spin::{RwLock, RwLockWriteGuard};
use super::*;
use crate::fs::utils::{
DirEntryVec, DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd,
Metadata, SuperBlock,
};
pub struct RamFS {
metadata: RwLock<SuperBlock>,
root: Arc<RamInode>,
inode_allocator: AtomicUsize,
}
impl RamFS {
pub fn new() -> Arc<Self> {
let sb = SuperBlock::new(RAMFS_MAGIC, BLOCK_SIZE, NAME_MAX);
let root = Arc::new(RamInode(RwLock::new(Inode_::new_dir(
ROOT_INO,
InodeMode::from_bits_truncate(0o755),
&sb,
))));
let ramfs = Arc::new(Self {
metadata: RwLock::new(sb),
root,
inode_allocator: AtomicUsize::new(ROOT_INO + 1),
});
let mut root = ramfs.root.0.write();
root.inner
.as_direntry_mut()
.unwrap()
.init(Arc::downgrade(&ramfs.root), Arc::downgrade(&ramfs.root));
root.this = Arc::downgrade(&ramfs.root);
root.fs = Arc::downgrade(&ramfs);
drop(root);
ramfs
}
fn alloc_id(&self) -> usize {
let next_id = self.inode_allocator.fetch_add(1, Ordering::SeqCst);
self.metadata.write().files += 1;
next_id
}
}
impl FileSystem for RamFS {
fn sync(&self) -> Result<()> {
// do nothing
Ok(())
}
fn root_inode(&self) -> Arc<dyn Inode> {
self.root.clone()
}
fn sb(&self) -> SuperBlock {
self.metadata.read().clone()
}
fn flags(&self) -> FsFlags {
FsFlags::DENTRY_UNEVICTABLE
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
struct RamInode(RwLock<Inode_>);
struct Inode_ {
inner: Inner,
metadata: Metadata,
this: Weak<RamInode>,
fs: Weak<RamFS>,
}
impl Inode_ {
pub fn new_dir(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
Self {
inner: Inner::Dir(DirEntry::new()),
metadata: Metadata::new_dir(ino, mode, sb),
this: Weak::default(),
fs: Weak::default(),
}
}
pub fn new_file(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
Self {
inner: Inner::File,
metadata: Metadata::new_file(ino, mode, sb),
this: Weak::default(),
fs: Weak::default(),
}
}
pub fn new_symlink(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
Self {
inner: Inner::SymLink(Str256::from("")),
metadata: Metadata::new_symlink(ino, mode, sb),
this: Weak::default(),
fs: Weak::default(),
}
}
pub fn inc_size(&mut self) {
self.metadata.size += 1;
self.metadata.blocks = (self.metadata.size + BLOCK_SIZE - 1) / BLOCK_SIZE;
}
pub fn dec_size(&mut self) {
debug_assert!(self.metadata.size > 0);
self.metadata.size -= 1;
self.metadata.blocks = (self.metadata.size + BLOCK_SIZE - 1) / BLOCK_SIZE;
}
pub fn resize(&mut self, new_size: usize) {
self.metadata.size = new_size;
self.metadata.blocks = (new_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
}
}
enum Inner {
Dir(DirEntry),
File,
SymLink(Str256),
}
impl Inner {
fn as_direntry(&self) -> Option<&DirEntry> {
match self {
Inner::Dir(dir_entry) => Some(dir_entry),
_ => None,
}
}
fn as_direntry_mut(&mut self) -> Option<&mut DirEntry> {
match self {
Inner::Dir(dir_entry) => Some(dir_entry),
_ => None,
}
}
fn as_symlink(&self) -> Option<&str> {
match self {
Inner::SymLink(link) => Some(link.as_ref()),
_ => None,
}
}
fn as_symlink_mut(&mut self) -> Option<&mut Str256> {
match self {
Inner::SymLink(link) => Some(link),
_ => None,
}
}
}
struct DirEntry {
children: DirEntryVec<(Str256, Arc<RamInode>)>,
this: Weak<RamInode>,
parent: Weak<RamInode>,
}
impl DirEntry {
fn new() -> Self {
Self {
children: DirEntryVec::new(),
this: Weak::default(),
parent: Weak::default(),
}
}
fn init(&mut self, this: Weak<RamInode>, parent: Weak<RamInode>) {
self.this = this;
self.set_parent(parent);
}
fn set_parent(&mut self, parent: Weak<RamInode>) {
self.parent = parent;
}
fn contains_entry(&self, name: &str) -> bool {
if name == "." || name == ".." {
true
} else {
self.children
.iter()
.find(|(child, _)| child == &Str256::from(name))
.is_some()
}
}
fn get_entry(&self, name: &str) -> Option<(usize, Arc<RamInode>)> {
if name == "." {
Some((0, self.this.upgrade().unwrap()))
} else if name == ".." {
Some((1, self.parent.upgrade().unwrap()))
} else {
self.children
.idxes_and_entries()
.find(|(_, (child, _))| child == &Str256::from(name))
.map(|(idx, (_, inode))| (idx + 2, inode.clone()))
}
}
fn append_entry(&mut self, name: &str, inode: Arc<RamInode>) {
self.children.put((Str256::from(name), inode))
}
fn remove_entry(&mut self, idx: usize) -> Option<(Str256, Arc<RamInode>)> {
assert!(idx >= 2);
self.children.remove(idx - 2)
}
fn substitute_entry(
&mut self,
idx: usize,
new_entry: (Str256, Arc<RamInode>),
) -> Option<(Str256, Arc<RamInode>)> {
assert!(idx >= 2);
self.children.put_at(idx - 2, new_entry)
}
fn visit_entry(&self, mut idx: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
let try_visit = |idx: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
// Read the two special entries("." and "..").
if *idx == 0 {
let this_inode = self.this.upgrade().unwrap();
visitor.visit(
".",
this_inode.metadata().ino as u64,
this_inode.metadata().type_,
*idx,
)?;
*idx += 1;
}
if *idx == 1 {
let parent_inode = self.parent.upgrade().unwrap();
visitor.visit(
"..",
parent_inode.metadata().ino as u64,
parent_inode.metadata().type_,
*idx,
)?;
*idx += 1;
}
// Read the normal child entries.
for (offset, (name, child)) in self
.children
.idxes_and_entries()
.map(|(offset, (name, child))| (offset + 2, (name, child)))
{
if offset < *idx {
continue;
}
visitor.visit(
name.as_ref(),
child.metadata().ino as u64,
child.metadata().type_,
offset,
)?;
*idx = offset + 1;
}
Ok(())
};
let initial_idx = idx;
match try_visit(&mut idx, visitor) {
Err(e) if idx == initial_idx => Err(e),
_ => Ok(idx - initial_idx),
}
}
fn is_empty_children(&self) -> bool {
self.children.is_empty()
}
}
#[repr(C)]
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct Str256([u8; 256]);
impl AsRef<str> for Str256 {
fn as_ref(&self) -> &str {
let len = self.0.iter().enumerate().find(|(_, &b)| b == 0).unwrap().0;
str::from_utf8(&self.0[0..len]).unwrap()
}
}
impl<'a> From<&'a str> for Str256 {
fn from(s: &'a str) -> Self {
let mut inner = [0u8; 256];
let len = if s.len() > NAME_MAX {
NAME_MAX
} else {
s.len()
};
inner[0..len].copy_from_slice(&s.as_bytes()[0..len]);
Str256(inner)
}
}
impl core::fmt::Debug for Str256 {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.as_ref())
}
}
impl Inode for RamInode {
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
// do nothing
Ok(())
}
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
// do nothing
Ok(())
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
return_errno_with_message!(Errno::EOPNOTSUPP, "direct read is not supported");
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
return_errno_with_message!(Errno::EOPNOTSUPP, "direct write is not supported");
}
fn len(&self) -> usize {
self.0.read().metadata.size
}
fn resize(&self, new_size: usize) {
self.0.write().resize(new_size)
}
fn atime(&self) -> Duration {
self.0.read().metadata.atime
}
fn set_atime(&self, time: Duration) {
self.0.write().metadata.atime = time;
}
fn mtime(&self) -> Duration {
self.0.read().metadata.mtime
}
fn set_mtime(&self, time: Duration) {
self.0.write().metadata.mtime = time;
}
fn mknod(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
let mut self_inode = self.0.write();
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exists");
}
let fs = self_inode.fs.upgrade().unwrap();
let new_inode = match type_ {
InodeType::File => {
let file_inode = Arc::new(RamInode(RwLock::new(Inode_::new_file(
fs.alloc_id(),
mode,
&fs.sb(),
))));
file_inode.0.write().fs = self_inode.fs.clone();
file_inode
}
InodeType::Dir => {
let dir_inode = Arc::new(RamInode(RwLock::new(Inode_::new_dir(
fs.alloc_id(),
mode,
&fs.sb(),
))));
dir_inode.0.write().fs = self_inode.fs.clone();
dir_inode.0.write().inner.as_direntry_mut().unwrap().init(
Arc::downgrade(&dir_inode),
self_inode.inner.as_direntry().unwrap().this.clone(),
);
self_inode.metadata.nlinks += 1;
dir_inode
}
InodeType::SymLink => {
let sym_inode = Arc::new(RamInode(RwLock::new(Inode_::new_symlink(
fs.alloc_id(),
mode,
&fs.sb(),
))));
sym_inode.0.write().fs = self_inode.fs.clone();
sym_inode
}
_ => {
panic!("unsupported inode type");
}
};
new_inode.0.write().this = Arc::downgrade(&new_inode);
self_inode
.inner
.as_direntry_mut()
.unwrap()
.append_entry(name, new_inode.clone());
self_inode.inc_size();
Ok(new_inode)
}
fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
let self_inode = self.0.read();
let cnt = self_inode
.inner
.as_direntry()
.unwrap()
.visit_entry(offset, visitor)?;
Ok(cnt)
}
fn link(&self, old: &Arc<dyn Inode>, name: &str) -> Result<()> {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
let old = old
.downcast_ref::<RamInode>()
.ok_or(Error::new(Errno::EXDEV))?;
if old.0.read().metadata.type_ == InodeType::Dir {
return_errno_with_message!(Errno::EPERM, "old is a dir");
}
let mut self_inode = self.0.write();
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exist");
}
self_inode
.inner
.as_direntry_mut()
.unwrap()
.append_entry(name, old.0.read().this.upgrade().unwrap());
self_inode.inc_size();
drop(self_inode);
old.0.write().metadata.nlinks += 1;
Ok(())
}
fn unlink(&self, name: &str) -> Result<()> {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if name == "." || name == ".." {
return_errno_with_message!(Errno::EISDIR, "unlink . or ..");
}
let mut self_inode = self.0.write();
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (idx, target) = self_dir.get_entry(name).ok_or(Error::new(Errno::ENOENT))?;
if target.0.read().metadata.type_ == InodeType::Dir {
return_errno_with_message!(Errno::EISDIR, "unlink on dir");
}
self_dir.remove_entry(idx);
self_inode.dec_size();
drop(self_inode);
target.0.write().metadata.nlinks -= 1;
Ok(())
}
fn rmdir(&self, name: &str) -> Result<()> {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if name == "." || name == ".." {
return_errno_with_message!(Errno::EISDIR, "rmdir on . or ..");
}
let mut self_inode = self.0.write();
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (idx, target) = self_dir.get_entry(name).ok_or(Error::new(Errno::ENOENT))?;
if target.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "rmdir on not dir");
}
if !target
.0
.read()
.inner
.as_direntry()
.unwrap()
.is_empty_children()
{
return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty");
}
self_dir.remove_entry(idx);
self_inode.dec_size();
self_inode.metadata.nlinks -= 1;
drop(self_inode);
target.0.write().metadata.nlinks -= 2;
Ok(())
}
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
let (_, inode) = self
.0
.read()
.inner
.as_direntry()
.unwrap()
.get_entry(name)
.ok_or(Error::new(Errno::ENOENT))?;
Ok(inode as _)
}
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()> {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
let target = target
.downcast_ref::<RamInode>()
.ok_or(Error::new(Errno::EXDEV))?;
if target.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "target is not dir");
}
if old_name == "." || old_name == ".." {
return_errno_with_message!(Errno::EISDIR, "old_name is . or ..");
}
if new_name == "." || new_name == ".." {
return_errno_with_message!(Errno::EISDIR, "new_name is . or ..");
}
let src_inode = self.lookup(old_name)?;
if src_inode.metadata().ino == target.metadata().ino {
return_errno_with_message!(Errno::EINVAL, "target is a descendant of old");
}
if let Ok(dst_inode) = target.lookup(new_name) {
if src_inode.metadata().ino == dst_inode.metadata().ino {
return Ok(());
}
match (src_inode.metadata().type_, dst_inode.metadata().type_) {
(InodeType::Dir, InodeType::Dir) => {
let dst_inode = dst_inode.downcast_ref::<RamInode>().unwrap();
if !dst_inode
.0
.read()
.inner
.as_direntry()
.unwrap()
.is_empty_children()
{
return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty");
}
}
(InodeType::Dir, _) => {
return_errno_with_message!(Errno::ENOTDIR, "old is not dir");
}
(_, InodeType::Dir) => {
return_errno_with_message!(Errno::EISDIR, "new is dir");
}
_ => {}
}
}
if self.metadata().ino == target.metadata().ino {
let mut self_inode = self.0.write();
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (idx, inode) = self_dir
.get_entry(old_name)
.ok_or(Error::new(Errno::ENOENT))?;
self_dir.substitute_entry(idx, (Str256::from(new_name), inode));
} else {
let (mut self_inode, mut target_inode) = write_lock_two_inodes(self, target);
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (idx, src_inode) = self_dir
.get_entry(old_name)
.ok_or(Error::new(Errno::ENOENT))?;
self_dir.remove_entry(idx);
target_inode
.inner
.as_direntry_mut()
.unwrap()
.append_entry(new_name, src_inode.clone());
self_inode.dec_size();
target_inode.inc_size();
if src_inode.0.read().metadata.type_ == InodeType::Dir {
self_inode.metadata.nlinks -= 1;
target_inode.metadata.nlinks += 1;
}
drop(self_inode);
drop(target_inode);
if src_inode.0.read().metadata.type_ == InodeType::Dir {
src_inode
.0
.write()
.inner
.as_direntry_mut()
.unwrap()
.set_parent(target.0.read().this.clone());
}
}
Ok(())
}
fn read_link(&self) -> Result<String> {
if self.0.read().metadata.type_ != InodeType::SymLink {
return_errno_with_message!(Errno::EINVAL, "self is not symlink");
}
let self_inode = self.0.read();
let link = self_inode.inner.as_symlink().unwrap();
Ok(String::from(link))
}
fn write_link(&self, target: &str) -> Result<()> {
if self.0.read().metadata.type_ != InodeType::SymLink {
return_errno_with_message!(Errno::EINVAL, "self is not symlink");
}
let mut self_inode = self.0.write();
let link = self_inode.inner.as_symlink_mut().unwrap();
*link = Str256::from(target);
// Symlink's metadata.blocks should be 0, so just set the size.
self_inode.metadata.size = target.len();
Ok(())
}
fn metadata(&self) -> Metadata {
self.0.read().metadata.clone()
}
fn sync(&self) -> Result<()> {
// do nothing
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
Weak::upgrade(&self.0.read().fs).unwrap()
}
fn ioctl(&self, cmd: &IoctlCmd) -> Result<()> {
return_errno!(Errno::ENOSYS);
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
fn write_lock_two_inodes<'a>(
this: &'a RamInode,
other: &'a RamInode,
) -> (RwLockWriteGuard<'a, Inode_>, RwLockWriteGuard<'a, Inode_>) {
if this.0.read().metadata.ino < other.0.read().metadata.ino {
let this = this.0.write();
let other = other.0.write();
(this, other)
} else {
let other = other.0.write();
let this = this.0.write();
(this, other)
}
}

View File

@ -0,0 +1,10 @@
//! Ramfs based on PageCache
pub use fs::RamFS;
mod fs;
const RAMFS_MAGIC: usize = 0x0102_1994;
const BLOCK_SIZE: usize = 4096;
const NAME_MAX: usize = 255;
const ROOT_INO: usize = 1;

View File

@ -0,0 +1,188 @@
use crate::prelude::*;
use crate::tty::{get_n_tty, Tty};
use super::file_handle::File;
use super::file_table::FileDescripter;
use super::utils::{InodeMode, InodeType, IoEvents, Metadata, SeekFrom};
pub const FD_STDIN: FileDescripter = 0;
pub const FD_STDOUT: FileDescripter = 1;
pub const FD_STDERR: FileDescripter = 2;
pub struct Stdin {
console: Option<Arc<Tty>>,
}
pub struct Stdout {
console: Option<Arc<Tty>>,
}
pub struct Stderr {
console: Option<Arc<Tty>>,
}
impl File for Stdin {
fn poll(&self) -> IoEvents {
if let Some(console) = self.console.as_ref() {
console.poll()
} else {
todo!()
}
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
if let Some(console) = self.console.as_ref() {
console.read(buf)
} else {
todo!()
}
}
fn ioctl(&self, cmd: super::utils::IoctlCmd, arg: usize) -> Result<i32> {
if let Some(console) = self.console.as_ref() {
console.ioctl(cmd, arg)
} else {
todo!()
}
}
fn seek(&self, seek_from: SeekFrom) -> Result<usize> {
// TODO: do real seek
Ok(0)
}
fn metadata(&self) -> Metadata {
Metadata {
dev: 0,
ino: 0,
size: 0,
blk_size: 1024,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::CharDevice,
mode: InodeMode::from_bits_truncate(0o620),
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
}
}
}
impl File for Stdout {
fn ioctl(&self, cmd: super::utils::IoctlCmd, arg: usize) -> Result<i32> {
if let Some(console) = self.console.as_ref() {
console.ioctl(cmd, arg)
} else {
todo!()
}
}
fn write(&self, buf: &[u8]) -> Result<usize> {
if let Some(console) = self.console.as_ref() {
console.write(buf)
} else {
todo!()
}
}
fn seek(&self, seek_from: SeekFrom) -> Result<usize> {
// TODO: do real seek
Ok(0)
}
fn metadata(&self) -> Metadata {
Metadata {
dev: 0,
ino: 0,
size: 0,
blk_size: 1024,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::CharDevice,
mode: InodeMode::from_bits_truncate(0o620),
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
}
}
}
impl File for Stderr {
fn ioctl(&self, cmd: super::utils::IoctlCmd, arg: usize) -> Result<i32> {
if let Some(console) = self.console.as_ref() {
console.ioctl(cmd, arg)
} else {
todo!()
}
}
fn write(&self, buf: &[u8]) -> Result<usize> {
if let Some(console) = self.console.as_ref() {
console.write(buf)
} else {
todo!()
}
}
fn seek(&self, seek_from: SeekFrom) -> Result<usize> {
// TODO: do real seek
Ok(0)
}
fn metadata(&self) -> Metadata {
Metadata {
dev: 0,
ino: 0,
size: 0,
blk_size: 1024,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::CharDevice,
mode: InodeMode::from_bits_truncate(0o620),
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
}
}
}
impl Stdin {
/// FIXME: console should be file under devfs.
/// reimplement the function when devfs is enabled.
pub fn new_with_default_console() -> Self {
let console = get_n_tty();
Self {
console: Some(console.clone()),
}
}
}
impl Stdout {
/// FIXME: console should be file under devfs.
/// reimplement the function when devfs is enabled.
pub fn new_with_default_console() -> Self {
let console = get_n_tty();
Self {
console: Some(console.clone()),
}
}
}
impl Stderr {
/// FIXME: console should be file under devfs.
/// reimplement the function when devfs is enabled.
pub fn new_with_default_console() -> Self {
let console = get_n_tty();
Self {
console: Some(console.clone()),
}
}
}

View File

@ -0,0 +1,69 @@
use crate::prelude::*;
use crate::rights::Rights;
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum AccessMode {
/// read only
O_RDONLY = 0,
/// write only
O_WRONLY = 1,
/// read write
O_RDWR = 2,
}
impl AccessMode {
pub fn is_readable(&self) -> bool {
match *self {
AccessMode::O_RDONLY | AccessMode::O_RDWR => true,
_ => false,
}
}
pub fn is_writable(&self) -> bool {
match *self {
AccessMode::O_WRONLY | AccessMode::O_RDWR => true,
_ => false,
}
}
}
impl AccessMode {
pub fn from_u32(flags: u32) -> Result<Self> {
let bits = (flags & 0b11) as u8;
if bits > Self::O_RDWR as u8 {
return_errno_with_message!(Errno::EINVAL, "invalid bits for access mode");
}
Ok(match bits {
0 => Self::O_RDONLY,
1 => Self::O_WRONLY,
2 => Self::O_RDWR,
_ => unreachable!(),
})
}
}
impl From<Rights> for AccessMode {
fn from(rights: Rights) -> AccessMode {
if rights.contains(Rights::READ) && rights.contains(Rights::WRITE) {
AccessMode::O_RDWR
} else if rights.contains(Rights::READ) {
AccessMode::O_RDONLY
} else if rights.contains(Rights::WRITE) {
AccessMode::O_WRONLY
} else {
panic!("invalid rights");
}
}
}
impl From<AccessMode> for Rights {
fn from(access_mode: AccessMode) -> Rights {
match access_mode {
AccessMode::O_RDONLY => Rights::READ,
AccessMode::O_WRONLY => Rights::WRITE,
AccessMode::O_RDWR => Rights::READ | Rights::WRITE,
}
}
}

View File

@ -0,0 +1,23 @@
use bitflags::bitflags;
bitflags! {
pub struct CreationFlags: u32 {
/// create file if it does not exist
const O_CREAT = 1 << 6;
/// error if CREATE and the file exists
const O_EXCL = 1 << 7;
/// not become the process's controlling terminal
const O_NOCTTY = 1 << 8;
/// truncate file upon open
const O_TRUNC = 1 << 9;
/// file is a directory
const O_DIRECTORY = 1 << 16;
/// pathname is not a symbolic link
const O_NOFOLLOW = 1 << 17;
/// close on exec
const O_CLOEXEC = 1 << 19;
/// create an unnamed temporary regular file
/// O_TMPFILE is (_O_TMPFILE | O_DIRECTORY)
const _O_TMPFILE = 1 << 22;
}
}

View File

@ -0,0 +1,349 @@
use crate::prelude::*;
use alloc::string::String;
use core::time::Duration;
use super::{InodeMode, InodeType, Metadata, Vnode, NAME_MAX};
lazy_static! {
static ref DCACHE: Mutex<BTreeMap<DentryKey, Arc<Dentry>>> = Mutex::new(BTreeMap::new());
}
/// The dentry cache to accelerate path lookup
pub struct Dentry {
vnode: Vnode,
name_and_parent: RwLock<(String, Option<Arc<Dentry>>)>,
this: Weak<Dentry>,
children: Mutex<Children>,
}
impl Dentry {
/// Create a new dentry cache with root inode
pub fn new_root(root_vnode: Vnode) -> Arc<Self> {
let root = Self::new("/", None, root_vnode);
DCACHE.lock().insert(root.key(), root.clone());
root
}
/// Internal constructor
fn new(name: &str, parent: Option<Arc<Dentry>>, vnode: Vnode) -> Arc<Self> {
Arc::new_cyclic(|weak_self| Self {
vnode,
name_and_parent: RwLock::new((String::from(name), parent)),
this: weak_self.clone(),
children: Mutex::new(Children::new()),
})
}
/// Get the name of Dentry.
pub fn name(&self) -> String {
self.name_and_parent.read().0.clone()
}
/// Get the parent dentry.
///
/// Returns None if it is root dentry.
pub fn parent(&self) -> Option<Arc<Dentry>> {
self.name_and_parent.read().1.clone()
}
fn set_name_and_parent(&self, name: &str, parent: Option<Arc<Dentry>>) {
let mut name_and_parent = self.name_and_parent.write();
name_and_parent.0 = String::from(name);
name_and_parent.1 = parent;
}
fn this(&self) -> Arc<Dentry> {
self.this.upgrade().unwrap()
}
fn key(&self) -> DentryKey {
let parent = self.parent().unwrap_or(self.this());
DentryKey::new(&self.name_and_parent.read().0, &parent)
}
pub fn vnode(&self) -> &Vnode {
&self.vnode
}
/// Create a dentry by making inode.
pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> {
if self.vnode.inode_type() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut children = self.children.lock();
if children.find_dentry(name).is_some() {
return_errno!(Errno::EEXIST);
}
let child = {
let vnode = self.vnode.mknod(name, type_, mode)?;
let dentry = Dentry::new(name, Some(self.this()), vnode);
children.insert_dentry(&dentry);
dentry
};
Ok(child)
}
/// Lookup a dentry.
pub fn lookup(&self, name: &str) -> Result<Arc<Self>> {
if self.vnode.inode_type() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
if name.len() > NAME_MAX {
return_errno!(Errno::ENAMETOOLONG);
}
let dentry = match name {
"." => self.this(),
".." => self.parent().unwrap_or(self.this()),
name => {
let mut children = self.children.lock();
match children.find_dentry(name) {
Some(dentry) => dentry.clone(),
None => {
let vnode = self.vnode.lookup(name)?;
let dentry = Dentry::new(name, Some(self.this()), vnode);
children.insert_dentry(&dentry);
dentry
}
}
}
};
Ok(dentry)
}
/// Link a new name for the dentry by linking inode.
pub fn link(&self, old: &Arc<Self>, name: &str) -> Result<()> {
if self.vnode.inode_type() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut children = self.children.lock();
if children.find_dentry(name).is_some() {
return_errno!(Errno::EEXIST);
}
let old_vnode = old.vnode();
self.vnode.link(old_vnode, name)?;
let dentry = Dentry::new(name, Some(self.this()), old_vnode.clone());
children.insert_dentry(&dentry);
Ok(())
}
/// Delete a dentry by unlinking inode.
pub fn unlink(&self, name: &str) -> Result<()> {
if self.vnode.inode_type() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut children = self.children.lock();
self.vnode.unlink(name)?;
children.delete_dentry(name);
Ok(())
}
/// Delete a directory dentry by rmdiring inode.
pub fn rmdir(&self, name: &str) -> Result<()> {
if self.vnode.inode_type() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut children = self.children.lock();
self.vnode.rmdir(name)?;
children.delete_dentry(name);
Ok(())
}
/// Read symbolic link.
pub fn read_link(&self) -> Result<String> {
if self.vnode.inode_type() != InodeType::SymLink {
return_errno!(Errno::EINVAL);
}
self.vnode.read_link()
}
/// Write symbolic link.
pub fn write_link(&self, target: &str) -> Result<()> {
if self.vnode.inode_type() != InodeType::SymLink {
return_errno!(Errno::EINVAL);
}
self.vnode.write_link(target)
}
/// Rename a dentry to the new dentry by renaming inode.
pub fn rename(&self, old_name: &str, new_dir: &Arc<Self>, new_name: &str) -> Result<()> {
if old_name == "." || old_name == ".." || new_name == "." || new_name == ".." {
return_errno_with_message!(Errno::EISDIR, "old_name or new_name is a directory");
}
if self.vnode.inode_type() != InodeType::Dir || new_dir.vnode.inode_type() != InodeType::Dir
{
return_errno!(Errno::ENOTDIR);
}
// Self and new_dir are same Dentry, just modify name
if Arc::ptr_eq(&self.this(), new_dir) {
if old_name == new_name {
return Ok(());
}
let mut children = self.children.lock();
self.vnode.rename(old_name, &self.vnode, new_name)?;
match children.find_dentry(old_name) {
Some(dentry) => {
children.delete_dentry(old_name);
dentry.set_name_and_parent(new_name, Some(self.this()));
children.insert_dentry(&dentry);
}
None => {
children.delete_dentry(new_name);
}
}
} else {
// Self and new_dir are different Dentry
let (mut self_children, mut new_dir_children) =
write_lock_children_on_two_dentries(&self, &new_dir);
self.vnode.rename(old_name, &new_dir.vnode, new_name)?;
match self_children.find_dentry(old_name) {
Some(dentry) => {
self_children.delete_dentry(old_name);
dentry.set_name_and_parent(new_name, Some(new_dir.this()));
new_dir_children.insert_dentry(&dentry);
}
None => {
new_dir_children.delete_dentry(new_name);
}
}
}
Ok(())
}
/// Get the inode metadata
pub fn inode_metadata(&self) -> Metadata {
self.vnode.metadata()
}
/// Get the inode type
pub fn inode_type(&self) -> InodeType {
self.vnode.inode_type()
}
/// Get the inode permission mode
pub fn inode_mode(&self) -> InodeMode {
self.vnode.inode_mode()
}
/// Get the inode length
pub fn inode_len(&self) -> usize {
self.vnode.len()
}
/// Get the access timestamp
pub fn atime(&self) -> Duration {
self.vnode.atime()
}
/// Set the access timestamp
pub fn set_atime(&self, time: Duration) {
self.vnode.set_atime(time)
}
/// Get the modified timestamp
pub fn mtime(&self) -> Duration {
self.vnode.mtime()
}
/// Set the modified timestamp
pub fn set_mtime(&self, time: Duration) {
self.vnode.set_mtime(time)
}
/// Get the absolute path.
pub fn abs_path(&self) -> String {
let mut path = self.name();
let mut dentry = self.this();
loop {
match dentry.parent() {
None => break,
Some(parent_dentry) => {
path = {
let parent_name = parent_dentry.name();
if parent_name != "/" {
parent_name + "/" + &path
} else {
parent_name + &path
}
};
dentry = parent_dentry;
}
}
}
debug_assert!(path.starts_with("/"));
path
}
}
struct Children {
inner: BTreeMap<String, Weak<Dentry>>,
}
impl Children {
pub fn new() -> Self {
Self {
inner: BTreeMap::new(),
}
}
pub fn insert_dentry(&mut self, dentry: &Arc<Dentry>) {
if dentry.vnode().is_dentry_cacheable() {
DCACHE.lock().insert(dentry.key(), dentry.clone());
}
self.inner.insert(dentry.name(), Arc::downgrade(dentry));
}
pub fn delete_dentry(&mut self, name: &str) -> Option<Arc<Dentry>> {
self.inner
.remove(name)
.and_then(|d| d.upgrade())
.and_then(|d| DCACHE.lock().remove(&d.key()))
}
pub fn find_dentry(&mut self, name: &str) -> Option<Arc<Dentry>> {
if let Some(dentry) = self.inner.get(name) {
dentry.upgrade().or_else(|| {
self.inner.remove(name);
None
})
} else {
None
}
}
}
#[derive(Clone, Hash, PartialOrd, Ord, Eq, PartialEq)]
struct DentryKey {
name: String,
parent_ptr: usize,
}
impl DentryKey {
pub fn new(name: &str, parent: &Arc<Dentry>) -> Self {
Self {
name: String::from(name),
parent_ptr: Arc::as_ptr(parent) as usize,
}
}
}
fn write_lock_children_on_two_dentries<'a>(
this: &'a Dentry,
other: &'a Dentry,
) -> (MutexGuard<'a, Children>, MutexGuard<'a, Children>) {
let this_key = this.key();
let other_key = other.key();
if this_key < other_key {
let this = this.children.lock();
let other = other.children.lock();
(this, other)
} else {
let other = other.children.lock();
let this = this.children.lock();
(this, other)
}
}

View File

@ -0,0 +1,32 @@
use super::InodeType;
use crate::prelude::*;
/// A visitor for dir entries.
pub trait DirentVisitor {
/// Visit a dir entry.
///
/// If the visitor succeeds in visiting the given inode, an `Ok(())` is returned;
/// Otherwise, an error is returned. Different implementations for `DirentVisitor`
/// may choose to report errors for different reasons. Regardless of the exact
/// errors and reasons, `readdir`-family methods shall stop feeding the visitor
/// with the next inode as long as an error is returned by the visitor.
///
/// # Example
///
/// `Vec<String>` is implemented as `DirentVisitor` so that the file names
/// under a dir can be easily collected, which is convenient for testing purposes.
///
/// ```no_run
/// let mut all_dirents = Vec::new();
/// let dir_inode = todo!("create an inode");
/// dir_inode.readdir_at(0, &mut all_dirents).unwrap();
/// ```
fn visit(&mut self, name: &str, ino: u64, type_: InodeType, offset: usize) -> Result<()>;
}
impl DirentVisitor for Vec<String> {
fn visit(&mut self, name: &str, ino: u64, type_: InodeType, offset: usize) -> Result<()> {
self.push(name.into());
Ok(())
}
}

View File

@ -0,0 +1,118 @@
use super::Inode;
use crate::prelude::*;
/// DirEntryVec is used to store the entries of a directory.
/// It can guarantee that the index of one dir entry remains unchanged during
/// adding or deleting other dir entries of it.
pub struct DirEntryVec<T> {
// The slots to store dir entries.
slots: Vec<Option<T>>,
// The number of occupied slots.
// The i-th slot is occupied if `self.slots[i].is_some()`.
num_occupied: usize,
}
impl<T> DirEntryVec<T> {
/// New an empty vec.
pub fn new() -> Self {
Self {
slots: Vec::new(),
num_occupied: 0,
}
}
/// Returns `true` if the vec contains no entries.
pub fn is_empty(&self) -> bool {
self.num_occupied == 0
}
/// Put a dir entry into the vec.
/// it may be put into an existing empty slot or the back of the vec.
pub fn put(&mut self, entry: T) {
if self.num_occupied == self.slots.len() {
self.slots.push(Some(entry));
} else {
let idx = self.slots.iter().position(|x| x.is_none()).unwrap();
self.slots[idx] = Some(entry);
}
self.num_occupied += 1;
}
/// Removes and returns the entry at position `idx`.
/// Returns `None` if `idx` is out of bounds or the entry has been removed.
pub fn remove(&mut self, idx: usize) -> Option<T> {
if idx >= self.slots.len() {
return None;
}
let mut del_entry = None;
core::mem::swap(&mut del_entry, &mut self.slots[idx]);
if del_entry.is_some() {
debug_assert!(self.num_occupied > 0);
self.num_occupied -= 1;
}
del_entry
}
/// Put and returns the entry at position `idx`.
/// Returns `None` if `idx` is out of bounds or the entry has been removed.
pub fn put_at(&mut self, idx: usize, entry: T) -> Option<T> {
if idx >= self.slots.len() {
return None;
}
let mut sub_entry = Some(entry);
core::mem::swap(&mut sub_entry, &mut self.slots[idx]);
if sub_entry.is_none() {
self.num_occupied += 1;
}
sub_entry
}
/// Creates an iterator which gives both of the index and the dir entry.
/// The index may not be continuous.
pub fn idxes_and_entries(&self) -> impl Iterator<Item = (usize, &'_ T)> {
self.slots
.iter()
.enumerate()
.filter(|(_, x)| x.is_some())
.map(|(idx, x)| (idx, x.as_ref().unwrap()))
}
/// Creates an iterator which gives the dir entry.
pub fn iter(&self) -> impl Iterator<Item = &'_ T> {
self.slots.iter().filter_map(|x| x.as_ref())
}
}
pub trait DirEntryVecExt {
/// If the entry is not found by `name`, use `f` to get the inode, then put the entry into vec.
fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc<dyn Inode>);
/// Remove and returns the entry by name.
/// Returns `None` if the entry has been removed.
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)>;
}
impl DirEntryVecExt for DirEntryVec<(String, Arc<dyn Inode>)> {
fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc<dyn Inode>) {
if self
.iter()
.find(|(child_name, _)| child_name == name)
.is_none()
{
let inode = f();
self.put((String::from(name), inode));
}
}
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)> {
let idx = self
.idxes_and_entries()
.find(|(_, (child_name, _))| child_name == name)
.map(|(idx, _)| idx);
if let Some(idx) = idx {
self.remove(idx)
} else {
None
}
}
}

View File

@ -0,0 +1,33 @@
use crate::prelude::*;
macro_rules! define_fcntl_cmd {
($($name: ident = $value: expr),*) => {
#[repr(i32)]
#[derive(Debug, Clone, Copy)]
#[allow(non_camel_case_types)]
pub enum FcntlCmd {
$($name = $value,)*
}
$(
pub const $name: i32 = $value;
)*
impl TryFrom<i32> for FcntlCmd {
type Error = Error;
fn try_from(value: i32) -> Result<Self> {
match value {
$($name => Ok(FcntlCmd::$name),)*
_ => return_errno_with_message!(Errno::EINVAL, "Unknown fcntl cmd"),
}
}
}
}
}
define_fcntl_cmd! {
F_DUPFD = 0,
F_GETFD = 1,
F_SETFD = 2,
F_DUPFD_CLOEXEC = 1030
}

View File

@ -0,0 +1,30 @@
/// A mask for the file mode of a newly-created file or directory.
///
/// This mask is always a subset of `0o777`.
pub struct FileCreationMask(u16);
impl FileCreationMask {
// Creates a new instance, the initial value is `0o777`.
pub fn new(val: u16) -> Self {
Self(0o777 & val)
}
/// Get a new value.
pub fn get(&self) -> u16 {
self.0
}
/// Set a new value.
pub fn set(&mut self, new_mask: u16) -> u16 {
let new_mask = new_mask & 0o777;
let old_mask = self.0;
self.0 = new_mask;
old_mask
}
}
impl Default for FileCreationMask {
fn default() -> Self {
Self(0o777)
}
}

View File

@ -0,0 +1,66 @@
use alloc::sync::Arc;
use bitflags::bitflags;
use core::any::Any;
use super::Inode;
use crate::prelude::*;
#[derive(Debug, Clone)]
pub struct SuperBlock {
pub magic: usize,
pub bsize: usize,
pub blocks: usize,
pub bfree: usize,
pub bavail: usize,
pub files: usize,
pub ffree: usize,
pub fsid: usize,
pub namelen: usize,
pub frsize: usize,
pub flags: usize,
}
impl SuperBlock {
pub fn new(magic: usize, block_size: usize, name_len: usize) -> Self {
Self {
magic,
bsize: block_size,
blocks: 0,
bfree: 0,
bavail: 0,
files: 0,
ffree: 0,
fsid: 0,
namelen: 255,
frsize: block_size,
flags: 0,
}
}
}
bitflags! {
pub struct FsFlags: u32 {
/// Disable page cache.
const NO_PAGECACHE = 1 << 0;
/// Dentry cannot be evicted.
const DENTRY_UNEVICTABLE = 1 << 1;
}
}
pub trait FileSystem: Any + Sync + Send {
fn sync(&self) -> Result<()>;
fn root_inode(&self) -> Arc<dyn Inode>;
fn sb(&self) -> SuperBlock;
fn flags(&self) -> FsFlags;
fn as_any_ref(&self) -> &dyn Any;
}
impl dyn FileSystem {
pub fn downcast_ref<T: FileSystem>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>()
}
}

View File

@ -0,0 +1,229 @@
use alloc::string::String;
use alloc::sync::Arc;
use bitflags::bitflags;
use core::any::Any;
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use super::{DirentVisitor, FileSystem, IoctlCmd, SuperBlock};
use crate::prelude::*;
#[repr(u32)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum InodeType {
NamedPipe = 0o010000,
CharDevice = 0o020000,
Dir = 0o040000,
BlockDevice = 0o060000,
File = 0o100000,
SymLink = 0o120000,
Socket = 0o140000,
}
bitflags! {
pub struct InodeMode: u16 {
/// set-user-ID
const S_ISUID = 0o4000;
/// set-group-ID
const S_ISGID = 0o2000;
/// sticky bit
const S_ISVTX = 0o1000;
/// read by owner
const S_IRUSR = 0o0400;
/// write by owner
const S_IWUSR = 0o0200;
/// execute/search by owner
const S_IXUSR = 0o0100;
/// read by group
const S_IRGRP = 0o0040;
/// write by group
const S_IWGRP = 0o0020;
/// execute/search by group
const S_IXGRP = 0o0010;
/// read by others
const S_IROTH = 0o0004;
/// write by others
const S_IWOTH = 0o0002;
/// execute/search by others
const S_IXOTH = 0o0001;
}
}
impl InodeMode {
pub fn is_readable(&self) -> bool {
self.contains(Self::S_IRUSR)
}
pub fn is_writable(&self) -> bool {
self.contains(Self::S_IWUSR)
}
pub fn is_executable(&self) -> bool {
self.contains(Self::S_IXUSR)
}
pub fn has_sticky_bit(&self) -> bool {
self.contains(Self::S_ISVTX)
}
pub fn has_set_uid(&self) -> bool {
self.contains(Self::S_ISUID)
}
pub fn has_set_gid(&self) -> bool {
self.contains(Self::S_ISGID)
}
}
#[derive(Debug, Clone)]
pub struct Metadata {
pub dev: usize,
pub ino: usize,
pub size: usize,
pub blk_size: usize,
pub blocks: usize,
pub atime: Duration,
pub mtime: Duration,
pub ctime: Duration,
pub type_: InodeType,
pub mode: InodeMode,
pub nlinks: usize,
pub uid: usize,
pub gid: usize,
pub rdev: usize,
}
impl Metadata {
pub fn new_dir(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
Self {
dev: 0,
ino,
size: 2,
blk_size: sb.bsize,
blocks: 1,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::Dir,
mode,
nlinks: 2,
uid: 0,
gid: 0,
rdev: 0,
}
}
pub fn new_file(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
Self {
dev: 0,
ino,
size: 0,
blk_size: sb.bsize,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::File,
mode,
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
}
}
pub fn new_symlink(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
Self {
dev: 0,
ino,
size: 0,
blk_size: sb.bsize,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
type_: InodeType::SymLink,
mode,
nlinks: 1,
uid: 0,
gid: 0,
rdev: 0,
}
}
}
pub trait Inode: Any + Sync + Send {
fn len(&self) -> usize;
fn resize(&self, new_size: usize);
fn metadata(&self) -> Metadata;
fn atime(&self) -> Duration;
fn set_atime(&self, time: Duration);
fn mtime(&self) -> Duration;
fn set_mtime(&self, time: Duration);
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()>;
fn write_page(&self, idx: usize, frame: &VmFrame) -> Result<()>;
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
fn mknod(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>>;
fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize>;
fn link(&self, old: &Arc<dyn Inode>, name: &str) -> Result<()>;
fn unlink(&self, name: &str) -> Result<()>;
fn rmdir(&self, name: &str) -> Result<()>;
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>>;
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()>;
fn read_link(&self) -> Result<String>;
fn write_link(&self, target: &str) -> Result<()>;
fn ioctl(&self, cmd: &IoctlCmd) -> Result<()>;
fn sync(&self) -> Result<()>;
fn fs(&self) -> Arc<dyn FileSystem>;
fn as_any_ref(&self) -> &dyn Any;
/// Returns whether a VFS dentry for this inode should be put into the dentry cache.
///
/// The dentry cache in the VFS layer can accelerate the lookup of inodes. So usually,
/// it is preferable to use the dentry cache. And thus, the default return value of this method
/// is `true`.
///
/// But this caching can raise consistency issues in certain use cases. Specifically, the dentry
/// cache works on the assumption that all FS operations go through the dentry layer first.
/// This is why the dentry cache can reflect the up-to-date FS state. Yet, this assumption
/// may be broken. For example, an inode in procfs (say, `/proc/1/fd/2`) can "disappear" without
/// notice from the perspective of the dentry cache. So for such inodes, they are incompatible
/// with the dentry cache. And this method returns `false`.
///
/// Note that if any ancestor directory of an inode has this method returns `false`, then
/// this inode would not be cached by the dentry cache, even when the method of this
/// inode returns `true`.
fn is_dentry_cacheable(&self) -> bool {
true
}
}
impl dyn Inode {
pub fn downcast_ref<T: Inode>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>()
}
}

View File

@ -0,0 +1,11 @@
crate::bitflags! {
pub struct IoEvents: u32 {
const POLLIN = 0x0001;
const POLLPRI = 0x0002;
const POLLOUT = 0x0004;
const POLLERR = 0x0008;
const POLLHUP = 0x0010;
const POLLNVAL = 0x0020;
const POLLRDHUP = 0x2000;
}
}

View File

@ -0,0 +1,38 @@
use crate::prelude::*;
macro_rules! define_ioctl_cmd {
($($name: ident = $value: expr),*) => {
#[repr(u32)]
#[derive(Debug, Clone, Copy)]
pub enum IoctlCmd {
$($name = $value,)*
}
$(
pub const $name: u32 = $value;
)*
impl TryFrom<u32> for IoctlCmd {
type Error = Error;
fn try_from(value: u32) -> Result<Self> {
match value {
$($name => Ok(IoctlCmd::$name),)*
_ => return_errno!(Errno::EINVAL),
}
}
}
}
}
define_ioctl_cmd! {
// Get terminal attributes
TCGETS = 0x5401,
TCSETS = 0x5402,
// Get the process group ID of the foreground process group on this terminal
TIOCGPGRP = 0x540f,
// Set the foreground process group ID of this terminal.
TIOCSPGRP = 0x5410,
// Set window size
TIOCGWINSZ = 0x5413,
TIOCSWINSZ = 0x5414
}

View File

@ -0,0 +1,49 @@
//! VFS components
pub use access_mode::AccessMode;
pub use creation_flags::CreationFlags;
pub use dentry_cache::Dentry;
pub use dirent_visitor::DirentVisitor;
pub use direntry_vec::{DirEntryVec, DirEntryVecExt};
pub use fcntl::FcntlCmd;
pub use file_creation_mask::FileCreationMask;
pub use fs::{FileSystem, FsFlags, SuperBlock};
pub use inode::{Inode, InodeMode, InodeType, Metadata};
pub use io_events::IoEvents;
pub use ioctl::IoctlCmd;
pub use page_cache::PageCache;
pub use poll::{c_nfds, c_pollfd, PollFd};
pub use status_flags::StatusFlags;
pub use vnode::Vnode;
mod access_mode;
mod creation_flags;
mod dentry_cache;
mod dirent_visitor;
mod direntry_vec;
mod fcntl;
mod file_creation_mask;
mod fs;
mod inode;
mod io_events;
mod ioctl;
mod page_cache;
mod poll;
mod status_flags;
mod vnode;
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum SeekFrom {
Start(usize),
End(isize),
Current(isize),
}
/// Maximum bytes in a path
pub const PATH_MAX: usize = 4096;
/// Maximum bytes in a file name
pub const NAME_MAX: usize = 255;
/// The upper limit for resolving symbolic links
pub const SYMLINKS_MAX: usize = 40;

View File

@ -0,0 +1,161 @@
use super::Inode;
use crate::prelude::*;
use crate::rights::Full;
use crate::vm::vmo::{Pager, Vmo, VmoFlags, VmoOptions};
use core::ops::Range;
use jinux_frame::vm::{VmAllocOptions, VmFrame, VmFrameVec};
use lru::LruCache;
pub struct PageCache {
pages: Vmo<Full>,
manager: Arc<PageCacheManager>,
}
impl PageCache {
pub fn new(inode: &Arc<dyn Inode>) -> Result<Self> {
let manager = Arc::new(PageCacheManager::new(Arc::downgrade(inode)));
let pages = VmoOptions::<Full>::new(inode.len())
.flags(VmoFlags::RESIZABLE)
.pager(manager.clone())
.alloc()?;
Ok(Self { pages, manager })
}
pub fn pages(&self) -> &Vmo<Full> {
&self.pages
}
/// Evict the data within a specified range from the page cache and persist
/// them to the disk.
pub fn evict_range(&self, range: Range<usize>) {
// TODO: Implement this method.
warn!("pagecache: evict_range is not implemented");
}
}
struct PageCacheManager {
pages: Mutex<LruCache<usize, Page>>,
backed_inode: Weak<dyn Inode>,
}
impl PageCacheManager {
pub fn new(inode: Weak<dyn Inode>) -> Self {
Self {
pages: Mutex::new(LruCache::unbounded()),
backed_inode: inode,
}
}
}
impl Pager for PageCacheManager {
fn commit_page(&self, offset: usize) -> Result<VmFrame> {
let page_idx = offset / PAGE_SIZE;
let mut pages = self.pages.lock();
let frame = if let Some(page) = pages.get(&page_idx) {
page.frame()
} else {
let page = if offset < self.backed_inode.upgrade().unwrap().metadata().size {
let mut page = Page::alloc_zero()?;
self.backed_inode
.upgrade()
.unwrap()
.read_page(page_idx, &page.frame())?;
page.set_state(PageState::UpToDate);
page
} else {
Page::alloc_zero()?
};
let frame = page.frame();
pages.put(page_idx, page);
frame
};
Ok(frame)
}
fn update_page(&self, offset: usize) -> Result<()> {
let page_idx = offset / PAGE_SIZE;
let mut pages = self.pages.lock();
if let Some(page) = pages.get_mut(&page_idx) {
page.set_state(PageState::Dirty);
} else {
error!("page {} is not in page cache", page_idx);
panic!();
}
Ok(())
}
fn decommit_page(&self, offset: usize) -> Result<()> {
let page_idx = offset / PAGE_SIZE;
let mut pages = self.pages.lock();
if let Some(page) = pages.pop(&page_idx) {
match page.state() {
PageState::Dirty => self
.backed_inode
.upgrade()
.unwrap()
.write_page(page_idx, &page.frame())?,
_ => (),
}
} else {
warn!("page {} is not in page cache, do nothing", page_idx);
}
Ok(())
}
}
struct Page {
frame: VmFrame,
state: PageState,
}
impl Page {
pub fn alloc() -> Result<Self> {
let frame = {
let vm_alloc_option = VmAllocOptions::new(1);
let mut frames = VmFrameVec::allocate(&vm_alloc_option)?;
frames.pop().unwrap()
};
Ok(Self {
frame,
state: PageState::Uninit,
})
}
pub fn alloc_zero() -> Result<Self> {
let frame = {
let vm_alloc_option = VmAllocOptions::new(1);
let mut frames = VmFrameVec::allocate(&vm_alloc_option)?;
frames.zero();
frames.pop().unwrap()
};
Ok(Self {
frame,
state: PageState::Dirty,
})
}
pub fn frame(&self) -> VmFrame {
self.frame.clone()
}
pub fn state(&self) -> &PageState {
&self.state
}
pub fn set_state(&mut self, new_state: PageState) {
self.state = new_state;
}
}
enum PageState {
/// `Uninit` indicates a new allocated page which content has not been initialized.
/// The page is available to write, not available to read.
Uninit,
/// `UpToDate` indicates a page which content is consistent with corresponding disk content.
/// The page is available to read and write.
UpToDate,
/// `Dirty` indicates a page which content has been updated and not written back to underlying disk.
/// The page is available to read and write.
Dirty,
}

View File

@ -0,0 +1,46 @@
#![allow(non_camel_case_types)]
use super::IoEvents;
use crate::fs::file_table::FileDescripter;
use crate::prelude::*;
pub type c_nfds = u64;
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/poll.h
#[derive(Debug, Clone, Copy, Pod)]
#[repr(C)]
pub struct c_pollfd {
fd: FileDescripter,
events: i16,
revents: i16,
}
#[derive(Debug, Clone, Copy)]
pub struct PollFd {
pub fd: FileDescripter,
pub events: IoEvents,
pub revents: IoEvents,
}
impl From<c_pollfd> for PollFd {
fn from(raw: c_pollfd) -> Self {
let events = IoEvents::from_bits_truncate(raw.events as _);
let revents = IoEvents::from_bits_truncate(raw.revents as _);
Self {
fd: raw.fd,
events,
revents,
}
}
}
impl From<PollFd> for c_pollfd {
fn from(raw: PollFd) -> Self {
let events = raw.events.bits() as i16;
let revents = raw.revents.bits() as i16;
Self {
fd: raw.fd,
events,
revents,
}
}
}

View File

@ -0,0 +1,23 @@
use bitflags::bitflags;
bitflags! {
pub struct StatusFlags: u32 {
/// append on each write
const O_APPEND = 1 << 10;
/// non block
const O_NONBLOCK = 1 << 11;
/// synchronized I/O, data
const O_DSYNC = 1 << 12;
/// signal-driven I/O
const O_ASYNC = 1 << 13;
/// direct I/O
const O_DIRECT = 1 << 14;
/// on x86_64, O_LARGEFILE is 0
/// not update st_atime
const O_NOATIME = 1 << 18;
/// synchronized I/O, data and metadata
const O_SYNC = 1 << 20;
/// equivalent of POSIX.1's O_EXEC
const O_PATH = 1 << 21;
}
}

View File

@ -0,0 +1,222 @@
use super::{DirentVisitor, FsFlags, Inode, InodeMode, InodeType, Metadata, PageCache};
use crate::prelude::*;
use crate::rights::Full;
use crate::vm::vmo::Vmo;
use alloc::string::String;
use core::time::Duration;
use jinux_frame::vm::VmIo;
/// VFS-level representation of an inode
#[derive(Clone)]
pub struct Vnode {
// The RwLock is to maintain the correct file length for concurrent read or write.
inner: Arc<RwLock<Inner>>,
}
struct Inner {
inode: Arc<dyn Inode>,
page_cache: Option<PageCache>,
}
impl Vnode {
pub fn page_cache(&self) -> Option<Vmo<Full>> {
self.inner
.read()
.page_cache
.as_ref()
.map(|page_chche| page_chche.pages().dup().unwrap())
}
pub fn new(inode: Arc<dyn Inode>) -> Result<Self> {
let page_cache = if inode.fs().flags().contains(FsFlags::NO_PAGECACHE) {
None
} else {
Some(PageCache::new(&inode)?)
};
Ok(Self {
inner: Arc::new(RwLock::new(Inner { inode, page_cache })),
})
}
pub fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let type_ = self.inode_type();
if type_ != InodeType::File && type_ != InodeType::Socket {
return_errno!(Errno::EINVAL);
}
let inner = self.inner.write();
match &inner.page_cache {
None => inner.inode.write_at(offset, buf),
Some(page_cache) => {
let file_len = inner.inode.len();
let should_expand_len = offset + buf.len() > file_len;
if should_expand_len {
page_cache.pages().resize(offset + buf.len())?;
}
page_cache.pages().write_bytes(offset, buf)?;
if should_expand_len {
inner.inode.resize(offset + buf.len());
}
Ok(buf.len())
}
}
}
pub fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let type_ = self.inode_type();
if type_ != InodeType::File && type_ != InodeType::Socket {
return_errno!(Errno::EINVAL);
}
let inner = self.inner.write();
if let Some(page_cache) = &inner.page_cache {
page_cache.evict_range(offset..offset + buf.len());
}
inner.inode.write_at(offset, buf)
}
pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let type_ = self.inode_type();
if type_ != InodeType::File && type_ != InodeType::Socket {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
match &inner.page_cache {
None => inner.inode.read_at(offset, buf),
Some(page_cache) => {
let (offset, read_len) = {
let file_len = inner.inode.len();
let start = file_len.min(offset);
let end = file_len.min(offset + buf.len());
(start, end - start)
};
page_cache
.pages()
.read_bytes(offset, &mut buf[..read_len])?;
Ok(read_len)
}
}
}
pub fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let type_ = self.inode_type();
if type_ != InodeType::File && type_ != InodeType::Socket {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
if let Some(page_cache) = &inner.page_cache {
page_cache.evict_range(offset..offset + buf.len());
}
inner.inode.read_at(offset, buf)
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
let type_ = self.inode_type();
if type_ != InodeType::File && type_ != InodeType::Socket {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
let file_len = inner.inode.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
}
match &inner.page_cache {
None => inner.inode.read_at(0, &mut buf[..file_len]),
Some(page_cache) => {
page_cache.pages().read_bytes(0, &mut buf[..file_len])?;
Ok(file_len)
}
}
}
pub fn read_direct_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
let type_ = self.inode_type();
if type_ != InodeType::File && type_ != InodeType::Socket {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
let file_len = inner.inode.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
}
if let Some(page_cache) = &inner.page_cache {
page_cache.evict_range(0..file_len);
}
inner.inode.read_at(0, &mut buf[..file_len])
}
pub fn mknod(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Self> {
let inode = self.inner.read().inode.mknod(name, type_, mode)?;
Self::new(inode)
}
pub fn lookup(&self, name: &str) -> Result<Self> {
let inode = self.inner.read().inode.lookup(name)?;
Self::new(inode)
}
pub fn link(&self, old: &Vnode, name: &str) -> Result<()> {
self.inner.read().inode.link(&old.inner.read().inode, name)
}
pub fn unlink(&self, name: &str) -> Result<()> {
self.inner.read().inode.unlink(name)
}
pub fn rmdir(&self, name: &str) -> Result<()> {
self.inner.read().inode.rmdir(name)
}
pub fn rename(&self, old_name: &str, target: &Vnode, new_name: &str) -> Result<()> {
self.inner
.read()
.inode
.rename(old_name, &target.inner.read().inode, new_name)
}
pub fn read_link(&self) -> Result<String> {
self.inner.read().inode.read_link()
}
pub fn write_link(&self, target: &str) -> Result<()> {
self.inner.write().inode.write_link(target)
}
pub fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
self.inner.read().inode.readdir_at(offset, visitor)
}
pub fn metadata(&self) -> Metadata {
self.inner.read().inode.metadata()
}
pub fn inode_type(&self) -> InodeType {
self.inner.read().inode.metadata().type_
}
pub fn inode_mode(&self) -> InodeMode {
self.inner.read().inode.metadata().mode
}
pub fn len(&self) -> usize {
self.inner.read().inode.len()
}
pub fn atime(&self) -> Duration {
self.inner.read().inode.atime()
}
pub fn set_atime(&self, time: Duration) {
self.inner.read().inode.set_atime(time)
}
pub fn mtime(&self) -> Duration {
self.inner.read().inode.mtime()
}
pub fn set_mtime(&self, time: Duration) {
self.inner.read().inode.set_mtime(time)
}
pub fn is_dentry_cacheable(&self) -> bool {
self.inner.read().inode.is_dentry_cacheable()
}
}