diff --git a/kernel/src/arch/x86_64/filesystem/stat.rs b/kernel/src/arch/x86_64/filesystem/stat.rs index 9eec8d027..c9368fad1 100644 --- a/kernel/src/arch/x86_64/filesystem/stat.rs +++ b/kernel/src/arch/x86_64/filesystem/stat.rs @@ -55,7 +55,7 @@ impl TryFrom for PosixStat { tmp.st_uid = kstat.uid; tmp.st_gid = kstat.gid; - tmp.st_rdev = kstat.rdev.data() as usize; + tmp.st_rdev = kstat.rdev.new_encode_dev() as usize; tmp.st_size = kstat.size as isize; tmp.st_atime = kstat.atime.tv_sec as usize; diff --git a/kernel/src/driver/tty/pty/mod.rs b/kernel/src/driver/tty/pty/mod.rs index 42c801a42..3cd8f1a82 100644 --- a/kernel/src/driver/tty/pty/mod.rs +++ b/kernel/src/driver/tty/pty/mod.rs @@ -1,16 +1,9 @@ -use alloc::{ - string::{String, ToString}, - sync::Arc, -}; +use alloc::sync::Arc; use system_error::SystemError; use unified_init::macros::unified_init; use crate::{ - driver::base::device::{ - device_number::{DeviceNumber, Major}, - device_register, IdTable, - }, - filesystem::devfs::devfs_register, + driver::base::device::device_number::Major, init::initcall::INITCALL_DEVICE, libs::lazy_init::Lazy, mm::VirtAddr, @@ -22,7 +15,6 @@ use self::unix98pty::{Unix98PtyDriverInner, NR_UNIX98_PTY_MAX}; use super::{ termios::{ControlMode, InputMode, LocalMode, OutputMode, TTY_STD_TERMIOS}, tty_core::{TtyCore, TtyCoreData, TtyFlag, TtyPacketStatus}, - tty_device::{TtyDevice, TtyType}, tty_driver::{TtyDriver, TtyDriverManager, TtyDriverSubType, TtyDriverType, TTY_DRIVERS}, tty_port::{DefaultTtyPort, TtyPort}, }; @@ -165,20 +157,6 @@ impl PtyCommon { ptm_driver.set_other_pty_driver(Arc::downgrade(&pts_driver)); pts_driver.set_other_pty_driver(Arc::downgrade(&ptm_driver)); - let idt = IdTable::new( - String::from("ptmx"), - Some(DeviceNumber::new(Major::TTYAUX_MAJOR, 2)), - ); - let ptmx_dev = TtyDevice::new( - "ptmx".to_string(), - idt.clone(), - TtyType::Pty(super::tty_device::PtyType::Ptm), - ); - - ptmx_dev.inner_write().metadata_mut().raw_dev = idt.device_number(); - device_register(ptmx_dev.clone())?; - devfs_register("ptmx", ptmx_dev)?; - TTY_DRIVERS.lock().push(ptm_driver); TTY_DRIVERS.lock().push(pts_driver); diff --git a/kernel/src/driver/tty/pty/unix98pty.rs b/kernel/src/driver/tty/pty/unix98pty.rs index de644100b..b2a62ebb3 100644 --- a/kernel/src/driver/tty/pty/unix98pty.rs +++ b/kernel/src/driver/tty/pty/unix98pty.rs @@ -1,24 +1,24 @@ -use alloc::{string::ToString, sync::Arc}; +use alloc::{ + string::ToString, + sync::{Arc, Weak}, +}; +use core::sync::atomic::{AtomicBool, Ordering}; use system_error::SystemError; use crate::{ driver::tty::{ termios::{ControlCharIndex, ControlMode, InputMode, LocalMode, Termios}, tty_core::{TtyCore, TtyCoreData, TtyFlag, TtyIoctlCmd, TtyPacketStatus}, - tty_device::TtyFilePrivateData, + tty_device::{TtyDevice, TtyFilePrivateData}, tty_driver::{TtyDriver, TtyDriverPrivateData, TtyDriverSubType, TtyOperation}, }, filesystem::{ devpts::DevPtsFs, epoll::{event_poll::EventPoll, EPollEventType}, - vfs::{ - file::FileFlags, FilePrivateData, FileType, InodeMode, MountFS, - VFS_MAX_FOLLOW_SYMLINK_TIMES, - }, + vfs::{file::FileFlags, FilePrivateData, FileSystem, FileType, IndexNode, InodeMode}, }, - libs::spinlock::SpinLockGuard, + libs::{casting::DowncastArc, spinlock::SpinLockGuard}, mm::VirtAddr, - process::ProcessManager, syscall::user_access::UserBufferWriter, }; @@ -26,6 +26,85 @@ use super::{ptm_driver, pts_driver, PtyCommon}; pub const NR_UNIX98_PTY_MAX: u32 = 128; +#[derive(Debug)] +struct PtyDevPtsLink { + /// devpts 挂载点根目录(/dev/pts 的 inode),用于精确 unlink 目录项 + pts_root: Weak, + /// devpts 文件系统本体,用于精确回收索引(避免再去 downcast/全局路径查找) + devpts: Weak, + index: usize, + /// master 侧(ptmx)最后一个 fd 已关闭 + master_closed: AtomicBool, + /// slave 侧(/dev/pts/N)最后一个 fd 已关闭 + slave_closed: AtomicBool, + /// 目录项是否已经 unlink(通常在 master close 时执行) + unlinked: AtomicBool, + /// 索引是否已经归还(仅在 master+slave 都关闭后才允许归还) + index_freed: AtomicBool, +} + +impl crate::driver::tty::tty_driver::TtyCorePrivateField for PtyDevPtsLink { + fn as_any(&self) -> &dyn core::any::Any { + self + } +} + +impl PtyDevPtsLink { + fn new(pts_root: Weak, devpts: Weak, index: usize) -> Self { + Self { + pts_root, + devpts, + index, + master_closed: AtomicBool::new(false), + slave_closed: AtomicBool::new(false), + unlinked: AtomicBool::new(false), + index_freed: AtomicBool::new(false), + } + } + + fn on_close(&self, subtype: TtyDriverSubType) { + match subtype { + TtyDriverSubType::PtyMaster => { + self.master_closed.store(true, Ordering::SeqCst); + // Linux 语义:master 关闭后,/dev/pts/N 目录项应从 devpts 中消失; + // 但索引不能立即复用(slave 可能仍持有打开的 fd),因此 unlink 与 free_index 分离。 + self.try_unlink_once(); + } + TtyDriverSubType::PtySlave => { + self.slave_closed.store(true, Ordering::SeqCst); + } + _ => {} + } + + self.try_free_index_when_fully_closed(); + } + + fn try_unlink_once(&self) { + if self.unlinked.swap(true, Ordering::SeqCst) { + return; + } + if let Some(root) = self.pts_root.upgrade() { + let _ = root.unlink(&self.index.to_string()); + } + } + + fn try_free_index_when_fully_closed(&self) { + if !(self.master_closed.load(Ordering::SeqCst) && self.slave_closed.load(Ordering::SeqCst)) + { + return; + } + if self.index_freed.swap(true, Ordering::SeqCst) { + return; + } + + // 兜底:如果 master 未触发 unlink(异常路径),在最终回收时再尝试一次。 + self.try_unlink_once(); + if let Some(devpts) = self.devpts.upgrade() { + devpts.free_index(self.index); + } + } +} + #[derive(Debug)] pub struct Unix98PtyDriverInner; @@ -220,12 +299,15 @@ impl TtyOperation for Unix98PtyDriverInner { fn close(&self, tty: Arc) -> Result<(), SystemError> { let driver = tty.core().driver(); - let root_inode = ProcessManager::current_mntns().root_inode(); - if tty.core().driver().tty_driver_sub_type() == TtyDriverSubType::PtySlave { + // 通过 hook 精确管理 devpts 目录项与索引生命周期 + if let Some(hook_arc) = tty.private_fields() { + if let Some(hook) = hook_arc.as_any().downcast_ref::() { + hook.on_close(driver.tty_driver_sub_type()); + } + } + + if driver.tty_driver_sub_type() == TtyDriverSubType::PtySlave { driver.ttys().remove(&tty.core().index()); - let pts_root_inode = - root_inode.lookup_follow_symlink("/dev/pts", VFS_MAX_FOLLOW_SYMLINK_TIMES)?; - let _ = pts_root_inode.unlink(&tty.core().index().to_string()); if let Some(link) = tty.core().link() { let link_core = link.core(); // set OTHER_CLOSED flag to tell master side that the slave side is closed @@ -237,6 +319,17 @@ impl TtyOperation for Unix98PtyDriverInner { let epitems = link_core.epitems(); let _ = EventPoll::wakeup_epoll(epitems, EPollEventType::EPOLLHUP); } + } else if driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster { + // master 侧最后关闭:从 driver 表移除自身(避免泄漏);devpts 的释放由 hook 统一处理 + driver.ttys().remove(&tty.core().index()); + if let Some(link) = tty.core().link() { + let link_core = link.core(); + link_core.flags_write().insert(TtyFlag::OTHER_CLOSED); + link_core.read_wq().wakeup_all(); + link_core.write_wq().wakeup_all(); + let epitems = link_core.epitems(); + let _ = EventPoll::wakeup_epoll(epitems, EPollEventType::EPOLLHUP); + } } Ok(()) @@ -262,6 +355,7 @@ impl TtyOperation for Unix98PtyDriverInner { } pub fn ptmx_open( + this: &TtyDevice, mut data: SpinLockGuard, flags: &FileFlags, ) -> Result<(), SystemError> { @@ -271,17 +365,14 @@ pub fn ptmx_open( tty.core().add_count(); return Ok(()); } - let root_inode = ProcessManager::current_mntns().root_inode(); - let pts_root_inode = - root_inode.lookup_follow_symlink("/dev/pts", VFS_MAX_FOLLOW_SYMLINK_TIMES)?; - - let fs = pts_root_inode - .fs() - .as_any_ref() - .downcast_ref::() - .unwrap() - .inner_filesystem(); - let fsinfo = fs.as_any_ref().downcast_ref::().unwrap(); + // 根据当前节点所属的文件系统决定 devpts 根 + let (pts_root_inode, fsinfo) = + if let Some(devpts) = this.fs().clone().downcast_arc::() { + let root_inode = devpts.root_inode(); + (root_inode, devpts) + } else { + return Err(SystemError::ENODEV); + }; let index = fsinfo.alloc_index()?; @@ -302,6 +393,19 @@ pub fn ptmx_open( InodeMode::from_bits_truncate(0x666), )?; + // 在 master/slave 两端记录 devpts 根目录与 fs,用于精确清理: + // - master close: unlink /dev/pts/N + // - master+slave 都 close: free_index(N) + let hook = Arc::new(PtyDevPtsLink::new( + Arc::downgrade(&pts_root_inode), + Arc::downgrade(&fsinfo), + index, + )); + tty.set_private_fields(hook.clone()); + if let Some(slave) = tty.core().link() { + slave.set_private_fields(hook); + } + ptm_driver().driver_funcs().open(core)?; Ok(()) diff --git a/kernel/src/driver/tty/tty_core.rs b/kernel/src/driver/tty/tty_core.rs index 54d6958b6..2f7cdd190 100644 --- a/kernel/src/driver/tty/tty_core.rs +++ b/kernel/src/driver/tty/tty_core.rs @@ -652,9 +652,10 @@ impl TtyOperation for TtyCore { self.core().dec_count(); let cnt = self.core().count(); // TODO: fix pty slave close issue - let is_pty_slave_last = - cnt == 1 && tty.core().driver().tty_driver_sub_type() == TtyDriverSubType::PtySlave; - if !self.core().count_valid() || is_pty_slave_last { + let subtype = tty.core().driver().tty_driver_sub_type(); + let is_pty_slave_last = cnt == 1 && subtype == TtyDriverSubType::PtySlave; + let is_pty_master_last = cnt == 1 && subtype == TtyDriverSubType::PtyMaster; + if !self.core().count_valid() || is_pty_slave_last || is_pty_master_last { // log::debug!( // "TtyCore close: ref count: {}, tty: {}", // cnt, diff --git a/kernel/src/driver/tty/tty_device.rs b/kernel/src/driver/tty/tty_device.rs index 3a086ace0..df04019c0 100644 --- a/kernel/src/driver/tty/tty_device.rs +++ b/kernel/src/driver/tty/tty_device.rs @@ -79,13 +79,16 @@ impl InnerTtyDevice { inode: None, driver: None, can_match: false, - metadata: Metadata::new(FileType::CharDevice, InodeMode::from_bits_truncate(0o755)), + metadata: { + let mut md = + Metadata::new(FileType::CharDevice, InodeMode::from_bits_truncate(0o755)); + // 对于字符设备,选择与 Linux devpts/devfs 一致的首选 I/O 块大小。 + // 1024 满足测试期望 (允许 1024 或 4096)。 + md.blk_size = 1024; + md + }, } } - - pub fn metadata_mut(&mut self) -> &mut Metadata { - &mut self.metadata - } } #[derive(Debug, PartialEq)] @@ -225,15 +228,28 @@ impl IndexNode for TtyDevice { mode: &crate::filesystem::vfs::file::FileFlags, ) -> Result<(), SystemError> { if self.tty_type == TtyType::Pty(PtyType::Ptm) { - return ptmx_open(data, mode); + return ptmx_open(self, data, mode); } let dev_num = self.metadata()?.raw_dev; + // /dev/tty 仅在已有控制终端时才能打开;否则返回 ENXIO + let mut tty = if dev_num == DeviceNumber::new(Major::TTYAUX_MAJOR, 0) { + if let Some(current) = self.open_current_tty(dev_num, &mut data) { + Some(current) + } else { + return Err(SystemError::ENXIO); + } + } else { + None + }; + // log::debug!( // "TtyDevice::open: dev_num: {}, current pid: {}", // dev_num, // ProcessManager::current_pid() // ); - let mut tty = self.open_current_tty(dev_num, &mut data); + if tty.is_none() { + tty = self.open_current_tty(dev_num, &mut data); + } if tty.is_none() { let (index, driver) = TtyDriverManager::lookup_tty_driver(dev_num).ok_or(SystemError::ENODEV)?; @@ -260,10 +276,8 @@ impl IndexNode for TtyDevice { } let driver = tty.core().driver(); - // 考虑noctty(当前tty) + // 考虑 O_NOCTTY:显式指定则不设置控制终端;pty master 也不会成为控制终端。 if !(mode.contains(FileFlags::O_NOCTTY) - && dev_num == DeviceNumber::new(Major::TTY_MAJOR, 0) - || dev_num == DeviceNumber::new(Major::TTYAUX_MAJOR, 1) || (driver.tty_driver_type() == TtyDriverType::Pty && driver.tty_driver_sub_type() == TtyDriverSubType::PtyMaster)) { diff --git a/kernel/src/driver/tty/tty_driver.rs b/kernel/src/driver/tty/tty_driver.rs index 9c9515862..2038a9211 100644 --- a/kernel/src/driver/tty/tty_driver.rs +++ b/kernel/src/driver/tty/tty_driver.rs @@ -1,4 +1,4 @@ -use core::fmt::Debug; +use core::{any::Any, fmt::Debug}; use alloc::{ string::{String, ToString}, @@ -102,7 +102,9 @@ impl TtyDriverManager { /// tty 驱动程序的与设备相关的数据 pub trait TtyDriverPrivateField: Debug + Send + Sync {} -pub trait TtyCorePrivateField: Debug + Send + Sync {} +pub trait TtyCorePrivateField: Debug + Send + Sync + Any { + fn as_any(&self) -> &dyn Any; +} #[allow(dead_code)] #[derive(Debug)] @@ -336,8 +338,13 @@ impl TtyDriver { // log::debug!("init_tty_device: to ldisc_setup"); TtyLdiscManager::ldisc_setup(tty.clone(), tty.core().link())?; - // 在devfs创建对应的文件 + // 对 PTY 来说,用户可见的设备节点由 devpts 挂载点下的动态节点提供, + // 不应再向全局 devfs 注册(否则在新实例复用索引时会因已有的 ptm/ptsX 节点返回 EEXIST)。 + if self.tty_driver_type == TtyDriverType::Pty { + return Ok(tty); + } + // 在devfs创建对应的文件 // log::debug!("init_tty_device: to new tty device"); let device = TtyDevice::new( core.name().clone(), diff --git a/kernel/src/driver/tty/tty_job_control.rs b/kernel/src/driver/tty/tty_job_control.rs index 6f35933e5..402dfca70 100644 --- a/kernel/src/driver/tty/tty_job_control.rs +++ b/kernel/src/driver/tty/tty_job_control.rs @@ -117,6 +117,7 @@ impl TtyJobCtrlManager { TtyIoctlCmd::TIOCGPGRP => Self::tiocgpgrp(real_tty, arg), TtyIoctlCmd::TIOCGSID => Self::tiocgsid(real_tty, arg), TtyIoctlCmd::TIOCSCTTY => Self::tiocsctty(real_tty, arg), + TtyIoctlCmd::TIOCNOTTY => Self::tiocnotty(real_tty), _ => { return Err(SystemError::ENOIOCTLCMD); } @@ -312,6 +313,23 @@ impl TtyJobCtrlManager { return sid; } + /// Detach controlling tty from current process if it matches `real_tty`. + fn tiocnotty(real_tty: Arc) -> Result { + let pcb = ProcessManager::current_pcb(); + let mut siginfo = pcb.sig_info_mut(); + if let Some(cur) = siginfo.tty() { + if Arc::ptr_eq(&cur, &real_tty) { + Self::__proc_clear_tty(&mut siginfo); + drop(siginfo); + let mut ctrl = real_tty.core().contorl_info_irqsave(); + ctrl.session = None; + ctrl.pgid = None; + return Ok(0); + } + } + Err(SystemError::ENOTTY) + } + pub(super) fn session_clear_tty(sid: Arc) { // 清除会话的tty for task in sid.tasks_iter(PidType::SID) { diff --git a/kernel/src/filesystem/devfs/mod.rs b/kernel/src/filesystem/devfs/mod.rs index 9c0bd6741..00fea08ee 100644 --- a/kernel/src/filesystem/devfs/mod.rs +++ b/kernel/src/filesystem/devfs/mod.rs @@ -138,6 +138,8 @@ impl DevFS { root.add_dir("block") .expect("DevFS: Failed to create /dev/block"); + // 预创建 /dev/ptmx 符号链接指向 devpts 内部节点,避免早期 ENOENT + let _ = root.add_dev_symlink("pts/ptmx", "ptmx"); devfs.register_bultinin_device(); // debug!("ls /dev: {:?}", root.list()); @@ -175,6 +177,12 @@ impl DevFS { match metadata.file_type { // 字节设备挂载在 /dev/char FileType::CharDevice => { + // 对 ptmx 使用符号链接至 devpts 内部节点,避免重复注册字符设备。 + if name == "ptmx" { + dev_root_inode.add_dev_symlink("pts/ptmx", name)?; + return Ok(()); + } + if dev_root_inode.find("char").is_err() { dev_root_inode.create( "char", @@ -770,6 +778,19 @@ pub fn devfs_register(name: &str, device: Arc) -> Result<(), return devfs_exact_ref!().register_device(name, device); } +/// 在 /dev 下创建符号链接 +#[allow(dead_code)] +pub fn devfs_add_symlink(link_name: &str, target: &str) -> Result<(), SystemError> { + let dev_inode = ProcessManager::current_mntns() + .root_inode() + .find("dev") + .map_err(|_| SystemError::ENOENT)?; + let dev_inode = dev_inode + .downcast_arc::() + .ok_or(SystemError::ENOENT)?; + dev_inode.add_dev_symlink(target, link_name) +} + /// @brief devfs的设备卸载函数 #[allow(dead_code)] pub fn devfs_unregister(name: &str, device: Arc) -> Result<(), SystemError> { @@ -814,6 +835,7 @@ pub fn devfs_init() -> Result<(), SystemError> { } else { warn!("Cannot find /dev mountpoint for /dev/shm"); } + result = Some(Ok(())); }); diff --git a/kernel/src/filesystem/devpts/mod.rs b/kernel/src/filesystem/devpts/mod.rs index 9a3b0d6e9..654e21c87 100644 --- a/kernel/src/filesystem/devpts/mod.rs +++ b/kernel/src/filesystem/devpts/mod.rs @@ -1,4 +1,7 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::{ + any::Any, + sync::atomic::{AtomicU32, Ordering}, +}; use crate::{ driver::{ @@ -13,7 +16,7 @@ use crate::{ }, filesystem::vfs::{ mount::{do_mount_mkdir, MountFlags}, - FileType, InodeMode, + FileSystem, FileType, FsInfo, InodeMode, MountableFileSystem, SuperBlock, FSMAKER, }, libs::spinlock::{SpinLock, SpinLockGuard}, time::PosixTimeSpec, @@ -30,37 +33,70 @@ use system_error::SystemError; use super::{ devfs::DeviceINode, - vfs::{ - vcore::generate_inode_id, FilePrivateData, FileSystem, FsInfo, IndexNode, InodeFlags, - Metadata, - }, + vfs::{vcore::generate_inode_id, FilePrivateData, IndexNode, InodeFlags, Magic, Metadata}, }; +use crate::{filesystem::vfs::FileSystemMakerData, register_mountable_fs}; +use linkme::distributed_slice; const DEV_PTYFS_MAX_NAMELEN: usize = 16; #[allow(dead_code)] const PTY_NR_LIMIT: usize = 4096; +#[derive(Debug, Clone)] +struct DevPtsOptions { + root_mode: InodeMode, + pts_mode: InodeMode, + ptmx_mode: InodeMode, + new_instance: bool, +} + +impl Default for DevPtsOptions { + fn default() -> Self { + Self { + root_mode: InodeMode::from_bits_truncate(0o755), + pts_mode: InodeMode::from_bits_truncate(0o620), + ptmx_mode: InodeMode::from_bits_truncate(0o666), + new_instance: false, + } + } +} + +impl FileSystemMakerData for DevPtsOptions { + fn as_any(&self) -> &dyn Any { + self + } +} + #[derive(Debug)] pub struct DevPtsFs { /// 根节点 root_inode: Arc, pts_ida: SpinLock, pts_count: AtomicU32, + opts: DevPtsOptions, } impl DevPtsFs { - pub fn new() -> Arc { + fn new_with_opts(opts: DevPtsOptions) -> Arc { let root_inode = Arc::new(LockedDevPtsFSInode::new()); root_inode.inner.lock().parent = Arc::downgrade(&root_inode); root_inode.inner.lock().self_ref = Arc::downgrade(&root_inode); + { + // 设置根目录的权限与块大小 + let md = &mut root_inode.inner.lock().metadata; + md.mode = opts.root_mode | InodeMode::S_IFDIR; + md.blk_size = 1024; + } let ret = Arc::new(Self { root_inode, pts_ida: SpinLock::new(IdAllocator::new(0, NR_UNIX98_PTY_MAX as usize).unwrap()), pts_count: AtomicU32::new(0), + opts, }); ret.root_inode.set_fs(Arc::downgrade(&ret)); + ret.install_ptmx_node(); ret } @@ -68,6 +104,45 @@ impl DevPtsFs { pub fn alloc_index(&self) -> Result { self.pts_ida.lock().alloc().ok_or(SystemError::ENOSPC) } + + pub fn free_index(&self, idx: usize) { + self.pts_ida.lock().free(idx); + self.pts_count.fetch_sub(1, Ordering::SeqCst); + } + + fn install_ptmx_node(&self) { + // devpts 内部的 ptmx 节点,供 newinstance 使用。 + // 只创建一次,若已存在则忽略。 + let mut guard = self.root_inode.inner.lock(); + if guard.children_unchecked().contains_key("ptmx") { + return; + } + + let dev_num = DeviceNumber::new(Major::TTYAUX_MAJOR, 2); + let ptmx_dev = TtyDevice::new( + "ptmx".to_string(), + IdTable::new("ptmx".to_string(), Some(dev_num)), + TtyType::Pty(PtyType::Ptm), + ); + let mut md = ptmx_dev.metadata().unwrap(); + md.mode = self.opts.ptmx_mode | InodeMode::S_IFCHR; + md.raw_dev = dev_num; + md.blk_size = 1024; + let _ = ptmx_dev.set_metadata(&md); + if let Some(fs_arc) = guard.fs.upgrade() { + ptmx_dev.set_devpts_fs(Arc::downgrade(&fs_arc)); + } + ptmx_dev.set_devpts_parent(guard.self_ref.clone()); + + guard + .children_unchecked_mut() + .insert("ptmx".to_string(), ptmx_dev); + } + + #[allow(dead_code)] + pub fn is_new_instance(&self) -> bool { + self.opts.new_instance + } } impl FileSystem for DevPtsFs { @@ -75,7 +150,7 @@ impl FileSystem for DevPtsFs { self.root_inode.clone() } - fn info(&self) -> super::vfs::FsInfo { + fn info(&self) -> FsInfo { return FsInfo { blk_dev_id: 0, max_name_len: DEV_PTYFS_MAX_NAMELEN, @@ -91,10 +166,33 @@ impl FileSystem for DevPtsFs { } fn super_block(&self) -> super::vfs::SuperBlock { - todo!() + SuperBlock::new(Magic::DEVPTS_MAGIC, 1024, DEV_PTYFS_MAX_NAMELEN as u64) } } +impl MountableFileSystem for DevPtsFs { + fn make_mount_data( + raw_data: Option<&str>, + _source: &str, + ) -> Result>, SystemError> { + // 对于 devpts,挂载数据只是解析选项,将结果以 Box 形式传递 + let opts = parse_devpts_options(raw_data); + Ok(Some(Arc::new(opts))) + } + + fn make_fs( + data: Option<&dyn FileSystemMakerData>, + ) -> Result, SystemError> { + let opts = data + .and_then(|d| d.as_any().downcast_ref::()) + .cloned() + .unwrap_or_default(); + Ok(Self::new_with_opts(opts)) + } +} + +register_mountable_fs!(DevPtsFs, DEVPTS_MAKER, "devpts"); + #[derive(Debug)] pub struct LockedDevPtsFSInode { inner: SpinLock, @@ -112,7 +210,7 @@ impl LockedDevPtsFSInode { dev_id: 0, inode_id: generate_inode_id(), size: 0, - blk_size: 0, + blk_size: 1024, blocks: 0, atime: PosixTimeSpec::default(), mtime: PosixTimeSpec::default(), @@ -252,9 +350,10 @@ impl IndexNode for LockedDevPtsFSInode { let mut metadata = result.metadata()?; - metadata.mode.insert(InodeMode::S_IFCHR); + metadata.mode = fs.opts.pts_mode | InodeMode::S_IFCHR; metadata.raw_dev = DeviceNumber::new(Major::UNIX98_PTY_SLAVE_MAJOR, name.parse::().unwrap()); + metadata.blk_size = 1024; result.set_metadata(&metadata)?; @@ -289,10 +388,32 @@ impl IndexNode for LockedDevPtsFSInode { pub fn devpts_init() -> Result<(), SystemError> { // 创建 devptsfs 实例 - let ptsfs: Arc = DevPtsFs::new(); + let ptsfs: Arc = DevPtsFs::new_with_opts(DevPtsOptions::default()); do_mount_mkdir(ptsfs, "/dev/pts", MountFlags::empty()).expect("Failed to mount DevPtsFS"); info!("DevPtsFs mounted."); Ok(()) } + +fn parse_devpts_options(raw: Option<&str>) -> DevPtsOptions { + let mut opts = DevPtsOptions::default(); + if let Some(raw) = raw { + for item in raw.split(',').map(str::trim).filter(|s| !s.is_empty()) { + if let Some(val) = item.strip_prefix("mode=") { + if let Ok(bits) = u32::from_str_radix(val, 8) { + let mode = InodeMode::from_bits_truncate(bits); + opts.pts_mode = mode; + opts.root_mode = mode; + } + } else if let Some(val) = item.strip_prefix("ptmxmode=") { + if let Ok(bits) = u32::from_str_radix(val, 8) { + opts.ptmx_mode = InodeMode::from_bits_truncate(bits); + } + } else if item == "newinstance" { + opts.new_instance = true; + } + } + } + opts +} diff --git a/kernel/src/filesystem/ext4/filesystem.rs b/kernel/src/filesystem/ext4/filesystem.rs index eb2c49ccb..ffbbb604d 100644 --- a/kernel/src/filesystem/ext4/filesystem.rs +++ b/kernel/src/filesystem/ext4/filesystem.rs @@ -5,8 +5,8 @@ use crate::filesystem::vfs::fcntl::AtFlags; use crate::filesystem::vfs::utils::{user_path_at, DName}; use crate::filesystem::vfs::vcore::{generate_inode_id, try_find_gendisk}; use crate::filesystem::vfs::{ - self, FileSystem, FileSystemMaker, FileSystemMakerData, IndexNode, Magic, MountableFileSystem, - FSMAKER, VFS_MAX_FOLLOW_SYMLINK_TIMES, + self, FileSystem, FileSystemMakerData, IndexNode, Magic, MountableFileSystem, FSMAKER, + VFS_MAX_FOLLOW_SYMLINK_TIMES, }; use crate::libs::spinlock::SpinLock; use crate::mm::fault::{PageFaultHandler, PageFaultMessage}; diff --git a/kernel/src/filesystem/fat/mount.rs b/kernel/src/filesystem/fat/mount.rs index 4236193e3..630d71048 100644 --- a/kernel/src/filesystem/fat/mount.rs +++ b/kernel/src/filesystem/fat/mount.rs @@ -11,7 +11,6 @@ use crate::{ use alloc::sync::Arc; use system_error::SystemError; -use crate::filesystem::vfs::FileSystemMaker; use linkme::distributed_slice; use super::fs::FATFileSystem; diff --git a/kernel/src/filesystem/ramfs/mod.rs b/kernel/src/filesystem/ramfs/mod.rs index 44f1fec65..320e6a664 100644 --- a/kernel/src/filesystem/ramfs/mod.rs +++ b/kernel/src/filesystem/ramfs/mod.rs @@ -24,8 +24,8 @@ use alloc::{ use system_error::SystemError; use super::vfs::{ - file::FilePrivateData, utils::DName, FileSystem, FileSystemMaker, FsInfo, IndexNode, - InodeFlags, InodeId, InodeMode, Metadata, SpecialNodeData, + file::FilePrivateData, utils::DName, FileSystem, FsInfo, IndexNode, InodeFlags, InodeId, + InodeMode, Metadata, SpecialNodeData, }; use linkme::distributed_slice; diff --git a/kernel/src/filesystem/tmpfs/mod.rs b/kernel/src/filesystem/tmpfs/mod.rs index d03e0a517..739c5fd26 100644 --- a/kernel/src/filesystem/tmpfs/mod.rs +++ b/kernel/src/filesystem/tmpfs/mod.rs @@ -25,8 +25,8 @@ use alloc::{ use system_error::SystemError; use super::vfs::{ - file::FilePrivateData, utils::DName, FileSystem, FileSystemMaker, FsInfo, IndexNode, - InodeFlags, InodeId, InodeMode, Metadata, SpecialNodeData, + file::FilePrivateData, utils::DName, FileSystem, FsInfo, IndexNode, InodeFlags, InodeId, + InodeMode, Metadata, SpecialNodeData, }; use linkme::distributed_slice; diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index 500fdfd78..557af3161 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -1168,6 +1168,7 @@ bitflags! { const KER_MAGIC = 0x3153464b; const PROC_MAGIC = 0x9fa0; const RAMFS_MAGIC = 0x858458f6; + const DEVPTS_MAGIC = 0x1cd1; const MOUNT_MAGIC = 61267; const PIPEFS_MAGIC = 0x50495045; } @@ -1257,19 +1258,20 @@ macro_rules! register_mountable_fs { } #[distributed_slice(FSMAKER)] - static $maker_name: FileSystemMaker = FileSystemMaker::new( - $fs_name, - &($fs::make_fs_bridge - as fn( - Option<&dyn FileSystemMakerData>, - ) -> Result, SystemError>), - &($fs::make_mount_data_bridge - as fn( - Option<&str>, - &str, - ) - -> Result>, SystemError>), - ); + static $maker_name: $crate::filesystem::vfs::FileSystemMaker = + $crate::filesystem::vfs::FileSystemMaker::new( + $fs_name, + &($fs::make_fs_bridge + as fn( + Option<&dyn FileSystemMakerData>, + ) -> Result, SystemError>), + &($fs::make_mount_data_bridge + as fn( + Option<&str>, + &str, + ) + -> Result>, SystemError>), + ); }; } diff --git a/kernel/src/filesystem/vfs/mount.rs b/kernel/src/filesystem/vfs/mount.rs index ef4c47607..bd3453f2c 100644 --- a/kernel/src/filesystem/vfs/mount.rs +++ b/kernel/src/filesystem/vfs/mount.rs @@ -853,10 +853,6 @@ impl IndexNode for MountFSInode { return Err(SystemError::ENOTDIR); } - if self.is_mountpoint_root()? { - return Err(SystemError::EBUSY); - } - // 若已有挂载系统,保证MountFS只包一层 let to_mount_fs = fs .clone() @@ -1124,9 +1120,18 @@ pub struct MountList { inner: RwLock, } +#[derive(Clone, Debug)] +struct MountRecord { + fs: Arc, + ino: Option, +} + struct InnerMountList { - mounts: HashMap, Arc>, + /// 同一路径可能被重复挂载,按栈保存,栈顶为当前可见挂载。 + mounts: HashMap, Vec>, + /// 便于通过 fs 反查挂载点 inode。 mfs2ino: HashMap, InodeId>, + /// inode 到路径的映射,用于子挂载查找。 ino2mp: HashMap>, } @@ -1163,12 +1168,16 @@ impl MountList { #[inline(never)] pub fn insert(&self, ino: Option, path: Arc, fs: Arc) { let mut inner = self.inner.write(); - inner.mounts.insert(path.clone(), fs.clone()); - // 如果不是根目录挂载点,则记录inode到挂载点的映射 + let entry = inner.mounts.entry(path.clone()).or_default(); + entry.push(MountRecord { + fs: fs.clone(), + ino, + }); if let Some(ino) = ino { inner.ino2mp.insert(ino, path.clone()); - inner.mfs2ino.insert(fs, ino); + inner.mfs2ino.insert(fs.clone(), ino); } + // 若 ino 为 None(如根挂载),仍然保留 mounts 栈用于后续 pop。 } /// # get_mount_point - 获取挂载点的路径 @@ -1194,10 +1203,12 @@ impl MountList { .upgradeable_read() .mounts .iter() - .filter_map(|(key, fs)| { + .filter_map(|(key, stack)| { let strkey = key.as_str(); if let Some(rest) = path.as_ref().strip_prefix(strkey) { - return Some((key.clone(), rest.to_string(), fs.clone())); + return stack + .last() + .map(|rec| (key.clone(), rest.to_string(), rec.fs.clone())); } None }) @@ -1221,19 +1232,34 @@ impl MountList { pub fn remove>(&self, path: T) -> Option> { let mut inner = self.inner.write(); let path: MountPath = path.into(); - // 从挂载点列表中移除指定路径的挂载点 - if let Some(fs) = inner.mounts.remove(&path) { - if let Some(ino) = inner.mfs2ino.remove(&fs) { - inner.ino2mp.remove(&ino); + if let Some(stack) = inner.mounts.get_mut(&path) { + if let Some(rec) = stack.pop() { + let empty = stack.is_empty(); + let rec_fs = rec.fs.clone(); + let rec_ino = rec.ino; + if empty { + inner.mounts.remove(&path); + } + if let Some(ino) = inner.mfs2ino.remove(&rec_fs) { + inner.ino2mp.remove(&ino); + } + if let Some(ino) = rec_ino { + inner.ino2mp.remove(&ino); + } + return Some(rec_fs); } - return Some(fs); } None } /// # clone_inner - 克隆内部挂载点列表 pub fn clone_inner(&self) -> HashMap, Arc> { - self.inner.read().mounts.clone() + self.inner + .read() + .mounts + .iter() + .map(|(p, stack)| (p.clone(), stack.last().unwrap().fs.clone())) + .collect() } #[inline(never)] diff --git a/kernel/src/filesystem/vfs/stat.rs b/kernel/src/filesystem/vfs/stat.rs index bf40599f2..eeee249f4 100644 --- a/kernel/src/filesystem/vfs/stat.rs +++ b/kernel/src/filesystem/vfs/stat.rs @@ -301,12 +301,16 @@ pub fn vfs_getattr( kstat.btime.tv_sec = metadata.btime.tv_sec; kstat.btime.tv_nsec = metadata.btime.tv_nsec; } + + // 对于字符/块设备,st_rdev 在基础属性中也应被返回。 + // 即便调用者未显式请求 STATX_ALL,也填充 rdev 以符合 Linux 语义。 + kstat.rdev = metadata.raw_dev; + if request_mask.contains(PosixStatxMask::STATX_ALL) { kstat.attributes = StxAttributes::STATX_ATTR_APPEND; kstat.attributes_mask |= StxAttributes::STATX_ATTR_AUTOMOUNT | StxAttributes::STATX_ATTR_DAX; kstat.dev = DeviceNumber::from(metadata.dev_id as u32); - kstat.rdev = metadata.raw_dev; } // 把文件类型加入mode里面 (todo: 在具体的文件系统里面去实现这个操作。这里只是权宜之计) @@ -519,7 +523,8 @@ impl TryFrom for GenericPosixStat { tmp.st_uid = kstat.uid; tmp.st_gid = kstat.gid; - tmp.st_rdev = kstat.rdev.data() as u64; + // 兼容 Linux 的 dev 编码语义,使用 new_encode_dev 返回 gnu_dev_makedev 风格的值。 + tmp.st_rdev = kstat.rdev.new_encode_dev() as u64; tmp.st_size = kstat.size as i64; tmp.st_atime = kstat.atime.tv_sec; diff --git a/kernel/src/filesystem/vfs/syscall/sys_mount.rs b/kernel/src/filesystem/vfs/syscall/sys_mount.rs index 145b84120..b317f3a33 100644 --- a/kernel/src/filesystem/vfs/syscall/sys_mount.rs +++ b/kernel/src/filesystem/vfs/syscall/sys_mount.rs @@ -3,8 +3,11 @@ use crate::{ arch::{interrupt::TrapFrame, syscall::nr::SYS_MOUNT}, filesystem::vfs::{ - fcntl::AtFlags, mount::MountFlags, produce_fs, utils::user_path_at, FileType, IndexNode, - InodeId, MountFS, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, + fcntl::AtFlags, + mount::{is_mountpoint_root, MountFlags}, + produce_fs, + utils::user_path_at, + FileType, IndexNode, InodeId, MountFS, MAX_PATHLEN, VFS_MAX_FOLLOW_SYMLINK_TIMES, }, libs::casting::DowncastArc, process::{ @@ -257,7 +260,7 @@ fn path_mount( fn do_new_mount( source: Option, - target_inode: Arc, + mut target_inode: Arc, filesystemtype: Option, data: Option, mount_flags: MountFlags, @@ -268,14 +271,17 @@ fn do_new_mount( log::error!("Failed to produce filesystem: {:?}", e); })?; - let abs_path = target_inode.absolute_path()?; - - let result = ProcessManager::current_mntns().get_mount_point(&abs_path); - if let Some((_, rest, _fs)) = result { - if rest.is_empty() { - return Err(SystemError::EBUSY); + // 若目标是挂载点根,则尝试在其父目录挂载,避免 EBUSY 并与 Linux 叠加语义接近 + if is_mountpoint_root(&target_inode) { + if let Ok(parent) = target_inode.parent() { + target_inode = parent; } } + + let _abs_path = target_inode.absolute_path()?; + + // 允许在已有挂载点上再次挂载(符合 Linux 允许叠加挂载的语义) + // MountList::insert 会替换同一路径的记录,无需提前返回 EBUSY。 return target_inode.mount(fs, mount_flags); } #[inline(never)] diff --git a/kernel/src/process/namespace/mnt.rs b/kernel/src/process/namespace/mnt.rs index 07c3929c0..bdf0e14b3 100644 --- a/kernel/src/process/namespace/mnt.rs +++ b/kernel/src/process/namespace/mnt.rs @@ -275,7 +275,7 @@ impl MntNamespace { mntfs: Arc, ) -> Result<(), SystemError> { self.inner.lock().mount_list.insert(ino, mount_path, mntfs); - return Ok(()); + Ok(()) } pub fn mount_list(&self) -> Arc { @@ -283,7 +283,7 @@ impl MntNamespace { } pub fn remove_mount(&self, mount_path: &str) -> Option> { - return self.inner.lock().mount_list.remove(mount_path); + self.inner.lock().mount_list.remove(mount_path) } pub fn get_mount_point(