diff --git a/src/kernel/src/errno.rs b/src/kernel/src/errno.rs index ab517bc87..7074b927a 100644 --- a/src/kernel/src/errno.rs +++ b/src/kernel/src/errno.rs @@ -108,7 +108,11 @@ pub trait Errno: Error { fn errno(&self) -> NonZeroI32; } -impl Error for Box {} +impl Error for Box { + fn source(&self) -> Option<&(dyn Error + 'static)> { + self.as_ref().source() + } +} /// Get human readable text. pub fn strerror(num: NonZeroI32) -> &'static str { diff --git a/src/kernel/src/fs/cdev.rs b/src/kernel/src/fs/dev/cdev.rs similarity index 50% rename from src/kernel/src/fs/cdev.rs rename to src/kernel/src/fs/dev/cdev.rs index 40d511d65..78aefde91 100644 --- a/src/kernel/src/fs/cdev.rs +++ b/src/kernel/src/fs/dev/cdev.rs @@ -1,30 +1,32 @@ +use super::dirent::Dirent; use crate::ucred::Ucred; use bitflags::bitflags; -use std::sync::Arc; +use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard}; +use std::sync::{Arc, Weak}; use std::time::SystemTime; /// An implementation of `cdev` and `cdev_priv` structures. #[derive(Debug)] pub struct Cdev { - sw: Arc, // si_devsw - inode: u32, // cdp_inode - unit: i32, // si_drv0 - name: String, // si_name - uid: i32, // si_uid - gid: i32, // si_gid - mode: u16, // si_mode - ctime: SystemTime, // si_ctime - atime: SystemTime, // si_atime - mtime: SystemTime, // si_mtime - cred: Option>, // si_cred - flags: DeviceFlags, // si_flags + sw: Arc, // si_devsw + unit: i32, // si_drv0 + name: String, // si_name + uid: i32, // si_uid + gid: i32, // si_gid + mode: u16, // si_mode + ctime: SystemTime, // si_ctime + atime: SystemTime, // si_atime + mtime: SystemTime, // si_mtime + cred: Option>, // si_cred + flags: DeviceFlags, // si_flags + inode: i32, // cdp_inode + dirents: Gutex>>>, // cdp_dirents + cdp_maxdirent } impl Cdev { /// See `devfs_alloc` on the PS4 for a reference. pub(super) fn new>( sw: &Arc, - inode: u32, unit: i32, name: N, uid: i32, @@ -32,7 +34,9 @@ impl Cdev { mode: u16, cred: Option>, flags: DeviceFlags, + inode: i32, ) -> Self { + let gg = GutexGroup::new(); let now = SystemTime::now(); Self { @@ -48,6 +52,7 @@ impl Cdev { mtime: now, cred, flags, + dirents: gg.spawn(vec![None]), } } @@ -55,16 +60,41 @@ impl Cdev { self.name.as_ref() } + pub fn uid(&self) -> i32 { + self.uid + } + + pub fn gid(&self) -> i32 { + self.gid + } + + pub fn mode(&self) -> u16 { + self.mode + } + pub fn flags(&self) -> DeviceFlags { self.flags } + + pub(super) fn inode(&self) -> i32 { + self.inode + } + + pub(super) fn dirents(&self) -> GutexReadGuard>>> { + self.dirents.read() + } + + pub(super) fn dirents_mut(&self) -> GutexWriteGuard>>> { + self.dirents.write() + } } bitflags! { /// Flags for [`Cdev`]. #[derive(Debug, Clone, Copy)] pub struct DeviceFlags: u32 { - const SI_ETERNAL = 0x0001; + const SI_ETERNAL = 0x01; + const SI_ALIAS = 0x02; } } diff --git a/src/kernel/src/fs/dev/dirent.rs b/src/kernel/src/fs/dev/dirent.rs index 2c1fec4c6..b0057c173 100644 --- a/src/kernel/src/fs/dev/dirent.rs +++ b/src/kernel/src/fs/dev/dirent.rs @@ -1,12 +1,15 @@ use crate::fs::{DirentType, Vnode}; use bitflags::bitflags; use gmtx::{Gutex, GutexGroup, GutexWriteGuard}; +use std::ops::Deref; use std::sync::{Arc, Weak}; use std::time::SystemTime; /// An implementation of `devfs_dirent` structure. pub struct Dirent { inode: i32, // de_inode + uid: Gutex, // de_uid + gid: Gutex, // de_gid mode: Gutex, // de_mode dir: Option>, // de_dir children: Gutex>>, // de_dlist @@ -22,6 +25,8 @@ impl Dirent { pub fn new( ty: DirentType, inode: i32, + uid: i32, + gid: i32, mode: u16, dir: Option>, flags: DirentFlags, @@ -35,6 +40,8 @@ impl Dirent { Self { inode, + uid: gg.spawn(uid), + gid: gg.spawn(gid), mode: gg.spawn(mode), dir, children: gg.spawn(Vec::new()), @@ -66,6 +73,29 @@ impl Dirent { pub fn dirent(&self) -> &crate::fs::Dirent { &self.dirent } + + /// See `devfs_find` on the PS4 for a reference. + pub fn find>(&self, name: N, ty: Option) -> Option> { + let name = name.as_ref(); + + for child in self.children.read().deref() { + // Check name. + if child.dirent.name() != name { + continue; + } + + // Check type. + if let Some(ty) = ty { + if child.dirent.ty() != ty { + continue; + } + } + + return Some(child.clone()); + } + + None + } } bitflags! { diff --git a/src/kernel/src/fs/dev/mod.rs b/src/kernel/src/fs/dev/mod.rs index bed9218e2..23f01bbe6 100644 --- a/src/kernel/src/fs/dev/mod.rs +++ b/src/kernel/src/fs/dev/mod.rs @@ -1,19 +1,18 @@ -use self::dirent::DirentFlags; -use super::{ - path_contains, Cdev, CdevSw, DeviceFlags, DirentType, DriverFlags, FsOps, Mount, MountFlags, - Vnode, VnodeType, VopVector, -}; +pub use self::cdev::*; +use self::dirent::{Dirent, DirentFlags}; +use self::vnode::VNODE_OPS; +use super::{path_contains, DirentType, FsOps, Mount, MountFlags, Vnode, VnodeType}; use crate::errno::{Errno, EEXIST, EOPNOTSUPP}; use crate::ucred::Ucred; use bitflags::bitflags; use std::any::Any; use std::collections::HashMap; use std::num::NonZeroI32; -use std::ops::Deref; -use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; -use std::sync::{Arc, RwLock}; +use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex, RwLock}; use thiserror::Error; +mod cdev; pub(super) mod console; pub(super) mod deci_tty6; pub(super) mod dipsw; @@ -21,6 +20,7 @@ mod dirent; pub(super) mod dmem0; pub(super) mod dmem1; pub(super) mod dmem2; +mod vnode; /// See `make_dev_credv` on the PS4 for a reference. pub fn make_dev>( @@ -54,7 +54,6 @@ pub fn make_dev>( // Create cdev. let dev = Arc::new(Cdev::new( sw, - INODE.fetch_add(1, Ordering::Relaxed), unit, name, uid, @@ -62,10 +61,10 @@ pub fn make_dev>( mode, cred, df, + INODE.fetch_add(1, Ordering::Relaxed).try_into().unwrap(), )); DEVICES.write().unwrap().push(dev.clone()); - GENERATION.fetch_add(1, Ordering::Release); // TODO: Implement the remaining logic from the PS4. Ok(dev) @@ -75,7 +74,7 @@ pub fn make_dev>( pub fn dev_exists>(name: N) -> bool { let name = name.as_ref(); - for dev in DEVICES.read().unwrap().deref() { + for dev in &DEVICES.read().unwrap().list { if path_contains(dev.name(), name) || path_contains(name, dev.name()) { return true; } @@ -87,21 +86,119 @@ pub fn dev_exists>(name: N) -> bool { /// An implementation of `devfs_mount` structure. pub struct DevFs { - idx: u32, // dm_idx - root: Arc, // dm_rootdir + index: usize, // dm_idx + root: Arc, // dm_rootdir + generation: Mutex, // dm_generation } impl DevFs { const DEVFS_ROOTINO: i32 = 2; - /// See `devfs_vmkdir` on the PS4 for a reference. - fn mkdir>( - name: N, - inode: i32, - parent: Option>, - ) -> Arc { - use self::dirent::Dirent; + /// See `devfs_populate` on the PS4 for a reference. + fn populate(&self) { + // Check if our data already latest. + let mut gen = self.generation.lock().unwrap(); + let devices = DEVICES.read().unwrap(); + if *gen == devices.generation { + return; + } + + // Populate our data. + for dev in &devices.list { + // Check if we already populated this device. + let dirents = dev.dirents(); + + if let Some(dirent) = dirents.get(self.index).and_then(|e| e.as_ref()) { + // If there is a strong reference that mean it is our dirent. + if dirent.strong_count() != 0 { + continue; + } + } + + drop(dirents); + + // Create directories along the path. + let mut dir = self.root.clone(); + let mut name = dev.name(); + + while let Some(i) = name.find('/') { + // Check if already exists. + let n = &name[..i]; + let mut c = dir.children_mut(); + let d = match c.iter().find(|&c| c.dirent().name() == n) { + Some(c) => { + if c.dirent().ty() == DirentType::Link { + todo!("devfs_populate with DT_LNK children"); + } + + // Not sure why FreeBSD does not check if a directory? + c.clone() + } + None => { + // TODO: Implement devfs_rules_apply. + let d = Self::mkdir(n, 0, Some(&dir)); + c.push(d.clone()); + d + } + }; + + drop(c); + + // Move to next component. + dir = d; + name = &name[(i + 1)..]; + } + + // Check if a link. + let mut children = dir.children_mut(); + + if children + .iter() + .find(|&c| c.dirent().ty() == DirentType::Link && c.dirent().name() == name) + .is_some() + { + todo!("devfs_populate with DT_LNK children"); + } + + // Check if alias. + let (ty, uid, gid, mode) = if dev.flags().intersects(DeviceFlags::SI_ALIAS) { + todo!("devfs_populate with SI_ALIAS"); + } else { + (DirentType::Character, dev.uid(), dev.gid(), dev.mode()) + }; + + // Create a new entry. + let dirent = Arc::new(Dirent::new( + ty, + dev.inode(), + uid, + gid, + mode, + Some(Arc::downgrade(&dir)), + DirentFlags::empty(), + name, + )); + + children.push(dirent.clone()); + drop(children); + + // TODO: Implement devfs_rules_apply. + let mut dirents = dev.dirents_mut(); + + if self.index >= dirents.len() { + dirents.resize(self.index + 1, None); + } + + dirents[self.index] = Some(Arc::downgrade(&dirent)); + } + + *gen = devices.generation; + } + + /// Partial implementation of `devfs_vmkdir`. The main different is this function does not add + /// the created directory to `parent` and does not run `devfs_rules_apply`. + fn mkdir>(name: N, inode: i32, parent: Option<&Arc>) -> Arc { // Create the directory. let dir = Arc::new(Dirent::new( DirentType::Directory, @@ -110,6 +207,8 @@ impl DevFs { } else { inode }, + 0, + 0, 0555, None, DirentFlags::empty(), @@ -121,6 +220,8 @@ impl DevFs { DirentType::Directory, 0, 0, + 0, + 0, Some(Arc::downgrade(&dir)), DirentFlags::DE_DOT, ".", @@ -133,27 +234,24 @@ impl DevFs { DirentType::Directory, 0, 0, - Some(Arc::downgrade(parent.as_ref().unwrap_or(&dir))), + 0, + 0, + Some(Arc::downgrade(parent.unwrap_or(&dir))), DirentFlags::DE_DOTDOT, "..", ); dir.children_mut().push(Arc::new(dd)); - - if let Some(p) = parent { - // TODO: Implement devfs_rules_apply. - p.children_mut().push(dir.clone()); - } - dir } /// See `devfs_allocv` on the PS4 for a reference. - fn alloc_vnode(mnt: &Arc, ent: &Arc) -> Arc { + fn alloc_vnode(mnt: &Arc, ent: &Arc) -> Arc { // Get type. let ty = match ent.dirent().ty() { DirentType::Character => todo!("devfs_allocv with DT_CHR"), DirentType::Directory => VnodeType::Directory(ent.inode() == Self::DEVFS_ROOTINO), + DirentType::Link => todo!("devfs_allocv with DT_LNK"), }; // Create vnode. @@ -180,6 +278,19 @@ bitflags! { } } +/// List of devices in the system. +struct Devices { + list: Vec>, // cdevp_list + generation: u32, // devfs_generation +} + +impl Devices { + fn push(&mut self, d: Arc) { + self.list.push(d); + self.generation += 1; + } +} + /// Represents an error when [`make_dev()`] is failed. #[derive(Debug, Error)] pub enum MakeDevError { @@ -210,11 +321,12 @@ fn mount(mount: &mut Mount, _: HashMap>) -> Result<(), Box< drop(flags); // Set mount data. - let idx = DEVFS.fetch_add(1, Ordering::Relaxed); + let index = DEVFS_INDEX.fetch_add(1, Ordering::Relaxed); mount.set_data(Arc::new(DevFs { - idx: idx.try_into().unwrap(), + index, root: DevFs::mkdir("", DevFs::DEVFS_ROOTINO, None), + generation: Mutex::new(0), })); Ok(()) @@ -245,8 +357,9 @@ impl Errno for MountError { } pub(super) static DEVFS_OPS: FsOps = FsOps { mount, root }; -static DEVFS: AtomicI32 = AtomicI32::new(0); // TODO: Use a proper implementation. +static DEVFS_INDEX: AtomicUsize = AtomicUsize::new(0); // TODO: Use a proper implementation. static INODE: AtomicU32 = AtomicU32::new(3); // TODO: Same here. -static DEVICES: RwLock>> = RwLock::new(Vec::new()); // cdevp_list -static GENERATION: AtomicU32 = AtomicU32::new(0); // devfs_generation -static VNODE_OPS: VopVector = VopVector {}; +static DEVICES: RwLock = RwLock::new(Devices { + list: Vec::new(), + generation: 0, +}); diff --git a/src/kernel/src/fs/dev/vnode.rs b/src/kernel/src/fs/dev/vnode.rs new file mode 100644 index 000000000..2f9375098 --- /dev/null +++ b/src/kernel/src/fs/dev/vnode.rs @@ -0,0 +1,22 @@ +use crate::errno::Errno; +use crate::fs::{DevFs, Vnode, VopVector, DEFAULT_VNODEOPS}; +use std::sync::Arc; + +pub static VNODE_OPS: VopVector = VopVector { + default: Some(&DEFAULT_VNODEOPS), + lookup: Some(lookup), +}; + +fn lookup(dir: &Arc) -> Result, Box> { + // Populate devices. + let fs = dir + .fs() + .data() + .and_then(|v| v.downcast_ref::()) + .unwrap(); + + fs.populate(); + + // TODO: Implement the remaining lookup. + todo!() +} diff --git a/src/kernel/src/fs/dirent.rs b/src/kernel/src/fs/dirent.rs index cb53a7fb7..f37a191cd 100644 --- a/src/kernel/src/fs/dirent.rs +++ b/src/kernel/src/fs/dirent.rs @@ -15,6 +15,10 @@ impl Dirent { pub fn ty(&self) -> DirentType { self.ty } + + pub fn name(&self) -> &str { + self.name.as_ref() + } } /// Type of [`Dirent`]. @@ -22,6 +26,7 @@ impl Dirent { pub enum DirentType { Character, // DT_CHR Directory, // DT_DIR + Link, // DT_LNK } impl DirentType { @@ -29,6 +34,7 @@ impl DirentType { match self { Self::Character => 2, Self::Directory => 4, + Self::Link => 10, } } } diff --git a/src/kernel/src/fs/host/mod.rs b/src/kernel/src/fs/host/mod.rs index bd94fa6c6..e8263a6f5 100644 --- a/src/kernel/src/fs/host/mod.rs +++ b/src/kernel/src/fs/host/mod.rs @@ -1,6 +1,6 @@ +use self::vnode::VNODE_OPS; use super::{ FsItem, FsOps, HostDir, HostFile, Mount, MountFlags, VPath, VPathBuf, Vnode, VnodeType, - VopVector, }; use crate::errno::Errno; use param::Param; @@ -9,6 +9,8 @@ use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; +mod vnode; + /// Mount data for host FS. /// /// We subtitute `exfatfs` with this because the root FS on the PS4 is exFAT. That mean we must @@ -179,4 +181,3 @@ enum MountSource { } pub(super) static HOST_OPS: FsOps = FsOps { mount, root }; -static VNODE_OPS: VopVector = VopVector {}; diff --git a/src/kernel/src/fs/host/vnode.rs b/src/kernel/src/fs/host/vnode.rs new file mode 100644 index 000000000..e0901b4a3 --- /dev/null +++ b/src/kernel/src/fs/host/vnode.rs @@ -0,0 +1,12 @@ +use crate::errno::Errno; +use crate::fs::{Vnode, VopVector, DEFAULT_VNODEOPS}; +use std::sync::Arc; + +pub static VNODE_OPS: VopVector = VopVector { + default: Some(&DEFAULT_VNODEOPS), + lookup: Some(lookup), +}; + +fn lookup(_: &Arc) -> Result, Box> { + todo!() +} diff --git a/src/kernel/src/fs/mod.rs b/src/kernel/src/fs/mod.rs index a56fdfa03..ddd1a6801 100644 --- a/src/kernel/src/fs/mod.rs +++ b/src/kernel/src/fs/mod.rs @@ -1,4 +1,3 @@ -pub use self::cdev::*; pub use self::dev::*; pub use self::dirent::*; pub use self::file::*; @@ -24,7 +23,6 @@ use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::Arc; use thiserror::Error; -mod cdev; mod dev; mod dirent; mod file; diff --git a/src/kernel/src/fs/vnode.rs b/src/kernel/src/fs/vnode.rs index 3da2179b4..caad5b6c6 100644 --- a/src/kernel/src/fs/vnode.rs +++ b/src/kernel/src/fs/vnode.rs @@ -1,6 +1,10 @@ use super::Mount; +use crate::errno::{Errno, ENOTDIR}; use gmtx::{Gutex, GutexGroup, GutexWriteGuard}; use std::any::Any; +use std::error::Error; +use std::fmt::{Display, Formatter}; +use std::num::NonZeroI32; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; @@ -38,6 +42,10 @@ impl Vnode { } } + pub fn fs(&self) -> &Arc { + &self.fs + } + pub fn item_mut(&self) -> GutexWriteGuard>> { self.item.write() } @@ -57,6 +65,33 @@ pub enum VnodeType { /// An implementation of `vop_vector` structure. #[derive(Debug)] -pub struct VopVector {} +pub struct VopVector { + pub default: Option<&'static Self>, // vop_default + pub lookup: Option) -> Result, Box>>, // vop_lookup +} + +/// Represents an error when [`DEFAULT_VNODEOPS`] is failed. +#[derive(Debug)] +struct DefaultError(NonZeroI32); + +impl Error for DefaultError {} + +impl Errno for DefaultError { + fn errno(&self) -> NonZeroI32 { + self.0 + } +} + +impl Display for DefaultError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("not implemented") + } +} + +/// An implementation of `default_vnodeops`. +pub static DEFAULT_VNODEOPS: VopVector = VopVector { + default: None, + lookup: Some(|_| Err(Box::new(DefaultError(ENOTDIR)))), +}; static ACTIVE: AtomicUsize = AtomicUsize::new(0); // numvnodes