mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 13:26:48 +00:00
Merge pull request #57 from liqinggd/dev-pagecache
Add page cache manager as the pager for vmo
This commit is contained in:
commit
6651a642d3
42
src/Cargo.lock
generated
42
src/Cargo.lock
generated
@ -13,6 +13,17 @@ dependencies = [
|
|||||||
"rsdp",
|
"rsdp",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.32"
|
version = "1.0.32"
|
||||||
@ -91,6 +102,15 @@ version = "0.2.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
|
checksum = "e63201c624b8c8883921b1a1accc8916c4fa9dbfb15d122b26e4dde945b86bbf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
@ -172,6 +192,7 @@ dependencies = [
|
|||||||
"jinux-util",
|
"jinux-util",
|
||||||
"jinux-virtio",
|
"jinux-virtio",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"lru",
|
||||||
"pod",
|
"pod",
|
||||||
"pod-derive",
|
"pod-derive",
|
||||||
"ringbuffer",
|
"ringbuffer",
|
||||||
@ -263,6 +284,21 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e7d46de488603ffdd5f30afbc64fbba2378214a2c3a2fb83abf3d33126df17"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pod"
|
name = "pod"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@ -429,6 +465,12 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "virtio-input-decoder"
|
name = "virtio-input-decoder"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -27,6 +27,7 @@ ringbuffer = "0.10.0"
|
|||||||
|
|
||||||
spin = "0.9.4"
|
spin = "0.9.4"
|
||||||
vte = "0.10"
|
vte = "0.10"
|
||||||
|
lru = "0.9.0"
|
||||||
|
|
||||||
[dependencies.lazy_static]
|
[dependencies.lazy_static]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
|
380
src/services/libs/jinux-std/src/fs/fs_resolver.rs
Normal file
380
src/services/libs/jinux-std/src/fs/fs_resolver.rs
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use alloc::str;
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
|
use super::file::FileDescripter;
|
||||||
|
use super::inode_handle::InodeHandle;
|
||||||
|
use super::ramfs::RamFS;
|
||||||
|
use super::utils::{
|
||||||
|
AccessMode, CreationFlags, Dentry, FileSystem, InodeMode, InodeType, StatusFlags, PATH_MAX,
|
||||||
|
SYMLINKS_MAX,
|
||||||
|
};
|
||||||
|
use super::vfs_inode::VfsInode;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref ROOT_FS: Arc<dyn FileSystem> = RamFS::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
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() -> Result<Self> {
|
||||||
|
let root = {
|
||||||
|
let root_inode = VfsInode::new(ROOT_FS.root_inode())?;
|
||||||
|
Dentry::new_root(root_inode)
|
||||||
|
};
|
||||||
|
Ok(Self {
|
||||||
|
root: root.clone(),
|
||||||
|
cwd: root,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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(path, follow_tail_link) {
|
||||||
|
Ok(dentry) => {
|
||||||
|
let inode = dentry.inode().raw_inode();
|
||||||
|
if inode.metadata().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)
|
||||||
|
&& inode.metadata().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(path)?;
|
||||||
|
if file_name.ends_with("/") {
|
||||||
|
return_errno_with_message!(Errno::EISDIR, "path refers to a directory");
|
||||||
|
}
|
||||||
|
if !dir_dentry.inode().raw_inode().metadata().mode.is_writable() {
|
||||||
|
return_errno_with_message!(Errno::EPERM, "file cannot be created");
|
||||||
|
}
|
||||||
|
let new_dentry =
|
||||||
|
dir_dentry.create_child(&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
|
||||||
|
pub fn lookup(&self, path: &FsPath, follow_tail_link: bool) -> Result<Arc<Dentry>> {
|
||||||
|
let dentry = match path.inner {
|
||||||
|
FsPathInner::Absolute(path) => {
|
||||||
|
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_dentry_from_fd(fd)?;
|
||||||
|
self.lookup_from_parent(&parent, path, follow_tail_link)?
|
||||||
|
}
|
||||||
|
FsPathInner::Fd(fd) => self.lookup_dentry_from_fd(fd)?,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(dentry)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup the dir dentry and base file name of the giving path.
|
||||||
|
///
|
||||||
|
/// If encounters symlinks, should deference them.
|
||||||
|
fn lookup_dir_and_base_name(&self, path: &FsPath) -> 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, path, true)?,
|
||||||
|
String::from(file_name),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
FsPathInner::FdRelative(fd, path) => {
|
||||||
|
let (dir, file_name) = split_path(path);
|
||||||
|
let parent = self.lookup_dentry_from_fd(fd)?;
|
||||||
|
(
|
||||||
|
self.lookup_from_parent(&parent, path, true)?,
|
||||||
|
String::from(file_name),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => return_errno!(Errno::ENOENT),
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match dir_dentry.get(&base_name.trim_end_matches('/')) {
|
||||||
|
Ok(dentry) if dentry.inode().raw_inode().metadata().type_ == InodeType::SymLink => {
|
||||||
|
let link = {
|
||||||
|
let mut link = dentry.inode().raw_inode().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))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.get(next_name)?;
|
||||||
|
let next_type = next_dentry.inode().raw_inode().metadata().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.inode().raw_inode().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
|
||||||
|
fn lookup_dentry_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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
@ -6,11 +6,11 @@ use super::*;
|
|||||||
|
|
||||||
impl InodeHandle<Rights> {
|
impl InodeHandle<Rights> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
inode: Arc<dyn Inode>,
|
dentry: Arc<Dentry>,
|
||||||
access_mode: AccessMode,
|
access_mode: AccessMode,
|
||||||
status_flags: StatusFlags,
|
status_flags: StatusFlags,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let inode_info = inode.metadata();
|
let inode_info = dentry.inode().raw_inode().metadata();
|
||||||
if access_mode.is_readable() && !inode_info.mode.is_readable() {
|
if access_mode.is_readable() && !inode_info.mode.is_readable() {
|
||||||
return_errno_with_message!(Errno::EACCES, "File is not readable");
|
return_errno_with_message!(Errno::EACCES, "File is not readable");
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@ impl InodeHandle<Rights> {
|
|||||||
return_errno_with_message!(Errno::EISDIR, "Directory cannot open to write");
|
return_errno_with_message!(Errno::EISDIR, "Directory cannot open to write");
|
||||||
}
|
}
|
||||||
let inner = Arc::new(InodeHandle_ {
|
let inner = Arc::new(InodeHandle_ {
|
||||||
inode,
|
dentry,
|
||||||
offset: Mutex::new(0),
|
offset: Mutex::new(0),
|
||||||
access_mode,
|
access_mode,
|
||||||
status_flags: Mutex::new(status_flags),
|
status_flags: Mutex::new(status_flags),
|
||||||
|
@ -4,16 +4,17 @@ mod dyn_cap;
|
|||||||
mod static_cap;
|
mod static_cap;
|
||||||
|
|
||||||
use super::utils::{
|
use super::utils::{
|
||||||
AccessMode, DirentWriter, DirentWriterContext, Inode, InodeType, SeekFrom, StatusFlags,
|
AccessMode, Dentry, DirentWriter, DirentWriterContext, InodeType, SeekFrom, StatusFlags,
|
||||||
};
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::rights::Rights;
|
use crate::rights::Rights;
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
|
use jinux_frame::vm::VmIo;
|
||||||
|
|
||||||
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
|
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
|
||||||
|
|
||||||
struct InodeHandle_ {
|
struct InodeHandle_ {
|
||||||
inode: Arc<dyn Inode>,
|
dentry: Arc<Dentry>,
|
||||||
offset: Mutex<usize>,
|
offset: Mutex<usize>,
|
||||||
access_mode: AccessMode,
|
access_mode: AccessMode,
|
||||||
status_flags: Mutex<StatusFlags>,
|
status_flags: Mutex<StatusFlags>,
|
||||||
@ -22,15 +23,20 @@ struct InodeHandle_ {
|
|||||||
impl InodeHandle_ {
|
impl InodeHandle_ {
|
||||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||||
let mut offset = self.offset.lock();
|
let mut offset = self.offset.lock();
|
||||||
let file_size = self.inode.metadata().size;
|
let file_size = self.dentry.inode().raw_inode().metadata().size;
|
||||||
let start = file_size.min(*offset);
|
let start = file_size.min(*offset);
|
||||||
let end = file_size.min(*offset + buf.len());
|
let end = file_size.min(*offset + buf.len());
|
||||||
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
|
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
|
||||||
self.inode.read_at(start, &mut buf[0..start - end])?
|
self.dentry
|
||||||
|
.inode()
|
||||||
|
.raw_inode()
|
||||||
|
.read_at(start, &mut buf[0..end - start])?
|
||||||
} else {
|
} else {
|
||||||
self.inode.read_at(start, &mut buf[0..start - end])?
|
self.dentry
|
||||||
// TODO: use page cache
|
.inode()
|
||||||
// self.inode.pages().read_at(start, buf[0..start - end])?
|
.pages()
|
||||||
|
.read_bytes(start, &mut buf[0..end - start])?;
|
||||||
|
end - start
|
||||||
};
|
};
|
||||||
|
|
||||||
*offset += len;
|
*offset += len;
|
||||||
@ -39,20 +45,26 @@ impl InodeHandle_ {
|
|||||||
|
|
||||||
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||||
let mut offset = self.offset.lock();
|
let mut offset = self.offset.lock();
|
||||||
let file_size = self.inode.metadata().size;
|
let file_size = self.dentry.inode().raw_inode().metadata().size;
|
||||||
if self.status_flags.lock().contains(StatusFlags::O_APPEND) {
|
if self.status_flags.lock().contains(StatusFlags::O_APPEND) {
|
||||||
*offset = file_size;
|
*offset = file_size;
|
||||||
}
|
}
|
||||||
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
|
let len = if self.status_flags.lock().contains(StatusFlags::O_DIRECT) {
|
||||||
self.inode.write_at(*offset, buf)?
|
self.dentry.inode().raw_inode().write_at(*offset, buf)?
|
||||||
} else {
|
} else {
|
||||||
self.inode.write_at(*offset, buf)?
|
let pages = self.dentry.inode().pages();
|
||||||
// TODO: use page cache
|
let should_expand_size = *offset + buf.len() > file_size;
|
||||||
// let len = self.inode.pages().write_at(*offset, buf)?;
|
if should_expand_size {
|
||||||
// if offset + len > file_size {
|
pages.resize(*offset + buf.len())?;
|
||||||
// self.inode.resize(offset + len)?;
|
}
|
||||||
// }
|
pages.write_bytes(*offset, buf)?;
|
||||||
// len
|
if should_expand_size {
|
||||||
|
self.dentry
|
||||||
|
.inode()
|
||||||
|
.raw_inode()
|
||||||
|
.resize(*offset + buf.len())?;
|
||||||
|
}
|
||||||
|
buf.len()
|
||||||
};
|
};
|
||||||
|
|
||||||
*offset += len;
|
*offset += len;
|
||||||
@ -69,7 +81,7 @@ impl InodeHandle_ {
|
|||||||
off as i64
|
off as i64
|
||||||
}
|
}
|
||||||
SeekFrom::End(off /* as i64 */) => {
|
SeekFrom::End(off /* as i64 */) => {
|
||||||
let file_size = self.inode.metadata().size as i64;
|
let file_size = self.dentry.inode().raw_inode().metadata().size as i64;
|
||||||
assert!(file_size >= 0);
|
assert!(file_size >= 0);
|
||||||
file_size
|
file_size
|
||||||
.checked_add(off)
|
.checked_add(off)
|
||||||
@ -116,7 +128,11 @@ impl InodeHandle_ {
|
|||||||
pub fn readdir(&self, writer: &mut dyn DirentWriter) -> Result<usize> {
|
pub fn readdir(&self, writer: &mut dyn DirentWriter) -> Result<usize> {
|
||||||
let mut offset = self.offset.lock();
|
let mut offset = self.offset.lock();
|
||||||
let mut dir_writer_ctx = DirentWriterContext::new(*offset, writer);
|
let mut dir_writer_ctx = DirentWriterContext::new(*offset, writer);
|
||||||
let written_size = self.inode.readdir(&mut dir_writer_ctx)?;
|
let written_size = self
|
||||||
|
.dentry
|
||||||
|
.inode()
|
||||||
|
.raw_inode()
|
||||||
|
.readdir(&mut dir_writer_ctx)?;
|
||||||
*offset = dir_writer_ctx.pos();
|
*offset = dir_writer_ctx.pos();
|
||||||
Ok(written_size)
|
Ok(written_size)
|
||||||
}
|
}
|
||||||
@ -143,4 +159,8 @@ impl<R> InodeHandle<R> {
|
|||||||
pub fn set_status_flags(&self, new_status_flags: StatusFlags) {
|
pub fn set_status_flags(&self, new_status_flags: StatusFlags) {
|
||||||
self.0.set_status_flags(new_status_flags)
|
self.0.set_status_flags(new_status_flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dentry(&self) -> &Arc<Dentry> {
|
||||||
|
&self.0.dentry
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,12 @@ pub mod fcntl;
|
|||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod file_handle;
|
pub mod file_handle;
|
||||||
pub mod file_table;
|
pub mod file_table;
|
||||||
|
pub mod fs_resolver;
|
||||||
pub mod inode_handle;
|
pub mod inode_handle;
|
||||||
pub mod ioctl;
|
pub mod ioctl;
|
||||||
pub mod poll;
|
pub mod poll;
|
||||||
|
pub mod ramfs;
|
||||||
pub mod stat;
|
pub mod stat;
|
||||||
pub mod stdio;
|
pub mod stdio;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
pub mod vfs_inode;
|
||||||
|
568
src/services/libs/jinux-std/src/fs/ramfs/fs.rs
Normal file
568
src/services/libs/jinux-std/src/fs/ramfs/fs.rs
Normal file
@ -0,0 +1,568 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use alloc::str;
|
||||||
|
use alloc::string::String;
|
||||||
|
use core::any::Any;
|
||||||
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
use jinux_frame::vm::VmFrame;
|
||||||
|
use spin::{RwLock, RwLockWriteGuard};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::fs::ioctl::IoctlCmd;
|
||||||
|
use crate::fs::utils::{
|
||||||
|
DirentWriterContext, FileSystem, Inode, InodeMode, InodeType, Metadata, SuperBlock,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RamFS {
|
||||||
|
metadata: RwLock<SuperBlock>,
|
||||||
|
root: Arc<RamInode>,
|
||||||
|
inode_allocator: AtomicUsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RamFS {
|
||||||
|
pub fn new() -> Arc<Self> {
|
||||||
|
let root = Arc::new(RamInode(RwLock::new(Inode_::new_dir(
|
||||||
|
ROOT_INO,
|
||||||
|
InodeMode::from_bits_truncate(0o755),
|
||||||
|
))));
|
||||||
|
let ramfs = Arc::new(Self {
|
||||||
|
metadata: RwLock::new(SuperBlock::new(RAMFS_MAGIC, BLOCK_SIZE, NAME_MAX)),
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Inner::Dir(DirEntry::new()),
|
||||||
|
metadata: Metadata::new_dir(ino, mode),
|
||||||
|
this: Weak::default(),
|
||||||
|
fs: Weak::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_file(ino: usize, mode: InodeMode) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Inner::File,
|
||||||
|
metadata: Metadata::new_file(ino, mode),
|
||||||
|
this: Weak::default(),
|
||||||
|
fs: Weak::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_symlink(ino: usize, mode: InodeMode) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Inner::SymLink(Str256::from("")),
|
||||||
|
metadata: Metadata::new_synlink(ino, mode),
|
||||||
|
this: Weak::default(),
|
||||||
|
fs: Weak::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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: LinkedList<(Str256, Arc<RamInode>)>,
|
||||||
|
this: Weak<RamInode>,
|
||||||
|
parent: Weak<RamInode>,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! write_inode_entry {
|
||||||
|
($ctx:expr, $name:expr, $inode:expr, $total_written:expr) => {
|
||||||
|
let ctx = $ctx;
|
||||||
|
let name = $name;
|
||||||
|
let inode = $inode;
|
||||||
|
let total_written = $total_written;
|
||||||
|
|
||||||
|
match ctx.write_entry(
|
||||||
|
name.as_ref(),
|
||||||
|
inode.metadata().ino as u64,
|
||||||
|
inode.metadata().type_,
|
||||||
|
) {
|
||||||
|
Ok(written_len) => {
|
||||||
|
*total_written += written_len;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if *total_written == 0 {
|
||||||
|
return Err(e);
|
||||||
|
} else {
|
||||||
|
return Ok(*total_written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirEntry {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
children: LinkedList::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
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(idx, (child, inode))| child == &Str256::from(name))
|
||||||
|
.map(|(idx, (_, inode))| (idx + 2, inode.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn append_entry(&mut self, name: &str, inode: Arc<RamInode>) {
|
||||||
|
self.children.push_back((Str256::from(name), inode))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_entry(&mut self, idx: usize) -> (Str256, Arc<RamInode>) {
|
||||||
|
assert!(idx >= 2);
|
||||||
|
self.children.remove(idx - 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn modify_entry(&mut self, idx: usize, new_name: &str) {
|
||||||
|
assert!(idx >= 2);
|
||||||
|
let (name, _) = self.children.iter_mut().nth(idx - 2).unwrap();
|
||||||
|
*name = Str256::from(new_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iterate_entries(&self, mut ctx: &mut DirentWriterContext) -> Result<usize> {
|
||||||
|
let mut total_written_len = 0;
|
||||||
|
let idx = ctx.pos();
|
||||||
|
// Write the two special entries
|
||||||
|
if idx == 0 {
|
||||||
|
let this_inode = self.this.upgrade().unwrap();
|
||||||
|
write_inode_entry!(&mut ctx, ".", &this_inode, &mut total_written_len);
|
||||||
|
}
|
||||||
|
if idx <= 1 {
|
||||||
|
let parent_inode = self.parent.upgrade().unwrap();
|
||||||
|
write_inode_entry!(&mut ctx, "..", &parent_inode, &mut total_written_len);
|
||||||
|
}
|
||||||
|
// Write the normal entries
|
||||||
|
let skipped_children = if idx < 2 { 0 } else { idx - 2 };
|
||||||
|
for (name, child) in self.children.iter().skip(skipped_children) {
|
||||||
|
write_inode_entry!(&mut ctx, name, child, &mut total_written_len);
|
||||||
|
}
|
||||||
|
Ok(total_written_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 read is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resize(&self, new_size: usize) -> Result<()> {
|
||||||
|
if self.0.read().metadata.type_ != InodeType::File {
|
||||||
|
return_errno_with_message!(Errno::EISDIR, "self is not file");
|
||||||
|
}
|
||||||
|
self.0.write().metadata.size = new_size;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 new_inode = match type_ {
|
||||||
|
InodeType::File => {
|
||||||
|
let file_inode = Arc::new(RamInode(RwLock::new(Inode_::new_file(
|
||||||
|
self_inode.fs.upgrade().unwrap().alloc_id(),
|
||||||
|
mode,
|
||||||
|
))));
|
||||||
|
file_inode.0.write().fs = self_inode.fs.clone();
|
||||||
|
file_inode
|
||||||
|
}
|
||||||
|
InodeType::Dir => {
|
||||||
|
let dir_inode = Arc::new(RamInode(RwLock::new(Inode_::new_dir(
|
||||||
|
self_inode.fs.upgrade().unwrap().alloc_id(),
|
||||||
|
mode,
|
||||||
|
))));
|
||||||
|
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(),
|
||||||
|
);
|
||||||
|
dir_inode
|
||||||
|
}
|
||||||
|
InodeType::SymLink => {
|
||||||
|
let sym_inode = Arc::new(RamInode(RwLock::new(Inode_::new_symlink(
|
||||||
|
self_inode.fs.upgrade().unwrap().alloc_id(),
|
||||||
|
mode,
|
||||||
|
))));
|
||||||
|
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());
|
||||||
|
Ok(new_inode)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn readdir(&self, ctx: &mut DirentWriterContext) -> 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 total_written_len = self_inode
|
||||||
|
.inner
|
||||||
|
.as_direntry()
|
||||||
|
.unwrap()
|
||||||
|
.iterate_entries(ctx)?;
|
||||||
|
Ok(total_written_len)
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
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, other) = self_dir.get_entry(name).ok_or(Error::new(Errno::ENOENT))?;
|
||||||
|
let other_inode = other.0.read();
|
||||||
|
if other_inode.metadata.type_ == InodeType::Dir
|
||||||
|
&& !other_inode.inner.as_direntry().unwrap().is_empty_children()
|
||||||
|
{
|
||||||
|
return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty");
|
||||||
|
}
|
||||||
|
self_dir.remove_entry(idx);
|
||||||
|
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, _) = self_dir
|
||||||
|
.get_entry(old_name)
|
||||||
|
.ok_or(Error::new(Errno::ENOENT))?;
|
||||||
|
self_dir.modify_entry(idx, new_name);
|
||||||
|
} 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());
|
||||||
|
drop(self_inode);
|
||||||
|
drop(target_inode);
|
||||||
|
if src_inode.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);
|
||||||
|
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
src/services/libs/jinux-std/src/fs/ramfs/mod.rs
Normal file
10
src/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;
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
use crate::rights::Rights;
|
use crate::rights::Rights;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
@ -28,6 +29,21 @@ impl AccessMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
impl From<Rights> for AccessMode {
|
||||||
fn from(rights: Rights) -> AccessMode {
|
fn from(rights: Rights) -> AccessMode {
|
||||||
if rights.contains(Rights::READ) && rights.contains(Rights::WRITE) {
|
if rights.contains(Rights::READ) && rights.contains(Rights::WRITE) {
|
||||||
|
23
src/services/libs/jinux-std/src/fs/utils/creation_flags.rs
Normal file
23
src/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;
|
||||||
|
}
|
||||||
|
}
|
127
src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs
Normal file
127
src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use alloc::string::String;
|
||||||
|
|
||||||
|
use super::{InodeMode, InodeType, NAME_MAX};
|
||||||
|
use crate::fs::vfs_inode::VfsInode;
|
||||||
|
|
||||||
|
pub struct Dentry {
|
||||||
|
inner: RwLock<Dentry_>,
|
||||||
|
inode: VfsInode,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dentry_ {
|
||||||
|
name: String,
|
||||||
|
this: Weak<Dentry>,
|
||||||
|
parent: Option<Weak<Dentry>>,
|
||||||
|
children: BTreeMap<String, Arc<Dentry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dentry_ {
|
||||||
|
pub fn new(name: &str, parent: Option<Weak<Dentry>>) -> Self {
|
||||||
|
Self {
|
||||||
|
name: String::from(name),
|
||||||
|
this: Weak::default(),
|
||||||
|
parent,
|
||||||
|
children: BTreeMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Dentry {
|
||||||
|
/// Create a new dentry cache with root inode
|
||||||
|
pub fn new_root(inode: VfsInode) -> Arc<Self> {
|
||||||
|
let root = Self::new("/", inode, None);
|
||||||
|
root
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Internal constructor
|
||||||
|
fn new(name: &str, inode: VfsInode, parent: Option<Weak<Dentry>>) -> Arc<Self> {
|
||||||
|
let dentry = {
|
||||||
|
let inner = RwLock::new(Dentry_::new(name, parent));
|
||||||
|
Arc::new(Self { inner, inode })
|
||||||
|
};
|
||||||
|
dentry.inner.write().this = Arc::downgrade(&dentry);
|
||||||
|
dentry
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
self.inner.read().name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inode(&self) -> &VfsInode {
|
||||||
|
&self.inode
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_child(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> {
|
||||||
|
let mut inner = self.inner.write();
|
||||||
|
let child = {
|
||||||
|
let inode = VfsInode::new(self.inode().raw_inode().mknod(name, type_, mode)?)?;
|
||||||
|
Dentry::new(name, inode, Some(inner.this.clone()))
|
||||||
|
};
|
||||||
|
inner.children.insert(String::from(name), child.clone());
|
||||||
|
Ok(child)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn this(&self) -> Arc<Dentry> {
|
||||||
|
self.inner.read().this.upgrade().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parent(&self) -> Option<Arc<Dentry>> {
|
||||||
|
self.inner
|
||||||
|
.read()
|
||||||
|
.parent
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.upgrade().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, name: &str) -> Result<Arc<Dentry>> {
|
||||||
|
if self.inode.raw_inode().metadata().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 inner = self.inner.write();
|
||||||
|
if let Some(dentry) = inner.children.get(name) {
|
||||||
|
dentry.clone()
|
||||||
|
} else {
|
||||||
|
let inode = VfsInode::new(self.inode.raw_inode().lookup(name)?)?;
|
||||||
|
let dentry = Dentry::new(name, inode, Some(inner.this.clone()));
|
||||||
|
inner.children.insert(String::from(name), dentry.clone());
|
||||||
|
dentry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(dentry)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,24 @@ pub struct SuperBlock {
|
|||||||
pub flags: 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FileSystem: Sync + Send {
|
pub trait FileSystem: Sync + Send {
|
||||||
fn sync(&self) -> Result<()>;
|
fn sync(&self) -> Result<()>;
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use alloc::string::String;
|
|||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
|
use jinux_frame::vm::VmFrame;
|
||||||
|
|
||||||
use super::{DirentWriterContext, FileSystem};
|
use super::{DirentWriterContext, FileSystem};
|
||||||
use crate::fs::ioctl::IoctlCmd;
|
use crate::fs::ioctl::IoctlCmd;
|
||||||
@ -90,6 +91,65 @@ pub struct Metadata {
|
|||||||
pub rdev: usize,
|
pub rdev: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Metadata {
|
||||||
|
pub fn new_dir(ino: usize, mode: InodeMode) -> Self {
|
||||||
|
Self {
|
||||||
|
dev: 0,
|
||||||
|
ino,
|
||||||
|
size: 2,
|
||||||
|
blk_size: 0,
|
||||||
|
blocks: 0,
|
||||||
|
atime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
mtime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
ctime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
type_: InodeType::Dir,
|
||||||
|
mode,
|
||||||
|
nlinks: 2,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdev: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_file(ino: usize, mode: InodeMode) -> Self {
|
||||||
|
Self {
|
||||||
|
dev: 0,
|
||||||
|
ino,
|
||||||
|
size: 0,
|
||||||
|
blk_size: 0,
|
||||||
|
blocks: 0,
|
||||||
|
atime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
mtime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
ctime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
type_: InodeType::File,
|
||||||
|
mode,
|
||||||
|
nlinks: 1,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdev: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_synlink(ino: usize, mode: InodeMode) -> Self {
|
||||||
|
Self {
|
||||||
|
dev: 0,
|
||||||
|
ino,
|
||||||
|
size: 0,
|
||||||
|
blk_size: 0,
|
||||||
|
blocks: 0,
|
||||||
|
atime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
mtime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
ctime: Timespec { sec: 0, nsec: 0 },
|
||||||
|
type_: InodeType::SymLink,
|
||||||
|
mode,
|
||||||
|
nlinks: 1,
|
||||||
|
uid: 0,
|
||||||
|
gid: 0,
|
||||||
|
rdev: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
#[derive(Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
|
||||||
pub struct Timespec {
|
pub struct Timespec {
|
||||||
pub sec: i64,
|
pub sec: i64,
|
||||||
@ -101,6 +161,10 @@ pub trait Inode: Any + Sync + Send {
|
|||||||
|
|
||||||
fn metadata(&self) -> Metadata;
|
fn metadata(&self) -> Metadata;
|
||||||
|
|
||||||
|
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 read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize>;
|
||||||
|
|
||||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
|
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize>;
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
//! VFS components
|
//! VFS components
|
||||||
|
|
||||||
pub use access_mode::AccessMode;
|
pub use access_mode::AccessMode;
|
||||||
|
pub use creation_flags::CreationFlags;
|
||||||
|
pub use dentry_cache::Dentry;
|
||||||
pub use dirent_writer::{DirentWriter, DirentWriterContext};
|
pub use dirent_writer::{DirentWriter, DirentWriterContext};
|
||||||
pub use fs::{FileSystem, SuperBlock};
|
pub use fs::{FileSystem, SuperBlock};
|
||||||
pub use inode::{Inode, InodeMode, InodeType, Metadata, Timespec};
|
pub use inode::{Inode, InodeMode, InodeType, Metadata, Timespec};
|
||||||
|
pub use page_cache::PageCacheManager;
|
||||||
pub use status_flags::StatusFlags;
|
pub use status_flags::StatusFlags;
|
||||||
|
|
||||||
mod access_mode;
|
mod access_mode;
|
||||||
|
mod creation_flags;
|
||||||
|
mod dentry_cache;
|
||||||
mod dirent_writer;
|
mod dirent_writer;
|
||||||
mod fs;
|
mod fs;
|
||||||
mod inode;
|
mod inode;
|
||||||
|
mod page_cache;
|
||||||
mod status_flags;
|
mod status_flags;
|
||||||
|
|
||||||
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
|
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
|
||||||
@ -18,3 +24,12 @@ pub enum SeekFrom {
|
|||||||
End(i64),
|
End(i64),
|
||||||
Current(i64),
|
Current(i64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
131
src/services/libs/jinux-std/src/fs/utils/page_cache.rs
Normal file
131
src/services/libs/jinux-std/src/fs/utils/page_cache.rs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
use super::Inode;
|
||||||
|
use crate::prelude::*;
|
||||||
|
use crate::vm::vmo::Pager;
|
||||||
|
use jinux_frame::vm::{VmAllocOptions, VmFrame, VmFrameVec};
|
||||||
|
use lru::LruCache;
|
||||||
|
|
||||||
|
pub 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.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()?;
|
||||||
|
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,
|
||||||
|
}
|
30
src/services/libs/jinux-std/src/fs/vfs_inode.rs
Normal file
30
src/services/libs/jinux-std/src/fs/vfs_inode.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
use super::utils::{Inode, PageCacheManager};
|
||||||
|
use crate::rights::Rights;
|
||||||
|
use crate::vm::vmo::{Vmo, VmoFlags, VmoOptions};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VfsInode {
|
||||||
|
raw_inode: Arc<dyn Inode>,
|
||||||
|
pages: Vmo,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VfsInode {
|
||||||
|
pub fn new(raw_inode: Arc<dyn Inode>) -> Result<Self> {
|
||||||
|
let page_cache_manager = Arc::new(PageCacheManager::new(&Arc::downgrade(&raw_inode)));
|
||||||
|
let pages = VmoOptions::<Rights>::new(raw_inode.metadata().size)
|
||||||
|
.flags(VmoFlags::RESIZABLE)
|
||||||
|
.pager(page_cache_manager)
|
||||||
|
.alloc()?;
|
||||||
|
Ok(Self { raw_inode, pages })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pages(&self) -> &Vmo {
|
||||||
|
&self.pages
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_inode(&self) -> &Arc<dyn Inode> {
|
||||||
|
&self.raw_inode
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@
|
|||||||
// We should find a proper method to replace this feature with min_specialization, which is a sound feature.
|
// We should find a proper method to replace this feature with min_specialization, which is a sound feature.
|
||||||
#![feature(specialization)]
|
#![feature(specialization)]
|
||||||
#![feature(fn_traits)]
|
#![feature(fn_traits)]
|
||||||
|
#![feature(linked_list_remove)]
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -32,6 +33,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
extern crate lru;
|
||||||
|
|
||||||
pub mod driver;
|
pub mod driver;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -265,14 +265,14 @@ impl Vmo_ {
|
|||||||
return_errno_with_message!(Errno::EINVAL, "read range exceeds vmo size");
|
return_errno_with_message!(Errno::EINVAL, "read range exceeds vmo size");
|
||||||
}
|
}
|
||||||
let read_range = offset..(offset + read_len);
|
let read_range = offset..(offset + read_len);
|
||||||
let frames = self.ensure_all_pages_exist(read_range, false)?;
|
let frames = self.ensure_all_pages_exist(&read_range, false)?;
|
||||||
let read_offset = offset % PAGE_SIZE;
|
let read_offset = offset % PAGE_SIZE;
|
||||||
Ok(frames.read_bytes(read_offset, buf)?)
|
Ok(frames.read_bytes(read_offset, buf)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ensure all pages inside range are backed up vm frames, returns the frames.
|
/// Ensure all pages inside range are backed up vm frames, returns the frames.
|
||||||
fn ensure_all_pages_exist(&self, range: Range<usize>, write_page: bool) -> Result<VmFrameVec> {
|
fn ensure_all_pages_exist(&self, range: &Range<usize>, write_page: bool) -> Result<VmFrameVec> {
|
||||||
let page_idx_range = get_page_idx_range(&range);
|
let page_idx_range = get_page_idx_range(range);
|
||||||
let mut frames = VmFrameVec::empty();
|
let mut frames = VmFrameVec::empty();
|
||||||
for page_idx in page_idx_range {
|
for page_idx in page_idx_range {
|
||||||
let mut page_frame = self.get_backup_frame(page_idx, write_page, true)?;
|
let mut page_frame = self.get_backup_frame(page_idx, write_page, true)?;
|
||||||
@ -382,9 +382,15 @@ impl Vmo_ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let write_range = offset..(offset + write_len);
|
let write_range = offset..(offset + write_len);
|
||||||
let frames = self.ensure_all_pages_exist(write_range, true)?;
|
let frames = self.ensure_all_pages_exist(&write_range, true)?;
|
||||||
let write_offset = offset % PAGE_SIZE;
|
let write_offset = offset % PAGE_SIZE;
|
||||||
frames.write_bytes(write_offset, buf)?;
|
frames.write_bytes(write_offset, buf)?;
|
||||||
|
if let Some(pager) = &self.inner.lock().pager {
|
||||||
|
let page_idx_range = get_page_idx_range(&write_range);
|
||||||
|
for page_idx in page_idx_range {
|
||||||
|
pager.update_page(page_idx * PAGE_SIZE)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user