mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 05:16:47 +00:00
Add support to handle file lookup with dentry cache
This commit is contained in:
parent
93dfaa1cd8
commit
01dba4b137
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> {
|
||||
pub fn new(
|
||||
inode: VfsInode,
|
||||
dentry: Arc<Dentry>,
|
||||
access_mode: AccessMode,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<Self> {
|
||||
let inode_info = inode.raw_inode().metadata();
|
||||
let inode_info = dentry.inode().raw_inode().metadata();
|
||||
if access_mode.is_readable() && !inode_info.mode.is_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");
|
||||
}
|
||||
let inner = Arc::new(InodeHandle_ {
|
||||
inode,
|
||||
dentry,
|
||||
offset: Mutex::new(0),
|
||||
access_mode,
|
||||
status_flags: Mutex::new(status_flags),
|
||||
|
@ -4,9 +4,8 @@ mod dyn_cap;
|
||||
mod static_cap;
|
||||
|
||||
use super::utils::{
|
||||
AccessMode, DirentWriter, DirentWriterContext, InodeType, SeekFrom, StatusFlags,
|
||||
AccessMode, Dentry, DirentWriter, DirentWriterContext, InodeType, SeekFrom, StatusFlags,
|
||||
};
|
||||
use super::vfs_inode::VfsInode;
|
||||
use crate::prelude::*;
|
||||
use crate::rights::Rights;
|
||||
use alloc::sync::Arc;
|
||||
@ -15,7 +14,7 @@ use jinux_frame::vm::VmIo;
|
||||
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
|
||||
|
||||
struct InodeHandle_ {
|
||||
inode: VfsInode,
|
||||
dentry: Arc<Dentry>,
|
||||
offset: Mutex<usize>,
|
||||
access_mode: AccessMode,
|
||||
status_flags: Mutex<StatusFlags>,
|
||||
@ -24,15 +23,17 @@ struct InodeHandle_ {
|
||||
impl InodeHandle_ {
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
let file_size = self.inode.raw_inode().metadata().size;
|
||||
let file_size = self.dentry.inode().raw_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
|
||||
self.dentry
|
||||
.inode()
|
||||
.raw_inode()
|
||||
.read_at(start, &mut buf[0..end - start])?
|
||||
} else {
|
||||
self.inode
|
||||
self.dentry
|
||||
.inode()
|
||||
.pages()
|
||||
.read_bytes(start, &mut buf[0..end - start])?;
|
||||
end - start
|
||||
@ -44,21 +45,24 @@ impl InodeHandle_ {
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
let file_size = self.inode.raw_inode().metadata().size;
|
||||
let file_size = self.dentry.inode().raw_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.raw_inode().write_at(*offset, buf)?
|
||||
self.dentry.inode().raw_inode().write_at(*offset, buf)?
|
||||
} else {
|
||||
let pages = self.inode.pages();
|
||||
let pages = self.dentry.inode().pages();
|
||||
let should_expand_size = *offset + buf.len() > file_size;
|
||||
if should_expand_size {
|
||||
pages.resize(*offset + buf.len())?;
|
||||
}
|
||||
pages.write_bytes(*offset, buf)?;
|
||||
if should_expand_size {
|
||||
self.inode.raw_inode().resize(*offset + buf.len())?;
|
||||
self.dentry
|
||||
.inode()
|
||||
.raw_inode()
|
||||
.resize(*offset + buf.len())?;
|
||||
}
|
||||
buf.len()
|
||||
};
|
||||
@ -77,7 +81,7 @@ impl InodeHandle_ {
|
||||
off as i64
|
||||
}
|
||||
SeekFrom::End(off /* as i64 */) => {
|
||||
let file_size = self.inode.raw_inode().metadata().size as i64;
|
||||
let file_size = self.dentry.inode().raw_inode().metadata().size as i64;
|
||||
assert!(file_size >= 0);
|
||||
file_size
|
||||
.checked_add(off)
|
||||
@ -124,7 +128,11 @@ impl InodeHandle_ {
|
||||
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.raw_inode().readdir(&mut dir_writer_ctx)?;
|
||||
let written_size = self
|
||||
.dentry
|
||||
.inode()
|
||||
.raw_inode()
|
||||
.readdir(&mut dir_writer_ctx)?;
|
||||
*offset = dir_writer_ctx.pos();
|
||||
Ok(written_size)
|
||||
}
|
||||
@ -151,4 +159,8 @@ impl<R> InodeHandle<R> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ pub mod fcntl;
|
||||
pub mod file;
|
||||
pub mod file_handle;
|
||||
pub mod file_table;
|
||||
pub mod fs_resolver;
|
||||
pub mod inode_handle;
|
||||
pub mod ioctl;
|
||||
pub mod poll;
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::prelude::*;
|
||||
use crate::rights::Rights;
|
||||
|
||||
#[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 {
|
||||
fn from(rights: Rights) -> AccessMode {
|
||||
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
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
//! VFS components
|
||||
|
||||
pub use access_mode::AccessMode;
|
||||
pub use creation_flags::CreationFlags;
|
||||
pub use dentry_cache::Dentry;
|
||||
pub use dirent_writer::{DirentWriter, DirentWriterContext};
|
||||
pub use fs::{FileSystem, SuperBlock};
|
||||
pub use inode::{Inode, InodeMode, InodeType, Metadata, Timespec};
|
||||
@ -8,6 +10,8 @@ pub use page_cache::PageCacheManager;
|
||||
pub use status_flags::StatusFlags;
|
||||
|
||||
mod access_mode;
|
||||
mod creation_flags;
|
||||
mod dentry_cache;
|
||||
mod dirent_writer;
|
||||
mod fs;
|
||||
mod inode;
|
||||
@ -20,3 +24,12 @@ pub enum SeekFrom {
|
||||
End(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;
|
||||
|
@ -4,6 +4,7 @@ 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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user