diff --git a/kernel/driver/fs/ext2/src/data.rs b/kernel/driver/fs/ext2/src/data.rs index ba7ad92d..9ef22754 100644 --- a/kernel/driver/fs/ext2/src/data.rs +++ b/kernel/driver/fs/ext2/src/data.rs @@ -5,6 +5,7 @@ use libk::vfs::Metadata; use yggdrasil_abi::{ bitflags, io::{FileMode, FileType, GroupId, UserId}, + time::SystemTime, }; use crate::Ext2Fs; @@ -226,6 +227,8 @@ impl Inode { inode: Some(ino), block_count: self.blocks(fs) as _, block_size: fs.block_size as _, + ctime: self.ctime as _, + mtime: self.mtime as _, } } } diff --git a/kernel/driver/fs/ext2/src/dir.rs b/kernel/driver/fs/ext2/src/dir.rs index ffcaf54e..9f6b8a6b 100644 --- a/kernel/driver/fs/ext2/src/dir.rs +++ b/kernel/driver/fs/ext2/src/dir.rs @@ -149,7 +149,7 @@ impl<'a> Iterator for DirentIter<'a> { impl DirectoryNode { pub fn new(fs: Arc, inode: InodeAccess) -> NodeRef { - Node::directory(Self { fs, inode }, NodeFlags::empty()) + Node::directory(Self { fs, inode }, NodeFlags::empty(), None) } pub async fn create( @@ -166,7 +166,7 @@ impl DirectoryNode { this.create_entry("..", parent).await?; } - Ok(Node::directory(this, NodeFlags::empty())) + Ok(Node::directory(this, NodeFlags::empty(), None)) } async fn create_entry(&self, name: &str, ino: u32) -> Result<(), Error> { diff --git a/kernel/driver/fs/ext2/src/file.rs b/kernel/driver/fs/ext2/src/file.rs index bb329e69..cffc8e9b 100644 --- a/kernel/driver/fs/ext2/src/file.rs +++ b/kernel/driver/fs/ext2/src/file.rs @@ -17,7 +17,7 @@ pub struct RegularNode { impl RegularNode { pub fn new(fs: Arc, inode: InodeAccess) -> NodeRef { - Node::regular(Self { fs, inode }, NodeFlags::empty()) + Node::regular(Self { fs, inode }, NodeFlags::empty(), None) } async fn resize(&self, new_size: u64) -> Result<(), Error> { diff --git a/kernel/driver/fs/ext2/src/symlink.rs b/kernel/driver/fs/ext2/src/symlink.rs index 03616066..610a5dbf 100644 --- a/kernel/driver/fs/ext2/src/symlink.rs +++ b/kernel/driver/fs/ext2/src/symlink.rs @@ -25,6 +25,7 @@ impl SymlinkNode { cache: IrqSafeRwLock::new(Vec::new()), }, NodeFlags::empty(), + None, ) } diff --git a/kernel/driver/fs/memfs/src/dir.rs b/kernel/driver/fs/memfs/src/dir.rs index 553b542f..cb0a5359 100644 --- a/kernel/driver/fs/memfs/src/dir.rs +++ b/kernel/driver/fs/memfs/src/dir.rs @@ -1,7 +1,12 @@ use core::marker::PhantomData; -use libk::vfs::{CommonImpl, DirectoryImpl, DirectoryOpenPosition, Node, NodeFlags, NodeRef}; -use yggdrasil_abi::{error::Error, io::FileType}; +use libk::vfs::{ + CommonImpl, DirectoryImpl, DirectoryOpenPosition, Metadata, Node, NodeFlags, NodeRef, +}; +use yggdrasil_abi::{ + error::Error, + io::{FileMode, FileType}, +}; use crate::{block::BlockAllocator, file::FileNode}; @@ -10,10 +15,11 @@ pub(crate) struct DirectoryNode { } impl DirectoryNode { - pub fn new() -> NodeRef { + pub fn new(metadata: Metadata) -> NodeRef { Node::directory( Self { _pd: PhantomData }, NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS, + Some(metadata), ) } } @@ -27,8 +33,10 @@ impl DirectoryImpl for DirectoryNode { fn create_node(&self, _parent: &NodeRef, ty: FileType) -> Result { match ty { - FileType::File => Ok(FileNode::::new()), - FileType::Directory => Ok(DirectoryNode::::new()), + FileType::File => Ok(FileNode::::new(Metadata::now_root(FileMode::new(0o644)))), + FileType::Directory => Ok(DirectoryNode::::new(Metadata::now_root(FileMode::new( + 0o755, + )))), _ => todo!(), } } diff --git a/kernel/driver/fs/memfs/src/file.rs b/kernel/driver/fs/memfs/src/file.rs index bbb8e6a6..724d9a61 100644 --- a/kernel/driver/fs/memfs/src/file.rs +++ b/kernel/driver/fs/memfs/src/file.rs @@ -1,6 +1,6 @@ use core::any::Any; -use libk::vfs::{CommonImpl, InstanceData, Node, NodeFlags, NodeRef, RegularImpl}; +use libk::vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl}; use libk_util::sync::IrqSafeSpinlock; use yggdrasil_abi::{error::Error, io::OpenOptions}; @@ -11,12 +11,13 @@ pub(crate) struct FileNode { } impl FileNode { - pub fn new() -> NodeRef { + pub fn new(metadata: Metadata) -> NodeRef { Node::regular( Self { data: IrqSafeSpinlock::new(BVec::new()), }, NodeFlags::IN_MEMORY_PROPS, + Some(metadata), ) } } diff --git a/kernel/driver/fs/memfs/src/lib.rs b/kernel/driver/fs/memfs/src/lib.rs index c0cf2f5e..3ac9b4ed 100644 --- a/kernel/driver/fs/memfs/src/lib.rs +++ b/kernel/driver/fs/memfs/src/lib.rs @@ -10,7 +10,7 @@ use alloc::rc::Rc; use block::BlockAllocator; use dir::DirectoryNode; use file::FileNode; -use libk::vfs::{impls::fixed_path_symlink, AccessToken, NodeRef}; +use libk::vfs::{impls::fixed_path_symlink, AccessToken, Metadata, NodeRef}; use tar::TarEntry; use yggdrasil_abi::{ error::Error, @@ -68,6 +68,7 @@ impl MemoryFilesystem { at: &NodeRef, path: &Path, create: bool, + mode: FileMode, ) -> Result { let access = unsafe { AccessToken::authorized() }; if path.is_empty() { @@ -87,7 +88,7 @@ impl MemoryFilesystem { return Err(Error::DoesNotExist); } - let node = DirectoryNode::::new(); + let node = DirectoryNode::::new(Metadata::now_root(mode)); at.add_child(element, node.clone())?; node @@ -102,15 +103,17 @@ impl MemoryFilesystem { Ok(node) } else { assert!(node.is_directory()); - Self::make_path(&node, rest, create) + Self::make_path(&node, rest, create, mode) } } fn create_node_initial(self: &Rc, hdr: &TarEntry) -> Result { let kind = hdr.node_kind(); + let mode = usize::from(&hdr.mode); + let mode = FileMode::new(0o777 & (mode as u32)); match kind { - FileType::File => Ok(FileNode::::new()), - FileType::Directory => Ok(DirectoryNode::::new()), + FileType::File => Ok(FileNode::::new(Metadata::now_root(mode))), + FileType::Directory => Ok(DirectoryNode::::new(Metadata::now_root(mode))), FileType::Symlink => { let target = hdr.symlink_target()?; Ok(fixed_path_symlink(target)) @@ -120,7 +123,7 @@ impl MemoryFilesystem { } fn from_slice_internal(self: &Rc, tar_data: &'static [u8]) -> Result { - let root = DirectoryNode::::new(); + let root = DirectoryNode::::new(Metadata::now_root(FileMode::new(0o755))); // 1. Create paths in tar for item in TarIterator::new(tar_data) { @@ -131,7 +134,7 @@ impl MemoryFilesystem { let path = Path::from_str(hdr.name.as_str()?.trim_matches('/')); let (dirname, filename) = path.split_right(); - let parent = Self::make_path(&root, dirname, true)?; + let parent = Self::make_path(&root, dirname, true, FileMode::new(0o755))?; let node = self.create_node_initial(hdr)?; parent.add_child(filename, node)?; @@ -144,7 +147,7 @@ impl MemoryFilesystem { }; let path = Path::from_str(hdr.name.as_str()?.trim_matches('/')); - let node = Self::make_path(&root, path, false)?; + let node = Self::make_path(&root, path, false, FileMode::empty())?; assert_eq!(node.ty(), hdr.node_kind()); let uid = unsafe { UserId::from_raw(usize::from(&hdr.uid) as u32) }; diff --git a/kernel/libk/src/fs/devfs.rs b/kernel/libk/src/fs/devfs.rs index b0b4de93..c42c41cd 100644 --- a/kernel/libk/src/fs/devfs.rs +++ b/kernel/libk/src/fs/devfs.rs @@ -8,7 +8,7 @@ use crate::{ device::{block::BlockDevice, char::CharDevice}, vfs::{ impls::{fixed_path_symlink, MemoryDirectory}, - AccessToken, Node, NodeFlags, NodeRef, + AccessToken, Metadata, Node, NodeFlags, NodeRef, }, }; @@ -46,7 +46,7 @@ pub fn add_named_char_device( ) -> Result<(), Error> { log::info!("Add char device: {}", name); - let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS, mode); + let node = Node::char(dev, NodeFlags::IN_MEMORY_PROPS, Metadata::now_root(mode)); DEVFS_ROOT.get().add_child(name, node) } @@ -60,7 +60,7 @@ pub fn add_named_block_device>( let name = name.into(); log::info!("Add block device: {}", name); - let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS, mode); + let node = Node::block(dev, NodeFlags::IN_MEMORY_PROPS, Metadata::now_root(mode)); DEVFS_ROOT.get().add_child(name, node.clone())?; diff --git a/kernel/libk/src/fs/sysfs/attribute/bytes.rs b/kernel/libk/src/fs/sysfs/attribute/bytes.rs index 86c08c6f..6cfc2153 100644 --- a/kernel/libk/src/fs/sysfs/attribute/bytes.rs +++ b/kernel/libk/src/fs/sysfs/attribute/bytes.rs @@ -8,7 +8,7 @@ use yggdrasil_abi::{ use crate::{ fs::sysfs::object::KObject, - vfs::{CommonImpl, InstanceData, Node, NodeFlags, NodeRef, RegularImpl}, + vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl}, }; use super::Attribute; @@ -112,13 +112,13 @@ impl Attribute for BytesAttribute { true => FileMode::new(0o644), }; - Ok(Node::regular_kernel( + Ok(Node::regular( BytesAttributeNode { object: parent.clone(), _pd: PhantomData::, }, NodeFlags::IN_MEMORY_PROPS, - mode, + Some(Metadata::now_root(mode)), )) } diff --git a/kernel/libk/src/fs/sysfs/attribute/string.rs b/kernel/libk/src/fs/sysfs/attribute/string.rs index e025312f..1442124b 100644 --- a/kernel/libk/src/fs/sysfs/attribute/string.rs +++ b/kernel/libk/src/fs/sysfs/attribute/string.rs @@ -13,7 +13,7 @@ use yggdrasil_abi::{ use crate::{ fs::sysfs::object::KObject, - vfs::{CommonImpl, InstanceData, Node, NodeFlags, NodeRef, RegularImpl}, + vfs::{CommonImpl, InstanceData, Metadata, Node, NodeFlags, NodeRef, RegularImpl}, }; use super::Attribute; @@ -180,13 +180,13 @@ impl Attribute for StringAttribute { true => FileMode::new(0o644), }; - Ok(Node::regular_kernel( + Ok(Node::regular( StringAttributeNode { object: parent.clone(), _pd: PhantomData::, }, NodeFlags::IN_MEMORY_PROPS, - mode, + Some(Metadata::now_root(mode)), )) } diff --git a/kernel/libk/src/fs/sysfs/object.rs b/kernel/libk/src/fs/sysfs/object.rs index 84e31f5f..14d36e5d 100644 --- a/kernel/libk/src/fs/sysfs/object.rs +++ b/kernel/libk/src/fs/sysfs/object.rs @@ -2,7 +2,7 @@ use alloc::{string::String, sync::Arc}; use libk_util::OneTimeInit; use yggdrasil_abi::{error::Error, io::FileMode}; -use crate::vfs::{impls::MemoryDirectory, Node, NodeFlags}; +use crate::vfs::{impls::MemoryDirectory, Metadata, Node, NodeFlags}; use super::attribute::Attribute; @@ -13,10 +13,10 @@ pub struct KObject { impl KObject { pub fn new(data: D) -> Arc { - let node = Node::directory_kernel( + let node = Node::directory( MemoryDirectory, NodeFlags::IN_MEMORY_SIZE | NodeFlags::IN_MEMORY_PROPS, - FileMode::new(0o555), + Some(Metadata::now_root(FileMode::new(0o555))), ); Arc::new(Self { data, node }) } diff --git a/kernel/libk/src/vfs/file/mod.rs b/kernel/libk/src/vfs/file/mod.rs index 06a763a5..e6fd287c 100644 --- a/kernel/libk/src/vfs/file/mod.rs +++ b/kernel/libk/src/vfs/file/mod.rs @@ -21,8 +21,8 @@ use libk_util::{ use yggdrasil_abi::{ error::Error, io::{ - DeviceRequest, DirectoryEntry, OpenOptions, PipeOptions, RawFd, SeekFrom, TerminalOptions, - TerminalSize, TimerOptions, + DeviceRequest, DirectoryEntry, FileMode, OpenOptions, PipeOptions, RawFd, SeekFrom, + TerminalOptions, TerminalSize, TimerOptions, }, process::{ProcessWait, WaitFlags}, }; @@ -56,6 +56,7 @@ use super::{ pid::PidFile, pty::{self, PseudoTerminalMaster, PseudoTerminalSlave}, socket::SocketWrapper, + Metadata, }; mod device; @@ -157,7 +158,8 @@ impl File { let (master, slave) = pty::create(config, size)?; let master = Arc::new(master); let slave = Arc::new(slave); - let (master_node, slave_node) = Node::pseudo_terminal_nodes(&master, &slave); + let (master_node, slave_node) = + Node::pseudo_terminal_nodes(&master, &slave, Metadata::now_root(FileMode::new(0o644))); Ok(( Arc::new(Self::PtyMaster(TerminalHalfWrapper { blocking: AtomicBool::new(true), diff --git a/kernel/libk/src/vfs/node/impls.rs b/kernel/libk/src/vfs/node/impls.rs index 54bca4cf..ae697275 100644 --- a/kernel/libk/src/vfs/node/impls.rs +++ b/kernel/libk/src/vfs/node/impls.rs @@ -10,14 +10,14 @@ use alloc::{ use libk_util::sync::IrqSafeSpinlock; use yggdrasil_abi::{ error::Error, - io::{FileMode, FileType, OpenOptions}, + io::{FileMode, FileType, GroupId, OpenOptions, UserId}, }; use crate::vfs::{DirectoryOpenPosition, InstanceData}; use super::{ - traits::HardlinkImpl, CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl, - SymlinkImpl, + traits::HardlinkImpl, CommonImpl, DirectoryImpl, Metadata, Node, NodeFlags, NodeRef, + RegularImpl, SymlinkImpl, }; trait SliceRead { @@ -126,7 +126,11 @@ where } pub fn new_node(read: R) -> NodeRef { - Node::regular(Self::new(read), NodeFlags::IN_MEMORY_PROPS) + Node::regular( + Self::new(read), + NodeFlags::IN_MEMORY_PROPS, + Some(Metadata::now_root(FileMode::new(0o444))), + ) } } @@ -225,7 +229,11 @@ where } pub fn new_node(read: R, write: W) -> NodeRef { - Node::regular(Self::new(read, write), NodeFlags::IN_MEMORY_PROPS) + Node::regular( + Self::new(read, write), + NodeFlags::IN_MEMORY_PROPS, + Some(Metadata::now_root(FileMode::new(0o644))), + ) } } @@ -361,10 +369,10 @@ where impl MemoryDirectory { /// Creates a [MemoryDirectory] with no children pub fn empty(mode: FileMode) -> NodeRef { - Node::directory_kernel( + Node::directory( MemoryDirectory, NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE, - mode, + Some(Metadata::now_root(mode)), ) } } @@ -444,10 +452,10 @@ where /// Creates a read-only node with given read function pub fn read_fn_node(read: R) -> NodeRef { - Node::regular_kernel( + Node::regular( ReadOnlyFnNode::new(read), NodeFlags::IN_MEMORY_PROPS, - FileMode::USER_READ | FileMode::GROUP_READ | FileMode::OTHER_READ, + Some(Metadata::now_root(FileMode::new(0o444))), ) } @@ -456,6 +464,7 @@ pub fn mdir, I: IntoIterator>(it: I) -> Nod let dir = Node::directory( MemoryDirectory, NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE, + Some(Metadata::now_root(FileMode::new(0o555))), ); for (name, node) in it { dir.add_child(name, node).unwrap(); @@ -469,6 +478,7 @@ pub fn fixed_path_symlink(target: impl Into) -> NodeRef { target: target.into(), }, NodeFlags::IN_MEMORY_PROPS, + Some(Metadata::now_root(FileMode::new(0o555))), ) } diff --git a/kernel/libk/src/vfs/node/mod.rs b/kernel/libk/src/vfs/node/mod.rs index 234e5438..42ef67c0 100644 --- a/kernel/libk/src/vfs/node/mod.rs +++ b/kernel/libk/src/vfs/node/mod.rs @@ -15,6 +15,7 @@ use yggdrasil_abi::{ bitflags, error::Error, io::{FileMode, FileType, GroupId, UserId}, + time::SystemTime, }; mod access; @@ -28,9 +29,12 @@ mod tree; pub use access::AccessToken; pub use traits::{CommonImpl, DirectoryImpl, RegularImpl, SymlinkImpl}; -use crate::device::{ - block::{BlockDevice, BlockDeviceFile}, - char::{CharDevice, CharDeviceFile}, +use crate::{ + device::{ + block::{BlockDevice, BlockDeviceFile}, + char::{CharDevice, CharDeviceFile}, + }, + time::real_time, }; use super::{ @@ -114,10 +118,14 @@ pub struct Metadata { pub block_size: u64, /// Size of the node (without metadata) in units of `block_size` pub block_count: u64, + /// Creation time (in seconds) + pub ctime: u64, + /// Modification time (in seconds) + pub mtime: u64, } struct PropertyCache { - metadata: Metadata, + metadata: Option, size: Option, } @@ -130,31 +138,27 @@ pub struct Node { } impl Metadata { - pub(crate) const fn default_dir() -> Metadata { + pub fn now(uid: UserId, gid: GroupId, mode: FileMode) -> Metadata { + let now = real_time().seconds; Metadata { - uid: UserId::root(), - gid: GroupId::root(), - mode: FileMode::new(0o755), - block_size: 0, - block_count: 0, + mode, + uid, + gid, + ctime: now, + mtime: now, inode: None, + block_size: 4096, + block_count: 0, } } - pub(crate) const fn default_file() -> Metadata { - Metadata { - uid: UserId::root(), - gid: GroupId::root(), - mode: FileMode::new(0o644), - block_size: 0, - block_count: 0, - inode: None, - } + pub fn now_root(mode: FileMode) -> Metadata { + Self::now(UserId::root(), GroupId::root(), mode) } } impl Node { - fn new(data: NodeImpl, flags: NodeFlags, metadata: Metadata) -> NodeRef { + fn new(data: NodeImpl, flags: NodeFlags, metadata: Option) -> NodeRef { Arc::new(Self { data, flags, @@ -169,16 +173,17 @@ impl Node { pub(crate) fn pseudo_terminal_nodes( master: &Arc, slave: &Arc, + metadata: Metadata, ) -> (NodeRef, NodeRef) { let master = Self::new( NodeImpl::PseudoTerminalMaster(Arc::downgrade(master)), NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE, - Metadata::default_file(), + Some(metadata), ); let slave = Self::new( NodeImpl::PseudoTerminalSlave(Arc::downgrade(slave)), NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE, - Metadata::default_file(), + Some(metadata), ); master.props.lock().size = Some(0); @@ -188,85 +193,69 @@ impl Node { } /// Creates a new directory node with given [DirectoryImpl] and permissions - pub fn directory_kernel( + pub fn directory( data: T, flags: NodeFlags, - mode: FileMode, + metadata: Option, ) -> NodeRef { let data = NodeImpl::Directory(DirectoryData { imp: Box::new(data), mountpoint: IrqSafeSpinlock::new(None), children: IrqSafeSpinlock::new(Vec::new()), }); - Self::new( - data, - flags, - Metadata { - mode, - ..Metadata::default_dir() - }, - ) + Self::new(data, flags, metadata) } - /// Creates a new directory node with given [DirectoryImpl] - pub fn directory(data: T, flags: NodeFlags) -> NodeRef { - Self::directory_kernel(data, flags, FileMode::default_dir()) - } + // /// Creates a new directory node with given [DirectoryImpl] + // pub fn directory(data: T, flags: NodeFlags) -> NodeRef { + // Self::directory_kernel(data, flags, FileMode::default_dir()) + // } /// Creates a new file node with given [RegularImpl] and permissions - pub fn regular_kernel( + pub fn regular( data: T, flags: NodeFlags, - mode: FileMode, + metadata: Option, ) -> NodeRef { - Self::new( - NodeImpl::Regular(Box::new(data)), - flags, - Metadata { - mode, - ..Metadata::default_file() - }, - ) + Self::new(NodeImpl::Regular(Box::new(data)), flags, metadata) } - /// Creates a new file node with given [RegularImpl] - pub fn regular(data: T, flags: NodeFlags) -> NodeRef { - Self::regular_kernel(data, flags, FileMode::default_file()) - } + // /// Creates a new file node with given [RegularImpl] + // pub fn regular(data: T, flags: NodeFlags) -> NodeRef { + // Self::regular_kernel(data, flags, FileMode::default_file()) + // } /// Creates a new block device node with given [BlockDevice] - pub fn block(device: Arc, flags: NodeFlags, mode: FileMode) -> NodeRef { + pub fn block(device: Arc, flags: NodeFlags, metadata: Metadata) -> NodeRef { Self::new( NodeImpl::Block(BlockDeviceFile(device)), flags, - Metadata { - mode, - ..Metadata::default_file() - }, + Some(metadata), ) } /// Creates a new character device node with given [CharDevice] - pub fn char(device: Arc, flags: NodeFlags, mode: FileMode) -> NodeRef { + pub fn char(device: Arc, flags: NodeFlags, metadata: Metadata) -> NodeRef { Self::new( NodeImpl::Char(CharDeviceFile(device)), flags, - Metadata { - mode, - ..Metadata::default_file() - }, + Some(metadata), ) } /// Creates a new symbolic link node with given [SymlinkImpl] - pub fn symlink(data: T, flags: NodeFlags) -> NodeRef { + pub fn symlink( + data: T, + flags: NodeFlags, + metadata: Option, + ) -> NodeRef { Self::new( NodeImpl::Symlink(SymlinkData { target: IrqSafeRwLock::new(None), imp: Box::new(data), }), flags, - Metadata::default_file(), + metadata, ) } @@ -277,7 +266,7 @@ impl Node { imp: Box::new(data), }), NodeFlags::IN_MEMORY_SIZE, - Metadata::default_file(), + None, ) } @@ -495,8 +484,8 @@ mod tests { #[test] fn dir_cache_add() { - let d1 = Node::directory(DummyDirectory, NodeFlags::empty()); - let d2 = Node::directory(DummyDirectory, NodeFlags::empty()); + let d1 = Node::directory(DummyDirectory, NodeFlags::empty(), None); + let d2 = Node::directory(DummyDirectory, NodeFlags::empty(), None); let f1 = Node::regular(DummyFile, NodeFlags::empty()); assert!(Arc::ptr_eq(&f1.parent(), &f1)); @@ -516,7 +505,7 @@ mod tests { #[test] fn in_mem_dir_size_coherence() { - let d = Node::directory(DummyDirectory, NodeFlags::IN_MEMORY_SIZE); + let d = Node::directory(DummyDirectory, NodeFlags::IN_MEMORY_SIZE, None); for i in 0..10 { let name = format!("f{}", i); @@ -540,7 +529,7 @@ mod tests { } impl DirectoryImpl for AnyData {} - let d = Node::directory(AnyData { value: 1234 }, NodeFlags::empty()); + let d = Node::directory(AnyData { value: 1234 }, NodeFlags::empty(), None); let r = d.data_as_ref::(); assert_eq!(r.value, 1234); diff --git a/kernel/libk/src/vfs/node/ops.rs b/kernel/libk/src/vfs/node/ops.rs index 13f9a9d3..be57b151 100644 --- a/kernel/libk/src/vfs/node/ops.rs +++ b/kernel/libk/src/vfs/node/ops.rs @@ -1,5 +1,6 @@ use core::mem::MaybeUninit; +use libk_util::ext::OptionExt; use yggdrasil_abi::{ error::Error, io::{DeviceRequest, DirectoryEntry, FileMode, GroupId, OpenOptions, UserId}, @@ -172,7 +173,13 @@ impl Node { return Err(Error::InvalidOperation); } - let mut metadata = self.metadata()?; + let mut cache = self.props.lock(); + + let metadata = cache + .metadata + .get_or_try_insert_with(|| self.data_as_common().metadata(self))?; + + // let mut metadata = self.metadata()?; if let Some(uid) = uid { metadata.uid = uid; @@ -184,9 +191,6 @@ impl Node { metadata.mode = mode; } - // Update cached props - self.props.lock().metadata = metadata; - if !self.flags.contains(NodeFlags::IN_MEMORY_PROPS) { // Update permissions in the real node // todo!(); @@ -200,7 +204,7 @@ impl Node { pub fn metadata(self: &NodeRef) -> Result { if self.flags.contains(NodeFlags::IN_MEMORY_PROPS) { let props = self.props.lock(); - return Ok(props.metadata); + return Ok(props.metadata.unwrap()); } self.data_as_common().metadata(self) diff --git a/kernel/src/syscall/imp/sys_io.rs b/kernel/src/syscall/imp/sys_io.rs index e3ce0e3b..1a92437a 100644 --- a/kernel/src/syscall/imp/sys_io.rs +++ b/kernel/src/syscall/imp/sys_io.rs @@ -227,6 +227,10 @@ pub(crate) fn get_metadata( inode: metadata.inode, block_count: metadata.block_count, block_size: metadata.block_size, + ctime: metadata.ctime, + mtime: metadata.mtime, + // TODO atime? + atime: metadata.mtime, }); Ok(()) diff --git a/lib/abi/def/io.abi b/lib/abi/def/io.abi index a7cae3c8..6518775f 100644 --- a/lib/abi/def/io.abi +++ b/lib/abi/def/io.abi @@ -98,6 +98,13 @@ struct FileAttr { pub uid: UserId, /// Owner group ID pub gid: GroupId, + + /// Creation time + pub ctime: u64, + /// Last modification time + pub mtime: u64, + /// Last access time + pub atime: u64, } /// Raw directory entry representation diff --git a/userspace/sysutils/src/ls.rs b/userspace/sysutils/src/ls.rs index d9c2dbd5..7809e014 100644 --- a/userspace/sysutils/src/ls.rs +++ b/userspace/sysutils/src/ls.rs @@ -2,7 +2,13 @@ #![feature(let_chains, decl_macro)] use std::{ - cmp::Ordering, fmt, fs::{read_dir, FileType, Metadata}, io, path::{Path, PathBuf}, process::ExitCode + cmp::Ordering, + fmt, + fs::{read_dir, FileType, Metadata}, + io, + path::{Path, PathBuf}, + process::ExitCode, + time::SystemTime, }; #[cfg(unix)] @@ -10,6 +16,7 @@ use std::os::unix::fs::MetadataExt; #[cfg(target_os = "yggdrasil")] use std::os::yggdrasil::fs::MetadataExt; +use chrono::{Datelike, Timelike}; use clap::Parser; use humansize::{FormatSize, BINARY}; @@ -51,6 +58,7 @@ trait MetadataImpl { fn size(&self) -> u64; fn inode(&self) -> Option; fn mode(&self) -> Self::Mode; + fn mtime(&self) -> SystemTime; } impl DisplaySizeBit for u64 { @@ -130,8 +138,11 @@ impl MetadataImpl for Metadata { fn inode(&self) -> Option { MetadataExt::inode(self) } -} + fn mtime(&self) -> SystemTime { + self.modified().unwrap() + } +} #[cfg(any(unix, rust_analyzer))] struct UnixFileMode(u32); @@ -177,14 +188,56 @@ impl MetadataImpl for Metadata { fn mode(&self) -> Self::Mode { UnixFileMode(MetadataExt::mode(self)) } + + fn mtime(&self) -> SystemTime { + self.modified().unwrap() + } +} + +fn convert_file_time(time: SystemTime) -> chrono::DateTime { + let timestamp = time.duration_since(SystemTime::UNIX_EPOCH).unwrap(); + chrono::DateTime::from_timestamp(timestamp.as_secs() as _, 0).unwrap() +} + +fn time_now() -> chrono::DateTime { + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap(); + chrono::DateTime::from_timestamp(now.as_secs() as _, 0).unwrap() } impl DisplayBit for Option { fn display_bit(&self, opts: &Args, f: &mut fmt::Formatter<'_>) -> fmt::Result { + const MONTHS: &[&str] = &[ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + ]; + + let now = time_now(); let this = self.as_ref(); let ino = this.and_then(MetadataImpl::inode); let mode = this.map(MetadataImpl::mode); let size = this.map(MetadataImpl::size); + let mtime = this + .map(MetadataImpl::mtime) + .map(convert_file_time) + .map(|time| { + if time.year() == now.year() { + format!( + "{} {:02} {:02}:{:02}", + MONTHS[time.month0() as usize], + time.day(), + time.hour(), + time.minute() + ) + } else { + format!( + "{} {:02}, {:04}", + MONTHS[time.month0() as usize], + time.day(), + time.year() + ) + } + }); if let Some(mode) = mode { mode.display_bit(opts, f)?; @@ -195,7 +248,13 @@ impl DisplayBit for Option { if let Some(size) = size { write!(f, " {:>12}", size.display_size_with(opts))?; } else { - write!(f, " {:<12}", "???")?; + write!(f, " {:>12}", "???")?; + } + + if let Some(mtime) = mtime { + write!(f, " {:>14}", mtime)?; + } else { + write!(f, " {:<14}", "???")?; } if opts.inodes { @@ -229,7 +288,10 @@ impl DisplayBit for Entry { Ok(()) } else { - let ino = self.attrs.as_ref().and_then(::inode); + let ino = self + .attrs + .as_ref() + .and_then(::inode); if opts.inodes { if let Some(ino) = ino { write!(f, "{ino:<8} ")?;