Add essential vfs support

This commit is contained in:
LI Qing 2022-12-30 11:22:04 +08:00
parent f0f61638b9
commit 1a17cee704
17 changed files with 606 additions and 11 deletions

View File

@ -0,0 +1,63 @@
use crate::prelude::*;
use crate::rights::{ReadOp, WriteOp};
use alloc::sync::Arc;
use super::file::File;
use super::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)
}
}
}
}

View File

@ -1,13 +1,14 @@
use crate::prelude::*;
use super::{
file::{File, FileDescripter},
file::FileDescripter,
file_handle::FileHandle,
stdio::{Stderr, Stdin, Stdout, FD_STDERR, FD_STDIN, FD_STDOUT},
};
#[derive(Clone)]
pub struct FileTable {
table: BTreeMap<FileDescripter, Arc<dyn File>>,
table: BTreeMap<FileDescripter, FileHandle>,
}
impl FileTable {
@ -22,9 +23,9 @@ impl FileTable {
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, Arc::new(stdin) as Arc<dyn File>);
table.insert(FD_STDOUT, Arc::new(stdout) as Arc<dyn File>);
table.insert(FD_STDERR, Arc::new(stderr) as Arc<dyn File>);
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 }
}
@ -50,7 +51,7 @@ impl FileTable {
self.table.iter().map(|(fd, _)| fd.clone()).max().unwrap()
}
pub fn insert(&mut self, item: Arc<dyn File>) -> FileDescripter {
pub fn insert(&mut self, item: FileHandle) -> FileDescripter {
let fd = self.max_fd() + 1;
self.table.insert(fd, item);
fd
@ -60,7 +61,7 @@ impl FileTable {
self.table.remove(&fd);
}
pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc<dyn File>> {
pub fn get_file(&self, fd: FileDescripter) -> Result<&FileHandle> {
self.table
.get(&fd)
.ok_or(Error::with_message(Errno::EBADF, "fd not exits"))

View File

@ -0,0 +1,66 @@
use crate::prelude::*;
use crate::rights::{Rights, TRights};
use super::*;
impl InodeHandle<Rights> {
pub fn new(
inode: Arc<dyn Inode>,
access_mode: AccessMode,
status_flags: StatusFlags,
) -> Result<Self> {
let inode_info = inode.metadata();
if access_mode.is_readable() && !inode_info.mode.is_readable() {
return_errno_with_message!(Errno::EACCES, "File is not readable");
}
if access_mode.is_writable() && !inode_info.mode.is_writable() {
return_errno_with_message!(Errno::EACCES, "File is not writable");
}
if access_mode.is_writable() && inode_info.type_ == InodeType::Dir {
return_errno_with_message!(Errno::EISDIR, "Directory cannot open to write");
}
let inner = Arc::new(InodeHandle_ {
inode,
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 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, writer: &mut dyn DirentWriter) -> Result<usize> {
if !self.1.contains(Rights::READ) {
return_errno_with_message!(Errno::EBADF, "File is not readable");
}
self.0.readdir(writer)
}
}
impl Clone for InodeHandle<Rights> {
fn clone(&self) -> Self {
Self(self.0.clone(), self.1.clone())
}
}

View File

@ -0,0 +1,146 @@
//! Opend File Handle
mod dyn_cap;
mod static_cap;
use super::utils::{
AccessMode, DirentWriter, DirentWriterContext, Inode, InodeType, SeekFrom, StatusFlags,
};
use crate::prelude::*;
use crate::rights::Rights;
use alloc::sync::Arc;
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
struct InodeHandle_ {
inode: Arc<dyn Inode>,
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 file_size = self.inode.metadata().size;
let start = file_size.min(*offset);
let end = file_size.min(*offset + buf.len());
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
self.inode.read_at(start, &mut buf[0..start - end])?
} else {
self.inode.read_at(start, &mut buf[0..start - end])?
// TODO: use page cache
// self.inode.pages().read_at(start, buf[0..start - end])?
};
*offset += len;
Ok(len)
}
pub fn write(&self, buf: &[u8]) -> Result<usize> {
let mut offset = self.offset.lock();
let file_size = self.inode.metadata().size;
if self.status_flags.lock().contains(StatusFlags::O_APPEND) {
*offset = file_size;
}
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
self.inode.write_at(*offset, buf)?
} else {
self.inode.write_at(*offset, buf)?
// TODO: use page cache
// let len = self.inode.pages().write_at(*offset, buf)?;
// if offset + len > file_size {
// self.inode.resize(offset + len)?;
// }
// len
};
*offset += len;
Ok(len)
}
pub fn seek(&self, pos: SeekFrom) -> Result<usize> {
let mut offset = self.offset.lock();
let new_offset: i64 = match pos {
SeekFrom::Start(off /* as u64 */) => {
if off > i64::max_value() as u64 {
return_errno_with_message!(Errno::EINVAL, "file offset is too large");
}
off as i64
}
SeekFrom::End(off /* as i64 */) => {
let file_size = self.inode.metadata().size as i64;
assert!(file_size >= 0);
file_size
.checked_add(off)
.ok_or_else(|| Error::with_message(Errno::EOVERFLOW, "file offset overflow"))?
}
SeekFrom::Current(off /* as i64 */) => (*offset as i64)
.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 <= i64::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 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, writer: &mut dyn DirentWriter) -> Result<usize> {
let mut offset = self.offset.lock();
let mut dir_writer_ctx = DirentWriterContext::new(*offset, writer);
let written_size = self.inode.readdir(&mut dir_writer_ctx)?;
*offset = dir_writer_ctx.pos();
Ok(written_size)
}
}
/// 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 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)
}
}

View File

@ -0,0 +1,22 @@
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 > Write)]
pub fn write(&self, buf: &[u8]) -> Result<usize> {
self.0.write(buf)
}
#[require(R > Read)]
pub fn readdir(&self, writer: &mut dyn DirentWriter) -> Result<usize> {
self.0.readdir(writer)
}
}

