Add fsnotify infrastructure for filesystem events

Signed-off-by: Zhenchen Wang <m202372036@hust.edu.cn>
This commit is contained in:
Zhenchen Wang 2025-11-17 23:38:28 +08:00 committed by Tate, Hongliang Tian
parent 1a536d31b0
commit 6eef70a634
34 changed files with 817 additions and 46 deletions

View File

@ -11,7 +11,10 @@ use crate::{
fs::{
cgroupfs::systree_node::CgroupSystem,
registry::{FsProperties, FsType},
utils::{systree_inode::SysTreeInodeTy, FileSystem, FsFlags, Inode, SuperBlock},
utils::{
systree_inode::SysTreeInodeTy, FileSystem, FsEventSubscriberStats, FsFlags, Inode,
SuperBlock,
},
Result,
},
prelude::*,
@ -21,6 +24,7 @@ use crate::{
pub(super) struct CgroupFs {
sb: SuperBlock,
root: Arc<dyn Inode>,
fs_event_subscriber_stats: FsEventSubscriberStats,
}
// Magic number for cgroupfs v2 (taken from Linux)
@ -43,6 +47,7 @@ impl CgroupFs {
Arc::new(Self {
sb,
root: root_inode,
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
})
}
}
@ -64,6 +69,10 @@ impl FileSystem for CgroupFs {
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
pub(super) struct CgroupFsType;

View File

@ -8,6 +8,7 @@ use super::fs::CgroupFs;
use crate::{
fs::{
cgroupfs::CgroupNode,
notify::FsEventPublisher,
path::{is_dot, is_dotdot},
utils::{
systree_inode::{SysTreeInodeTy, SysTreeNodeKind},
@ -23,6 +24,8 @@ pub(super) struct CgroupInode {
node_kind: SysTreeNodeKind,
/// The metadata of this inode.
metadata: Metadata,
/// FS event publisher.
fs_event_publisher: FsEventPublisher,
/// The file mode (permissions) of this inode, protected by a lock.
mode: RwLock<InodeMode>,
/// Weak reference to the parent inode.
@ -44,6 +47,7 @@ impl SysTreeInodeTy for CgroupInode {
Arc::new_cyclic(|this| Self {
node_kind,
metadata,
fs_event_publisher: FsEventPublisher::new(),
mode: RwLock::new(mode),
parent,
this: this.clone(),
@ -58,6 +62,10 @@ impl SysTreeInodeTy for CgroupInode {
&self.metadata
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
fn mode(&self) -> Result<InodeMode> {
Ok(*self.mode.read())
}

View File

@ -11,7 +11,10 @@ use crate::{
fs::{
configfs::systree_node::ConfigRootNode,
registry::{FsProperties, FsType},
utils::{systree_inode::SysTreeInodeTy, FileSystem, FsFlags, Inode, SuperBlock},
utils::{
systree_inode::SysTreeInodeTy, FileSystem, FsEventSubscriberStats, FsFlags, Inode,
SuperBlock,
},
Result,
},
prelude::*,
@ -26,6 +29,7 @@ use crate::{
pub struct ConfigFs {
sb: SuperBlock,
root: Arc<dyn Inode>,
fs_event_subscriber_stats: FsEventSubscriberStats,
}
// Magic number for `ConfigFs` (taken from Linux).
@ -48,6 +52,7 @@ impl ConfigFs {
Arc::new(Self {
sb,
root: root_inode,
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
})
}
}
@ -69,6 +74,10 @@ impl FileSystem for ConfigFs {
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
pub(super) struct ConfigFsType;

View File

@ -7,6 +7,7 @@ use ostd::sync::RwLock;
use crate::{
fs::{
configfs::fs::ConfigFs,
notify::FsEventPublisher,
utils::{
systree_inode::{SysTreeInodeTy, SysTreeNodeKind},
FileSystem, Inode, InodeMode, Metadata,
@ -21,6 +22,8 @@ pub struct ConfigInode {
node_kind: SysTreeNodeKind,
/// The metadata of this inode.
metadata: Metadata,
/// FS event publisher.
fs_event_publisher: FsEventPublisher,
/// The file mode (permissions) of this inode, protected by a lock.
mode: RwLock<InodeMode>,
/// Weak reference to the parent inode.
@ -42,6 +45,7 @@ impl SysTreeInodeTy for ConfigInode {
Arc::new_cyclic(|this| Self {
node_kind,
metadata,
fs_event_publisher: FsEventPublisher::new(),
mode: RwLock::new(mode),
parent,
this: this.clone(),
@ -69,6 +73,10 @@ impl SysTreeInodeTy for ConfigInode {
&self.parent
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
fn this(&self) -> Arc<Self> {
self.this.upgrade().expect("Weak ref invalid")
}

View File

@ -9,15 +9,16 @@ use id_alloc::IdAlloc;
pub use self::ptmx::Ptmx;
use self::slave::PtySlaveInode;
use super::utils::{InodeIo, MknodType, StatusFlags};
use super::utils::{MknodType, StatusFlags};
use crate::{
device::PtyMaster,
fs::{
device::{Device, DeviceType},
notify::FsEventPublisher,
registry::{FsProperties, FsType},
utils::{
mkmod, DirEntryVecExt, DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType,
Metadata, SuperBlock, NAME_MAX,
mkmod, DirEntryVecExt, DirentVisitor, FileSystem, FsEventSubscriberStats, FsFlags,
Inode, InodeIo, InodeMode, InodeType, Metadata, SuperBlock, NAME_MAX,
},
},
prelude::*,
@ -47,6 +48,7 @@ pub struct DevPts {
sb: SuperBlock,
root: Arc<RootInode>,
index_alloc: Mutex<IdAlloc>,
fs_event_subscriber_stats: FsEventSubscriberStats,
this: Weak<Self>,
}
@ -56,6 +58,7 @@ impl DevPts {
sb: SuperBlock::new(DEVPTS_MAGIC, BLOCK_SIZE, NAME_MAX),
root: RootInode::new(weak_self.clone()),
index_alloc: Mutex::new(IdAlloc::with_capacity(MAX_PTY_NUM)),
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
this: weak_self.clone(),
})
}
@ -109,6 +112,10 @@ impl FileSystem for DevPts {
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
struct DevPtsType;
@ -144,6 +151,7 @@ struct RootInode {
ptmx: Arc<Ptmx>,
slaves: RwLock<SlotVec<(String, Arc<dyn Inode>)>>,
metadata: RwLock<Metadata>,
fs_event_publisher: FsEventPublisher,
fs: Weak<DevPts>,
}
@ -153,6 +161,7 @@ impl RootInode {
ptmx: Ptmx::new(fs.clone()),
slaves: RwLock::new(SlotVec::new()),
metadata: RwLock::new(Metadata::new_dir(ROOT_INO, mkmod!(a+rx, u+w), BLOCK_SIZE)),
fs_event_publisher: FsEventPublisher::new(),
fs,
})
}
@ -191,6 +200,10 @@ impl Inode for RootInode {
*self.metadata.read()
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
fn ino(&self) -> u64 {
self.metadata.read().ino as _
}

View File

@ -5,7 +5,8 @@ use device_id::{DeviceId, MajorId, MinorId};
use super::*;
use crate::fs::{
inode_handle::FileIo,
utils::{AccessMode, StatusFlags},
notify::FsEventPublisher,
utils::{AccessMode, InodeIo, StatusFlags},
};
/// Same major number with Linux.
@ -20,6 +21,7 @@ const PTMX_MINOR_NUM: u32 = 2;
pub struct Ptmx {
inner: Inner,
metadata: RwLock<Metadata>,
fs_event_publisher: FsEventPublisher,
}
#[derive(Clone)]
@ -36,6 +38,7 @@ impl Ptmx {
&inner,
)),
inner,
fs_event_publisher: FsEventPublisher::new(),
})
}
@ -79,6 +82,10 @@ impl Inode for Ptmx {
*self.metadata.read()
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
fn ino(&self) -> u64 {
self.metadata.read().ino as _
}

View File

@ -8,7 +8,8 @@ use crate::{
device::PtySlave,
fs::{
inode_handle::FileIo,
utils::{AccessMode, StatusFlags},
notify::FsEventPublisher,
utils::{AccessMode, InodeIo, StatusFlags},
},
};
@ -19,6 +20,7 @@ const SLAVE_MAJOR_NUM: u32 = 3;
pub struct PtySlaveInode {
device: Arc<PtySlave>,
metadata: RwLock<Metadata>,
fs_event_publisher: FsEventPublisher,
fs: Weak<DevPts>,
}
@ -32,6 +34,7 @@ impl PtySlaveInode {
device.as_ref(),
)),
device,
fs_event_publisher: FsEventPublisher::new(),
fs,
})
}
@ -78,6 +81,10 @@ impl Inode for PtySlaveInode {
*self.metadata.read()
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
fn ino(&self) -> u64 {
self.metadata.read().ino as _
}

View File

@ -26,7 +26,10 @@ use crate::{
fs::{
exfat::{constants::*, inode::Ino},
registry::{FsProperties, FsType},
utils::{CachePage, FileSystem, FsFlags, Inode, PageCache, PageCacheBackend, SuperBlock},
utils::{
CachePage, FileSystem, FsEventSubscriberStats, FsFlags, Inode, PageCache,
PageCacheBackend, SuperBlock,
},
},
prelude::*,
};
@ -53,6 +56,8 @@ pub struct ExfatFs {
//A global lock, We need to hold the mutex before accessing bitmap or inode, otherwise there will be deadlocks.
mutex: Mutex<()>,
fs_event_subscriber_stats: FsEventSubscriberStats,
}
const FAT_LRU_CACHE_SIZE: usize = 1024;
@ -80,6 +85,7 @@ impl ExfatFs {
)),
meta_cache: PageCache::with_capacity(fs_size, weak_self.clone() as _).unwrap(),
mutex: Mutex::new(()),
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
});
// TODO: if the main superblock is corrupted, should we load the backup?
@ -417,6 +423,10 @@ impl FileSystem for ExfatFs {
fn sb(&self) -> SuperBlock {
SuperBlock::new(BOOT_SIGNATURE as u64, self.sector_size(), MAX_NAME_LENGTH)
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
#[derive(Clone, Debug, Default)]

View File

@ -27,6 +27,7 @@ use super::{
use crate::{
fs::{
exfat::{dentry::ExfatDentryIterator, fat::ExfatChain, fs::ExfatFs},
notify::FsEventPublisher,
path::{is_dot, is_dot_or_dotdot, is_dotdot},
utils::{
mkmod, CachePage, DirentVisitor, Extension, Inode, InodeIo, InodeMode, InodeType,
@ -78,6 +79,7 @@ impl FatAttr {
pub struct ExfatInode {
inner: RwMutex<ExfatInodeInner>,
extension: Extension,
fs_event_publisher: FsEventPublisher,
}
#[derive(Debug)]
@ -862,6 +864,7 @@ impl ExfatInode {
page_cache: PageCache::with_capacity(size, weak_self.clone() as _).unwrap(),
}),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
});
let inner = inode.inner.upread();
@ -972,6 +975,7 @@ impl ExfatInode {
page_cache: PageCache::with_capacity(size, weak_self.clone() as _).unwrap(),
}),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
});
if matches!(inode_type, InodeType::Dir) {
@ -1735,4 +1739,8 @@ impl Inode for ExfatInode {
fn extension(&self) -> Option<&Extension> {
Some(&self.extension)
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
}

View File

@ -11,7 +11,7 @@ use super::{
};
use crate::fs::{
registry::{FsProperties, FsType},
utils::{FileSystem, FsFlags},
utils::{FileSystem, FsEventSubscriberStats, FsFlags},
};
/// The root inode number.
@ -28,6 +28,7 @@ pub struct Ext2 {
inode_size: usize,
block_size: usize,
group_descriptors_segment: USegment,
fs_event_subscriber_stats: FsEventSubscriberStats,
self_ref: Weak<Self>,
}
@ -98,6 +99,7 @@ impl Ext2 {
block_device,
super_block: RwMutex::new(Dirty::new(super_block)),
group_descriptors_segment,
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
self_ref: weak_ref.clone(),
});
Ok(ext2)
@ -437,6 +439,10 @@ impl Ext2 {
fn block_idx(&self, bid: Ext2Bid) -> Ext2Bid {
bid % self.blocks_per_group
}
pub fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
pub(super) struct Ext2Type;

View File

@ -5,7 +5,7 @@ use ostd::sync::RwMutexReadGuard;
use crate::{
fs::{
ext2::{utils::Dirty, Ext2, SuperBlock as Ext2SuperBlock, MAGIC_NUM as EXT2_MAGIC},
utils::{FileSystem, Inode, SuperBlock, NAME_MAX},
utils::{FileSystem, FsEventSubscriberStats, Inode, SuperBlock, NAME_MAX},
},
prelude::*,
};
@ -30,6 +30,10 @@ impl FileSystem for Ext2 {
fn sb(&self) -> SuperBlock {
SuperBlock::from(self.super_block())
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
self.fs_event_subscriber_stats()
}
}
impl From<RwMutexReadGuard<'_, Dirty<Ext2SuperBlock>>> for SuperBlock {

View File

@ -5,6 +5,7 @@ use core::time::Duration;
use crate::{
fs::{
ext2::{FilePerm, Inode as Ext2Inode},
notify::FsEventPublisher,
utils::{
DirentVisitor, Extension, FallocMode, FileSystem, Inode, InodeIo, InodeMode, InodeType,
Metadata, MknodType, StatusFlags, SymbolicLink, XattrName, XattrNamespace,
@ -200,6 +201,10 @@ impl Inode for Ext2Inode {
Some(self.extension())
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
self.fs_event_publisher()
}
fn set_xattr(
&self,
name: XattrName,

View File

@ -20,6 +20,7 @@ use super::{
};
use crate::{
fs::{
notify::FsEventPublisher,
path::{is_dot, is_dot_or_dotdot, is_dotdot},
utils::{
Extension, FallocMode, Inode as _, InodeMode, Metadata, Permission, XattrName,
@ -43,6 +44,7 @@ pub struct Inode {
inner: RwMutex<InodeInner>,
fs: Weak<Ext2>,
extension: Extension,
fs_event_publisher: FsEventPublisher,
xattr: Option<Xattr>,
}
@ -63,6 +65,7 @@ impl Inode {
inner: RwMutex::new(InodeInner::new(desc, weak_self.clone(), fs.clone())),
fs,
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
})
}
@ -107,6 +110,10 @@ impl Inode {
}
}
pub fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
pub fn resize(&self, new_size: usize) -> Result<()> {
if self.type_ == InodeType::Dir {
return_errno!(Errno::EISDIR);

View File

@ -8,7 +8,7 @@ use core::fmt::Display;
use ostd::io::IoMem;
use super::inode_handle::InodeHandle;
use super::{inode_handle::InodeHandle, path::Path};
use crate::{
fs::{
file_table::FdFlags,
@ -143,6 +143,12 @@ impl dyn FileLike {
.ok_or_else(|| Error::with_message(Errno::ENOTSOCK, "the file is not a socket"))
}
pub fn path(&self) -> Option<&Path> {
self.as_inode_handle_or_err()
.ok()
.map(|inode_handle| inode_handle.path())
}
pub fn as_inode_handle_or_err(&self) -> Result<&InodeHandle> {
self.downcast_ref().ok_or_else(|| {
Error::with_message(Errno::EINVAL, "the file is not related to an inode")

View File

@ -11,6 +11,7 @@ pub mod file_handle;
pub mod file_table;
pub mod fs_resolver;
pub mod inode_handle;
pub mod notify;
pub mod overlayfs;
pub mod path;
pub mod pipe;

430
kernel/src/fs/notify/mod.rs Normal file
View File

@ -0,0 +1,430 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::{sync::Arc, vec::Vec};
use core::{
any::Any,
sync::atomic::{AtomicBool, AtomicU32, Ordering},
};
use atomic_integer_wrapper::define_atomic_version_of_integer_like_type;
use bitflags::bitflags;
use ostd::sync::RwLock;
use crate::{
fs::{file_handle::FileLike, path::Path, utils::AccessMode},
prelude::*,
};
use super::utils::{Inode, InodeType};
/// Publishes filesystem events to subscribers.
///
/// Each inode has an associated `FsEventPublisher` that maintains a list of
/// subscribers interested in filesystem events. When an event occurs, the publisher
/// notifies all subscribers whose interesting events match the event.
pub struct FsEventPublisher {
/// List of FS event subscribers.
subscribers: RwLock<Vec<Arc<dyn FsEventSubscriber>>>,
/// All interesting FS event types (aggregated from all subscribers).
all_interesting_events: AtomicFsEvents,
/// Whether this publisher still accepts new subscribers.
accepts_new_subscribers: AtomicBool,
}
impl Debug for FsEventPublisher {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let subscribers = self.subscribers.read();
write!(
f,
"FsEventPublisher: num_subscribers: {}",
subscribers.len()
)?;
Ok(())
}
}
impl Default for FsEventPublisher {
fn default() -> Self {
Self::new()
}
}
impl FsEventPublisher {
pub fn new() -> Self {
Self {
subscribers: RwLock::new(Vec::new()),
all_interesting_events: AtomicFsEvents::new(FsEvents::empty()),
accepts_new_subscribers: AtomicBool::new(true),
}
}
/// Adds a subscriber to this publisher.
pub fn add_subscriber(&self, subscriber: Arc<dyn FsEventSubscriber>) -> bool {
if !self.accepts_new_subscribers.load(Ordering::Acquire) {
return false;
}
let mut subscribers = self.subscribers.write();
let current = self.all_interesting_events.load(Ordering::Relaxed);
self.all_interesting_events
.store(current | subscriber.interesting_events(), Ordering::Relaxed);
subscribers.push(subscriber);
true
}
/// Removes a subscriber from this publisher.
pub fn remove_subscriber(&self, subscriber: &Arc<dyn FsEventSubscriber>) -> bool {
let mut subscribers = self.subscribers.write();
let orig_len = subscribers.len();
subscribers.retain(|m| !Arc::ptr_eq(m, subscriber));
let removed = subscribers.len() != orig_len;
if removed {
subscriber.deliver_event(FsEvents::IN_IGNORED, None);
self.recalc_interesting_events(&subscribers);
}
removed
}
/// Removes all subscribers from this publisher.
pub fn remove_all_subscribers(&self) -> usize {
let mut subscribers = self.subscribers.write();
for subscriber in subscribers.iter() {
subscriber.deliver_event(FsEvents::IN_IGNORED, None);
}
let num_subscribers = subscribers.len();
subscribers.clear();
self.all_interesting_events
.store(FsEvents::empty(), Ordering::Relaxed);
num_subscribers
}
/// Forbids new subscribers from attaching to this publisher.
pub fn disable_new_subscribers(&self) {
self.accepts_new_subscribers.store(false, Ordering::Release);
}
/// Broadcasts an event to all the subscribers of this publisher.
pub fn publish_event(&self, events: FsEvents, name: Option<String>) {
let interesting = self.all_interesting_events.load(Ordering::Relaxed);
if !interesting.intersects(events) {
return;
}
let subscribers = self.subscribers.read();
for subscriber in subscribers.iter() {
subscriber.deliver_event(events, name.clone());
}
}
/// Recalculates the aggregated interesting events from all subscribers.
fn recalc_interesting_events(&self, subscribers: &[Arc<dyn FsEventSubscriber>]) {
let mut new_events = FsEvents::empty();
for subscriber in subscribers.iter() {
new_events |= subscriber.interesting_events();
}
self.all_interesting_events
.store(new_events, Ordering::Relaxed);
}
/// Updates the aggregated events when a subscriber's interesting events change.
pub fn update_subscriber_events(&self) {
let subscribers = self.subscribers.read();
let mut new_events = FsEvents::empty();
for subscriber in subscribers.iter() {
new_events |= subscriber.interesting_events();
}
self.all_interesting_events
.store(new_events, Ordering::Relaxed);
}
/// Finds a subscriber and applies an action if found.
///
/// The matcher should return `Some(T)` if the subscriber matches and processing
/// should stop, or `None` to continue searching.
pub fn find_subscriber_and_process<F, T>(&self, mut matcher: F) -> Option<T>
where
F: FnMut(&Arc<dyn FsEventSubscriber>) -> Option<T>,
{
let subscribers = self.subscribers.read();
for subscriber in subscribers.iter() {
if let Some(result) = matcher(subscriber) {
return Some(result);
}
}
None
}
}
/// Represents a subscriber to filesystem events on an `FsEventPublisher`.
///
/// A subscriber receives notifications from a publisher when filesystem events occur
/// that match the subscriber's interesting events. The subscriber specifies which events
/// it is interested in using `FsEvents`, which define the types of events (e.g.,
/// read, write, modify, delete) the subscriber wants to be notified about. When an event
/// occurs, the publisher (attached to an inode) broadcasts it to all subscribers whose
/// interesting events match the event type.
pub trait FsEventSubscriber: Any + Send + Sync {
/// Delivers a filesystem event notification to the subscriber.
///
/// Invariant: This method must not sleep or perform blocking operations. The publisher
/// may hold a spin lock when calling this method.
fn deliver_event(&self, events: FsEvents, name: Option<String>);
/// Returns the events that this subscriber is interested in.
fn interesting_events(&self) -> FsEvents;
}
bitflags! {
/// Represents filesystem events that have occurred.
///
/// These events are used to notify subscribers about specific filesystem actions.
/// Subscribers specify which events they are interested in to filter and receive
/// only the events they care about.
pub struct FsEvents: u32 {
const ACCESS = 0x00000001; // File was accessed
const MODIFY = 0x00000002; // File was modified
const ATTRIB = 0x00000004; // Metadata changed
const CLOSE_WRITE = 0x00000008; // Writable file was closed
const CLOSE_NOWRITE = 0x00000010; // Unwritable file closed
const OPEN = 0x00000020; // File was opened
const MOVED_FROM = 0x00000040; // File was moved from X
const MOVED_TO = 0x00000080; // File was moved to Y
const CREATE = 0x00000100; // Subfile was created
const DELETE = 0x00000200; // Subfile was deleted
const DELETE_SELF = 0x00000400; // Self was deleted
const MOVE_SELF = 0x00000800; // Self was moved
const OPEN_EXEC = 0x00001000; // File was opened for exec
const UNMOUNT = 0x00002000; // Inode on umount fs
const Q_OVERFLOW = 0x00004000; // Event queued overflowed
const ERROR = 0x00008000; // Filesystem Error (fanotify)
const IN_IGNORED = 0x00008000; // Last inotify event here (inotify)
const OPEN_PERM = 0x00010000; // Open event in a permission hook
const ACCESS_PERM = 0x00020000; // Access event in a permissions hook
const OPEN_EXEC_PERM = 0x00040000; // Open/exec event in a permission hook
const EVENT_ON_CHILD = 0x08000000; // Set on inode mark that cares about things that happen to its children.
const RENAME = 0x10000000; // File was renamed
const DN_MULTISHOT = 0x20000000; // dnotify multishot
const ISDIR = 0x40000000; // Event occurred against dir
}
}
impl From<u32> for FsEvents {
fn from(value: u32) -> Self {
Self::from_bits_truncate(value)
}
}
impl From<FsEvents> for u32 {
fn from(value: FsEvents) -> Self {
value.bits()
}
}
define_atomic_version_of_integer_like_type!(FsEvents, {
#[derive(Debug)]
pub(super) struct AtomicFsEvents(AtomicU32);
});
/// Notifies that a file was accessed.
pub fn on_access(file: &Arc<dyn FileLike>) {
// TODO: Check fmode flags (FMODE_NONOTIFY, FMODE_NONOTIFY_PERM).
if let Some(path) = file.path() {
if !path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_parent(path, FsEvents::ACCESS, path.effective_name());
}
}
/// Notifies that a file was modified.
pub fn on_modify(file: &Arc<dyn FileLike>) {
// TODO: Check fmode flags (FMODE_NONOTIFY, FMODE_NONOTIFY_PERM).
let Some(path) = file.path() else {
return;
};
if !path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_parent(path, FsEvents::MODIFY, path.effective_name());
}
/// Notifies that a path's content was changed.
pub fn on_change(path: &Path) {
if !path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_parent(path, FsEvents::MODIFY, path.effective_name());
}
/// Notifies that a file was deleted from a directory.
pub fn on_delete(
dir_inode: &Arc<dyn Inode>,
inode: &Arc<dyn Inode>,
name: impl FnOnce() -> String,
) {
if !dir_inode
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
let name = name();
if inode.type_() == InodeType::Dir {
notify_inode(dir_inode, FsEvents::DELETE | FsEvents::ISDIR, Some(name))
} else {
notify_inode(dir_inode, FsEvents::DELETE, Some(name))
}
}
/// Notifies that an inode's link count changed.
pub fn on_link_count(inode: &Arc<dyn Inode>) {
if !inode.fs().fs_event_subscriber_stats().has_any_subscribers() {
return;
}
notify_inode(inode, FsEvents::ATTRIB, None);
}
/// Notifies that an inode was removed (link count reached 0).
pub fn on_inode_removed(inode: &Arc<dyn Inode>) {
if !inode.fs().fs_event_subscriber_stats().has_any_subscribers() {
return;
}
notify_inode(inode, FsEvents::DELETE_SELF, None);
}
/// Notifies that a file was linked to a directory.
pub fn on_link(dir_inode: &Arc<dyn Inode>, inode: &Arc<dyn Inode>, name: String) {
if !dir_inode
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_inode(inode, FsEvents::ATTRIB, None);
notify_inode(dir_inode, FsEvents::CREATE, Some(name));
}
/// Notifies that a directory was created.
pub fn on_mkdir(dir_path: &Path, name: String) {
if !dir_path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_inode(
dir_path.inode(),
FsEvents::CREATE | FsEvents::ISDIR,
Some(name),
);
}
/// Notifies that a file was created.
pub fn on_create(file_path: &Path, name: String) {
if !file_path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_inode(file_path.inode(), FsEvents::CREATE, Some(name));
}
/// Notifies that a file was opened.
pub fn on_open(file: &Arc<dyn FileLike>) {
// TODO: Check fmode flags (FMODE_NONOTIFY, FMODE_NONOTIFY_PERM).
if let Some(path) = file.path() {
if !path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_parent(path, FsEvents::OPEN, path.effective_name());
}
}
/// Notifies that a file was closed.
pub fn on_close(file: &Arc<dyn FileLike>) {
// TODO: Check fmode flags (FMODE_NONOTIFY, FMODE_NONOTIFY_PERM).
if let Some(path) = file.path() {
if !path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
let events = match file.access_mode() {
AccessMode::O_RDONLY => FsEvents::CLOSE_NOWRITE,
_ => FsEvents::CLOSE_WRITE,
};
notify_parent(path, events, path.effective_name());
}
}
/// Notifies that a file's attributes changed.
pub fn on_attr_change(path: &Path) {
if !path
.inode()
.fs()
.fs_event_subscriber_stats()
.has_any_subscribers()
{
return;
}
notify_parent(path, FsEvents::ATTRIB, path.effective_name());
}
/// Notifies a path's parent and the path itself about filesystem events.
///
/// If the parent is watching or if subscribers have registered interesting events with
/// parent and name information, notifies the parent with child name info.
/// Otherwise, notifies only the child without name information.
/// This function is already called after filesystem checking in the callers.
fn notify_parent(path: &Path, mut events: FsEvents, name: String) {
if path.inode().type_() == InodeType::Dir {
events |= FsEvents::ISDIR;
}
let parent = path.effective_parent();
if let Some(parent) = parent {
notify_inode(parent.inode(), events, Some(name));
}
notify_inode(path.inode(), events, None);
}
/// Sends a filesystem notification event to all subscribers of an inode.
///
/// This is the main entry point for fsnotify. The VFS layer calls hook-specific
/// functions in `fs/notify/`, which then call this function to broadcast events
/// to all registered subscribers through the inode's publisher.
fn notify_inode(inode: &Arc<dyn Inode>, events: FsEvents, name: Option<String>) {
inode.fs_event_publisher().publish_event(events, name);
}

View File

@ -21,12 +21,14 @@ use crate::{
fs::{
fs_resolver::FsPath,
inode_handle::FileIo,
notify::FsEventPublisher,
path::Path,
registry::{FsProperties, FsType},
utils::{
mkmod, AccessMode, DirentCounter, DirentVisitor, FallocMode, FileSystem, FsFlags,
Inode, InodeIo, InodeMode, InodeType, Metadata, MknodType, StatusFlags, SuperBlock,
SymbolicLink, XattrName, XattrNamespace, XattrSetFlags, NAME_MAX, XATTR_VALUE_MAX_LEN,
mkmod, AccessMode, DirentCounter, DirentVisitor, FallocMode, FileSystem,
FsEventSubscriberStats, FsFlags, Inode, InodeIo, InodeMode, InodeType, Metadata,
MknodType, StatusFlags, SuperBlock, SymbolicLink, XattrName, XattrNamespace,
XattrSetFlags, NAME_MAX, XATTR_VALUE_MAX_LEN,
},
},
prelude::*,
@ -52,6 +54,8 @@ pub struct OverlayFs {
sb: OverlaySB,
/// Unique inode number generator.
next_ino: AtomicU64,
/// FS event subscriber stats for this file system.
fs_event_subscriber_stats: FsEventSubscriberStats,
/// Weak self reference.
self_: Weak<OverlayFs>,
}
@ -87,6 +91,8 @@ struct OverlayInode {
/// This field is used to build hierarchical upper inodes.
/// The lock is intended to implement `rename`.
name_upon_creation: SpinLock<String>,
/// FS event publisher.
fs_event_publisher: FsEventPublisher,
/// The parent inode. `None` for root inode.
parent: Option<Arc<OverlayInode>>,
/// The mutable upper regular inode.
@ -126,6 +132,7 @@ impl OverlayFs {
config: OverlayConfig::default(),
sb: OverlaySB,
next_ino: AtomicU64::new(0),
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
self_: weak.clone(),
}))
}
@ -166,6 +173,7 @@ impl FileSystem for OverlayFs {
ino,
type_: InodeType::Dir,
name_upon_creation: SpinLock::new(String::from("")),
fs_event_publisher: FsEventPublisher::new(),
parent: None,
upper: Mutex::new(Some(upper_inode)),
upper_is_opaque: false,
@ -190,6 +198,10 @@ impl FileSystem for OverlayFs {
// TODO: Fill the super block with valid field values.
SuperBlock::new(OVERLAY_FS_MAGIC, BLOCK_SIZE, NAME_MAX)
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
impl OverlayFs {
@ -267,6 +279,7 @@ impl OverlayInode {
ino: new_upper.ino(),
type_,
name_upon_creation: SpinLock::new(String::from(name)),
fs_event_publisher: FsEventPublisher::new(),
parent: Some(self.self_.upgrade().unwrap()),
upper: Mutex::new(Some(new_upper)),
upper_is_opaque,
@ -439,6 +452,10 @@ impl OverlayInode {
self.type_
}
pub fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
pub fn page_cache(&self) -> Option<Arc<Vmo>> {
let _ = self.get_top_valid_inode().page_cache()?;
// Do copy-up for the potential memory mapping operations
@ -676,6 +693,7 @@ impl OverlayInode {
ino,
type_: type_.unwrap(),
name_upon_creation: SpinLock::new(String::from(name)),
fs_event_publisher: FsEventPublisher::new(),
parent: Some(self.self_.upgrade().unwrap()),
upper: Mutex::new(upper_child),
upper_is_opaque,
@ -916,6 +934,7 @@ impl Inode for OverlayInode {
fn size(&self) -> usize;
fn resize(&self, new_size: usize) -> Result<()>;
fn metadata(&self) -> Metadata;
fn fs_event_publisher(&self) -> &FsEventPublisher;
fn ino(&self) -> u64;
fn type_(&self) -> InodeType;
fn mode(&self) -> Result<InodeMode>;

View File

@ -5,8 +5,9 @@ use core::sync::atomic::{AtomicU32, Ordering};
use hashbrown::HashMap;
use ostd::sync::RwMutexWriteGuard;
use super::is_dot_or_dotdot;
use super::{is_dot, is_dot_or_dotdot, is_dotdot};
use crate::{
fs,
fs::utils::{Inode, InodeMode, InodeType, MknodType},
prelude::*,
};
@ -229,8 +230,9 @@ impl Dentry {
);
if dentry.is_dentry_cacheable() {
children.upgrade().insert(name, dentry.clone());
children.upgrade().insert(name.clone(), dentry.clone());
}
fs::notify::on_link(dentry.parent().unwrap().inode(), dentry.inode(), name);
Ok(())
}
@ -240,13 +242,39 @@ impl Dentry {
return_errno!(Errno::ENOTDIR);
}
if is_dot_or_dotdot(name) {
return_errno_with_message!(Errno::EINVAL, "unlink on . or ..");
}
let children = self.children.upread();
children.check_mountpoint(name)?;
self.inode.unlink(name)?;
let mut children = children.upgrade();
children.delete(name);
let cached_child = children.delete(name);
let child_inode = match cached_child {
Some(child) => {
// Cache hit: use the cached dentry
child.inode().clone()
}
None => {
// Cache miss: need to lookup from the underlying filesystem
drop(children);
self.inode.lookup(name)?
}
};
self.inode.unlink(name)?;
fs::notify::on_link_count(&child_inode);
if child_inode.metadata().nlinks == 0 {
fs::notify::on_inode_removed(&child_inode);
let deleted_watches = child_inode.fsnotify_publisher().remove_all_subscribers();
child_inode
.fs()
.fsnotify_info()
.remove_subscribers(deleted_watches);
}
fs::notify::on_delete(self.inode(), &child_inode, String::from(name));
Ok(())
}
@ -256,13 +284,41 @@ impl Dentry {
return_errno!(Errno::ENOTDIR);
}
if is_dot(name) {
return_errno_with_message!(Errno::EINVAL, "rmdir on .");
}
if is_dotdot(name) {
return_errno_with_message!(Errno::ENOTEMPTY, "rmdir on ..");
}
let children = self.children.upread();
children.check_mountpoint(name)?;
self.inode.rmdir(name)?;
let mut children = children.upgrade();
children.delete(name);
let cached_child = children.delete(name);
let child_inode = match cached_child {
Some(child) => {
// Cache hit: use the cached dentry
child.inode().clone()
}
None => {
// Cache miss: need to lookup from the underlying filesystem
drop(children);
self.inode.lookup(name)?
}
};
self.inode.rmdir(name)?;
if child_inode.metadata().nlinks == 0 {
fs::notify::on_inode_removed(&child_inode);
let deleted_watches = child_inode.fsnotify_publisher().remove_all_subscribers();
child_inode
.fs()
.fsnotify_info()
.remove_subscribers(deleted_watches);
}
fs::notify::on_delete(self.inode(), &child_inode, String::from(name));
Ok(())
}

View File

@ -145,9 +145,7 @@ impl Path {
///
/// If it is the root of a mount, it will go up to the mountpoint
/// to get the name of the mountpoint recursively.
//
// FIXME: This method needs to be aware of the current process's root path.
fn effective_name(&self) -> String {
pub fn effective_name(&self) -> String {
if !self.is_mount_root() {
return self.dentry.name();
}
@ -167,7 +165,7 @@ impl Path {
///
/// If it is the root of a mount, it will go up to the mountpoint
/// to get the parent of the mountpoint recursively.
fn effective_parent(&self) -> Option<Self> {
pub fn effective_parent(&self) -> Option<Self> {
if !self.is_mount_root() {
return Some(Self::new(self.mount.clone(), self.dentry.parent().unwrap()));
}

View File

@ -23,7 +23,10 @@ use crate::{
fs::{
procfs::{filesystems::FileSystemsFileOps, stat::StatFileOps},
registry::{FsProperties, FsType},
utils::{mkmod, DirEntryVecExt, FileSystem, FsFlags, Inode, SuperBlock, NAME_MAX},
utils::{
mkmod, DirEntryVecExt, FileSystem, FsEventSubscriberStats, FsFlags, Inode, SuperBlock,
NAME_MAX,
},
},
prelude::*,
process::{
@ -64,6 +67,7 @@ struct ProcFs {
sb: SuperBlock,
root: Arc<dyn Inode>,
inode_allocator: AtomicU64,
fs_event_subscriber_stats: FsEventSubscriberStats,
}
impl ProcFs {
@ -72,6 +76,7 @@ impl ProcFs {
sb: SuperBlock::new(PROC_MAGIC, BLOCK_SIZE, NAME_MAX),
root: RootDirOps::new_inode(weak_fs.clone()),
inode_allocator: AtomicU64::new(PROC_ROOT_INO + 1),
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
})
}
@ -96,6 +101,10 @@ impl FileSystem for ProcFs {
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
struct ProcFsType;

View File

@ -9,6 +9,7 @@ use ostd::sync::RwMutexUpgradeableGuard;
use super::{Common, ProcFs};
use crate::{
fs::{
notify::FsEventPublisher,
path::{is_dot, is_dotdot},
utils::{
DirEntryVecExt, DirentVisitor, FileSystem, Inode, InodeIo, InodeMode, InodeType,
@ -95,6 +96,7 @@ impl<D: DirOps + 'static> InodeIo for ProcDir<D> {
impl<D: DirOps + 'static> Inode for ProcDir<D> {
fn size(&self) -> usize;
fn metadata(&self) -> Metadata;
fn fs_event_publisher(&self) -> &FsEventPublisher;
fn ino(&self) -> u64;
fn mode(&self) -> Result<InodeMode>;
fn set_mode(&self, mode: InodeMode) -> Result<()>;

View File

@ -6,8 +6,11 @@ use inherit_methods_macro::inherit_methods;
use super::{Common, ProcFs};
use crate::{
fs::utils::{
FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, StatusFlags, SymbolicLink,
fs::{
notify::FsEventPublisher,
utils::{
FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, StatusFlags, SymbolicLink,
},
},
prelude::*,
process::{Gid, Uid},
@ -66,6 +69,7 @@ impl<F: FileOps + 'static> InodeIo for ProcFile<F> {
impl<F: FileOps + 'static> Inode for ProcFile<F> {
fn size(&self) -> usize;
fn metadata(&self) -> Metadata;
fn fs_event_publisher(&self) -> &FsEventPublisher;
fn ino(&self) -> u64;
fn mode(&self) -> Result<InodeMode>;
fn set_mode(&self, mode: InodeMode) -> Result<()>;

View File

@ -10,7 +10,10 @@ pub(super) use self::{
};
use super::{ProcFs, BLOCK_SIZE};
use crate::{
fs::utils::{FileSystem, InodeMode, InodeType, Metadata},
fs::{
notify::FsEventPublisher,
utils::{FileSystem, InodeMode, InodeType, Metadata},
},
prelude::*,
process::{Gid, Uid},
};
@ -22,6 +25,7 @@ mod sym;
struct Common {
metadata: RwLock<Metadata>,
fs_event_publisher: FsEventPublisher,
fs: Weak<dyn FileSystem>,
is_volatile: bool,
}
@ -30,6 +34,7 @@ impl Common {
pub fn new(metadata: Metadata, fs: Weak<dyn FileSystem>, is_volatile: bool) -> Self {
Self {
metadata: RwLock::new(metadata),
fs_event_publisher: FsEventPublisher::new(),
fs,
is_volatile,
}
@ -109,4 +114,8 @@ impl Common {
pub fn is_volatile(&self) -> bool {
self.is_volatile
}
pub fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
}

View File

@ -6,8 +6,11 @@ use inherit_methods_macro::inherit_methods;
use super::{Common, ProcFs};
use crate::{
fs::utils::{
FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, StatusFlags, SymbolicLink,
fs::{
notify::FsEventPublisher,
utils::{
FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, StatusFlags, SymbolicLink,
},
},
prelude::*,
process::{Gid, Uid},
@ -63,6 +66,7 @@ impl<S: SymOps + 'static> InodeIo for ProcSym<S> {
impl<S: SymOps + 'static> Inode for ProcSym<S> {
fn size(&self) -> usize;
fn metadata(&self) -> Metadata;
fn fs_event_publisher(&self) -> &FsEventPublisher;
fn ino(&self) -> u64;
fn mode(&self) -> Result<InodeMode>;
fn set_mode(&self, mode: InodeMode) -> Result<()>;

View File

@ -7,9 +7,11 @@ use spin::Once;
use super::utils::{InodeIo, StatusFlags};
use crate::{
fs::{
notify::FsEventPublisher,
registry::{FsProperties, FsType},
utils::{
mkmod, FileSystem, FsFlags, Inode, InodeMode, InodeType, Metadata, SuperBlock, NAME_MAX,
mkmod, FileSystem, FsEventSubscriberStats, FsFlags, Inode, InodeMode, InodeType,
Metadata, SuperBlock, NAME_MAX,
},
},
prelude::*,
@ -22,6 +24,7 @@ pub struct PseudoFs {
name: &'static str,
sb: SuperBlock,
root: Arc<dyn Inode>,
fs_event_subscriber_stats: FsEventSubscriberStats,
}
impl FileSystem for PseudoFs {
@ -41,6 +44,10 @@ impl FileSystem for PseudoFs {
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
impl PseudoFs {
@ -63,6 +70,7 @@ impl PseudoFs {
aster_block::BLOCK_SIZE,
weak_fs.clone(),
)),
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
})
})
}
@ -174,6 +182,7 @@ const ANON_INODEFS_MAGIC: u64 = 0x09041934;
/// A pseudo inode that does not correspond to any real path in the file system.
pub struct PseudoInode {
metadata: SpinLock<Metadata>,
fs_events_publisher: FsEventPublisher,
fs: Weak<PseudoFs>,
}
@ -206,6 +215,7 @@ impl PseudoInode {
};
PseudoInode {
metadata: SpinLock::new(metadata),
fs_events_publisher: FsEventPublisher::new(),
fs,
}
}
@ -250,6 +260,10 @@ impl Inode for PseudoInode {
*self.metadata.lock()
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_events_publisher
}
fn ino(&self) -> u64 {
self.metadata.lock().ino
}

View File

@ -19,14 +19,15 @@ use crate::{
fs::{
device::Device,
inode_handle::FileIo,
notify::FsEventPublisher,
path::{is_dot, is_dot_or_dotdot, is_dotdot},
pipe::NamedPipe,
registry::{FsProperties, FsType},
utils::{
mkmod, AccessMode, CStr256, CachePage, DirentVisitor, Extension, FallocMode,
FileSystem, FsFlags, Inode, InodeIo, InodeMode, InodeType, Metadata, MknodType,
PageCache, PageCacheBackend, Permission, StatusFlags, SuperBlock, SymbolicLink,
XattrName, XattrNamespace, XattrSetFlags,
FileSystem, FsEventSubscriberStats, FsFlags, Inode, InodeIo, InodeMode, InodeType,
Metadata, MknodType, PageCache, PageCacheBackend, Permission, StatusFlags, SuperBlock,
SymbolicLink, XattrName, XattrNamespace, XattrSetFlags,
},
},
prelude::*,
@ -43,6 +44,8 @@ pub struct RamFs {
root: Arc<RamInode>,
/// An inode allocator
inode_allocator: AtomicU64,
/// FS event subscriber stats for this file system
fs_event_subscriber_stats: FsEventSubscriberStats,
}
impl RamFs {
@ -62,8 +65,10 @@ impl RamFs {
fs: weak_fs.clone(),
extension: Extension::new(),
xattr: RamXattr::new(),
fs_event_publisher: FsEventPublisher::new(),
}),
inode_allocator: AtomicU64::new(ROOT_INO + 1),
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
})
}
@ -89,6 +94,10 @@ impl FileSystem for RamFs {
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
/// An inode of `RamFs`.
@ -107,6 +116,8 @@ pub(super) struct RamInode {
fs: Weak<RamFs>,
/// Extensions
extension: Extension,
/// FS event publisher
fs_event_publisher: FsEventPublisher,
/// Extended attributes
xattr: RamXattr,
}
@ -411,6 +422,7 @@ impl RamInode {
this: weak_self.clone(),
fs: Arc::downgrade(fs),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
xattr: RamXattr::new(),
})
}
@ -424,6 +436,7 @@ impl RamInode {
this: weak_self.clone(),
fs: Arc::downgrade(fs),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
xattr: RamXattr::new(),
})
}
@ -443,6 +456,7 @@ impl RamInode {
this: Weak::new(),
fs: Weak::new(),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
xattr: RamXattr::new(),
}
}
@ -456,6 +470,7 @@ impl RamInode {
this: weak_self.clone(),
fs: Arc::downgrade(fs),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
xattr: RamXattr::new(),
})
}
@ -475,6 +490,7 @@ impl RamInode {
this: weak_self.clone(),
fs: Arc::downgrade(fs),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
xattr: RamXattr::new(),
})
}
@ -488,6 +504,7 @@ impl RamInode {
this: weak_self.clone(),
fs: Arc::downgrade(fs),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
xattr: RamXattr::new(),
})
}
@ -501,6 +518,7 @@ impl RamInode {
this: weak_self.clone(),
fs: Arc::downgrade(fs),
extension: Extension::new(),
fs_event_publisher: FsEventPublisher::new(),
xattr: RamXattr::new(),
})
}
@ -1182,6 +1200,10 @@ impl Inode for RamInode {
Some(&self.extension)
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
fn set_xattr(
&self,
name: XattrName,

View File

@ -22,6 +22,7 @@ use crate::{
file_handle::{FileLike, Mappable},
file_table::FdFlags,
inode_handle::{do_fallocate_util, do_resize_util, do_seek_util},
notify::FsEventPublisher,
path::{check_open_util, RESERVED_MOUNT_ID},
tmpfs::TmpFs,
utils::{
@ -178,6 +179,7 @@ impl Inode for MemfdInode {
fn set_group(&self, gid: Gid) -> Result<()>;
fn page_cache(&self) -> Option<Arc<Vmo>>;
fn extension(&self) -> Option<&Extension>;
fn fs_event_publisher(&self) -> &FsEventPublisher;
fn set_xattr(
&self,
name: XattrName,

View File

@ -6,7 +6,10 @@ use crate::{
fs::{
registry::{FsProperties, FsType},
sysfs::{self, inode::SysFsInode},
utils::{systree_inode::SysTreeInodeTy, FileSystem, FsFlags, Inode, SuperBlock},
utils::{
systree_inode::SysTreeInodeTy, FileSystem, FsEventSubscriberStats, FsFlags, Inode,
SuperBlock,
},
Result,
},
prelude::*,
@ -17,6 +20,7 @@ use crate::{
pub(super) struct SysFs {
sb: SuperBlock,
root: Arc<dyn Inode>,
fs_event_subscriber_stats: FsEventSubscriberStats,
}
const MAGIC_NUMBER: u64 = 0x62656572; // SYSFS_MAGIC
@ -44,6 +48,7 @@ impl SysFs {
Arc::new(Self {
sb,
root: root_inode,
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
})
}
}
@ -65,6 +70,10 @@ impl FileSystem for SysFs {
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
&self.fs_event_subscriber_stats
}
}
pub(super) struct SysFsType;

View File

@ -6,9 +6,12 @@ use ostd::sync::RwLock;
use super::fs::SysFs;
use crate::{
fs::utils::{
systree_inode::{SysTreeInodeTy, SysTreeNodeKind},
FileSystem, Inode, InodeMode, InodeType, Metadata,
fs::{
notify::FsEventPublisher,
utils::{
systree_inode::{SysTreeInodeTy, SysTreeNodeKind},
FileSystem, Inode, InodeMode, InodeType, Metadata,
},
},
prelude::*,
};
@ -25,6 +28,8 @@ pub(super) struct SysFsInode {
/// Currently, the only mutable metadata is `mode`,
/// which allows user space to `chmod` an inode on sysfs.
metadata: Metadata,
/// FS event publisher.
fs_event_publisher: FsEventPublisher,
/// The file mode (permissions) of this inode, protected by a lock.
mode: RwLock<InodeMode>,
/// Weak reference to the parent inode.
@ -46,6 +51,7 @@ impl SysTreeInodeTy for SysFsInode {
Arc::new_cyclic(|this| Self {
node_kind,
metadata,
fs_event_publisher: FsEventPublisher::new(),
mode: RwLock::new(mode),
parent,
this: this.clone(),
@ -78,6 +84,10 @@ impl SysTreeInodeTy for SysFsInode {
.upgrade()
.expect("invalid weak reference to `self`")
}
fn fs_event_publisher(&self) -> &FsEventPublisher {
&self.fs_event_publisher
}
}
impl Inode for SysFsInode {

View File

@ -4,7 +4,7 @@ use crate::{
fs::{
ramfs::RamFs,
registry::{FsProperties, FsType},
utils::{FileSystem, FsFlags, Inode, SuperBlock},
utils::{FileSystem, FsEventSubscriberStats, FsFlags, Inode, SuperBlock},
},
prelude::*,
};
@ -43,6 +43,10 @@ impl FileSystem for TmpFs {
fn sb(&self) -> SuperBlock {
self.inner.sb()
}
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
self.inner.fs_event_subscriber_stats()
}
}
pub(super) struct TmpFsType;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::AtomicU32;
use core::sync::atomic::{AtomicI64, AtomicU32, Ordering};
use atomic_integer_wrapper::define_atomic_version_of_integer_like_type;
@ -103,6 +103,57 @@ define_atomic_version_of_integer_like_type!(FsFlags, {
pub struct AtomicFsFlags(AtomicU32);
});
#[derive(Debug)]
pub struct FsEventSubscriberStats {
// The number of subscribers to this file system.
num_subscribers: AtomicI64,
}
impl FsEventSubscriberStats {
pub fn new() -> Self {
Self {
num_subscribers: AtomicI64::new(0),
}
}
pub fn add_subscriber(&self) {
self.num_subscribers.fetch_add(1, Ordering::Release);
}
pub fn remove_subscriber(&self) {
let subscribers = self.num_subscribers.fetch_sub(1, Ordering::Release);
debug_assert!(
subscribers >= 0,
"The number of subscribers is negative: {}",
subscribers
);
}
pub fn remove_subscribers(&self, num_subscribers: usize) {
let num_subscribers = num_subscribers as i64;
let old_value = self.num_subscribers.load(Ordering::Acquire);
debug_assert!(
old_value >= num_subscribers,
"integer overflow: attempting to remove {} subscribers when only {} exist",
num_subscribers,
old_value
);
let subscribers = self
.num_subscribers
.fetch_sub(num_subscribers, Ordering::Release);
debug_assert!(
subscribers >= 0,
"The number of subscribers is negative: {}",
subscribers
);
}
pub fn has_any_subscribers(&self) -> bool {
self.num_subscribers.load(Ordering::Acquire) > 0
}
}
pub trait FileSystem: Any + Sync + Send {
/// Gets the name of this FS type such as `"ext4"` or `"sysfs"`.
fn name(&self) -> &'static str;
@ -129,6 +180,9 @@ pub trait FileSystem: Any + Sync + Send {
warn!("setting file system flags is not implemented");
Ok(())
}
/// Returns the FS event subscriber stats of this file system.
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats;
}
impl dyn FileSystem {

View File

@ -16,6 +16,7 @@ use crate::{
device::{Device, DeviceType},
fs_resolver::PathOrInode,
inode_handle::FileIo,
notify::FsEventPublisher,
path::Path,
utils::StatusFlags,
},
@ -409,6 +410,16 @@ pub trait Inode: Any + InodeIo + Send + Sync {
None
}
// TODO: Add `FsEventPublisher` as an extension.
//
// Conceptually, an `FsEventPublisher` attached to an inode is also a kind of extension
// as this object is required by the VFS layer, not by FS implementations.
// But we do not add it to `Extension` because `FsEventPublisher` is used in the most time-critical path
// and looking up an extension object in an `Extension` incurs some extra overheads.
// If we could make `Extension` a zero-cost abstraction,
// then `FsEventPublisher` can be moved into `Extension`.
fn fs_event_publisher(&self) -> &FsEventPublisher;
fn set_xattr(
&self,
name: XattrName,

View File

@ -10,7 +10,7 @@ pub use endpoint::{Endpoint, EndpointState};
pub use falloc_mode::FallocMode;
pub use file_creation_mask::{AtomicFileCreationMask, FileCreationMask};
pub use flock::{FlockItem, FlockList, FlockType};
pub use fs::{FileSystem, FsFlags, SuperBlock};
pub use fs::{FileSystem, FsEventSubscriberStats, FsFlags, SuperBlock};
pub use id_bitmap::IdBitmap;
pub use inode::{
Extension, Inode, InodeIo, InodeType, Metadata, MknodType, Permission, SymbolicLink,

View File

@ -13,13 +13,13 @@ use aster_systree::{
SysAttr, SysBranchNode, SysNode, SysNodeId, SysNodeType, SysObj, SysStr, SysSymlink,
};
use super::InodeIo;
use crate::{
fs::{
inode_handle::FileIo,
notify::FsEventPublisher,
utils::{
mkmod, AccessMode, DirentVisitor, FallocMode, FileSystem, Inode, InodeMode, InodeType,
Metadata, MknodType, StatusFlags, SymbolicLink,
mkmod, AccessMode, DirentVisitor, FallocMode, FileSystem, Inode, InodeIo, InodeMode,
InodeType, Metadata, MknodType, StatusFlags, SymbolicLink,
},
},
prelude::*,
@ -54,6 +54,8 @@ pub(in crate::fs) trait SysTreeInodeTy: Send + Sync + 'static {
fn set_mode(&self, mode: InodeMode) -> Result<()>;
fn fs_event_publisher(&self) -> &FsEventPublisher;
fn parent(&self) -> &Weak<Self>;
fn this(&self) -> Arc<Self>
@ -579,6 +581,10 @@ impl<KInode: SysTreeInodeTy + Send + Sync + 'static> Inode for KInode {
default fn is_dentry_cacheable(&self) -> bool {
true
}
default fn fs_event_publisher(&self) -> &FsEventPublisher {
self.fs_event_publisher()
}
}
// Update AttrDentryIter to filter by min_ino