Add support to handle file lookup with dentry cache

This commit is contained in:
LI Qing 2023-01-06 16:06:33 +08:00
parent 93dfaa1cd8
commit 01dba4b137
9 changed files with 588 additions and 15 deletions

View 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)
}

View File

@ -6,11 +6,11 @@ use super::*;
impl InodeHandle<Rights> { impl InodeHandle<Rights> {
pub fn new( pub fn new(
inode: VfsInode, dentry: Arc<Dentry>,
access_mode: AccessMode, access_mode: AccessMode,
status_flags: StatusFlags, status_flags: StatusFlags,
) -> Result<Self> { ) -> 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() { 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),

View File

@ -4,9 +4,8 @@ mod dyn_cap;
mod static_cap; mod static_cap;
use super::utils::{ 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::prelude::*;
use crate::rights::Rights; use crate::rights::Rights;
use alloc::sync::Arc; use alloc::sync::Arc;
@ -15,7 +14,7 @@ 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: VfsInode, dentry: Arc<Dentry>,
offset: Mutex<usize>, offset: Mutex<usize>,
access_mode: AccessMode, access_mode: AccessMode,
status_flags: Mutex<StatusFlags>, status_flags: Mutex<StatusFlags>,
@ -24,15 +23,17 @@ 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.raw_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 self.dentry
.inode()
.raw_inode() .raw_inode()
.read_at(start, &mut buf[0..end - start])? .read_at(start, &mut buf[0..end - start])?
} else { } else {
self.inode self.dentry
.inode()
.pages() .pages()
.read_bytes(start, &mut buf[0..end - start])?; .read_bytes(start, &mut buf[0..end - start])?;
end - start end - start
@ -44,21 +45,24 @@ 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.raw_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.raw_inode().write_at(*offset, buf)? self.dentry.inode().raw_inode().write_at(*offset, buf)?
} else { } else {
let pages = self.inode.pages(); let pages = self.dentry.inode().pages();
let should_expand_size = *offset + buf.len() > file_size; let should_expand_size = *offset + buf.len() > file_size;
if should_expand_size { if should_expand_size {
pages.resize(*offset + buf.len())?; pages.resize(*offset + buf.len())?;
} }
pages.write_bytes(*offset, buf)?; pages.write_bytes(*offset, buf)?;
if should_expand_size { if should_expand_size {
self.inode.raw_inode().resize(*offset + buf.len())?; self.dentry
.inode()
.raw_inode()
.resize(*offset + buf.len())?;
} }
buf.len() buf.len()
}; };
@ -77,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.raw_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)
@ -124,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.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(); *offset = dir_writer_ctx.pos();
Ok(written_size) Ok(written_size)
} }
@ -151,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
}
} }

View File

@ -3,6 +3,7 @@ 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;

View File

@ -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) {

View File

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

View File

@ -0,0 +1,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
}
}

View File

@ -1,6 +1,8 @@
//! 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};
@ -8,6 +10,8 @@ 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;
@ -20,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;

View File

@ -4,6 +4,7 @@ use super::utils::{Inode, PageCacheManager};
use crate::rights::Rights; use crate::rights::Rights;
use crate::vm::vmo::{Vmo, VmoFlags, VmoOptions}; use crate::vm::vmo::{Vmo, VmoFlags, VmoOptions};
#[derive(Clone)]
pub struct VfsInode { pub struct VfsInode {
raw_inode: Arc<dyn Inode>, raw_inode: Arc<dyn Inode>,
pages: Vmo, pages: Vmo,