View File

@ -1,8 +1,11 @@
pub mod events;
pub mod fcntl;
pub mod file;
pub mod file_handle;
pub mod file_table;
pub mod inode_handle;
pub mod ioctl;
pub mod poll;
pub mod stat;
pub mod stdio;
pub mod utils;

View File

@ -0,0 +1,53 @@
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 From<Rights> for AccessMode {
fn from(rights: Rights) -> AccessMode {
if rights.contains(Rights::READ) && rights.contains(Rights::WRITE) {
AccessMode::O_RDWR
} else if rights.contains(Rights::READ) {
AccessMode::O_RDONLY
} else if rights.contains(Rights::WRITE) {
AccessMode::O_WRONLY
} else {
panic!("invalid rights");
}
}
}
impl From<AccessMode> for Rights {
fn from(access_mode: AccessMode) -> Rights {
match access_mode {
AccessMode::O_RDONLY => Rights::READ,
AccessMode::O_WRONLY => Rights::WRITE,
AccessMode::O_RDWR => Rights::READ | Rights::WRITE,
}
}
}

View File

@ -0,0 +1,31 @@
use super::InodeType;
use crate::prelude::*;
/// DirentWriterContext is a wrapper of DirentWriter with directory position
/// After a successful write, the position increases correspondingly
pub struct DirentWriterContext<'a> {
pos: usize,
writer: &'a mut dyn DirentWriter,
}
impl<'a> DirentWriterContext<'a> {
pub fn new(pos: usize, writer: &'a mut dyn DirentWriter) -> Self {
Self { pos, writer }
}
pub fn write_entry(&mut self, name: &str, ino: u64, type_: InodeType) -> Result<usize> {
let written_len = self.writer.write_entry(name, ino, type_)?;
self.pos += 1;
Ok(written_len)
}
pub fn pos(&self) -> usize {
self.pos
}
}
/// DirentWriter is used to write directory entry,
/// the object which implements it can decide how to format the data
pub trait DirentWriter: Sync + Send {
fn write_entry(&mut self, name: &str, ino: u64, type_: InodeType) -> Result<usize>;
}

View File

@ -0,0 +1,27 @@
use alloc::sync::Arc;
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,
}
pub trait FileSystem: Sync + Send {
fn sync(&self) -> Result<()>;
fn root_inode(&self) -> Arc<dyn Inode>;
fn sb(&self) -> SuperBlock;
}

