Add devpts to support the ptmx

This commit is contained in:
LI Qing 2023-07-18 17:19:32 +08:00 committed by Tate, Hongliang Tian
parent 04db7c8c3d
commit 4f00e5a167
15 changed files with 814 additions and 73 deletions

1
Cargo.lock generated
View File

@ -811,6 +811,7 @@ dependencies = [
name = "jinux-util"
version = "0.1.0"
dependencies = [
"bitvec",
"jinux-frame",
"jinux-rights",
"jinux-rights-proc",

View File

@ -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(())
}

View File

@ -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(())
}

View File

@ -46,6 +46,7 @@ pub enum DeviceType {
}
/// Device Id
#[derive(Clone, Copy)]
pub struct DeviceId(u64);
impl DeviceId {

View File

@ -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!();
}
}

View File

@ -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()
}
}

View File

@ -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())
}
}

View File

@ -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!();
}
}

View File

@ -1,4 +1,5 @@
pub mod device;
pub mod devpts;
pub mod epoll;
pub mod file_handle;
pub mod file_table;

View File

@ -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),
}
}

View File

@ -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()

View File

@ -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

View File

@ -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]

View File

@ -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]
}
}

View File

@ -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;