Add timestamp support for ext2

This commit is contained in:
LI Qing 2024-07-01 16:50:07 +08:00 committed by Tate, Hongliang Tian
parent 608eba369c
commit 9377a43f8f
2 changed files with 189 additions and 76 deletions

View File

@ -14,8 +14,8 @@ use super::{
fs::Ext2,
indirect_block_cache::{IndirectBlock, IndirectBlockCache},
prelude::*,
utils::now,
};
use crate::time::clocks::RealTimeCoarseClock;
/// Max length of file name.
pub const MAX_FNAME_LEN: usize = 255;
@ -69,6 +69,10 @@ impl Inode {
let mut inner = inner.upgrade();
inner.resize(new_size)?;
let now = now();
inner.set_mtime(now);
inner.set_ctime(now);
Ok(())
}
@ -112,6 +116,10 @@ impl Inode {
self.fs().free_inode(inode.ino, is_dir).unwrap();
return Err(e);
}
let now = now();
inner.set_mtime(now);
inner.set_ctime(now);
Ok(inode)
}
@ -158,9 +166,15 @@ impl Inode {
let new_entry = DirEntry::new(inode.ino, name, inode_type);
let mut inner = inner.upgrade();
inner.append_entry(new_entry)?;
let now = now();
inner.set_mtime(now);
inner.set_ctime(now);
drop(inner);
inode.inner.write().inc_hard_links();
let mut inode_inner = inode.inner.write();
inode_inner.inc_hard_links();
inode_inner.set_ctime(now);
Ok(())
}
@ -193,6 +207,11 @@ impl Inode {
self_inner.remove_entry_at(name, offset)?;
file_inner.dec_hard_links();
let now = now();
self_inner.set_mtime(now);
self_inner.set_ctime(now);
file_inner.set_ctime(now);
Ok(())
}
@ -238,8 +257,12 @@ impl Inode {
}
self_inner.remove_entry_at(name, offset)?;
let now = now();
self_inner.set_mtime(now);
self_inner.set_ctime(now);
dir_inner.dec_hard_links();
dir_inner.dec_hard_links(); // For "."
Ok(())
}
@ -264,6 +287,12 @@ impl Inode {
let Some(dst_ino) = self_inner.get_entry_ino(new_name) else {
let mut self_inner = self_inner.upgrade();
self_inner.rename_entry_at(old_name, new_name, src_offset)?;
let now = now();
self_inner.set_mtime(now);
self_inner.set_ctime(now);
drop(self_inner);
src_inode.set_ctime(now);
return Ok(());
};
if src_inode.ino == dst_ino {
@ -320,10 +349,19 @@ impl Inode {
self_inner.remove_entry_at(new_name, dst_offset)?;
self_inner.rename_entry_at(old_name, new_name, src_offset)?;
let now = now();
self_inner.set_mtime(now);
self_inner.set_ctime(now);
dst_inner.dec_hard_links();
if dst_inode_typ == FileType::Dir {
dst_inner.dec_hard_links(); // For "."
}
dst_inner.set_ctime(now);
drop(self_inner);
drop(dst_inner);
src_inode.set_ctime(now);
Ok(())
}
@ -393,11 +431,22 @@ impl Inode {
self_inner.remove_entry_at(old_name, src_offset)?;
let new_entry = DirEntry::new(src_inode.ino, new_name, src_inode_typ);
target_inner.append_entry(new_entry)?;
let now = now();
self_inner.set_mtime(now);
self_inner.set_ctime(now);
target_inner.set_mtime(now);
target_inner.set_ctime(now);
if is_dir {
let mut src_inner = write_guards.pop().unwrap();
src_inner.set_parent_ino(target.ino)?;
src_inner.set_ctime(now);
} else {
drop(self_inner);
drop(target_inner);
src_inode.set_ctime(now);
}
return Ok(());
};
if src_inode.ino == dst_ino {
@ -470,44 +519,63 @@ impl Inode {
let new_entry = DirEntry::new(src_inode.ino, new_name, src_inode_typ);
target_inner.append_entry(new_entry)?;
dst_inner.dec_hard_links();
let now = now();
self_inner.set_mtime(now);
self_inner.set_ctime(now);
target_inner.set_mtime(now);
target_inner.set_ctime(now);
dst_inner.set_ctime(now);
if is_dir {
dst_inner.dec_hard_links(); // For "."
let mut src_inner = write_guards.pop().unwrap();
src_inner.set_parent_ino(target.ino)?;
src_inner.set_ctime(now);
} else {
drop(self_inner);
drop(target_inner);
drop(dst_inner);
src_inode.set_ctime(now);
}
Ok(())
}
pub fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
let inner = self.inner.read();
if inner.file_type() != FileType::Dir {
return_errno!(Errno::ENOTDIR);
}
if inner.hard_links() == 0 {
return_errno_with_message!(Errno::ENOENT, "dir removed");
}
let try_readdir = |offset: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
let dir_entry_reader = DirEntryReader::new(&inner.page_cache, *offset);
for (_, dir_entry) in dir_entry_reader {
visitor.visit(
dir_entry.name(),
dir_entry.ino() as u64,
InodeType::from(dir_entry.type_()),
dir_entry.record_len(),
)?;
*offset += dir_entry.record_len();
let offset_read = {
let inner = self.inner.read();
if inner.file_type() != FileType::Dir {
return_errno!(Errno::ENOTDIR);
}
if inner.hard_links() == 0 {
return_errno_with_message!(Errno::ENOENT, "dir removed");
}
Ok(())
let try_readdir = |offset: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
let dir_entry_reader = DirEntryReader::new(&inner.page_cache, *offset);
for (_, dir_entry) in dir_entry_reader {
visitor.visit(
dir_entry.name(),
dir_entry.ino() as u64,
InodeType::from(dir_entry.type_()),
dir_entry.record_len(),
)?;
*offset += dir_entry.record_len();
}
Ok(())
};
let mut iterate_offset = offset;
match try_readdir(&mut iterate_offset, visitor) {
Err(e) if iterate_offset == offset => Err(e),
_ => Ok(iterate_offset - offset),
}?
};
let mut iterate_offset = offset;
match try_readdir(&mut iterate_offset, visitor) {
Err(e) if iterate_offset == offset => Err(e),
_ => Ok(iterate_offset - offset),
}
self.set_atime(now());
Ok(offset_read)
}
pub fn write_link(&self, target: &str) -> Result<()> {
@ -550,58 +618,83 @@ impl Inode {
}
pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let inner = self.inner.read();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
let bytes_read = {
let inner = self.inner.read();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
inner.read_at(offset, buf)
inner.read_at(offset, buf)?
};
self.set_atime(now());
Ok(bytes_read)
}
// The offset and the length of buffer must be multiples of the block size.
pub fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let inner = self.inner.read();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
if !is_block_aligned(offset) || !is_block_aligned(buf.len()) {
return_errno_with_message!(Errno::EINVAL, "not block-aligned");
}
let bytes_read = {
let inner = self.inner.read();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
if !is_block_aligned(offset) || !is_block_aligned(buf.len()) {
return_errno_with_message!(Errno::EINVAL, "not block-aligned");
}
inner.read_direct_at(offset, buf)
inner.read_direct_at(offset, buf)?
};
self.set_atime(now());
Ok(bytes_read)
}
pub fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let inner = self.inner.upread();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
let bytes_written = {
let inner = self.inner.upread();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
let file_size = inner.file_size();
let new_size = offset + buf.len();
if new_size > file_size {
let mut inner = inner.upgrade();
inner.extend_write_at(offset, buf)?;
} else {
inner.write_at(offset, buf)?;
}
let file_size = inner.file_size();
let new_size = offset + buf.len();
if new_size > file_size {
let mut inner = inner.upgrade();
inner.extend_write_at(offset, buf)?
} else {
inner.write_at(offset, buf)?
}
};
Ok(buf.len())
let now = now();
self.set_mtime(now);
self.set_ctime(now);
Ok(bytes_written)
}
// The offset and the length of buffer must be multiples of the block size.
pub fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let inner = self.inner.upread();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
if !is_block_aligned(offset) || !is_block_aligned(buf.len()) {
return_errno_with_message!(Errno::EINVAL, "not block aligned");
}
let bytes_written = {
let inner = self.inner.upread();
if inner.file_type() != FileType::File {
return_errno!(Errno::EISDIR);
}
if !is_block_aligned(offset) || !is_block_aligned(buf.len()) {
return_errno_with_message!(Errno::EINVAL, "not block aligned");
}
let mut inner = inner.upgrade();
inner.write_direct_at(offset, buf)?;
Ok(buf.len())
let mut inner = inner.upgrade();
inner.write_direct_at(offset, buf)?
};
let now = now();
self.set_mtime(now);
self.set_ctime(now);
Ok(bytes_written)
}
fn init(&self, dir_ino: u32) -> Result<()> {
@ -623,6 +716,24 @@ impl Inode {
inner.sync_metadata()?;
Ok(())
}
pub fn set_file_perm(&self, perm: FilePerm) {
let mut inner = self.inner.write();
inner.set_file_perm(perm);
inner.set_ctime(now());
}
pub fn set_uid(&self, uid: u32) {
let mut inner = self.inner.write();
inner.set_uid(uid);
inner.set_ctime(now());
}
pub fn set_gid(&self, gid: u32) {
let mut inner = self.inner.write();
inner.set_gid(gid);
inner.set_ctime(now());
}
}
#[inherit_methods(from = "self.inner.read()")]
@ -645,9 +756,6 @@ impl Inode {
#[inherit_methods(from = "self.inner.write()")]
impl Inode {
pub fn set_file_perm(&self, perm: FilePerm);
pub fn set_uid(&self, uid: u32);
pub fn set_gid(&self, gid: u32);
pub fn set_atime(&self, time: Duration);
pub fn set_mtime(&self, time: Duration);
pub fn set_ctime(&self, time: Duration);
@ -790,20 +898,20 @@ impl Inner {
Ok(read_len)
}
pub fn write_at(&self, offset: usize, buf: &[u8]) -> Result<()> {
pub fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
self.page_cache.pages().write_bytes(offset, buf)?;
Ok(())
Ok(buf.len())
}
pub fn extend_write_at(&mut self, offset: usize, buf: &[u8]) -> Result<()> {
pub fn extend_write_at(&mut self, offset: usize, buf: &[u8]) -> Result<usize> {
let new_size = offset + buf.len();
self.page_cache.pages().resize(new_size)?;
self.page_cache.pages().write_bytes(offset, buf)?;
self.inode_impl.resize(new_size)?;
Ok(())
Ok(buf.len())
}
pub fn write_direct_at(&mut self, offset: usize, buf: &[u8]) -> Result<()> {
pub fn write_direct_at(&mut self, offset: usize, buf: &[u8]) -> Result<usize> {
let file_size = self.inode_impl.file_size();
let end_offset = offset + buf.len();
@ -831,7 +939,7 @@ impl Inner {
buf_offset += BLOCK_SIZE;
}
Ok(())
Ok(buf.len())
}
pub fn write_link(&mut self, target: &str) -> Result<()> {
@ -1782,11 +1890,11 @@ pub(super) struct InodeDesc {
gid: u32,
/// Size in bytes.
size: usize,
/// Access time.
/// Access time. This is the time when the file was last accessed.
atime: Duration,
/// Creation time.
/// Change time. This timestamp gets updated when the file's metadata changes.
ctime: Duration,
/// Modification time.
/// Modification time. This timestamp records the last modification of the file's content.
mtime: Duration,
/// Deletion time.
dtime: Duration,
@ -1837,7 +1945,7 @@ impl TryFrom<RawInode> for InodeDesc {
impl InodeDesc {
pub fn new(type_: FileType, perm: FilePerm) -> Dirty<Self> {
let now = RealTimeCoarseClock::get().read_time();
let now = now();
Dirty::new_dirty(Self {
type_,
perm,
@ -2002,7 +2110,7 @@ pub(super) struct RawInode {
pub size_low: u32,
/// Access time.
pub atime: UnixTime,
/// Creation time.
/// Change time.
pub ctime: UnixTime,
/// Modification time.
pub mtime: UnixTime,

View File

@ -4,6 +4,11 @@ use core::ops::MulAssign;
use super::prelude::*;
/// Returns the current time.
pub fn now() -> Duration {
crate::time::clocks::RealTimeCoarseClock::get().read_time()
}
pub trait IsPowerOf: Copy + Sized + MulAssign + PartialOrd {
/// Returns true if and only if `self == x^k` for some `k` where `k > 0`.
///