vfs: add ctime/mtime/atime

This commit is contained in:
Mark Poliakov 2024-12-22 15:22:40 +02:00
parent 4acb148d0e
commit 76f1872764
18 changed files with 212 additions and 118 deletions

View File

@ -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 _,
}
}
}

View File

@ -149,7 +149,7 @@ impl<'a> Iterator for DirentIter<'a> {
impl DirectoryNode {
pub fn new(fs: Arc<Ext2Fs>, 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> {

View File

@ -17,7 +17,7 @@ pub struct RegularNode {
impl RegularNode {
pub fn new(fs: Arc<Ext2Fs>, 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> {

View File

@ -25,6 +25,7 @@ impl SymlinkNode {
cache: IrqSafeRwLock::new(Vec::new()),
},
NodeFlags::empty(),
None,
)
}

View File

@ -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<A: BlockAllocator> {
}
impl<A: BlockAllocator> DirectoryNode<A> {
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<A: BlockAllocator> DirectoryImpl for DirectoryNode<A> {
fn create_node(&self, _parent: &NodeRef, ty: FileType) -> Result<NodeRef, Error> {
match ty {
FileType::File => Ok(FileNode::<A>::new()),
FileType::Directory => Ok(DirectoryNode::<A>::new()),
FileType::File => Ok(FileNode::<A>::new(Metadata::now_root(FileMode::new(0o644)))),
FileType::Directory => Ok(DirectoryNode::<A>::new(Metadata::now_root(FileMode::new(
0o755,
)))),
_ => todo!(),
}
}

View File

@ -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<A: BlockAllocator> {
}
impl<A: BlockAllocator> FileNode<A> {
pub fn new() -> NodeRef {
pub fn new(metadata: Metadata) -> NodeRef {
Node::regular(
Self {
data: IrqSafeSpinlock::new(BVec::new()),
},
NodeFlags::IN_MEMORY_PROPS,
Some(metadata),
)
}
}

View File

@ -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<A: BlockAllocator> MemoryFilesystem<A> {
at: &NodeRef,
path: &Path,
create: bool,
mode: FileMode,
) -> Result<NodeRef, Error> {
let access = unsafe { AccessToken::authorized() };
if path.is_empty() {
@ -87,7 +88,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
return Err(Error::DoesNotExist);
}
let node = DirectoryNode::<A>::new();
let node = DirectoryNode::<A>::new(Metadata::now_root(mode));
at.add_child(element, node.clone())?;
node
@ -102,15 +103,17 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
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<Self>, hdr: &TarEntry) -> Result<NodeRef, Error> {
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::<A>::new()),
FileType::Directory => Ok(DirectoryNode::<A>::new()),
FileType::File => Ok(FileNode::<A>::new(Metadata::now_root(mode))),
FileType::Directory => Ok(DirectoryNode::<A>::new(Metadata::now_root(mode))),
FileType::Symlink => {
let target = hdr.symlink_target()?;
Ok(fixed_path_symlink(target))
@ -120,7 +123,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
}
fn from_slice_internal(self: &Rc<Self>, tar_data: &'static [u8]) -> Result<NodeRef, Error> {
let root = DirectoryNode::<A>::new();
let root = DirectoryNode::<A>::new(Metadata::now_root(FileMode::new(0o755)));
// 1. Create paths in tar
for item in TarIterator::new(tar_data) {
@ -131,7 +134,7 @@ impl<A: BlockAllocator> MemoryFilesystem<A> {
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<A: BlockAllocator> MemoryFilesystem<A> {
};
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) };

View File

@ -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<S: Into<String>>(
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())?;

View File

@ -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<V: BytesAttributeOps> Attribute<V::Data> for BytesAttribute<V> {
true => FileMode::new(0o644),
};
Ok(Node::regular_kernel(
Ok(Node::regular(
BytesAttributeNode {
object: parent.clone(),
_pd: PhantomData::<V>,
},
NodeFlags::IN_MEMORY_PROPS,
mode,
Some(Metadata::now_root(mode)),
))
}

View File

@ -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<V: StringAttributeOps> Attribute<V::Data> for StringAttribute<V> {
true => FileMode::new(0o644),
};
Ok(Node::regular_kernel(
Ok(Node::regular(
StringAttributeNode {
object: parent.clone(),
_pd: PhantomData::<V>,
},
NodeFlags::IN_MEMORY_PROPS,
mode,
Some(Metadata::now_root(mode)),
))
}

View File

@ -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<D> {
impl<D> KObject<D> {
pub fn new(data: D) -> Arc<Self> {
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 })
}

View File

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

View File

@ -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<R: ReadFn + 'static>(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<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(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<String>) -> NodeRef {
target: target.into(),
},
NodeFlags::IN_MEMORY_PROPS,
Some(Metadata::now_root(FileMode::new(0o555))),
)
}

View File

@ -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<Metadata>,
size: Option<u64>,
}
@ -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<Metadata>) -> NodeRef {
Arc::new(Self {
data,
flags,
@ -169,16 +173,17 @@ impl Node {
pub(crate) fn pseudo_terminal_nodes(
master: &Arc<PseudoTerminalMaster>,
slave: &Arc<PseudoTerminalSlave>,
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<T: DirectoryImpl + 'static>(
pub fn directory<T: DirectoryImpl + 'static>(
data: T,
flags: NodeFlags,
mode: FileMode,
metadata: Option<Metadata>,
) -> 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<T: DirectoryImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
Self::directory_kernel(data, flags, FileMode::default_dir())
}
// /// Creates a new directory node with given [DirectoryImpl]
// pub fn directory<T: DirectoryImpl + 'static>(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<T: RegularImpl + 'static>(
pub fn regular<T: RegularImpl + 'static>(
data: T,
flags: NodeFlags,
mode: FileMode,
metadata: Option<Metadata>,
) -> 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<T: RegularImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
Self::regular_kernel(data, flags, FileMode::default_file())
}
// /// Creates a new file node with given [RegularImpl]
// pub fn regular<T: RegularImpl + 'static>(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<dyn BlockDevice>, flags: NodeFlags, mode: FileMode) -> NodeRef {
pub fn block(device: Arc<dyn BlockDevice>, 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<dyn CharDevice>, flags: NodeFlags, mode: FileMode) -> NodeRef {
pub fn char(device: Arc<dyn CharDevice>, 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<T: SymlinkImpl + 'static>(data: T, flags: NodeFlags) -> NodeRef {
pub fn symlink<T: SymlinkImpl + 'static>(
data: T,
flags: NodeFlags,
metadata: Option<Metadata>,
) -> 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::<AnyData>();
assert_eq!(r.value, 1234);

View File

@ -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<Metadata, Error> {
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)

View File

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

View File

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

View File

@ -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<u32>;
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<u32> {
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<chrono::Utc> {
let timestamp = time.duration_since(SystemTime::UNIX_EPOCH).unwrap();
chrono::DateTime::from_timestamp(timestamp.as_secs() as _, 0).unwrap()
}
fn time_now() -> chrono::DateTime<chrono::Utc> {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
chrono::DateTime::from_timestamp(now.as_secs() as _, 0).unwrap()
}
impl DisplayBit for Option<Metadata> {
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<Metadata> {
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(<Metadata as MetadataImpl>::inode);
let ino = self
.attrs
.as_ref()
.and_then(<Metadata as MetadataImpl>::inode);
if opts.inodes {
if let Some(ino) = ino {
write!(f, "{ino:<8} ")?;