Add devpts to support the ptmx
This commit is contained in:
parent
04db7c8c3d
commit
4f00e5a167
|
@ -811,6 +811,7 @@ dependencies = [
|
|||
name = "jinux-util"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"jinux-frame",
|
||||
"jinux-rights",
|
||||
"jinux-rights-proc",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod null;
|
||||
mod pty;
|
||||
mod random;
|
||||
pub mod tty;
|
||||
mod urandom;
|
||||
|
@ -22,5 +23,6 @@ pub fn init() -> Result<()> {
|
|||
add_node(random, "random")?;
|
||||
let urandom = Arc::new(urandom::Urandom);
|
||||
add_node(urandom, "urandom")?;
|
||||
pty::init()?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
use crate::fs::{
|
||||
devpts::DevPts,
|
||||
fs_resolver::{FsPath, FsResolver},
|
||||
utils::{InodeMode, InodeType},
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
let fs = FsResolver::new();
|
||||
|
||||
let dev = fs.lookup(&FsPath::try_from("/dev")?)?;
|
||||
// Create the "pts" directory and mount devpts on it.
|
||||
let devpts = dev.create("pts", InodeType::Dir, InodeMode::from_bits_truncate(0o755))?;
|
||||
devpts.mount(DevPts::new())?;
|
||||
|
||||
// Create the "ptmx" symlink.
|
||||
let ptmx = dev.create(
|
||||
"ptmx",
|
||||
InodeType::SymLink,
|
||||
InodeMode::from_bits_truncate(0o777),
|
||||
)?;
|
||||
ptmx.write_link("pts/ptmx")?;
|
||||
Ok(())
|
||||
}
|
|
@ -46,6 +46,7 @@ pub enum DeviceType {
|
|||
}
|
||||
|
||||
/// Device Id
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DeviceId(u64);
|
||||
|
||||
impl DeviceId {
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Pty master inode for the master device.
|
||||
pub struct PtyMasterInode(Arc<PtyMaster>);
|
||||
|
||||
impl PtyMasterInode {
|
||||
pub fn new(device: Arc<PtyMaster>) -> Arc<Self> {
|
||||
Arc::new(Self(device))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PtyMasterInode {
|
||||
fn drop(&mut self) {
|
||||
// Remove the slave from fs.
|
||||
let index = self.0.slave_index();
|
||||
let _ = self.0.ptmx().devpts().remove_slave(index);
|
||||
}
|
||||
}
|
||||
|
||||
impl Inode for PtyMasterInode {
|
||||
/// Do not cache dentry in DCACHE.
|
||||
///
|
||||
/// Each file descriptor obtained by opening "/dev/ptmx" is an independent pty master
|
||||
/// with its own associated pty slave.
|
||||
fn is_dentry_cacheable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.0.ptmx().metadata().size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) {}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.0.ptmx().metadata()
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.0.ptmx().metadata().atime
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.0.ptmx().metadata().mtime
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {}
|
||||
|
||||
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
self.0.ioctl(cmd, arg)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.0.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.0.ptmx().devpts()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement real pty master.
|
||||
pub struct PtyMaster {
|
||||
slave_index: u32,
|
||||
ptmx: Arc<Ptmx>,
|
||||
}
|
||||
|
||||
impl PtyMaster {
|
||||
pub fn new(slave_index: u32, ptmx: Arc<Ptmx>) -> Arc<Self> {
|
||||
Arc::new(Self { slave_index, ptmx })
|
||||
}
|
||||
|
||||
pub fn slave_index(&self) -> u32 {
|
||||
self.slave_index
|
||||
}
|
||||
|
||||
fn ptmx(&self) -> &Ptmx {
|
||||
&self.ptmx
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PtyMaster {
|
||||
fn type_(&self) -> DeviceType {
|
||||
self.ptmx.device_type()
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
self.ptmx.device_id()
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
todo!();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::utils::{
|
||||
DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoEvents, IoctlCmd, Metadata,
|
||||
Poller, SuperBlock, NAME_MAX,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
use core::time::Duration;
|
||||
use jinux_frame::vm::VmFrame;
|
||||
use jinux_util::{id_allocator::IdAlloc, slot_vec::SlotVec};
|
||||
|
||||
use self::master::{PtyMaster, PtyMasterInode};
|
||||
use self::ptmx::Ptmx;
|
||||
use self::slave::{PtySlave, PtySlaveInode};
|
||||
|
||||
mod master;
|
||||
mod ptmx;
|
||||
mod slave;
|
||||
|
||||
const DEVPTS_MAGIC: u64 = 0x1cd1;
|
||||
const BLOCK_SIZE: usize = 1024;
|
||||
|
||||
const ROOT_INO: usize = 1;
|
||||
const PTMX_INO: usize = 2;
|
||||
const FIRST_SLAVE_INO: usize = 3;
|
||||
|
||||
/// 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 {
|
||||
root: Arc<RootInode>,
|
||||
sb: SuperBlock,
|
||||
index_alloc: Mutex<IdAlloc>,
|
||||
this: Weak<Self>,
|
||||
}
|
||||
|
||||
impl DevPts {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let sb = SuperBlock::new(DEVPTS_MAGIC, BLOCK_SIZE, NAME_MAX);
|
||||
let devpts = Arc::new_cyclic(|weak_self| Self {
|
||||
root: RootInode::new(weak_self.clone(), &sb),
|
||||
sb,
|
||||
index_alloc: Mutex::new(IdAlloc::with_capacity(MAX_PTY_NUM)),
|
||||
this: weak_self.clone(),
|
||||
});
|
||||
devpts
|
||||
}
|
||||
|
||||
/// Create the master and slave pair.
|
||||
fn create_master_slave_pair(&self) -> Result<(Arc<PtyMasterInode>, Arc<PtySlaveInode>)> {
|
||||
let index = self
|
||||
.index_alloc
|
||||
.lock()
|
||||
.alloc()
|
||||
.ok_or_else(|| Error::with_message(Errno::EIO, "cannot alloc index"))?;
|
||||
|
||||
let master = PtyMaster::new(index as u32, self.root.ptmx.clone());
|
||||
let slave = PtySlave::new(master.clone());
|
||||
|
||||
let master_inode = PtyMasterInode::new(master);
|
||||
let slave_inode = PtySlaveInode::new(slave, self.this.clone());
|
||||
self.root.add_slave(index.to_string(), slave_inode.clone());
|
||||
|
||||
Ok((master_inode, slave_inode))
|
||||
}
|
||||
|
||||
/// Remove the slave from fs.
|
||||
///
|
||||
/// This is called when the master is being dropped.
|
||||
fn remove_slave(&self, index: u32) -> Option<Arc<PtySlaveInode>> {
|
||||
let removed_slave = self.root.remove_slave(&index.to_string());
|
||||
if removed_slave.is_some() {
|
||||
self.index_alloc.lock().free(index as usize);
|
||||
}
|
||||
removed_slave
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystem for DevPts {
|
||||
fn sync(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn root_inode(&self) -> Arc<dyn Inode> {
|
||||
self.root.clone()
|
||||
}
|
||||
|
||||
fn sb(&self) -> SuperBlock {
|
||||
self.sb.clone()
|
||||
}
|
||||
|
||||
fn flags(&self) -> FsFlags {
|
||||
FsFlags::NO_PAGECACHE
|
||||
}
|
||||
}
|
||||
|
||||
struct RootInode {
|
||||
ptmx: Arc<Ptmx>,
|
||||
slaves: RwLock<SlotVec<(String, Arc<PtySlaveInode>)>>,
|
||||
metadata: Metadata,
|
||||
fs: Weak<DevPts>,
|
||||
}
|
||||
|
||||
impl RootInode {
|
||||
pub fn new(fs: Weak<DevPts>, sb: &SuperBlock) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
ptmx: Ptmx::new(sb, fs.clone()),
|
||||
slaves: RwLock::new(SlotVec::new()),
|
||||
metadata: Metadata::new_dir(ROOT_INO, InodeMode::from_bits_truncate(0o755), sb),
|
||||
fs,
|
||||
})
|
||||
}
|
||||
|
||||
fn add_slave(&self, name: String, slave: Arc<PtySlaveInode>) {
|
||||
self.slaves.write().put((name, slave));
|
||||
}
|
||||
|
||||
fn remove_slave(&self, name: &str) -> Option<Arc<PtySlaveInode>> {
|
||||
let removed_slave = {
|
||||
let mut slaves = self.slaves.write();
|
||||
let pos = slaves
|
||||
.idxes_and_items()
|
||||
.find(|(_, (child, _))| child == name)
|
||||
.map(|(pos, _)| pos);
|
||||
match pos {
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
Some(pos) => slaves.remove(pos).map(|(_, node)| node).unwrap(),
|
||||
}
|
||||
};
|
||||
Some(removed_slave)
|
||||
}
|
||||
}
|
||||
|
||||
impl Inode for RootInode {
|
||||
fn len(&self) -> usize {
|
||||
self.metadata.size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) {}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.metadata.atime
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.metadata.mtime
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {}
|
||||
|
||||
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn mknod(&self, name: &str, mode: InodeMode, dev: Arc<dyn Device>) -> Result<Arc<dyn Inode>> {
|
||||
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 {
|
||||
visitor.visit(".", self.metadata.ino as u64, self.metadata.type_, *offset)?;
|
||||
*offset += 1;
|
||||
}
|
||||
if *offset == 1 {
|
||||
visitor.visit("..", self.metadata.ino as u64, self.metadata.type_, *offset)?;
|
||||
*offset += 1;
|
||||
}
|
||||
if *offset == 2 {
|
||||
visitor.visit(
|
||||
"ptmx",
|
||||
self.ptmx.metadata().ino as u64,
|
||||
self.ptmx.metadata().type_,
|
||||
*offset,
|
||||
)?;
|
||||
*offset += 1;
|
||||
}
|
||||
|
||||
// Read the slaves.
|
||||
let slaves = self.slaves.read();
|
||||
let start_offset = offset.clone();
|
||||
for (idx, (name, node)) in slaves
|
||||
.idxes_and_items()
|
||||
.map(|(idx, (name, node))| (idx + 3, (name, node)))
|
||||
.skip_while(|(idx, _)| idx < &start_offset)
|
||||
{
|
||||
visitor.visit(
|
||||
name.as_ref(),
|
||||
node.metadata().ino as u64,
|
||||
node.metadata().type_,
|
||||
idx,
|
||||
)?;
|
||||
*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<()> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
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.
|
||||
"ptmx" => self.ptmx.open()?,
|
||||
slave => self
|
||||
.slaves
|
||||
.read()
|
||||
.idxes_and_items()
|
||||
.find(|(_, (child_name, _))| child_name == &slave)
|
||||
.map(|(_, (_, node))| node.clone())
|
||||
.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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Same major number with Linux.
|
||||
const PTMX_MAJOR_NUM: u32 = 5;
|
||||
/// Same minor number with Linux.
|
||||
const PTMX_MINOR_NUM: u32 = 2;
|
||||
|
||||
/// Ptmx is the multiplexing master of devpts.
|
||||
///
|
||||
/// Every time the multiplexing master is opened, a new instance of pty master inode is returned
|
||||
/// and an corresponding pty slave inode is also created.
|
||||
pub struct Ptmx {
|
||||
inner: Inner,
|
||||
metadata: Metadata,
|
||||
fs: Weak<DevPts>,
|
||||
}
|
||||
|
||||
impl Ptmx {
|
||||
pub fn new(sb: &SuperBlock, fs: Weak<DevPts>) -> Arc<Self> {
|
||||
let inner = Inner;
|
||||
Arc::new(Self {
|
||||
metadata: Metadata::new_device(
|
||||
PTMX_INO,
|
||||
InodeMode::from_bits_truncate(0o666),
|
||||
sb,
|
||||
&inner,
|
||||
),
|
||||
inner,
|
||||
fs,
|
||||
})
|
||||
}
|
||||
|
||||
/// The open method for ptmx.
|
||||
///
|
||||
/// Creates a master and slave pair and returns the master inode.
|
||||
pub fn open(&self) -> Result<Arc<PtyMasterInode>> {
|
||||
let (master, _) = self.devpts().create_master_slave_pair()?;
|
||||
Ok(master)
|
||||
}
|
||||
|
||||
pub fn devpts(&self) -> Arc<DevPts> {
|
||||
self.fs.upgrade().unwrap()
|
||||
}
|
||||
|
||||
pub fn device_type(&self) -> DeviceType {
|
||||
self.inner.type_()
|
||||
}
|
||||
|
||||
pub fn device_id(&self) -> DeviceId {
|
||||
self.inner.id()
|
||||
}
|
||||
}
|
||||
|
||||
// Many methods are left to do nothing because every time the ptmx is being opened,
|
||||
// it returns the pty master. So the ptmx can not be used at upper layer.
|
||||
impl Inode for Ptmx {
|
||||
fn len(&self) -> usize {
|
||||
self.metadata.size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) {}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.metadata.atime
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.metadata.mtime
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {}
|
||||
|
||||
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.devpts()
|
||||
}
|
||||
}
|
||||
|
||||
struct Inner;
|
||||
|
||||
impl Device for Inner {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(PTMX_MAJOR_NUM, PTMX_MINOR_NUM)
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
// do nothing because it should not be used to read.
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
// do nothing because it should not be used to write.
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Same major number with Linux, the minor number is the index of slave.
|
||||
const SLAVE_MAJOR_NUM: u32 = 3;
|
||||
|
||||
/// Pty slave inode for the slave device.
|
||||
pub struct PtySlaveInode {
|
||||
device: Arc<PtySlave>,
|
||||
metadata: Metadata,
|
||||
fs: Weak<DevPts>,
|
||||
}
|
||||
|
||||
impl PtySlaveInode {
|
||||
pub fn new(device: Arc<PtySlave>, fs: Weak<DevPts>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
metadata: Metadata::new_device(
|
||||
device.index() as usize + FIRST_SLAVE_INO,
|
||||
InodeMode::from_bits_truncate(0o620),
|
||||
&fs.upgrade().unwrap().sb(),
|
||||
device.as_ref(),
|
||||
),
|
||||
device,
|
||||
fs,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Inode for PtySlaveInode {
|
||||
/// Do not cache dentry in DCACHE.
|
||||
///
|
||||
/// The slave will be deleted by the master when the master is released.
|
||||
/// So we should not cache the dentry.
|
||||
fn is_dentry_cacheable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.metadata.size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) {}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.metadata.atime
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.metadata.mtime
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {}
|
||||
|
||||
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.device.read(buf)
|
||||
}
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
self.device.write(buf)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
self.device.ioctl(cmd, arg)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.device.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.fs.upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: implement real pty slave.
|
||||
pub struct PtySlave {
|
||||
master: Arc<PtyMaster>,
|
||||
}
|
||||
|
||||
impl PtySlave {
|
||||
pub fn new(master: Arc<PtyMaster>) -> Arc<Self> {
|
||||
Arc::new(Self { master })
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
self.master.slave_index()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PtySlave {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(SLAVE_MAJOR_NUM, self.index())
|
||||
}
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
todo!();
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
pub mod device;
|
||||
pub mod devpts;
|
||||
pub mod epoll;
|
||||
pub mod file_handle;
|
||||
pub mod file_table;
|
||||
|
|
|
@ -97,7 +97,7 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
|
|||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn readdir_at(&self, mut offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
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 two special entries.
|
||||
if *offset == 0 {
|
||||
|
@ -124,13 +124,12 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
|
|||
// Read the normal child entries.
|
||||
self.inner.populate_children(self.this.clone());
|
||||
let cached_children = self.cached_children.read();
|
||||
let start_offset = offset.clone();
|
||||
for (idx, (name, child)) in cached_children
|
||||
.idxes_and_items()
|
||||
.map(|(idx, (name, child))| (idx + 2, (name, child)))
|
||||
.skip_while(|(idx, _)| idx < &start_offset)
|
||||
{
|
||||
if idx < *offset {
|
||||
continue;
|
||||
}
|
||||
visitor.visit(
|
||||
name.as_ref(),
|
||||
child.metadata().ino as u64,
|
||||
|
@ -142,10 +141,10 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
|
|||
Ok(())
|
||||
};
|
||||
|
||||
let initial_offset = offset;
|
||||
match try_readdir(&mut offset, visitor) {
|
||||
Err(e) if initial_offset == offset => Err(e),
|
||||
_ => Ok(offset - initial_offset),
|
||||
let mut iterate_offset = offset;
|
||||
match try_readdir(&mut iterate_offset, visitor) {
|
||||
Err(e) if iterate_offset == offset => Err(e),
|
||||
_ => Ok(iterate_offset - offset),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,11 +123,9 @@ impl Inode_ {
|
|||
sb: &SuperBlock,
|
||||
device: Arc<dyn Device>,
|
||||
) -> Self {
|
||||
let type_ = device.type_();
|
||||
let rdev = device.id().into();
|
||||
Self {
|
||||
metadata: Metadata::new_device(ino, mode, sb, device.as_ref()),
|
||||
inner: Inner::Device(device),
|
||||
metadata: Metadata::new_device(ino, mode, sb, type_, rdev),
|
||||
this: Weak::default(),
|
||||
fs: Weak::default(),
|
||||
}
|
||||
|
@ -260,7 +258,7 @@ impl DirEntry {
|
|||
self.children.put_at(idx - 2, new_entry)
|
||||
}
|
||||
|
||||
fn visit_entry(&self, mut idx: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
fn visit_entry(&self, idx: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
let try_visit = |idx: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
|
||||
// Read the two special entries("." and "..").
|
||||
if *idx == 0 {
|
||||
|
@ -284,14 +282,13 @@ impl DirEntry {
|
|||
*idx += 1;
|
||||
}
|
||||
// Read the normal child entries.
|
||||
let start_idx = idx.clone();
|
||||
for (offset, (name, child)) in self
|
||||
.children
|
||||
.idxes_and_items()
|
||||
.map(|(offset, (name, child))| (offset + 2, (name, child)))
|
||||
.skip_while(|(offset, _)| offset < &start_idx)
|
||||
{
|
||||
if offset < *idx {
|
||||
continue;
|
||||
}
|
||||
visitor.visit(
|
||||
name.as_ref(),
|
||||
child.metadata().ino as u64,
|
||||
|
@ -303,10 +300,10 @@ impl DirEntry {
|
|||
Ok(())
|
||||
};
|
||||
|
||||
let initial_idx = idx;
|
||||
match try_visit(&mut idx, visitor) {
|
||||
Err(e) if idx == initial_idx => Err(e),
|
||||
_ => Ok(idx - initial_idx),
|
||||
let mut iterate_idx = idx;
|
||||
match try_visit(&mut iterate_idx, visitor) {
|
||||
Err(e) if idx == iterate_idx => Err(e),
|
||||
_ => Ok(iterate_idx - idx),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,6 +342,60 @@ impl core::fmt::Debug for Str256 {
|
|||
}
|
||||
}
|
||||
|
||||
impl RamInode {
|
||||
fn new_dir(fs: &Arc<RamFS>, mode: InodeMode, parent: &Weak<Self>) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| {
|
||||
let inode = RamInode(RwLock::new(Inode_::new_dir(fs.alloc_id(), mode, &fs.sb())));
|
||||
inode.0.write().fs = Arc::downgrade(fs);
|
||||
inode.0.write().this = weak_self.clone();
|
||||
inode
|
||||
.0
|
||||
.write()
|
||||
.inner
|
||||
.as_direntry_mut()
|
||||
.unwrap()
|
||||
.init(weak_self.clone(), parent.clone());
|
||||
inode
|
||||
})
|
||||
}
|
||||
|
||||
fn new_file(fs: &Arc<RamFS>, mode: InodeMode) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| {
|
||||
let inode = RamInode(RwLock::new(Inode_::new_file(fs.alloc_id(), mode, &fs.sb())));
|
||||
inode.0.write().fs = Arc::downgrade(fs);
|
||||
inode.0.write().this = weak_self.clone();
|
||||
inode
|
||||
})
|
||||
}
|
||||
|
||||
fn new_symlink(fs: &Arc<RamFS>, mode: InodeMode) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| {
|
||||
let inode = RamInode(RwLock::new(Inode_::new_symlink(
|
||||
fs.alloc_id(),
|
||||
mode,
|
||||
&fs.sb(),
|
||||
)));
|
||||
inode.0.write().fs = Arc::downgrade(fs);
|
||||
inode.0.write().this = weak_self.clone();
|
||||
inode
|
||||
})
|
||||
}
|
||||
|
||||
fn new_device(fs: &Arc<RamFS>, mode: InodeMode, device: Arc<dyn Device>) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| {
|
||||
let inode = RamInode(RwLock::new(Inode_::new_device(
|
||||
fs.alloc_id(),
|
||||
mode,
|
||||
&fs.sb(),
|
||||
device,
|
||||
)));
|
||||
inode.0.write().fs = Arc::downgrade(fs);
|
||||
inode.0.write().this = weak_self.clone();
|
||||
inode
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Inode for RamInode {
|
||||
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
|
||||
// do nothing
|
||||
|
@ -411,18 +462,7 @@ impl Inode for RamInode {
|
|||
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
|
||||
return_errno_with_message!(Errno::EEXIST, "entry exists");
|
||||
}
|
||||
let device_inode = {
|
||||
let fs = self_inode.fs.upgrade().unwrap();
|
||||
let device_inode = Arc::new(RamInode(RwLock::new(Inode_::new_device(
|
||||
fs.alloc_id(),
|
||||
mode,
|
||||
&fs.sb(),
|
||||
device,
|
||||
))));
|
||||
device_inode.0.write().fs = self_inode.fs.clone();
|
||||
device_inode.0.write().this = Arc::downgrade(&device_inode);
|
||||
device_inode
|
||||
};
|
||||
let device_inode = RamInode::new_device(&self_inode.fs.upgrade().unwrap(), mode, device);
|
||||
self_inode
|
||||
.inner
|
||||
.as_direntry_mut()
|
||||
|
@ -442,43 +482,17 @@ impl Inode for RamInode {
|
|||
}
|
||||
let fs = self_inode.fs.upgrade().unwrap();
|
||||
let new_inode = match type_ {
|
||||
InodeType::File => {
|
||||
let file_inode = Arc::new(RamInode(RwLock::new(Inode_::new_file(
|
||||
fs.alloc_id(),
|
||||
mode,
|
||||
&fs.sb(),
|
||||
))));
|
||||
file_inode.0.write().fs = self_inode.fs.clone();
|
||||
file_inode
|
||||
}
|
||||
InodeType::File => RamInode::new_file(&fs, mode),
|
||||
InodeType::SymLink => RamInode::new_symlink(&fs, mode),
|
||||
InodeType::Dir => {
|
||||
let dir_inode = Arc::new(RamInode(RwLock::new(Inode_::new_dir(
|
||||
fs.alloc_id(),
|
||||
mode,
|
||||
&fs.sb(),
|
||||
))));
|
||||
dir_inode.0.write().fs = self_inode.fs.clone();
|
||||
dir_inode.0.write().inner.as_direntry_mut().unwrap().init(
|
||||
Arc::downgrade(&dir_inode),
|
||||
self_inode.inner.as_direntry().unwrap().this.clone(),
|
||||
);
|
||||
let dir_inode = RamInode::new_dir(&fs, mode, &self_inode.this);
|
||||
self_inode.metadata.nlinks += 1;
|
||||
dir_inode
|
||||
}
|
||||
InodeType::SymLink => {
|
||||
let sym_inode = Arc::new(RamInode(RwLock::new(Inode_::new_symlink(
|
||||
fs.alloc_id(),
|
||||
mode,
|
||||
&fs.sb(),
|
||||
))));
|
||||
sym_inode.0.write().fs = self_inode.fs.clone();
|
||||
sym_inode
|
||||
}
|
||||
_ => {
|
||||
panic!("unsupported inode type");
|
||||
}
|
||||
};
|
||||
new_inode.0.write().this = Arc::downgrade(&new_inode);
|
||||
self_inode
|
||||
.inner
|
||||
.as_direntry_mut()
|
||||
|
|
|
@ -186,13 +186,7 @@ impl Metadata {
|
|||
rdev: 0,
|
||||
}
|
||||
}
|
||||
pub fn new_device(
|
||||
ino: usize,
|
||||
mode: InodeMode,
|
||||
sb: &SuperBlock,
|
||||
type_: DeviceType,
|
||||
rdev: u64,
|
||||
) -> Self {
|
||||
pub fn new_device(ino: usize, mode: InodeMode, sb: &SuperBlock, device: &dyn Device) -> Self {
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
|
@ -202,12 +196,12 @@ impl Metadata {
|
|||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::from(type_),
|
||||
type_: InodeType::from(device.type_()),
|
||||
mode,
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev,
|
||||
rdev: device.id().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -289,7 +283,9 @@ pub trait Inode: Any + Sync + Send {
|
|||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()>;
|
||||
fn sync(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&Poller>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
|
@ -307,9 +303,10 @@ pub trait Inode: Any + Sync + Send {
|
|||
/// But this caching can raise consistency issues in certain use cases. Specifically, the dentry
|
||||
/// cache works on the assumption that all FS operations go through the dentry layer first.
|
||||
/// This is why the dentry cache can reflect the up-to-date FS state. Yet, this assumption
|
||||
/// may be broken. For example, an inode in procfs (say, `/proc/1/fd/2`) can "disappear" without
|
||||
/// notice from the perspective of the dentry cache. So for such inodes, they are incompatible
|
||||
/// with the dentry cache. And this method returns `false`.
|
||||
/// may be broken. If the inodes of a file system may "disappear" without unlinking through the
|
||||
/// VFS layer, then their dentries should not be cached. For example, an inode in procfs
|
||||
/// (say, `/proc/1/fd/2`) can "disappear" without notice from the perspective of the dentry cache.
|
||||
/// So for such inodes, they are incompatible with the dentry cache. And this method returns `false`.
|
||||
///
|
||||
/// Note that if any ancestor directory of an inode has this method returns `false`, then
|
||||
/// this inode would not be cached by the dentry cache, even when the method of this
|
||||
|
|
|
@ -11,5 +11,6 @@ pod = { git = "https://github.com/jinzhao-dev/pod", rev = "71e59ec" }
|
|||
typeflags-util = { path = "../typeflags-util" }
|
||||
jinux-rights-proc = { path = "../jinux-rights-proc" }
|
||||
jinux-rights = { path = "../jinux-rights" }
|
||||
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
|
||||
[features]
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
use bitvec::prelude::BitVec;
|
||||
|
||||
/// An id allocator with BitVec.
|
||||
/// The true bit means the id is allocated and vice versa.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct IdAlloc {
|
||||
bitset: BitVec,
|
||||
first_available_id: usize,
|
||||
}
|
||||
|
||||
impl IdAlloc {
|
||||
/// Constructs a new id allocator with the maximum capacity.
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
let mut bitset = BitVec::with_capacity(capacity);
|
||||
bitset.resize(capacity, false);
|
||||
Self {
|
||||
bitset,
|
||||
first_available_id: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates and returns an id.
|
||||
///
|
||||
/// Returns None if can not allocate.
|
||||
pub fn alloc(&mut self) -> Option<usize> {
|
||||
if self.first_available_id < self.bitset.len() {
|
||||
let id = self.first_available_id;
|
||||
self.bitset.set(id, true);
|
||||
self.first_available_id = (id + 1..self.bitset.len())
|
||||
.find(|&i| !self.bitset[i])
|
||||
.map_or(self.bitset.len(), |i| i);
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Frees the allocated id.
|
||||
///
|
||||
/// This panics if the id is out of bounds.
|
||||
pub fn free(&mut self, id: usize) {
|
||||
debug_assert!(self.is_allocated(id));
|
||||
|
||||
self.bitset.set(id, false);
|
||||
if id < self.first_available_id {
|
||||
self.first_available_id = id;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true is the id is allocated.
|
||||
///
|
||||
/// This panics if the id is out of bounds.
|
||||
pub fn is_allocated(&self, id: usize) -> bool {
|
||||
self.bitset[id]
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ extern crate alloc;
|
|||
|
||||
pub mod dup;
|
||||
pub mod frame_ptr;
|
||||
pub mod id_allocator;
|
||||
pub mod safe_ptr;
|
||||
pub mod slot_vec;
|
||||
pub mod union_read_ptr;
|
||||
|
|
Loading…
Reference in New Issue