2025-11-04 08:42:12 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
use core::{
|
|
|
|
|
sync::atomic::{AtomicU64, Ordering},
|
|
|
|
|
time::Duration,
|
|
|
|
|
};
|
2025-11-04 08:42:12 +00:00
|
|
|
|
|
|
|
|
use spin::Once;
|
|
|
|
|
|
2025-11-20 15:51:19 +00:00
|
|
|
use super::utils::{Extension, InodeIo, StatusFlags};
|
2025-11-04 08:42:12 +00:00
|
|
|
use crate::{
|
|
|
|
|
fs::{
|
|
|
|
|
registry::{FsProperties, FsType},
|
|
|
|
|
utils::{
|
2025-12-08 12:53:18 +00:00
|
|
|
FileSystem, FsEventSubscriberStats, FsFlags, Inode, InodeMode, InodeType, Metadata,
|
|
|
|
|
NAME_MAX, SuperBlock, mkmod,
|
2025-11-04 08:42:12 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
prelude::*,
|
|
|
|
|
process::{Gid, Uid},
|
|
|
|
|
time::clocks::RealTimeCoarseClock,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// A pseudo file system that manages pseudo inodes, such as pipe inodes and socket inodes.
|
|
|
|
|
pub struct PseudoFs {
|
|
|
|
|
name: &'static str,
|
|
|
|
|
sb: SuperBlock,
|
|
|
|
|
root: Arc<dyn Inode>,
|
2025-12-24 08:50:40 +00:00
|
|
|
inode_allocator: AtomicU64,
|
2025-11-17 15:38:28 +00:00
|
|
|
fs_event_subscriber_stats: FsEventSubscriberStats,
|
2025-11-04 08:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FileSystem for PseudoFs {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
self.name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn sync(&self) -> Result<()> {
|
|
|
|
|
// do nothing
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn root_inode(&self) -> Arc<dyn Inode> {
|
|
|
|
|
self.root.clone()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn sb(&self) -> SuperBlock {
|
|
|
|
|
self.sb.clone()
|
|
|
|
|
}
|
2025-11-17 15:38:28 +00:00
|
|
|
|
|
|
|
|
fn fs_event_subscriber_stats(&self) -> &FsEventSubscriberStats {
|
|
|
|
|
&self.fs_event_subscriber_stats
|
|
|
|
|
}
|
2025-11-04 08:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PseudoFs {
|
|
|
|
|
/// Returns a reference to the singleton pseudo file system.
|
|
|
|
|
fn singleton(
|
|
|
|
|
fs: &'static Once<Arc<Self>>,
|
|
|
|
|
name: &'static str,
|
|
|
|
|
magic: u64,
|
|
|
|
|
) -> &'static Arc<Self> {
|
2025-12-24 08:50:40 +00:00
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/libfs.c#L659-L689>
|
2025-11-04 08:42:12 +00:00
|
|
|
fs.call_once(|| {
|
|
|
|
|
Arc::new_cyclic(|weak_fs: &Weak<Self>| Self {
|
|
|
|
|
name,
|
|
|
|
|
sb: SuperBlock::new(magic, aster_block::BLOCK_SIZE, NAME_MAX),
|
|
|
|
|
root: Arc::new(PseudoInode::new(
|
2025-12-24 08:50:40 +00:00
|
|
|
ROOT_INO,
|
|
|
|
|
InodeType::Dir,
|
2025-11-04 08:42:12 +00:00
|
|
|
mkmod!(u+rw),
|
|
|
|
|
Uid::new_root(),
|
|
|
|
|
Gid::new_root(),
|
|
|
|
|
weak_fs.clone(),
|
|
|
|
|
)),
|
2025-12-24 08:50:40 +00:00
|
|
|
inode_allocator: AtomicU64::new(ROOT_INO + 1),
|
2025-11-17 15:38:28 +00:00
|
|
|
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
|
2025-11-04 08:42:12 +00:00
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
pub fn alloc_inode(
|
|
|
|
|
self: &Arc<Self>,
|
|
|
|
|
type_: InodeType,
|
|
|
|
|
mode: InodeMode,
|
|
|
|
|
uid: Uid,
|
|
|
|
|
gid: Gid,
|
|
|
|
|
) -> PseudoInode {
|
|
|
|
|
PseudoInode::new(self.alloc_id(), type_, mode, uid, gid, Arc::downgrade(self))
|
|
|
|
|
}
|
2025-11-04 08:42:12 +00:00
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
fn alloc_id(&self) -> u64 {
|
|
|
|
|
self.inode_allocator.fetch_add(1, Ordering::Relaxed)
|
|
|
|
|
}
|
2025-11-04 08:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
pub(super) struct PipeFs {
|
|
|
|
|
_private: (),
|
|
|
|
|
}
|
2025-11-04 08:42:12 +00:00
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
impl PipeFs {
|
|
|
|
|
/// Returns the singleton instance of the anonymous pipe file system.
|
|
|
|
|
pub(super) fn singleton() -> &'static Arc<PseudoFs> {
|
|
|
|
|
static PIPEFS: Once<Arc<PseudoFs>> = Once::new();
|
|
|
|
|
|
|
|
|
|
PseudoFs::singleton(&PIPEFS, "pipefs", PIPEFS_MAGIC)
|
|
|
|
|
}
|
2025-11-04 08:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
pub struct SockFs {
|
|
|
|
|
_private: (),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SockFs {
|
|
|
|
|
/// Returns the singleton instance of the socket file system.
|
|
|
|
|
pub fn singleton() -> &'static Arc<PseudoFs> {
|
|
|
|
|
static SOCKFS: Once<Arc<PseudoFs>> = Once::new();
|
|
|
|
|
|
|
|
|
|
PseudoFs::singleton(&SOCKFS, "sockfs", SOCKFS_MAGIC)
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-11-04 08:42:12 +00:00
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
pub struct AnonInodeFs {
|
|
|
|
|
_private: (),
|
2025-11-04 08:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
impl AnonInodeFs {
|
|
|
|
|
/// Returns the singleton instance of the anonymous inode file system.
|
|
|
|
|
fn singleton() -> &'static Arc<PseudoFs> {
|
|
|
|
|
static ANON_INODEFS: Once<Arc<PseudoFs>> = Once::new();
|
|
|
|
|
|
|
|
|
|
PseudoFs::singleton(&ANON_INODEFS, "anon_inodefs", ANON_INODEFS_MAGIC)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Returns the shared inode of the anonymous inode file system singleton.
|
|
|
|
|
//
|
|
|
|
|
// Some members of anon_inodefs (such as epollfd, eventfd, timerfd, etc.) share
|
|
|
|
|
// the same inode. The sharing is not only within the same category (e.g., two
|
|
|
|
|
// epollfds share the same inode) but also across different categories (e.g.,
|
|
|
|
|
// an epollfd and a timerfd share the same inode). Even across namespaces, this
|
|
|
|
|
// inode is still shared. Although this Linux behavior is a bit odd, we keep it
|
|
|
|
|
// for compatibility.
|
|
|
|
|
//
|
|
|
|
|
// A small subset of members in anon_inodefs (i.e., userfaultfd, io_uring, and
|
|
|
|
|
// kvm_guest_memfd) have their own dedicated inodes. We need to support creating
|
|
|
|
|
// independent inodes within anon_inodefs for them in the future.
|
|
|
|
|
//
|
|
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/anon_inodes.c#L153-L164>
|
|
|
|
|
pub fn shared_inode() -> &'static Arc<dyn Inode> {
|
|
|
|
|
static SHARED_INODE: Once<Arc<dyn Inode>> = Once::new();
|
|
|
|
|
|
|
|
|
|
SHARED_INODE.call_once(|| {
|
|
|
|
|
let shared_inode = Self::singleton().alloc_inode(
|
|
|
|
|
InodeType::Unknown,
|
|
|
|
|
mkmod!(u+rw),
|
|
|
|
|
Uid::new_root(),
|
|
|
|
|
Gid::new_root(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Arc::new(shared_inode)
|
|
|
|
|
})
|
|
|
|
|
}
|
2025-11-04 08:42:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn init() {
|
|
|
|
|
super::registry::register(&PipeFsType).unwrap();
|
|
|
|
|
super::registry::register(&SockFsType).unwrap();
|
|
|
|
|
// Note: `AnonInodeFs` does not need to be registered in the FS registry.
|
|
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/A/ident/anon_inode_fs_type>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) struct PipeFsType;
|
|
|
|
|
|
|
|
|
|
impl FsType for PipeFsType {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
"pipefs"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn properties(&self) -> FsProperties {
|
|
|
|
|
FsProperties::empty()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create(
|
|
|
|
|
&self,
|
|
|
|
|
_flags: FsFlags,
|
|
|
|
|
_args: Option<CString>,
|
|
|
|
|
_disk: Option<Arc<dyn aster_block::BlockDevice>>,
|
|
|
|
|
) -> Result<Arc<dyn FileSystem>> {
|
|
|
|
|
return_errno_with_message!(Errno::EINVAL, "pipefs cannot be mounted");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn sysnode(&self) -> Option<Arc<dyn aster_systree::SysNode>> {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) struct SockFsType;
|
|
|
|
|
|
|
|
|
|
impl FsType for SockFsType {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
"sockfs"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn properties(&self) -> FsProperties {
|
|
|
|
|
FsProperties::empty()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn create(
|
|
|
|
|
&self,
|
|
|
|
|
_flags: FsFlags,
|
|
|
|
|
_args: Option<CString>,
|
|
|
|
|
_disk: Option<Arc<dyn aster_block::BlockDevice>>,
|
|
|
|
|
) -> Result<Arc<dyn FileSystem>> {
|
|
|
|
|
return_errno_with_message!(Errno::EINVAL, "sockfs cannot be mounted");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn sysnode(&self) -> Option<Arc<dyn aster_systree::SysNode>> {
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-24 08:50:40 +00:00
|
|
|
/// Root Inode ID.
|
|
|
|
|
const ROOT_INO: u64 = 1;
|
2025-11-04 08:42:12 +00:00
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/include/uapi/linux/magic.h#L87>
|
|
|
|
|
const PIPEFS_MAGIC: u64 = 0x50495045;
|
|
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/include/uapi/linux/magic.h#L89>
|
|
|
|
|
const SOCKFS_MAGIC: u64 = 0x534F434B;
|
|
|
|
|
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/include/uapi/linux/magic.h#L93>
|
|
|
|
|
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>,
|
2025-11-20 15:51:19 +00:00
|
|
|
extension: Extension,
|
2025-11-04 08:42:12 +00:00
|
|
|
fs: Weak<PseudoFs>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl PseudoInode {
|
2025-12-24 08:50:40 +00:00
|
|
|
fn new(
|
2025-11-04 08:42:12 +00:00
|
|
|
ino: u64,
|
|
|
|
|
type_: InodeType,
|
|
|
|
|
mode: InodeMode,
|
|
|
|
|
uid: Uid,
|
|
|
|
|
gid: Gid,
|
|
|
|
|
fs: Weak<PseudoFs>,
|
|
|
|
|
) -> Self {
|
|
|
|
|
let now = now();
|
|
|
|
|
let metadata = Metadata {
|
|
|
|
|
dev: 0,
|
|
|
|
|
ino,
|
|
|
|
|
size: 0,
|
2025-12-24 08:50:40 +00:00
|
|
|
blk_size: aster_block::BLOCK_SIZE,
|
2025-11-04 08:42:12 +00:00
|
|
|
blocks: 0,
|
|
|
|
|
atime: now,
|
|
|
|
|
mtime: now,
|
|
|
|
|
ctime: now,
|
|
|
|
|
type_,
|
|
|
|
|
mode,
|
|
|
|
|
nlinks: 1,
|
|
|
|
|
uid,
|
|
|
|
|
gid,
|
|
|
|
|
rdev: 0,
|
|
|
|
|
};
|
|
|
|
|
PseudoInode {
|
|
|
|
|
metadata: SpinLock::new(metadata),
|
2025-11-20 15:51:19 +00:00
|
|
|
extension: Extension::new(),
|
2025-11-04 08:42:12 +00:00
|
|
|
fs,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:13:34 +00:00
|
|
|
impl InodeIo for PseudoInode {
|
|
|
|
|
fn read_at(
|
|
|
|
|
&self,
|
|
|
|
|
_offset: usize,
|
|
|
|
|
_writer: &mut VmWriter,
|
|
|
|
|
_status: StatusFlags,
|
|
|
|
|
) -> Result<usize> {
|
|
|
|
|
return_errno_with_message!(
|
|
|
|
|
Errno::ESPIPE,
|
|
|
|
|
"pseudo inodes cannot be read at a specific offset"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write_at(
|
|
|
|
|
&self,
|
|
|
|
|
_offset: usize,
|
|
|
|
|
_reader: &mut VmReader,
|
|
|
|
|
_status: StatusFlags,
|
|
|
|
|
) -> Result<usize> {
|
|
|
|
|
return_errno_with_message!(
|
|
|
|
|
Errno::ESPIPE,
|
|
|
|
|
"pseudo inodes cannot be written at a specific offset"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-04 08:42:12 +00:00
|
|
|
impl Inode for PseudoInode {
|
|
|
|
|
fn size(&self) -> usize {
|
|
|
|
|
self.metadata.lock().size
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn resize(&self, _new_size: usize) -> Result<()> {
|
|
|
|
|
return_errno_with_message!(Errno::EINVAL, "pseudo inodes can not be resized");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn metadata(&self) -> Metadata {
|
|
|
|
|
*self.metadata.lock()
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-20 15:51:19 +00:00
|
|
|
fn extension(&self) -> &Extension {
|
|
|
|
|
&self.extension
|
2025-11-17 15:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-04 08:42:12 +00:00
|
|
|
fn ino(&self) -> u64 {
|
|
|
|
|
self.metadata.lock().ino
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn type_(&self) -> InodeType {
|
|
|
|
|
self.metadata.lock().type_
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mode(&self) -> Result<InodeMode> {
|
|
|
|
|
Ok(self.metadata.lock().mode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_mode(&self, mode: InodeMode) -> Result<()> {
|
|
|
|
|
let mut meta = self.metadata.lock();
|
|
|
|
|
meta.mode = mode;
|
|
|
|
|
meta.ctime = now();
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn owner(&self) -> Result<Uid> {
|
|
|
|
|
Ok(self.metadata.lock().uid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_owner(&self, uid: Uid) -> Result<()> {
|
|
|
|
|
let mut meta = self.metadata.lock();
|
|
|
|
|
meta.uid = uid;
|
|
|
|
|
meta.ctime = now();
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn group(&self) -> Result<Gid> {
|
|
|
|
|
Ok(self.metadata.lock().gid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_group(&self, gid: Gid) -> Result<()> {
|
|
|
|
|
let mut meta = self.metadata.lock();
|
|
|
|
|
meta.gid = gid;
|
|
|
|
|
meta.ctime = now();
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn atime(&self) -> Duration {
|
|
|
|
|
self.metadata.lock().atime
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_atime(&self, time: Duration) {
|
|
|
|
|
self.metadata.lock().atime = time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn mtime(&self) -> Duration {
|
|
|
|
|
self.metadata.lock().mtime
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_mtime(&self, time: Duration) {
|
|
|
|
|
self.metadata.lock().mtime = time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn ctime(&self) -> Duration {
|
|
|
|
|
self.metadata.lock().ctime
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_ctime(&self, time: Duration) {
|
|
|
|
|
self.metadata.lock().ctime = time;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fs(&self) -> Arc<dyn FileSystem> {
|
|
|
|
|
self.fs.upgrade().unwrap()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn now() -> Duration {
|
|
|
|
|
RealTimeCoarseClock::get().read_time()
|
|
|
|
|
}
|