View File

@ -0,0 +1,137 @@
use alloc::string::String;
use alloc::sync::Arc;
use bitflags::bitflags;
use core::any::Any;
use super::{DirentWriterContext, FileSystem};
use crate::fs::ioctl::IoctlCmd;
use crate::prelude::*;
#[repr(u32)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum InodeType {
File = 1,
Dir = 2,
SymLink = 3,
CharDevice = 4,
BlockDevice = 5,
}
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: Timespec,
pub mtime: Timespec,
pub ctime: Timespec,
pub type_: InodeType,
pub mode: InodeMode,
pub nlinks: usize,
pub uid: usize,
pub gid: usize,
pub rdev: usize,
}
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Timespec {
pub sec: i64,
pub nsec: i64,
}
pub trait Inode: Any + Sync + Send {
fn resize(&self, new_size: usize) -> Result<()>;
fn metadata(&self) -> Metadata;
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(&self, ctx: &mut DirentWriterContext) -> Result<usize>;
fn link(&self, old: &Arc<dyn Inode>, name: &str) -> Result<()>;
fn unlink(&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;
}
impl dyn Inode {
pub fn downcast_ref<T: Inode>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>()
}
}

View File

@ -0,0 +1,20 @@
//! VFS components
pub use access_mode::AccessMode;
pub use dirent_writer::{DirentWriter, DirentWriterContext};
pub use fs::{FileSystem, SuperBlock};
pub use inode::{Inode, InodeMode, InodeType, Metadata, Timespec};
pub use status_flags::StatusFlags;
mod access_mode;
mod dirent_writer;
mod fs;
mod inode;
mod status_flags;
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum SeekFrom {
Start(u64),
End(i64),
Current(i64),
}

View File

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

View File

@ -15,7 +15,7 @@ pub(crate) use core::ffi::CStr;
pub(crate) use jinux_frame::config::PAGE_SIZE;
pub(crate) use jinux_frame::vm::Vaddr;
pub(crate) use jinux_frame::{debug, error, info, print, println, trace, warn};
pub(crate) use spin::Mutex;
pub(crate) use spin::{Mutex, RwLock};
#[macro_export]
macro_rules! current {

View File

@ -47,3 +47,5 @@ typeflags! {
/// The full set of access rights.
pub type Full = TRights![Dup, Read, Write, Exec, Signal];
pub type ReadOp = TRights![Read];
pub type WriteOp = TRights![Write];

View File

@ -16,6 +16,6 @@ pub fn sys_ioctl(fd: FileDescripter, cmd: u32, arg: Vaddr) -> Result<SyscallRetu
let current = current!();
let file_table = current.file_table().lock();
let file = file_table.get_file(fd)?;
let res = file.ioctl(ioctl_cmd, arg)?;
let res = file.as_file().unwrap().ioctl(ioctl_cmd, arg)?;
return Ok(SyscallReturn::Return(res as _));
}

View File

@ -1,5 +1,6 @@
use crate::fs::file::File;
use crate::fs::file::FileDescripter;
use crate::fs::file_handle::FileHandle;
use crate::log_syscall_entry;
use crate::prelude::*;
use crate::syscall::constants::MAX_FILENAME_LEN;
@ -41,7 +42,7 @@ pub fn sys_openat(
}
if dirfd == AT_FDCWD && pathname == CString::new("/dev/tty")? {
let tty_file = get_console().clone() as Arc<dyn File>;
let tty_file = FileHandle::new_file(get_console().clone() as Arc<dyn File>);
let current = current!();
let mut file_table = current.file_table().lock();
let fd = file_table.insert(tty_file);

View File

@ -39,7 +39,7 @@ pub fn sys_poll(fds: Vaddr, nfds: c_nfds, timeout: i32) -> Result<SyscallReturn>
match file {
Err(_) => return Some(Err(Error::new(Errno::EBADF))),
Ok(file) => {
let file_events = file.poll();
let file_events = file.as_file().unwrap().poll();
let polled_events = pollfd.events.intersection(file_events);
if !polled_events.is_empty() {
ready_files += 1;