mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-24 09:53:24 +00:00
Reorganize the codebase
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
888853a6de
commit
271a16d492
43
services/libs/jinux-std/src/fs/file_handle/file.rs
Normal file
43
services/libs/jinux-std/src/fs/file_handle/file.rs
Normal 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");
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
151
services/libs/jinux-std/src/fs/file_handle/inode_handle/mod.rs
Normal file
151
services/libs/jinux-std/src/fs/file_handle/inode_handle/mod.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
93
services/libs/jinux-std/src/fs/file_handle/mod.rs
Normal file
93
services/libs/jinux-std/src/fs/file_handle/mod.rs
Normal 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(())
|
||||
}
|
||||
}
|
128
services/libs/jinux-std/src/fs/file_table.rs
Normal file
128
services/libs/jinux-std/src/fs/file_table.rs
Normal 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 {}
|
423
services/libs/jinux-std/src/fs/fs_resolver.rs
Normal file
423
services/libs/jinux-std/src/fs/fs_resolver.rs
Normal 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)
|
||||
}
|
55
services/libs/jinux-std/src/fs/initramfs.rs
Normal file
55
services/libs/jinux-std/src/fs/initramfs.rs
Normal 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(())
|
||||
}
|
8
services/libs/jinux-std/src/fs/mod.rs
Normal file
8
services/libs/jinux-std/src/fs/mod.rs
Normal 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;
|
125
services/libs/jinux-std/src/fs/procfs/mod.rs
Normal file
125
services/libs/jinux-std/src/fs/procfs/mod.rs
Normal 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())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
30
services/libs/jinux-std/src/fs/procfs/pid/comm.rs
Normal file
30
services/libs/jinux-std/src/fs/procfs/pid/comm.rs
Normal 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;
|
19
services/libs/jinux-std/src/fs/procfs/pid/exe.rs
Normal file
19
services/libs/jinux-std/src/fs/procfs/pid/exe.rs
Normal 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())
|
||||
}
|
||||
}
|
88
services/libs/jinux-std/src/fs/procfs/pid/fd.rs
Normal file
88
services/libs/jinux-std/src/fs/procfs/pid/fd.rs
Normal 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)
|
||||
}
|
||||
}
|
72
services/libs/jinux-std/src/fs/procfs/pid/mod.rs
Normal file
72
services/libs/jinux-std/src/fs/procfs/pid/mod.rs
Normal 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())
|
||||
})
|
||||
}
|
||||
}
|
16
services/libs/jinux-std/src/fs/procfs/self_.rs
Normal file
16
services/libs/jinux-std/src/fs/procfs/self_.rs
Normal 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())
|
||||
}
|
||||
}
|
175
services/libs/jinux-std/src/fs/procfs/template/builder.rs
Normal file
175
services/libs/jinux-std/src/fs/procfs/template/builder.rs
Normal 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))
|
||||
}
|
||||
}
|
225
services/libs/jinux-std/src/fs/procfs/template/dir.rs
Normal file
225
services/libs/jinux-std/src/fs/procfs/template/dir.rs
Normal 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>) {}
|
||||
}
|
136
services/libs/jinux-std/src/fs/procfs/template/file.rs
Normal file
136
services/libs/jinux-std/src/fs/procfs/template/file.rs
Normal 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>>;
|
||||
}
|
42
services/libs/jinux-std/src/fs/procfs/template/mod.rs
Normal file
42
services/libs/jinux-std/src/fs/procfs/template/mod.rs
Normal 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
|
||||
}
|
||||
}
|
131
services/libs/jinux-std/src/fs/procfs/template/sym.rs
Normal file
131
services/libs/jinux-std/src/fs/procfs/template/sym.rs
Normal 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>;
|
||||
}
|
665
services/libs/jinux-std/src/fs/ramfs/fs.rs
Normal file
665
services/libs/jinux-std/src/fs/ramfs/fs.rs
Normal 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)
|
||||
}
|
||||
}
|
10
services/libs/jinux-std/src/fs/ramfs/mod.rs
Normal file
10
services/libs/jinux-std/src/fs/ramfs/mod.rs
Normal 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;
|
188
services/libs/jinux-std/src/fs/stdio.rs
Normal file
188
services/libs/jinux-std/src/fs/stdio.rs
Normal 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()),
|
||||
}
|
||||
}
|
||||
}
|
69
services/libs/jinux-std/src/fs/utils/access_mode.rs
Normal file
69
services/libs/jinux-std/src/fs/utils/access_mode.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
23
services/libs/jinux-std/src/fs/utils/creation_flags.rs
Normal file
23
services/libs/jinux-std/src/fs/utils/creation_flags.rs
Normal 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;
|
||||
}
|
||||
}
|
349
services/libs/jinux-std/src/fs/utils/dentry_cache.rs
Normal file
349
services/libs/jinux-std/src/fs/utils/dentry_cache.rs
Normal 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)
|
||||
}
|
||||
}
|
32
services/libs/jinux-std/src/fs/utils/dirent_visitor.rs
Normal file
32
services/libs/jinux-std/src/fs/utils/dirent_visitor.rs
Normal 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(())
|
||||
}
|
||||
}
|
118
services/libs/jinux-std/src/fs/utils/direntry_vec.rs
Normal file
118
services/libs/jinux-std/src/fs/utils/direntry_vec.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
33
services/libs/jinux-std/src/fs/utils/fcntl.rs
Normal file
33
services/libs/jinux-std/src/fs/utils/fcntl.rs
Normal 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
|
||||
}
|
30
services/libs/jinux-std/src/fs/utils/file_creation_mask.rs
Normal file
30
services/libs/jinux-std/src/fs/utils/file_creation_mask.rs
Normal 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)
|
||||
}
|
||||
}
|
66
services/libs/jinux-std/src/fs/utils/fs.rs
Normal file
66
services/libs/jinux-std/src/fs/utils/fs.rs
Normal 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>()
|
||||
}
|
||||
}
|
229
services/libs/jinux-std/src/fs/utils/inode.rs
Normal file
229
services/libs/jinux-std/src/fs/utils/inode.rs
Normal 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>()
|
||||
}
|
||||
}
|
11
services/libs/jinux-std/src/fs/utils/io_events.rs
Normal file
11
services/libs/jinux-std/src/fs/utils/io_events.rs
Normal 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;
|
||||
}
|
||||
}
|
38
services/libs/jinux-std/src/fs/utils/ioctl.rs
Normal file
38
services/libs/jinux-std/src/fs/utils/ioctl.rs
Normal 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
|
||||
}
|
49
services/libs/jinux-std/src/fs/utils/mod.rs
Normal file
49
services/libs/jinux-std/src/fs/utils/mod.rs
Normal 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;
|
161
services/libs/jinux-std/src/fs/utils/page_cache.rs
Normal file
161
services/libs/jinux-std/src/fs/utils/page_cache.rs
Normal 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,
|
||||
}
|
46
services/libs/jinux-std/src/fs/utils/poll.rs
Normal file
46
services/libs/jinux-std/src/fs/utils/poll.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
23
services/libs/jinux-std/src/fs/utils/status_flags.rs
Normal file
23
services/libs/jinux-std/src/fs/utils/status_flags.rs
Normal 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;
|
||||
}
|
||||
}
|
222
services/libs/jinux-std/src/fs/utils/vnode.rs
Normal file
222
services/libs/jinux-std/src/fs/utils/vnode.rs
Normal 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()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user