2024-01-03 03:22:36 +00:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
|
|
2025-01-24 10:06:53 +00:00
|
|
|
#![expect(unused_variables)]
|
2024-06-03 18:34:33 +00:00
|
|
|
|
2024-02-25 14:09:24 +00:00
|
|
|
use core::time::Duration;
|
2023-07-18 09:19:32 +00:00
|
|
|
|
2024-05-02 09:44:10 +00:00
|
|
|
use aster_util::slot_vec::SlotVec;
|
|
|
|
|
use id_alloc::IdAlloc;
|
2023-07-18 09:19:32 +00:00
|
|
|
|
2025-11-03 10:47:38 +00:00
|
|
|
pub use self::ptmx::Ptmx;
|
|
|
|
|
use self::slave::PtySlaveInode;
|
2025-11-17 15:38:28 +00:00
|
|
|
use super::utils::{MknodType, StatusFlags};
|
2024-02-25 14:09:24 +00:00
|
|
|
use crate::{
|
|
|
|
|
device::PtyMaster,
|
|
|
|
|
fs::{
|
2025-11-13 02:39:19 +00:00
|
|
|
device::{Device, DeviceType},
|
2025-11-17 15:38:28 +00:00
|
|
|
notify::FsEventPublisher,
|
2025-07-17 03:24:21 +00:00
|
|
|
registry::{FsProperties, FsType},
|
2024-02-25 14:09:24 +00:00
|
|
|
utils::{
|
2025-12-08 12:53:18 +00:00
|
|
|
DirEntryVecExt, DirentVisitor, FileSystem, FsEventSubscriberStats, FsFlags, Inode,
|
|
|
|
|
InodeIo, InodeMode, InodeType, Metadata, NAME_MAX, SuperBlock, mkmod,
|
2024-02-25 14:09:24 +00:00
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
prelude::*,
|
|
|
|
|
process::{Gid, Uid},
|
|
|
|
|
};
|
2023-07-18 09:19:32 +00:00
|
|
|
|
|
|
|
|
mod ptmx;
|
|
|
|
|
mod slave;
|
|
|
|
|
|
|
|
|
|
const DEVPTS_MAGIC: u64 = 0x1cd1;
|
|
|
|
|
const BLOCK_SIZE: usize = 1024;
|
|
|
|
|
|
2024-05-24 02:41:33 +00:00
|
|
|
const ROOT_INO: u64 = 1;
|
|
|
|
|
const PTMX_INO: u64 = 2;
|
|
|
|
|
const FIRST_SLAVE_INO: u64 = 3;
|
2023-07-18 09:19:32 +00:00
|
|
|
|
|
|
|
|
/// The max number of pty pairs.
|
|
|
|
|
const MAX_PTY_NUM: usize = 4096;
|
|
|
|
|
|
|
|
|
|
/// Devpts(device pseudo terminal filesystem) is a virtual filesystem.
|
|
|
|
|
///
|
|
|
|
|
/// It is normally mounted at "/dev/pts" and contains solely devices files which
|
|
|
|
|
/// represent slaves to the multiplexing master located at "/dev/ptmx".
|
|
|
|
|
///
|
|
|
|
|
/// Actually, the "/dev/ptmx" is a symlink to the real device at "/dev/pts/ptmx".
|
|
|
|
|
pub struct DevPts {
|
|
|
|
|
sb: SuperBlock,
|
2024-05-24 02:33:38 +00:00
|
|
|
root: Arc<RootInode>,
|
2023-07-18 09:19:32 +00:00
|
|
|
index_alloc: Mutex<IdAlloc>,
|
2025-11-17 15:38:28 +00:00
|
|
|
fs_event_subscriber_stats: FsEventSubscriberStats,
|
2023-07-18 09:19:32 +00:00
|
|
|
this: Weak<Self>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl DevPts {
|
|
|
|
|
pub fn new() -> Arc<Self> {
|
2023-09-04 03:04:42 +00:00
|
|
|
Arc::new_cyclic(|weak_self| Self {
|
2024-05-24 02:33:38 +00:00
|
|
|
sb: SuperBlock::new(DEVPTS_MAGIC, BLOCK_SIZE, NAME_MAX),
|
|
|
|
|
root: RootInode::new(weak_self.clone()),
|
2023-07-18 09:19:32 +00:00
|
|
|
index_alloc: Mutex::new(IdAlloc::with_capacity(MAX_PTY_NUM)),
|
2025-11-17 15:38:28 +00:00
|
|
|
fs_event_subscriber_stats: FsEventSubscriberStats::new(),
|
2023-07-18 09:19:32 +00:00
|
|
|
this: weak_self.clone(),
|
2023-09-04 03:04:42 +00:00
|
|
|
})
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Create the master and slave pair.
|
2025-11-06 01:32:17 +00:00
|
|
|
fn create_master_slave_pair(&self) -> Result<(Box<PtyMaster>, Arc<PtySlaveInode>)> {
|
2023-07-18 09:19:32 +00:00
|
|
|
let index = self
|
|
|
|
|
.index_alloc
|
|
|
|
|
.lock()
|
|
|
|
|
.alloc()
|
|
|
|
|
.ok_or_else(|| Error::with_message(Errno::EIO, "cannot alloc index"))?;
|
|
|
|
|
|
2023-08-28 06:28:23 +00:00
|
|
|
let (master, slave) = crate::device::new_pty_pair(index as u32, self.root.ptmx.clone())?;
|
2023-07-18 09:19:32 +00:00
|
|
|
|
|
|
|
|
let slave_inode = PtySlaveInode::new(slave, self.this.clone());
|
2025-11-04 03:13:45 +00:00
|
|
|
self.root
|
|
|
|
|
.slaves
|
|
|
|
|
.write()
|
|
|
|
|
.put_entry_if_not_found(&index.to_string(), || slave_inode.clone());
|
2023-07-18 09:19:32 +00:00
|
|
|
|
2023-11-16 09:26:42 +00:00
|
|
|
Ok((master, slave_inode))
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Remove the slave from fs.
|
|
|
|
|
///
|
|
|
|
|
/// This is called when the master is being dropped.
|
2025-11-04 03:13:45 +00:00
|
|
|
pub fn remove_slave(&self, index: u32) -> Option<Arc<dyn Inode>> {
|
|
|
|
|
let (_, removed_slave) = self
|
|
|
|
|
.root
|
|
|
|
|
.slaves
|
|
|
|
|
.write()
|
|
|
|
|
.remove_entry_by_name(&index.to_string())?;
|
|
|
|
|
self.index_alloc.lock().free(index as usize);
|
|
|
|
|
Some(removed_slave)
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FileSystem for DevPts {
|
2025-10-24 02:36:32 +00:00
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
"devpts"
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-18 09:19:32 +00:00
|
|
|
fn sync(&self) -> Result<()> {
|
|
|
|
|
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
|
|
|
|
|
}
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
2025-07-17 03:24:21 +00:00
|
|
|
struct DevPtsType;
|
|
|
|
|
|
|
|
|
|
impl FsType for DevPtsType {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
"devpts"
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 02:33:22 +00:00
|
|
|
fn properties(&self) -> FsProperties {
|
|
|
|
|
FsProperties::empty()
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 03:24:21 +00:00
|
|
|
fn create(
|
|
|
|
|
&self,
|
2025-10-31 08:42:39 +00:00
|
|
|
_flags: FsFlags,
|
2025-07-17 03:24:21 +00:00
|
|
|
_args: Option<CString>,
|
|
|
|
|
_disk: Option<Arc<dyn aster_block::BlockDevice>>,
|
|
|
|
|
) -> Result<Arc<dyn FileSystem>> {
|
|
|
|
|
Ok(DevPts::new())
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-05 02:33:22 +00:00
|
|
|
fn sysnode(&self) -> Option<Arc<dyn aster_systree::SysNode>> {
|
2025-07-17 03:24:21 +00:00
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub(super) fn init() {
|
2025-09-05 02:49:47 +00:00
|
|
|
super::registry::register(&DevPtsType).unwrap();
|
2025-07-17 03:24:21 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-18 09:19:32 +00:00
|
|
|
struct RootInode {
|
|
|
|
|
ptmx: Arc<Ptmx>,
|
2025-11-04 03:13:45 +00:00
|
|
|
slaves: RwLock<SlotVec<(String, Arc<dyn Inode>)>>,
|
2024-01-04 09:52:27 +00:00
|
|
|
metadata: RwLock<Metadata>,
|
2025-11-17 15:38:28 +00:00
|
|
|
fs_event_publisher: FsEventPublisher,
|
2023-07-18 09:19:32 +00:00
|
|
|
fs: Weak<DevPts>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RootInode {
|
2024-05-24 02:33:38 +00:00
|
|
|
pub fn new(fs: Weak<DevPts>) -> Arc<Self> {
|
2023-07-18 09:19:32 +00:00
|
|
|
Arc::new(Self {
|
2024-05-24 02:33:38 +00:00
|
|
|
ptmx: Ptmx::new(fs.clone()),
|
2023-07-18 09:19:32 +00:00
|
|
|
slaves: RwLock::new(SlotVec::new()),
|
2025-09-21 13:26:39 +00:00
|
|
|
metadata: RwLock::new(Metadata::new_dir(ROOT_INO, mkmod!(a+rx, u+w), BLOCK_SIZE)),
|
2025-11-17 15:38:28 +00:00
|
|
|
fs_event_publisher: FsEventPublisher::new(),
|
2023-07-18 09:19:32 +00:00
|
|
|
fs,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-05 15:13:34 +00:00
|
|
|
impl InodeIo for RootInode {
|
|
|
|
|
fn read_at(
|
|
|
|
|
&self,
|
|
|
|
|
_offset: usize,
|
|
|
|
|
_writer: &mut VmWriter,
|
|
|
|
|
_status_flags: StatusFlags,
|
|
|
|
|
) -> Result<usize> {
|
|
|
|
|
Err(Error::new(Errno::EISDIR))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn write_at(
|
|
|
|
|
&self,
|
|
|
|
|
_offset: usize,
|
|
|
|
|
_reader: &mut VmReader,
|
|
|
|
|
_status_flags: StatusFlags,
|
|
|
|
|
) -> Result<usize> {
|
|
|
|
|
Err(Error::new(Errno::EISDIR))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-18 09:19:32 +00:00
|
|
|
impl Inode for RootInode {
|
2024-01-05 06:44:19 +00:00
|
|
|
fn size(&self) -> usize {
|
2024-01-04 09:52:27 +00:00
|
|
|
self.metadata.read().size
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-18 03:47:17 +00:00
|
|
|
fn resize(&self, new_size: usize) -> Result<()> {
|
|
|
|
|
Err(Error::new(Errno::EISDIR))
|
|
|
|
|
}
|
2023-07-18 09:19:32 +00:00
|
|
|
|
|
|
|
|
fn metadata(&self) -> Metadata {
|
2024-01-04 09:52:27 +00:00
|
|
|
*self.metadata.read()
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-17 15:38:28 +00:00
|
|
|
fn fs_event_publisher(&self) -> &FsEventPublisher {
|
|
|
|
|
&self.fs_event_publisher
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-18 03:47:17 +00:00
|
|
|
fn ino(&self) -> u64 {
|
2024-01-04 09:52:27 +00:00
|
|
|
self.metadata.read().ino as _
|
2023-09-18 03:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
2023-09-13 04:07:58 +00:00
|
|
|
fn type_(&self) -> InodeType {
|
2024-01-04 09:52:27 +00:00
|
|
|
self.metadata.read().type_
|
2023-09-13 04:07:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-04 09:52:27 +00:00
|
|
|
fn mode(&self) -> Result<InodeMode> {
|
|
|
|
|
Ok(self.metadata.read().mode)
|
2023-09-13 04:07:58 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-04 09:52:27 +00:00
|
|
|
fn set_mode(&self, mode: InodeMode) -> Result<()> {
|
|
|
|
|
self.metadata.write().mode = mode;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn owner(&self) -> Result<Uid> {
|
|
|
|
|
Ok(self.metadata.read().uid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_owner(&self, uid: Uid) -> Result<()> {
|
|
|
|
|
self.metadata.write().uid = uid;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn group(&self) -> Result<Gid> {
|
|
|
|
|
Ok(self.metadata.read().gid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_group(&self, gid: Gid) -> Result<()> {
|
|
|
|
|
self.metadata.write().gid = gid;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2023-09-13 04:07:58 +00:00
|
|
|
|
2023-07-18 09:19:32 +00:00
|
|
|
fn atime(&self) -> Duration {
|
2024-01-04 09:52:27 +00:00
|
|
|
self.metadata.read().atime
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-04 09:52:27 +00:00
|
|
|
fn set_atime(&self, time: Duration) {
|
|
|
|
|
self.metadata.write().atime = time;
|
|
|
|
|
}
|
2023-07-18 09:19:32 +00:00
|
|
|
|
|
|
|
|
fn mtime(&self) -> Duration {
|
2024-01-04 09:52:27 +00:00
|
|
|
self.metadata.read().mtime
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-04 09:52:27 +00:00
|
|
|
fn set_mtime(&self, time: Duration) {
|
|
|
|
|
self.metadata.write().mtime = time;
|
|
|
|
|
}
|
2023-07-18 09:19:32 +00:00
|
|
|
|
2024-06-13 06:53:09 +00:00
|
|
|
fn ctime(&self) -> Duration {
|
|
|
|
|
self.metadata.read().ctime
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn set_ctime(&self, time: Duration) {
|
|
|
|
|
self.metadata.write().ctime = time;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-18 09:19:32 +00:00
|
|
|
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
|
|
|
|
|
Err(Error::new(Errno::EPERM))
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-16 02:47:48 +00:00
|
|
|
fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result<Arc<dyn Inode>> {
|
2023-07-18 09:19:32 +00:00
|
|
|
Err(Error::new(Errno::EPERM))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
|
|
|
|
let try_readdir = |offset: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
|
|
|
|
|
// Read the 3 special entries.
|
|
|
|
|
if *offset == 0 {
|
2024-01-04 09:52:27 +00:00
|
|
|
visitor.visit(".", self.ino(), self.type_(), *offset)?;
|
2023-07-18 09:19:32 +00:00
|
|
|
*offset += 1;
|
|
|
|
|
}
|
|
|
|
|
if *offset == 1 {
|
2024-01-04 09:52:27 +00:00
|
|
|
visitor.visit("..", self.ino(), self.type_(), *offset)?;
|
2023-07-18 09:19:32 +00:00
|
|
|
*offset += 1;
|
|
|
|
|
}
|
|
|
|
|
if *offset == 2 {
|
2024-01-04 09:52:27 +00:00
|
|
|
visitor.visit("ptmx", self.ptmx.ino(), self.ptmx.type_(), *offset)?;
|
2023-07-18 09:19:32 +00:00
|
|
|
*offset += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read the slaves.
|
|
|
|
|
let slaves = self.slaves.read();
|
2023-09-04 03:04:42 +00:00
|
|
|
let start_offset = *offset;
|
2023-07-18 09:19:32 +00:00
|
|
|
for (idx, (name, node)) in slaves
|
|
|
|
|
.idxes_and_items()
|
|
|
|
|
.map(|(idx, (name, node))| (idx + 3, (name, node)))
|
|
|
|
|
.skip_while(|(idx, _)| idx < &start_offset)
|
|
|
|
|
{
|
2024-01-04 09:52:27 +00:00
|
|
|
visitor.visit(name.as_ref(), node.ino(), node.type_(), idx)?;
|
2023-07-18 09:19:32 +00:00
|
|
|
*offset = idx + 1;
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut iterate_offset = offset;
|
|
|
|
|
match try_readdir(&mut iterate_offset, visitor) {
|
|
|
|
|
Err(e) if offset == iterate_offset => Err(e),
|
|
|
|
|
_ => Ok(iterate_offset - offset),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn link(&self, old: &Arc<dyn Inode>, name: &str) -> Result<()> {
|
|
|
|
|
Err(Error::new(Errno::EPERM))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn unlink(&self, name: &str) -> Result<()> {
|
2025-11-04 03:13:45 +00:00
|
|
|
match name {
|
|
|
|
|
"." | ".." => {
|
|
|
|
|
return_errno_with_message!(Errno::EISDIR, "the devpts directory cannot be unlinked")
|
|
|
|
|
}
|
|
|
|
|
"ptmx" => return_errno_with_message!(Errno::EPERM, "the ptmx inode cannot be unlinked"),
|
|
|
|
|
slave => {
|
|
|
|
|
if self.slaves.read().find_entry_by_name(slave).is_none() {
|
|
|
|
|
return_errno_with_message!(Errno::ENOENT, "the slave inode does not exist");
|
|
|
|
|
}
|
|
|
|
|
return_errno_with_message!(Errno::EPERM, "the slave inode cannot be unlinked");
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn rmdir(&self, name: &str) -> Result<()> {
|
|
|
|
|
Err(Error::new(Errno::EPERM))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
|
|
|
|
|
let inode = match name {
|
|
|
|
|
"." | ".." => self.fs().root_inode(),
|
|
|
|
|
// Call the "open" method of ptmx to create a master and slave pair.
|
2023-11-16 09:26:42 +00:00
|
|
|
"ptmx" => self.ptmx.clone(),
|
2023-07-18 09:19:32 +00:00
|
|
|
slave => self
|
|
|
|
|
.slaves
|
|
|
|
|
.read()
|
2025-11-04 03:13:45 +00:00
|
|
|
.find_entry_by_name(slave)
|
|
|
|
|
.cloned()
|
2023-07-18 09:19:32 +00:00
|
|
|
.ok_or(Error::new(Errno::ENOENT))?,
|
|
|
|
|
};
|
|
|
|
|
Ok(inode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()> {
|
|
|
|
|
Err(Error::new(Errno::EPERM))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn fs(&self) -> Arc<dyn FileSystem> {
|
|
|
|
|
self.fs.upgrade().unwrap()
|
|
|
|
|
}
|
2025-01-14 09:32:02 +00:00
|
|
|
|
|
|
|
|
fn is_dentry_cacheable(&self) -> bool {
|
|
|
|
|
false
|
|
|
|
|
}
|
2023-07-18 09:19:32 +00:00
|
|
|
}
|