123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- pub mod dcache;
- use core::{
- hash::{BuildHasher, BuildHasherDefault, Hasher},
- ops::ControlFlow,
- sync::atomic::{AtomicPtr, Ordering},
- };
- use crate::{
- hash::KernelHasher,
- io::{Buffer, ByteBuffer},
- kernel::{block::BlockDevice, CharDevice},
- path::{Path, PathComponent},
- prelude::*,
- rcu::{RCUNode, RCUPointer},
- };
- use alloc::sync::Arc;
- use bindings::{
- statx, EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOTDIR, EPERM, ERANGE, O_CREAT, O_EXCL,
- };
- use super::{
- inode::{Ino, Inode, Mode, WriteOffset},
- s_isblk, s_ischr, s_isdir, s_isreg, DevId, FsContext,
- };
- struct DentryData {
- inode: Arc<dyn Inode>,
- flags: u64,
- }
- /// # Safety
- ///
- /// We wrap `Dentry` in `Arc` to ensure that the `Dentry` is not dropped while it is still in use.
- ///
- /// Since a `Dentry` is created and marked as live(some data is saved to it), it keeps alive until
- /// the last reference is dropped.
- pub struct Dentry {
- // Const after insertion into dcache
- parent: Arc<Dentry>,
- name: Arc<[u8]>,
- hash: u64,
- // Used by the dentry cache
- prev: AtomicPtr<Dentry>,
- next: AtomicPtr<Dentry>,
- // RCU Mutable
- data: RCUPointer<DentryData>,
- }
- impl core::fmt::Debug for Dentry {
- fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
- f.debug_struct("Dentry")
- .field("name", &String::from_utf8_lossy(&self.name))
- .field("parent", &String::from_utf8_lossy(&self.parent.name))
- .finish()
- }
- }
- const D_DIRECTORY: u64 = 1;
- #[allow(dead_code)]
- const D_MOUNTPOINT: u64 = 2;
- const D_SYMLINK: u64 = 4;
- const D_REGULAR: u64 = 8;
- impl RCUNode<Dentry> for Dentry {
- fn rcu_prev(&self) -> &AtomicPtr<Self> {
- &self.prev
- }
- fn rcu_next(&self) -> &AtomicPtr<Self> {
- &self.next
- }
- }
- impl Dentry {
- fn rehash(self: &Arc<Self>) -> u64 {
- let builder: BuildHasherDefault<KernelHasher> = Default::default();
- let mut hasher = builder.build_hasher();
- hasher.write_usize(self.parent_addr() as usize);
- hasher.write(self.name.as_ref());
- hasher.finish()
- }
- fn find(self: &Arc<Self>, name: &[u8]) -> KResult<Arc<Self>> {
- let data = self.data.load();
- let data = data.as_ref().ok_or(ENOENT)?;
- if data.flags & D_DIRECTORY == 0 {
- return Err(ENOTDIR);
- }
- match name {
- b"." => Ok(self.clone()),
- b".." => Ok(self.parent.clone()),
- _ => {
- let dentry = Dentry::create(self.clone(), name);
- Ok(dcache::d_find_fast(&dentry).unwrap_or_else(|| {
- dcache::d_try_revalidate(&dentry);
- dcache::d_add(&dentry);
- dentry
- }))
- }
- }
- }
- }
- impl Dentry {
- pub fn create(parent: Arc<Dentry>, name: &[u8]) -> Arc<Self> {
- let mut val = Arc::new(Self {
- parent,
- name: Arc::from(name),
- hash: 0,
- prev: AtomicPtr::default(),
- next: AtomicPtr::default(),
- data: RCUPointer::empty(),
- });
- let hash = val.rehash();
- let val_mut = Arc::get_mut(&mut val).unwrap();
- val_mut.hash = hash;
- val
- }
- /// Check the equality of two denties inside the same dentry cache hash group
- /// where `other` is identified by `hash`, `parent` and `name`
- ///
- fn hash_eq(self: &Arc<Self>, other: &Arc<Self>) -> bool {
- self.hash == other.hash
- && self.parent_addr() == other.parent_addr()
- && self.name == other.name
- }
- pub fn name(&self) -> &Arc<[u8]> {
- &self.name
- }
- pub fn parent(&self) -> &Arc<Self> {
- &self.parent
- }
- pub fn parent_addr(&self) -> *const Self {
- Arc::as_ptr(&self.parent)
- }
- fn save_data(&self, inode: Arc<dyn Inode>, flags: u64) -> KResult<()> {
- let new = DentryData { inode, flags };
- // TODO!!!: We don't actually need to use `RCUPointer` here
- // Safety: this function may only be called from `create`-like functions which requires the
- // superblock's write locks to be held, so only one creation can happen at a time and we
- // can't get a reference to the old data.
- let old = unsafe { self.data.swap(Some(Arc::new(new))) };
- assert!(old.is_none());
- Ok(())
- }
- pub fn save_reg(&self, file: Arc<dyn Inode>) -> KResult<()> {
- self.save_data(file, D_REGULAR)
- }
- pub fn save_symlink(&self, link: Arc<dyn Inode>) -> KResult<()> {
- self.save_data(link, D_SYMLINK)
- }
- pub fn save_dir(&self, dir: Arc<dyn Inode>) -> KResult<()> {
- self.save_data(dir, D_DIRECTORY)
- }
- pub fn get_inode(&self) -> KResult<Arc<dyn Inode>> {
- self.data
- .load()
- .as_ref()
- .ok_or(ENOENT)
- .map(|data| data.inode.clone())
- }
- pub fn is_directory(&self) -> bool {
- let data = self.data.load();
- data.as_ref()
- .map_or(false, |data| data.flags & D_DIRECTORY != 0)
- }
- pub fn is_valid(&self) -> bool {
- self.data.load().is_some()
- }
- pub fn open_check(self: &Arc<Self>, flags: u32, mode: Mode) -> KResult<()> {
- let data = self.data.load();
- let create = flags & O_CREAT != 0;
- let excl = flags & O_EXCL != 0;
- if data.is_some() {
- if create && excl {
- return Err(EEXIST);
- }
- return Ok(());
- } else {
- if !create {
- return Err(ENOENT);
- }
- let parent = self.parent().get_inode()?;
- parent.creat(self, mode as u32)
- }
- }
- }
- impl Dentry {
- fn resolve_directory(
- context: &FsContext,
- dentry: Arc<Self>,
- nrecur: u32,
- ) -> KResult<Arc<Self>> {
- if nrecur >= 16 {
- return Err(ELOOP);
- }
- let data = dentry.data.load();
- let data = data.as_ref().ok_or(ENOENT)?;
- match data.flags {
- flags if flags & D_REGULAR != 0 => Err(ENOTDIR),
- flags if flags & D_DIRECTORY != 0 => Ok(dentry),
- flags if flags & D_SYMLINK != 0 => {
- let mut buffer = [0u8; 256];
- let mut buffer = ByteBuffer::new(&mut buffer);
- data.inode.readlink(&mut buffer)?;
- let path = Path::new(buffer.data())?;
- let dentry = Self::open_recursive(context, &dentry.parent, path, true, nrecur + 1)?;
- Self::resolve_directory(context, dentry, nrecur + 1)
- }
- _ => panic!("Invalid dentry flags"),
- }
- }
- pub fn open_recursive(
- context: &FsContext,
- cwd: &Arc<Self>,
- path: Path,
- follow: bool,
- nrecur: u32,
- ) -> KResult<Arc<Self>> {
- // too many recursive search layers will cause stack overflow
- // so we use 16 for now
- if nrecur >= 16 {
- return Err(ELOOP);
- }
- let mut cwd = if path.is_absolute() {
- context.fsroot.clone()
- } else {
- cwd.clone()
- };
- for item in path.iter() {
- if let PathComponent::TrailingEmpty = item {
- if cwd.data.load().as_ref().is_none() {
- return Ok(cwd);
- }
- }
- cwd = Self::resolve_directory(context, cwd, nrecur)?;
- match item {
- PathComponent::TrailingEmpty | PathComponent::Current => {} // pass
- PathComponent::Parent => {
- if !cwd.hash_eq(&context.fsroot) {
- cwd = Self::resolve_directory(context, cwd.parent.clone(), nrecur)?;
- }
- continue;
- }
- PathComponent::Name(name) => {
- cwd = cwd.find(name)?;
- }
- }
- }
- if follow {
- let data = cwd.data.load();
- if let Some(data) = data.as_ref() {
- if data.flags & D_SYMLINK != 0 {
- let data = cwd.data.load();
- let data = data.as_ref().unwrap();
- let mut buffer = [0u8; 256];
- let mut buffer = ByteBuffer::new(&mut buffer);
- data.inode.readlink(&mut buffer)?;
- let path = Path::new(buffer.data())?;
- cwd = Self::open_recursive(context, &cwd.parent, path, true, nrecur + 1)?;
- }
- }
- }
- Ok(cwd)
- }
- pub fn open(context: &FsContext, path: Path, follow_symlinks: bool) -> KResult<Arc<Self>> {
- let cwd = context.cwd.lock().clone();
- Dentry::open_recursive(context, &cwd, path, follow_symlinks, 0)
- }
- pub fn open_at(
- context: &FsContext,
- at: &Arc<Self>,
- path: Path,
- follow_symlinks: bool,
- ) -> KResult<Arc<Self>> {
- Dentry::open_recursive(context, at, path, follow_symlinks, 0)
- }
- pub fn get_path(
- self: &Arc<Dentry>,
- context: &FsContext,
- buffer: &mut dyn Buffer,
- ) -> KResult<()> {
- let mut dentry = self;
- let root = &context.fsroot;
- let mut path = vec![];
- while Arc::as_ptr(dentry) != Arc::as_ptr(root) {
- if path.len() > 32 {
- return Err(ELOOP);
- }
- path.push(dentry.name().clone());
- dentry = dentry.parent();
- }
- buffer.fill(b"/")?.ok_or(ERANGE)?;
- for item in path.iter().rev().map(|name| name.as_ref()) {
- buffer.fill(item)?.ok_or(ERANGE)?;
- buffer.fill(b"/")?.ok_or(ERANGE)?;
- }
- buffer.fill(&[0])?.ok_or(ERANGE)?;
- Ok(())
- }
- }
- impl Dentry {
- pub fn read(&self, buffer: &mut dyn Buffer, offset: usize) -> KResult<usize> {
- let inode = self.get_inode()?;
- // Safety: Changing mode alone will have no effect on the file's contents
- match inode.mode.load(Ordering::Relaxed) {
- mode if s_isdir(mode) => Err(EISDIR),
- mode if s_isreg(mode) => inode.read(buffer, offset),
- mode if s_isblk(mode) => {
- let device = BlockDevice::get(inode.devid()?)?;
- Ok(device.read_some(offset, buffer)?.allow_partial())
- }
- mode if s_ischr(mode) => {
- let device = CharDevice::get(inode.devid()?).ok_or(EPERM)?;
- device.read(buffer)
- }
- _ => Err(EINVAL),
- }
- }
- pub fn write(&self, buffer: &[u8], offset: WriteOffset) -> KResult<usize> {
- let inode = self.get_inode()?;
- // Safety: Changing mode alone will have no effect on the file's contents
- match inode.mode.load(Ordering::Relaxed) {
- mode if s_isdir(mode) => Err(EISDIR),
- mode if s_isreg(mode) => inode.write(buffer, offset),
- mode if s_isblk(mode) => Err(EINVAL), // TODO
- mode if s_ischr(mode) => CharDevice::get(inode.devid()?).ok_or(EPERM)?.write(buffer),
- _ => Err(EINVAL),
- }
- }
- pub fn readdir<F>(&self, offset: usize, mut callback: F) -> KResult<usize>
- where
- F: FnMut(&[u8], Ino) -> KResult<ControlFlow<(), ()>>,
- {
- self.get_inode()?.do_readdir(offset, &mut callback)
- }
- pub fn mkdir(&self, mode: Mode) -> KResult<()> {
- if self.get_inode().is_ok() {
- Err(EEXIST)
- } else {
- self.parent.get_inode().unwrap().mkdir(self, mode)
- }
- }
- pub fn statx(&self, stat: &mut statx, mask: u32) -> KResult<()> {
- self.get_inode()?.statx(stat, mask)
- }
- pub fn truncate(&self, size: usize) -> KResult<()> {
- self.get_inode()?.truncate(size)
- }
- pub fn unlink(self: &Arc<Self>) -> KResult<()> {
- if self.get_inode().is_err() {
- Err(ENOENT)
- } else {
- self.parent.get_inode().unwrap().unlink(self)
- }
- }
- pub fn symlink(self: &Arc<Self>, link: &[u8]) -> KResult<()> {
- if self.get_inode().is_ok() {
- Err(EEXIST)
- } else {
- self.parent.get_inode().unwrap().symlink(self, link)
- }
- }
- pub fn readlink(&self, buffer: &mut dyn Buffer) -> KResult<usize> {
- self.get_inode()?.readlink(buffer)
- }
- pub fn mknod(&self, mode: Mode, devid: DevId) -> KResult<()> {
- if self.get_inode().is_ok() {
- Err(EEXIST)
- } else {
- self.parent.get_inode().unwrap().mknod(self, mode, devid)
- }
- }
- pub fn chmod(&self, mode: Mode) -> KResult<()> {
- self.get_inode()?.chmod(mode)
- }
- }
|