mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-27 19:33:23 +00:00
Rename aster-std to aster-nix
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
8d456ebe8f
commit
ab03ef0fe8
@ -1,76 +0,0 @@
|
||||
[package]
|
||||
name = "aster-std"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
aster-frame = { path = "../../../framework/aster-frame" }
|
||||
align_ext = { path = "../../../framework/libs/align_ext" }
|
||||
pod = { git = "https://github.com/asterinas/pod", rev = "d7dba56" }
|
||||
aster-input = { path = "../../comps/input" }
|
||||
aster-block = { path = "../../comps/block" }
|
||||
aster-network = { path = "../../comps/network" }
|
||||
aster-console = { path = "../../comps/console" }
|
||||
aster-time = { path = "../../comps/time" }
|
||||
aster-virtio = { path = "../../comps/virtio" }
|
||||
aster-rights = { path = "../aster-rights" }
|
||||
controlled = { path = "../../libs/comp-sys/controlled" }
|
||||
typeflags = { path = "../typeflags" }
|
||||
typeflags-util = { path = "../typeflags-util" }
|
||||
aster-rights-proc = { path = "../aster-rights-proc" }
|
||||
aster-util = { path = "../aster-util" }
|
||||
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
|
||||
cpio-decoder = { path = "../cpio-decoder" }
|
||||
ascii = { version = "1.1", default-features = false, features = ["alloc"] }
|
||||
intrusive-collections = "0.9.5"
|
||||
time = { version = "0.3", default-features = false, features = ["alloc"] }
|
||||
smoltcp = { version = "0.9.1", default-features = false, features = [
|
||||
"alloc",
|
||||
"log",
|
||||
"medium-ethernet",
|
||||
"medium-ip",
|
||||
"proto-dhcpv4",
|
||||
"proto-ipv4",
|
||||
"proto-igmp",
|
||||
"socket-icmp",
|
||||
"socket-udp",
|
||||
"socket-tcp",
|
||||
"socket-raw",
|
||||
"socket-dhcpv4",
|
||||
] }
|
||||
ktest = { path = "../../../framework/libs/ktest" }
|
||||
tdx-guest = { path = "../../../framework/libs/tdx-guest", optional = true }
|
||||
|
||||
# parse elf file
|
||||
xmas-elf = "0.8.0"
|
||||
# goblin = {version= "0.5.3", default-features = false, features = ["elf64"]}
|
||||
# data-structures
|
||||
bitflags = "1.3"
|
||||
ringbuf = { version = "0.3.2", default-features = false, features = ["alloc"] }
|
||||
keyable-arc = { path = "../keyable-arc" }
|
||||
# unzip initramfs
|
||||
libflate = { git = "https://github.com/asterinas/libflate", rev = "b781da6", features = [
|
||||
"no_std",
|
||||
] }
|
||||
core2 = { version = "0.4", default_features = false, features = ["alloc"] }
|
||||
lending-iterator = "0.1.7"
|
||||
spin = "0.9.4"
|
||||
vte = "0.10"
|
||||
lru = "0.9.0"
|
||||
log = "0.4"
|
||||
getrandom = { version = "0.2.10", default-features = false, features = [
|
||||
"rdrand",
|
||||
] }
|
||||
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
static_assertions = "1.1.0"
|
||||
inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-macro", rev = "98f7e3e" }
|
||||
getset = "0.1.2"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.0"
|
||||
features = ["spin_no_std"]
|
||||
|
||||
[features]
|
||||
intel_tdx = ["dep:tdx-guest"]
|
@ -1,42 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! `print` and `println` macros
|
||||
//!
|
||||
//! FIXME: It will print to all `virtio-console` devices, which is not a good choice.
|
||||
//!
|
||||
|
||||
use core::fmt::{Arguments, Write};
|
||||
|
||||
struct VirtioConsolesPrinter;
|
||||
|
||||
impl Write for VirtioConsolesPrinter {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
for (_, device) in aster_console::all_devices() {
|
||||
device.send(s.as_bytes());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _print(args: Arguments) {
|
||||
VirtioConsolesPrinter.write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
/// Copy from Rust std: https://github.com/rust-lang/rust/blob/master/library/std/src/macros.rs
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => {{
|
||||
$crate::console::_print(format_args!($($arg)*));
|
||||
}};
|
||||
}
|
||||
|
||||
/// Copy from Rust std: https://github.com/rust-lang/rust/blob/master/library/std/src/macros.rs
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
() => {
|
||||
$crate::print!("\n")
|
||||
};
|
||||
($($arg:tt)*) => {{
|
||||
$crate::console::_print(format_args_nl!($($arg)*));
|
||||
}};
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod null;
|
||||
mod pty;
|
||||
mod random;
|
||||
#[cfg(feature = "intel_tdx")]
|
||||
mod tdxguest;
|
||||
pub mod tty;
|
||||
mod urandom;
|
||||
mod zero;
|
||||
|
||||
use crate::fs::device::{add_node, Device, DeviceId, DeviceType};
|
||||
use crate::prelude::*;
|
||||
pub use pty::new_pty_pair;
|
||||
pub use pty::{PtyMaster, PtySlave};
|
||||
pub use random::Random;
|
||||
#[cfg(feature = "intel_tdx")]
|
||||
pub use tdxguest::TdxGuest;
|
||||
pub use urandom::Urandom;
|
||||
|
||||
use self::tty::get_n_tty;
|
||||
|
||||
/// Init the device node in fs, must be called after mounting rootfs.
|
||||
pub fn init() -> Result<()> {
|
||||
let null = Arc::new(null::Null);
|
||||
add_node(null, "null")?;
|
||||
let zero = Arc::new(zero::Zero);
|
||||
add_node(zero, "zero")?;
|
||||
tty::init();
|
||||
let console = get_n_tty().clone();
|
||||
add_node(console, "console")?;
|
||||
let tty = Arc::new(tty::TtyDevice);
|
||||
add_node(tty, "tty")?;
|
||||
#[cfg(feature = "intel_tdx")]
|
||||
let tdx_guest = Arc::new(tdxguest::TdxGuest);
|
||||
#[cfg(feature = "intel_tdx")]
|
||||
add_node(tdx_guest, "tdx-guest")?;
|
||||
let random = Arc::new(random::Random);
|
||||
add_node(random, "random")?;
|
||||
let urandom = Arc::new(urandom::Urandom);
|
||||
add_node(urandom, "urandom")?;
|
||||
pty::init()?;
|
||||
Ok(())
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::*;
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
pub struct Null;
|
||||
|
||||
impl Device for Null {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// Same value with Linux
|
||||
DeviceId::new(1, 3)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Null {
|
||||
fn read(&self, _buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::fs::devpts::DevPts;
|
||||
use crate::fs::fs_resolver::{FsPath, FsResolver};
|
||||
use crate::fs::utils::{Dentry, Inode, InodeMode, InodeType};
|
||||
use crate::prelude::*;
|
||||
|
||||
#[allow(clippy::module_inception)]
|
||||
mod pty;
|
||||
|
||||
pub use pty::{PtyMaster, PtySlave};
|
||||
use spin::Once;
|
||||
|
||||
static DEV_PTS: Once<Arc<Dentry>> = Once::new();
|
||||
|
||||
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())?;
|
||||
|
||||
DEV_PTS.call_once(|| devpts);
|
||||
|
||||
// Create the "ptmx" symlink.
|
||||
let ptmx = dev.create(
|
||||
"ptmx",
|
||||
InodeType::SymLink,
|
||||
InodeMode::from_bits_truncate(0o777),
|
||||
)?;
|
||||
ptmx.inode().write_link("pts/ptmx")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn new_pty_pair(index: u32, ptmx: Arc<dyn Inode>) -> Result<(Arc<PtyMaster>, Arc<PtySlave>)> {
|
||||
debug!("pty index = {}", index);
|
||||
let master = PtyMaster::new(ptmx, index);
|
||||
let slave = PtySlave::new(&master);
|
||||
Ok((master, slave))
|
||||
}
|
@ -1,398 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::format;
|
||||
use ringbuf::{ring_buffer::RbBase, HeapRb, Rb};
|
||||
|
||||
use crate::device::tty::line_discipline::LineDiscipline;
|
||||
use crate::device::tty::new_job_control_and_ldisc;
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::devpts::DevPts;
|
||||
use crate::fs::fs_resolver::FsPath;
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::fs::utils::{AccessMode, Inode, InodeMode, IoctlCmd};
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
use crate::process::{JobControl, Terminal};
|
||||
use crate::util::{read_val_from_user, write_val_to_user};
|
||||
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
|
||||
/// Pesudo terminal master.
|
||||
/// Internally, it has two buffers.
|
||||
/// One is inside ldisc, which is written by master and read by slave,
|
||||
/// the other is a ring buffer, which is written by slave and read by master.
|
||||
pub struct PtyMaster {
|
||||
ptmx: Arc<dyn Inode>,
|
||||
index: u32,
|
||||
output: Arc<LineDiscipline>,
|
||||
input: SpinLock<HeapRb<u8>>,
|
||||
job_control: Arc<JobControl>,
|
||||
/// The state of input buffer
|
||||
pollee: Pollee,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl PtyMaster {
|
||||
pub fn new(ptmx: Arc<dyn Inode>, index: u32) -> Arc<Self> {
|
||||
let (job_control, ldisc) = new_job_control_and_ldisc();
|
||||
Arc::new_cyclic(move |weak_ref| PtyMaster {
|
||||
ptmx,
|
||||
index,
|
||||
output: ldisc,
|
||||
input: SpinLock::new(HeapRb::new(BUFFER_CAPACITY)),
|
||||
job_control,
|
||||
pollee: Pollee::new(IoEvents::OUT),
|
||||
weak_self: weak_ref.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
self.index
|
||||
}
|
||||
|
||||
pub fn ptmx(&self) -> &Arc<dyn Inode> {
|
||||
&self.ptmx
|
||||
}
|
||||
|
||||
pub(super) fn slave_push_char(&self, ch: u8) {
|
||||
let mut input = self.input.lock_irq_disabled();
|
||||
input.push_overwrite(ch);
|
||||
self.update_state(&input);
|
||||
}
|
||||
|
||||
pub(super) fn slave_poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let mut poll_status = IoEvents::empty();
|
||||
|
||||
let poll_in_mask = mask & IoEvents::IN;
|
||||
if !poll_in_mask.is_empty() {
|
||||
let poll_in_status = self.output.poll(poll_in_mask, poller);
|
||||
poll_status |= poll_in_status;
|
||||
}
|
||||
|
||||
let poll_out_mask = mask & IoEvents::OUT;
|
||||
if !poll_out_mask.is_empty() {
|
||||
let poll_out_status = self.pollee.poll(poll_out_mask, poller);
|
||||
poll_status |= poll_out_status;
|
||||
}
|
||||
|
||||
poll_status
|
||||
}
|
||||
|
||||
pub(super) fn slave_buf_len(&self) -> usize {
|
||||
self.output.buffer_len()
|
||||
}
|
||||
|
||||
fn update_state(&self, buf: &HeapRb<u8>) {
|
||||
if buf.is_empty() {
|
||||
self.pollee.del_events(IoEvents::IN)
|
||||
} else {
|
||||
self.pollee.add_events(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for PtyMaster {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
// TODO: deal with nonblocking read
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
let mut input = self.input.lock_irq_disabled();
|
||||
|
||||
if input.is_empty() {
|
||||
let events = self.pollee.poll(IoEvents::IN, Some(&poller));
|
||||
|
||||
if events.contains(IoEvents::ERR) {
|
||||
return_errno_with_message!(Errno::EACCES, "unexpected err");
|
||||
}
|
||||
|
||||
if events.is_empty() {
|
||||
drop(input);
|
||||
// FIXME: deal with pty read timeout
|
||||
poller.wait()?;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let read_len = input.len().min(buf.len());
|
||||
input.pop_slice(&mut buf[..read_len]);
|
||||
self.update_state(&input);
|
||||
return Ok(read_len);
|
||||
}
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let mut input = self.input.lock();
|
||||
for character in buf {
|
||||
self.output.push_char(*character, |content| {
|
||||
for byte in content.as_bytes() {
|
||||
input.push_overwrite(*byte);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.update_state(&input);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
let termios = self.output.termios();
|
||||
write_val_to_user(arg, &termios)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TCSETS => {
|
||||
let termios = read_val_from_user(arg)?;
|
||||
self.output.set_termios(termios);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSPTLCK => {
|
||||
// TODO: lock/unlock pty
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPTN => {
|
||||
let idx = self.index();
|
||||
write_val_to_user(arg, &idx)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPTPEER => {
|
||||
let current = current!();
|
||||
|
||||
// TODO: deal with open options
|
||||
let slave = {
|
||||
let slave_name = {
|
||||
let devpts_path = super::DEV_PTS.get().unwrap().abs_path();
|
||||
format!("{}/{}", devpts_path, self.index())
|
||||
};
|
||||
|
||||
let fs_path = FsPath::try_from(slave_name.as_str())?;
|
||||
|
||||
let inode_handle = {
|
||||
let fs = current.fs().read();
|
||||
let flags = AccessMode::O_RDWR as u32;
|
||||
let mode = (InodeMode::S_IRUSR | InodeMode::S_IWUSR).bits();
|
||||
fs.open(&fs_path, flags, mode)?
|
||||
};
|
||||
Arc::new(inode_handle)
|
||||
};
|
||||
|
||||
let fd = {
|
||||
let mut file_table = current.file_table().lock();
|
||||
file_table.insert(slave)
|
||||
};
|
||||
Ok(fd)
|
||||
}
|
||||
IoctlCmd::TIOCGWINSZ => {
|
||||
let winsize = self.output.window_size();
|
||||
write_val_to_user(arg, &winsize)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSWINSZ => {
|
||||
let winsize = read_val_from_user(arg)?;
|
||||
self.output.set_window_size(winsize);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPGRP => {
|
||||
let Some(foreground) = self.foreground() else {
|
||||
return_errno_with_message!(
|
||||
Errno::ESRCH,
|
||||
"the foreground process group does not exist"
|
||||
);
|
||||
};
|
||||
let fg_pgid = foreground.pgid();
|
||||
write_val_to_user(arg, &fg_pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSPGRP => {
|
||||
let pgid = {
|
||||
let pgid: i32 = read_val_from_user(arg)?;
|
||||
if pgid < 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "negative pgid");
|
||||
}
|
||||
pgid as u32
|
||||
};
|
||||
|
||||
self.set_foreground(&pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
self.set_current_session()?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCNOTTY => {
|
||||
self.release_current_session()?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let len = self.input.lock().len() as i32;
|
||||
write_val_to_user(arg, &len)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let mut poll_status = IoEvents::empty();
|
||||
|
||||
let poll_in_mask = mask & IoEvents::IN;
|
||||
if !poll_in_mask.is_empty() {
|
||||
let poll_in_status = self.pollee.poll(poll_in_mask, poller);
|
||||
poll_status |= poll_in_status;
|
||||
}
|
||||
|
||||
let poll_out_mask = mask & IoEvents::OUT;
|
||||
if !poll_out_mask.is_empty() {
|
||||
let poll_out_status = self.output.poll(poll_out_mask, poller);
|
||||
poll_status |= poll_out_status;
|
||||
}
|
||||
|
||||
poll_status
|
||||
}
|
||||
}
|
||||
|
||||
impl Terminal for PtyMaster {
|
||||
fn arc_self(&self) -> Arc<dyn Terminal> {
|
||||
self.weak_self.upgrade().unwrap() as _
|
||||
}
|
||||
|
||||
fn job_control(&self) -> &JobControl {
|
||||
&self.job_control
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PtyMaster {
|
||||
fn drop(&mut self) {
|
||||
let fs = self.ptmx.fs();
|
||||
let devpts = fs.downcast_ref::<DevPts>().unwrap();
|
||||
|
||||
let index = self.index;
|
||||
devpts.remove_slave(index);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PtySlave {
|
||||
master: Weak<PtyMaster>,
|
||||
job_control: JobControl,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl PtySlave {
|
||||
pub fn new(master: &Arc<PtyMaster>) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_ref| PtySlave {
|
||||
master: Arc::downgrade(master),
|
||||
job_control: JobControl::new(),
|
||||
weak_self: weak_ref.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u32 {
|
||||
self.master().index()
|
||||
}
|
||||
|
||||
fn master(&self) -> Arc<PtyMaster> {
|
||||
self.master.upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for PtySlave {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> crate::fs::device::DeviceId {
|
||||
DeviceId::new(88, self.index())
|
||||
}
|
||||
}
|
||||
|
||||
impl Terminal for PtySlave {
|
||||
fn arc_self(&self) -> Arc<dyn Terminal> {
|
||||
self.weak_self.upgrade().unwrap() as _
|
||||
}
|
||||
|
||||
fn job_control(&self) -> &JobControl {
|
||||
&self.job_control
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for PtySlave {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.job_control.wait_until_in_foreground()?;
|
||||
self.master().output.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let master = self.master();
|
||||
for ch in buf {
|
||||
// do we need to add '\r' here?
|
||||
if *ch == b'\n' {
|
||||
master.slave_push_char(b'\r');
|
||||
master.slave_push_char(b'\n');
|
||||
} else {
|
||||
master.slave_push_char(*ch);
|
||||
}
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.master().slave_poll(mask, poller)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS
|
||||
| IoctlCmd::TCSETS
|
||||
| IoctlCmd::TIOCGPTN
|
||||
| IoctlCmd::TIOCGWINSZ
|
||||
| IoctlCmd::TIOCSWINSZ => self.master().ioctl(cmd, arg),
|
||||
IoctlCmd::TIOCGPGRP => {
|
||||
if !self.is_controlling_terminal() {
|
||||
return_errno_with_message!(Errno::ENOTTY, "slave is not controlling terminal");
|
||||
}
|
||||
|
||||
let Some(foreground) = self.foreground() else {
|
||||
return_errno_with_message!(
|
||||
Errno::ESRCH,
|
||||
"the foreground process group does not exist"
|
||||
);
|
||||
};
|
||||
|
||||
let fg_pgid = foreground.pgid();
|
||||
write_val_to_user(arg, &fg_pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSPGRP => {
|
||||
let pgid = {
|
||||
let pgid: i32 = read_val_from_user(arg)?;
|
||||
if pgid < 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "negative pgid");
|
||||
}
|
||||
pgid as u32
|
||||
};
|
||||
|
||||
self.set_foreground(&pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
self.set_current_session()?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCNOTTY => {
|
||||
self.release_current_session()?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::FIONREAD => {
|
||||
let buffer_len = self.master().slave_buf_len() as i32;
|
||||
write_val_to_user(arg, &buffer_len)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => Ok(0),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
pub struct Random;
|
||||
|
||||
impl Random {
|
||||
pub fn getrandom(buf: &mut [u8]) -> Result<usize> {
|
||||
getrandom::getrandom(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Random {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(1, 8)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Random {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
Self::getrandom(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl From<getrandom::Error> for Error {
|
||||
fn from(value: getrandom::Error) -> Self {
|
||||
Error::with_message(Errno::ENOSYS, "cannot generate random bytes")
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::*;
|
||||
use crate::error::Error;
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::fs::utils::IoctlCmd;
|
||||
use crate::process::signal::Poller;
|
||||
use crate::util::{read_val_from_user, write_val_to_user};
|
||||
use tdx_guest::tdcall::{get_report, TdCallError};
|
||||
|
||||
const TDX_REPORTDATA_LEN: usize = 64;
|
||||
const TDX_REPORT_LEN: usize = 1024;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct TdxReportRequest {
|
||||
reportdata: [u8; TDX_REPORTDATA_LEN],
|
||||
tdreport: [u8; TDX_REPORT_LEN],
|
||||
}
|
||||
|
||||
pub struct TdxGuest;
|
||||
|
||||
impl Device for TdxGuest {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::MiscDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(0xa, 0x7b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TdCallError> for Error {
|
||||
fn from(err: TdCallError) -> Self {
|
||||
match err {
|
||||
TdCallError::TdxNoValidVeInfo => {
|
||||
Error::with_message(Errno::EINVAL, "TdCallError::TdxNoValidVeInfo")
|
||||
}
|
||||
TdCallError::TdxOperandInvalid => {
|
||||
Error::with_message(Errno::EINVAL, "TdCallError::TdxOperandInvalid")
|
||||
}
|
||||
TdCallError::TdxPageAlreadyAccepted => {
|
||||
Error::with_message(Errno::EINVAL, "TdCallError::TdxPageAlreadyAccepted")
|
||||
}
|
||||
TdCallError::TdxPageSizeMismatch => {
|
||||
Error::with_message(Errno::EINVAL, "TdCallError::TdxPageSizeMismatch")
|
||||
}
|
||||
TdCallError::TdxOperandBusy => {
|
||||
Error::with_message(Errno::EBUSY, "TdCallError::TdxOperandBusy")
|
||||
}
|
||||
TdCallError::Other => Error::with_message(Errno::EAGAIN, "TdCallError::Other"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for TdxGuest {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EPERM, "Read operation not supported")
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EPERM, "Write operation not supported")
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TDXGETREPORT => {
|
||||
let mut tdx_report: TdxReportRequest = read_val_from_user(arg)?;
|
||||
match get_report(&mut tdx_report.tdreport, &tdx_report.reportdata) {
|
||||
Ok(_) => {}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
write_val_to_user(arg, &tdx_report)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => return_errno_with_message!(Errno::EPERM, "Unsupported ioctl"),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
/// Corresponds to `/dev/tty` in the file system. This device represents the controlling terminal
|
||||
/// of the session of current process.
|
||||
pub struct TtyDevice;
|
||||
|
||||
impl Device for TtyDevice {
|
||||
fn open(&self) -> Result<Option<Arc<dyn FileIo>>> {
|
||||
let current = current!();
|
||||
let session = current.session().unwrap();
|
||||
|
||||
let Some(terminal) = session.terminal() else {
|
||||
return_errno_with_message!(
|
||||
Errno::ENOTTY,
|
||||
"the session does not have controlling terminal"
|
||||
);
|
||||
};
|
||||
|
||||
Ok(Some(terminal as Arc<dyn FileIo>))
|
||||
}
|
||||
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(5, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for TtyDevice {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot read tty device");
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot write tty device");
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
IoEvents::empty()
|
||||
}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub use aster_frame::arch::console::register_console_input_callback;
|
||||
use spin::Once;
|
||||
|
||||
use crate::{
|
||||
device::tty::{get_n_tty, Tty},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub static TTY_DRIVER: Once<Arc<TtyDriver>> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
for (_, device) in aster_console::all_devices() {
|
||||
device.register_callback(&console_input_callback)
|
||||
}
|
||||
let tty_driver = Arc::new(TtyDriver::new());
|
||||
// FIXME: install n_tty into tty_driver?
|
||||
let n_tty = get_n_tty();
|
||||
tty_driver.install(n_tty.clone());
|
||||
TTY_DRIVER.call_once(|| tty_driver);
|
||||
}
|
||||
|
||||
pub struct TtyDriver {
|
||||
ttys: SpinLock<Vec<Arc<Tty>>>,
|
||||
}
|
||||
|
||||
impl TtyDriver {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
ttys: SpinLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the tty device in driver's internal table.
|
||||
pub fn lookup(&self, index: usize) -> Result<Arc<Tty>> {
|
||||
let ttys = self.ttys.lock_irq_disabled();
|
||||
// Return the tty device corresponding to idx
|
||||
if index >= ttys.len() {
|
||||
return_errno_with_message!(Errno::ENODEV, "lookup failed. No tty device");
|
||||
}
|
||||
let tty = ttys[index].clone();
|
||||
drop(ttys);
|
||||
Ok(tty)
|
||||
}
|
||||
|
||||
/// Install a new tty into the driver's internal tables.
|
||||
pub fn install(self: &Arc<Self>, tty: Arc<Tty>) {
|
||||
tty.set_driver(Arc::downgrade(self));
|
||||
self.ttys.lock_irq_disabled().push(tty);
|
||||
}
|
||||
|
||||
/// remove a new tty into the driver's internal tables.
|
||||
pub fn remove(&self, index: usize) -> Result<()> {
|
||||
let mut ttys = self.ttys.lock_irq_disabled();
|
||||
if index >= ttys.len() {
|
||||
return_errno_with_message!(Errno::ENODEV, "lookup failed. No tty device");
|
||||
}
|
||||
let removed_tty = ttys.remove(index);
|
||||
removed_tty.set_driver(Weak::new());
|
||||
drop(ttys);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn receive_char(&self, item: u8) {
|
||||
// FIXME: should the char send to all ttys?
|
||||
for tty in &*self.ttys.lock_irq_disabled() {
|
||||
tty.receive_char(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn console_input_callback(items: &[u8]) {
|
||||
let tty_driver = get_tty_driver();
|
||||
for item in items {
|
||||
tty_driver.receive_char(*item);
|
||||
}
|
||||
}
|
||||
|
||||
fn serial_input_callback(item: u8) {
|
||||
let tty_driver = get_tty_driver();
|
||||
tty_driver.receive_char(item);
|
||||
}
|
||||
|
||||
fn get_tty_driver() -> &'static TtyDriver {
|
||||
TTY_DRIVER.get().unwrap()
|
||||
}
|
@ -1,427 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::constants::{SIGINT, SIGQUIT};
|
||||
use crate::process::signal::signals::kernel::KernelSignal;
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
use crate::thread::work_queue::work_item::WorkItem;
|
||||
use crate::thread::work_queue::{submit_work_item, WorkPriority};
|
||||
use alloc::format;
|
||||
use aster_frame::trap::disable_local;
|
||||
use ringbuf::{ring_buffer::RbBase, Rb, StaticRb};
|
||||
|
||||
use super::termio::{KernelTermios, WinSize, CC_C_CHAR};
|
||||
|
||||
// This implementation refers the implementation of linux
|
||||
// https://elixir.bootlin.com/linux/latest/source/include/linux/tty_ldisc.h
|
||||
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
|
||||
pub type LdiscSignalSender = Arc<dyn Fn(KernelSignal) + Send + Sync + 'static>;
|
||||
|
||||
pub struct LineDiscipline {
|
||||
/// current line
|
||||
current_line: SpinLock<CurrentLine>,
|
||||
/// The read buffer
|
||||
read_buffer: SpinLock<StaticRb<u8, BUFFER_CAPACITY>>,
|
||||
/// termios
|
||||
termios: SpinLock<KernelTermios>,
|
||||
/// Windows size,
|
||||
winsize: SpinLock<WinSize>,
|
||||
/// Pollee
|
||||
pollee: Pollee,
|
||||
/// Used to send signal for foreground processes, when some char comes.
|
||||
send_signal: LdiscSignalSender,
|
||||
/// work item
|
||||
work_item: Arc<WorkItem>,
|
||||
/// Parameters used by a work item.
|
||||
work_item_para: Arc<SpinLock<LineDisciplineWorkPara>>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct CurrentLine {
|
||||
buffer: StaticRb<u8, BUFFER_CAPACITY>,
|
||||
}
|
||||
|
||||
impl CurrentLine {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
buffer: StaticRb::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// read all bytes inside current line and clear current line
|
||||
pub fn drain(&mut self) -> Vec<u8> {
|
||||
self.buffer.pop_iter().collect()
|
||||
}
|
||||
|
||||
pub fn push_char(&mut self, char: u8) {
|
||||
// What should we do if line is full?
|
||||
debug_assert!(!self.is_full());
|
||||
self.buffer.push_overwrite(char);
|
||||
}
|
||||
|
||||
pub fn backspace(&mut self) {
|
||||
self.buffer.pop();
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.buffer.is_full()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.buffer.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl LineDiscipline {
|
||||
/// Create a new line discipline
|
||||
pub fn new(send_signal: LdiscSignalSender) -> Arc<Self> {
|
||||
Arc::new_cyclic(move |line_ref: &Weak<LineDiscipline>| {
|
||||
let line_discipline = line_ref.clone();
|
||||
let work_item = Arc::new(WorkItem::new(Box::new(move || {
|
||||
if let Some(line_discipline) = line_discipline.upgrade() {
|
||||
line_discipline.update_readable_state_after();
|
||||
}
|
||||
})));
|
||||
Self {
|
||||
current_line: SpinLock::new(CurrentLine::new()),
|
||||
read_buffer: SpinLock::new(StaticRb::default()),
|
||||
termios: SpinLock::new(KernelTermios::default()),
|
||||
winsize: SpinLock::new(WinSize::default()),
|
||||
pollee: Pollee::new(IoEvents::empty()),
|
||||
send_signal,
|
||||
work_item,
|
||||
work_item_para: Arc::new(SpinLock::new(LineDisciplineWorkPara::new())),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Push char to line discipline.
|
||||
pub fn push_char<F2: FnMut(&str)>(&self, ch: u8, echo_callback: F2) {
|
||||
let termios = self.termios.lock_irq_disabled();
|
||||
|
||||
let ch = if termios.contains_icrnl() && ch == b'\r' {
|
||||
b'\n'
|
||||
} else {
|
||||
ch
|
||||
};
|
||||
|
||||
if self.may_send_signal(&termios, ch) {
|
||||
// The char is already dealt with, so just return
|
||||
return;
|
||||
}
|
||||
|
||||
// Typically, a tty in raw mode does not echo. But the tty can also be in a cbreak mode,
|
||||
// with ICANON closed and ECHO opened.
|
||||
if termios.contain_echo() {
|
||||
self.output_char(ch, &termios, echo_callback);
|
||||
}
|
||||
|
||||
// Raw mode
|
||||
if !termios.is_canonical_mode() {
|
||||
self.read_buffer.lock_irq_disabled().push_overwrite(ch);
|
||||
self.update_readable_state_deferred();
|
||||
return;
|
||||
}
|
||||
|
||||
// Canonical mode
|
||||
|
||||
if ch == *termios.get_special_char(CC_C_CHAR::VKILL) {
|
||||
// Erase current line
|
||||
self.current_line.lock_irq_disabled().drain();
|
||||
}
|
||||
|
||||
if ch == *termios.get_special_char(CC_C_CHAR::VERASE) {
|
||||
// Type backspace
|
||||
let mut current_line = self.current_line.lock_irq_disabled();
|
||||
if !current_line.is_empty() {
|
||||
current_line.backspace();
|
||||
}
|
||||
}
|
||||
|
||||
if is_line_terminator(ch, &termios) {
|
||||
// If a new line is met, all bytes in current_line will be moved to read_buffer
|
||||
let mut current_line = self.current_line.lock_irq_disabled();
|
||||
current_line.push_char(ch);
|
||||
let current_line_chars = current_line.drain();
|
||||
for char in current_line_chars {
|
||||
self.read_buffer.lock_irq_disabled().push_overwrite(char);
|
||||
}
|
||||
}
|
||||
|
||||
if is_printable_char(ch) {
|
||||
// Printable character
|
||||
self.current_line.lock_irq_disabled().push_char(ch);
|
||||
}
|
||||
|
||||
self.update_readable_state_deferred();
|
||||
}
|
||||
|
||||
fn may_send_signal(&self, termios: &KernelTermios, ch: u8) -> bool {
|
||||
if !termios.is_canonical_mode() || !termios.contains_isig() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let signal = match ch {
|
||||
ch if ch == *termios.get_special_char(CC_C_CHAR::VINTR) => KernelSignal::new(SIGINT),
|
||||
ch if ch == *termios.get_special_char(CC_C_CHAR::VQUIT) => KernelSignal::new(SIGQUIT),
|
||||
_ => return false,
|
||||
};
|
||||
// `kernel_signal()` may cause sleep, so only construct parameters here.
|
||||
self.work_item_para.lock_irq_disabled().kernel_signal = Some(signal);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn update_readable_state(&self) {
|
||||
let buffer = self.read_buffer.lock_irq_disabled();
|
||||
if !buffer.is_empty() {
|
||||
self.pollee.add_events(IoEvents::IN);
|
||||
} else {
|
||||
self.pollee.del_events(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
|
||||
fn update_readable_state_deferred(&self) {
|
||||
let buffer = self.read_buffer.lock_irq_disabled();
|
||||
// add/del events may sleep, so only construct parameters here.
|
||||
if !buffer.is_empty() {
|
||||
self.work_item_para.lock_irq_disabled().pollee_type = Some(PolleeType::Add);
|
||||
} else {
|
||||
self.work_item_para.lock_irq_disabled().pollee_type = Some(PolleeType::Del);
|
||||
}
|
||||
submit_work_item(self.work_item.clone(), WorkPriority::High);
|
||||
}
|
||||
|
||||
/// include all operations that may cause sleep, and processes by a work queue.
|
||||
fn update_readable_state_after(&self) {
|
||||
if let Some(signal) = self.work_item_para.lock_irq_disabled().kernel_signal.take() {
|
||||
(self.send_signal)(signal);
|
||||
};
|
||||
if let Some(pollee_type) = self.work_item_para.lock_irq_disabled().pollee_type.take() {
|
||||
match pollee_type {
|
||||
PolleeType::Add => {
|
||||
self.pollee.add_events(IoEvents::IN);
|
||||
}
|
||||
PolleeType::Del => {
|
||||
self.pollee.del_events(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: respect output flags
|
||||
fn output_char<F: FnMut(&str)>(&self, ch: u8, termios: &KernelTermios, mut echo_callback: F) {
|
||||
match ch {
|
||||
b'\n' => echo_callback("\n"),
|
||||
b'\r' => echo_callback("\r\n"),
|
||||
ch if ch == *termios.get_special_char(CC_C_CHAR::VERASE) => {
|
||||
// write a space to overwrite current character
|
||||
let backspace: &str = core::str::from_utf8(&[b'\x08', b' ', b'\x08']).unwrap();
|
||||
echo_callback(backspace);
|
||||
}
|
||||
ch if is_printable_char(ch) => print!("{}", char::from(ch)),
|
||||
ch if is_ctrl_char(ch) && termios.contains_echo_ctl() => {
|
||||
let ctrl_char = format!("^{}", get_printable_char(ch));
|
||||
echo_callback(&ctrl_char);
|
||||
}
|
||||
item => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
loop {
|
||||
let res = self.try_read(buf);
|
||||
match res {
|
||||
Ok(len) => return Ok(len),
|
||||
Err(e) if e.error() != Errno::EAGAIN => return Err(e),
|
||||
Err(_) => {
|
||||
let poller = Some(Poller::new());
|
||||
if self.poll(IoEvents::IN, poller.as_ref()).is_empty() {
|
||||
poller.as_ref().unwrap().wait()?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// read all bytes buffered to dst, return the actual read length.
|
||||
fn try_read(&self, dst: &mut [u8]) -> Result<usize> {
|
||||
let (vmin, vtime) = {
|
||||
let termios = self.termios.lock_irq_disabled();
|
||||
let vmin = *termios.get_special_char(CC_C_CHAR::VMIN);
|
||||
let vtime = *termios.get_special_char(CC_C_CHAR::VTIME);
|
||||
(vmin, vtime)
|
||||
};
|
||||
let read_len = {
|
||||
let len = self.read_buffer.lock_irq_disabled().len();
|
||||
let max_read_len = len.min(dst.len());
|
||||
if vmin == 0 && vtime == 0 {
|
||||
// poll read
|
||||
self.poll_read(dst)
|
||||
} else if vmin > 0 && vtime == 0 {
|
||||
// block read
|
||||
self.block_read(dst, vmin)?
|
||||
} else if vmin == 0 && vtime > 0 {
|
||||
todo!()
|
||||
} else if vmin > 0 && vtime > 0 {
|
||||
todo!()
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
self.update_readable_state();
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
/// returns immediately with the lesser of the number of bytes available or the number of bytes requested.
|
||||
/// If no bytes are available, completes immediately, returning 0.
|
||||
fn poll_read(&self, dst: &mut [u8]) -> usize {
|
||||
let mut buffer = self.read_buffer.lock_irq_disabled();
|
||||
let len = buffer.len();
|
||||
let max_read_len = len.min(dst.len());
|
||||
if max_read_len == 0 {
|
||||
return 0;
|
||||
}
|
||||
let mut read_len = 0;
|
||||
for dst_i in dst.iter_mut().take(max_read_len) {
|
||||
if let Some(next_char) = buffer.pop() {
|
||||
let termios = self.termios.lock_irq_disabled();
|
||||
if termios.is_canonical_mode() {
|
||||
// canonical mode, read until meet new line
|
||||
if is_line_terminator(next_char, &termios) {
|
||||
// The eof should not be read
|
||||
if !is_eof(next_char, &termios) {
|
||||
*dst_i = next_char;
|
||||
read_len += 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
*dst_i = next_char;
|
||||
read_len += 1;
|
||||
}
|
||||
} else {
|
||||
// raw mode
|
||||
// FIXME: avoid addtional bound check
|
||||
*dst_i = next_char;
|
||||
read_len += 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
read_len
|
||||
}
|
||||
|
||||
// The read() blocks until the number of bytes requested or
|
||||
// at least vmin bytes are available, and returns the real read value.
|
||||
pub fn block_read(&self, dst: &mut [u8], vmin: u8) -> Result<usize> {
|
||||
let _guard = disable_local();
|
||||
let buffer_len = self.read_buffer.lock().len();
|
||||
if buffer_len >= dst.len() {
|
||||
return Ok(self.poll_read(dst));
|
||||
}
|
||||
if buffer_len < vmin as usize {
|
||||
return_errno!(Errno::EAGAIN);
|
||||
}
|
||||
Ok(self.poll_read(&mut dst[..buffer_len]))
|
||||
}
|
||||
|
||||
/// write bytes to buffer, if flush to console, then write the content to console
|
||||
pub fn write(&self, src: &[u8], flush_to_console: bool) -> Result<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// whether there is buffered data
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.read_buffer.lock_irq_disabled().len() == 0
|
||||
}
|
||||
|
||||
pub fn termios(&self) -> KernelTermios {
|
||||
*self.termios.lock_irq_disabled()
|
||||
}
|
||||
|
||||
pub fn set_termios(&self, termios: KernelTermios) {
|
||||
*self.termios.lock_irq_disabled() = termios;
|
||||
}
|
||||
|
||||
pub fn drain_input(&self) {
|
||||
self.current_line.lock().drain();
|
||||
let _: Vec<_> = self.read_buffer.lock().pop_iter().collect();
|
||||
}
|
||||
|
||||
pub fn buffer_len(&self) -> usize {
|
||||
self.read_buffer.lock().len()
|
||||
}
|
||||
|
||||
pub fn window_size(&self) -> WinSize {
|
||||
*self.winsize.lock()
|
||||
}
|
||||
|
||||
pub fn set_window_size(&self, winsize: WinSize) {
|
||||
*self.winsize.lock() = winsize;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_line_terminator(item: u8, termios: &KernelTermios) -> bool {
|
||||
if item == b'\n'
|
||||
|| item == *termios.get_special_char(CC_C_CHAR::VEOF)
|
||||
|| item == *termios.get_special_char(CC_C_CHAR::VEOL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if termios.contains_iexten() && item == *termios.get_special_char(CC_C_CHAR::VEOL2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_eof(ch: u8, termios: &KernelTermios) -> bool {
|
||||
ch == *termios.get_special_char(CC_C_CHAR::VEOF)
|
||||
}
|
||||
|
||||
fn is_printable_char(ch: u8) -> bool {
|
||||
(0x20..0x7f).contains(&ch)
|
||||
}
|
||||
|
||||
fn is_ctrl_char(ch: u8) -> bool {
|
||||
if ch == b'\r' || ch == b'\n' {
|
||||
return false;
|
||||
}
|
||||
|
||||
(0..0x20).contains(&ch)
|
||||
}
|
||||
|
||||
fn get_printable_char(ctrl_char: u8) -> u8 {
|
||||
debug_assert!(is_ctrl_char(ctrl_char));
|
||||
ctrl_char + b'A' - 1
|
||||
}
|
||||
|
||||
enum PolleeType {
|
||||
Add,
|
||||
Del,
|
||||
}
|
||||
|
||||
struct LineDisciplineWorkPara {
|
||||
#[allow(clippy::type_complexity)]
|
||||
kernel_signal: Option<KernelSignal>,
|
||||
pollee_type: Option<PolleeType>,
|
||||
}
|
||||
|
||||
impl LineDisciplineWorkPara {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
kernel_signal: None,
|
||||
pollee_type: None,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,219 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use spin::Once;
|
||||
|
||||
use self::driver::TtyDriver;
|
||||
use self::line_discipline::LineDiscipline;
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::fs::utils::IoctlCmd;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::signals::kernel::KernelSignal;
|
||||
use crate::process::signal::Poller;
|
||||
use crate::process::{JobControl, Process, Terminal};
|
||||
use crate::util::{read_val_from_user, write_val_to_user};
|
||||
|
||||
mod device;
|
||||
pub mod driver;
|
||||
pub mod line_discipline;
|
||||
pub mod termio;
|
||||
|
||||
pub use device::TtyDevice;
|
||||
|
||||
static N_TTY: Once<Arc<Tty>> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
let name = CString::new("console").unwrap();
|
||||
let tty = Tty::new(name);
|
||||
N_TTY.call_once(|| tty);
|
||||
driver::init();
|
||||
}
|
||||
|
||||
pub struct Tty {
|
||||
/// tty_name
|
||||
name: CString,
|
||||
/// line discipline
|
||||
ldisc: Arc<LineDiscipline>,
|
||||
job_control: Arc<JobControl>,
|
||||
/// driver
|
||||
driver: SpinLock<Weak<TtyDriver>>,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl Tty {
|
||||
pub fn new(name: CString) -> Arc<Self> {
|
||||
let (job_control, ldisc) = new_job_control_and_ldisc();
|
||||
Arc::new_cyclic(move |weak_ref| Tty {
|
||||
name,
|
||||
ldisc,
|
||||
job_control,
|
||||
driver: SpinLock::new(Weak::new()),
|
||||
weak_self: weak_ref.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_driver(&self, driver: Weak<TtyDriver>) {
|
||||
*self.driver.lock_irq_disabled() = driver;
|
||||
}
|
||||
|
||||
pub fn receive_char(&self, ch: u8) {
|
||||
self.ldisc.push_char(ch, |content| print!("{}", content));
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Tty {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.job_control.wait_until_in_foreground()?;
|
||||
self.ldisc.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
if let Ok(content) = alloc::str::from_utf8(buf) {
|
||||
print!("{content}");
|
||||
} else {
|
||||
println!("Not utf-8 content: {:?}", buf);
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.ldisc.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
match cmd {
|
||||
IoctlCmd::TCGETS => {
|
||||
// Get terminal attributes
|
||||
let termios = self.ldisc.termios();
|
||||
trace!("get termios = {:?}", termios);
|
||||
write_val_to_user(arg, &termios)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGPGRP => {
|
||||
let Some(foreground) = self.foreground() else {
|
||||
return_errno_with_message!(Errno::ESRCH, "No fg process group")
|
||||
};
|
||||
let fg_pgid = foreground.pgid();
|
||||
debug!("fg_pgid = {}", fg_pgid);
|
||||
write_val_to_user(arg, &fg_pgid)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSPGRP => {
|
||||
// Set the process group id of fg progress group
|
||||
let pgid = {
|
||||
let pgid: i32 = read_val_from_user(arg)?;
|
||||
if pgid < 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "negative pgid");
|
||||
}
|
||||
pgid as u32
|
||||
};
|
||||
|
||||
self.set_foreground(&pgid)?;
|
||||
// Some background processes may be waiting on the wait queue,
|
||||
// when set_fg, the background processes may be able to read.
|
||||
self.ldisc.update_readable_state();
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TCSETS => {
|
||||
// Set terminal attributes
|
||||
let termios = read_val_from_user(arg)?;
|
||||
debug!("set termios = {:?}", termios);
|
||||
self.ldisc.set_termios(termios);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TCSETSW => {
|
||||
let termios = read_val_from_user(arg)?;
|
||||
debug!("set termios = {:?}", termios);
|
||||
self.ldisc.set_termios(termios);
|
||||
// TODO: drain output buffer
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TCSETSF => {
|
||||
let termios = read_val_from_user(arg)?;
|
||||
debug!("set termios = {:?}", termios);
|
||||
self.ldisc.set_termios(termios);
|
||||
self.ldisc.drain_input();
|
||||
// TODO: drain output buffer
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCGWINSZ => {
|
||||
let winsize = self.ldisc.window_size();
|
||||
write_val_to_user(arg, &winsize)?;
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSWINSZ => {
|
||||
let winsize = read_val_from_user(arg)?;
|
||||
self.ldisc.set_window_size(winsize);
|
||||
Ok(0)
|
||||
}
|
||||
IoctlCmd::TIOCSCTTY => {
|
||||
self.set_current_session()?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Terminal for Tty {
|
||||
fn arc_self(&self) -> Arc<dyn Terminal> {
|
||||
self.weak_self.upgrade().unwrap() as _
|
||||
}
|
||||
|
||||
fn job_control(&self) -> &JobControl {
|
||||
&self.job_control
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Tty {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as /dev/console in linux.
|
||||
DeviceId::new(88, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_job_control_and_ldisc() -> (Arc<JobControl>, Arc<LineDiscipline>) {
|
||||
let job_control = Arc::new(JobControl::new());
|
||||
|
||||
let send_signal = {
|
||||
let cloned_job_control = job_control.clone();
|
||||
move |signal: KernelSignal| {
|
||||
let Some(foreground) = cloned_job_control.foreground() else {
|
||||
return;
|
||||
};
|
||||
|
||||
foreground.broadcast_signal(signal);
|
||||
}
|
||||
};
|
||||
|
||||
let ldisc = LineDiscipline::new(Arc::new(send_signal));
|
||||
|
||||
(job_control, ldisc)
|
||||
}
|
||||
|
||||
pub fn get_n_tty() -> &'static Arc<Tty> {
|
||||
N_TTY.get().unwrap()
|
||||
}
|
||||
|
||||
/// Open `N_TTY` as the controlling terminal for the process. This method should
|
||||
/// only be called when creating the init process.
|
||||
pub fn open_ntty_as_controlling_terminal(process: &Process) -> Result<()> {
|
||||
let tty = get_n_tty();
|
||||
|
||||
let session = &process.session().unwrap();
|
||||
let process_group = process.process_group().unwrap();
|
||||
|
||||
session.set_terminal(|| {
|
||||
tty.job_control.set_session(session);
|
||||
Ok(tty.clone())
|
||||
})?;
|
||||
|
||||
tty.job_control.set_foreground(Some(&process_group))?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,302 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
// This definition is from occlum
|
||||
const KERNEL_NCCS: usize = 19;
|
||||
|
||||
type TcflagT = u32;
|
||||
type CcT = u8;
|
||||
type SpeedT = u32;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Pod)]
|
||||
#[repr(C)]
|
||||
pub struct C_IFLAGS: u32 {
|
||||
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits-common.h
|
||||
const IGNBRK = 0x001; /* Ignore break condition */
|
||||
const BRKINT = 0x002; /* Signal interrupt on break */
|
||||
const IGNPAR = 0x004; /* Ignore characters with parity errors */
|
||||
const PARMRK = 0x008; /* Mark parity and framing errors */
|
||||
const INPCK = 0x010; /* Enable input parity check */
|
||||
const ISTRIP = 0x020; /* Strip 8th bit off characters */
|
||||
const INLCR = 0x040; /* Map NL to CR on input */
|
||||
const IGNCR = 0x080; /* Ignore CR */
|
||||
const ICRNL = 0x100; /* Map CR to NL on input */
|
||||
const IXANY = 0x800; /* Any character will restart after stop */
|
||||
// https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/asm-generic/termbits.h
|
||||
const IUCLC = 0x0200;
|
||||
const IXON = 0x0400;
|
||||
const IXOFF = 0x1000;
|
||||
const IMAXBEL = 0x2000;
|
||||
const IUTF8 = 0x4000;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for C_IFLAGS {
|
||||
fn default() -> Self {
|
||||
C_IFLAGS::ICRNL | C_IFLAGS::IXON
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Pod)]
|
||||
pub struct C_OFLAGS: u32 {
|
||||
const OPOST = 1 << 0; /* Perform output processing */
|
||||
const OLCUC = 1 << 1;
|
||||
const ONLCR = 1 << 2;
|
||||
const OCRNL = 1 << 3;
|
||||
const ONOCR = 1 << 4;
|
||||
const ONLRET = 1 << 5;
|
||||
const OFILL = 1 << 6;
|
||||
const OFDEL = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for C_OFLAGS {
|
||||
fn default() -> Self {
|
||||
C_OFLAGS::OPOST | C_OFLAGS::ONLCR
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
pub struct C_CFLAGS(u32);
|
||||
|
||||
impl Default for C_CFLAGS {
|
||||
fn default() -> Self {
|
||||
let cbaud = C_CFLAGS_BAUD::B38400 as u32;
|
||||
let csize = C_CFLAGS_CSIZE::CS8 as u32;
|
||||
let c_cflags = cbaud | csize | CREAD;
|
||||
Self(c_cflags)
|
||||
}
|
||||
}
|
||||
|
||||
impl C_CFLAGS {
|
||||
pub fn cbaud(&self) -> Result<C_CFLAGS_BAUD> {
|
||||
let cbaud = self.0 & CBAUD_MASK;
|
||||
Ok(C_CFLAGS_BAUD::try_from(cbaud)?)
|
||||
}
|
||||
|
||||
pub fn csize(&self) -> Result<C_CFLAGS_CSIZE> {
|
||||
let csize = self.0 & CSIZE_MASK;
|
||||
Ok(C_CFLAGS_CSIZE::try_from(csize)?)
|
||||
}
|
||||
|
||||
pub fn cread(&self) -> bool {
|
||||
self.0 & CREAD != 0
|
||||
}
|
||||
}
|
||||
|
||||
const CREAD: u32 = 0x00000080;
|
||||
const CBAUD_MASK: u32 = 0x0000100f;
|
||||
const CSIZE_MASK: u32 = 0x00000030;
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Clone, Copy, TryFromInt)]
|
||||
pub enum C_CFLAGS_CSIZE {
|
||||
CS5 = 0x00000000,
|
||||
CS6 = 0x00000010,
|
||||
CS7 = 0x00000020,
|
||||
CS8 = 0x00000030,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, TryFromInt)]
|
||||
pub enum C_CFLAGS_BAUD {
|
||||
B0 = 0x00000000, /* hang up */
|
||||
B50 = 0x00000001,
|
||||
B75 = 0x00000002,
|
||||
B110 = 0x00000003,
|
||||
B134 = 0x00000004,
|
||||
B150 = 0x00000005,
|
||||
B200 = 0x00000006,
|
||||
B300 = 0x00000007,
|
||||
B600 = 0x00000008,
|
||||
B1200 = 0x00000009,
|
||||
B1800 = 0x0000000a,
|
||||
B2400 = 0x0000000b,
|
||||
B4800 = 0x0000000c,
|
||||
B9600 = 0x0000000d,
|
||||
B19200 = 0x0000000e,
|
||||
B38400 = 0x0000000f,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[repr(C)]
|
||||
#[derive(Pod)]
|
||||
pub struct C_LFLAGS: u32 {
|
||||
const ISIG = 0x00001;
|
||||
const ICANON = 0x00002;
|
||||
const XCASE = 0x00004;
|
||||
const ECHO = 0x00008;
|
||||
const ECHOE = 0x00010;
|
||||
const ECHOK = 0x00020;
|
||||
const ECHONL = 0x00040;
|
||||
const NOFLSH = 0x00080;
|
||||
const TOSTOP = 0x00100;
|
||||
const ECHOCTL = 0x00200;
|
||||
const ECHOPRT = 0x00400;
|
||||
const ECHOKE = 0x00800;
|
||||
const FLUSHO = 0x01000;
|
||||
const PENDIN = 0x04000;
|
||||
const IEXTEN = 0x08000;
|
||||
const EXTPROC = 0x10000;
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for C_LFLAGS {
|
||||
fn default() -> Self {
|
||||
C_LFLAGS::ICANON
|
||||
| C_LFLAGS::ECHO
|
||||
| C_LFLAGS::ISIG
|
||||
| C_LFLAGS::ECHOE
|
||||
| C_LFLAGS::ECHOK
|
||||
| C_LFLAGS::ECHOCTL
|
||||
| C_LFLAGS::ECHOKE
|
||||
| C_LFLAGS::IEXTEN
|
||||
}
|
||||
}
|
||||
|
||||
/* c_cc characters index*/
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, TryFromInt)]
|
||||
pub enum CC_C_CHAR {
|
||||
VINTR = 0,
|
||||
VQUIT = 1,
|
||||
VERASE = 2,
|
||||
VKILL = 3,
|
||||
VEOF = 4,
|
||||
VTIME = 5,
|
||||
VMIN = 6,
|
||||
VSWTC = 7,
|
||||
VSTART = 8,
|
||||
VSTOP = 9,
|
||||
VSUSP = 10,
|
||||
VEOL = 11,
|
||||
VREPRINT = 12,
|
||||
VDISCARD = 13,
|
||||
VWERASE = 14,
|
||||
VLNEXT = 15,
|
||||
VEOL2 = 16,
|
||||
}
|
||||
|
||||
impl CC_C_CHAR {
|
||||
// The special char is from gvisor
|
||||
pub fn default_char(&self) -> u8 {
|
||||
match self {
|
||||
CC_C_CHAR::VINTR => control_character('C'),
|
||||
CC_C_CHAR::VQUIT => control_character('\\'),
|
||||
CC_C_CHAR::VERASE => b'\x7f',
|
||||
CC_C_CHAR::VKILL => control_character('U'),
|
||||
CC_C_CHAR::VEOF => control_character('D'),
|
||||
CC_C_CHAR::VTIME => b'\0',
|
||||
CC_C_CHAR::VMIN => 1,
|
||||
CC_C_CHAR::VSWTC => b'\0',
|
||||
CC_C_CHAR::VSTART => control_character('Q'),
|
||||
CC_C_CHAR::VSTOP => control_character('S'),
|
||||
CC_C_CHAR::VSUSP => control_character('Z'),
|
||||
CC_C_CHAR::VEOL => b'\0',
|
||||
CC_C_CHAR::VREPRINT => control_character('R'),
|
||||
CC_C_CHAR::VDISCARD => control_character('O'),
|
||||
CC_C_CHAR::VWERASE => control_character('W'),
|
||||
CC_C_CHAR::VLNEXT => control_character('V'),
|
||||
CC_C_CHAR::VEOL2 => b'\0',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct KernelTermios {
|
||||
c_iflags: C_IFLAGS,
|
||||
c_oflags: C_OFLAGS,
|
||||
c_cflags: C_CFLAGS,
|
||||
c_lflags: C_LFLAGS,
|
||||
c_line: CcT,
|
||||
c_cc: [CcT; KERNEL_NCCS],
|
||||
}
|
||||
|
||||
impl Default for KernelTermios {
|
||||
fn default() -> Self {
|
||||
let mut termios = Self {
|
||||
c_iflags: C_IFLAGS::default(),
|
||||
c_oflags: C_OFLAGS::default(),
|
||||
c_cflags: C_CFLAGS::default(),
|
||||
c_lflags: C_LFLAGS::default(),
|
||||
c_line: 0,
|
||||
c_cc: [CcT::default(); KERNEL_NCCS],
|
||||
};
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VINTR) = CC_C_CHAR::VINTR.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VQUIT) = CC_C_CHAR::VQUIT.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VERASE) = CC_C_CHAR::VERASE.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VKILL) = CC_C_CHAR::VKILL.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VEOF) = CC_C_CHAR::VEOF.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VTIME) = CC_C_CHAR::VTIME.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VMIN) = CC_C_CHAR::VMIN.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VSWTC) = CC_C_CHAR::VSWTC.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VSTART) = CC_C_CHAR::VSTART.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VSTOP) = CC_C_CHAR::VSTOP.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VSUSP) = CC_C_CHAR::VSUSP.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VEOL) = CC_C_CHAR::VEOL.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VREPRINT) = CC_C_CHAR::VREPRINT.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VDISCARD) = CC_C_CHAR::VDISCARD.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VWERASE) = CC_C_CHAR::VWERASE.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VLNEXT) = CC_C_CHAR::VLNEXT.default_char();
|
||||
*termios.get_special_char_mut(CC_C_CHAR::VEOL2) = CC_C_CHAR::VEOL2.default_char();
|
||||
termios
|
||||
}
|
||||
}
|
||||
|
||||
impl KernelTermios {
|
||||
pub fn get_special_char(&self, cc_c_char: CC_C_CHAR) -> &CcT {
|
||||
&self.c_cc[cc_c_char as usize]
|
||||
}
|
||||
|
||||
pub fn get_special_char_mut(&mut self, cc_c_char: CC_C_CHAR) -> &mut CcT {
|
||||
&mut self.c_cc[cc_c_char as usize]
|
||||
}
|
||||
|
||||
/// Canonical mode means we will handle input by lines, not by single character
|
||||
pub fn is_canonical_mode(&self) -> bool {
|
||||
self.c_lflags.contains(C_LFLAGS::ICANON)
|
||||
}
|
||||
|
||||
/// ICRNL means we should map \r to \n
|
||||
pub fn contains_icrnl(&self) -> bool {
|
||||
self.c_iflags.contains(C_IFLAGS::ICRNL)
|
||||
}
|
||||
|
||||
pub fn contains_isig(&self) -> bool {
|
||||
self.c_lflags.contains(C_LFLAGS::ISIG)
|
||||
}
|
||||
|
||||
pub fn contain_echo(&self) -> bool {
|
||||
self.c_lflags.contains(C_LFLAGS::ECHO)
|
||||
}
|
||||
|
||||
pub fn contains_echo_ctl(&self) -> bool {
|
||||
self.c_lflags.contains(C_LFLAGS::ECHOCTL)
|
||||
}
|
||||
|
||||
pub fn contains_iexten(&self) -> bool {
|
||||
self.c_lflags.contains(C_LFLAGS::IEXTEN)
|
||||
}
|
||||
}
|
||||
|
||||
const fn control_character(c: char) -> u8 {
|
||||
debug_assert!(c as u8 >= b'A');
|
||||
c as u8 - b'A' + 1u8
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, Pod)]
|
||||
#[repr(C)]
|
||||
pub struct WinSize {
|
||||
ws_row: u16,
|
||||
ws_col: u16,
|
||||
ws_xpixel: u16,
|
||||
ws_ypixel: u16,
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
pub struct Urandom;
|
||||
|
||||
impl Urandom {
|
||||
pub fn getrandom(buf: &mut [u8]) -> Result<usize> {
|
||||
getrandom::getrandom(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Urandom {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(1, 9)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Urandom {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
Self::getrandom(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::*;
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
pub struct Zero;
|
||||
|
||||
impl Device for Zero {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
// Same value with Linux
|
||||
DeviceId::new(1, 5)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Zero {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
for byte in buf.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use log::info;
|
||||
|
||||
pub fn init() {
|
||||
// print all the input device to make sure input crate will compile
|
||||
for (name, _) in aster_input::all_devices() {
|
||||
info!("Found Input device, name:{}", name);
|
||||
}
|
||||
}
|
@ -1,316 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// Error number.
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum Errno {
|
||||
EPERM = 1, /* Operation not permitted */
|
||||
ENOENT = 2, /* No such file or directory */
|
||||
ESRCH = 3, /* No such process */
|
||||
EINTR = 4, /* Interrupted system call */
|
||||
EIO = 5, /* I/O error */
|
||||
ENXIO = 6, /* No such device or address */
|
||||
E2BIG = 7, /* Argument list too long */
|
||||
ENOEXEC = 8, /* Exec format error */
|
||||
EBADF = 9, /* Bad file number */
|
||||
ECHILD = 10, /* No child processes */
|
||||
EAGAIN = 11, /* Try again */
|
||||
ENOMEM = 12, /* Out of memory */
|
||||
EACCES = 13, /* Permission denied */
|
||||
EFAULT = 14, /* Bad address */
|
||||
ENOTBLK = 15, /* Block device required */
|
||||
EBUSY = 16, /* Device or resource busy */
|
||||
EEXIST = 17, /* File exists */
|
||||
EXDEV = 18, /* Cross-device link */
|
||||
ENODEV = 19, /* No such device */
|
||||
ENOTDIR = 20, /* Not a directory */
|
||||
EISDIR = 21, /* Is a directory */
|
||||
EINVAL = 22, /* Invalid argument */
|
||||
ENFILE = 23, /* File table overflow */
|
||||
EMFILE = 24, /* Too many open files */
|
||||
ENOTTY = 25, /* Not a typewriter */
|
||||
ETXTBSY = 26, /* Text file busy */
|
||||
EFBIG = 27, /* File too large */
|
||||
ENOSPC = 28, /* No space left on device */
|
||||
ESPIPE = 29, /* Illegal seek */
|
||||
EROFS = 30, /* Read-only file system */
|
||||
EMLINK = 31, /* Too many links */
|
||||
EPIPE = 32, /* Broken pipe */
|
||||
EDOM = 33, /* Math argument out of domain of func */
|
||||
ERANGE = 34, /* Math result not representable */
|
||||
|
||||
EDEADLK = 35, /* Resource deadlock would occur */
|
||||
ENAMETOOLONG = 36, /* File name too long */
|
||||
ENOLCK = 37, /* No record locks available */
|
||||
/*
|
||||
* This error code is special: arch syscall entry code will return
|
||||
* -ENOSYS if users try to call a syscall that doesn't exist. To keep
|
||||
* failures of syscalls that really do exist distinguishable from
|
||||
* failures due to attempts to use a nonexistent syscall, syscall
|
||||
* implementations should refrain from returning -ENOSYS.
|
||||
*/
|
||||
ENOSYS = 38, /* Invalid system call number */
|
||||
ENOTEMPTY = 39, /* Directory not empty */
|
||||
ELOOP = 40, /* Too many symbolic links encountered */
|
||||
// EWOULDBLOCK EAGAIN /* Operation would block */
|
||||
ENOMSG = 42, /* No message of desired type */
|
||||
EIDRM = 43, /* Identifier removed */
|
||||
ECHRNG = 44, /* Channel number out of range */
|
||||
EL2NSYNC = 45, /* Level 2 not synchronized */
|
||||
EL3HLT = 46, /* Level 3 halted */
|
||||
EL3RST = 47, /* Level 3 reset */
|
||||
ELNRNG = 48, /* Link number out of range */
|
||||
EUNATCH = 49, /* Protocol driver not attached */
|
||||
ENOCSI = 50, /* No CSI structure available */
|
||||
EL2HLT = 51, /* Level 2 halted */
|
||||
EBADE = 52, /* Invalid exchange */
|
||||
EBADR = 53, /* Invalid request descriptor */
|
||||
EXFULL = 54, /* Exchange full */
|
||||
ENOANO = 55, /* No anode */
|
||||
EBADRQC = 56, /* Invalid request code */
|
||||
EBADSLT = 57, /* Invalid slot */
|
||||
// EDEADLOCK EDEADLK
|
||||
EBFONT = 59, /* Bad font file format */
|
||||
ENOSTR = 60, /* Device not a stream */
|
||||
ENODATA = 61, /* No data available */
|
||||
ETIME = 62, /* Timer expired */
|
||||
ENOSR = 63, /* Out of streams resources */
|
||||
ENONET = 64, /* Machine is not on the network */
|
||||
ENOPKG = 65, /* Package not installed */
|
||||
EREMOTE = 66, /* Object is remote */
|
||||
ENOLINK = 67, /* Link has been severed */
|
||||
EADV = 68, /* Advertise error */
|
||||
ESRMNT = 69, /* Srmount error */
|
||||
ECOMM = 70, /* Communication error on send */
|
||||
EPROTO = 71, /* Protocol error */
|
||||
EMULTIHOP = 72, /* Multihop attempted */
|
||||
EDOTDOT = 73, /* RFS specific error */
|
||||
EBADMSG = 74, /* Not a data message */
|
||||
EOVERFLOW = 75, /* Value too large for defined data type */
|
||||
ENOTUNIQ = 76, /* Name not unique on network */
|
||||
EBADFD = 77, /* File descriptor in bad state */
|
||||
EREMCHG = 78, /* Remote address changed */
|
||||
ELIBACC = 79, /* Can not access a needed shared library */
|
||||
ELIBBAD = 80, /* Accessing a corrupted shared library */
|
||||
ELIBSCN = 81, /* .lib section in a.out corrupted */
|
||||
ELIBMAX = 82, /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC = 83, /* Cannot exec a shared library directly */
|
||||
EILSEQ = 84, /* Illegal byte sequence */
|
||||
ERESTART = 85, /* Interrupted system call should be restarted */
|
||||
ESTRPIPE = 86, /* Streams pipe error */
|
||||
EUSERS = 87, /* Too many users */
|
||||
ENOTSOCK = 88, /* Socket operation on non-socket */
|
||||
EDESTADDRREQ = 89, /* Destination address required */
|
||||
EMSGSIZE = 90, /* Message too long */
|
||||
EPROTOTYPE = 91, /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT = 92, /* Protocol not available */
|
||||
EPROTONOSUPPORT = 93, /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT = 94, /* Socket type not supported */
|
||||
EOPNOTSUPP = 95, /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT = 96, /* Protocol family not supported */
|
||||
EAFNOSUPPORT = 97, /* Address family not supported by protocol */
|
||||
EADDRINUSE = 98, /* Address already in use */
|
||||
EADDRNOTAVAIL = 99, /* Cannot assign requested address */
|
||||
ENETDOWN = 100, /* Network is down */
|
||||
ENETUNREACH = 101, /* Network is unreachable */
|
||||
ENETRESET = 102, /* Network dropped connection because of reset */
|
||||
ECONNABORTED = 103, /* Software caused connection abort */
|
||||
ECONNRESET = 104, /* Connection reset by peer */
|
||||
ENOBUFS = 105, /* No buffer space available */
|
||||
EISCONN = 106, /* Transport endpoint is already connected */
|
||||
ENOTCONN = 107, /* Transport endpoint is not connected */
|
||||
ESHUTDOWN = 108, /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS = 109, /* Too many references: cannot splice */
|
||||
ETIMEDOUT = 110, /* Connection timed out */
|
||||
ECONNREFUSED = 111, /* Connection refused */
|
||||
EHOSTDOWN = 112, /* Host is down */
|
||||
EHOSTUNREACH = 113, /* No route to host */
|
||||
EALREADY = 114, /* Operation already in progress */
|
||||
EINPROGRESS = 115, /* Operation now in progress */
|
||||
ESTALE = 116, /* Stale file handle */
|
||||
EUCLEAN = 117, /* Structure needs cleaning */
|
||||
ENOTNAM = 118, /* Not a XENIX named type file */
|
||||
ENAVAIL = 119, /* No XENIX semaphores available */
|
||||
EISNAM = 120, /* Is a named type file */
|
||||
EREMOTEIO = 121, /* Remote I/O error */
|
||||
EDQUOT = 122, /* Quota exceeded */
|
||||
ENOMEDIUM = 123, /* No medium found */
|
||||
EMEDIUMTYPE = 124, /* Wrong medium type */
|
||||
ECANCELED = 125, /* Operation Canceled */
|
||||
ENOKEY = 126, /* Required key not available */
|
||||
EKEYEXPIRED = 127, /* Key has expired */
|
||||
EKEYREVOKED = 128, /* Key has been revoked */
|
||||
EKEYREJECTED = 129, /* Key was rejected by service */
|
||||
/* for robust mutexes */
|
||||
EOWNERDEAD = 130, /* Owner died */
|
||||
ENOTRECOVERABLE = 131, /* State not recoverable */
|
||||
|
||||
ERFKILL = 132, /* Operation not possible due to RF-kill */
|
||||
|
||||
EHWPOISON = 133, /* Memory page has hardware error */
|
||||
}
|
||||
|
||||
/// error used in this crate
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Error {
|
||||
errno: Errno,
|
||||
msg: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub const fn new(errno: Errno) -> Self {
|
||||
Error { errno, msg: None }
|
||||
}
|
||||
|
||||
pub const fn with_message(errno: Errno, msg: &'static str) -> Self {
|
||||
Error {
|
||||
errno,
|
||||
msg: Some(msg),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn error(&self) -> Errno {
|
||||
self.errno
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Errno> for Error {
|
||||
fn from(errno: Errno) -> Self {
|
||||
Error::new(errno)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<aster_frame::Error> for Error {
|
||||
fn from(frame_error: aster_frame::Error) -> Self {
|
||||
match frame_error {
|
||||
aster_frame::Error::AccessDenied => Error::new(Errno::EFAULT),
|
||||
aster_frame::Error::NoMemory => Error::new(Errno::ENOMEM),
|
||||
aster_frame::Error::InvalidArgs => Error::new(Errno::EINVAL),
|
||||
aster_frame::Error::IoError => Error::new(Errno::EIO),
|
||||
aster_frame::Error::NotEnoughResources => Error::new(Errno::EBUSY),
|
||||
aster_frame::Error::PageFault => Error::new(Errno::EFAULT),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<aster_block::bio::BioEnqueueError> for Error {
|
||||
fn from(error: aster_block::bio::BioEnqueueError) -> Self {
|
||||
match error {
|
||||
aster_block::bio::BioEnqueueError::IsFull => {
|
||||
Error::with_message(Errno::EBUSY, "The request queue is full")
|
||||
}
|
||||
aster_block::bio::BioEnqueueError::Refused => {
|
||||
Error::with_message(Errno::EBUSY, "Refuse to enqueue the bio")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<aster_block::bio::BioStatus> for Error {
|
||||
fn from(err_status: aster_block::bio::BioStatus) -> Self {
|
||||
match err_status {
|
||||
aster_block::bio::BioStatus::NotSupported => {
|
||||
Error::with_message(Errno::EIO, "I/O operation is not supported")
|
||||
}
|
||||
aster_block::bio::BioStatus::NoSpace => {
|
||||
Error::with_message(Errno::ENOSPC, "Insufficient space on device")
|
||||
}
|
||||
aster_block::bio::BioStatus::IoError => {
|
||||
Error::with_message(Errno::EIO, "I/O operation fails")
|
||||
}
|
||||
status => panic!("Can not convert the status: {:?} to an error", status),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::str::Utf8Error> for Error {
|
||||
fn from(_: core::str::Utf8Error) -> Self {
|
||||
Error::with_message(Errno::EINVAL, "Invalid utf-8 string")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<alloc::string::FromUtf8Error> for Error {
|
||||
fn from(_: alloc::string::FromUtf8Error) -> Self {
|
||||
Error::with_message(Errno::EINVAL, "Invalid utf-8 string")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::ffi::FromBytesUntilNulError> for Error {
|
||||
fn from(_: core::ffi::FromBytesUntilNulError) -> Self {
|
||||
Error::with_message(Errno::E2BIG, "Cannot find null in cstring")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<core::ffi::FromBytesWithNulError> for Error {
|
||||
fn from(_: core::ffi::FromBytesWithNulError) -> Self {
|
||||
Error::with_message(Errno::E2BIG, "Cannot find null in cstring")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<cpio_decoder::error::Error> for Error {
|
||||
fn from(cpio_error: cpio_decoder::error::Error) -> Self {
|
||||
match cpio_error {
|
||||
cpio_decoder::error::Error::MagicError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid magic number")
|
||||
}
|
||||
cpio_decoder::error::Error::Utf8Error => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid utf-8 string")
|
||||
}
|
||||
cpio_decoder::error::Error::ParseIntError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO parse int error")
|
||||
}
|
||||
cpio_decoder::error::Error::FileTypeError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid file type")
|
||||
}
|
||||
cpio_decoder::error::Error::FileNameError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO invalid file name")
|
||||
}
|
||||
cpio_decoder::error::Error::BufferShortError => {
|
||||
Error::with_message(Errno::EINVAL, "CPIO buffer is too short")
|
||||
}
|
||||
cpio_decoder::error::Error::IoError => {
|
||||
Error::with_message(Errno::EIO, "CPIO buffer I/O error")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for aster_frame::Error {
|
||||
fn from(error: Error) -> Self {
|
||||
match error.errno {
|
||||
Errno::EACCES => aster_frame::Error::AccessDenied,
|
||||
Errno::EIO => aster_frame::Error::IoError,
|
||||
Errno::ENOMEM => aster_frame::Error::NoMemory,
|
||||
Errno::EFAULT => aster_frame::Error::PageFault,
|
||||
Errno::EINVAL => aster_frame::Error::InvalidArgs,
|
||||
Errno::EBUSY => aster_frame::Error::NotEnoughResources,
|
||||
_ => aster_frame::Error::InvalidArgs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<alloc::ffi::NulError> for Error {
|
||||
fn from(_: alloc::ffi::NulError) -> Self {
|
||||
Error::with_message(Errno::E2BIG, "Cannot find null in cstring")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<int_to_c_enum::TryFromIntError> for Error {
|
||||
fn from(_: int_to_c_enum::TryFromIntError) -> Self {
|
||||
Error::with_message(Errno::EINVAL, "Invalid enum value")
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_errno {
|
||||
($errno: expr) => {
|
||||
return Err($crate::error::Error::new($errno))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_errno_with_message {
|
||||
($errno: expr, $message: expr) => {
|
||||
return Err($crate::error::Error::with_message($errno, $message))
|
||||
};
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// A trait to represent any events.
|
||||
///
|
||||
/// # The unit event
|
||||
///
|
||||
/// The unit type `()` can serve as a unit event.
|
||||
/// It can be used if there is only one kind of event
|
||||
/// and the event carries no additional information.
|
||||
pub trait Events: Copy + Clone + Send + Sync + 'static {}
|
||||
|
||||
impl Events for () {}
|
||||
|
||||
/// A trait to filter events.
|
||||
///
|
||||
/// # The no-op event filter
|
||||
///
|
||||
/// The unit type `()` can serve as a no-op event filter.
|
||||
/// It implements `EventsFilter<E>` for any events type `E`,
|
||||
/// with a `filter` method that always returns `true`.
|
||||
/// If the `F` type of `Subject<E, F>` is not specified explicitly,
|
||||
/// then the unit type `()` is chosen as the event filter.
|
||||
///
|
||||
/// # Per-object event filter
|
||||
///
|
||||
/// Any `Option<F: EventsFilter>` is also an event filter thanks to
|
||||
/// the blanket implementations the `EventsFilter` trait.
|
||||
/// By using `Option<F: EventsFilter>`, we can decide, on a per-observer basis,
|
||||
/// if an observer needs an event filter.
|
||||
pub trait EventsFilter<E: Events>: Send + Sync + 'static {
|
||||
fn filter(&self, event: &E) -> bool;
|
||||
}
|
||||
|
||||
impl<E: Events> EventsFilter<E> for () {
|
||||
fn filter(&self, _events: &E) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Events, F: EventsFilter<E>> EventsFilter<E> for Option<F> {
|
||||
fn filter(&self, events: &E) -> bool {
|
||||
self.as_ref().map_or(true, |f| f.filter(events))
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{Events, EventsFilter};
|
||||
|
||||
crate::bitflags! {
|
||||
pub struct IoEvents: u32 {
|
||||
const IN = 0x0001;
|
||||
const PRI = 0x0002;
|
||||
const OUT = 0x0004;
|
||||
const ERR = 0x0008;
|
||||
const HUP = 0x0010;
|
||||
const NVAL = 0x0020;
|
||||
const RDHUP = 0x2000;
|
||||
/// Events that are always polled even without specifying them.
|
||||
const ALWAYS_POLL = Self::ERR.bits | Self::HUP.bits;
|
||||
}
|
||||
}
|
||||
|
||||
impl Events for IoEvents {}
|
||||
|
||||
impl EventsFilter<IoEvents> for IoEvents {
|
||||
fn filter(&self, events: &IoEvents) -> bool {
|
||||
self.intersects(*events)
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#[allow(clippy::module_inception)]
|
||||
mod events;
|
||||
mod io_events;
|
||||
mod observer;
|
||||
mod subject;
|
||||
|
||||
pub use self::events::{Events, EventsFilter};
|
||||
pub use self::observer::Observer;
|
||||
pub use self::subject::Subject;
|
||||
pub use io_events::IoEvents;
|
@ -1,37 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::Events;
|
||||
|
||||
/// An observer for events.
|
||||
///
|
||||
/// In a sense, event observers are just a fancy form of callback functions.
|
||||
/// An observer's `on_events` methods are supposed to be called when
|
||||
/// some events that are interesting to the observer happen.
|
||||
///
|
||||
/// # The no-op observer
|
||||
///
|
||||
/// The unit type `()` can serve as a no-op observer.
|
||||
/// It implements `Observer<E>` for any events type `E`,
|
||||
/// with an `on_events` method that simply does nothing.
|
||||
///
|
||||
/// It can be used to create an empty `Weak`, as shown in the example below.
|
||||
/// Using the unit type is necessary, as creating an empty `Weak` needs to
|
||||
/// have a sized type (e.g. the unit type).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use alloc::sync::Weak;
|
||||
/// use crate::events::Observer;
|
||||
///
|
||||
/// let empty: Weak<dyn Observer<()>> = Weak::<()>::new();
|
||||
/// assert!(empty.upgrade().is_empty());
|
||||
/// ```
|
||||
pub trait Observer<E: Events>: Send + Sync {
|
||||
/// Notify the observer that some interesting events happen.
|
||||
fn on_events(&self, events: &E);
|
||||
}
|
||||
|
||||
impl<E: Events> Observer<E> for () {
|
||||
fn on_events(&self, events: &E) {}
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use keyable_arc::KeyableWeak;
|
||||
|
||||
use super::{Events, EventsFilter, Observer};
|
||||
|
||||
/// A Subject notifies interesting events to registered observers.
|
||||
pub struct Subject<E: Events, F: EventsFilter<E> = ()> {
|
||||
// A table that maintains all interesting observers.
|
||||
observers: Mutex<BTreeMap<KeyableWeak<dyn Observer<E>>, F>>,
|
||||
// To reduce lock contentions, we maintain a counter for the size of the table
|
||||
num_observers: AtomicUsize,
|
||||
}
|
||||
|
||||
impl<E: Events, F: EventsFilter<E>> Subject<E, F> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
observers: Mutex::new(BTreeMap::new()),
|
||||
num_observers: AtomicUsize::new(0),
|
||||
}
|
||||
}
|
||||
/// Register an observer.
|
||||
///
|
||||
/// A registered observer will get notified through its `on_events` method.
|
||||
/// If events `filter` is provided, only filtered events will notify the observer.
|
||||
///
|
||||
/// If the given observer has already been registered, then its registered events
|
||||
/// filter will be updated.
|
||||
pub fn register_observer(&self, observer: Weak<dyn Observer<E>>, filter: F) {
|
||||
let mut observers = self.observers.lock();
|
||||
let is_new = {
|
||||
let observer: KeyableWeak<dyn Observer<E>> = observer.into();
|
||||
observers.insert(observer, filter).is_none()
|
||||
};
|
||||
if is_new {
|
||||
self.num_observers.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
/// Unregister an observer.
|
||||
///
|
||||
/// If such an observer is found, then the registered observer will be
|
||||
/// removed from the subject and returned as the return value. Otherwise,
|
||||
/// a `None` will be returned.
|
||||
pub fn unregister_observer(
|
||||
&self,
|
||||
observer: &Weak<dyn Observer<E>>,
|
||||
) -> Option<Weak<dyn Observer<E>>> {
|
||||
let observer: KeyableWeak<dyn Observer<E>> = observer.clone().into();
|
||||
let mut observers = self.observers.lock();
|
||||
let observer = observers
|
||||
.remove_entry(&observer)
|
||||
.map(|(observer, _)| observer.into());
|
||||
if observer.is_some() {
|
||||
self.num_observers.fetch_sub(1, Ordering::Relaxed);
|
||||
}
|
||||
observer
|
||||
}
|
||||
|
||||
/// Notify events to all registered observers.
|
||||
///
|
||||
/// It will remove the observers which have been freed.
|
||||
pub fn notify_observers(&self, events: &E) {
|
||||
// Fast path.
|
||||
if self.num_observers.load(Ordering::Relaxed) == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
// Slow path: broadcast the new events to all observers.
|
||||
let mut observers = self.observers.lock();
|
||||
observers.retain(|observer, filter| {
|
||||
if let Some(observer) = observer.upgrade() {
|
||||
if !filter.filter(events) {
|
||||
return true;
|
||||
}
|
||||
observer.on_events(events);
|
||||
true
|
||||
} else {
|
||||
self.num_observers.fetch_sub(1, Ordering::Relaxed);
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::fs::fs_resolver::{FsPath, FsResolver};
|
||||
use crate::fs::utils::Dentry;
|
||||
use crate::fs::utils::{InodeMode, InodeType};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::inode_handle::FileIo;
|
||||
|
||||
/// The abstract of device
|
||||
pub trait Device: Sync + Send + FileIo {
|
||||
/// Return the device type.
|
||||
fn type_(&self) -> DeviceType;
|
||||
|
||||
/// Return the device ID.
|
||||
fn id(&self) -> DeviceId;
|
||||
|
||||
/// Open a device.
|
||||
fn open(&self) -> Result<Option<Arc<dyn FileIo>>> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for dyn Device {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("Device")
|
||||
.field("type", &self.type_())
|
||||
.field("id", &self.id())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Device type
|
||||
pub enum DeviceType {
|
||||
CharDevice,
|
||||
BlockDevice,
|
||||
MiscDevice,
|
||||
}
|
||||
|
||||
/// Device Id
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct DeviceId(u64);
|
||||
|
||||
impl DeviceId {
|
||||
pub fn new(major: u32, minor: u32) -> Self {
|
||||
let major = major as u64;
|
||||
let minor = minor as u64;
|
||||
Self(
|
||||
(major & 0xffff_f000) << 32
|
||||
| (major & 0x0000_0fff) << 8
|
||||
| (minor & 0xffff_ff00) << 12
|
||||
| (minor & 0x0000_00ff),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn major(&self) -> u32 {
|
||||
((self.0 >> 32) & 0xffff_f000 | (self.0 >> 8) & 0x0000_0fff) as u32
|
||||
}
|
||||
|
||||
pub fn minor(&self) -> u32 {
|
||||
((self.0 >> 12) & 0xffff_ff00 | self.0 & 0x0000_00ff) as u32
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DeviceId {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("DeviceId")
|
||||
.field("major", &self.major())
|
||||
.field("minor", &self.minor())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceId> for u64 {
|
||||
fn from(value: DeviceId) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a device node to FS for the device.
|
||||
///
|
||||
/// If the parent path is not existing, `mkdir -p` the parent path.
|
||||
/// This function is used in registering device.
|
||||
pub fn add_node(device: Arc<dyn Device>, path: &str) -> Result<Arc<Dentry>> {
|
||||
let mut dentry = {
|
||||
let fs_resolver = FsResolver::new();
|
||||
fs_resolver.lookup(&FsPath::try_from("/dev").unwrap())?
|
||||
};
|
||||
let mut relative_path = {
|
||||
let relative_path = path.trim_start_matches('/');
|
||||
if relative_path.is_empty() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid device path");
|
||||
}
|
||||
relative_path
|
||||
};
|
||||
|
||||
while !relative_path.is_empty() {
|
||||
let (next_name, path_remain) = if let Some((prefix, suffix)) = relative_path.split_once('/')
|
||||
{
|
||||
(prefix, suffix.trim_start_matches('/'))
|
||||
} else {
|
||||
(relative_path, "")
|
||||
};
|
||||
|
||||
match dentry.lookup(next_name) {
|
||||
Ok(next_dentry) => {
|
||||
if path_remain.is_empty() {
|
||||
return_errno_with_message!(Errno::EEXIST, "device node is existing");
|
||||
}
|
||||
dentry = next_dentry;
|
||||
}
|
||||
Err(_) => {
|
||||
if path_remain.is_empty() {
|
||||
// Create the device node
|
||||
dentry = dentry.mknod(
|
||||
next_name,
|
||||
InodeMode::from_bits_truncate(0o666),
|
||||
device.clone(),
|
||||
)?;
|
||||
} else {
|
||||
// Mkdir parent path
|
||||
dentry = dentry.create(
|
||||
next_name,
|
||||
InodeType::Dir,
|
||||
InodeMode::from_bits_truncate(0o755),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
relative_path = path_remain;
|
||||
}
|
||||
|
||||
Ok(dentry)
|
||||
}
|
||||
|
||||
/// Delete the device node from FS for the device.
|
||||
///
|
||||
/// This function is used in unregistering device.
|
||||
pub fn delete_node(path: &str) -> Result<()> {
|
||||
let abs_path = {
|
||||
let device_path = path.trim_start_matches('/');
|
||||
if device_path.is_empty() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid device path");
|
||||
}
|
||||
String::from("/dev") + "/" + device_path
|
||||
};
|
||||
|
||||
let (parent_dentry, name) = {
|
||||
let fs_resolver = FsResolver::new();
|
||||
fs_resolver.lookup_dir_and_base_name(&FsPath::try_from(abs_path.as_str()).unwrap())?
|
||||
};
|
||||
|
||||
parent_dentry.unlink(&name)?;
|
||||
Ok(())
|
||||
}
|
@ -1,268 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::device::PtyMaster;
|
||||
use crate::fs::device::{Device, DeviceId, DeviceType};
|
||||
use crate::fs::utils::{
|
||||
DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
|
||||
SuperBlock, NAME_MAX,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
|
||||
use aster_util::{id_allocator::IdAlloc, slot_vec::SlotVec};
|
||||
use core::time::Duration;
|
||||
|
||||
use self::ptmx::Ptmx;
|
||||
use self::slave::PtySlaveInode;
|
||||
|
||||
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);
|
||||
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(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Create the master and slave pair.
|
||||
fn create_master_slave_pair(&self) -> Result<(Arc<PtyMaster>, Arc<PtySlaveInode>)> {
|
||||
let index = self
|
||||
.index_alloc
|
||||
.lock()
|
||||
.alloc()
|
||||
.ok_or_else(|| Error::with_message(Errno::EIO, "cannot alloc index"))?;
|
||||
|
||||
let (master, slave) = crate::device::new_pty_pair(index as u32, self.root.ptmx.clone())?;
|
||||
|
||||
let slave_inode = PtySlaveInode::new(slave, self.this.clone());
|
||||
self.root.add_slave(index.to_string(), slave_inode.clone());
|
||||
|
||||
Ok((master, slave_inode))
|
||||
}
|
||||
|
||||
/// Remove the slave from fs.
|
||||
///
|
||||
/// This is called when the master is being dropped.
|
||||
pub 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::empty()
|
||||
}
|
||||
}
|
||||
|
||||
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 size(&self) -> usize {
|
||||
self.metadata.size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.metadata.ino as _
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
self.metadata.type_
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
self.metadata.mode
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {}
|
||||
|
||||
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 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;
|
||||
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.clone(),
|
||||
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()
|
||||
}
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::device::PtyMaster;
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Inner(Weak<DevPts>);
|
||||
|
||||
impl Ptmx {
|
||||
pub fn new(sb: &SuperBlock, fs: Weak<DevPts>) -> Arc<Self> {
|
||||
let inner = Inner(fs);
|
||||
Arc::new(Self {
|
||||
metadata: Metadata::new_device(
|
||||
PTMX_INO,
|
||||
InodeMode::from_bits_truncate(0o666),
|
||||
sb,
|
||||
&inner,
|
||||
),
|
||||
inner,
|
||||
})
|
||||
}
|
||||
|
||||
/// The open method for ptmx.
|
||||
///
|
||||
/// Creates a master and slave pair and returns the master inode.
|
||||
pub fn open(&self) -> Result<Arc<PtyMaster>> {
|
||||
let (master, _) = self.devpts().create_master_slave_pair()?;
|
||||
Ok(master)
|
||||
}
|
||||
|
||||
pub fn devpts(&self) -> Arc<DevPts> {
|
||||
self.inner.0.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 size(&self) -> usize {
|
||||
self.metadata.size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.metadata.ino as _
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
self.metadata.type_
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
self.metadata.mode
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {}
|
||||
|
||||
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 read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn write_direct_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()
|
||||
}
|
||||
|
||||
fn as_device(&self) -> Option<Arc<dyn Device>> {
|
||||
Some(Arc::new(self.inner.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Inner {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::CharDevice
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(PTMX_MAJOR_NUM, PTMX_MINOR_NUM)
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Option<Arc<dyn FileIo>>> {
|
||||
let devpts = self.0.upgrade().unwrap();
|
||||
let (master, _) = devpts.create_master_slave_pair()?;
|
||||
Ok(Some(master as _))
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for Inner {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot read ptmx");
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot write ptmx");
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
IoEvents::empty()
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::inode_handle::FileIo;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
use super::*;
|
||||
|
||||
use crate::device::PtySlave;
|
||||
|
||||
/// 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 size(&self) -> usize {
|
||||
self.metadata.size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.metadata.ino as _
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
self.metadata.type_
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
self.metadata.mode
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {}
|
||||
|
||||
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 read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.device.read(buf)
|
||||
}
|
||||
|
||||
fn read_direct_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 write_direct_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()
|
||||
}
|
||||
|
||||
fn as_device(&self) -> Option<Arc<dyn Device>> {
|
||||
Some(self.device.clone())
|
||||
}
|
||||
}
|
@ -1,502 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::{IoEvents, Observer};
|
||||
use crate::fs::file_handle::FileLike;
|
||||
use crate::fs::file_table::{FdEvents, FileDescripter};
|
||||
use crate::fs::utils::IoctlCmd;
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::time::Duration;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// A file-like object that provides epoll API.
|
||||
///
|
||||
/// Conceptually, we maintain two lists: one consists of all interesting files,
|
||||
/// which can be managed by the epoll ctl commands; the other are for ready files,
|
||||
/// which are files that have some events. A epoll wait only needs to iterate the
|
||||
/// ready list and poll each file to see if the file is ready for the interesting
|
||||
/// I/O.
|
||||
///
|
||||
/// To maintain the ready list, we need to monitor interesting events that happen
|
||||
/// on the files. To do so, the `EpollFile` registers itself as an `Observer` to
|
||||
/// the monotored files. Thus, we can add a file to the ready list when an interesting
|
||||
/// event happens on the file.
|
||||
pub struct EpollFile {
|
||||
// All interesting entries.
|
||||
interest: Mutex<BTreeMap<FileDescripter, Arc<EpollEntry>>>,
|
||||
// Entries that are probably ready (having events happened).
|
||||
ready: Mutex<VecDeque<Arc<EpollEntry>>>,
|
||||
// EpollFile itself is also pollable
|
||||
pollee: Pollee,
|
||||
// Any EpollFile is wrapped with Arc when created.
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl EpollFile {
|
||||
/// Creates a new epoll file.
|
||||
pub fn new() -> Arc<Self> {
|
||||
Arc::new_cyclic(|me| Self {
|
||||
interest: Mutex::new(BTreeMap::new()),
|
||||
ready: Mutex::new(VecDeque::new()),
|
||||
pollee: Pollee::new(IoEvents::empty()),
|
||||
weak_self: me.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Control the interest list of the epoll file.
|
||||
pub fn control(&self, cmd: &EpollCtl) -> Result<()> {
|
||||
match *cmd {
|
||||
EpollCtl::Add(fd, ep_event, ep_flags) => self.add_interest(fd, ep_event, ep_flags),
|
||||
EpollCtl::Del(fd) => {
|
||||
self.del_interest(fd)?;
|
||||
self.unregister_from_file_table_entry(fd);
|
||||
Ok(())
|
||||
}
|
||||
EpollCtl::Mod(fd, ep_event, ep_flags) => self.mod_interest(fd, ep_event, ep_flags),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_interest(
|
||||
&self,
|
||||
fd: FileDescripter,
|
||||
ep_event: EpollEvent,
|
||||
ep_flags: EpollFlags,
|
||||
) -> Result<()> {
|
||||
self.warn_unsupported_flags(&ep_flags);
|
||||
|
||||
let current = current!();
|
||||
let file_table = current.file_table().lock();
|
||||
let file_table_entry = file_table.get_entry(fd)?;
|
||||
let file = file_table_entry.file();
|
||||
let weak_file = Arc::downgrade(file);
|
||||
let mask = ep_event.events;
|
||||
let entry = EpollEntry::new(fd, weak_file, ep_event, ep_flags, self.weak_self.clone());
|
||||
|
||||
// Add the new entry to the interest list and start monitering its events
|
||||
let mut interest = self.interest.lock();
|
||||
if interest.contains_key(&fd) {
|
||||
return_errno_with_message!(Errno::EEXIST, "the fd has been added");
|
||||
}
|
||||
file.register_observer(entry.self_weak() as _, IoEvents::all())?;
|
||||
interest.insert(fd, entry.clone());
|
||||
// Register self to the file table entry
|
||||
file_table_entry.register_observer(self.weak_self.clone() as _);
|
||||
let file = file.clone();
|
||||
drop(file_table);
|
||||
drop(interest);
|
||||
|
||||
// Add the new entry to the ready list if the file is ready
|
||||
let events = file.poll(mask, None);
|
||||
if !events.is_empty() {
|
||||
self.push_ready(entry);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn del_interest(&self, fd: FileDescripter) -> Result<()> {
|
||||
let mut interest = self.interest.lock();
|
||||
let entry = interest
|
||||
.remove(&fd)
|
||||
.ok_or_else(|| Error::with_message(Errno::ENOENT, "fd is not in the interest list"))?;
|
||||
|
||||
// If this epoll entry is in the ready list, then we should delete it.
|
||||
// But unfortunately, deleting an entry from the ready list has a
|
||||
// complexity of O(N).
|
||||
//
|
||||
// To optimize the performance, we only mark the epoll entry as
|
||||
// deleted at this moment. The real deletion happens when the ready list
|
||||
// is scanned in EpolFile::wait.
|
||||
entry.set_deleted();
|
||||
|
||||
let file = match entry.file() {
|
||||
Some(file) => file,
|
||||
// TODO: should we warn about it?
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
file.unregister_observer(&(entry.self_weak() as _)).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mod_interest(
|
||||
&self,
|
||||
fd: FileDescripter,
|
||||
new_ep_event: EpollEvent,
|
||||
new_ep_flags: EpollFlags,
|
||||
) -> Result<()> {
|
||||
self.warn_unsupported_flags(&new_ep_flags);
|
||||
|
||||
// Update the epoll entry
|
||||
let interest = self.interest.lock();
|
||||
let entry = interest
|
||||
.get(&fd)
|
||||
.ok_or_else(|| Error::with_message(Errno::ENOENT, "fd is not in the interest list"))?;
|
||||
if entry.is_deleted() {
|
||||
return_errno_with_message!(Errno::ENOENT, "fd is not in the interest list");
|
||||
}
|
||||
let new_mask = new_ep_event.events;
|
||||
entry.update(new_ep_event, new_ep_flags);
|
||||
let entry = entry.clone();
|
||||
drop(interest);
|
||||
|
||||
// Add the updated entry to the ready list if the file is ready
|
||||
let file = match entry.file() {
|
||||
Some(file) => file,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let events = file.poll(new_mask, None);
|
||||
if !events.is_empty() {
|
||||
self.push_ready(entry);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister_from_file_table_entry(&self, fd: FileDescripter) {
|
||||
let current = current!();
|
||||
let file_table = current.file_table().lock();
|
||||
if let Ok(entry) = file_table.get_entry(fd) {
|
||||
entry.unregister_observer(&(self.weak_self.clone() as _));
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for interesting events happen on the files in the interest list
|
||||
/// of the epoll file.
|
||||
///
|
||||
/// This method blocks until either some interesting events happen or
|
||||
/// the timeout expires or a signal arrives. The first case returns
|
||||
/// `Ok(events)`, where `events` is a `Vec` containing at most `max_events`
|
||||
/// number of `EpollEvent`s. The second and third case returns errors.
|
||||
///
|
||||
/// When `max_events` equals to zero, the method returns when the timeout
|
||||
/// expires or a signal arrives.
|
||||
pub fn wait(&self, max_events: usize, timeout: Option<&Duration>) -> Result<Vec<EpollEvent>> {
|
||||
let mut ep_events = Vec::new();
|
||||
let mut poller = None;
|
||||
loop {
|
||||
// Try to pop some ready entries
|
||||
if self.pop_ready(max_events, &mut ep_events) > 0 {
|
||||
return Ok(ep_events);
|
||||
}
|
||||
|
||||
// Return immediately if specifying a timeout of zero
|
||||
if timeout.is_some() && timeout.as_ref().unwrap().is_zero() {
|
||||
return Ok(ep_events);
|
||||
}
|
||||
|
||||
// If no ready entries for now, wait for them
|
||||
if poller.is_none() {
|
||||
poller = Some(Poller::new());
|
||||
let events = self.pollee.poll(IoEvents::IN, poller.as_ref());
|
||||
if !events.is_empty() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(timeout) = timeout {
|
||||
poller.as_ref().unwrap().wait_timeout(timeout)?;
|
||||
} else {
|
||||
poller.as_ref().unwrap().wait()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn push_ready(&self, entry: Arc<EpollEntry>) {
|
||||
let mut ready = self.ready.lock();
|
||||
if entry.is_deleted() {
|
||||
return;
|
||||
}
|
||||
|
||||
if !entry.is_ready() {
|
||||
entry.set_ready();
|
||||
ready.push_back(entry);
|
||||
}
|
||||
|
||||
// Even if the entry is already set to ready, there might be new events that we are interested in.
|
||||
// Wake the poller anyway.
|
||||
self.pollee.add_events(IoEvents::IN);
|
||||
}
|
||||
|
||||
fn pop_ready(&self, max_events: usize, ep_events: &mut Vec<EpollEvent>) -> usize {
|
||||
let mut count_events = 0;
|
||||
let mut ready = self.ready.lock();
|
||||
let mut pop_quota = ready.len();
|
||||
loop {
|
||||
// Pop some ready entries per round.
|
||||
//
|
||||
// Since the popped ready entries may contain "false positive" and
|
||||
// we want to return as many results as possible, this has to
|
||||
// be done in a loop.
|
||||
let pop_count = (max_events - count_events).min(pop_quota);
|
||||
if pop_count == 0 {
|
||||
break;
|
||||
}
|
||||
let ready_entries: Vec<Arc<EpollEntry>> = ready
|
||||
.drain(..pop_count)
|
||||
.filter(|entry| !entry.is_deleted())
|
||||
.collect();
|
||||
pop_quota -= pop_count;
|
||||
|
||||
// Examine these ready entries, which are candidates for the results
|
||||
// to be returned.
|
||||
for entry in ready_entries {
|
||||
let (ep_event, ep_flags) = entry.event_and_flags();
|
||||
// If this entry's file is ready, save it in the output array.
|
||||
// EPOLLHUP and EPOLLERR should always be reported.
|
||||
let ready_events = entry.poll() & (ep_event.events | IoEvents::HUP | IoEvents::ERR);
|
||||
// If there are no events, the entry should be removed from the ready list.
|
||||
if ready_events.is_empty() {
|
||||
entry.reset_ready();
|
||||
// For EPOLLONESHOT flag, this entry should also be removed from the interest list
|
||||
if ep_flags.intersects(EpollFlags::ONE_SHOT) {
|
||||
self.del_interest(entry.fd())
|
||||
.expect("this entry should be in the interest list");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Records the events from the ready list
|
||||
ep_events.push(EpollEvent::new(ready_events, ep_event.user_data));
|
||||
count_events += 1;
|
||||
|
||||
// If the epoll entry is neither edge-triggered or one-shot, then we should
|
||||
// keep the entry in the ready list.
|
||||
if !ep_flags.intersects(EpollFlags::ONE_SHOT | EpollFlags::EDGE_TRIGGER) {
|
||||
ready.push_back(entry);
|
||||
}
|
||||
// Otherwise, the entry is indeed removed the ready list and we should reset
|
||||
// its ready flag.
|
||||
else {
|
||||
entry.reset_ready();
|
||||
// For EPOLLONESHOT flag, this entry should also be removed from the interest list
|
||||
if ep_flags.intersects(EpollFlags::ONE_SHOT) {
|
||||
self.del_interest(entry.fd())
|
||||
.expect("this entry should be in the interest list");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear the epoll file's events if no ready entries
|
||||
if ready.len() == 0 {
|
||||
self.pollee.del_events(IoEvents::IN);
|
||||
}
|
||||
count_events
|
||||
}
|
||||
|
||||
fn warn_unsupported_flags(&self, flags: &EpollFlags) {
|
||||
if flags.intersects(EpollFlags::EXCLUSIVE | EpollFlags::WAKE_UP) {
|
||||
warn!("{:?} contains unsupported flags", flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<FdEvents> for EpollFile {
|
||||
fn on_events(&self, events: &FdEvents) {
|
||||
// Delete the file from the interest list if it is closed.
|
||||
if let FdEvents::Close(fd) = events {
|
||||
let _ = self.del_interest(*fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EpollFile {
|
||||
fn drop(&mut self) {
|
||||
trace!("EpollFile Drop");
|
||||
let mut interest = self.interest.lock();
|
||||
let fds: Vec<_> = interest
|
||||
.extract_if(|_, _| true)
|
||||
.map(|(fd, entry)| {
|
||||
entry.set_deleted();
|
||||
if let Some(file) = entry.file() {
|
||||
let _ = file.unregister_observer(&(entry.self_weak() as _));
|
||||
}
|
||||
fd
|
||||
})
|
||||
.collect();
|
||||
drop(interest);
|
||||
|
||||
fds.iter()
|
||||
.for_each(|&fd| self.unregister_from_file_table_entry(fd));
|
||||
}
|
||||
}
|
||||
|
||||
// Implement the common methods required by FileHandle
|
||||
impl FileLike for EpollFile {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "epoll files do not support read");
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "epoll files do not support write");
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
return_errno_with_message!(Errno::EINVAL, "epoll files do not support ioctl");
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn register_observer(
|
||||
&self,
|
||||
observer: Weak<dyn Observer<IoEvents>>,
|
||||
mask: IoEvents,
|
||||
) -> Result<()> {
|
||||
self.pollee.register_observer(observer, mask);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unregister_observer(
|
||||
&self,
|
||||
observer: &Weak<dyn Observer<IoEvents>>,
|
||||
) -> Result<Weak<dyn Observer<IoEvents>>> {
|
||||
self.pollee
|
||||
.unregister_observer(observer)
|
||||
.ok_or_else(|| Error::with_message(Errno::ENOENT, "observer is not registered"))
|
||||
}
|
||||
}
|
||||
|
||||
/// An epoll entry contained in an epoll file. Each epoll entry is added, modified,
|
||||
/// or deleted by the `EpollCtl` command.
|
||||
pub struct EpollEntry {
|
||||
fd: FileDescripter,
|
||||
file: Weak<dyn FileLike>,
|
||||
inner: Mutex<Inner>,
|
||||
// Whether the entry is in the ready list
|
||||
is_ready: AtomicBool,
|
||||
// Whether the entry has been deleted from the interest list
|
||||
is_deleted: AtomicBool,
|
||||
// Refers to the epoll file containing this epoll entry
|
||||
weak_epoll: Weak<EpollFile>,
|
||||
// An EpollEntry is always contained inside Arc
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
event: EpollEvent,
|
||||
flags: EpollFlags,
|
||||
}
|
||||
|
||||
impl EpollEntry {
|
||||
/// Creates a new epoll entry associated with the given epoll file.
|
||||
pub fn new(
|
||||
fd: FileDescripter,
|
||||
file: Weak<dyn FileLike>,
|
||||
event: EpollEvent,
|
||||
flags: EpollFlags,
|
||||
weak_epoll: Weak<EpollFile>,
|
||||
) -> Arc<Self> {
|
||||
Arc::new_cyclic(|me| Self {
|
||||
fd,
|
||||
file,
|
||||
inner: Mutex::new(Inner { event, flags }),
|
||||
is_ready: AtomicBool::new(false),
|
||||
is_deleted: AtomicBool::new(false),
|
||||
weak_epoll,
|
||||
weak_self: me.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the epoll file associated with this epoll entry.
|
||||
pub fn epoll_file(&self) -> Option<Arc<EpollFile>> {
|
||||
self.weak_epoll.upgrade()
|
||||
}
|
||||
|
||||
/// Get an instance of `Arc` that refers to this epoll entry.
|
||||
pub fn self_arc(&self) -> Arc<Self> {
|
||||
self.weak_self.upgrade().unwrap()
|
||||
}
|
||||
|
||||
/// Get an instance of `Weak` that refers to this epoll entry.
|
||||
pub fn self_weak(&self) -> Weak<Self> {
|
||||
self.weak_self.clone()
|
||||
}
|
||||
|
||||
/// Get the file associated with this epoll entry.
|
||||
///
|
||||
/// Since an epoll entry only holds a weak reference to the file,
|
||||
/// it is possible (albeit unlikely) that the file has been dropped.
|
||||
pub fn file(&self) -> Option<Arc<dyn FileLike>> {
|
||||
self.file.upgrade()
|
||||
}
|
||||
|
||||
/// Get the epoll event associated with the epoll entry.
|
||||
pub fn event(&self) -> EpollEvent {
|
||||
let inner = self.inner.lock();
|
||||
inner.event
|
||||
}
|
||||
|
||||
/// Get the epoll flags associated with the epoll entry.
|
||||
pub fn flags(&self) -> EpollFlags {
|
||||
let inner = self.inner.lock();
|
||||
inner.flags
|
||||
}
|
||||
|
||||
/// Get the epoll event and flags that are associated with this epoll entry.
|
||||
pub fn event_and_flags(&self) -> (EpollEvent, EpollFlags) {
|
||||
let inner = self.inner.lock();
|
||||
(inner.event, inner.flags)
|
||||
}
|
||||
|
||||
/// Poll the events of the file associated with this epoll entry.
|
||||
///
|
||||
/// If the returned events is not empty, then the file is considered ready.
|
||||
pub fn poll(&self) -> IoEvents {
|
||||
match self.file.upgrade() {
|
||||
Some(file) => file.poll(IoEvents::all(), None),
|
||||
None => IoEvents::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the epoll entry, most likely to be triggered via `EpollCtl::Mod`.
|
||||
pub fn update(&self, event: EpollEvent, flags: EpollFlags) {
|
||||
let mut inner = self.inner.lock();
|
||||
*inner = Inner { event, flags }
|
||||
}
|
||||
|
||||
/// Returns whether the epoll entry is in the ready list.
|
||||
pub fn is_ready(&self) -> bool {
|
||||
self.is_ready.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Mark the epoll entry as being in the ready list.
|
||||
pub fn set_ready(&self) {
|
||||
self.is_ready.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Mark the epoll entry as not being in the ready list.
|
||||
pub fn reset_ready(&self) {
|
||||
self.is_ready.store(false, Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Returns whether the epoll entry has been deleted from the interest list.
|
||||
pub fn is_deleted(&self) -> bool {
|
||||
self.is_deleted.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Mark the epoll entry as having been deleted from the interest list.
|
||||
pub fn set_deleted(&self) {
|
||||
self.is_deleted.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Get the file descriptor associated with the epoll entry.
|
||||
pub fn fd(&self) -> FileDescripter {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<IoEvents> for EpollEntry {
|
||||
fn on_events(&self, _events: &IoEvents) {
|
||||
// Fast path
|
||||
if self.is_deleted() {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(epoll_file) = self.epoll_file() {
|
||||
epoll_file.push_ready(self.self_arc());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::file_table::FileDescripter;
|
||||
use crate::events::IoEvents;
|
||||
use crate::prelude::*;
|
||||
|
||||
mod epoll_file;
|
||||
|
||||
pub use self::epoll_file::EpollFile;
|
||||
|
||||
/// An epoll control command.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum EpollCtl {
|
||||
Add(FileDescripter, EpollEvent, EpollFlags),
|
||||
Del(FileDescripter),
|
||||
Mod(FileDescripter, EpollEvent, EpollFlags),
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Linux's epoll flags.
|
||||
pub struct EpollFlags: u32 {
|
||||
const EXCLUSIVE = (1 << 28);
|
||||
const WAKE_UP = (1 << 29);
|
||||
const ONE_SHOT = (1 << 30);
|
||||
const EDGE_TRIGGER = (1 << 31);
|
||||
}
|
||||
}
|
||||
|
||||
/// An epoll event.
|
||||
///
|
||||
/// This could be used as either an input of epoll ctl or an output of epoll wait.
|
||||
/// The memory layout is compatible with that of C's struct epoll_event.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct EpollEvent {
|
||||
/// I/O events.
|
||||
///
|
||||
/// When `EpollEvent` is used as inputs, this is treated as a mask of events.
|
||||
/// When `EpollEvent` is used as outputs, this is the active events.
|
||||
pub events: IoEvents,
|
||||
/// A 64-bit, user-given data.
|
||||
pub user_data: u64,
|
||||
}
|
||||
|
||||
impl EpollEvent {
|
||||
/// Create a new epoll event.
|
||||
pub fn new(events: IoEvents, user_data: u64) -> Self {
|
||||
Self { events, user_data }
|
||||
}
|
||||
}
|
@ -1,478 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::fs::Ext2;
|
||||
use super::inode::{Inode, InodeDesc, RawInode};
|
||||
use super::prelude::*;
|
||||
use super::super_block::SuperBlock;
|
||||
|
||||
use aster_util::id_allocator::IdAlloc;
|
||||
|
||||
/// Blocks are clustered into block groups in order to reduce fragmentation and minimise
|
||||
/// the amount of head seeking when reading a large amount of consecutive data.
|
||||
pub(super) struct BlockGroup {
|
||||
idx: usize,
|
||||
bg_impl: Arc<BlockGroupImpl>,
|
||||
raw_inodes_cache: PageCache,
|
||||
}
|
||||
|
||||
struct BlockGroupImpl {
|
||||
inode_table_bid: Bid,
|
||||
raw_inodes_size: usize,
|
||||
inner: RwMutex<Inner>,
|
||||
fs: Weak<Ext2>,
|
||||
}
|
||||
|
||||
impl BlockGroup {
|
||||
/// Loads and constructs a block group.
|
||||
pub fn load(
|
||||
group_descriptors_segment: &VmSegment,
|
||||
idx: usize,
|
||||
block_device: &dyn BlockDevice,
|
||||
super_block: &SuperBlock,
|
||||
fs: Weak<Ext2>,
|
||||
) -> Result<Self> {
|
||||
let raw_inodes_size = (super_block.inodes_per_group() as usize) * super_block.inode_size();
|
||||
|
||||
let bg_impl = {
|
||||
let metadata = {
|
||||
let descriptor = {
|
||||
// Read the block group descriptor
|
||||
// TODO: if the main is corrupted, should we load the backup?
|
||||
let offset = idx * core::mem::size_of::<RawGroupDescriptor>();
|
||||
let raw_descriptor = group_descriptors_segment
|
||||
.read_val::<RawGroupDescriptor>(offset)
|
||||
.unwrap();
|
||||
GroupDescriptor::from(raw_descriptor)
|
||||
};
|
||||
|
||||
let get_bitmap = |bid: Bid, capacity: usize| -> Result<IdAlloc> {
|
||||
if capacity > BLOCK_SIZE * 8 {
|
||||
return_errno_with_message!(Errno::EINVAL, "bad bitmap");
|
||||
}
|
||||
let mut buf = vec![0u8; BLOCK_SIZE];
|
||||
block_device.read_bytes(bid.to_offset(), &mut buf)?;
|
||||
Ok(IdAlloc::from_bytes_with_capacity(&buf, capacity))
|
||||
};
|
||||
|
||||
let block_bitmap = get_bitmap(
|
||||
descriptor.block_bitmap_bid,
|
||||
super_block.blocks_per_group() as usize,
|
||||
)?;
|
||||
let inode_bitmap = get_bitmap(
|
||||
descriptor.inode_bitmap_bid,
|
||||
super_block.inodes_per_group() as usize,
|
||||
)?;
|
||||
|
||||
GroupMetadata {
|
||||
descriptor,
|
||||
block_bitmap,
|
||||
inode_bitmap,
|
||||
}
|
||||
};
|
||||
|
||||
Arc::new(BlockGroupImpl {
|
||||
inode_table_bid: metadata.descriptor.inode_table_bid,
|
||||
raw_inodes_size,
|
||||
inner: RwMutex::new(Inner {
|
||||
metadata: Dirty::new(metadata),
|
||||
inode_cache: BTreeMap::new(),
|
||||
}),
|
||||
fs,
|
||||
})
|
||||
};
|
||||
|
||||
let raw_inodes_cache =
|
||||
PageCache::with_capacity(raw_inodes_size, Arc::downgrade(&bg_impl) as _)?;
|
||||
|
||||
Ok(Self {
|
||||
idx,
|
||||
bg_impl,
|
||||
raw_inodes_cache,
|
||||
})
|
||||
}
|
||||
|
||||
/// Finds and returns the inode.
|
||||
pub fn lookup_inode(&self, inode_idx: u32) -> Result<Arc<Inode>> {
|
||||
// The fast path
|
||||
let inner = self.bg_impl.inner.read();
|
||||
if !inner.metadata.is_inode_allocated(inode_idx) {
|
||||
return_errno!(Errno::ENOENT);
|
||||
}
|
||||
if let Some(inode) = inner.inode_cache.get(&inode_idx) {
|
||||
return Ok(inode.clone());
|
||||
}
|
||||
|
||||
// The slow path
|
||||
drop(inner);
|
||||
let mut inner = self.bg_impl.inner.write();
|
||||
if !inner.metadata.is_inode_allocated(inode_idx) {
|
||||
return_errno!(Errno::ENOENT);
|
||||
}
|
||||
if let Some(inode) = inner.inode_cache.get(&inode_idx) {
|
||||
return Ok(inode.clone());
|
||||
}
|
||||
|
||||
// Loads the inode, then inserts it into the inode cache.
|
||||
let inode = self.load_inode(inode_idx)?;
|
||||
inner.inode_cache.insert(inode_idx, inode.clone());
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
/// Loads an existing inode.
|
||||
///
|
||||
/// This method may load the raw inode metadata from block device.
|
||||
fn load_inode(&self, inode_idx: u32) -> Result<Arc<Inode>> {
|
||||
let fs = self.fs();
|
||||
let raw_inode = {
|
||||
let offset = (inode_idx as usize) * fs.inode_size();
|
||||
self.raw_inodes_cache
|
||||
.pages()
|
||||
.read_val::<RawInode>(offset)
|
||||
.unwrap()
|
||||
};
|
||||
let inode_desc = Dirty::new(InodeDesc::try_from(raw_inode)?);
|
||||
let ino = inode_idx + self.idx as u32 * fs.inodes_per_group() + 1;
|
||||
|
||||
Ok(Inode::new(ino, self.idx, inode_desc, Arc::downgrade(&fs)))
|
||||
}
|
||||
|
||||
/// Inserts the inode into the inode cache.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If `inode_idx` has not been allocated before, then the method panics.
|
||||
pub fn insert_cache(&self, inode_idx: u32, inode: Arc<Inode>) {
|
||||
let mut inner = self.bg_impl.inner.write();
|
||||
assert!(inner.metadata.is_inode_allocated(inode_idx));
|
||||
inner.inode_cache.insert(inode_idx, inode);
|
||||
}
|
||||
|
||||
/// Allocates and returns an inode index.
|
||||
pub fn alloc_inode(&self, is_dir: bool) -> Option<u32> {
|
||||
// The fast path
|
||||
if self.bg_impl.inner.read().metadata.free_inodes_count() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// The slow path
|
||||
self.bg_impl.inner.write().metadata.alloc_inode(is_dir)
|
||||
}
|
||||
|
||||
/// Frees the allocated inode idx.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If `inode_idx` has not been allocated before, then the method panics.
|
||||
pub fn free_inode(&self, inode_idx: u32, is_dir: bool) {
|
||||
let mut inner = self.bg_impl.inner.write();
|
||||
assert!(inner.metadata.is_inode_allocated(inode_idx));
|
||||
|
||||
inner.metadata.free_inode(inode_idx, is_dir);
|
||||
inner.inode_cache.remove(&inode_idx);
|
||||
}
|
||||
|
||||
/// Allocates and returns a block index.
|
||||
pub fn alloc_block(&self) -> Option<u32> {
|
||||
// The fast path
|
||||
if self.bg_impl.inner.read().metadata.free_blocks_count() == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// The slow path
|
||||
self.bg_impl.inner.write().metadata.alloc_block()
|
||||
}
|
||||
|
||||
/// Frees the allocated block idx.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If `block_idx` has not been allocated before, then the method panics.
|
||||
pub fn free_block(&self, block_idx: u32) {
|
||||
let mut inner = self.bg_impl.inner.write();
|
||||
assert!(inner.metadata.is_block_allocated(block_idx));
|
||||
|
||||
inner.metadata.free_block(block_idx);
|
||||
}
|
||||
|
||||
/// Writes back the raw inode metadata to the raw inode metadata cache.
|
||||
pub fn sync_raw_inode(&self, inode_idx: u32, raw_inode: &RawInode) {
|
||||
let offset = (inode_idx as usize) * self.fs().inode_size();
|
||||
self.raw_inodes_cache
|
||||
.pages()
|
||||
.write_val(offset, raw_inode)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Writes back the metadata of this group.
|
||||
pub fn sync_metadata(&self, super_block: &SuperBlock) -> Result<()> {
|
||||
if !self.bg_impl.inner.read().metadata.is_dirty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut inner = self.bg_impl.inner.write();
|
||||
let fs = self.fs();
|
||||
// Writes back the descriptor.
|
||||
let raw_descriptor = RawGroupDescriptor::from(&inner.metadata.descriptor);
|
||||
self.fs().sync_group_descriptor(self.idx, &raw_descriptor)?;
|
||||
|
||||
let mut bio_waiter = BioWaiter::new();
|
||||
// Writes back the inode bitmap.
|
||||
let inode_bitmap_bid = inner.metadata.descriptor.inode_bitmap_bid;
|
||||
bio_waiter.concat(fs.block_device().write_bytes_async(
|
||||
inode_bitmap_bid.to_offset(),
|
||||
inner.metadata.inode_bitmap.as_bytes(),
|
||||
)?);
|
||||
|
||||
// Writes back the block bitmap.
|
||||
let block_bitmap_bid = inner.metadata.descriptor.block_bitmap_bid;
|
||||
bio_waiter.concat(fs.block_device().write_bytes_async(
|
||||
block_bitmap_bid.to_offset(),
|
||||
inner.metadata.block_bitmap.as_bytes(),
|
||||
)?);
|
||||
|
||||
// Waits for the completion of all submitted bios.
|
||||
bio_waiter.wait().ok_or_else(|| {
|
||||
Error::with_message(Errno::EIO, "failed to sync metadata of block group")
|
||||
})?;
|
||||
|
||||
inner.metadata.clear_dirty();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes back all of the cached inodes.
|
||||
///
|
||||
/// The `sync_all` method of inode may modify the data of this block group,
|
||||
/// so we should not hold the lock while syncing the inodes.
|
||||
pub fn sync_all_inodes(&self) -> Result<()> {
|
||||
// Removes the inodes that is unused from the inode cache.
|
||||
let unused_inodes: Vec<Arc<Inode>> = self
|
||||
.bg_impl
|
||||
.inner
|
||||
.write()
|
||||
.inode_cache
|
||||
.extract_if(|_, inode| Arc::strong_count(inode) == 1)
|
||||
.map(|(_, inode)| inode)
|
||||
.collect();
|
||||
|
||||
// Writes back the unused inodes.
|
||||
for inode in unused_inodes.iter() {
|
||||
inode.sync_all()?;
|
||||
}
|
||||
drop(unused_inodes);
|
||||
|
||||
// Writes back the remaining inodes in the inode cache.
|
||||
let remaining_inodes: Vec<Arc<Inode>> = self
|
||||
.bg_impl
|
||||
.inner
|
||||
.read()
|
||||
.inode_cache
|
||||
.values()
|
||||
.cloned()
|
||||
.collect();
|
||||
for inode in remaining_inodes.iter() {
|
||||
inode.sync_all()?;
|
||||
}
|
||||
drop(remaining_inodes);
|
||||
|
||||
// Writes back the raw inode metadata.
|
||||
self.raw_inodes_cache
|
||||
.pages()
|
||||
.decommit(0..self.bg_impl.raw_inodes_size)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<Ext2> {
|
||||
self.bg_impl.fs.upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for BlockGroup {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("BlockGroup")
|
||||
.field("idx", &self.idx)
|
||||
.field("descriptor", &self.bg_impl.inner.read().metadata.descriptor)
|
||||
.field(
|
||||
"block_bitmap",
|
||||
&self.bg_impl.inner.read().metadata.block_bitmap,
|
||||
)
|
||||
.field(
|
||||
"inode_bitmap",
|
||||
&self.bg_impl.inner.read().metadata.inode_bitmap,
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl PageCacheBackend for BlockGroupImpl {
|
||||
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
let bid = self.inode_table_bid + idx as u64;
|
||||
self.fs.upgrade().unwrap().read_block(bid, frame)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
let bid = self.inode_table_bid + idx as u64;
|
||||
self.fs.upgrade().unwrap().write_block(bid, frame)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn npages(&self) -> usize {
|
||||
self.raw_inodes_size.div_ceil(BLOCK_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
metadata: Dirty<GroupMetadata>,
|
||||
inode_cache: BTreeMap<u32, Arc<Inode>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct GroupMetadata {
|
||||
descriptor: GroupDescriptor,
|
||||
block_bitmap: IdAlloc,
|
||||
inode_bitmap: IdAlloc,
|
||||
}
|
||||
|
||||
impl GroupMetadata {
|
||||
pub fn is_inode_allocated(&self, inode_idx: u32) -> bool {
|
||||
self.inode_bitmap.is_allocated(inode_idx as usize)
|
||||
}
|
||||
|
||||
pub fn alloc_inode(&mut self, is_dir: bool) -> Option<u32> {
|
||||
let Some(inode_idx) = self.inode_bitmap.alloc() else {
|
||||
return None;
|
||||
};
|
||||
self.dec_free_inodes();
|
||||
if is_dir {
|
||||
self.inc_dirs();
|
||||
}
|
||||
Some(inode_idx as u32)
|
||||
}
|
||||
|
||||
pub fn free_inode(&mut self, inode_idx: u32, is_dir: bool) {
|
||||
self.inode_bitmap.free(inode_idx as usize);
|
||||
self.inc_free_inodes();
|
||||
if is_dir {
|
||||
self.dec_dirs();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_block_allocated(&self, block_idx: u32) -> bool {
|
||||
self.block_bitmap.is_allocated(block_idx as usize)
|
||||
}
|
||||
|
||||
pub fn alloc_block(&mut self) -> Option<u32> {
|
||||
let Some(block_idx) = self.block_bitmap.alloc() else {
|
||||
return None;
|
||||
};
|
||||
self.dec_free_blocks();
|
||||
Some(block_idx as u32)
|
||||
}
|
||||
|
||||
pub fn free_block(&mut self, block_idx: u32) {
|
||||
self.block_bitmap.free(block_idx as usize);
|
||||
self.inc_free_blocks();
|
||||
}
|
||||
|
||||
pub fn free_inodes_count(&self) -> u16 {
|
||||
self.descriptor.free_inodes_count
|
||||
}
|
||||
|
||||
pub fn free_blocks_count(&self) -> u16 {
|
||||
self.descriptor.free_blocks_count
|
||||
}
|
||||
|
||||
pub fn inc_free_inodes(&mut self) {
|
||||
self.descriptor.free_inodes_count += 1;
|
||||
}
|
||||
|
||||
pub fn dec_free_inodes(&mut self) {
|
||||
debug_assert!(self.descriptor.free_inodes_count > 0);
|
||||
self.descriptor.free_inodes_count -= 1;
|
||||
}
|
||||
|
||||
pub fn inc_free_blocks(&mut self) {
|
||||
self.descriptor.free_blocks_count += 1;
|
||||
}
|
||||
|
||||
pub fn dec_free_blocks(&mut self) {
|
||||
debug_assert!(self.descriptor.free_blocks_count > 0);
|
||||
self.descriptor.free_blocks_count -= 1;
|
||||
}
|
||||
|
||||
pub fn inc_dirs(&mut self) {
|
||||
self.descriptor.dirs_count += 1;
|
||||
}
|
||||
|
||||
pub fn dec_dirs(&mut self) {
|
||||
debug_assert!(self.descriptor.dirs_count > 0);
|
||||
self.descriptor.dirs_count -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// The in-memory rust block group descriptor.
|
||||
///
|
||||
/// The block group descriptor contains information regarding where important data
|
||||
/// structures for that group are located.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct GroupDescriptor {
|
||||
/// Blocks usage bitmap block
|
||||
block_bitmap_bid: Bid,
|
||||
/// Inodes usage bitmap block
|
||||
inode_bitmap_bid: Bid,
|
||||
/// Starting block of inode table
|
||||
inode_table_bid: Bid,
|
||||
/// Number of free blocks in group
|
||||
free_blocks_count: u16,
|
||||
/// Number of free inodes in group
|
||||
free_inodes_count: u16,
|
||||
/// Number of directories in group
|
||||
dirs_count: u16,
|
||||
}
|
||||
|
||||
impl From<RawGroupDescriptor> for GroupDescriptor {
|
||||
fn from(desc: RawGroupDescriptor) -> Self {
|
||||
Self {
|
||||
block_bitmap_bid: Bid::new(desc.block_bitmap as _),
|
||||
inode_bitmap_bid: Bid::new(desc.inode_bitmap as _),
|
||||
inode_table_bid: Bid::new(desc.inode_table as _),
|
||||
free_blocks_count: desc.free_blocks_count,
|
||||
free_inodes_count: desc.free_inodes_count,
|
||||
dirs_count: desc.dirs_count,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const_assert!(core::mem::size_of::<RawGroupDescriptor>() == 32);
|
||||
|
||||
/// The raw block group descriptor.
|
||||
///
|
||||
/// The table starts on the first block following the superblock.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod)]
|
||||
pub(super) struct RawGroupDescriptor {
|
||||
pub block_bitmap: u32,
|
||||
pub inode_bitmap: u32,
|
||||
pub inode_table: u32,
|
||||
pub free_blocks_count: u16,
|
||||
pub free_inodes_count: u16,
|
||||
pub dirs_count: u16,
|
||||
pad: u16,
|
||||
reserved: [u32; 3],
|
||||
}
|
||||
|
||||
impl From<&GroupDescriptor> for RawGroupDescriptor {
|
||||
fn from(desc: &GroupDescriptor) -> Self {
|
||||
Self {
|
||||
block_bitmap: desc.block_bitmap_bid.to_raw() as _,
|
||||
inode_bitmap: desc.inode_bitmap_bid.to_raw() as _,
|
||||
inode_table: desc.inode_table_bid.to_raw() as _,
|
||||
free_blocks_count: desc.free_blocks_count,
|
||||
free_inodes_count: desc.free_inodes_count,
|
||||
dirs_count: desc.dirs_count,
|
||||
pad: 0u16,
|
||||
reserved: [0u32; 3],
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use bitvec::prelude::BitVec;
|
||||
|
||||
/// A blocks hole descriptor implemented by the `BitVec`.
|
||||
///
|
||||
/// The true bit implies that the block is a hole, and conversely.
|
||||
pub(super) struct BlocksHoleDesc(BitVec);
|
||||
|
||||
impl BlocksHoleDesc {
|
||||
/// Constructs a blocks hole descriptor with initial size.
|
||||
///
|
||||
/// The `initial_size` usually is the number of blocks for a file.
|
||||
pub fn new(initial_size: usize) -> Self {
|
||||
let mut bit_vec = BitVec::with_capacity(initial_size);
|
||||
bit_vec.resize(initial_size, false);
|
||||
Self(bit_vec)
|
||||
}
|
||||
|
||||
/// Returns the size.
|
||||
pub fn size(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
/// Resizes the blocks hole to a new size.
|
||||
///
|
||||
/// If `new_size` is greater than current size, the new blocks are all marked as hole.
|
||||
pub fn resize(&mut self, new_size: usize) {
|
||||
self.0.resize(new_size, true);
|
||||
}
|
||||
|
||||
/// Returns if the block `idx` is a hole.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the `idx` is out of bounds, this method will panic.
|
||||
pub fn is_hole(&self, idx: usize) -> bool {
|
||||
self.0[idx]
|
||||
}
|
||||
|
||||
/// Marks the block `idx` as a hole.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the `idx` is out of bounds, this method will panic.
|
||||
pub fn set(&mut self, idx: usize) {
|
||||
self.0.set(idx, true);
|
||||
}
|
||||
|
||||
/// Unmarks the block `idx` as a hole.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If the `idx` is out of bounds, this method will panic.
|
||||
pub fn unset(&mut self, idx: usize) {
|
||||
self.0.set(idx, false);
|
||||
}
|
||||
}
|
@ -1,323 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::inode::{FileType, MAX_FNAME_LEN};
|
||||
use super::prelude::*;
|
||||
|
||||
use core::iter::Iterator;
|
||||
|
||||
/// The data structure in a directory's data block. It is stored in a linked list.
|
||||
///
|
||||
/// Each entry contains the name of the entry, the inode number, the file type,
|
||||
/// and the distance within the directory file to the next entry.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DirEntry {
|
||||
/// The header part.
|
||||
header: DirEntryHeader,
|
||||
/// Name of the entry, up to 255 bytes (excluding the null terminator).
|
||||
name: CStr256,
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
/// Constructs a new `DirEntry` object with the specified inode (`ino`),
|
||||
/// name (`name`), and file type (`file_type`).
|
||||
pub(super) fn new(ino: u32, name: &str, file_type: FileType) -> Self {
|
||||
debug_assert!(name.len() <= MAX_FNAME_LEN);
|
||||
|
||||
let record_len = (Self::header_len() + name.len()).align_up(4) as u16;
|
||||
Self {
|
||||
header: DirEntryHeader {
|
||||
ino,
|
||||
record_len,
|
||||
name_len: name.len() as u8,
|
||||
file_type: DirEntryFileType::from(file_type) as _,
|
||||
},
|
||||
name: CStr256::from(name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a `DirEntry` with the name "." and `self_ino` as its inode.
|
||||
pub(super) fn self_entry(self_ino: u32) -> Self {
|
||||
Self::new(self_ino, ".", FileType::Dir)
|
||||
}
|
||||
|
||||
/// Constructs a `DirEntry` with the name ".." and `parent_ino` as its inode.
|
||||
pub(super) fn parent_entry(parent_ino: u32) -> Self {
|
||||
Self::new(parent_ino, "..", FileType::Dir)
|
||||
}
|
||||
|
||||
/// Returns a reference to the header.
|
||||
fn header(&self) -> &DirEntryHeader {
|
||||
&self.header
|
||||
}
|
||||
|
||||
/// Returns the length of the header.
|
||||
fn header_len() -> usize {
|
||||
core::mem::size_of::<DirEntryHeader>()
|
||||
}
|
||||
|
||||
/// Returns the inode number.
|
||||
pub fn ino(&self) -> u32 {
|
||||
self.header.ino
|
||||
}
|
||||
|
||||
/// Modifies the inode number.
|
||||
pub fn set_ino(&mut self, ino: u32) {
|
||||
self.header.ino = ino;
|
||||
}
|
||||
|
||||
/// Returns the name.
|
||||
pub fn name(&self) -> &str {
|
||||
self.name.as_str().unwrap()
|
||||
}
|
||||
|
||||
/// Returns the type.
|
||||
pub fn type_(&self) -> FileType {
|
||||
FileType::from(DirEntryFileType::try_from(self.header.file_type).unwrap())
|
||||
}
|
||||
|
||||
/// Returns the distance to the next entry.
|
||||
pub fn record_len(&self) -> usize {
|
||||
self.header.record_len as _
|
||||
}
|
||||
|
||||
/// Modifies the distance to the next entry.
|
||||
pub(super) fn set_record_len(&mut self, record_len: usize) {
|
||||
debug_assert!(record_len >= self.actual_len());
|
||||
self.header.record_len = record_len as _;
|
||||
}
|
||||
|
||||
/// Returns the actual length of the current entry.
|
||||
pub(super) fn actual_len(&self) -> usize {
|
||||
(Self::header_len() + self.name.len()).align_up(4)
|
||||
}
|
||||
|
||||
/// Returns the length of the gap between the current entry and the next entry.
|
||||
pub(super) fn gap_len(&self) -> usize {
|
||||
self.record_len() - self.actual_len()
|
||||
}
|
||||
}
|
||||
|
||||
/// The header of `DirEntry`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod)]
|
||||
struct DirEntryHeader {
|
||||
/// Inode number
|
||||
ino: u32,
|
||||
/// Directory entry length
|
||||
record_len: u16,
|
||||
/// Name Length
|
||||
name_len: u8,
|
||||
/// Type indicator
|
||||
file_type: u8,
|
||||
}
|
||||
|
||||
/// The type indicator in the `DirEntry`.
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromInt)]
|
||||
enum DirEntryFileType {
|
||||
Unknown = 0,
|
||||
File = 1,
|
||||
Dir = 2,
|
||||
Char = 3,
|
||||
Block = 4,
|
||||
Fifo = 5,
|
||||
Socket = 6,
|
||||
Symlink = 7,
|
||||
}
|
||||
|
||||
impl From<FileType> for DirEntryFileType {
|
||||
fn from(file_type: FileType) -> Self {
|
||||
match file_type {
|
||||
FileType::Fifo => Self::Fifo,
|
||||
FileType::Char => Self::Char,
|
||||
FileType::Dir => Self::Dir,
|
||||
FileType::Block => Self::Block,
|
||||
FileType::File => Self::File,
|
||||
FileType::Symlink => Self::Symlink,
|
||||
FileType::Socket => Self::Socket,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DirEntryFileType> for FileType {
|
||||
fn from(file_type: DirEntryFileType) -> Self {
|
||||
match file_type {
|
||||
DirEntryFileType::Fifo => Self::Fifo,
|
||||
DirEntryFileType::Char => Self::Char,
|
||||
DirEntryFileType::Dir => Self::Dir,
|
||||
DirEntryFileType::Block => Self::Block,
|
||||
DirEntryFileType::File => Self::File,
|
||||
DirEntryFileType::Symlink => Self::Symlink,
|
||||
DirEntryFileType::Socket => Self::Socket,
|
||||
DirEntryFileType::Unknown => panic!("unknown file type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reader for reading `DirEntry` from the page cache.
|
||||
pub struct DirEntryReader<'a> {
|
||||
page_cache: &'a PageCache,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> DirEntryReader<'a> {
|
||||
/// Constructs a reader with the given page cache and offset.
|
||||
pub(super) fn new(page_cache: &'a PageCache, from_offset: usize) -> Self {
|
||||
Self {
|
||||
page_cache,
|
||||
offset: from_offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads one `DirEntry` from the current offset.
|
||||
pub fn read_entry(&mut self) -> Result<DirEntry> {
|
||||
let header = self
|
||||
.page_cache
|
||||
.pages()
|
||||
.read_val::<DirEntryHeader>(self.offset)?;
|
||||
if header.ino == 0 {
|
||||
return_errno!(Errno::ENOENT);
|
||||
}
|
||||
|
||||
let mut name = vec![0u8; header.name_len as _];
|
||||
self.page_cache
|
||||
.pages()
|
||||
.read_bytes(self.offset + DirEntry::header_len(), &mut name)?;
|
||||
let entry = DirEntry {
|
||||
header,
|
||||
name: CStr256::from(name.as_slice()),
|
||||
};
|
||||
self.offset += entry.record_len();
|
||||
|
||||
Ok(entry)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for DirEntryReader<'a> {
|
||||
type Item = (usize, DirEntry);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let offset = self.offset;
|
||||
let entry = match self.read_entry() {
|
||||
Ok(entry) => entry,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some((offset, entry))
|
||||
}
|
||||
}
|
||||
|
||||
/// A writer for modifying `DirEntry` of the page cache.
|
||||
pub struct DirEntryWriter<'a> {
|
||||
page_cache: &'a PageCache,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> DirEntryWriter<'a> {
|
||||
/// Constructs a writer with the given page cache and offset.
|
||||
pub(super) fn new(page_cache: &'a PageCache, from_offset: usize) -> Self {
|
||||
Self {
|
||||
page_cache,
|
||||
offset: from_offset,
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes a `DirEntry` at the current offset.
|
||||
pub fn write_entry(&mut self, entry: &DirEntry) -> Result<()> {
|
||||
self.page_cache
|
||||
.pages()
|
||||
.write_val(self.offset, entry.header())?;
|
||||
self.page_cache.pages().write_bytes(
|
||||
self.offset + DirEntry::header_len(),
|
||||
entry.name().as_bytes(),
|
||||
)?;
|
||||
self.offset += entry.record_len();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Appends a new `DirEntry` starting from the current offset.
|
||||
///
|
||||
/// If there is a gap between existing entries, inserts the new entry into the gap;
|
||||
/// If there is no available space, expands the size and appends the new entry at the end.
|
||||
pub fn append_entry(&mut self, mut new_entry: DirEntry) -> Result<()> {
|
||||
let Some((offset, mut entry)) = DirEntryReader::new(self.page_cache, self.offset)
|
||||
.find(|(_, entry)| entry.gap_len() >= new_entry.record_len())
|
||||
else {
|
||||
// Resize and append it at the new block.
|
||||
let old_size = self.page_cache.pages().size();
|
||||
let new_size = old_size + BLOCK_SIZE;
|
||||
self.page_cache.pages().resize(new_size)?;
|
||||
new_entry.set_record_len(BLOCK_SIZE);
|
||||
self.offset = old_size;
|
||||
self.write_entry(&new_entry)?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
// Write in the gap between existing entries.
|
||||
new_entry.set_record_len(entry.gap_len());
|
||||
entry.set_record_len(entry.actual_len());
|
||||
self.offset = offset;
|
||||
self.write_entry(&entry)?;
|
||||
self.write_entry(&new_entry)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes and returns an existing `DirEntry` indicated by `name`.
|
||||
pub fn remove_entry(&mut self, name: &str) -> Result<DirEntry> {
|
||||
let self_entry_record_len = DirEntry::self_entry(0).record_len();
|
||||
let reader = DirEntryReader::new(self.page_cache, 0);
|
||||
let next_reader = DirEntryReader::new(self.page_cache, self_entry_record_len);
|
||||
let Some(((pre_offset, mut pre_entry), (offset, entry))) = reader
|
||||
.zip(next_reader)
|
||||
.find(|((offset, _), (_, dir_entry))| dir_entry.name() == name)
|
||||
else {
|
||||
return_errno!(Errno::ENOENT);
|
||||
};
|
||||
|
||||
if DirEntryReader::new(self.page_cache, offset)
|
||||
.next()
|
||||
.is_none()
|
||||
&& Bid::from_offset(pre_offset) != Bid::from_offset(offset)
|
||||
{
|
||||
// Shrink the size.
|
||||
let new_size = pre_offset.align_up(BLOCK_SIZE);
|
||||
self.page_cache.pages().resize(new_size)?;
|
||||
pre_entry.set_record_len(new_size - pre_offset);
|
||||
self.offset = pre_offset;
|
||||
self.write_entry(&pre_entry)?;
|
||||
} else {
|
||||
// Update the previous entry.
|
||||
pre_entry.set_record_len(pre_entry.record_len() + entry.record_len());
|
||||
self.offset = pre_offset;
|
||||
self.write_entry(&pre_entry)?;
|
||||
}
|
||||
|
||||
Ok(entry)
|
||||
}
|
||||
|
||||
/// Renames the `DirEntry` from `old_name` to the `new_name` from the current offset.
|
||||
///
|
||||
/// It will moves the `DirEntry` to another position,
|
||||
/// if the record length is not big enough.
|
||||
pub fn rename_entry(&mut self, old_name: &str, new_name: &str) -> Result<()> {
|
||||
let (offset, entry) = DirEntryReader::new(self.page_cache, self.offset)
|
||||
.find(|(offset, entry)| entry.name() == old_name)
|
||||
.ok_or(Error::new(Errno::ENOENT))?;
|
||||
|
||||
let mut new_entry = DirEntry::new(entry.ino(), new_name, entry.type_());
|
||||
if new_entry.record_len() <= entry.record_len() {
|
||||
// Just rename the entry.
|
||||
new_entry.set_record_len(entry.record_len());
|
||||
self.offset = offset;
|
||||
self.write_entry(&new_entry)?;
|
||||
} else {
|
||||
// Move to another position.
|
||||
self.remove_entry(old_name)?;
|
||||
self.offset = 0;
|
||||
self.append_entry(new_entry)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,368 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::block_group::{BlockGroup, RawGroupDescriptor};
|
||||
use super::inode::{FilePerm, FileType, Inode, InodeDesc, RawInode};
|
||||
use super::prelude::*;
|
||||
use super::super_block::{RawSuperBlock, SuperBlock, SUPER_BLOCK_OFFSET};
|
||||
|
||||
/// The root inode number.
|
||||
const ROOT_INO: u32 = 2;
|
||||
|
||||
/// The Ext2 filesystem.
|
||||
#[derive(Debug)]
|
||||
pub struct Ext2 {
|
||||
block_device: Arc<dyn BlockDevice>,
|
||||
super_block: RwMutex<Dirty<SuperBlock>>,
|
||||
block_groups: Vec<BlockGroup>,
|
||||
inodes_per_group: u32,
|
||||
blocks_per_group: u32,
|
||||
inode_size: usize,
|
||||
block_size: usize,
|
||||
group_descriptors_segment: VmSegment,
|
||||
self_ref: Weak<Self>,
|
||||
}
|
||||
|
||||
impl Ext2 {
|
||||
/// Opens and loads an Ext2 from the `block_device`.
|
||||
pub fn open(block_device: Arc<dyn BlockDevice>) -> Result<Arc<Self>> {
|
||||
// Load the superblock
|
||||
// TODO: if the main superblock is corrupted, should we load the backup?
|
||||
let super_block = {
|
||||
let raw_super_block = block_device.read_val::<RawSuperBlock>(SUPER_BLOCK_OFFSET)?;
|
||||
SuperBlock::try_from(raw_super_block)?
|
||||
};
|
||||
assert!(super_block.block_size() == BLOCK_SIZE);
|
||||
|
||||
let group_descriptors_segment = {
|
||||
let npages = ((super_block.block_groups_count() as usize)
|
||||
* core::mem::size_of::<RawGroupDescriptor>())
|
||||
.div_ceil(BLOCK_SIZE);
|
||||
let segment = VmAllocOptions::new(npages)
|
||||
.uninit(true)
|
||||
.is_contiguous(true)
|
||||
.alloc_contiguous()?;
|
||||
match block_device.read_blocks_sync(super_block.group_descriptors_bid(0), &segment)? {
|
||||
BioStatus::Complete => (),
|
||||
err_status => {
|
||||
return Err(Error::from(err_status));
|
||||
}
|
||||
}
|
||||
segment
|
||||
};
|
||||
|
||||
// Load the block groups information
|
||||
let load_block_groups = |fs: Weak<Ext2>,
|
||||
block_device: &dyn BlockDevice,
|
||||
group_descriptors_segment: &VmSegment|
|
||||
-> Result<Vec<BlockGroup>> {
|
||||
let block_groups_count = super_block.block_groups_count() as usize;
|
||||
let mut block_groups = Vec::with_capacity(block_groups_count);
|
||||
for idx in 0..block_groups_count {
|
||||
let block_group = BlockGroup::load(
|
||||
group_descriptors_segment,
|
||||
idx,
|
||||
block_device,
|
||||
&super_block,
|
||||
fs.clone(),
|
||||
)?;
|
||||
block_groups.push(block_group);
|
||||
}
|
||||
Ok(block_groups)
|
||||
};
|
||||
|
||||
let ext2 = Arc::new_cyclic(|weak_ref| Self {
|
||||
inodes_per_group: super_block.inodes_per_group(),
|
||||
blocks_per_group: super_block.blocks_per_group(),
|
||||
inode_size: super_block.inode_size(),
|
||||
block_size: super_block.block_size(),
|
||||
block_groups: load_block_groups(
|
||||
weak_ref.clone(),
|
||||
block_device.as_ref(),
|
||||
&group_descriptors_segment,
|
||||
)
|
||||
.unwrap(),
|
||||
block_device,
|
||||
super_block: RwMutex::new(Dirty::new(super_block)),
|
||||
group_descriptors_segment,
|
||||
self_ref: weak_ref.clone(),
|
||||
});
|
||||
Ok(ext2)
|
||||
}
|
||||
|
||||
/// Returns the block device.
|
||||
pub fn block_device(&self) -> &dyn BlockDevice {
|
||||
self.block_device.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the size of block.
|
||||
pub fn block_size(&self) -> usize {
|
||||
self.block_size
|
||||
}
|
||||
|
||||
/// Returns the size of inode.
|
||||
pub fn inode_size(&self) -> usize {
|
||||
self.inode_size
|
||||
}
|
||||
|
||||
/// Returns the number of inodes in each block group.
|
||||
pub fn inodes_per_group(&self) -> u32 {
|
||||
self.inodes_per_group
|
||||
}
|
||||
|
||||
/// Returns the number of blocks in each block group.
|
||||
pub fn blocks_per_group(&self) -> u32 {
|
||||
self.blocks_per_group
|
||||
}
|
||||
|
||||
/// Returns the super block.
|
||||
pub fn super_block(&self) -> RwMutexReadGuard<'_, Dirty<SuperBlock>> {
|
||||
self.super_block.read()
|
||||
}
|
||||
|
||||
/// Returns the root inode.
|
||||
pub fn root_inode(&self) -> Result<Arc<Inode>> {
|
||||
self.lookup_inode(ROOT_INO)
|
||||
}
|
||||
|
||||
/// Finds and returns the inode by `ino`.
|
||||
pub(super) fn lookup_inode(&self, ino: u32) -> Result<Arc<Inode>> {
|
||||
let (_, block_group) = self.block_group_of_ino(ino)?;
|
||||
let inode_idx = self.inode_idx(ino);
|
||||
block_group.lookup_inode(inode_idx)
|
||||
}
|
||||
|
||||
/// Creates a new inode.
|
||||
pub(super) fn create_inode(
|
||||
&self,
|
||||
dir_block_group_idx: usize,
|
||||
file_type: FileType,
|
||||
file_perm: FilePerm,
|
||||
) -> Result<Arc<Inode>> {
|
||||
let (block_group_idx, ino) =
|
||||
self.alloc_ino(dir_block_group_idx, file_type == FileType::Dir)?;
|
||||
let inode = {
|
||||
let inode_desc = InodeDesc::new(file_type, file_perm);
|
||||
Inode::new(ino, block_group_idx, inode_desc, self.self_ref.clone())
|
||||
};
|
||||
let block_group = &self.block_groups[block_group_idx];
|
||||
block_group.insert_cache(self.inode_idx(ino), inode.clone());
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
/// Allocates a new inode number, internally used by `new_inode`.
|
||||
///
|
||||
/// Attempts to allocate from the `dir_block_group_idx` group first.
|
||||
/// If allocation is not possible from this group, then search the remaining groups.
|
||||
fn alloc_ino(&self, dir_block_group_idx: usize, is_dir: bool) -> Result<(usize, u32)> {
|
||||
let mut block_group_idx = dir_block_group_idx;
|
||||
if block_group_idx >= self.block_groups.len() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid block group idx");
|
||||
}
|
||||
|
||||
for _ in 0..self.block_groups.len() {
|
||||
if block_group_idx >= self.block_groups.len() {
|
||||
block_group_idx = 0;
|
||||
}
|
||||
let block_group = &self.block_groups[block_group_idx];
|
||||
if let Some(inode_idx) = block_group.alloc_inode(is_dir) {
|
||||
let ino = block_group_idx as u32 * self.inodes_per_group + inode_idx + 1;
|
||||
self.super_block.write().dec_free_inodes();
|
||||
return Ok((block_group_idx, ino));
|
||||
}
|
||||
block_group_idx += 1;
|
||||
}
|
||||
|
||||
return_errno_with_message!(Errno::ENOSPC, "no space on device");
|
||||
}
|
||||
|
||||
/// Frees an inode.
|
||||
pub(super) fn free_inode(&self, ino: u32, is_dir: bool) -> Result<()> {
|
||||
let (_, block_group) = self.block_group_of_ino(ino)?;
|
||||
let inode_idx = self.inode_idx(ino);
|
||||
// In order to prevent value underflow, it is necessary to increment
|
||||
// the free inode counter prior to freeing the inode.
|
||||
self.super_block.write().inc_free_inodes();
|
||||
block_group.free_inode(inode_idx, is_dir);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes back the metadata of inode.
|
||||
pub(super) fn sync_inode(&self, ino: u32, inode: &InodeDesc) -> Result<()> {
|
||||
let (_, block_group) = self.block_group_of_ino(ino)?;
|
||||
let inode_idx = self.inode_idx(ino);
|
||||
block_group.sync_raw_inode(inode_idx, &RawInode::from(inode));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes back the block group descriptor to the descriptors table.
|
||||
pub(super) fn sync_group_descriptor(
|
||||
&self,
|
||||
block_group_idx: usize,
|
||||
raw_descriptor: &RawGroupDescriptor,
|
||||
) -> Result<()> {
|
||||
let offset = block_group_idx * core::mem::size_of::<RawGroupDescriptor>();
|
||||
self.group_descriptors_segment
|
||||
.write_val(offset, raw_descriptor)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Allocates a new block.
|
||||
///
|
||||
/// Attempts to allocate from the `block_group_idx` group first.
|
||||
/// If allocation is not possible from this group, then search the remaining groups.
|
||||
pub(super) fn alloc_block(&self, block_group_idx: usize) -> Result<Bid> {
|
||||
let mut block_group_idx = block_group_idx;
|
||||
if block_group_idx >= self.block_groups.len() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid block group idx");
|
||||
}
|
||||
|
||||
for _ in 0..self.block_groups.len() {
|
||||
if block_group_idx >= self.block_groups.len() {
|
||||
block_group_idx = 0;
|
||||
}
|
||||
let block_group = &self.block_groups[block_group_idx];
|
||||
if let Some(block_idx) = block_group.alloc_block() {
|
||||
let bid = block_group_idx as u32 * self.blocks_per_group + block_idx;
|
||||
self.super_block.write().dec_free_blocks();
|
||||
return Ok(Bid::new(bid as _));
|
||||
}
|
||||
block_group_idx += 1;
|
||||
}
|
||||
|
||||
return_errno_with_message!(Errno::ENOSPC, "no space on device");
|
||||
}
|
||||
|
||||
/// Frees a block.
|
||||
pub(super) fn free_block(&self, bid: Bid) -> Result<()> {
|
||||
let (_, block_group) = self.block_group_of_bid(bid)?;
|
||||
let block_idx = self.block_idx(bid);
|
||||
// In order to prevent value underflow, it is necessary to increment
|
||||
// the free block counter prior to freeing the block.
|
||||
self.super_block.write().inc_free_blocks();
|
||||
block_group.free_block(block_idx);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads contiguous blocks starting from the `bid` synchronously.
|
||||
pub(super) fn read_blocks(&self, bid: Bid, segment: &VmSegment) -> Result<()> {
|
||||
let status = self.block_device.read_blocks_sync(bid, segment)?;
|
||||
match status {
|
||||
BioStatus::Complete => Ok(()),
|
||||
err_status => Err(Error::from(err_status)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads one block indicated by the `bid` synchronously.
|
||||
pub(super) fn read_block(&self, bid: Bid, frame: &VmFrame) -> Result<()> {
|
||||
let status = self.block_device.read_block_sync(bid, frame)?;
|
||||
match status {
|
||||
BioStatus::Complete => Ok(()),
|
||||
err_status => Err(Error::from(err_status)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes contiguous blocks starting from the `bid` synchronously.
|
||||
pub(super) fn write_blocks(&self, bid: Bid, segment: &VmSegment) -> Result<()> {
|
||||
let status = self.block_device.write_blocks_sync(bid, segment)?;
|
||||
match status {
|
||||
BioStatus::Complete => Ok(()),
|
||||
err_status => Err(Error::from(err_status)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes one block indicated by the `bid` synchronously.
|
||||
pub(super) fn write_block(&self, bid: Bid, frame: &VmFrame) -> Result<()> {
|
||||
let status = self.block_device.write_block_sync(bid, frame)?;
|
||||
match status {
|
||||
BioStatus::Complete => Ok(()),
|
||||
err_status => Err(Error::from(err_status)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes back the metadata to the block device.
|
||||
pub fn sync_metadata(&self) -> Result<()> {
|
||||
// If the superblock is clean, the block groups must be clean.
|
||||
if !self.super_block.read().is_dirty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut super_block = self.super_block.write();
|
||||
// Writes back the metadata of block groups
|
||||
for block_group in &self.block_groups {
|
||||
block_group.sync_metadata(&super_block)?;
|
||||
}
|
||||
|
||||
let mut bio_waiter = BioWaiter::new();
|
||||
// Writes back the main superblock and group descriptor table.
|
||||
let raw_super_block = RawSuperBlock::from((*super_block).deref());
|
||||
bio_waiter.concat(
|
||||
self.block_device
|
||||
.write_bytes_async(SUPER_BLOCK_OFFSET, raw_super_block.as_bytes())?,
|
||||
);
|
||||
bio_waiter.concat(self.block_device.write_blocks(
|
||||
super_block.group_descriptors_bid(0),
|
||||
&self.group_descriptors_segment,
|
||||
)?);
|
||||
|
||||
// Writes back the backups of superblock and group descriptor table.
|
||||
let mut raw_super_block_backup = raw_super_block;
|
||||
for idx in 1..super_block.block_groups_count() {
|
||||
if super_block.is_backup_group(idx as usize) {
|
||||
raw_super_block_backup.block_group_idx = idx as u16;
|
||||
bio_waiter.concat(self.block_device.write_bytes_async(
|
||||
super_block.bid(idx as usize).to_offset(),
|
||||
raw_super_block_backup.as_bytes(),
|
||||
)?);
|
||||
bio_waiter.concat(self.block_device.write_blocks(
|
||||
super_block.group_descriptors_bid(idx as usize),
|
||||
&self.group_descriptors_segment,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
||||
// Waits for the completion of all submitted bios.
|
||||
bio_waiter
|
||||
.wait()
|
||||
.ok_or_else(|| Error::with_message(Errno::EIO, "failed to sync metadata of fs"))?;
|
||||
|
||||
// Reset to clean.
|
||||
super_block.clear_dirty();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes back all the cached inodes to the block device.
|
||||
pub fn sync_all_inodes(&self) -> Result<()> {
|
||||
for block_group in &self.block_groups {
|
||||
block_group.sync_all_inodes()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn block_group_of_bid(&self, bid: Bid) -> Result<(usize, &BlockGroup)> {
|
||||
let block_group_idx = (bid.to_raw() / (self.blocks_per_group as u64)) as usize;
|
||||
if block_group_idx >= self.block_groups.len() {
|
||||
return_errno!(Errno::ENOENT);
|
||||
}
|
||||
Ok((block_group_idx, &self.block_groups[block_group_idx]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn block_group_of_ino(&self, ino: u32) -> Result<(usize, &BlockGroup)> {
|
||||
let block_group_idx = ((ino - 1) / self.inodes_per_group) as usize;
|
||||
if block_group_idx >= self.block_groups.len() {
|
||||
return_errno!(Errno::ENOENT);
|
||||
}
|
||||
Ok((block_group_idx, &self.block_groups[block_group_idx]))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn inode_idx(&self, ino: u32) -> u32 {
|
||||
(ino - 1) % self.inodes_per_group
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn block_idx(&self, bid: Bid) -> u32 {
|
||||
(bid.to_raw() as u32) % self.blocks_per_group
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::fs::ext2::{utils::Dirty, Ext2, SuperBlock as Ext2SuperBlock, MAGIC_NUM as EXT2_MAGIC};
|
||||
use crate::fs::utils::{FileSystem, FsFlags, Inode, SuperBlock, NAME_MAX};
|
||||
use crate::prelude::*;
|
||||
|
||||
use aster_frame::sync::RwMutexReadGuard;
|
||||
|
||||
impl FileSystem for Ext2 {
|
||||
fn sync(&self) -> Result<()> {
|
||||
self.sync_all_inodes()?;
|
||||
self.sync_metadata()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn root_inode(&self) -> Arc<dyn Inode> {
|
||||
self.root_inode().unwrap()
|
||||
}
|
||||
|
||||
fn sb(&self) -> SuperBlock {
|
||||
SuperBlock::from(self.super_block())
|
||||
}
|
||||
|
||||
fn flags(&self) -> FsFlags {
|
||||
FsFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RwMutexReadGuard<'_, Dirty<Ext2SuperBlock>>> for SuperBlock {
|
||||
fn from(ext2_sb: RwMutexReadGuard<Dirty<Ext2SuperBlock>>) -> Self {
|
||||
Self {
|
||||
magic: EXT2_MAGIC as _,
|
||||
bsize: ext2_sb.block_size(),
|
||||
blocks: ext2_sb.total_blocks() as _,
|
||||
bfree: ext2_sb.free_blocks() as _,
|
||||
bavail: ext2_sb.free_blocks() as _,
|
||||
files: ext2_sb.total_inodes() as _,
|
||||
ffree: ext2_sb.free_inodes() as _,
|
||||
fsid: 0, // TODO
|
||||
namelen: NAME_MAX,
|
||||
frsize: ext2_sb.fragment_size(),
|
||||
flags: 0, // TODO
|
||||
}
|
||||
}
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::fs::device::Device;
|
||||
use crate::fs::ext2::{FilePerm, FileType, Inode as Ext2Inode};
|
||||
use crate::fs::utils::{
|
||||
DirentVisitor, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::vm::vmo::Vmo;
|
||||
|
||||
use aster_rights::Full;
|
||||
use core::time::Duration;
|
||||
|
||||
impl Inode for Ext2Inode {
|
||||
fn size(&self) -> usize {
|
||||
self.file_size() as _
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()> {
|
||||
self.resize(new_size)
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
dev: 0, // TODO: ID of block device
|
||||
ino: self.ino() as _,
|
||||
size: self.file_size() as _,
|
||||
blk_size: self.fs().super_block().block_size(),
|
||||
blocks: self.blocks_count() as _,
|
||||
atime: self.atime(),
|
||||
mtime: self.mtime(),
|
||||
ctime: self.ctime(),
|
||||
type_: InodeType::from(self.file_type()),
|
||||
mode: InodeMode::from(self.file_perm()),
|
||||
nlinks: self.hard_links() as _,
|
||||
uid: self.uid() as _,
|
||||
gid: self.gid() as _,
|
||||
rdev: self.device_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.atime()
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {
|
||||
self.set_atime(time)
|
||||
}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.mtime()
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {
|
||||
self.set_mtime(time)
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.ino() as _
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
InodeType::from(self.file_type())
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
InodeMode::from(self.file_perm())
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {
|
||||
self.set_file_perm(mode.into());
|
||||
}
|
||||
|
||||
fn page_cache(&self) -> Option<Vmo<Full>> {
|
||||
Some(self.page_cache())
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.read_at(offset, buf)
|
||||
}
|
||||
|
||||
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.read_direct_at(offset, buf)
|
||||
}
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
self.write_at(offset, buf)
|
||||
}
|
||||
|
||||
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
self.write_direct_at(offset, buf)
|
||||
}
|
||||
|
||||
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
|
||||
Ok(self.create(name, type_.into(), mode.into())?)
|
||||
}
|
||||
|
||||
fn mknod(&self, name: &str, mode: InodeMode, dev: Arc<dyn Device>) -> Result<Arc<dyn Inode>> {
|
||||
let inode = self.create(name, InodeType::from(dev.type_()).into(), mode.into())?;
|
||||
inode.set_device_id(dev.id().into()).unwrap();
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
|
||||
Ok(self.lookup(name)?)
|
||||
}
|
||||
|
||||
fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
self.readdir_at(offset, visitor)
|
||||
}
|
||||
|
||||
fn link(&self, old: &Arc<dyn Inode>, name: &str) -> Result<()> {
|
||||
let old = old
|
||||
.downcast_ref::<Ext2Inode>()
|
||||
.ok_or_else(|| Error::with_message(Errno::EXDEV, "not same fs"))?;
|
||||
self.link(old, name)
|
||||
}
|
||||
|
||||
fn unlink(&self, name: &str) -> Result<()> {
|
||||
self.unlink(name)
|
||||
}
|
||||
|
||||
fn rmdir(&self, name: &str) -> Result<()> {
|
||||
self.rmdir(name)
|
||||
}
|
||||
|
||||
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()> {
|
||||
let target = target
|
||||
.downcast_ref::<Ext2Inode>()
|
||||
.ok_or_else(|| Error::with_message(Errno::EXDEV, "not same fs"))?;
|
||||
self.rename(old_name, target, new_name)
|
||||
}
|
||||
|
||||
fn read_link(&self) -> Result<String> {
|
||||
self.read_link()
|
||||
}
|
||||
|
||||
fn write_link(&self, target: &str) -> Result<()> {
|
||||
self.write_link(target)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
Err(Error::new(Errno::EINVAL))
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()> {
|
||||
self.sync_all()
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.fs()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FilePerm> for InodeMode {
|
||||
fn from(perm: FilePerm) -> Self {
|
||||
Self::from_bits_truncate(perm.bits() as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InodeMode> for FilePerm {
|
||||
fn from(mode: InodeMode) -> Self {
|
||||
Self::from_bits_truncate(mode.bits() as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FileType> for InodeType {
|
||||
fn from(type_: FileType) -> Self {
|
||||
Self::try_from(type_ as u32).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<InodeType> for FileType {
|
||||
fn from(type_: InodeType) -> Self {
|
||||
Self::try_from(type_ as u16).unwrap()
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod fs;
|
||||
mod inode;
|
File diff suppressed because it is too large
Load Diff
@ -1,52 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! A safe Rust Ext2 filesystem.
|
||||
//!
|
||||
//! The Second Extended File System(Ext2) is a major rewrite of the Ext filesystem.
|
||||
//! It is the predominant filesystem in use by Linux from the early 1990s to the early 2000s.
|
||||
//! The structures of Ext3 and Ext4 are based on Ext2 and add some additional options
|
||||
//! such as journaling.
|
||||
//!
|
||||
//! The features of this version of Ext2 are as follows:
|
||||
//! 1. No unsafe Rust. The filesystem is written is Rust without any unsafe code,
|
||||
//! ensuring that there are no memory safety issues in the code.
|
||||
//! 2. Deep integration with PageCache. The data and metadata of the filesystem are
|
||||
//! stored in PageCache, which accelerates the performance of data access.
|
||||
//! 3. Compatible with queue-based block device. The filesystem can submits multiple
|
||||
//! BIO requests to be block device at once, thereby enhancing I/O performance.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```no_run
|
||||
//! // Opens an Ext2 from the block device.
|
||||
//! let ext2 = Ext2::open(block_device)?;
|
||||
//! // Lookup the root inode.
|
||||
//! let root = ext2.root_inode()?;
|
||||
//! // Create a file inside root directory.
|
||||
//! let file = root.create("file", FileType::File, FilePerm::from_bits_truncate(0o666))?;
|
||||
//! // Write data into the file.
|
||||
//! const WRITE_DATA: &[u8] = b"Hello, World";
|
||||
//! let len = file.write_at(0, WRITE_DATA)?;
|
||||
//! assert!(len == WRITE_DATA.len());
|
||||
//! ```
|
||||
//!
|
||||
//! # Limitation
|
||||
//!
|
||||
//! Here we summarizes the features that need to be implemented in the future.
|
||||
//! 1. Supports large file.
|
||||
//! 2. Supports merging small read/write operations.
|
||||
//! 3. Handles the intermediate failure status correctly.
|
||||
|
||||
pub use fs::Ext2;
|
||||
pub use inode::{FilePerm, FileType, Inode};
|
||||
pub use super_block::{SuperBlock, MAGIC_NUM};
|
||||
|
||||
mod block_group;
|
||||
mod blocks_hole;
|
||||
mod dir;
|
||||
mod fs;
|
||||
mod impl_for_vfs;
|
||||
mod inode;
|
||||
mod prelude;
|
||||
mod super_block;
|
||||
mod utils;
|
@ -1,25 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub(super) use super::utils::{Dirty, IsPowerOf};
|
||||
|
||||
pub(super) use crate::fs::utils::{
|
||||
CStr256, DirentVisitor, InodeType, PageCache, PageCacheBackend, Str16, Str64,
|
||||
};
|
||||
pub(super) use crate::prelude::*;
|
||||
pub(super) use crate::time::UnixTime;
|
||||
pub(super) use crate::vm::vmo::Vmo;
|
||||
|
||||
pub(super) use align_ext::AlignExt;
|
||||
pub(super) use aster_block::{
|
||||
bio::{BioStatus, BioWaiter},
|
||||
id::Bid,
|
||||
BlockDevice, BLOCK_SIZE,
|
||||
};
|
||||
pub(super) use aster_frame::sync::{RwMutex, RwMutexReadGuard};
|
||||
pub(super) use aster_frame::vm::VmAllocOptions;
|
||||
pub(super) use aster_frame::vm::VmIo;
|
||||
pub(super) use aster_frame::vm::{VmFrame, VmSegment};
|
||||
pub(super) use aster_rights::Full;
|
||||
pub(super) use core::ops::{Deref, DerefMut};
|
||||
pub(super) use core::time::Duration;
|
||||
pub(super) use static_assertions::const_assert;
|
@ -1,544 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::inode::RawInode;
|
||||
use super::prelude::*;
|
||||
|
||||
/// The magic number of Ext2.
|
||||
pub const MAGIC_NUM: u16 = 0xef53;
|
||||
|
||||
/// The main superblock is located at byte 1024 from the beginning of the device.
|
||||
pub const SUPER_BLOCK_OFFSET: usize = 1024;
|
||||
|
||||
const SUPER_BLOCK_SIZE: usize = 1024;
|
||||
|
||||
/// The in-memory rust superblock.
|
||||
///
|
||||
/// It contains all information about the layout of the Ext2.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct SuperBlock {
|
||||
/// Total number of inodes.
|
||||
inodes_count: u32,
|
||||
/// Total number of blocks.
|
||||
blocks_count: u32,
|
||||
/// Total number of reserved blocks.
|
||||
reserved_blocks_count: u32,
|
||||
/// Total number of free blocks.
|
||||
free_blocks_count: u32,
|
||||
/// Total number of free inodes.
|
||||
free_inodes_count: u32,
|
||||
/// First data block.
|
||||
first_data_block: Bid,
|
||||
/// Block size.
|
||||
block_size: usize,
|
||||
/// Fragment size.
|
||||
frag_size: usize,
|
||||
/// Number of blocks in each block group.
|
||||
blocks_per_group: u32,
|
||||
/// Number of fragments in each block group.
|
||||
frags_per_group: u32,
|
||||
/// Number of inodes in each block group.
|
||||
inodes_per_group: u32,
|
||||
/// Mount time.
|
||||
mtime: UnixTime,
|
||||
/// Write time.
|
||||
wtime: UnixTime,
|
||||
/// Mount count.
|
||||
mnt_count: u16,
|
||||
/// Maximal mount count.
|
||||
max_mnt_count: u16,
|
||||
/// Magic signature.
|
||||
magic: u16,
|
||||
/// Filesystem state.
|
||||
state: FsState,
|
||||
/// Behaviour when detecting errors.
|
||||
errors_behaviour: ErrorsBehaviour,
|
||||
/// Time of last check.
|
||||
last_check_time: UnixTime,
|
||||
/// Interval between checks.
|
||||
check_interval: Duration,
|
||||
/// Creator OS ID.
|
||||
creator_os: OsId,
|
||||
/// Revision level.
|
||||
rev_level: RevLevel,
|
||||
/// Default uid for reserved blocks.
|
||||
def_resuid: u32,
|
||||
/// Default gid for reserved blocks.
|
||||
def_resgid: u32,
|
||||
//
|
||||
// These fields are valid for RevLevel::Dynamic only.
|
||||
//
|
||||
/// First non-reserved inode number.
|
||||
first_ino: u32,
|
||||
/// Size of inode structure.
|
||||
inode_size: usize,
|
||||
/// Block group that this superblock is part of (if backup copy).
|
||||
block_group_idx: usize,
|
||||
/// Compatible feature set.
|
||||
feature_compat: FeatureCompatSet,
|
||||
/// Incompatible feature set.
|
||||
feature_incompat: FeatureInCompatSet,
|
||||
/// Readonly-compatible feature set.
|
||||
feature_ro_compat: FeatureRoCompatSet,
|
||||
/// 128-bit uuid for volume.
|
||||
uuid: [u8; 16],
|
||||
/// Volume name.
|
||||
volume_name: Str16,
|
||||
/// Directory where last mounted.
|
||||
last_mounted_dir: Str64,
|
||||
///
|
||||
/// This fields are valid if the FeatureCompatSet::DIR_PREALLOC is set.
|
||||
///
|
||||
/// Number of blocks to preallocate for files.
|
||||
prealloc_file_blocks: u8,
|
||||
/// Number of blocks to preallocate for directories.
|
||||
prealloc_dir_blocks: u8,
|
||||
}
|
||||
|
||||
impl TryFrom<RawSuperBlock> for SuperBlock {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(sb: RawSuperBlock) -> Result<Self> {
|
||||
Ok(Self {
|
||||
inodes_count: sb.inodes_count,
|
||||
blocks_count: sb.blocks_count,
|
||||
reserved_blocks_count: sb.reserved_blocks_count,
|
||||
free_blocks_count: sb.free_blocks_count,
|
||||
free_inodes_count: sb.free_inodes_count,
|
||||
first_data_block: Bid::new(sb.first_data_block as _),
|
||||
block_size: 1024 << sb.log_block_size,
|
||||
frag_size: 1024 << sb.log_frag_size,
|
||||
blocks_per_group: sb.blocks_per_group,
|
||||
frags_per_group: sb.frags_per_group,
|
||||
inodes_per_group: sb.inodes_per_group,
|
||||
mtime: sb.mtime,
|
||||
wtime: sb.wtime,
|
||||
mnt_count: sb.mnt_count,
|
||||
max_mnt_count: sb.max_mnt_count,
|
||||
magic: {
|
||||
if sb.magic != MAGIC_NUM {
|
||||
return_errno_with_message!(Errno::EINVAL, "bad ext2 magic number");
|
||||
}
|
||||
MAGIC_NUM
|
||||
},
|
||||
state: {
|
||||
let state = FsState::try_from(sb.state)
|
||||
.map_err(|_| Error::with_message(Errno::EINVAL, "invalid fs state"))?;
|
||||
if state == FsState::Corrupted {
|
||||
return_errno_with_message!(Errno::EUCLEAN, "fs is corrupted");
|
||||
}
|
||||
state
|
||||
},
|
||||
errors_behaviour: ErrorsBehaviour::try_from(sb.errors)
|
||||
.map_err(|_| Error::with_message(Errno::EINVAL, "invalid errors behaviour"))?,
|
||||
last_check_time: sb.last_check_time,
|
||||
check_interval: Duration::from_secs(sb.check_interval as _),
|
||||
creator_os: {
|
||||
let os_id = OsId::try_from(sb.creator_os)
|
||||
.map_err(|_| Error::with_message(Errno::EINVAL, "invalid creater os"))?;
|
||||
if os_id != OsId::Linux {
|
||||
return_errno_with_message!(Errno::EINVAL, "not supported os id");
|
||||
}
|
||||
OsId::Linux
|
||||
},
|
||||
rev_level: {
|
||||
let rev_level = RevLevel::try_from(sb.rev_level)
|
||||
.map_err(|_| Error::with_message(Errno::EINVAL, "invalid revision level"))?;
|
||||
if rev_level != RevLevel::Dynamic {
|
||||
return_errno_with_message!(Errno::EINVAL, "not supported rev level");
|
||||
}
|
||||
RevLevel::Dynamic
|
||||
},
|
||||
def_resuid: sb.def_resuid as _,
|
||||
def_resgid: sb.def_resgid as _,
|
||||
first_ino: sb.first_ino,
|
||||
inode_size: {
|
||||
let inode_size = sb.inode_size as _;
|
||||
if inode_size < core::mem::size_of::<RawInode>() {
|
||||
return_errno_with_message!(Errno::EINVAL, "inode size is too small");
|
||||
}
|
||||
inode_size
|
||||
},
|
||||
block_group_idx: sb.block_group_idx as _,
|
||||
feature_compat: FeatureCompatSet::from_bits(sb.feature_compat).ok_or(
|
||||
Error::with_message(Errno::EINVAL, "invalid feature compat set"),
|
||||
)?,
|
||||
feature_incompat: FeatureInCompatSet::from_bits(sb.feature_incompat).ok_or(
|
||||
Error::with_message(Errno::EINVAL, "invalid feature incompat set"),
|
||||
)?,
|
||||
feature_ro_compat: FeatureRoCompatSet::from_bits(sb.feature_ro_compat).ok_or(
|
||||
Error::with_message(Errno::EINVAL, "invalid feature ro compat set"),
|
||||
)?,
|
||||
uuid: sb.uuid,
|
||||
volume_name: sb.volume_name,
|
||||
last_mounted_dir: sb.last_mounted_dir,
|
||||
prealloc_file_blocks: sb.prealloc_file_blocks,
|
||||
prealloc_dir_blocks: sb.prealloc_dir_blocks,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl SuperBlock {
|
||||
/// Returns the block size.
|
||||
pub fn block_size(&self) -> usize {
|
||||
self.block_size
|
||||
}
|
||||
|
||||
/// Returns the size of inode structure.
|
||||
pub fn inode_size(&self) -> usize {
|
||||
self.inode_size
|
||||
}
|
||||
|
||||
/// Returns the fragment size.
|
||||
pub fn fragment_size(&self) -> usize {
|
||||
self.frag_size
|
||||
}
|
||||
|
||||
/// Returns total number of inodes.
|
||||
pub fn total_inodes(&self) -> u32 {
|
||||
self.inodes_count
|
||||
}
|
||||
|
||||
/// Returns total number of blocks.
|
||||
pub fn total_blocks(&self) -> u32 {
|
||||
self.blocks_count
|
||||
}
|
||||
|
||||
/// Returns the number of blocks in each block group.
|
||||
pub fn blocks_per_group(&self) -> u32 {
|
||||
self.blocks_per_group
|
||||
}
|
||||
|
||||
/// Returns the number of inodes in each block group.
|
||||
pub fn inodes_per_group(&self) -> u32 {
|
||||
self.inodes_per_group
|
||||
}
|
||||
|
||||
/// Returns the number of block groups.
|
||||
pub fn block_groups_count(&self) -> u32 {
|
||||
self.blocks_count / self.blocks_per_group
|
||||
}
|
||||
|
||||
/// Returns the filesystem state.
|
||||
pub fn state(&self) -> FsState {
|
||||
self.state
|
||||
}
|
||||
|
||||
/// Returns the revision level.
|
||||
pub fn rev_level(&self) -> RevLevel {
|
||||
self.rev_level
|
||||
}
|
||||
|
||||
/// Returns the compatible feature set.
|
||||
pub fn feature_compat(&self) -> FeatureCompatSet {
|
||||
self.feature_compat
|
||||
}
|
||||
|
||||
/// Returns the incompatible feature set.
|
||||
pub fn feature_incompat(&self) -> FeatureInCompatSet {
|
||||
self.feature_incompat
|
||||
}
|
||||
|
||||
/// Returns the readonly-compatible feature set.
|
||||
pub fn feature_ro_compat(&self) -> FeatureRoCompatSet {
|
||||
self.feature_ro_compat
|
||||
}
|
||||
|
||||
/// Returns the number of free blocks.
|
||||
pub fn free_blocks(&self) -> u32 {
|
||||
self.free_blocks_count
|
||||
}
|
||||
|
||||
/// Increase the number of free blocks.
|
||||
pub(super) fn inc_free_blocks(&mut self) {
|
||||
self.free_blocks_count += 1;
|
||||
}
|
||||
|
||||
/// Decrease the number of free blocks.
|
||||
pub(super) fn dec_free_blocks(&mut self) {
|
||||
debug_assert!(self.free_blocks_count > 0);
|
||||
self.free_blocks_count -= 1;
|
||||
}
|
||||
|
||||
/// Returns the number of free inodes.
|
||||
pub fn free_inodes(&self) -> u32 {
|
||||
self.free_inodes_count
|
||||
}
|
||||
|
||||
/// Increase the number of free inodes.
|
||||
pub(super) fn inc_free_inodes(&mut self) {
|
||||
self.free_inodes_count += 1;
|
||||
}
|
||||
|
||||
/// Decrease the number of free inodes.
|
||||
pub(super) fn dec_free_inodes(&mut self) {
|
||||
debug_assert!(self.free_inodes_count > 0);
|
||||
self.free_inodes_count -= 1;
|
||||
}
|
||||
|
||||
/// Checks if the block group will backup the super block.
|
||||
pub(super) fn is_backup_group(&self, block_group_idx: usize) -> bool {
|
||||
if block_group_idx == 0 {
|
||||
false
|
||||
} else if self
|
||||
.feature_ro_compat
|
||||
.contains(FeatureRoCompatSet::SPARSE_SUPER)
|
||||
{
|
||||
// The backup groups chosen are 1 and powers of 3, 5 and 7.
|
||||
block_group_idx == 1
|
||||
|| block_group_idx.is_power_of(3)
|
||||
|| block_group_idx.is_power_of(5)
|
||||
|| block_group_idx.is_power_of(7)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the starting block id of the super block
|
||||
/// inside the block group pointed by `block_group_idx`.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If `block_group_idx` is neither 0 nor a backup block group index,
|
||||
/// then the method panics.
|
||||
pub(super) fn bid(&self, block_group_idx: usize) -> Bid {
|
||||
if block_group_idx == 0 {
|
||||
let bid = (SUPER_BLOCK_OFFSET / self.block_size) as u64;
|
||||
return Bid::new(bid);
|
||||
}
|
||||
|
||||
assert!(self.is_backup_group(block_group_idx));
|
||||
let super_block_bid = block_group_idx * (self.blocks_per_group as usize);
|
||||
Bid::new(super_block_bid as u64)
|
||||
}
|
||||
|
||||
/// Returns the starting block id of the block group descripter table
|
||||
/// inside the block group pointed by `block_group_idx`.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// If `block_group_idx` is neither 0 nor a backup block group index,
|
||||
/// then the method panics.
|
||||
pub(super) fn group_descriptors_bid(&self, block_group_idx: usize) -> Bid {
|
||||
let super_block_bid = self.bid(block_group_idx);
|
||||
super_block_bid + (SUPER_BLOCK_SIZE.div_ceil(self.block_size) as u64)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Compatible feature set.
|
||||
pub struct FeatureCompatSet: u32 {
|
||||
/// Preallocate some number of blocks to a directory when creating a new one
|
||||
const DIR_PREALLOC = 1 << 0;
|
||||
/// AFS server inodes exist
|
||||
const IMAGIC_INODES = 1 << 1;
|
||||
/// File system has a journal
|
||||
const HAS_JOURNAL = 1 << 2;
|
||||
/// Inodes have extended attributes
|
||||
const EXT_ATTR = 1 << 3;
|
||||
/// File system can resize itself for larger partitions
|
||||
const RESIZE_INO = 1 << 4;
|
||||
/// Directories use hash index
|
||||
const DIR_INDEX = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Incompatible feature set.
|
||||
pub struct FeatureInCompatSet: u32 {
|
||||
/// Compression is used
|
||||
const COMPRESSION = 1 << 0;
|
||||
/// Directory entries contain a type field
|
||||
const FILETYPE = 1 << 1;
|
||||
/// File system needs to replay its journal
|
||||
const RECOVER = 1 << 2;
|
||||
/// File system uses a journal device
|
||||
const JOURNAL_DEV = 1 << 3;
|
||||
/// Metablock block group
|
||||
const META_BG = 1 << 4;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Readonly-compatible feature set.
|
||||
pub struct FeatureRoCompatSet: u32 {
|
||||
/// Sparse superblocks and group descriptor tables
|
||||
const SPARSE_SUPER = 1 << 0;
|
||||
/// File system uses a 64-bit file size
|
||||
const LARGE_FILE = 1 << 1;
|
||||
/// Directory contents are stored in the form of a Binary Tree
|
||||
const BTREE_DIR = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromInt)]
|
||||
pub enum FsState {
|
||||
/// Unmounted cleanly
|
||||
Valid = 1,
|
||||
/// Errors detected
|
||||
Err = 2,
|
||||
/// Filesystem is corrupted (EUCLEAN)
|
||||
Corrupted = 117,
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromInt)]
|
||||
pub enum ErrorsBehaviour {
|
||||
/// Continue execution
|
||||
Continue = 1,
|
||||
// Remount fs read-only
|
||||
RemountReadonly = 2,
|
||||
// Should panic
|
||||
Panic = 3,
|
||||
}
|
||||
|
||||
impl Default for ErrorsBehaviour {
|
||||
fn default() -> Self {
|
||||
Self::Continue
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromInt)]
|
||||
pub enum OsId {
|
||||
Linux = 0,
|
||||
Hurd = 1,
|
||||
Masix = 2,
|
||||
FreeBSD = 3,
|
||||
Lites = 4,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromInt)]
|
||||
pub enum RevLevel {
|
||||
/// The good old (original) format.
|
||||
GoodOld = 0,
|
||||
/// V2 format with dynamic inode size.
|
||||
Dynamic = 1,
|
||||
}
|
||||
|
||||
const_assert!(core::mem::size_of::<RawSuperBlock>() == SUPER_BLOCK_SIZE);
|
||||
|
||||
/// The raw superblock, it must be exactly 1024 bytes in length.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod, Default)]
|
||||
pub(super) struct RawSuperBlock {
|
||||
pub inodes_count: u32,
|
||||
pub blocks_count: u32,
|
||||
pub reserved_blocks_count: u32,
|
||||
pub free_blocks_count: u32,
|
||||
pub free_inodes_count: u32,
|
||||
pub first_data_block: u32,
|
||||
/// The number to left-shift 1024 to obtain the block size.
|
||||
pub log_block_size: u32,
|
||||
/// The number to left-shift 1024 to obtain the fragment size.
|
||||
pub log_frag_size: u32,
|
||||
pub blocks_per_group: u32,
|
||||
pub frags_per_group: u32,
|
||||
pub inodes_per_group: u32,
|
||||
/// Mount time.
|
||||
pub mtime: UnixTime,
|
||||
/// Write time.
|
||||
pub wtime: UnixTime,
|
||||
pub mnt_count: u16,
|
||||
pub max_mnt_count: u16,
|
||||
pub magic: u16,
|
||||
pub state: u16,
|
||||
pub errors: u16,
|
||||
pub min_rev_level: u16,
|
||||
/// Time of last check.
|
||||
pub last_check_time: UnixTime,
|
||||
pub check_interval: u32,
|
||||
pub creator_os: u32,
|
||||
pub rev_level: u32,
|
||||
pub def_resuid: u16,
|
||||
pub def_resgid: u16,
|
||||
pub first_ino: u32,
|
||||
pub inode_size: u16,
|
||||
pub block_group_idx: u16,
|
||||
pub feature_compat: u32,
|
||||
pub feature_incompat: u32,
|
||||
pub feature_ro_compat: u32,
|
||||
pub uuid: [u8; 16],
|
||||
pub volume_name: Str16,
|
||||
pub last_mounted_dir: Str64,
|
||||
pub algorithm_usage_bitmap: u32,
|
||||
pub prealloc_file_blocks: u8,
|
||||
pub prealloc_dir_blocks: u8,
|
||||
padding1: u16,
|
||||
///
|
||||
/// This fileds are for journaling support in Ext3.
|
||||
///
|
||||
/// Uuid of journal superblock.
|
||||
pub journal_uuid: [u8; 16],
|
||||
/// Inode number of journal file.
|
||||
pub journal_ino: u32,
|
||||
/// Device number of journal file.
|
||||
pub journal_dev: u32,
|
||||
/// Start of list of inodes to delete.
|
||||
pub last_orphan: u32,
|
||||
/// HTREE hash seed.
|
||||
pub hash_seed: [u32; 4],
|
||||
/// Default hash version to use
|
||||
pub def_hash_version: u8,
|
||||
reserved_char_pad: u8,
|
||||
reserved_word_pad: u16,
|
||||
/// Default mount options.
|
||||
pub default_mount_opts: u32,
|
||||
/// First metablock block group.
|
||||
pub first_meta_bg: u32,
|
||||
reserved: Reserved,
|
||||
}
|
||||
|
||||
impl From<&SuperBlock> for RawSuperBlock {
|
||||
fn from(sb: &SuperBlock) -> Self {
|
||||
Self {
|
||||
inodes_count: sb.inodes_count,
|
||||
blocks_count: sb.blocks_count,
|
||||
reserved_blocks_count: sb.reserved_blocks_count,
|
||||
free_blocks_count: sb.free_blocks_count,
|
||||
free_inodes_count: sb.free_inodes_count,
|
||||
first_data_block: sb.first_data_block.to_raw() as u32,
|
||||
log_block_size: (sb.block_size >> 11) as u32,
|
||||
log_frag_size: (sb.frag_size >> 11) as u32,
|
||||
blocks_per_group: sb.blocks_per_group,
|
||||
frags_per_group: sb.frags_per_group,
|
||||
inodes_per_group: sb.inodes_per_group,
|
||||
mtime: sb.mtime,
|
||||
wtime: sb.wtime,
|
||||
mnt_count: sb.mnt_count,
|
||||
max_mnt_count: sb.max_mnt_count,
|
||||
magic: sb.magic,
|
||||
state: sb.state as u16,
|
||||
errors: sb.errors_behaviour as u16,
|
||||
last_check_time: sb.last_check_time,
|
||||
check_interval: sb.check_interval.as_secs() as u32,
|
||||
creator_os: sb.creator_os as u32,
|
||||
rev_level: sb.rev_level as u32,
|
||||
def_resuid: sb.def_resuid as u16,
|
||||
def_resgid: sb.def_resgid as u16,
|
||||
first_ino: sb.first_ino,
|
||||
inode_size: sb.inode_size as u16,
|
||||
block_group_idx: sb.block_group_idx as u16,
|
||||
feature_compat: sb.feature_compat.bits(),
|
||||
feature_incompat: sb.feature_incompat.bits(),
|
||||
feature_ro_compat: sb.feature_ro_compat.bits(),
|
||||
uuid: sb.uuid,
|
||||
volume_name: sb.volume_name,
|
||||
last_mounted_dir: sb.last_mounted_dir,
|
||||
prealloc_file_blocks: sb.prealloc_file_blocks,
|
||||
prealloc_dir_blocks: sb.prealloc_dir_blocks,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Pod)]
|
||||
struct Reserved([u32; 190]);
|
||||
|
||||
impl Default for Reserved {
|
||||
fn default() -> Self {
|
||||
Self([0u32; 190])
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
use core::ops::MulAssign;
|
||||
|
||||
pub trait IsPowerOf: Copy + Sized + MulAssign + PartialOrd {
|
||||
/// Returns true if and only if `self == x^k` for some `k` where `k > 0`.
|
||||
///
|
||||
/// The `x` must be a positive value.
|
||||
fn is_power_of(&self, x: Self) -> bool {
|
||||
let mut power = x;
|
||||
while power < *self {
|
||||
power *= x;
|
||||
}
|
||||
|
||||
power == *self
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_ipo_for {
|
||||
($($ipo_ty:ty),*) => {
|
||||
$(impl IsPowerOf for $ipo_ty {})*
|
||||
};
|
||||
}
|
||||
|
||||
impl_ipo_for!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, isize, usize);
|
||||
|
||||
/// The `Dirty` wraps a value of type `T` with functions similar to that of a rw-lock,
|
||||
/// but simply sets a dirty flag on `write()`.
|
||||
pub struct Dirty<T: Debug> {
|
||||
value: T,
|
||||
dirty: bool,
|
||||
}
|
||||
|
||||
impl<T: Debug> Dirty<T> {
|
||||
/// Creates a new Dirty without setting the dirty flag.
|
||||
pub fn new(val: T) -> Dirty<T> {
|
||||
Dirty {
|
||||
value: val,
|
||||
dirty: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new Dirty with setting the dirty flag.
|
||||
pub fn new_dirty(val: T) -> Dirty<T> {
|
||||
Dirty {
|
||||
value: val,
|
||||
dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if dirty, false otherwise.
|
||||
pub fn is_dirty(&self) -> bool {
|
||||
self.dirty
|
||||
}
|
||||
|
||||
/// Clears the dirty flag.
|
||||
pub fn clear_dirty(&mut self) {
|
||||
self.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Deref for Dirty<T> {
|
||||
type Target = T;
|
||||
|
||||
/// Returns the imutable value.
|
||||
fn deref(&self) -> &T {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> DerefMut for Dirty<T> {
|
||||
/// Returns the mutable value, sets the dirty flag.
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
self.dirty = true;
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Drop for Dirty<T> {
|
||||
/// Guards if it is dirty when dropping.
|
||||
fn drop(&mut self) {
|
||||
if self.is_dirty() {
|
||||
warn!("[{:?}] is dirty then dropping", self.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug> Debug for Dirty<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
let tag = if self.dirty { "Dirty" } else { "Clean" };
|
||||
write!(f, "[{}] {:?}", tag, self.value)
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Opend File Handle
|
||||
|
||||
use crate::events::{IoEvents, Observer};
|
||||
use crate::fs::device::Device;
|
||||
use crate::fs::utils::{AccessMode, IoctlCmd, Metadata, SeekFrom, StatusFlags};
|
||||
use crate::net::socket::Socket;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
use core::any::Any;
|
||||
|
||||
/// The basic operations defined on a file
|
||||
pub trait FileLike: Send + Sync + Any {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "read is not supported");
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "write is not supported");
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
return_errno_with_message!(Errno::EINVAL, "ioctl is not supported");
|
||||
}
|
||||
|
||||
fn poll(&self, _mask: IoEvents, _poller: Option<&Poller>) -> IoEvents {
|
||||
IoEvents::empty()
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "resize is not supported");
|
||||
}
|
||||
|
||||
fn flush(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
panic!("metadata unsupported");
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
StatusFlags::empty()
|
||||
}
|
||||
|
||||
fn set_status_flags(&self, _new_flags: StatusFlags) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "set_status_flags is not supported");
|
||||
}
|
||||
|
||||
fn access_mode(&self) -> AccessMode {
|
||||
AccessMode::O_RDWR
|
||||
}
|
||||
|
||||
fn seek(&self, seek_from: SeekFrom) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "seek is not supported");
|
||||
}
|
||||
|
||||
fn clean_for_close(&self) -> Result<()> {
|
||||
self.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn register_observer(
|
||||
&self,
|
||||
observer: Weak<dyn Observer<IoEvents>>,
|
||||
mask: IoEvents,
|
||||
) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "register_observer is not supported")
|
||||
}
|
||||
|
||||
fn unregister_observer(
|
||||
&self,
|
||||
observer: &Weak<dyn Observer<IoEvents>>,
|
||||
) -> Result<Weak<dyn Observer<IoEvents>>> {
|
||||
return_errno_with_message!(Errno::EINVAL, "unregister_observer is not supported")
|
||||
}
|
||||
|
||||
fn as_socket(self: Arc<Self>) -> Option<Arc<dyn Socket>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_device(&self) -> Option<Arc<dyn Device>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn FileLike {
|
||||
pub fn downcast_ref<T: FileLike>(&self) -> Option<&T> {
|
||||
(self as &dyn Any).downcast_ref::<T>()
|
||||
}
|
||||
}
|
@ -1,233 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::{Events, Observer, Subject};
|
||||
use crate::net::socket::Socket;
|
||||
use crate::prelude::*;
|
||||
|
||||
use aster_util::slot_vec::SlotVec;
|
||||
use core::cell::Cell;
|
||||
|
||||
use super::file_handle::FileLike;
|
||||
use super::fs_resolver::{FsPath, FsResolver, AT_FDCWD};
|
||||
use super::utils::{AccessMode, InodeMode};
|
||||
|
||||
pub type FileDescripter = i32;
|
||||
|
||||
pub struct FileTable {
|
||||
table: SlotVec<FileTableEntry>,
|
||||
subject: Subject<FdEvents>,
|
||||
}
|
||||
|
||||
impl FileTable {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
table: SlotVec::new(),
|
||||
subject: Subject::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_stdio() -> Self {
|
||||
let mut table = SlotVec::new();
|
||||
let fs_resolver = FsResolver::new();
|
||||
let tty_path = FsPath::new(AT_FDCWD, "/dev/console").expect("cannot find tty");
|
||||
let stdin = {
|
||||
let flags = AccessMode::O_RDONLY as u32;
|
||||
let mode = InodeMode::S_IRUSR;
|
||||
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
|
||||
};
|
||||
let stdout = {
|
||||
let flags = AccessMode::O_WRONLY as u32;
|
||||
let mode = InodeMode::S_IWUSR;
|
||||
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
|
||||
};
|
||||
let stderr = {
|
||||
let flags = AccessMode::O_WRONLY as u32;
|
||||
let mode = InodeMode::S_IWUSR;
|
||||
fs_resolver.open(&tty_path, flags, mode.bits()).unwrap()
|
||||
};
|
||||
table.put(FileTableEntry::new(Arc::new(stdin), false));
|
||||
table.put(FileTableEntry::new(Arc::new(stdout), false));
|
||||
table.put(FileTableEntry::new(Arc::new(stderr), false));
|
||||
Self {
|
||||
table,
|
||||
subject: Subject::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dup(&mut self, fd: FileDescripter, new_fd: FileDescripter) -> Result<FileDescripter> {
|
||||
let entry = self.table.get(fd as usize).map_or_else(
|
||||
|| return_errno_with_message!(Errno::ENOENT, "No such file"),
|
||||
|e| Ok(e.clone()),
|
||||
)?;
|
||||
|
||||
// Get the lowest-numbered available fd equal to or greater than `new_fd`.
|
||||
let get_min_free_fd = || -> usize {
|
||||
let new_fd = new_fd as usize;
|
||||
if self.table.get(new_fd).is_none() {
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
for idx in new_fd + 1..self.table.slots_len() {
|
||||
if self.table.get(idx).is_none() {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
self.table.slots_len()
|
||||
};
|
||||
|
||||
let min_free_fd = get_min_free_fd();
|
||||
self.table.put_at(min_free_fd, entry);
|
||||
Ok(min_free_fd as FileDescripter)
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, item: Arc<dyn FileLike>) -> FileDescripter {
|
||||
let entry = FileTableEntry::new(item, false);
|
||||
self.table.put(entry) as FileDescripter
|
||||
}
|
||||
|
||||
pub fn insert_at(
|
||||
&mut self,
|
||||
fd: FileDescripter,
|
||||
item: Arc<dyn FileLike>,
|
||||
) -> Option<Arc<dyn FileLike>> {
|
||||
let entry = FileTableEntry::new(item, false);
|
||||
let entry = self.table.put_at(fd as usize, entry);
|
||||
if entry.is_some() {
|
||||
let events = FdEvents::Close(fd);
|
||||
self.notify_fd_events(&events);
|
||||
entry.as_ref().unwrap().notify_fd_events(&events);
|
||||
}
|
||||
entry.map(|e| e.file)
|
||||
}
|
||||
|
||||
pub fn close_file(&mut self, fd: FileDescripter) -> Option<Arc<dyn FileLike>> {
|
||||
let entry = self.table.remove(fd as usize);
|
||||
if entry.is_some() {
|
||||
let events = FdEvents::Close(fd);
|
||||
self.notify_fd_events(&events);
|
||||
entry.as_ref().unwrap().notify_fd_events(&events);
|
||||
}
|
||||
entry.map(|e| e.file)
|
||||
}
|
||||
|
||||
pub fn close_all(&mut self) -> Vec<Arc<dyn FileLike>> {
|
||||
let mut closed_files = Vec::new();
|
||||
let closed_fds: Vec<FileDescripter> = self
|
||||
.table
|
||||
.idxes_and_items()
|
||||
.map(|(idx, _)| idx as FileDescripter)
|
||||
.collect();
|
||||
for fd in closed_fds {
|
||||
let entry = self.table.remove(fd as usize).unwrap();
|
||||
let events = FdEvents::Close(fd);
|
||||
self.notify_fd_events(&events);
|
||||
entry.notify_fd_events(&events);
|
||||
closed_files.push(entry.file);
|
||||
}
|
||||
closed_files
|
||||
}
|
||||
|
||||
pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc<dyn FileLike>> {
|
||||
self.table
|
||||
.get(fd as usize)
|
||||
.map(|entry| &entry.file)
|
||||
.ok_or(Error::with_message(Errno::EBADF, "fd not exits"))
|
||||
}
|
||||
|
||||
pub fn get_socket(&self, sockfd: FileDescripter) -> Result<Arc<dyn Socket>> {
|
||||
let file_like = self.get_file(sockfd)?.clone();
|
||||
file_like
|
||||
.as_socket()
|
||||
.ok_or_else(|| Error::with_message(Errno::ENOTSOCK, "the fd is not a socket"))
|
||||
}
|
||||
|
||||
pub fn get_entry(&self, fd: FileDescripter) -> Result<&FileTableEntry> {
|
||||
self.table
|
||||
.get(fd as usize)
|
||||
.ok_or(Error::with_message(Errno::EBADF, "fd not exits"))
|
||||
}
|
||||
|
||||
pub fn fds_and_files(&self) -> impl Iterator<Item = (FileDescripter, &'_ Arc<dyn FileLike>)> {
|
||||
self.table
|
||||
.idxes_and_items()
|
||||
.map(|(idx, entry)| (idx as FileDescripter, &entry.file))
|
||||
}
|
||||
|
||||
pub fn register_observer(&self, observer: Weak<dyn Observer<FdEvents>>) {
|
||||
self.subject.register_observer(observer, ());
|
||||
}
|
||||
|
||||
pub fn unregister_observer(&self, observer: &Weak<dyn Observer<FdEvents>>) {
|
||||
self.subject.unregister_observer(observer);
|
||||
}
|
||||
|
||||
fn notify_fd_events(&self, events: &FdEvents) {
|
||||
self.subject.notify_observers(events);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FileTable {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
table: self.table.clone(),
|
||||
subject: Subject::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for FileTable {
|
||||
fn drop(&mut self) {
|
||||
let events = FdEvents::DropFileTable;
|
||||
self.subject.notify_observers(&events);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum FdEvents {
|
||||
Close(FileDescripter),
|
||||
DropFileTable,
|
||||
}
|
||||
|
||||
impl Events for FdEvents {}
|
||||
|
||||
pub struct FileTableEntry {
|
||||
file: Arc<dyn FileLike>,
|
||||
close_on_exec: Cell<bool>,
|
||||
subject: Subject<FdEvents>,
|
||||
}
|
||||
|
||||
impl FileTableEntry {
|
||||
pub fn new(file: Arc<dyn FileLike>, close_on_exec: bool) -> Self {
|
||||
Self {
|
||||
file,
|
||||
close_on_exec: Cell::new(close_on_exec),
|
||||
subject: Subject::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(&self) -> &Arc<dyn FileLike> {
|
||||
&self.file
|
||||
}
|
||||
|
||||
pub fn register_observer(&self, epoll: Weak<dyn Observer<FdEvents>>) {
|
||||
self.subject.register_observer(epoll, ());
|
||||
}
|
||||
|
||||
pub fn unregister_observer(&self, epoll: &Weak<dyn Observer<FdEvents>>) {
|
||||
self.subject.unregister_observer(epoll);
|
||||
}
|
||||
|
||||
pub fn notify_fd_events(&self, events: &FdEvents) {
|
||||
self.subject.notify_observers(events);
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for FileTableEntry {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
file: self.file.clone(),
|
||||
close_on_exec: self.close_on_exec.clone(),
|
||||
subject: Subject::new(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,402 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
use alloc::str;
|
||||
|
||||
use super::file_table::FileDescripter;
|
||||
use super::inode_handle::InodeHandle;
|
||||
use super::rootfs::root_mount;
|
||||
use super::utils::{
|
||||
AccessMode, CreationFlags, Dentry, InodeMode, InodeType, StatusFlags, PATH_MAX, SYMLINKS_MAX,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FsResolver {
|
||||
root: Arc<Dentry>,
|
||||
cwd: Arc<Dentry>,
|
||||
}
|
||||
|
||||
impl Clone for FsResolver {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
root: self.root.clone(),
|
||||
cwd: self.cwd.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FsResolver {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl FsResolver {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
root: root_mount().root_dentry().clone(),
|
||||
cwd: root_mount().root_dentry().clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the root directory
|
||||
pub fn root(&self) -> &Arc<Dentry> {
|
||||
&self.root
|
||||
}
|
||||
|
||||
/// Get the current working directory
|
||||
pub fn cwd(&self) -> &Arc<Dentry> {
|
||||
&self.cwd
|
||||
}
|
||||
|
||||
/// Set the current working directory.
|
||||
pub fn set_cwd(&mut self, dentry: Arc<Dentry>) {
|
||||
self.cwd = dentry;
|
||||
}
|
||||
|
||||
/// Open or create a file inode handler.
|
||||
pub fn open(&self, path: &FsPath, flags: u32, mode: u16) -> Result<InodeHandle> {
|
||||
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||
let status_flags = StatusFlags::from_bits_truncate(flags);
|
||||
let access_mode = AccessMode::from_u32(flags)?;
|
||||
let inode_mode = InodeMode::from_bits_truncate(mode);
|
||||
|
||||
let follow_tail_link = !(creation_flags.contains(CreationFlags::O_NOFOLLOW)
|
||||
|| creation_flags.contains(CreationFlags::O_CREAT)
|
||||
&& creation_flags.contains(CreationFlags::O_EXCL));
|
||||
let dentry = match self.lookup_inner(path, follow_tail_link) {
|
||||
Ok(dentry) => {
|
||||
let inode = dentry.inode();
|
||||
if inode.type_() == InodeType::SymLink
|
||||
&& creation_flags.contains(CreationFlags::O_NOFOLLOW)
|
||||
&& !status_flags.contains(StatusFlags::O_PATH)
|
||||
{
|
||||
return_errno_with_message!(Errno::ELOOP, "file is a symlink");
|
||||
}
|
||||
if creation_flags.contains(CreationFlags::O_CREAT)
|
||||
&& creation_flags.contains(CreationFlags::O_EXCL)
|
||||
{
|
||||
return_errno_with_message!(Errno::EEXIST, "file exists");
|
||||
}
|
||||
if creation_flags.contains(CreationFlags::O_DIRECTORY)
|
||||
&& inode.type_() != InodeType::Dir
|
||||
{
|
||||
return_errno_with_message!(
|
||||
Errno::ENOTDIR,
|
||||
"O_DIRECTORY is specified but file is not a directory"
|
||||
);
|
||||
}
|
||||
dentry
|
||||
}
|
||||
Err(e)
|
||||
if e.error() == Errno::ENOENT
|
||||
&& creation_flags.contains(CreationFlags::O_CREAT) =>
|
||||
{
|
||||
if creation_flags.contains(CreationFlags::O_DIRECTORY) {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "cannot create directory");
|
||||
}
|
||||
let (dir_dentry, file_name) =
|
||||
self.lookup_dir_and_base_name_inner(path, follow_tail_link)?;
|
||||
if file_name.ends_with('/') {
|
||||
return_errno_with_message!(Errno::EISDIR, "path refers to a directory");
|
||||
}
|
||||
if !dir_dentry.inode_mode().is_writable() {
|
||||
return_errno_with_message!(Errno::EACCES, "file cannot be created");
|
||||
}
|
||||
dir_dentry.create(&file_name, InodeType::File, inode_mode)?
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let inode_handle = InodeHandle::new(dentry, access_mode, status_flags)?;
|
||||
Ok(inode_handle)
|
||||
}
|
||||
|
||||
/// Lookup dentry according to FsPath, always follow symlinks
|
||||
pub fn lookup(&self, path: &FsPath) -> Result<Arc<Dentry>> {
|
||||
self.lookup_inner(path, true)
|
||||
}
|
||||
|
||||
/// Lookup dentry according to FsPath, do not follow it if last component is a symlink
|
||||
pub fn lookup_no_follow(&self, path: &FsPath) -> Result<Arc<Dentry>> {
|
||||
self.lookup_inner(path, false)
|
||||
}
|
||||
|
||||
fn lookup_inner(&self, path: &FsPath, follow_tail_link: bool) -> Result<Arc<Dentry>> {
|
||||
let dentry = match path.inner {
|
||||
FsPathInner::Absolute(path) => {
|
||||
self.lookup_from_parent(&self.root, path.trim_start_matches('/'), follow_tail_link)?
|
||||
}
|
||||
FsPathInner::CwdRelative(path) => {
|
||||
self.lookup_from_parent(&self.cwd, path, follow_tail_link)?
|
||||
}
|
||||
FsPathInner::Cwd => self.cwd.clone(),
|
||||
FsPathInner::FdRelative(fd, path) => {
|
||||
let parent = self.lookup_from_fd(fd)?;
|
||||
self.lookup_from_parent(&parent, path, follow_tail_link)?
|
||||
}
|
||||
FsPathInner::Fd(fd) => self.lookup_from_fd(fd)?,
|
||||
};
|
||||
|
||||
Ok(dentry)
|
||||
}
|
||||
|
||||
/// Lookup dentry from parent
|
||||
///
|
||||
/// The length of `path` cannot exceed PATH_MAX.
|
||||
/// If `path` ends with `/`, then the returned inode must be a directory inode.
|
||||
///
|
||||
/// While looking up the dentry, symbolic links will be followed for
|
||||
/// at most `SYMLINKS_MAX` times.
|
||||
///
|
||||
/// If `follow_tail_link` is true and the trailing component is a symlink,
|
||||
/// it will be followed.
|
||||
/// Symlinks in earlier components of the path will always be followed.
|
||||
fn lookup_from_parent(
|
||||
&self,
|
||||
parent: &Arc<Dentry>,
|
||||
relative_path: &str,
|
||||
follow_tail_link: bool,
|
||||
) -> Result<Arc<Dentry>> {
|
||||
debug_assert!(!relative_path.starts_with('/'));
|
||||
|
||||
if relative_path.len() > PATH_MAX {
|
||||
return_errno_with_message!(Errno::ENAMETOOLONG, "path is too long");
|
||||
}
|
||||
|
||||
// To handle symlinks
|
||||
let mut link_path = String::new();
|
||||
let mut follows = 0;
|
||||
|
||||
// Initialize the first dentry and the relative path
|
||||
let (mut dentry, mut relative_path) = (parent.clone(), relative_path);
|
||||
|
||||
while !relative_path.is_empty() {
|
||||
let (next_name, path_remain, must_be_dir) =
|
||||
if let Some((prefix, suffix)) = relative_path.split_once('/') {
|
||||
let suffix = suffix.trim_start_matches('/');
|
||||
(prefix, suffix, true)
|
||||
} else {
|
||||
(relative_path, "", false)
|
||||
};
|
||||
|
||||
// Iterate next dentry
|
||||
let next_dentry = dentry.lookup(next_name)?;
|
||||
let next_type = next_dentry.inode_type();
|
||||
let next_is_tail = path_remain.is_empty();
|
||||
|
||||
// If next inode is a symlink, follow symlinks at most `SYMLINKS_MAX` times.
|
||||
if next_type == InodeType::SymLink && (follow_tail_link || !next_is_tail) {
|
||||
if follows >= SYMLINKS_MAX {
|
||||
return_errno_with_message!(Errno::ELOOP, "too many symlinks");
|
||||
}
|
||||
let link_path_remain = {
|
||||
let mut tmp_link_path = next_dentry.inode().read_link()?;
|
||||
if tmp_link_path.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "empty symlink");
|
||||
}
|
||||
if !path_remain.is_empty() {
|
||||
tmp_link_path += "/";
|
||||
tmp_link_path += path_remain;
|
||||
} else if must_be_dir {
|
||||
tmp_link_path += "/";
|
||||
}
|
||||
tmp_link_path
|
||||
};
|
||||
|
||||
// Change the dentry and relative path according to symlink
|
||||
if link_path_remain.starts_with('/') {
|
||||
dentry = self.root.clone();
|
||||
}
|
||||
link_path.clear();
|
||||
link_path.push_str(link_path_remain.trim_start_matches('/'));
|
||||
relative_path = &link_path;
|
||||
follows += 1;
|
||||
} else {
|
||||
// If path ends with `/`, the inode must be a directory
|
||||
if must_be_dir && next_type != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "inode is not dir");
|
||||
}
|
||||
dentry = next_dentry;
|
||||
relative_path = path_remain;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(dentry)
|
||||
}
|
||||
|
||||
/// Lookup dentry from the giving fd
|
||||
pub fn lookup_from_fd(&self, fd: FileDescripter) -> Result<Arc<Dentry>> {
|
||||
let current = current!();
|
||||
let file_table = current.file_table().lock();
|
||||
let inode_handle = file_table
|
||||
.get_file(fd)?
|
||||
.downcast_ref::<InodeHandle>()
|
||||
.ok_or(Error::with_message(Errno::EBADF, "not inode"))?;
|
||||
Ok(inode_handle.dentry().clone())
|
||||
}
|
||||
|
||||
/// Lookup the dir dentry and base file name of the giving path.
|
||||
///
|
||||
/// If the last component is a symlink, do not deference it
|
||||
pub fn lookup_dir_and_base_name(&self, path: &FsPath) -> Result<(Arc<Dentry>, String)> {
|
||||
self.lookup_dir_and_base_name_inner(path, false)
|
||||
}
|
||||
|
||||
fn lookup_dir_and_base_name_inner(
|
||||
&self,
|
||||
path: &FsPath,
|
||||
follow_tail_link: bool,
|
||||
) -> Result<(Arc<Dentry>, String)> {
|
||||
let (mut dir_dentry, mut base_name) = match path.inner {
|
||||
FsPathInner::Absolute(path) => {
|
||||
let (dir, file_name) = split_path(path);
|
||||
(
|
||||
self.lookup_from_parent(&self.root, dir.trim_start_matches('/'), true)?,
|
||||
String::from(file_name),
|
||||
)
|
||||
}
|
||||
FsPathInner::CwdRelative(path) => {
|
||||
let (dir, file_name) = split_path(path);
|
||||
(
|
||||
self.lookup_from_parent(&self.cwd, dir, true)?,
|
||||
String::from(file_name),
|
||||
)
|
||||
}
|
||||
FsPathInner::FdRelative(fd, path) => {
|
||||
let (dir, file_name) = split_path(path);
|
||||
let parent = self.lookup_from_fd(fd)?;
|
||||
(
|
||||
self.lookup_from_parent(&parent, dir, true)?,
|
||||
String::from(file_name),
|
||||
)
|
||||
}
|
||||
_ => return_errno!(Errno::ENOENT),
|
||||
};
|
||||
if !follow_tail_link {
|
||||
return Ok((dir_dentry, base_name));
|
||||
}
|
||||
|
||||
// Dereference the tail symlinks if needed
|
||||
loop {
|
||||
match dir_dentry.lookup(base_name.trim_end_matches('/')) {
|
||||
Ok(dentry) if dentry.inode_type() == InodeType::SymLink => {
|
||||
let link = {
|
||||
let mut link = dentry.inode().read_link()?;
|
||||
if link.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "invalid symlink");
|
||||
}
|
||||
if base_name.ends_with('/') && !link.ends_with('/') {
|
||||
link += "/";
|
||||
}
|
||||
link
|
||||
};
|
||||
let (dir, file_name) = split_path(&link);
|
||||
if dir.starts_with('/') {
|
||||
dir_dentry =
|
||||
self.lookup_from_parent(&self.root, dir.trim_start_matches('/'), true)?;
|
||||
base_name = String::from(file_name);
|
||||
} else {
|
||||
dir_dentry = self.lookup_from_parent(&dir_dentry, dir, true)?;
|
||||
base_name = String::from(file_name);
|
||||
}
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok((dir_dentry, base_name))
|
||||
}
|
||||
}
|
||||
|
||||
pub const AT_FDCWD: FileDescripter = -100;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FsPath<'a> {
|
||||
inner: FsPathInner<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum FsPathInner<'a> {
|
||||
// absolute path
|
||||
Absolute(&'a str),
|
||||
// path is relative to Cwd
|
||||
CwdRelative(&'a str),
|
||||
// Cwd
|
||||
Cwd,
|
||||
// path is relative to DirFd
|
||||
FdRelative(FileDescripter, &'a str),
|
||||
// Fd
|
||||
Fd(FileDescripter),
|
||||
}
|
||||
|
||||
impl<'a> FsPath<'a> {
|
||||
pub fn new(dirfd: FileDescripter, path: &'a str) -> Result<Self> {
|
||||
if path.len() > PATH_MAX {
|
||||
return_errno_with_message!(Errno::ENAMETOOLONG, "path name too long");
|
||||
}
|
||||
|
||||
let fs_path_inner = if path.starts_with('/') {
|
||||
FsPathInner::Absolute(path)
|
||||
} else if dirfd >= 0 {
|
||||
if path.is_empty() {
|
||||
FsPathInner::Fd(dirfd)
|
||||
} else {
|
||||
FsPathInner::FdRelative(dirfd, path)
|
||||
}
|
||||
} else if dirfd == AT_FDCWD {
|
||||
if path.is_empty() {
|
||||
FsPathInner::Cwd
|
||||
} else {
|
||||
FsPathInner::CwdRelative(path)
|
||||
}
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EBADF, "invalid dirfd number");
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
inner: fs_path_inner,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for FsPath<'a> {
|
||||
type Error = crate::error::Error;
|
||||
|
||||
fn try_from(path: &'a str) -> Result<FsPath> {
|
||||
if path.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "path is an empty string");
|
||||
}
|
||||
FsPath::new(AT_FDCWD, path)
|
||||
}
|
||||
}
|
||||
|
||||
/// Split a `path` to (`dir_path`, `file_name`).
|
||||
///
|
||||
/// The `dir_path` must be a directory.
|
||||
///
|
||||
/// The `file_name` is the last component. It can be suffixed by "/".
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// The path "/dir/file/" will be split to ("/dir", "file/").
|
||||
pub fn split_path(path: &str) -> (&str, &str) {
|
||||
let file_name = path
|
||||
.split_inclusive('/')
|
||||
.filter(|&x| x != "/")
|
||||
.last()
|
||||
.unwrap_or(".");
|
||||
|
||||
let mut split = path.trim_end_matches('/').rsplitn(2, '/');
|
||||
let dir_path = if split.next().unwrap().is_empty() {
|
||||
"/"
|
||||
} else {
|
||||
let mut dir = split.next().unwrap_or(".").trim_end_matches('/');
|
||||
if dir.is_empty() {
|
||||
dir = "/";
|
||||
}
|
||||
dir
|
||||
};
|
||||
|
||||
(dir_path, file_name)
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
use aster_rights::{Rights, TRights};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl InodeHandle<Rights> {
|
||||
pub fn new(
|
||||
dentry: Arc<Dentry>,
|
||||
access_mode: AccessMode,
|
||||
status_flags: StatusFlags,
|
||||
) -> Result<Self> {
|
||||
let inode = dentry.inode();
|
||||
if access_mode.is_readable() && !inode.mode().is_readable() {
|
||||
return_errno_with_message!(Errno::EACCES, "File is not readable");
|
||||
}
|
||||
if access_mode.is_writable() && !inode.mode().is_writable() {
|
||||
return_errno_with_message!(Errno::EACCES, "File is not writable");
|
||||
}
|
||||
if access_mode.is_writable() && inode.type_() == InodeType::Dir {
|
||||
return_errno_with_message!(Errno::EISDIR, "Directory cannot open to write");
|
||||
}
|
||||
|
||||
let file_io = if let Some(device) = inode.as_device() {
|
||||
device.open()?
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let inner = Arc::new(InodeHandle_ {
|
||||
dentry,
|
||||
file_io,
|
||||
offset: Mutex::new(0),
|
||||
access_mode,
|
||||
status_flags: AtomicU32::new(status_flags.bits()),
|
||||
});
|
||||
Ok(Self(inner, Rights::from(access_mode)))
|
||||
}
|
||||
|
||||
pub fn to_static<R1: TRights>(self) -> Result<InodeHandle<R1>> {
|
||||
let rights = Rights::from_bits(R1::BITS).ok_or(Error::new(Errno::EBADF))?;
|
||||
if !self.1.contains(rights) {
|
||||
return_errno_with_message!(Errno::EBADF, "check rights failed");
|
||||
}
|
||||
Ok(InodeHandle(self.0, R1::new()))
|
||||
}
|
||||
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
if !self.1.contains(Rights::READ) {
|
||||
return_errno_with_message!(Errno::EBADF, "File is not readable");
|
||||
}
|
||||
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
|
||||
pub fn readdir(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
if !self.1.contains(Rights::READ) {
|
||||
return_errno_with_message!(Errno::EBADF, "File is not readable");
|
||||
}
|
||||
self.0.readdir(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for InodeHandle<Rights> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone(), self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for InodeHandle<Rights> {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
if !self.1.contains(Rights::READ) {
|
||||
return_errno_with_message!(Errno::EBADF, "File is not readable");
|
||||
}
|
||||
self.0.read(buf)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
if !self.1.contains(Rights::WRITE) {
|
||||
return_errno_with_message!(Errno::EBADF, "File is not writable");
|
||||
}
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.0.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
self.0.ioctl(cmd, arg)
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()> {
|
||||
if !self.1.contains(Rights::WRITE) {
|
||||
return_errno_with_message!(Errno::EINVAL, "File is not writable");
|
||||
}
|
||||
self.0.resize(new_size)
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.dentry().inode_metadata()
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
self.0.status_flags()
|
||||
}
|
||||
|
||||
fn set_status_flags(&self, new_status_flags: StatusFlags) -> Result<()> {
|
||||
self.0.set_status_flags(new_status_flags);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn access_mode(&self) -> AccessMode {
|
||||
self.0.access_mode()
|
||||
}
|
||||
|
||||
fn seek(&self, seek_from: SeekFrom) -> Result<usize> {
|
||||
self.0.seek(seek_from)
|
||||
}
|
||||
|
||||
fn clean_for_close(&self) -> Result<()> {
|
||||
// Close does not guarantee that the data has been successfully saved to disk.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn as_device(&self) -> Option<Arc<dyn Device>> {
|
||||
self.dentry().inode().as_device()
|
||||
}
|
||||
}
|
@ -1,196 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Opend Inode-backed File Handle
|
||||
|
||||
mod dyn_cap;
|
||||
mod static_cap;
|
||||
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::Device;
|
||||
use crate::fs::file_handle::FileLike;
|
||||
use crate::fs::utils::{
|
||||
AccessMode, Dentry, DirentVisitor, InodeType, IoctlCmd, Metadata, SeekFrom, StatusFlags,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
use aster_rights::Rights;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
|
||||
|
||||
struct InodeHandle_ {
|
||||
dentry: Arc<Dentry>,
|
||||
/// `file_io` is Similar to `file_private` field in `file` structure in linux. If
|
||||
/// `file_io` is Some, typical file operations including `read`, `write`, `poll`,
|
||||
/// `ioctl` will be provided by `file_io`, instead of `dentry`.
|
||||
file_io: Option<Arc<dyn FileIo>>,
|
||||
offset: Mutex<usize>,
|
||||
access_mode: AccessMode,
|
||||
status_flags: AtomicU32,
|
||||
}
|
||||
|
||||
impl InodeHandle_ {
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
|
||||
if let Some(ref file_io) = self.file_io {
|
||||
return file_io.read(buf);
|
||||
}
|
||||
|
||||
let len = if self.status_flags().contains(StatusFlags::O_DIRECT) {
|
||||
self.dentry.inode().read_direct_at(*offset, buf)?
|
||||
} else {
|
||||
self.dentry.inode().read_at(*offset, buf)?
|
||||
};
|
||||
|
||||
*offset += len;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
|
||||
if let Some(ref file_io) = self.file_io {
|
||||
return file_io.write(buf);
|
||||
}
|
||||
|
||||
if self.status_flags().contains(StatusFlags::O_APPEND) {
|
||||
*offset = self.dentry.inode_size();
|
||||
}
|
||||
let len = if self.status_flags().contains(StatusFlags::O_DIRECT) {
|
||||
self.dentry.inode().write_direct_at(*offset, buf)?
|
||||
} else {
|
||||
self.dentry.inode().write_at(*offset, buf)?
|
||||
};
|
||||
|
||||
*offset += len;
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
if self.file_io.is_some() {
|
||||
return_errno_with_message!(Errno::EINVAL, "file io does not support read to end");
|
||||
}
|
||||
|
||||
let len = if self.status_flags().contains(StatusFlags::O_DIRECT) {
|
||||
self.dentry.inode().read_direct_all(buf)?
|
||||
} else {
|
||||
self.dentry.inode().read_all(buf)?
|
||||
};
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn seek(&self, pos: SeekFrom) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
let new_offset: isize = match pos {
|
||||
SeekFrom::Start(off /* as usize */) => {
|
||||
if off > isize::max_value() as usize {
|
||||
return_errno_with_message!(Errno::EINVAL, "file offset is too large");
|
||||
}
|
||||
off as isize
|
||||
}
|
||||
SeekFrom::End(off /* as isize */) => {
|
||||
let file_size = self.dentry.inode_size() as isize;
|
||||
assert!(file_size >= 0);
|
||||
file_size
|
||||
.checked_add(off)
|
||||
.ok_or_else(|| Error::with_message(Errno::EOVERFLOW, "file offset overflow"))?
|
||||
}
|
||||
SeekFrom::Current(off /* as isize */) => (*offset as isize)
|
||||
.checked_add(off)
|
||||
.ok_or_else(|| Error::with_message(Errno::EOVERFLOW, "file offset overflow"))?,
|
||||
};
|
||||
if new_offset < 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "file offset must not be negative");
|
||||
}
|
||||
// Invariant: 0 <= new_offset <= isize::max_value()
|
||||
let new_offset = new_offset as usize;
|
||||
*offset = new_offset;
|
||||
Ok(new_offset)
|
||||
}
|
||||
|
||||
pub fn offset(&self) -> usize {
|
||||
let offset = self.offset.lock();
|
||||
*offset
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.dentry.inode_size()
|
||||
}
|
||||
|
||||
pub fn resize(&self, new_size: usize) -> Result<()> {
|
||||
if self.status_flags().contains(StatusFlags::O_APPEND) {
|
||||
return_errno_with_message!(Errno::EPERM, "can not resize append-only file");
|
||||
}
|
||||
self.dentry.set_inode_size(new_size)
|
||||
}
|
||||
|
||||
pub fn access_mode(&self) -> AccessMode {
|
||||
self.access_mode
|
||||
}
|
||||
|
||||
pub fn status_flags(&self) -> StatusFlags {
|
||||
let bits = self.status_flags.load(Ordering::Relaxed);
|
||||
StatusFlags::from_bits(bits).unwrap()
|
||||
}
|
||||
|
||||
pub fn set_status_flags(&self, new_status_flags: StatusFlags) {
|
||||
self.status_flags
|
||||
.store(new_status_flags.bits(), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn readdir(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
let mut offset = self.offset.lock();
|
||||
let read_cnt = self.dentry.inode().readdir_at(*offset, visitor)?;
|
||||
*offset += read_cnt;
|
||||
Ok(read_cnt)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
if let Some(ref file_io) = self.file_io {
|
||||
return file_io.poll(mask, poller);
|
||||
}
|
||||
|
||||
self.dentry.inode().poll(mask, poller)
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
if let Some(ref file_io) = self.file_io {
|
||||
return file_io.ioctl(cmd, arg);
|
||||
}
|
||||
|
||||
self.dentry.inode().ioctl(cmd, arg)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for InodeHandle_ {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("InodeHandle_")
|
||||
.field("dentry", &self.dentry)
|
||||
.field("offset", &self.offset())
|
||||
.field("access_mode", &self.access_mode())
|
||||
.field("status_flags", &self.status_flags())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Methods for both dyn and static
|
||||
impl<R> InodeHandle<R> {
|
||||
pub fn dentry(&self) -> &Arc<Dentry> {
|
||||
&self.0.dentry
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FileIo: Send + Sync + 'static {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize>;
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize>;
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents;
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
return_errno_with_message!(Errno::EINVAL, "ioctl is not supported");
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
use aster_rights::{Read, TRightSet, TRights, Write};
|
||||
use aster_rights_proc::require;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl<R: TRights> InodeHandle<TRightSet<R>> {
|
||||
#[require(R > Read)]
|
||||
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.0.read(buf)
|
||||
}
|
||||
|
||||
#[require(R > Read)]
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
self.0.read_to_end(buf)
|
||||
}
|
||||
|
||||
#[require(R > Write)]
|
||||
pub fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
self.0.write(buf)
|
||||
}
|
||||
|
||||
#[require(R > Read)]
|
||||
pub fn readdir(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
self.0.readdir(visitor)
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
pub mod device;
|
||||
pub mod devpts;
|
||||
pub mod epoll;
|
||||
pub mod ext2;
|
||||
pub mod file_handle;
|
||||
pub mod file_table;
|
||||
pub mod fs_resolver;
|
||||
pub mod inode_handle;
|
||||
pub mod pipe;
|
||||
pub mod procfs;
|
||||
pub mod ramfs;
|
||||
pub mod rootfs;
|
||||
pub mod utils;
|
||||
|
||||
use crate::fs::{ext2::Ext2, fs_resolver::FsPath};
|
||||
use crate::prelude::*;
|
||||
use crate::thread::kernel_thread::KernelThreadExt;
|
||||
use aster_virtio::device::block::device::BlockDevice as VirtIoBlockDevice;
|
||||
use aster_virtio::device::block::DEVICE_NAME as VIRTIO_BLOCK_NAME;
|
||||
|
||||
pub fn lazy_init() {
|
||||
let block_device = aster_block::get_device(VIRTIO_BLOCK_NAME).unwrap();
|
||||
let cloned_block_device = block_device.clone();
|
||||
|
||||
let task_fn = move || {
|
||||
info!("spawn the virt-io-block thread");
|
||||
let virtio_block_device = block_device.downcast_ref::<VirtIoBlockDevice>().unwrap();
|
||||
loop {
|
||||
virtio_block_device.handle_requests();
|
||||
}
|
||||
};
|
||||
crate::Thread::spawn_kernel_thread(crate::ThreadOptions::new(task_fn));
|
||||
|
||||
let ext2_fs = Ext2::open(cloned_block_device).unwrap();
|
||||
let target_path = FsPath::try_from("/ext2").unwrap();
|
||||
println!("[kernel] Mount Ext2 fs at {:?} ", target_path);
|
||||
self::rootfs::mount_fs_at(ext2_fs, &target_path).unwrap();
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::{IoEvents, Observer};
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
use super::file_handle::FileLike;
|
||||
use super::utils::{AccessMode, Consumer, InodeMode, InodeType, Metadata, Producer, StatusFlags};
|
||||
|
||||
pub struct PipeReader {
|
||||
consumer: Consumer<u8>,
|
||||
}
|
||||
|
||||
impl PipeReader {
|
||||
pub fn new(consumer: Consumer<u8>) -> Self {
|
||||
Self { consumer }
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for PipeReader {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.consumer.read(buf)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.consumer.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
self.consumer.status_flags()
|
||||
}
|
||||
|
||||
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
self.consumer.set_status_flags(new_flags)
|
||||
}
|
||||
|
||||
fn access_mode(&self) -> AccessMode {
|
||||
AccessMode::O_RDONLY
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
dev: 0,
|
||||
ino: 0,
|
||||
size: 0,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::NamedPipe,
|
||||
mode: InodeMode::from_bits_truncate(0o400),
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn register_observer(
|
||||
&self,
|
||||
observer: Weak<dyn Observer<IoEvents>>,
|
||||
mask: IoEvents,
|
||||
) -> Result<()> {
|
||||
self.consumer.register_observer(observer, mask)
|
||||
}
|
||||
|
||||
fn unregister_observer(
|
||||
&self,
|
||||
observer: &Weak<dyn Observer<IoEvents>>,
|
||||
) -> Result<Weak<dyn Observer<IoEvents>>> {
|
||||
self.consumer.unregister_observer(observer)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PipeWriter {
|
||||
producer: Producer<u8>,
|
||||
}
|
||||
|
||||
impl PipeWriter {
|
||||
pub fn new(producer: Producer<u8>) -> Self {
|
||||
Self { producer }
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for PipeWriter {
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
self.producer.write(buf)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.producer.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
self.producer.status_flags()
|
||||
}
|
||||
|
||||
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
self.producer.set_status_flags(new_flags)
|
||||
}
|
||||
|
||||
fn access_mode(&self) -> AccessMode {
|
||||
AccessMode::O_WRONLY
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
Metadata {
|
||||
dev: 0,
|
||||
ino: 0,
|
||||
size: 0,
|
||||
blk_size: 0,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::NamedPipe,
|
||||
mode: InodeMode::from_bits_truncate(0o200),
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn register_observer(
|
||||
&self,
|
||||
observer: Weak<dyn Observer<IoEvents>>,
|
||||
mask: IoEvents,
|
||||
) -> Result<()> {
|
||||
self.producer.register_observer(observer, mask)
|
||||
}
|
||||
|
||||
fn unregister_observer(
|
||||
&self,
|
||||
observer: &Weak<dyn Observer<IoEvents>>,
|
||||
) -> Result<Weak<dyn Observer<IoEvents>>> {
|
||||
self.producer.unregister_observer(observer)
|
||||
}
|
||||
}
|
||||
|
||||
fn should_io_return(res: &Result<usize>, is_nonblocking: bool) -> bool {
|
||||
if is_nonblocking {
|
||||
return true;
|
||||
}
|
||||
match res {
|
||||
Ok(_) => true,
|
||||
Err(e) if e.error() == Errno::EAGAIN => false,
|
||||
Err(_) => true,
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
use crate::events::Observer;
|
||||
use crate::fs::utils::{DirEntryVecExt, FileSystem, FsFlags, Inode, SuperBlock, NAME_MAX};
|
||||
use crate::prelude::*;
|
||||
use crate::process::{process_table, process_table::PidEvent, Pid};
|
||||
|
||||
use self::pid::PidDirOps;
|
||||
use self::self_::SelfSymOps;
|
||||
use self::template::{DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps};
|
||||
|
||||
mod pid;
|
||||
mod self_;
|
||||
mod template;
|
||||
|
||||
/// Magic number.
|
||||
const PROC_MAGIC: u64 = 0x9fa0;
|
||||
/// Root Inode ID.
|
||||
const PROC_ROOT_INO: usize = 1;
|
||||
/// Block size.
|
||||
const BLOCK_SIZE: usize = 1024;
|
||||
|
||||
pub struct ProcFS {
|
||||
sb: RwLock<SuperBlock>,
|
||||
root: RwLock<Option<Arc<dyn Inode>>>,
|
||||
inode_allocator: AtomicUsize,
|
||||
}
|
||||
|
||||
impl ProcFS {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let procfs = {
|
||||
let sb = SuperBlock::new(PROC_MAGIC, BLOCK_SIZE, NAME_MAX);
|
||||
Arc::new(Self {
|
||||
sb: RwLock::new(sb),
|
||||
root: RwLock::new(None),
|
||||
inode_allocator: AtomicUsize::new(PROC_ROOT_INO),
|
||||
})
|
||||
};
|
||||
|
||||
let root = RootDirOps::new_inode(&procfs);
|
||||
*procfs.root.write() = Some(root);
|
||||
procfs
|
||||
}
|
||||
|
||||
pub(in crate::fs::procfs) fn alloc_id(&self) -> usize {
|
||||
self.inode_allocator.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystem for ProcFS {
|
||||
fn sync(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn root_inode(&self) -> Arc<dyn Inode> {
|
||||
self.root.read().as_ref().unwrap().clone()
|
||||
}
|
||||
|
||||
fn sb(&self) -> SuperBlock {
|
||||
self.sb.read().clone()
|
||||
}
|
||||
|
||||
fn flags(&self) -> FsFlags {
|
||||
FsFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the inode at `/proc`.
|
||||
struct RootDirOps;
|
||||
|
||||
impl RootDirOps {
|
||||
pub fn new_inode(fs: &Arc<ProcFS>) -> Arc<dyn Inode> {
|
||||
let root_inode = ProcDirBuilder::new(Self).fs(fs.clone()).build().unwrap();
|
||||
let weak_ptr = Arc::downgrade(&root_inode);
|
||||
process_table::register_observer(weak_ptr);
|
||||
root_inode
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<PidEvent> for ProcDir<RootDirOps> {
|
||||
fn on_events(&self, events: &PidEvent) {
|
||||
let PidEvent::Exit(pid) = events;
|
||||
let mut cached_children = self.cached_children().write();
|
||||
cached_children.remove_entry_by_name(&pid.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
impl DirOps for RootDirOps {
|
||||
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
|
||||
let child = if name == "self" {
|
||||
SelfSymOps::new_inode(this_ptr.clone())
|
||||
} else if let Ok(pid) = name.parse::<Pid>() {
|
||||
let process_ref =
|
||||
process_table::get_process(&pid).ok_or_else(|| Error::new(Errno::ENOENT))?;
|
||||
PidDirOps::new_inode(process_ref, this_ptr.clone())
|
||||
} else {
|
||||
return_errno!(Errno::ENOENT);
|
||||
};
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
|
||||
let this = {
|
||||
let this = this_ptr.upgrade().unwrap();
|
||||
this.downcast_ref::<ProcDir<RootDirOps>>().unwrap().this()
|
||||
};
|
||||
let mut cached_children = this.cached_children().write();
|
||||
cached_children.put_entry_if_not_found("self", || SelfSymOps::new_inode(this_ptr.clone()));
|
||||
|
||||
let processes = process_table::get_all_processes();
|
||||
for process in processes {
|
||||
let pid = process.pid().to_string();
|
||||
cached_children.put_entry_if_not_found(&pid, || {
|
||||
PidDirOps::new_inode(process.clone(), this_ptr.clone())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/comm`.
|
||||
pub struct CommFileOps(Arc<Process>);
|
||||
|
||||
impl CommFileOps {
|
||||
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
ProcFileBuilder::new(Self(process_ref))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOps for CommFileOps {
|
||||
fn data(&self) -> Result<Vec<u8>> {
|
||||
let mut comm_output = {
|
||||
let exe_path = self.0.executable_path();
|
||||
let last_component = exe_path.rsplit('/').next().unwrap_or(&exe_path);
|
||||
let mut comm = last_component.as_bytes().to_vec();
|
||||
comm.push(b'\0');
|
||||
comm.truncate(TASK_COMM_LEN);
|
||||
comm
|
||||
};
|
||||
comm_output.push(b'\n');
|
||||
Ok(comm_output)
|
||||
}
|
||||
}
|
||||
|
||||
const TASK_COMM_LEN: usize = 16;
|
@ -1,21 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/exe`.
|
||||
pub struct ExeSymOps(Arc<Process>);
|
||||
|
||||
impl ExeSymOps {
|
||||
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
ProcSymBuilder::new(Self(process_ref))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl SymOps for ExeSymOps {
|
||||
fn read_link(&self) -> Result<String> {
|
||||
Ok(self.0.executable_path())
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::*;
|
||||
use crate::fs::file_handle::FileLike;
|
||||
use crate::fs::file_table::FileDescripter;
|
||||
use crate::fs::inode_handle::InodeHandle;
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/fd`.
|
||||
pub struct FdDirOps(Arc<Process>);
|
||||
|
||||
impl FdDirOps {
|
||||
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
let fd_inode = ProcDirBuilder::new(Self(process_ref.clone()))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap();
|
||||
let file_table = process_ref.file_table().lock();
|
||||
let weak_ptr = Arc::downgrade(&fd_inode);
|
||||
file_table.register_observer(weak_ptr);
|
||||
fd_inode
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<FdEvents> for ProcDir<FdDirOps> {
|
||||
fn on_events(&self, events: &FdEvents) {
|
||||
let fd_string = if let FdEvents::Close(fd) = events {
|
||||
fd.to_string()
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut cached_children = self.cached_children().write();
|
||||
cached_children.remove_entry_by_name(&fd_string);
|
||||
}
|
||||
}
|
||||
|
||||
impl DirOps for FdDirOps {
|
||||
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
|
||||
let file = {
|
||||
let fd = name
|
||||
.parse::<FileDescripter>()
|
||||
.map_err(|_| Error::new(Errno::ENOENT))?;
|
||||
let file_table = self.0.file_table().lock();
|
||||
file_table
|
||||
.get_file(fd)
|
||||
.map_err(|_| Error::new(Errno::ENOENT))?
|
||||
.clone()
|
||||
};
|
||||
Ok(FileSymOps::new_inode(file, this_ptr.clone()))
|
||||
}
|
||||
|
||||
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
|
||||
let this = {
|
||||
let this = this_ptr.upgrade().unwrap();
|
||||
this.downcast_ref::<ProcDir<FdDirOps>>().unwrap().this()
|
||||
};
|
||||
let file_table = self.0.file_table().lock();
|
||||
let mut cached_children = this.cached_children().write();
|
||||
for (fd, file) in file_table.fds_and_files() {
|
||||
cached_children.put_entry_if_not_found(&fd.to_string(), || {
|
||||
FileSymOps::new_inode(file.clone(), this_ptr.clone())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/fd/N`.
|
||||
struct FileSymOps(Arc<dyn FileLike>);
|
||||
|
||||
impl FileSymOps {
|
||||
pub fn new_inode(file: Arc<dyn FileLike>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
ProcSymBuilder::new(Self(file))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl SymOps for FileSymOps {
|
||||
fn read_link(&self) -> Result<String> {
|
||||
let path = if let Some(inode_handle) = self.0.downcast_ref::<InodeHandle>() {
|
||||
inode_handle.dentry().abs_path()
|
||||
} else {
|
||||
// TODO: get the real path for other FileLike object
|
||||
String::from("/dev/tty")
|
||||
};
|
||||
Ok(path)
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::Observer;
|
||||
use crate::fs::file_table::FdEvents;
|
||||
use crate::fs::utils::{DirEntryVecExt, Inode};
|
||||
use crate::prelude::*;
|
||||
use crate::process::Process;
|
||||
|
||||
use self::comm::CommFileOps;
|
||||
use self::exe::ExeSymOps;
|
||||
use self::fd::FdDirOps;
|
||||
use super::template::{
|
||||
DirOps, FileOps, ProcDir, ProcDirBuilder, ProcFileBuilder, ProcSymBuilder, SymOps,
|
||||
};
|
||||
|
||||
mod comm;
|
||||
mod exe;
|
||||
mod fd;
|
||||
|
||||
/// Represents the inode at `/proc/[pid]`.
|
||||
pub struct PidDirOps(Arc<Process>);
|
||||
|
||||
impl PidDirOps {
|
||||
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
let pid_inode = ProcDirBuilder::new(Self(process_ref.clone()))
|
||||
.parent(parent)
|
||||
// The pid directories must be volatile, because it is just associated with one process.
|
||||
.volatile()
|
||||
.build()
|
||||
.unwrap();
|
||||
let file_table = process_ref.file_table().lock();
|
||||
let weak_ptr = Arc::downgrade(&pid_inode);
|
||||
file_table.register_observer(weak_ptr);
|
||||
pid_inode
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<FdEvents> for ProcDir<PidDirOps> {
|
||||
fn on_events(&self, events: &FdEvents) {
|
||||
if let FdEvents::DropFileTable = events {
|
||||
let mut cached_children = self.cached_children().write();
|
||||
cached_children.remove_entry_by_name("fd");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DirOps for PidDirOps {
|
||||
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
|
||||
let inode = match name {
|
||||
"exe" => ExeSymOps::new_inode(self.0.clone(), this_ptr.clone()),
|
||||
"comm" => CommFileOps::new_inode(self.0.clone(), this_ptr.clone()),
|
||||
"fd" => FdDirOps::new_inode(self.0.clone(), this_ptr.clone()),
|
||||
_ => return_errno!(Errno::ENOENT),
|
||||
};
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
|
||||
let this = {
|
||||
let this = this_ptr.upgrade().unwrap();
|
||||
this.downcast_ref::<ProcDir<PidDirOps>>().unwrap().this()
|
||||
};
|
||||
let mut cached_children = this.cached_children().write();
|
||||
cached_children.put_entry_if_not_found("exe", || {
|
||||
ExeSymOps::new_inode(self.0.clone(), this_ptr.clone())
|
||||
});
|
||||
cached_children.put_entry_if_not_found("comm", || {
|
||||
CommFileOps::new_inode(self.0.clone(), this_ptr.clone())
|
||||
});
|
||||
cached_children.put_entry_if_not_found("fd", || {
|
||||
FdDirOps::new_inode(self.0.clone(), this_ptr.clone())
|
||||
})
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Represents the inode at `/proc/self`.
|
||||
pub struct SelfSymOps;
|
||||
|
||||
impl SelfSymOps {
|
||||
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
ProcSymBuilder::new(Self).parent(parent).build().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl SymOps for SelfSymOps {
|
||||
fn read_link(&self) -> Result<String> {
|
||||
Ok(current!().pid().to_string())
|
||||
}
|
||||
}
|
@ -1,178 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::fs::utils::{FileSystem, Inode};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::{
|
||||
dir::{DirOps, ProcDir},
|
||||
file::{FileOps, ProcFile},
|
||||
sym::{ProcSym, SymOps},
|
||||
};
|
||||
|
||||
pub struct ProcDirBuilder<O: DirOps> {
|
||||
// Mandatory field
|
||||
dir: O,
|
||||
// Optional fields
|
||||
optional_builder: Option<OptionalBuilder>,
|
||||
}
|
||||
|
||||
impl<O: DirOps> ProcDirBuilder<O> {
|
||||
pub fn new(dir: O) -> Self {
|
||||
let optional_builder: OptionalBuilder = Default::default();
|
||||
Self {
|
||||
dir,
|
||||
optional_builder: Some(optional_builder),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
|
||||
self.optional_builder(|ob| ob.parent(parent))
|
||||
}
|
||||
|
||||
pub fn fs(self, fs: Arc<dyn FileSystem>) -> Self {
|
||||
self.optional_builder(|ob| ob.fs(fs))
|
||||
}
|
||||
|
||||
pub fn volatile(self) -> Self {
|
||||
self.optional_builder(|ob| ob.volatile())
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Result<Arc<ProcDir<O>>> {
|
||||
let (fs, parent, is_volatile) = self.optional_builder.take().unwrap().build()?;
|
||||
Ok(ProcDir::new(self.dir, fs, parent, is_volatile))
|
||||
}
|
||||
|
||||
fn optional_builder<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
|
||||
{
|
||||
let optional_builder = self.optional_builder.take().unwrap();
|
||||
self.optional_builder = Some(f(optional_builder));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProcFileBuilder<O: FileOps> {
|
||||
// Mandatory field
|
||||
file: O,
|
||||
// Optional fields
|
||||
optional_builder: Option<OptionalBuilder>,
|
||||
}
|
||||
|
||||
impl<O: FileOps> ProcFileBuilder<O> {
|
||||
pub fn new(file: O) -> Self {
|
||||
let optional_builder: OptionalBuilder = Default::default();
|
||||
Self {
|
||||
file,
|
||||
optional_builder: Some(optional_builder),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
|
||||
self.optional_builder(|ob| ob.parent(parent))
|
||||
}
|
||||
|
||||
pub fn volatile(self) -> Self {
|
||||
self.optional_builder(|ob| ob.volatile())
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Result<Arc<ProcFile<O>>> {
|
||||
let (fs, _, is_volatile) = self.optional_builder.take().unwrap().build()?;
|
||||
Ok(ProcFile::new(self.file, fs, is_volatile))
|
||||
}
|
||||
|
||||
fn optional_builder<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
|
||||
{
|
||||
let optional_builder = self.optional_builder.take().unwrap();
|
||||
self.optional_builder = Some(f(optional_builder));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProcSymBuilder<O: SymOps> {
|
||||
// Mandatory field
|
||||
sym: O,
|
||||
// Optional fields
|
||||
optional_builder: Option<OptionalBuilder>,
|
||||
}
|
||||
|
||||
impl<O: SymOps> ProcSymBuilder<O> {
|
||||
pub fn new(sym: O) -> Self {
|
||||
let optional_builder: OptionalBuilder = Default::default();
|
||||
Self {
|
||||
sym,
|
||||
optional_builder: Some(optional_builder),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
|
||||
self.optional_builder(|ob| ob.parent(parent))
|
||||
}
|
||||
|
||||
pub fn volatile(self) -> Self {
|
||||
self.optional_builder(|ob| ob.volatile())
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Result<Arc<ProcSym<O>>> {
|
||||
let (fs, _, is_volatile) = self.optional_builder.take().unwrap().build()?;
|
||||
Ok(ProcSym::new(self.sym, fs, is_volatile))
|
||||
}
|
||||
|
||||
fn optional_builder<F>(mut self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
|
||||
{
|
||||
let optional_builder = self.optional_builder.take().unwrap();
|
||||
self.optional_builder = Some(f(optional_builder));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct OptionalBuilder {
|
||||
parent: Option<Weak<dyn Inode>>,
|
||||
fs: Option<Arc<dyn FileSystem>>,
|
||||
is_volatile: bool,
|
||||
}
|
||||
|
||||
impl OptionalBuilder {
|
||||
pub fn parent(mut self, parent: Weak<dyn Inode>) -> Self {
|
||||
self.parent = Some(parent);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn fs(mut self, fs: Arc<dyn FileSystem>) -> Self {
|
||||
self.fs = Some(fs);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn volatile(mut self) -> Self {
|
||||
self.is_volatile = true;
|
||||
self
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn build(self) -> Result<(Arc<dyn FileSystem>, Option<Weak<dyn Inode>>, bool)> {
|
||||
if self.parent.is_none() && self.fs.is_none() {
|
||||
return_errno_with_message!(Errno::EINVAL, "must have parent or fs");
|
||||
}
|
||||
let fs = self
|
||||
.fs
|
||||
.unwrap_or_else(|| self.parent.as_ref().unwrap().upgrade().unwrap().fs());
|
||||
|
||||
// The volatile property is inherited from parent.
|
||||
let is_volatile = {
|
||||
let mut is_volatile = self.is_volatile;
|
||||
if let Some(parent) = self.parent.as_ref() {
|
||||
if !parent.upgrade().unwrap().is_dentry_cacheable() {
|
||||
is_volatile = true;
|
||||
}
|
||||
}
|
||||
is_volatile
|
||||
};
|
||||
|
||||
Ok((fs, self.parent, is_volatile))
|
||||
}
|
||||
}
|
@ -1,222 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_util::slot_vec::SlotVec;
|
||||
use core::time::Duration;
|
||||
|
||||
use crate::fs::device::Device;
|
||||
use crate::fs::utils::{DirentVisitor, FileSystem, Inode, InodeMode, InodeType, Metadata};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::{ProcFS, ProcInodeInfo};
|
||||
|
||||
pub struct ProcDir<D: DirOps> {
|
||||
inner: D,
|
||||
this: Weak<ProcDir<D>>,
|
||||
parent: Option<Weak<dyn Inode>>,
|
||||
cached_children: RwLock<SlotVec<(String, Arc<dyn Inode>)>>,
|
||||
info: ProcInodeInfo,
|
||||
}
|
||||
|
||||
impl<D: DirOps> ProcDir<D> {
|
||||
pub fn new(
|
||||
dir: D,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
parent: Option<Weak<dyn Inode>>,
|
||||
is_volatile: bool,
|
||||
) -> Arc<Self> {
|
||||
let info = {
|
||||
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
|
||||
let metadata = Metadata::new_dir(
|
||||
procfs.alloc_id(),
|
||||
InodeMode::from_bits_truncate(0o555),
|
||||
&fs.sb(),
|
||||
);
|
||||
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
|
||||
};
|
||||
Arc::new_cyclic(|weak_self| Self {
|
||||
inner: dir,
|
||||
this: weak_self.clone(),
|
||||
parent,
|
||||
cached_children: RwLock::new(SlotVec::new()),
|
||||
info,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn this(&self) -> Arc<ProcDir<D>> {
|
||||
self.this.upgrade().unwrap()
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<Arc<dyn Inode>> {
|
||||
self.parent.as_ref().and_then(|p| p.upgrade())
|
||||
}
|
||||
|
||||
pub fn cached_children(&self) -> &RwLock<SlotVec<(String, Arc<dyn Inode>)>> {
|
||||
&self.cached_children
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DirOps + 'static> Inode for ProcDir<D> {
|
||||
fn size(&self) -> usize {
|
||||
self.info.size()
|
||||
}
|
||||
|
||||
fn resize(&self, _new_size: usize) -> Result<()> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.info.metadata()
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.info.ino()
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
InodeType::Dir
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
self.info.mode()
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {
|
||||
self.info.set_mode(mode)
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.info.atime()
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {
|
||||
self.info.set_atime(time)
|
||||
}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.info.mtime()
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {
|
||||
self.info.set_mtime(time)
|
||||
}
|
||||
|
||||
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,
|
||||
_device: 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 two special entries.
|
||||
if *offset == 0 {
|
||||
let this_inode = self.this();
|
||||
visitor.visit(
|
||||
".",
|
||||
this_inode.info.metadata().ino as u64,
|
||||
this_inode.info.metadata().type_,
|
||||
*offset,
|
||||
)?;
|
||||
*offset += 1;
|
||||
}
|
||||
if *offset == 1 {
|
||||
let parent_inode = self.parent().unwrap_or(self.this());
|
||||
visitor.visit(
|
||||
"..",
|
||||
parent_inode.metadata().ino as u64,
|
||||
parent_inode.metadata().type_,
|
||||
*offset,
|
||||
)?;
|
||||
*offset += 1;
|
||||
}
|
||||
|
||||
// Read the normal child entries.
|
||||
self.inner.populate_children(self.this.clone());
|
||||
let cached_children = self.cached_children.read();
|
||||
let start_offset = *offset;
|
||||
for (idx, (name, child)) in cached_children
|
||||
.idxes_and_items()
|
||||
.map(|(idx, (name, child))| (idx + 2, (name, child)))
|
||||
.skip_while(|(idx, _)| idx < &start_offset)
|
||||
{
|
||||
visitor.visit(
|
||||
name.as_ref(),
|
||||
child.metadata().ino as u64,
|
||||
child.metadata().type_,
|
||||
idx,
|
||||
)?;
|
||||
*offset = idx + 1;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut iterate_offset = offset;
|
||||
match try_readdir(&mut iterate_offset, visitor) {
|
||||
Err(e) if iterate_offset == 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.this(),
|
||||
".." => self.parent().unwrap_or(self.this()),
|
||||
name => {
|
||||
let mut cached_children = self.cached_children.write();
|
||||
if let Some((_, inode)) = cached_children
|
||||
.iter()
|
||||
.find(|(child_name, inode)| child_name.as_str() == name)
|
||||
{
|
||||
return Ok(inode.clone());
|
||||
}
|
||||
let inode = self.inner.lookup_child(self.this.clone(), name)?;
|
||||
cached_children.put((String::from(name), inode.clone()));
|
||||
inode
|
||||
}
|
||||
};
|
||||
Ok(inode)
|
||||
}
|
||||
|
||||
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.info.fs().upgrade().unwrap()
|
||||
}
|
||||
|
||||
fn is_dentry_cacheable(&self) -> bool {
|
||||
!self.info.is_volatile()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DirOps: Sync + Send {
|
||||
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
|
||||
Err(Error::new(Errno::ENOENT))
|
||||
}
|
||||
|
||||
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
use crate::fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::{ProcFS, ProcInodeInfo};
|
||||
|
||||
pub struct ProcFile<F: FileOps> {
|
||||
inner: F,
|
||||
info: ProcInodeInfo,
|
||||
}
|
||||
|
||||
impl<F: FileOps> ProcFile<F> {
|
||||
pub fn new(file: F, fs: Arc<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
|
||||
let info = {
|
||||
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
|
||||
let metadata = Metadata::new_file(
|
||||
procfs.alloc_id(),
|
||||
InodeMode::from_bits_truncate(0o444),
|
||||
&fs.sb(),
|
||||
);
|
||||
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
|
||||
};
|
||||
Arc::new(Self { inner: file, info })
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FileOps + 'static> Inode for ProcFile<F> {
|
||||
fn size(&self) -> usize {
|
||||
self.info.size()
|
||||
}
|
||||
|
||||
fn resize(&self, _new_size: usize) -> Result<()> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.info.metadata()
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.info.ino()
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
InodeType::File
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
self.info.mode()
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {
|
||||
self.info.set_mode(mode)
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.info.atime()
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {
|
||||
self.info.set_atime(time)
|
||||
}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.info.mtime()
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {
|
||||
self.info.set_mtime(time)
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let data = self.inner.data()?;
|
||||
let start = data.len().min(offset);
|
||||
let end = data.len().min(offset + buf.len());
|
||||
let len = end - start;
|
||||
buf[0..len].copy_from_slice(&data[start..end]);
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.read_at(offset, buf)
|
||||
}
|
||||
|
||||
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn write_direct_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn read_link(&self) -> Result<String> {
|
||||
Err(Error::new(Errno::EINVAL))
|
||||
}
|
||||
|
||||
fn write_link(&self, _target: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::EINVAL))
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.info.fs().upgrade().unwrap()
|
||||
}
|
||||
|
||||
fn is_dentry_cacheable(&self) -> bool {
|
||||
!self.info.is_volatile()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FileOps: Sync + Send {
|
||||
fn data(&self) -> Result<Vec<u8>>;
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
use crate::fs::utils::{FileSystem, InodeMode, Metadata};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::ProcFS;
|
||||
|
||||
pub use self::builder::{ProcDirBuilder, ProcFileBuilder, ProcSymBuilder};
|
||||
pub use self::dir::{DirOps, ProcDir};
|
||||
pub use self::file::FileOps;
|
||||
pub use self::sym::SymOps;
|
||||
|
||||
mod builder;
|
||||
mod dir;
|
||||
mod file;
|
||||
mod sym;
|
||||
|
||||
struct ProcInodeInfo {
|
||||
metadata: RwLock<Metadata>,
|
||||
fs: Weak<dyn FileSystem>,
|
||||
is_volatile: bool,
|
||||
}
|
||||
|
||||
impl ProcInodeInfo {
|
||||
pub fn new(metadata: Metadata, fs: Weak<dyn FileSystem>, is_volatile: bool) -> Self {
|
||||
Self {
|
||||
metadata: RwLock::new(metadata),
|
||||
fs,
|
||||
is_volatile,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fs(&self) -> &Weak<dyn FileSystem> {
|
||||
&self.fs
|
||||
}
|
||||
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
self.metadata.read().clone()
|
||||
}
|
||||
|
||||
pub fn ino(&self) -> u64 {
|
||||
self.metadata.read().ino as _
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.metadata.read().size
|
||||
}
|
||||
|
||||
pub fn atime(&self) -> Duration {
|
||||
self.metadata.read().atime
|
||||
}
|
||||
|
||||
pub fn set_atime(&self, time: Duration) {
|
||||
self.metadata.write().atime = time;
|
||||
}
|
||||
|
||||
pub fn mtime(&self) -> Duration {
|
||||
self.metadata.read().mtime
|
||||
}
|
||||
|
||||
pub fn set_mtime(&self, time: Duration) {
|
||||
self.metadata.write().mtime = time;
|
||||
}
|
||||
|
||||
pub fn mode(&self) -> InodeMode {
|
||||
self.metadata.read().mode
|
||||
}
|
||||
|
||||
pub fn set_mode(&self, mode: InodeMode) {
|
||||
self.metadata.write().mode = mode;
|
||||
}
|
||||
|
||||
pub fn is_volatile(&self) -> bool {
|
||||
self.is_volatile
|
||||
}
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
use crate::fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata};
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::{ProcFS, ProcInodeInfo};
|
||||
|
||||
pub struct ProcSym<S: SymOps> {
|
||||
inner: S,
|
||||
info: ProcInodeInfo,
|
||||
}
|
||||
|
||||
impl<S: SymOps> ProcSym<S> {
|
||||
pub fn new(sym: S, fs: Arc<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
|
||||
let info = {
|
||||
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
|
||||
let metadata = Metadata::new_symlink(
|
||||
procfs.alloc_id(),
|
||||
InodeMode::from_bits_truncate(0o777),
|
||||
&fs.sb(),
|
||||
);
|
||||
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
|
||||
};
|
||||
Arc::new(Self { inner: sym, info })
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SymOps + 'static> Inode for ProcSym<S> {
|
||||
fn size(&self) -> usize {
|
||||
self.info.size()
|
||||
}
|
||||
|
||||
fn resize(&self, _new_size: usize) -> Result<()> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.info.metadata()
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.info.ino()
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
InodeType::SymLink
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
self.info.mode()
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {
|
||||
self.info.set_mode(mode)
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.info.atime()
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {
|
||||
self.info.set_atime(time)
|
||||
}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.info.mtime()
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {
|
||||
self.info.set_mtime(time)
|
||||
}
|
||||
|
||||
fn read_at(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn read_direct_at(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn write_direct_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn read_link(&self) -> Result<String> {
|
||||
self.inner.read_link()
|
||||
}
|
||||
|
||||
fn write_link(&self, _target: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn ioctl(&self, _cmd: IoctlCmd, _arg: usize) -> Result<i32> {
|
||||
Err(Error::new(Errno::EPERM))
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.info.fs().upgrade().unwrap()
|
||||
}
|
||||
|
||||
fn is_dentry_cacheable(&self) -> bool {
|
||||
!self.info.is_volatile()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SymOps: Sync + Send {
|
||||
fn read_link(&self) -> Result<String>;
|
||||
}
|
@ -1,915 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_frame::sync::RwLockWriteGuard;
|
||||
use aster_frame::vm::VmFrame;
|
||||
use aster_frame::vm::VmIo;
|
||||
use aster_rights::Full;
|
||||
use aster_util::slot_vec::SlotVec;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use core::time::Duration;
|
||||
|
||||
use super::*;
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::Device;
|
||||
use crate::fs::utils::{
|
||||
CStr256, DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
|
||||
PageCache, PageCacheBackend, SuperBlock,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
use crate::vm::vmo::Vmo;
|
||||
|
||||
/// A volatile file system whose data and metadata exists only in memory.
|
||||
pub struct RamFS {
|
||||
metadata: RwLock<SuperBlock>,
|
||||
root: Arc<RamInode>,
|
||||
inode_allocator: AtomicUsize,
|
||||
}
|
||||
|
||||
impl RamFS {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let sb = SuperBlock::new(RAMFS_MAGIC, BLOCK_SIZE, NAME_MAX);
|
||||
let root = Arc::new(RamInode(RwLock::new(Inode_::new_dir(
|
||||
ROOT_INO,
|
||||
InodeMode::from_bits_truncate(0o755),
|
||||
&sb,
|
||||
))));
|
||||
let ramfs = Arc::new(Self {
|
||||
metadata: RwLock::new(sb),
|
||||
root,
|
||||
inode_allocator: AtomicUsize::new(ROOT_INO + 1),
|
||||
});
|
||||
let mut root = ramfs.root.0.write();
|
||||
root.inner
|
||||
.as_direntry_mut()
|
||||
.unwrap()
|
||||
.init(Arc::downgrade(&ramfs.root), Arc::downgrade(&ramfs.root));
|
||||
root.this = Arc::downgrade(&ramfs.root);
|
||||
root.fs = Arc::downgrade(&ramfs);
|
||||
drop(root);
|
||||
ramfs
|
||||
}
|
||||
|
||||
fn alloc_id(&self) -> usize {
|
||||
let next_id = self.inode_allocator.fetch_add(1, Ordering::SeqCst);
|
||||
self.metadata.write().files += 1;
|
||||
next_id
|
||||
}
|
||||
}
|
||||
|
||||
impl FileSystem for RamFS {
|
||||
fn sync(&self) -> Result<()> {
|
||||
// do nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn root_inode(&self) -> Arc<dyn Inode> {
|
||||
self.root.clone()
|
||||
}
|
||||
|
||||
fn sb(&self) -> SuperBlock {
|
||||
self.metadata.read().clone()
|
||||
}
|
||||
|
||||
fn flags(&self) -> FsFlags {
|
||||
FsFlags::DENTRY_UNEVICTABLE
|
||||
}
|
||||
}
|
||||
|
||||
struct RamInode(RwLock<Inode_>);
|
||||
|
||||
struct Inode_ {
|
||||
inner: Inner,
|
||||
metadata: Metadata,
|
||||
this: Weak<RamInode>,
|
||||
fs: Weak<RamFS>,
|
||||
}
|
||||
|
||||
impl Inode_ {
|
||||
pub fn new_dir(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
|
||||
Self {
|
||||
inner: Inner::Dir(DirEntry::new()),
|
||||
metadata: Metadata::new_dir(ino, mode, sb),
|
||||
this: Weak::default(),
|
||||
fs: Weak::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_file(
|
||||
ino: usize,
|
||||
mode: InodeMode,
|
||||
sb: &SuperBlock,
|
||||
weak_inode: Weak<RamInode>,
|
||||
) -> Self {
|
||||
Self {
|
||||
inner: Inner::File(PageCache::new(weak_inode).unwrap()),
|
||||
metadata: Metadata::new_file(ino, mode, sb),
|
||||
this: Weak::default(),
|
||||
fs: Weak::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_symlink(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
|
||||
Self {
|
||||
inner: Inner::SymLink(String::from("")),
|
||||
metadata: Metadata::new_symlink(ino, mode, sb),
|
||||
this: Weak::default(),
|
||||
fs: Weak::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_socket(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
|
||||
Self {
|
||||
inner: Inner::Socket,
|
||||
metadata: Metadata::new_socket(ino, mode, sb),
|
||||
this: Weak::default(),
|
||||
fs: Weak::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_device(
|
||||
ino: usize,
|
||||
mode: InodeMode,
|
||||
sb: &SuperBlock,
|
||||
device: Arc<dyn Device>,
|
||||
) -> Self {
|
||||
Self {
|
||||
metadata: Metadata::new_device(ino, mode, sb, device.as_ref()),
|
||||
inner: Inner::Device(device),
|
||||
this: Weak::default(),
|
||||
fs: Weak::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inc_size(&mut self) {
|
||||
self.metadata.size += 1;
|
||||
self.metadata.blocks = (self.metadata.size + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
||||
}
|
||||
|
||||
pub fn dec_size(&mut self) {
|
||||
debug_assert!(self.metadata.size > 0);
|
||||
self.metadata.size -= 1;
|
||||
self.metadata.blocks = (self.metadata.size + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, new_size: usize) {
|
||||
self.metadata.size = new_size;
|
||||
self.metadata.blocks = (new_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
|
||||
}
|
||||
|
||||
pub fn inc_nlinks(&mut self) {
|
||||
self.metadata.nlinks += 1;
|
||||
}
|
||||
|
||||
pub fn dec_nlinks(&mut self) {
|
||||
debug_assert!(self.metadata.nlinks > 0);
|
||||
self.metadata.nlinks -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum Inner {
|
||||
Dir(DirEntry),
|
||||
File(PageCache),
|
||||
SymLink(String),
|
||||
Device(Arc<dyn Device>),
|
||||
Socket,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn as_file(&self) -> Option<&PageCache> {
|
||||
match self {
|
||||
Inner::File(page_cache) => Some(page_cache),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_direntry(&self) -> Option<&DirEntry> {
|
||||
match self {
|
||||
Inner::Dir(dir_entry) => Some(dir_entry),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_direntry_mut(&mut self) -> Option<&mut DirEntry> {
|
||||
match self {
|
||||
Inner::Dir(dir_entry) => Some(dir_entry),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_symlink(&self) -> Option<&str> {
|
||||
match self {
|
||||
Inner::SymLink(link) => Some(link.as_ref()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_symlink_mut(&mut self) -> Option<&mut String> {
|
||||
match self {
|
||||
Inner::SymLink(link) => Some(link),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_device(&self) -> Option<&Arc<dyn Device>> {
|
||||
match self {
|
||||
Inner::Device(device) => Some(device),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DirEntry {
|
||||
children: SlotVec<(CStr256, Arc<RamInode>)>,
|
||||
this: Weak<RamInode>,
|
||||
parent: Weak<RamInode>,
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
children: SlotVec::new(),
|
||||
this: Weak::default(),
|
||||
parent: Weak::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(&mut self, this: Weak<RamInode>, parent: Weak<RamInode>) {
|
||||
self.this = this;
|
||||
self.set_parent(parent);
|
||||
}
|
||||
|
||||
fn set_parent(&mut self, parent: Weak<RamInode>) {
|
||||
self.parent = parent;
|
||||
}
|
||||
|
||||
fn contains_entry(&self, name: &str) -> bool {
|
||||
if name == "." || name == ".." {
|
||||
true
|
||||
} else {
|
||||
self.children
|
||||
.iter()
|
||||
.any(|(child, _)| child.as_str().unwrap() == name)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_entry(&self, name: &str) -> Option<(usize, Arc<RamInode>)> {
|
||||
if name == "." {
|
||||
Some((0, self.this.upgrade().unwrap()))
|
||||
} else if name == ".." {
|
||||
Some((1, self.parent.upgrade().unwrap()))
|
||||
} else {
|
||||
self.children
|
||||
.idxes_and_items()
|
||||
.find(|(_, (child, _))| child.as_str().unwrap() == name)
|
||||
.map(|(idx, (_, inode))| (idx + 2, inode.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn append_entry(&mut self, name: &str, inode: Arc<RamInode>) -> usize {
|
||||
self.children.put((CStr256::from(name), inode))
|
||||
}
|
||||
|
||||
fn remove_entry(&mut self, idx: usize) -> Option<(CStr256, Arc<RamInode>)> {
|
||||
assert!(idx >= 2);
|
||||
self.children.remove(idx - 2)
|
||||
}
|
||||
|
||||
fn substitute_entry(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
new_entry: (CStr256, Arc<RamInode>),
|
||||
) -> Option<(CStr256, Arc<RamInode>)> {
|
||||
assert!(idx >= 2);
|
||||
self.children.put_at(idx - 2, new_entry)
|
||||
}
|
||||
|
||||
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 {
|
||||
let this_inode = self.this.upgrade().unwrap();
|
||||
visitor.visit(
|
||||
".",
|
||||
this_inode.metadata().ino as u64,
|
||||
this_inode.metadata().type_,
|
||||
*idx,
|
||||
)?;
|
||||
*idx += 1;
|
||||
}
|
||||
if *idx == 1 {
|
||||
let parent_inode = self.parent.upgrade().unwrap();
|
||||
visitor.visit(
|
||||
"..",
|
||||
parent_inode.metadata().ino as u64,
|
||||
parent_inode.metadata().type_,
|
||||
*idx,
|
||||
)?;
|
||||
*idx += 1;
|
||||
}
|
||||
// Read the normal child entries.
|
||||
let start_idx = *idx;
|
||||
for (offset, (name, child)) in self
|
||||
.children
|
||||
.idxes_and_items()
|
||||
.map(|(offset, (name, child))| (offset + 2, (name, child)))
|
||||
.skip_while(|(offset, _)| offset < &start_idx)
|
||||
{
|
||||
visitor.visit(
|
||||
name.as_str().unwrap(),
|
||||
child.metadata().ino as u64,
|
||||
child.metadata().type_,
|
||||
offset,
|
||||
)?;
|
||||
*idx = offset + 1;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut iterate_idx = idx;
|
||||
match try_visit(&mut iterate_idx, visitor) {
|
||||
Err(e) if idx == iterate_idx => Err(e),
|
||||
_ => Ok(iterate_idx - idx),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty_children(&self) -> bool {
|
||||
self.children.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
weak_self.clone(),
|
||||
)));
|
||||
inode.0.write().fs = Arc::downgrade(fs);
|
||||
inode.0.write().this = weak_self.clone();
|
||||
inode
|
||||
})
|
||||
}
|
||||
|
||||
fn new_socket(fs: &Arc<RamFS>, mode: InodeMode) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| {
|
||||
let inode = RamInode(RwLock::new(Inode_::new_socket(
|
||||
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 PageCacheBackend for RamInode {
|
||||
fn read_page(&self, _idx: usize, frame: &VmFrame) -> Result<()> {
|
||||
// Initially, any block/page in a RamFs inode contains all zeros
|
||||
frame.writer().fill(0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
|
||||
// do nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn npages(&self) -> usize {
|
||||
self.0.read().metadata.blocks
|
||||
}
|
||||
}
|
||||
|
||||
impl Inode for RamInode {
|
||||
fn page_cache(&self) -> Option<Vmo<Full>> {
|
||||
self.0
|
||||
.read()
|
||||
.inner
|
||||
.as_file()
|
||||
.map(|page_cache| page_cache.pages())
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
if let Some(device) = self.0.read().inner.as_device() {
|
||||
return device.read(buf);
|
||||
}
|
||||
|
||||
let self_inode = self.0.read();
|
||||
let Some(page_cache) = self_inode.inner.as_file() else {
|
||||
return_errno_with_message!(Errno::EISDIR, "read is not supported");
|
||||
};
|
||||
let (offset, read_len) = {
|
||||
let file_size = self_inode.metadata.size;
|
||||
let start = file_size.min(offset);
|
||||
let end = file_size.min(offset + buf.len());
|
||||
(start, end - start)
|
||||
};
|
||||
page_cache
|
||||
.pages()
|
||||
.read_bytes(offset, &mut buf[..read_len])?;
|
||||
Ok(read_len)
|
||||
}
|
||||
|
||||
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
self.read_at(offset, buf)
|
||||
}
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
if let Some(device) = self.0.read().inner.as_device() {
|
||||
return device.write(buf);
|
||||
}
|
||||
|
||||
let self_inode = self.0.upread();
|
||||
let Some(page_cache) = self_inode.inner.as_file() else {
|
||||
return_errno_with_message!(Errno::EISDIR, "write is not supported");
|
||||
};
|
||||
let file_size = self_inode.metadata.size;
|
||||
let new_size = offset + buf.len();
|
||||
let should_expand_size = new_size > file_size;
|
||||
if should_expand_size {
|
||||
page_cache.pages().resize(new_size)?;
|
||||
}
|
||||
page_cache.pages().write_bytes(offset, buf)?;
|
||||
if should_expand_size {
|
||||
// Turn the read guard into a write guard without releasing the lock.
|
||||
let mut self_inode = self_inode.upgrade();
|
||||
self_inode.resize(new_size);
|
||||
}
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
self.write_at(offset, buf)
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.0.read().metadata.size
|
||||
}
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()> {
|
||||
let self_inode = self.0.upread();
|
||||
if self_inode.inner.as_file().is_none() {
|
||||
return_errno!(Errno::EISDIR);
|
||||
}
|
||||
|
||||
let file_size = self_inode.metadata.size;
|
||||
if file_size == new_size {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut self_inode = self_inode.upgrade();
|
||||
self_inode.resize(new_size);
|
||||
let page_cache = self_inode.inner.as_file().unwrap();
|
||||
page_cache.pages().resize(new_size)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn atime(&self) -> Duration {
|
||||
self.0.read().metadata.atime
|
||||
}
|
||||
|
||||
fn set_atime(&self, time: Duration) {
|
||||
self.0.write().metadata.atime = time;
|
||||
}
|
||||
|
||||
fn mtime(&self) -> Duration {
|
||||
self.0.read().metadata.mtime
|
||||
}
|
||||
|
||||
fn set_mtime(&self, time: Duration) {
|
||||
self.0.write().metadata.mtime = time;
|
||||
}
|
||||
|
||||
fn ino(&self) -> u64 {
|
||||
self.0.read().metadata.ino as _
|
||||
}
|
||||
|
||||
fn type_(&self) -> InodeType {
|
||||
self.0.read().metadata.type_
|
||||
}
|
||||
|
||||
fn mode(&self) -> InodeMode {
|
||||
self.0.read().metadata.mode
|
||||
}
|
||||
|
||||
fn set_mode(&self, mode: InodeMode) {
|
||||
self.0.write().metadata.mode = mode;
|
||||
}
|
||||
|
||||
fn mknod(
|
||||
&self,
|
||||
name: &str,
|
||||
mode: InodeMode,
|
||||
device: Arc<dyn Device>,
|
||||
) -> Result<Arc<dyn Inode>> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
if name.len() > NAME_MAX {
|
||||
return_errno!(Errno::ENAMETOOLONG);
|
||||
}
|
||||
|
||||
let mut self_inode = self.0.write();
|
||||
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
|
||||
return_errno_with_message!(Errno::EEXIST, "entry exists");
|
||||
}
|
||||
let device_inode = RamInode::new_device(&self_inode.fs.upgrade().unwrap(), mode, device);
|
||||
self_inode
|
||||
.inner
|
||||
.as_direntry_mut()
|
||||
.unwrap()
|
||||
.append_entry(name, device_inode.clone());
|
||||
self_inode.inc_size();
|
||||
Ok(device_inode)
|
||||
}
|
||||
|
||||
fn as_device(&self) -> Option<Arc<dyn Device>> {
|
||||
self.0.read().inner.as_device().cloned()
|
||||
}
|
||||
|
||||
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
if name.len() > NAME_MAX {
|
||||
return_errno!(Errno::ENAMETOOLONG);
|
||||
}
|
||||
|
||||
let mut self_inode = self.0.write();
|
||||
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
|
||||
return_errno_with_message!(Errno::EEXIST, "entry exists");
|
||||
}
|
||||
let fs = self_inode.fs.upgrade().unwrap();
|
||||
let new_inode = match type_ {
|
||||
InodeType::File => RamInode::new_file(&fs, mode),
|
||||
InodeType::SymLink => RamInode::new_symlink(&fs, mode),
|
||||
InodeType::Socket => RamInode::new_socket(&fs, mode),
|
||||
InodeType::Dir => {
|
||||
let dir_inode = RamInode::new_dir(&fs, mode, &self_inode.this);
|
||||
self_inode.inc_nlinks();
|
||||
dir_inode
|
||||
}
|
||||
_ => {
|
||||
panic!("unsupported inode type");
|
||||
}
|
||||
};
|
||||
self_inode
|
||||
.inner
|
||||
.as_direntry_mut()
|
||||
.unwrap()
|
||||
.append_entry(name, new_inode.clone());
|
||||
self_inode.inc_size();
|
||||
Ok(new_inode)
|
||||
}
|
||||
|
||||
fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
let self_inode = self.0.read();
|
||||
let cnt = self_inode
|
||||
.inner
|
||||
.as_direntry()
|
||||
.unwrap()
|
||||
.visit_entry(offset, visitor)?;
|
||||
Ok(cnt)
|
||||
}
|
||||
|
||||
fn link(&self, old: &Arc<dyn Inode>, name: &str) -> Result<()> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
if !Arc::ptr_eq(&self.fs(), &old.fs()) {
|
||||
return_errno_with_message!(Errno::EXDEV, "not same fs");
|
||||
}
|
||||
let old = old
|
||||
.downcast_ref::<RamInode>()
|
||||
.ok_or(Error::new(Errno::EXDEV))?;
|
||||
if old.0.read().metadata.type_ == InodeType::Dir {
|
||||
return_errno_with_message!(Errno::EPERM, "old is a dir");
|
||||
}
|
||||
let mut self_inode = self.0.write();
|
||||
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
|
||||
if self_dir.contains_entry(name) {
|
||||
return_errno_with_message!(Errno::EEXIST, "entry exist");
|
||||
}
|
||||
|
||||
self_dir.append_entry(name, old.0.read().this.upgrade().unwrap());
|
||||
self_inode.inc_size();
|
||||
drop(self_inode);
|
||||
old.0.write().inc_nlinks();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unlink(&self, name: &str) -> Result<()> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
if name == "." || name == ".." {
|
||||
return_errno_with_message!(Errno::EISDIR, "unlink . or ..");
|
||||
}
|
||||
let mut self_inode = self.0.write();
|
||||
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
|
||||
let (idx, target) = self_dir.get_entry(name).ok_or(Error::new(Errno::ENOENT))?;
|
||||
if target.0.read().metadata.type_ == InodeType::Dir {
|
||||
return_errno_with_message!(Errno::EISDIR, "unlink on dir");
|
||||
}
|
||||
self_dir.remove_entry(idx);
|
||||
self_inode.dec_size();
|
||||
drop(self_inode);
|
||||
target.0.write().dec_nlinks();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn rmdir(&self, name: &str) -> Result<()> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
if name == "." {
|
||||
return_errno_with_message!(Errno::EINVAL, "rmdir on .");
|
||||
}
|
||||
if name == ".." {
|
||||
return_errno_with_message!(Errno::ENOTEMPTY, "rmdir on ..");
|
||||
}
|
||||
let mut self_inode = self.0.write();
|
||||
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
|
||||
let (idx, target) = self_dir.get_entry(name).ok_or(Error::new(Errno::ENOENT))?;
|
||||
if target.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "rmdir on not dir");
|
||||
}
|
||||
if !target
|
||||
.0
|
||||
.read()
|
||||
.inner
|
||||
.as_direntry()
|
||||
.unwrap()
|
||||
.is_empty_children()
|
||||
{
|
||||
return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty");
|
||||
}
|
||||
self_dir.remove_entry(idx);
|
||||
self_inode.dec_size();
|
||||
self_inode.dec_nlinks();
|
||||
drop(self_inode);
|
||||
let mut target_inode = target.0.write();
|
||||
target_inode.dec_nlinks();
|
||||
target_inode.dec_nlinks();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
|
||||
let (_, inode) = self
|
||||
.0
|
||||
.read()
|
||||
.inner
|
||||
.as_direntry()
|
||||
.unwrap()
|
||||
.get_entry(name)
|
||||
.ok_or(Error::new(Errno::ENOENT))?;
|
||||
Ok(inode as _)
|
||||
}
|
||||
|
||||
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()> {
|
||||
if self.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
|
||||
}
|
||||
if !Arc::ptr_eq(&self.fs(), &target.fs()) {
|
||||
return_errno_with_message!(Errno::EXDEV, "not same fs");
|
||||
}
|
||||
let target = target
|
||||
.downcast_ref::<RamInode>()
|
||||
.ok_or(Error::new(Errno::EXDEV))?;
|
||||
if target.0.read().metadata.type_ != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "target is not dir");
|
||||
}
|
||||
if old_name == "." || old_name == ".." {
|
||||
return_errno_with_message!(Errno::EISDIR, "old_name is . or ..");
|
||||
}
|
||||
if new_name == "." || new_name == ".." {
|
||||
return_errno_with_message!(Errno::EISDIR, "new_name is . or ..");
|
||||
}
|
||||
|
||||
// Perform necessary checks to ensure that `dst_inode` can be replaced by `src_inode`.
|
||||
let check_replace_inode =
|
||||
|src_inode: &Arc<RamInode>, dst_inode: &Arc<RamInode>| -> Result<()> {
|
||||
if src_inode.metadata().ino == dst_inode.metadata().ino {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
match (src_inode.metadata().type_, dst_inode.metadata().type_) {
|
||||
(InodeType::Dir, InodeType::Dir) => {
|
||||
if !dst_inode
|
||||
.0
|
||||
.read()
|
||||
.inner
|
||||
.as_direntry()
|
||||
.unwrap()
|
||||
.is_empty_children()
|
||||
{
|
||||
return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty");
|
||||
}
|
||||
}
|
||||
(InodeType::Dir, _) => {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "old is not dir");
|
||||
}
|
||||
(_, InodeType::Dir) => {
|
||||
return_errno_with_message!(Errno::EISDIR, "new is dir");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
// Rename in the same directory
|
||||
if self.metadata().ino == target.metadata().ino {
|
||||
let mut self_inode = self.0.write();
|
||||
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
|
||||
let (src_idx, src_inode) = self_dir
|
||||
.get_entry(old_name)
|
||||
.ok_or(Error::new(Errno::ENOENT))?;
|
||||
let is_dir = src_inode.0.read().metadata.type_ == InodeType::Dir;
|
||||
|
||||
if let Some((dst_idx, dst_inode)) = self_dir.get_entry(new_name) {
|
||||
check_replace_inode(&src_inode, &dst_inode)?;
|
||||
self_dir.remove_entry(dst_idx);
|
||||
self_dir.substitute_entry(src_idx, (CStr256::from(new_name), src_inode.clone()));
|
||||
self_inode.dec_size();
|
||||
if is_dir {
|
||||
self_inode.dec_nlinks();
|
||||
}
|
||||
} else {
|
||||
self_dir.substitute_entry(src_idx, (CStr256::from(new_name), src_inode.clone()));
|
||||
}
|
||||
}
|
||||
// Or rename across different directories
|
||||
else {
|
||||
let (mut self_inode, mut target_inode) = write_lock_two_inodes(self, target);
|
||||
let self_inode_arc = self_inode.this.upgrade().unwrap();
|
||||
let target_inode_arc = target_inode.this.upgrade().unwrap();
|
||||
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
|
||||
let (src_idx, src_inode) = self_dir
|
||||
.get_entry(old_name)
|
||||
.ok_or(Error::new(Errno::ENOENT))?;
|
||||
// Avoid renaming a directory to a subdirectory of itself
|
||||
if Arc::ptr_eq(&src_inode, &target_inode_arc) {
|
||||
return_errno!(Errno::EINVAL);
|
||||
}
|
||||
let is_dir = src_inode.0.read().metadata.type_ == InodeType::Dir;
|
||||
|
||||
let target_dir = target_inode.inner.as_direntry_mut().unwrap();
|
||||
if let Some((dst_idx, dst_inode)) = target_dir.get_entry(new_name) {
|
||||
// Avoid renaming a subdirectory to a directory.
|
||||
if Arc::ptr_eq(&self_inode_arc, &dst_inode) {
|
||||
return_errno!(Errno::ENOTEMPTY);
|
||||
}
|
||||
check_replace_inode(&src_inode, &dst_inode)?;
|
||||
self_dir.remove_entry(src_idx);
|
||||
target_dir.remove_entry(dst_idx);
|
||||
target_dir.append_entry(new_name, src_inode.clone());
|
||||
self_inode.dec_size();
|
||||
if is_dir {
|
||||
self_inode.dec_nlinks();
|
||||
}
|
||||
} else {
|
||||
self_dir.remove_entry(src_idx);
|
||||
target_dir.append_entry(new_name, src_inode.clone());
|
||||
self_inode.dec_size();
|
||||
target_inode.inc_size();
|
||||
if is_dir {
|
||||
self_inode.dec_nlinks();
|
||||
target_inode.inc_nlinks();
|
||||
}
|
||||
}
|
||||
drop(self_inode);
|
||||
drop(target_inode);
|
||||
if is_dir {
|
||||
src_inode
|
||||
.0
|
||||
.write()
|
||||
.inner
|
||||
.as_direntry_mut()
|
||||
.unwrap()
|
||||
.set_parent(target.0.read().this.clone());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_link(&self) -> Result<String> {
|
||||
if self.0.read().metadata.type_ != InodeType::SymLink {
|
||||
return_errno_with_message!(Errno::EINVAL, "self is not symlink");
|
||||
}
|
||||
let self_inode = self.0.read();
|
||||
let link = self_inode.inner.as_symlink().unwrap();
|
||||
Ok(String::from(link))
|
||||
}
|
||||
|
||||
fn write_link(&self, target: &str) -> Result<()> {
|
||||
if self.0.read().metadata.type_ != InodeType::SymLink {
|
||||
return_errno_with_message!(Errno::EINVAL, "self is not symlink");
|
||||
}
|
||||
let mut self_inode = self.0.write();
|
||||
let link = self_inode.inner.as_symlink_mut().unwrap();
|
||||
*link = String::from(target);
|
||||
// Symlink's metadata.blocks should be 0, so just set the size.
|
||||
self_inode.metadata.size = target.len();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Metadata {
|
||||
self.0.read().metadata.clone()
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()> {
|
||||
// do nothing
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
if let Some(device) = self.0.read().inner.as_device() {
|
||||
device.poll(mask, poller)
|
||||
} else {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
Weak::upgrade(&self.0.read().fs).unwrap()
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
if let Some(device) = self.0.read().inner.as_device() {
|
||||
return device.ioctl(cmd, arg);
|
||||
}
|
||||
return_errno_with_message!(Errno::EINVAL, "ioctl is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
fn write_lock_two_inodes<'a>(
|
||||
this: &'a RamInode,
|
||||
other: &'a RamInode,
|
||||
) -> (RwLockWriteGuard<'a, Inode_>, RwLockWriteGuard<'a, Inode_>) {
|
||||
if this.0.read().metadata.ino < other.0.read().metadata.ino {
|
||||
let this = this.0.write();
|
||||
let other = other.0.write();
|
||||
(this, other)
|
||||
} else {
|
||||
let other = other.0.write();
|
||||
let this = this.0.write();
|
||||
(this, other)
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! Ramfs based on PageCache
|
||||
|
||||
pub use fs::RamFS;
|
||||
|
||||
mod fs;
|
||||
|
||||
const RAMFS_MAGIC: u64 = 0x0102_1994;
|
||||
const BLOCK_SIZE: usize = 4096;
|
||||
const ROOT_INO: usize = 1;
|
||||
const NAME_MAX: usize = 255;
|
@ -1,105 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::fs_resolver::{FsPath, FsResolver};
|
||||
use super::procfs::ProcFS;
|
||||
use super::ramfs::RamFS;
|
||||
use super::utils::{FileSystem, InodeMode, InodeType, MountNode};
|
||||
|
||||
use cpio_decoder::{CpioDecoder, FileType};
|
||||
use lending_iterator::LendingIterator;
|
||||
use libflate::gzip::Decoder as GZipDecoder;
|
||||
use spin::Once;
|
||||
|
||||
/// Unpack and prepare the rootfs from the initramfs CPIO buffer.
|
||||
pub fn init(initramfs_buf: &[u8]) -> Result<()> {
|
||||
init_root_mount();
|
||||
|
||||
println!("[kernel] unpacking the initramfs.cpio.gz to rootfs ...");
|
||||
let fs = FsResolver::new();
|
||||
let mut decoder = CpioDecoder::new(
|
||||
GZipDecoder::new(initramfs_buf)
|
||||
.map_err(|_| Error::with_message(Errno::EINVAL, "invalid gzip buffer"))?,
|
||||
);
|
||||
|
||||
loop {
|
||||
let Some(entry_result) = decoder.next() else {
|
||||
break;
|
||||
};
|
||||
|
||||
let mut entry = entry_result?;
|
||||
|
||||
// Make sure the name is a relative path, and is not end with "/".
|
||||
let entry_name = entry.name().trim_start_matches('/').trim_end_matches('/');
|
||||
if entry_name.is_empty() {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid entry name");
|
||||
}
|
||||
if entry_name == "." {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Here we assume that the directory referred by "prefix" must has been created.
|
||||
// The basis of this assumption is:
|
||||
// The mkinitramfs script uses `find` command to ensure that the entries are
|
||||
// sorted that a directory always appears before its child directories and files.
|
||||
let (parent, name) = if let Some((prefix, last)) = entry_name.rsplit_once('/') {
|
||||
(fs.lookup(&FsPath::try_from(prefix)?)?, last)
|
||||
} else {
|
||||
(fs.root().clone(), entry_name)
|
||||
};
|
||||
|
||||
let metadata = entry.metadata();
|
||||
let mode = InodeMode::from_bits_truncate(metadata.permission_mode());
|
||||
match metadata.file_type() {
|
||||
FileType::File => {
|
||||
let dentry = parent.create(name, InodeType::File, mode)?;
|
||||
entry.read_all(dentry.inode().writer(0))?;
|
||||
}
|
||||
FileType::Dir => {
|
||||
let _ = parent.create(name, InodeType::Dir, mode)?;
|
||||
}
|
||||
FileType::Link => {
|
||||
let dentry = parent.create(name, InodeType::SymLink, mode)?;
|
||||
let link_content = {
|
||||
let mut link_data: Vec<u8> = Vec::new();
|
||||
entry.read_all(&mut link_data)?;
|
||||
core::str::from_utf8(&link_data)?.to_string()
|
||||
};
|
||||
dentry.inode().write_link(&link_content)?;
|
||||
}
|
||||
type_ => {
|
||||
panic!("unsupported file type = {:?} in initramfs", type_);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Mount ProcFS
|
||||
let proc_dentry = fs.lookup(&FsPath::try_from("/proc")?)?;
|
||||
proc_dentry.mount(ProcFS::new())?;
|
||||
// Mount DevFS
|
||||
let dev_dentry = fs.lookup(&FsPath::try_from("/dev")?)?;
|
||||
dev_dentry.mount(RamFS::new())?;
|
||||
|
||||
println!("[kernel] rootfs is ready");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mount_fs_at(fs: Arc<dyn FileSystem>, fs_path: &FsPath) -> Result<()> {
|
||||
let target_dentry = FsResolver::new().lookup(fs_path)?;
|
||||
target_dentry.mount(fs)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
static ROOT_MOUNT: Once<Arc<MountNode>> = Once::new();
|
||||
|
||||
pub fn init_root_mount() {
|
||||
ROOT_MOUNT.call_once(|| -> Arc<MountNode> {
|
||||
let rootfs = RamFS::new();
|
||||
MountNode::new_root(rootfs)
|
||||
});
|
||||
}
|
||||
|
||||
pub fn root_mount() -> &'static Arc<MountNode> {
|
||||
ROOT_MOUNT.get().unwrap()
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
use aster_rights::Rights;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(u8)]
|
||||
pub enum AccessMode {
|
||||
/// read only
|
||||
O_RDONLY = 0,
|
||||
/// write only
|
||||
O_WRONLY = 1,
|
||||
/// read write
|
||||
O_RDWR = 2,
|
||||
}
|
||||
|
||||
impl AccessMode {
|
||||
pub fn is_readable(&self) -> bool {
|
||||
matches!(*self, AccessMode::O_RDONLY | AccessMode::O_RDWR)
|
||||
}
|
||||
|
||||
pub fn is_writable(&self) -> bool {
|
||||
matches!(*self, AccessMode::O_WRONLY | AccessMode::O_RDWR)
|
||||
}
|
||||
}
|
||||
|
||||
impl AccessMode {
|
||||
pub fn from_u32(flags: u32) -> Result<Self> {
|
||||
let bits = (flags & 0b11) as u8;
|
||||
if bits > Self::O_RDWR as u8 {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid bits for access mode");
|
||||
}
|
||||
Ok(match bits {
|
||||
0 => Self::O_RDONLY,
|
||||
1 => Self::O_WRONLY,
|
||||
2 => Self::O_RDWR,
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rights> for AccessMode {
|
||||
fn from(rights: Rights) -> AccessMode {
|
||||
if rights.contains(Rights::READ) && rights.contains(Rights::WRITE) {
|
||||
AccessMode::O_RDWR
|
||||
} else if rights.contains(Rights::READ) {
|
||||
AccessMode::O_RDONLY
|
||||
} else if rights.contains(Rights::WRITE) {
|
||||
AccessMode::O_WRONLY
|
||||
} else {
|
||||
panic!("invalid rights");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AccessMode> for Rights {
|
||||
fn from(access_mode: AccessMode) -> Rights {
|
||||
match access_mode {
|
||||
AccessMode::O_RDONLY => Rights::READ,
|
||||
AccessMode::O_WRONLY => Rights::WRITE,
|
||||
AccessMode::O_RDWR => Rights::READ | Rights::WRITE,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,413 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_rights_proc::require;
|
||||
use core::sync::atomic::{AtomicBool, AtomicU32, Ordering};
|
||||
use ringbuf::{HeapConsumer as HeapRbConsumer, HeapProducer as HeapRbProducer, HeapRb};
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::events::Observer;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
use aster_rights::{Read, ReadOp, TRights, Write, WriteOp};
|
||||
|
||||
use super::StatusFlags;
|
||||
|
||||
/// A unidirectional communication channel, intended to implement IPC, e.g., pipe,
|
||||
/// unix domain sockets, etc.
|
||||
pub struct Channel<T> {
|
||||
producer: Producer<T>,
|
||||
consumer: Consumer<T>,
|
||||
}
|
||||
|
||||
impl<T> Channel<T> {
|
||||
pub fn with_capacity(capacity: usize) -> Result<Self> {
|
||||
Self::with_capacity_and_flags(capacity, StatusFlags::empty())
|
||||
}
|
||||
|
||||
pub fn with_capacity_and_flags(capacity: usize, flags: StatusFlags) -> Result<Self> {
|
||||
let common = Arc::new(Common::with_capacity_and_flags(capacity, flags)?);
|
||||
let producer = Producer(EndPoint::new(common.clone(), WriteOp::new()));
|
||||
let consumer = Consumer(EndPoint::new(common, ReadOp::new()));
|
||||
Ok(Self { producer, consumer })
|
||||
}
|
||||
|
||||
pub fn split(self) -> (Producer<T>, Consumer<T>) {
|
||||
let Self { producer, consumer } = self;
|
||||
(producer, consumer)
|
||||
}
|
||||
|
||||
pub fn producer(&self) -> &Producer<T> {
|
||||
&self.producer
|
||||
}
|
||||
|
||||
pub fn consumer(&self) -> &Consumer<T> {
|
||||
&self.consumer
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.producer.0.common.capacity()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Producer<T>(EndPoint<T, WriteOp>);
|
||||
|
||||
pub struct Consumer<T>(EndPoint<T, ReadOp>);
|
||||
|
||||
macro_rules! impl_common_methods_for_channel {
|
||||
() => {
|
||||
pub fn shutdown(&self) {
|
||||
self.this_end().shutdown()
|
||||
}
|
||||
|
||||
pub fn is_shutdown(&self) -> bool {
|
||||
self.this_end().is_shutdown()
|
||||
}
|
||||
|
||||
pub fn is_peer_shutdown(&self) -> bool {
|
||||
self.peer_end().is_shutdown()
|
||||
}
|
||||
|
||||
pub fn status_flags(&self) -> StatusFlags {
|
||||
self.this_end().status_flags()
|
||||
}
|
||||
|
||||
pub fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
self.this_end().set_status_flags(new_flags)
|
||||
}
|
||||
|
||||
pub fn is_nonblocking(&self) -> bool {
|
||||
self.this_end()
|
||||
.status_flags()
|
||||
.contains(StatusFlags::O_NONBLOCK)
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.this_end().pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
pub fn register_observer(
|
||||
&self,
|
||||
observer: Weak<dyn Observer<IoEvents>>,
|
||||
mask: IoEvents,
|
||||
) -> Result<()> {
|
||||
self.this_end().pollee.register_observer(observer, mask);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unregister_observer(
|
||||
&self,
|
||||
observer: &Weak<dyn Observer<IoEvents>>,
|
||||
) -> Result<Weak<dyn Observer<IoEvents>>> {
|
||||
self.this_end()
|
||||
.pollee
|
||||
.unregister_observer(observer)
|
||||
.ok_or_else(|| Error::with_message(Errno::ENOENT, "the observer is not registered"))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<T> Producer<T> {
|
||||
fn this_end(&self) -> &EndPointInner<HeapRbProducer<T>> {
|
||||
&self.0.common.producer
|
||||
}
|
||||
|
||||
fn peer_end(&self) -> &EndPointInner<HeapRbConsumer<T>> {
|
||||
&self.0.common.consumer
|
||||
}
|
||||
|
||||
fn update_pollee(&self) {
|
||||
let this_end = self.this_end();
|
||||
let peer_end = self.peer_end();
|
||||
|
||||
// Update the event of pollee in a critical region so that pollee
|
||||
// always reflects the _true_ state of the underlying ring buffer
|
||||
// regardless of any race conditions.
|
||||
self.0.common.lock_event();
|
||||
|
||||
let rb = this_end.rb();
|
||||
if rb.is_full() {
|
||||
this_end.pollee.del_events(IoEvents::OUT);
|
||||
}
|
||||
if !rb.is_empty() {
|
||||
peer_end.pollee.add_events(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
|
||||
impl_common_methods_for_channel!();
|
||||
}
|
||||
|
||||
impl<T: Copy> Producer<T> {
|
||||
pub fn write(&self, buf: &[T]) -> Result<usize> {
|
||||
let is_nonblocking = self.is_nonblocking();
|
||||
|
||||
// Fast path
|
||||
let res = self.try_write(buf);
|
||||
if should_io_return(&res, is_nonblocking) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Slow path
|
||||
let mask = IoEvents::OUT;
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
let res = self.try_write(buf);
|
||||
if should_io_return(&res, is_nonblocking) {
|
||||
return res;
|
||||
}
|
||||
let events = self.poll(mask, Some(&poller));
|
||||
if events.is_empty() {
|
||||
// FIXME: should channel deal with timeout?
|
||||
poller.wait()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_write(&self, buf: &[T]) -> Result<usize> {
|
||||
if self.is_shutdown() || self.is_peer_shutdown() {
|
||||
return_errno!(Errno::EPIPE);
|
||||
}
|
||||
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let written_len = self.0.write(buf);
|
||||
|
||||
self.update_pollee();
|
||||
|
||||
if written_len > 0 {
|
||||
Ok(written_len)
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EAGAIN, "try write later");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Producer<T> {
|
||||
fn drop(&mut self) {
|
||||
self.shutdown();
|
||||
|
||||
self.0.common.lock_event();
|
||||
|
||||
// When reading from a channel such as a pipe or a stream socket,
|
||||
// POLLHUP merely indicates that the peer closed its end of the channel.
|
||||
self.peer_end().pollee.add_events(IoEvents::HUP);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Consumer<T> {
|
||||
fn this_end(&self) -> &EndPointInner<HeapRbConsumer<T>> {
|
||||
&self.0.common.consumer
|
||||
}
|
||||
|
||||
fn peer_end(&self) -> &EndPointInner<HeapRbProducer<T>> {
|
||||
&self.0.common.producer
|
||||
}
|
||||
|
||||
fn update_pollee(&self) {
|
||||
let this_end = self.this_end();
|
||||
let peer_end = self.peer_end();
|
||||
|
||||
// Update the event of pollee in a critical region so that pollee
|
||||
// always reflects the _true_ state of the underlying ring buffer
|
||||
// regardless of any race conditions.
|
||||
self.0.common.lock_event();
|
||||
|
||||
let rb = this_end.rb();
|
||||
if rb.is_empty() {
|
||||
this_end.pollee.del_events(IoEvents::IN);
|
||||
}
|
||||
if !rb.is_full() {
|
||||
peer_end.pollee.add_events(IoEvents::OUT);
|
||||
}
|
||||
}
|
||||
|
||||
impl_common_methods_for_channel!();
|
||||
}
|
||||
|
||||
impl<T: Copy> Consumer<T> {
|
||||
pub fn read(&self, buf: &mut [T]) -> Result<usize> {
|
||||
let is_nonblocking = self.is_nonblocking();
|
||||
|
||||
// Fast path
|
||||
let res = self.try_read(buf);
|
||||
if should_io_return(&res, is_nonblocking) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// Slow path
|
||||
let mask = IoEvents::IN;
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
let res = self.try_read(buf);
|
||||
if should_io_return(&res, is_nonblocking) {
|
||||
return res;
|
||||
}
|
||||
let events = self.poll(mask, Some(&poller));
|
||||
if events.is_empty() {
|
||||
// FIXME: should channel have timeout?
|
||||
poller.wait()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_read(&self, buf: &mut [T]) -> Result<usize> {
|
||||
if self.is_shutdown() {
|
||||
return_errno!(Errno::EPIPE);
|
||||
}
|
||||
if buf.is_empty() {
|
||||
return Ok(0);
|
||||
}
|
||||
|
||||
let read_len = self.0.read(buf);
|
||||
|
||||
self.update_pollee();
|
||||
|
||||
if self.is_peer_shutdown() {
|
||||
return Ok(read_len);
|
||||
}
|
||||
|
||||
if read_len > 0 {
|
||||
Ok(read_len)
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EAGAIN, "try read later");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for Consumer<T> {
|
||||
fn drop(&mut self) {
|
||||
self.shutdown();
|
||||
|
||||
self.0.common.lock_event();
|
||||
|
||||
// POLLERR is also set for a file descriptor referring to the write end of a pipe
|
||||
// when the read end has been closed.
|
||||
self.peer_end().pollee.add_events(IoEvents::ERR);
|
||||
}
|
||||
}
|
||||
|
||||
struct EndPoint<T, R: TRights> {
|
||||
common: Arc<Common<T>>,
|
||||
rights: R,
|
||||
}
|
||||
|
||||
impl<T, R: TRights> EndPoint<T, R> {
|
||||
pub fn new(common: Arc<Common<T>>, rights: R) -> Self {
|
||||
Self { common, rights }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy, R: TRights> EndPoint<T, R> {
|
||||
#[require(R > Read)]
|
||||
pub fn read(&self, buf: &mut [T]) -> usize {
|
||||
let mut rb = self.common.consumer.rb();
|
||||
rb.pop_slice(buf)
|
||||
}
|
||||
|
||||
#[require(R > Write)]
|
||||
pub fn write(&self, buf: &[T]) -> usize {
|
||||
let mut rb = self.common.producer.rb();
|
||||
rb.push_slice(buf)
|
||||
}
|
||||
}
|
||||
|
||||
struct Common<T> {
|
||||
producer: EndPointInner<HeapRbProducer<T>>,
|
||||
consumer: EndPointInner<HeapRbConsumer<T>>,
|
||||
event_lock: Mutex<()>,
|
||||
}
|
||||
|
||||
impl<T> Common<T> {
|
||||
fn with_capacity_and_flags(capacity: usize, flags: StatusFlags) -> Result<Self> {
|
||||
check_status_flags(flags)?;
|
||||
|
||||
if capacity == 0 {
|
||||
return_errno_with_message!(Errno::EINVAL, "capacity cannot be zero");
|
||||
}
|
||||
|
||||
let rb: HeapRb<T> = HeapRb::new(capacity);
|
||||
let (rb_producer, rb_consumer) = rb.split();
|
||||
|
||||
let producer = EndPointInner::new(rb_producer, IoEvents::OUT, flags);
|
||||
let consumer = EndPointInner::new(rb_consumer, IoEvents::empty(), flags);
|
||||
let event_lock = Mutex::new(());
|
||||
|
||||
Ok(Self {
|
||||
producer,
|
||||
consumer,
|
||||
event_lock,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lock_event(&self) -> MutexGuard<()> {
|
||||
self.event_lock.lock()
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.producer.rb().capacity()
|
||||
}
|
||||
}
|
||||
|
||||
struct EndPointInner<T> {
|
||||
rb: Mutex<T>,
|
||||
pollee: Pollee,
|
||||
is_shutdown: AtomicBool,
|
||||
status_flags: AtomicU32,
|
||||
}
|
||||
|
||||
impl<T> EndPointInner<T> {
|
||||
pub fn new(rb: T, init_events: IoEvents, status_flags: StatusFlags) -> Self {
|
||||
Self {
|
||||
rb: Mutex::new(rb),
|
||||
pollee: Pollee::new(init_events),
|
||||
is_shutdown: AtomicBool::new(false),
|
||||
status_flags: AtomicU32::new(status_flags.bits()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rb(&self) -> MutexGuard<T> {
|
||||
self.rb.lock()
|
||||
}
|
||||
|
||||
pub fn is_shutdown(&self) -> bool {
|
||||
self.is_shutdown.load(Ordering::Acquire)
|
||||
}
|
||||
|
||||
pub fn shutdown(&self) {
|
||||
self.is_shutdown.store(true, Ordering::Release)
|
||||
}
|
||||
|
||||
pub fn status_flags(&self) -> StatusFlags {
|
||||
let bits = self.status_flags.load(Ordering::Relaxed);
|
||||
StatusFlags::from_bits(bits).unwrap()
|
||||
}
|
||||
|
||||
pub fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
check_status_flags(new_flags)?;
|
||||
self.status_flags.store(new_flags.bits(), Ordering::Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn check_status_flags(flags: StatusFlags) -> Result<()> {
|
||||
let valid_flags: StatusFlags = StatusFlags::O_NONBLOCK | StatusFlags::O_DIRECT;
|
||||
if !valid_flags.contains(flags) {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid flags");
|
||||
}
|
||||
if flags.contains(StatusFlags::O_DIRECT) {
|
||||
return_errno_with_message!(Errno::EINVAL, "O_DIRECT is not supported");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn should_io_return(res: &Result<usize>, is_nonblocking: bool) -> bool {
|
||||
if is_nonblocking {
|
||||
return true;
|
||||
}
|
||||
match res {
|
||||
Ok(_) => true,
|
||||
Err(e) if e.error() == Errno::EAGAIN => false,
|
||||
Err(_) => true,
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
pub struct CreationFlags: u32 {
|
||||
/// create file if it does not exist
|
||||
const O_CREAT = 1 << 6;
|
||||
/// error if CREATE and the file exists
|
||||
const O_EXCL = 1 << 7;
|
||||
/// not become the process's controlling terminal
|
||||
const O_NOCTTY = 1 << 8;
|
||||
/// truncate file upon open
|
||||
const O_TRUNC = 1 << 9;
|
||||
/// file is a directory
|
||||
const O_DIRECTORY = 1 << 16;
|
||||
/// pathname is not a symbolic link
|
||||
const O_NOFOLLOW = 1 << 17;
|
||||
/// close on exec
|
||||
const O_CLOEXEC = 1 << 19;
|
||||
/// create an unnamed temporary regular file
|
||||
/// O_TMPFILE is (_O_TMPFILE | O_DIRECTORY)
|
||||
const _O_TMPFILE = 1 << 22;
|
||||
}
|
||||
}
|
@ -1,586 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::fs::device::Device;
|
||||
use crate::prelude::*;
|
||||
|
||||
use alloc::string::String;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
use core::time::Duration;
|
||||
|
||||
use super::{FileSystem, Inode, InodeMode, InodeType, Metadata, MountNode, NAME_MAX};
|
||||
|
||||
lazy_static! {
|
||||
static ref DCACHE: Mutex<BTreeMap<DentryKey, Arc<Dentry>>> = Mutex::new(BTreeMap::new());
|
||||
}
|
||||
|
||||
/// The dentry cache to accelerate path lookup
|
||||
pub struct Dentry {
|
||||
inode: Arc<dyn Inode>,
|
||||
name_and_parent: RwLock<Option<(String, Arc<Dentry>)>>,
|
||||
this: Weak<Dentry>,
|
||||
children: Mutex<Children>,
|
||||
mount_node: Weak<MountNode>,
|
||||
flags: AtomicU32,
|
||||
}
|
||||
|
||||
impl Dentry {
|
||||
/// Create a new root dentry with the giving inode and mount node.
|
||||
///
|
||||
/// It is been created during the construction of MountNode struct. The MountNode
|
||||
/// struct holds an arc reference to this root dentry, while this dentry holds a
|
||||
/// weak reference to the MountNode struct.
|
||||
pub(super) fn new_root(inode: Arc<dyn Inode>, mount: Weak<MountNode>) -> Arc<Self> {
|
||||
let root = Self::new(inode, DentryOptions::Root(mount));
|
||||
DCACHE.lock().insert(root.key(), root.clone());
|
||||
root
|
||||
}
|
||||
|
||||
/// Internal constructor.
|
||||
fn new(inode: Arc<dyn Inode>, options: DentryOptions) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| Self {
|
||||
inode,
|
||||
mount_node: match &options {
|
||||
DentryOptions::Root(mount) => mount.clone(),
|
||||
DentryOptions::Leaf(name_and_parent) => name_and_parent.1.mount_node.clone(),
|
||||
},
|
||||
flags: AtomicU32::new(DentryFlags::empty().bits()),
|
||||
name_and_parent: match options {
|
||||
DentryOptions::Leaf(name_and_parent) => RwLock::new(Some(name_and_parent)),
|
||||
_ => RwLock::new(None),
|
||||
},
|
||||
this: weak_self.clone(),
|
||||
children: Mutex::new(Children::new()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the overlaid dentry of self.
|
||||
///
|
||||
/// It will jump into the child mount if it is a mountpoint.
|
||||
fn overlaid_dentry(&self) -> Arc<Self> {
|
||||
if !self.is_mountpoint() {
|
||||
return self.this();
|
||||
}
|
||||
match self.mount_node().get(self) {
|
||||
Some(child_mount) => child_mount.root_dentry().overlaid_dentry(),
|
||||
None => self.this(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of dentry.
|
||||
///
|
||||
/// Returns "/" if it is a root dentry.
|
||||
fn name(&self) -> String {
|
||||
match self.name_and_parent.read().as_ref() {
|
||||
Some(name_and_parent) => name_and_parent.0.clone(),
|
||||
None => String::from("/"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the effective name of dentry.
|
||||
///
|
||||
/// If it is the root of mount, it will go up to the mountpoint to get the name
|
||||
/// of the mountpoint recursively.
|
||||
fn effective_name(&self) -> String {
|
||||
if !self.is_root_of_mount() {
|
||||
return self.name();
|
||||
}
|
||||
|
||||
match self.mount_node().mountpoint_dentry() {
|
||||
Some(self_mountpoint) => self_mountpoint.effective_name(),
|
||||
None => self.name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the parent.
|
||||
///
|
||||
/// Returns None if it is root dentry.
|
||||
fn parent(&self) -> Option<Arc<Self>> {
|
||||
self.name_and_parent
|
||||
.read()
|
||||
.as_ref()
|
||||
.map(|name_and_parent| name_and_parent.1.clone())
|
||||
}
|
||||
|
||||
/// Get the effective parent of dentry.
|
||||
///
|
||||
/// If it is the root of mount, it will go up to the mountpoint to get the parent
|
||||
/// of the mountpoint recursively.
|
||||
fn effective_parent(&self) -> Option<Arc<Self>> {
|
||||
if !self.is_root_of_mount() {
|
||||
return self.parent();
|
||||
}
|
||||
|
||||
match self.mount_node().mountpoint_dentry() {
|
||||
Some(self_mountpoint) => self_mountpoint.effective_parent(),
|
||||
None => self.parent(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_name_and_parent(&self, name: &str, parent: Arc<Self>) {
|
||||
let mut name_and_parent = self.name_and_parent.write();
|
||||
*name_and_parent = Some((String::from(name), parent));
|
||||
}
|
||||
|
||||
/// Get the arc reference to self.
|
||||
fn this(&self) -> Arc<Self> {
|
||||
self.this.upgrade().unwrap()
|
||||
}
|
||||
|
||||
/// Get the DentryKey.
|
||||
pub fn key(&self) -> DentryKey {
|
||||
DentryKey::new(self)
|
||||
}
|
||||
|
||||
/// Get the inode.
|
||||
pub fn inode(&self) -> &Arc<dyn Inode> {
|
||||
&self.inode
|
||||
}
|
||||
|
||||
/// Get the DentryFlags.
|
||||
fn flags(&self) -> DentryFlags {
|
||||
let flags = self.flags.load(Ordering::Relaxed);
|
||||
DentryFlags::from_bits(flags).unwrap()
|
||||
}
|
||||
|
||||
fn is_mountpoint(&self) -> bool {
|
||||
self.flags().contains(DentryFlags::MOUNTED)
|
||||
}
|
||||
|
||||
fn set_mountpoint(&self) {
|
||||
self.flags
|
||||
.fetch_or(DentryFlags::MOUNTED.bits(), Ordering::Release);
|
||||
}
|
||||
|
||||
fn clear_mountpoint(&self) {
|
||||
self.flags
|
||||
.fetch_and(!(DentryFlags::MOUNTED.bits()), Ordering::Release);
|
||||
}
|
||||
|
||||
/// Currently, the root dentry of a fs is the root of a mount.
|
||||
fn is_root_of_mount(&self) -> bool {
|
||||
self.name_and_parent.read().as_ref().is_none()
|
||||
}
|
||||
|
||||
/// Get the mount node which the dentry belongs to.
|
||||
pub fn mount_node(&self) -> Arc<MountNode> {
|
||||
self.mount_node.upgrade().unwrap()
|
||||
}
|
||||
|
||||
/// Create a dentry by making inode.
|
||||
pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> {
|
||||
if self.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
let mut children = self.children.lock();
|
||||
if children.find_dentry(name).is_some() {
|
||||
return_errno!(Errno::EEXIST);
|
||||
}
|
||||
|
||||
let child = {
|
||||
let inode = self.inode.create(name, type_, mode)?;
|
||||
let dentry = Self::new(
|
||||
inode,
|
||||
DentryOptions::Leaf((String::from(name), self.this())),
|
||||
);
|
||||
children.insert_dentry(&dentry);
|
||||
dentry
|
||||
};
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
/// Create a dentry by making a device inode.
|
||||
pub fn mknod(&self, name: &str, mode: InodeMode, device: Arc<dyn Device>) -> Result<Arc<Self>> {
|
||||
if self.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
let mut children = self.children.lock();
|
||||
if children.find_dentry(name).is_some() {
|
||||
return_errno!(Errno::EEXIST);
|
||||
}
|
||||
|
||||
let child = {
|
||||
let inode = self.inode.mknod(name, mode, device)?;
|
||||
let dentry = Self::new(
|
||||
inode,
|
||||
DentryOptions::Leaf((String::from(name), self.this())),
|
||||
);
|
||||
children.insert_dentry(&dentry);
|
||||
dentry
|
||||
};
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
/// Lookup a dentry.
|
||||
pub fn lookup(&self, name: &str) -> Result<Arc<Self>> {
|
||||
if self.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
if !self.inode.mode().is_executable() {
|
||||
return_errno!(Errno::EACCES);
|
||||
}
|
||||
if name.len() > NAME_MAX {
|
||||
return_errno!(Errno::ENAMETOOLONG);
|
||||
}
|
||||
|
||||
let dentry = match name {
|
||||
"." => self.this(),
|
||||
".." => self.effective_parent().unwrap_or(self.this()),
|
||||
name => {
|
||||
let mut children = self.children.lock();
|
||||
match children.find_dentry(name) {
|
||||
Some(dentry) => dentry.overlaid_dentry(),
|
||||
None => {
|
||||
let inode = self.inode.lookup(name)?;
|
||||
let dentry = Self::new(
|
||||
inode,
|
||||
DentryOptions::Leaf((String::from(name), self.this())),
|
||||
);
|
||||
children.insert_dentry(&dentry);
|
||||
dentry
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(dentry)
|
||||
}
|
||||
|
||||
/// Link a new name for the dentry by linking inode.
|
||||
pub fn link(&self, old: &Arc<Self>, name: &str) -> Result<()> {
|
||||
if self.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
let mut children = self.children.lock();
|
||||
if children.find_dentry(name).is_some() {
|
||||
return_errno!(Errno::EEXIST);
|
||||
}
|
||||
if !Arc::ptr_eq(&old.mount_node(), &self.mount_node()) {
|
||||
return_errno_with_message!(Errno::EXDEV, "cannot cross mount");
|
||||
}
|
||||
let old_inode = old.inode();
|
||||
self.inode.link(old_inode, name)?;
|
||||
let dentry = Self::new(
|
||||
old_inode.clone(),
|
||||
DentryOptions::Leaf((String::from(name), self.this())),
|
||||
);
|
||||
children.insert_dentry(&dentry);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a dentry by unlinking inode.
|
||||
pub fn unlink(&self, name: &str) -> Result<()> {
|
||||
if self.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
let mut children = self.children.lock();
|
||||
let _ = children.find_dentry_with_checking_mountpoint(name)?;
|
||||
self.inode.unlink(name)?;
|
||||
children.delete_dentry(name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a directory dentry by rmdiring inode.
|
||||
pub fn rmdir(&self, name: &str) -> Result<()> {
|
||||
if self.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
let mut children = self.children.lock();
|
||||
let _ = children.find_dentry_with_checking_mountpoint(name)?;
|
||||
self.inode.rmdir(name)?;
|
||||
children.delete_dentry(name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rename a dentry to the new dentry by renaming inode.
|
||||
pub fn rename(&self, old_name: &str, new_dir: &Arc<Self>, new_name: &str) -> Result<()> {
|
||||
if old_name == "." || old_name == ".." || new_name == "." || new_name == ".." {
|
||||
return_errno_with_message!(Errno::EISDIR, "old_name or new_name is a directory");
|
||||
}
|
||||
if self.inode.type_() != InodeType::Dir || new_dir.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
|
||||
// Self and new_dir are same Dentry, just modify name
|
||||
if Arc::ptr_eq(&self.this(), new_dir) {
|
||||
if old_name == new_name {
|
||||
return Ok(());
|
||||
}
|
||||
let mut children = self.children.lock();
|
||||
let old_dentry = children.find_dentry_with_checking_mountpoint(old_name)?;
|
||||
let _ = children.find_dentry_with_checking_mountpoint(new_name)?;
|
||||
self.inode.rename(old_name, &self.inode, new_name)?;
|
||||
match old_dentry.as_ref() {
|
||||
Some(dentry) => {
|
||||
children.delete_dentry(old_name);
|
||||
dentry.set_name_and_parent(new_name, self.this());
|
||||
children.insert_dentry(dentry);
|
||||
}
|
||||
None => {
|
||||
children.delete_dentry(new_name);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Self and new_dir are different Dentry
|
||||
if !Arc::ptr_eq(&self.mount_node(), &new_dir.mount_node()) {
|
||||
return_errno_with_message!(Errno::EXDEV, "cannot cross mount");
|
||||
}
|
||||
let (mut self_children, mut new_dir_children) =
|
||||
write_lock_children_on_two_dentries(self, new_dir);
|
||||
let old_dentry = self_children.find_dentry_with_checking_mountpoint(old_name)?;
|
||||
let _ = new_dir_children.find_dentry_with_checking_mountpoint(new_name)?;
|
||||
self.inode.rename(old_name, &new_dir.inode, new_name)?;
|
||||
match old_dentry.as_ref() {
|
||||
Some(dentry) => {
|
||||
self_children.delete_dentry(old_name);
|
||||
dentry.set_name_and_parent(new_name, new_dir.this());
|
||||
new_dir_children.insert_dentry(dentry);
|
||||
}
|
||||
None => {
|
||||
new_dir_children.delete_dentry(new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Mount the fs on this dentry. It will make this dentry to be a mountpoint.
|
||||
///
|
||||
/// If the given mountpoint has already been mounted, then its mounted child mount
|
||||
/// will be updated.
|
||||
/// The root dentry cannot be mounted.
|
||||
///
|
||||
/// Return the mounted child mount.
|
||||
pub fn mount(&self, fs: Arc<dyn FileSystem>) -> Result<Arc<MountNode>> {
|
||||
if self.inode.type_() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
if self.effective_parent().is_none() {
|
||||
return_errno_with_message!(Errno::EINVAL, "can not mount on root");
|
||||
}
|
||||
|
||||
let child_mount = self.mount_node().mount(fs, &self.this())?;
|
||||
self.set_mountpoint();
|
||||
Ok(child_mount)
|
||||
}
|
||||
|
||||
/// Unmount and return the mounted child mount.
|
||||
///
|
||||
/// Note that the root mount cannot be unmounted.
|
||||
pub fn umount(&self) -> Result<Arc<MountNode>> {
|
||||
if !self.is_root_of_mount() {
|
||||
return_errno_with_message!(Errno::EINVAL, "not mounted");
|
||||
}
|
||||
|
||||
let mount_node = self.mount_node();
|
||||
let Some(mountpoint) = mount_node.mountpoint_dentry() else {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot umount root mount");
|
||||
};
|
||||
|
||||
let child_mount = mountpoint.mount_node().umount(mountpoint)?;
|
||||
mountpoint.clear_mountpoint();
|
||||
Ok(child_mount)
|
||||
}
|
||||
|
||||
/// Get the filesystem the inode belongs to
|
||||
pub fn fs(&self) -> Arc<dyn FileSystem> {
|
||||
self.inode.fs()
|
||||
}
|
||||
|
||||
/// Flushes all changes made to data and metadata to the device.
|
||||
pub fn sync(&self) -> Result<()> {
|
||||
self.inode.sync()
|
||||
}
|
||||
|
||||
/// Get the inode metadata
|
||||
pub fn inode_metadata(&self) -> Metadata {
|
||||
self.inode.metadata()
|
||||
}
|
||||
|
||||
/// Get the inode type
|
||||
pub fn inode_type(&self) -> InodeType {
|
||||
self.inode.type_()
|
||||
}
|
||||
|
||||
/// Get the inode permission mode
|
||||
pub fn inode_mode(&self) -> InodeMode {
|
||||
self.inode.mode()
|
||||
}
|
||||
|
||||
/// Set the inode permission mode
|
||||
pub fn set_inode_mode(&self, mode: InodeMode) {
|
||||
self.inode.set_mode(mode)
|
||||
}
|
||||
|
||||
/// Gets the size of the inode
|
||||
pub fn inode_size(&self) -> usize {
|
||||
self.inode.size()
|
||||
}
|
||||
|
||||
/// Sets the size of the inode
|
||||
pub fn set_inode_size(&self, new_size: usize) -> Result<()> {
|
||||
self.inode.resize(new_size)
|
||||
}
|
||||
|
||||
/// Get the access timestamp
|
||||
pub fn atime(&self) -> Duration {
|
||||
self.inode.atime()
|
||||
}
|
||||
|
||||
/// Set the access timestamp
|
||||
pub fn set_atime(&self, time: Duration) {
|
||||
self.inode.set_atime(time)
|
||||
}
|
||||
|
||||
/// Get the modified timestamp
|
||||
pub fn mtime(&self) -> Duration {
|
||||
self.inode.mtime()
|
||||
}
|
||||
|
||||
/// Set the modified timestamp
|
||||
pub fn set_mtime(&self, time: Duration) {
|
||||
self.inode.set_mtime(time)
|
||||
}
|
||||
|
||||
/// Get the absolute path.
|
||||
///
|
||||
/// It will resolve the mountpoint automatically.
|
||||
pub fn abs_path(&self) -> String {
|
||||
let mut path = self.effective_name();
|
||||
let mut dentry = self.this();
|
||||
|
||||
loop {
|
||||
match dentry.effective_parent() {
|
||||
None => break,
|
||||
Some(parent_dentry) => {
|
||||
path = {
|
||||
let parent_name = parent_dentry.effective_name();
|
||||
if parent_name != "/" {
|
||||
parent_name + "/" + &path
|
||||
} else {
|
||||
parent_name + &path
|
||||
}
|
||||
};
|
||||
dentry = parent_dentry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(path.starts_with('/'));
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Dentry {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("Dentry")
|
||||
.field("abs_path", &self.abs_path())
|
||||
.field("inode", &self.inode)
|
||||
.field("flags", &self.flags())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// DentryKey is the unique identifier for Dentry in DCACHE.
|
||||
///
|
||||
/// For none-root dentries, it uses self's name and parent's pointer to form the key,
|
||||
/// meanwhile, the root dentry uses "/" and self's pointer to form the key.
|
||||
#[derive(Debug, Clone, Hash, PartialOrd, Ord, Eq, PartialEq)]
|
||||
pub struct DentryKey {
|
||||
name: String,
|
||||
parent_ptr: usize,
|
||||
}
|
||||
|
||||
impl DentryKey {
|
||||
/// Form the DentryKey for the dentry.
|
||||
pub fn new(dentry: &Dentry) -> Self {
|
||||
let (name, parent) = match dentry.name_and_parent.read().as_ref() {
|
||||
Some(name_and_parent) => name_and_parent.clone(),
|
||||
None => (String::from("/"), dentry.this()),
|
||||
};
|
||||
Self {
|
||||
name,
|
||||
parent_ptr: Arc::as_ptr(&parent) as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct DentryFlags: u32 {
|
||||
const MOUNTED = 1 << 0;
|
||||
}
|
||||
}
|
||||
|
||||
enum DentryOptions {
|
||||
Root(Weak<MountNode>),
|
||||
Leaf((String, Arc<Dentry>)),
|
||||
}
|
||||
|
||||
struct Children {
|
||||
inner: BTreeMap<String, Weak<Dentry>>,
|
||||
}
|
||||
|
||||
impl Children {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_dentry(&mut self, dentry: &Arc<Dentry>) {
|
||||
// Do not cache it in DCACHE and children if is not cacheable.
|
||||
// When we look up it from the parent, it will always be newly created.
|
||||
if !dentry.inode().is_dentry_cacheable() {
|
||||
return;
|
||||
}
|
||||
|
||||
DCACHE.lock().insert(dentry.key(), dentry.clone());
|
||||
self.inner.insert(dentry.name(), Arc::downgrade(dentry));
|
||||
}
|
||||
|
||||
pub fn delete_dentry(&mut self, name: &str) -> Option<Arc<Dentry>> {
|
||||
self.inner
|
||||
.remove(name)
|
||||
.and_then(|d| d.upgrade())
|
||||
.and_then(|d| DCACHE.lock().remove(&d.key()))
|
||||
}
|
||||
|
||||
pub fn find_dentry(&mut self, name: &str) -> Option<Arc<Dentry>> {
|
||||
if let Some(dentry) = self.inner.get(name) {
|
||||
dentry.upgrade().or_else(|| {
|
||||
self.inner.remove(name);
|
||||
None
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_dentry_with_checking_mountpoint(
|
||||
&mut self,
|
||||
name: &str,
|
||||
) -> Result<Option<Arc<Dentry>>> {
|
||||
let dentry = self.find_dentry(name);
|
||||
if let Some(dentry) = dentry.as_ref() {
|
||||
if dentry.is_mountpoint() {
|
||||
return_errno_with_message!(Errno::EBUSY, "dentry is mountpint");
|
||||
}
|
||||
}
|
||||
Ok(dentry)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_lock_children_on_two_dentries<'a>(
|
||||
this: &'a Dentry,
|
||||
other: &'a Dentry,
|
||||
) -> (MutexGuard<'a, Children>, MutexGuard<'a, Children>) {
|
||||
let this_key = this.key();
|
||||
let other_key = other.key();
|
||||
if this_key < other_key {
|
||||
let this = this.children.lock();
|
||||
let other = other.children.lock();
|
||||
(this, other)
|
||||
} else {
|
||||
let other = other.children.lock();
|
||||
let this = this.children.lock();
|
||||
(this, other)
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::InodeType;
|
||||
use crate::prelude::*;
|
||||
|
||||
/// A visitor for dir entries.
|
||||
pub trait DirentVisitor {
|
||||
/// Visit a dir entry.
|
||||
///
|
||||
/// If the visitor succeeds in visiting the given inode, an `Ok(())` is returned;
|
||||
/// Otherwise, an error is returned. Different implementations for `DirentVisitor`
|
||||
/// may choose to report errors for different reasons. Regardless of the exact
|
||||
/// errors and reasons, `readdir`-family methods shall stop feeding the visitor
|
||||
/// with the next inode as long as an error is returned by the visitor.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// `Vec<String>` is implemented as `DirentVisitor` so that the file names
|
||||
/// under a dir can be easily collected, which is convenient for testing purposes.
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut all_dirents = Vec::new();
|
||||
/// let dir_inode = todo!("create an inode");
|
||||
/// dir_inode.readdir_at(0, &mut all_dirents).unwrap();
|
||||
/// ```
|
||||
fn visit(&mut self, name: &str, ino: u64, type_: InodeType, offset: usize) -> Result<()>;
|
||||
}
|
||||
|
||||
impl DirentVisitor for Vec<String> {
|
||||
fn visit(&mut self, name: &str, ino: u64, type_: InodeType, offset: usize) -> Result<()> {
|
||||
self.push(name.into());
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::Inode;
|
||||
use crate::prelude::*;
|
||||
|
||||
use aster_util::slot_vec::SlotVec;
|
||||
|
||||
pub trait DirEntryVecExt {
|
||||
/// If the entry is not found by `name`, use `f` to get the inode, then put the entry into vec.
|
||||
fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc<dyn Inode>);
|
||||
|
||||
/// Remove and returns the entry by name.
|
||||
/// Returns `None` if the entry has been removed.
|
||||
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)>;
|
||||
}
|
||||
|
||||
impl DirEntryVecExt for SlotVec<(String, Arc<dyn Inode>)> {
|
||||
fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc<dyn Inode>) {
|
||||
if !self.iter().any(|(child_name, _)| child_name == name) {
|
||||
let inode = f();
|
||||
self.put((String::from(name), inode));
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)> {
|
||||
let idx = self
|
||||
.idxes_and_items()
|
||||
.find(|(_, (child_name, _))| child_name == name)
|
||||
.map(|(idx, _)| idx);
|
||||
if let Some(idx) = idx {
|
||||
self.remove(idx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
/// A mask for the file mode of a newly-created file or directory.
|
||||
///
|
||||
/// This mask is always a subset of `0o777`.
|
||||
pub struct FileCreationMask(u16);
|
||||
|
||||
impl FileCreationMask {
|
||||
// Creates a new instance, the initial value is `0o777`.
|
||||
pub fn new(val: u16) -> Self {
|
||||
Self(0o777 & val)
|
||||
}
|
||||
|
||||
/// Get a new value.
|
||||
pub fn get(&self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Set a new value.
|
||||
pub fn set(&mut self, new_mask: u16) -> u16 {
|
||||
let new_mask = new_mask & 0o777;
|
||||
let old_mask = self.0;
|
||||
self.0 = new_mask;
|
||||
old_mask
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for FileCreationMask {
|
||||
fn default() -> Self {
|
||||
Self(0o022)
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::Inode;
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SuperBlock {
|
||||
pub magic: u64,
|
||||
pub bsize: usize,
|
||||
pub blocks: usize,
|
||||
pub bfree: usize,
|
||||
pub bavail: usize,
|
||||
pub files: usize,
|
||||
pub ffree: usize,
|
||||
pub fsid: u64,
|
||||
pub namelen: usize,
|
||||
pub frsize: usize,
|
||||
pub flags: u64,
|
||||
}
|
||||
|
||||
impl SuperBlock {
|
||||
pub fn new(magic: u64, block_size: usize, name_max_len: usize) -> Self {
|
||||
Self {
|
||||
magic,
|
||||
bsize: block_size,
|
||||
blocks: 0,
|
||||
bfree: 0,
|
||||
bavail: 0,
|
||||
files: 0,
|
||||
ffree: 0,
|
||||
fsid: 0,
|
||||
namelen: name_max_len,
|
||||
frsize: block_size,
|
||||
flags: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct FsFlags: u32 {
|
||||
/// Dentry cannot be evicted.
|
||||
const DENTRY_UNEVICTABLE = 1 << 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FileSystem: Any + Sync + Send {
|
||||
fn sync(&self) -> Result<()>;
|
||||
|
||||
fn root_inode(&self) -> Arc<dyn Inode>;
|
||||
|
||||
fn sb(&self) -> SuperBlock;
|
||||
|
||||
fn flags(&self) -> FsFlags;
|
||||
}
|
||||
|
||||
impl dyn FileSystem {
|
||||
pub fn downcast_ref<T: FileSystem>(&self) -> Option<&T> {
|
||||
(self as &dyn Any).downcast_ref::<T>()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for dyn FileSystem {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("FileSystem")
|
||||
.field("super_block", &self.sb())
|
||||
.field("flags", &self.flags())
|
||||
.finish()
|
||||
}
|
||||
}
|
@ -1,419 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_rights::Full;
|
||||
use core::time::Duration;
|
||||
use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
|
||||
|
||||
use super::{DirentVisitor, FileSystem, IoctlCmd, SuperBlock};
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::device::{Device, DeviceType};
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
use crate::vm::vmo::Vmo;
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromInt)]
|
||||
pub enum InodeType {
|
||||
NamedPipe = 0o010000,
|
||||
CharDevice = 0o020000,
|
||||
Dir = 0o040000,
|
||||
BlockDevice = 0o060000,
|
||||
File = 0o100000,
|
||||
SymLink = 0o120000,
|
||||
Socket = 0o140000,
|
||||
}
|
||||
|
||||
impl InodeType {
|
||||
pub fn support_read(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
InodeType::File | InodeType::Socket | InodeType::CharDevice | InodeType::BlockDevice
|
||||
)
|
||||
}
|
||||
|
||||
pub fn support_write(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
InodeType::File | InodeType::Socket | InodeType::CharDevice | InodeType::BlockDevice
|
||||
)
|
||||
}
|
||||
|
||||
pub fn is_reguler_file(&self) -> bool {
|
||||
*self == InodeType::File
|
||||
}
|
||||
|
||||
pub fn is_directory(&self) -> bool {
|
||||
*self == InodeType::Dir
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceType> for InodeType {
|
||||
fn from(type_: DeviceType) -> InodeType {
|
||||
match type_ {
|
||||
DeviceType::CharDevice => InodeType::CharDevice,
|
||||
DeviceType::BlockDevice => InodeType::BlockDevice,
|
||||
DeviceType::MiscDevice => InodeType::CharDevice,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
pub struct InodeMode: u16 {
|
||||
/// set-user-ID
|
||||
const S_ISUID = 0o4000;
|
||||
/// set-group-ID
|
||||
const S_ISGID = 0o2000;
|
||||
/// sticky bit
|
||||
const S_ISVTX = 0o1000;
|
||||
/// read by owner
|
||||
const S_IRUSR = 0o0400;
|
||||
/// write by owner
|
||||
const S_IWUSR = 0o0200;
|
||||
/// execute/search by owner
|
||||
const S_IXUSR = 0o0100;
|
||||
/// read by group
|
||||
const S_IRGRP = 0o0040;
|
||||
/// write by group
|
||||
const S_IWGRP = 0o0020;
|
||||
/// execute/search by group
|
||||
const S_IXGRP = 0o0010;
|
||||
/// read by others
|
||||
const S_IROTH = 0o0004;
|
||||
/// write by others
|
||||
const S_IWOTH = 0o0002;
|
||||
/// execute/search by others
|
||||
const S_IXOTH = 0o0001;
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeMode {
|
||||
pub fn is_readable(&self) -> bool {
|
||||
self.contains(Self::S_IRUSR)
|
||||
}
|
||||
|
||||
pub fn is_writable(&self) -> bool {
|
||||
self.contains(Self::S_IWUSR)
|
||||
}
|
||||
|
||||
pub fn is_executable(&self) -> bool {
|
||||
self.contains(Self::S_IXUSR)
|
||||
}
|
||||
|
||||
pub fn has_sticky_bit(&self) -> bool {
|
||||
self.contains(Self::S_ISVTX)
|
||||
}
|
||||
|
||||
pub fn has_set_uid(&self) -> bool {
|
||||
self.contains(Self::S_ISUID)
|
||||
}
|
||||
|
||||
pub fn has_set_gid(&self) -> bool {
|
||||
self.contains(Self::S_ISGID)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Metadata {
|
||||
pub dev: u64,
|
||||
pub ino: usize,
|
||||
pub size: usize,
|
||||
pub blk_size: usize,
|
||||
pub blocks: usize,
|
||||
pub atime: Duration,
|
||||
pub mtime: Duration,
|
||||
pub ctime: Duration,
|
||||
pub type_: InodeType,
|
||||
pub mode: InodeMode,
|
||||
pub nlinks: usize,
|
||||
pub uid: usize,
|
||||
pub gid: usize,
|
||||
pub rdev: u64,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
pub fn new_dir(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 2,
|
||||
blk_size: sb.bsize,
|
||||
blocks: 1,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::Dir,
|
||||
mode,
|
||||
nlinks: 2,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_file(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size: sb.bsize,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::File,
|
||||
mode,
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_symlink(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size: sb.bsize,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::SymLink,
|
||||
mode,
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
pub fn new_device(ino: usize, mode: InodeMode, sb: &SuperBlock, device: &dyn Device) -> Self {
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size: sb.bsize,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::from(device.type_()),
|
||||
mode,
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: device.id().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_socket(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Metadata {
|
||||
Self {
|
||||
dev: 0,
|
||||
ino,
|
||||
size: 0,
|
||||
blk_size: sb.bsize,
|
||||
blocks: 0,
|
||||
atime: Default::default(),
|
||||
mtime: Default::default(),
|
||||
ctime: Default::default(),
|
||||
type_: InodeType::Socket,
|
||||
mode,
|
||||
nlinks: 1,
|
||||
uid: 0,
|
||||
gid: 0,
|
||||
rdev: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Inode: Any + Sync + Send {
|
||||
fn size(&self) -> usize;
|
||||
|
||||
fn resize(&self, new_size: usize) -> Result<()>;
|
||||
|
||||
fn metadata(&self) -> Metadata;
|
||||
|
||||
fn ino(&self) -> u64;
|
||||
|
||||
fn type_(&self) -> InodeType;
|
||||
|
||||
fn mode(&self) -> InodeMode;
|
||||
|
||||
fn set_mode(&self, mode: InodeMode);
|
||||
|
||||
fn atime(&self) -> Duration;
|
||||
|
||||
fn set_atime(&self, time: Duration);
|
||||
|
||||
fn mtime(&self) -> Duration;
|
||||
|
||||
fn set_mtime(&self, time: Duration);
|
||||
|
||||
fn page_cache(&self) -> Option<Vmo<Full>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn mknod(&self, name: &str, mode: InodeMode, dev: Arc<dyn Device>) -> Result<Arc<dyn Inode>> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn as_device(&self) -> Option<Arc<dyn Device>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn link(&self, old: &Arc<dyn Inode>, name: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn unlink(&self, name: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn rmdir(&self, name: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::ENOTDIR))
|
||||
}
|
||||
|
||||
fn read_link(&self) -> Result<String> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn write_link(&self, target: &str) -> Result<()> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
|
||||
Err(Error::new(Errno::EISDIR))
|
||||
}
|
||||
|
||||
fn sync(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, _poller: Option<&Poller>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
|
||||
fn fs(&self) -> Arc<dyn FileSystem>;
|
||||
|
||||
/// Returns whether a VFS dentry for this inode should be put into the dentry cache.
|
||||
///
|
||||
/// The dentry cache in the VFS layer can accelerate the lookup of inodes. So usually,
|
||||
/// it is preferable to use the dentry cache. And thus, the default return value of this method
|
||||
/// is `true`.
|
||||
///
|
||||
/// 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. 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
|
||||
/// inode returns `true`.
|
||||
fn is_dentry_cacheable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl dyn Inode {
|
||||
pub fn downcast_ref<T: Inode>(&self) -> Option<&T> {
|
||||
(self as &dyn Any).downcast_ref::<T>()
|
||||
}
|
||||
|
||||
pub fn read_all(&self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
if !self.type_().support_read() {
|
||||
return_errno!(Errno::EISDIR);
|
||||
}
|
||||
|
||||
let file_size = self.size();
|
||||
if buf.len() < file_size {
|
||||
buf.resize(file_size, 0);
|
||||
}
|
||||
self.read_at(0, &mut buf[..file_size])
|
||||
}
|
||||
|
||||
pub fn read_direct_all(&self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||
if !self.type_().support_read() {
|
||||
return_errno!(Errno::EISDIR);
|
||||
}
|
||||
|
||||
let file_size = self.size();
|
||||
if buf.len() < file_size {
|
||||
buf.resize(file_size, 0);
|
||||
}
|
||||
self.read_direct_at(0, &mut buf[..file_size])
|
||||
}
|
||||
|
||||
pub fn writer(&self, from_offset: usize) -> InodeWriter {
|
||||
InodeWriter {
|
||||
inner: self,
|
||||
offset: from_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InodeWriter<'a> {
|
||||
inner: &'a dyn Inode,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Write for InodeWriter<'a> {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
|
||||
let write_len = self
|
||||
.inner
|
||||
.write_at(self.offset, buf)
|
||||
.map_err(|_| IoError::new(IoErrorKind::WriteZero, "failed to write buffer"))?;
|
||||
self.offset += write_len;
|
||||
Ok(write_len)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> IoResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for dyn Inode {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("Inode")
|
||||
.field("metadata", &self.metadata())
|
||||
.field("fs", &self.fs())
|
||||
.finish()
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[repr(u32)]
|
||||
#[derive(Debug, Clone, Copy, TryFromInt)]
|
||||
pub enum IoctlCmd {
|
||||
/// Get terminal attributes
|
||||
TCGETS = 0x5401,
|
||||
TCSETS = 0x5402,
|
||||
/// Drain the output buffer and set attributes
|
||||
TCSETSW = 0x5403,
|
||||
/// Drain the output buffer, and discard pending input, and set attributes
|
||||
TCSETSF = 0x5404,
|
||||
/// Make the given terminal the controlling terminal of the calling process.
|
||||
TIOCSCTTY = 0x540e,
|
||||
/// Get the process group ID of the foreground process group on this terminal
|
||||
TIOCGPGRP = 0x540f,
|
||||
/// Set the foreground process group ID of this terminal.
|
||||
TIOCSPGRP = 0x5410,
|
||||
/// Get the number of bytes in the input buffer.
|
||||
FIONREAD = 0x541B,
|
||||
/// Set window size
|
||||
TIOCGWINSZ = 0x5413,
|
||||
TIOCSWINSZ = 0x5414,
|
||||
/// the calling process gives up this controlling terminal
|
||||
TIOCNOTTY = 0x5422,
|
||||
/// Get Pty Number
|
||||
TIOCGPTN = 0x80045430,
|
||||
/// Lock/unlock Pty
|
||||
TIOCSPTLCK = 0x40045431,
|
||||
/// Safely open the slave
|
||||
TIOCGPTPEER = 0x40045441,
|
||||
/// Get tdx report using TDCALL
|
||||
TDXGETREPORT = 0xc4405401,
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! VFS components
|
||||
|
||||
pub use access_mode::AccessMode;
|
||||
pub use channel::{Channel, Consumer, Producer};
|
||||
pub use creation_flags::CreationFlags;
|
||||
pub use dentry::{Dentry, DentryKey};
|
||||
pub use dirent_visitor::DirentVisitor;
|
||||
pub use direntry_vec::DirEntryVecExt;
|
||||
pub use file_creation_mask::FileCreationMask;
|
||||
pub use fs::{FileSystem, FsFlags, SuperBlock};
|
||||
pub use inode::{Inode, InodeMode, InodeType, Metadata};
|
||||
pub use ioctl::IoctlCmd;
|
||||
pub use mount::MountNode;
|
||||
pub use page_cache::{PageCache, PageCacheBackend};
|
||||
pub use status_flags::StatusFlags;
|
||||
|
||||
mod access_mode;
|
||||
mod channel;
|
||||
mod creation_flags;
|
||||
mod dentry;
|
||||
mod dirent_visitor;
|
||||
mod direntry_vec;
|
||||
mod file_creation_mask;
|
||||
mod fs;
|
||||
mod inode;
|
||||
mod ioctl;
|
||||
mod mount;
|
||||
mod page_cache;
|
||||
mod status_flags;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
|
||||
pub enum SeekFrom {
|
||||
Start(usize),
|
||||
End(isize),
|
||||
Current(isize),
|
||||
}
|
||||
|
||||
/// Maximum bytes in a path
|
||||
pub const PATH_MAX: usize = 4096;
|
||||
|
||||
/// Maximum bytes in a file name
|
||||
pub const NAME_MAX: usize = 255;
|
||||
|
||||
/// The upper limit for resolving symbolic links
|
||||
pub const SYMLINKS_MAX: usize = 40;
|
||||
|
||||
pub type CStr256 = FixedCStr<256>;
|
||||
pub type Str16 = FixedStr<16>;
|
||||
pub type Str64 = FixedStr<64>;
|
||||
|
||||
/// An owned C-compatible string with a fixed capacity of `N`.
|
||||
///
|
||||
/// The string is terminated with a null byte.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Pod)]
|
||||
pub struct FixedCStr<const N: usize>([u8; N]);
|
||||
|
||||
impl<const N: usize> FixedCStr<N> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.iter().position(|&b| b == 0).unwrap()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Result<&str> {
|
||||
Ok(alloc::str::from_utf8(self.as_bytes())?)
|
||||
}
|
||||
|
||||
pub fn as_cstr(&self) -> Result<&CStr> {
|
||||
Ok(CStr::from_bytes_with_nul(self.as_bytes_with_nul())?)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0[0..self.len()]
|
||||
}
|
||||
|
||||
pub fn as_bytes_with_nul(&self) -> &[u8] {
|
||||
&self.0[0..=self.len()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> From<&'a [u8]> for FixedCStr<N> {
|
||||
fn from(bytes: &'a [u8]) -> Self {
|
||||
assert!(N > 0);
|
||||
|
||||
let mut inner = [0u8; N];
|
||||
let len = {
|
||||
let mut nul_byte_idx = match bytes.iter().position(|&b| b == 0) {
|
||||
Some(idx) => idx,
|
||||
None => bytes.len(),
|
||||
};
|
||||
if nul_byte_idx >= N {
|
||||
nul_byte_idx = N - 1;
|
||||
}
|
||||
nul_byte_idx
|
||||
};
|
||||
inner[0..len].copy_from_slice(&bytes[0..len]);
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> From<&'a str> for FixedCStr<N> {
|
||||
fn from(string: &'a str) -> Self {
|
||||
let bytes = string.as_bytes();
|
||||
Self::from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> From<&'a CStr> for FixedCStr<N> {
|
||||
fn from(cstr: &'a CStr) -> Self {
|
||||
let bytes = cstr.to_bytes_with_nul();
|
||||
Self::from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Default for FixedCStr<N> {
|
||||
fn default() -> Self {
|
||||
Self([0u8; N])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Debug for FixedCStr<N> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self.as_cstr() {
|
||||
Ok(cstr) => write!(f, "{:?}", cstr),
|
||||
Err(_) => write!(f, "{:?}", self.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An owned string with a fixed capacity of `N`.
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Pod)]
|
||||
pub struct FixedStr<const N: usize>([u8; N]);
|
||||
|
||||
impl<const N: usize> FixedStr<N> {
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.iter().position(|&b| b == 0).unwrap_or(N)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> Result<&str> {
|
||||
Ok(alloc::str::from_utf8(self.as_bytes())?)
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0[0..self.len()]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> From<&'a [u8]> for FixedStr<N> {
|
||||
fn from(bytes: &'a [u8]) -> Self {
|
||||
let mut inner = [0u8; N];
|
||||
let len = {
|
||||
let mut nul_byte_idx = match bytes.iter().position(|&b| b == 0) {
|
||||
Some(idx) => idx,
|
||||
None => bytes.len(),
|
||||
};
|
||||
if nul_byte_idx > N {
|
||||
nul_byte_idx = N;
|
||||
}
|
||||
nul_byte_idx
|
||||
};
|
||||
inner[0..len].copy_from_slice(&bytes[0..len]);
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, const N: usize> From<&'a str> for FixedStr<N> {
|
||||
fn from(string: &'a str) -> Self {
|
||||
let bytes = string.as_bytes();
|
||||
Self::from(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Default for FixedStr<N> {
|
||||
fn default() -> Self {
|
||||
Self([0u8; N])
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Debug for FixedStr<N> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
match self.as_str() {
|
||||
Ok(string) => write!(f, "{}", string),
|
||||
Err(_) => write!(f, "{:?}", self.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::{Dentry, DentryKey, FileSystem, InodeType};
|
||||
|
||||
/// The MountNode can form a mount tree to maintain the mount information.
|
||||
pub struct MountNode {
|
||||
/// Root dentry.
|
||||
root_dentry: Arc<Dentry>,
|
||||
/// Mountpoint dentry. A mount node can be mounted on one dentry of another mount node,
|
||||
/// which makes the mount being the child of the mount node.
|
||||
mountpoint_dentry: Option<Arc<Dentry>>,
|
||||
/// The associated FS.
|
||||
fs: Arc<dyn FileSystem>,
|
||||
/// Child mount nodes which are mounted on one dentry of self.
|
||||
children: Mutex<BTreeMap<DentryKey, Arc<Self>>>,
|
||||
/// Reference to self.
|
||||
this: Weak<Self>,
|
||||
}
|
||||
|
||||
impl MountNode {
|
||||
/// Create a root mount node with an associated FS.
|
||||
///
|
||||
/// The root mount node is not mounted on other mount nodes(which means it has no
|
||||
/// parent). The root inode of the fs will form the root dentry of it.
|
||||
///
|
||||
/// It is allowed to create a mount node even if the fs has been provided to another
|
||||
/// mount node. It is the fs's responsibility to ensure the data consistency.
|
||||
pub fn new_root(fs: Arc<dyn FileSystem>) -> Arc<Self> {
|
||||
Self::new(fs, None)
|
||||
}
|
||||
|
||||
/// The internal constructor.
|
||||
///
|
||||
/// Root mount node has no mountpoint which other mount nodes must have mountpoint.
|
||||
fn new(fs: Arc<dyn FileSystem>, mountpoint: Option<Arc<Dentry>>) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| Self {
|
||||
root_dentry: Dentry::new_root(fs.root_inode(), weak_self.clone()),
|
||||
mountpoint_dentry: mountpoint,
|
||||
children: Mutex::new(BTreeMap::new()),
|
||||
fs,
|
||||
this: weak_self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Mount an fs on the mountpoint, it will create a new child mount node.
|
||||
///
|
||||
/// If the given mountpoint has already been mounted, then its mounted child mount
|
||||
/// node will be updated.
|
||||
///
|
||||
/// The mountpoint should belong to this mount node, or an error is returned.
|
||||
///
|
||||
/// It is allowed to mount a fs even if the fs has been provided to another
|
||||
/// mountpoint. It is the fs's responsibility to ensure the data consistency.
|
||||
///
|
||||
/// Return the mounted child mount.
|
||||
pub fn mount(&self, fs: Arc<dyn FileSystem>, mountpoint: &Arc<Dentry>) -> Result<Arc<Self>> {
|
||||
if !Arc::ptr_eq(&mountpoint.mount_node(), &self.this()) {
|
||||
return_errno_with_message!(Errno::EINVAL, "mountpoint not belongs to this");
|
||||
}
|
||||
if mountpoint.inode_type() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
|
||||
let key = mountpoint.key();
|
||||
let child_mount = Self::new(fs, Some(mountpoint.clone()));
|
||||
self.children.lock().insert(key, child_mount.clone());
|
||||
Ok(child_mount)
|
||||
}
|
||||
|
||||
/// Unmount a child mount node from the mountpoint and return it.
|
||||
///
|
||||
/// The mountpoint should belong to this mount node, or an error is returned.
|
||||
pub fn umount(&self, mountpoint: &Dentry) -> Result<Arc<Self>> {
|
||||
if !Arc::ptr_eq(&mountpoint.mount_node(), &self.this()) {
|
||||
return_errno_with_message!(Errno::EINVAL, "mountpoint not belongs to this");
|
||||
}
|
||||
|
||||
let child_mount = self
|
||||
.children
|
||||
.lock()
|
||||
.remove(&mountpoint.key())
|
||||
.ok_or_else(|| Error::with_message(Errno::ENOENT, "can not find child mount"))?;
|
||||
Ok(child_mount)
|
||||
}
|
||||
|
||||
/// Try to get a child mount node from the mountpoint.
|
||||
pub fn get(&self, mountpoint: &Dentry) -> Option<Arc<Self>> {
|
||||
if !Arc::ptr_eq(&mountpoint.mount_node(), &self.this()) {
|
||||
return None;
|
||||
}
|
||||
self.children.lock().get(&mountpoint.key()).cloned()
|
||||
}
|
||||
|
||||
/// Get the root dentry of this mount node.
|
||||
pub fn root_dentry(&self) -> &Arc<Dentry> {
|
||||
&self.root_dentry
|
||||
}
|
||||
|
||||
/// Try to get the mountpoint dentry of this mount node.
|
||||
pub fn mountpoint_dentry(&self) -> Option<&Arc<Dentry>> {
|
||||
self.mountpoint_dentry.as_ref()
|
||||
}
|
||||
|
||||
/// Flushes all pending filesystem metadata and cached file data to the device.
|
||||
pub fn sync(&self) -> Result<()> {
|
||||
let children = self.children.lock();
|
||||
for child in children.values() {
|
||||
child.sync()?;
|
||||
}
|
||||
drop(children);
|
||||
|
||||
self.fs.sync()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Try to get the parent mount node.
|
||||
pub fn parent(&self) -> Option<Arc<Self>> {
|
||||
self.mountpoint_dentry
|
||||
.as_ref()
|
||||
.map(|dentry| dentry.mount_node())
|
||||
}
|
||||
|
||||
/// Get strong reference to self.
|
||||
fn this(&self) -> Arc<Self> {
|
||||
self.this.upgrade().unwrap()
|
||||
}
|
||||
|
||||
/// Get the associated fs.
|
||||
pub fn fs(&self) -> &Arc<dyn FileSystem> {
|
||||
&self.fs
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for MountNode {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("MountNode")
|
||||
.field("root", &self.root_dentry)
|
||||
.field("mountpoint", &self.mountpoint_dentry)
|
||||
.field("fs", &self.fs)
|
||||
.finish()
|
||||
}
|
||||
}
|
@ -1,230 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::vm::vmo::{get_page_idx_range, Pager, Vmo, VmoFlags, VmoOptions};
|
||||
use aster_rights::Full;
|
||||
|
||||
use aster_frame::vm::{VmAllocOptions, VmFrame};
|
||||
use core::ops::Range;
|
||||
use lru::LruCache;
|
||||
|
||||
pub struct PageCache {
|
||||
pages: Vmo<Full>,
|
||||
manager: Arc<PageCacheManager>,
|
||||
}
|
||||
|
||||
impl PageCache {
|
||||
/// Creates an empty size page cache associated with a new backend.
|
||||
pub fn new(backend: Weak<dyn PageCacheBackend>) -> Result<Self> {
|
||||
let manager = Arc::new(PageCacheManager::new(backend));
|
||||
let pages = VmoOptions::<Full>::new(0)
|
||||
.flags(VmoFlags::RESIZABLE)
|
||||
.pager(manager.clone())
|
||||
.alloc()?;
|
||||
Ok(Self { pages, manager })
|
||||
}
|
||||
|
||||
/// Creates a page cache associated with an existing backend.
|
||||
///
|
||||
/// The `capacity` is the initial cache size required by the backend.
|
||||
/// This size usually corresponds to the size of the backend.
|
||||
pub fn with_capacity(capacity: usize, backend: Weak<dyn PageCacheBackend>) -> Result<Self> {
|
||||
let manager = Arc::new(PageCacheManager::new(backend));
|
||||
let pages = VmoOptions::<Full>::new(capacity)
|
||||
.flags(VmoFlags::RESIZABLE)
|
||||
.pager(manager.clone())
|
||||
.alloc()?;
|
||||
Ok(Self { pages, manager })
|
||||
}
|
||||
|
||||
/// Returns the Vmo object.
|
||||
// TODO: The capability is too high,restrict it to eliminate the possibility of misuse.
|
||||
// For example, the `resize` api should be forbidded.
|
||||
pub fn pages(&self) -> Vmo<Full> {
|
||||
self.pages.dup().unwrap()
|
||||
}
|
||||
|
||||
/// Evict the data within a specified range from the page cache and persist
|
||||
/// them to the backend.
|
||||
pub fn evict_range(&self, range: Range<usize>) -> Result<()> {
|
||||
self.manager.evict_range(range)
|
||||
}
|
||||
|
||||
/// Returns the backend.
|
||||
pub fn backend(&self) -> Arc<dyn PageCacheBackend> {
|
||||
self.manager.backend()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PageCache {
|
||||
fn drop(&mut self) {
|
||||
// TODO:
|
||||
// The default destruction procedure exhibits slow performance.
|
||||
// In contrast, resizing the `VMO` to zero greatly accelerates the process.
|
||||
// We need to find out the underlying cause of this discrepancy.
|
||||
let _ = self.pages.resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PageCache {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("PageCache")
|
||||
.field("size", &self.pages.size())
|
||||
.field("mamager", &self.manager)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
struct PageCacheManager {
|
||||
pages: Mutex<LruCache<usize, Page>>,
|
||||
backend: Weak<dyn PageCacheBackend>,
|
||||
}
|
||||
|
||||
impl PageCacheManager {
|
||||
pub fn new(backend: Weak<dyn PageCacheBackend>) -> Self {
|
||||
Self {
|
||||
pages: Mutex::new(LruCache::unbounded()),
|
||||
backend,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backend(&self) -> Arc<dyn PageCacheBackend> {
|
||||
self.backend.upgrade().unwrap()
|
||||
}
|
||||
|
||||
pub fn evict_range(&self, range: Range<usize>) -> Result<()> {
|
||||
let page_idx_range = get_page_idx_range(&range);
|
||||
let mut pages = self.pages.lock();
|
||||
for idx in page_idx_range {
|
||||
if let Some(page) = pages.get_mut(&idx) {
|
||||
if let PageState::Dirty = page.state() {
|
||||
let backend = self.backend();
|
||||
if idx < backend.npages() {
|
||||
backend.write_page(idx, page.frame())?;
|
||||
page.set_state(PageState::UpToDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PageCacheManager {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
||||
f.debug_struct("PageCacheManager")
|
||||
.field("pages", &self.pages.lock())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Pager for PageCacheManager {
|
||||
fn commit_page(&self, idx: usize) -> Result<VmFrame> {
|
||||
let mut pages = self.pages.lock();
|
||||
let frame = if let Some(page) = pages.get(&idx) {
|
||||
page.frame().clone()
|
||||
} else {
|
||||
let backend = self.backend();
|
||||
let page = if idx < backend.npages() {
|
||||
let mut page = Page::alloc()?;
|
||||
backend.read_page(idx, page.frame())?;
|
||||
page.set_state(PageState::UpToDate);
|
||||
page
|
||||
} else {
|
||||
Page::alloc_zero()?
|
||||
};
|
||||
let frame = page.frame().clone();
|
||||
pages.put(idx, page);
|
||||
frame
|
||||
};
|
||||
|
||||
Ok(frame)
|
||||
}
|
||||
|
||||
fn update_page(&self, idx: usize) -> Result<()> {
|
||||
let mut pages = self.pages.lock();
|
||||
if let Some(page) = pages.get_mut(&idx) {
|
||||
page.set_state(PageState::Dirty);
|
||||
} else {
|
||||
warn!("The page {} is not in page cache", idx);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decommit_page(&self, idx: usize) -> Result<()> {
|
||||
let mut pages = self.pages.lock();
|
||||
if let Some(page) = pages.pop(&idx) {
|
||||
if let PageState::Dirty = page.state() {
|
||||
let Some(backend) = self.backend.upgrade() else {
|
||||
return Ok(());
|
||||
};
|
||||
if idx < backend.npages() {
|
||||
backend.write_page(idx, page.frame())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Page {
|
||||
frame: VmFrame,
|
||||
state: PageState,
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn alloc() -> Result<Self> {
|
||||
let frame = VmAllocOptions::new(1).uninit(true).alloc_single()?;
|
||||
Ok(Self {
|
||||
frame,
|
||||
state: PageState::Uninit,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn alloc_zero() -> Result<Self> {
|
||||
let frame = VmAllocOptions::new(1).alloc_single()?;
|
||||
Ok(Self {
|
||||
frame,
|
||||
state: PageState::Dirty,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn frame(&self) -> &VmFrame {
|
||||
&self.frame
|
||||
}
|
||||
|
||||
pub fn state(&self) -> &PageState {
|
||||
&self.state
|
||||
}
|
||||
|
||||
pub fn set_state(&mut self, new_state: PageState) {
|
||||
self.state = new_state;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PageState {
|
||||
/// `Uninit` indicates a new allocated page which content has not been initialized.
|
||||
/// The page is available to write, not available to read.
|
||||
Uninit,
|
||||
/// `UpToDate` indicates a page which content is consistent with corresponding disk content.
|
||||
/// The page is available to read and write.
|
||||
UpToDate,
|
||||
/// `Dirty` indicates a page which content has been updated and not written back to underlying disk.
|
||||
/// The page is available to read and write.
|
||||
Dirty,
|
||||
}
|
||||
|
||||
/// This trait represents the backend for the page cache.
|
||||
pub trait PageCacheBackend: Sync + Send {
|
||||
/// Reads a page from the backend.
|
||||
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()>;
|
||||
/// Writes a page to the backend.
|
||||
fn write_page(&self, idx: usize, frame: &VmFrame) -> Result<()>;
|
||||
/// Returns the number of pages in the backend.
|
||||
fn npages(&self) -> usize;
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
pub struct StatusFlags: u32 {
|
||||
/// append on each write
|
||||
const O_APPEND = 1 << 10;
|
||||
/// non block
|
||||
const O_NONBLOCK = 1 << 11;
|
||||
/// synchronized I/O, data
|
||||
const O_DSYNC = 1 << 12;
|
||||
/// signal-driven I/O
|
||||
const O_ASYNC = 1 << 13;
|
||||
/// direct I/O
|
||||
const O_DIRECT = 1 << 14;
|
||||
/// on x86_64, O_LARGEFILE is 0
|
||||
/// not update st_atime
|
||||
const O_NOATIME = 1 << 18;
|
||||
/// synchronized I/O, data and metadata
|
||||
const O_SYNC = 1 << 20;
|
||||
/// equivalent of POSIX.1's O_EXEC
|
||||
const O_PATH = 1 << 21;
|
||||
}
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//! The std library of Asterinas.
|
||||
#![no_std]
|
||||
#![forbid(unsafe_code)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(incomplete_features)]
|
||||
#![allow(unused_variables)]
|
||||
#![feature(exclusive_range_pattern)]
|
||||
#![feature(btree_extract_if)]
|
||||
#![feature(const_option)]
|
||||
#![feature(extend_one)]
|
||||
#![feature(let_chains)]
|
||||
// FIXME: This feature is used to support vm capbility now as a work around.
|
||||
// Since this is an incomplete feature, use this feature is unsafe.
|
||||
// We should find a proper method to replace this feature with min_specialization, which is a sound feature.
|
||||
#![feature(specialization)]
|
||||
#![feature(fn_traits)]
|
||||
#![feature(linked_list_remove)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(register_tool)]
|
||||
#![feature(format_args_nl)]
|
||||
#![feature(int_roundings)]
|
||||
#![feature(step_trait)]
|
||||
#![feature(btree_cursors)]
|
||||
#![register_tool(component_access_control)]
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
thread::{
|
||||
kernel_thread::{KernelThreadExt, ThreadOptions},
|
||||
Thread,
|
||||
},
|
||||
};
|
||||
use aster_frame::{
|
||||
arch::qemu::{exit_qemu, QemuExitCode},
|
||||
boot,
|
||||
};
|
||||
use process::Process;
|
||||
|
||||
extern crate alloc;
|
||||
extern crate lru;
|
||||
#[macro_use]
|
||||
extern crate controlled;
|
||||
#[macro_use]
|
||||
extern crate ktest;
|
||||
#[macro_use]
|
||||
extern crate getset;
|
||||
|
||||
pub mod console;
|
||||
pub mod device;
|
||||
pub mod driver;
|
||||
pub mod error;
|
||||
pub mod events;
|
||||
pub mod fs;
|
||||
pub mod net;
|
||||
pub mod prelude;
|
||||
mod process;
|
||||
mod sched;
|
||||
pub mod syscall;
|
||||
pub mod thread;
|
||||
pub mod time;
|
||||
mod util;
|
||||
pub(crate) mod vdso;
|
||||
pub mod vm;
|
||||
|
||||
pub fn init() {
|
||||
driver::init();
|
||||
net::init();
|
||||
sched::init();
|
||||
fs::rootfs::init(boot::initramfs()).unwrap();
|
||||
device::init().unwrap();
|
||||
vdso::init();
|
||||
}
|
||||
|
||||
fn init_thread() {
|
||||
println!(
|
||||
"[kernel] Spawn init thread, tid = {}",
|
||||
current_thread!().tid()
|
||||
);
|
||||
net::lazy_init();
|
||||
fs::lazy_init();
|
||||
// driver::pci::virtio::block::block_device_test();
|
||||
let thread = Thread::spawn_kernel_thread(ThreadOptions::new(|| {
|
||||
println!("[kernel] Hello world from kernel!");
|
||||
let current = current_thread!();
|
||||
let tid = current.tid();
|
||||
debug!("current tid = {}", tid);
|
||||
}));
|
||||
thread.join();
|
||||
info!(
|
||||
"[aster-std/lib.rs] spawn kernel thread, tid = {}",
|
||||
thread.tid()
|
||||
);
|
||||
thread::work_queue::init();
|
||||
|
||||
print_banner();
|
||||
|
||||
let karg = boot::kernel_cmdline();
|
||||
|
||||
let initproc = Process::spawn_user_process(
|
||||
karg.get_initproc_path().unwrap(),
|
||||
karg.get_initproc_argv().to_vec(),
|
||||
karg.get_initproc_envp().to_vec(),
|
||||
)
|
||||
.expect("Run init process failed.");
|
||||
|
||||
// Wait till initproc become zombie.
|
||||
while !initproc.is_zombie() {
|
||||
// We don't have preemptive scheduler now.
|
||||
// The long running init thread should yield its own execution to allow other tasks to go on.
|
||||
Thread::yield_now();
|
||||
}
|
||||
|
||||
// TODO: exit via qemu isa debug device should not be the only way.
|
||||
let exit_code = if initproc.exit_code().unwrap() == 0 {
|
||||
QemuExitCode::Success
|
||||
} else {
|
||||
QemuExitCode::Failed
|
||||
};
|
||||
exit_qemu(exit_code);
|
||||
}
|
||||
|
||||
/// first process never return
|
||||
#[controlled]
|
||||
pub fn run_first_process() -> ! {
|
||||
Thread::spawn_kernel_thread(ThreadOptions::new(init_thread));
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn print_banner() {
|
||||
println!("\x1B[36m");
|
||||
println!(
|
||||
r"
|
||||
_ ___ _____ ___ ___ ___ _ _ _ ___
|
||||
/_\ / __|_ _| __| _ \_ _| \| | /_\ / __|
|
||||
/ _ \\__ \ | | | _|| /| || .` |/ _ \\__ \
|
||||
/_/ \_\___/ |_| |___|_|_\___|_|\_/_/ \_\___/
|
||||
"
|
||||
);
|
||||
println!("\x1B[0m");
|
||||
}
|
@ -1,177 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::Observer;
|
||||
use crate::prelude::*;
|
||||
|
||||
use super::Iface;
|
||||
use super::{IpAddress, IpEndpoint};
|
||||
|
||||
pub type RawTcpSocket = smoltcp::socket::tcp::Socket<'static>;
|
||||
pub type RawUdpSocket = smoltcp::socket::udp::Socket<'static>;
|
||||
pub type RawSocketHandle = smoltcp::iface::SocketHandle;
|
||||
|
||||
pub struct AnyUnboundSocket {
|
||||
socket_family: AnyRawSocket,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub(super) enum AnyRawSocket {
|
||||
Tcp(RawTcpSocket),
|
||||
Udp(RawUdpSocket),
|
||||
}
|
||||
|
||||
pub(super) enum SocketFamily {
|
||||
Tcp,
|
||||
Udp,
|
||||
}
|
||||
|
||||
impl AnyUnboundSocket {
|
||||
pub fn new_tcp() -> Self {
|
||||
let raw_tcp_socket = {
|
||||
let rx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0u8; RECV_BUF_LEN]);
|
||||
let tx_buffer = smoltcp::socket::tcp::SocketBuffer::new(vec![0u8; SEND_BUF_LEN]);
|
||||
RawTcpSocket::new(rx_buffer, tx_buffer)
|
||||
};
|
||||
AnyUnboundSocket {
|
||||
socket_family: AnyRawSocket::Tcp(raw_tcp_socket),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_udp() -> Self {
|
||||
let raw_udp_socket = {
|
||||
let metadata = smoltcp::socket::udp::PacketMetadata::EMPTY;
|
||||
let rx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
||||
vec![metadata; UDP_METADATA_LEN],
|
||||
vec![0u8; UDP_RECEIVE_PAYLOAD_LEN],
|
||||
);
|
||||
let tx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
||||
vec![metadata; UDP_METADATA_LEN],
|
||||
vec![0u8; UDP_RECEIVE_PAYLOAD_LEN],
|
||||
);
|
||||
RawUdpSocket::new(rx_buffer, tx_buffer)
|
||||
};
|
||||
AnyUnboundSocket {
|
||||
socket_family: AnyRawSocket::Udp(raw_udp_socket),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn raw_socket_family(self) -> AnyRawSocket {
|
||||
self.socket_family
|
||||
}
|
||||
|
||||
pub(super) fn socket_family(&self) -> SocketFamily {
|
||||
match &self.socket_family {
|
||||
AnyRawSocket::Tcp(_) => SocketFamily::Tcp,
|
||||
AnyRawSocket::Udp(_) => SocketFamily::Udp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AnyBoundSocket {
|
||||
iface: Arc<dyn Iface>,
|
||||
handle: smoltcp::iface::SocketHandle,
|
||||
port: u16,
|
||||
socket_family: SocketFamily,
|
||||
observer: RwLock<Weak<dyn Observer<()>>>,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl AnyBoundSocket {
|
||||
pub(super) fn new(
|
||||
iface: Arc<dyn Iface>,
|
||||
handle: smoltcp::iface::SocketHandle,
|
||||
port: u16,
|
||||
socket_family: SocketFamily,
|
||||
) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| Self {
|
||||
iface,
|
||||
handle,
|
||||
port,
|
||||
socket_family,
|
||||
observer: RwLock::new(Weak::<()>::new()),
|
||||
weak_self: weak_self.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn on_iface_events(&self) {
|
||||
if let Some(observer) = Weak::upgrade(&*self.observer.read()) {
|
||||
observer.on_events(&())
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the observer whose `on_events` will be called when certain iface events happen. After
|
||||
/// setting, the new observer will fire once immediately to avoid missing any events.
|
||||
///
|
||||
/// If there is an existing observer, due to race conditions, this function does not guarentee
|
||||
/// that the old observer will never be called after the setting. Users should be aware of this
|
||||
/// and proactively handle the race conditions if necessary.
|
||||
pub fn set_observer(&self, handler: Weak<dyn Observer<()>>) {
|
||||
*self.observer.write() = handler;
|
||||
|
||||
self.on_iface_events();
|
||||
}
|
||||
|
||||
pub fn local_endpoint(&self) -> Option<IpEndpoint> {
|
||||
let ip_addr = {
|
||||
let ipv4_addr = self.iface.ipv4_addr()?;
|
||||
IpAddress::Ipv4(ipv4_addr)
|
||||
};
|
||||
Some(IpEndpoint::new(ip_addr, self.port))
|
||||
}
|
||||
|
||||
pub fn raw_with<T: smoltcp::socket::AnySocket<'static>, R, F: FnMut(&mut T) -> R>(
|
||||
&self,
|
||||
mut f: F,
|
||||
) -> R {
|
||||
let mut sockets = self.iface.sockets();
|
||||
let socket = sockets.get_mut::<T>(self.handle);
|
||||
f(socket)
|
||||
}
|
||||
|
||||
/// Try to connect to a remote endpoint. Tcp socket only.
|
||||
pub fn do_connect(&self, remote_endpoint: IpEndpoint) -> Result<()> {
|
||||
let mut sockets = self.iface.sockets();
|
||||
let socket = sockets.get_mut::<RawTcpSocket>(self.handle);
|
||||
let port = self.port;
|
||||
let mut iface_inner = self.iface.iface_inner();
|
||||
let cx = iface_inner.context();
|
||||
socket
|
||||
.connect(cx, remote_endpoint, port)
|
||||
.map_err(|_| Error::with_message(Errno::ENOBUFS, "send connection request failed"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn iface(&self) -> &Arc<dyn Iface> {
|
||||
&self.iface
|
||||
}
|
||||
|
||||
pub(super) fn weak_ref(&self) -> Weak<Self> {
|
||||
self.weak_self.clone()
|
||||
}
|
||||
|
||||
fn close(&self) {
|
||||
match self.socket_family {
|
||||
SocketFamily::Tcp => self.raw_with(|socket: &mut RawTcpSocket| socket.close()),
|
||||
SocketFamily::Udp => self.raw_with(|socket: &mut RawUdpSocket| socket.close()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AnyBoundSocket {
|
||||
fn drop(&mut self) {
|
||||
self.close();
|
||||
self.iface.poll();
|
||||
self.iface.common().remove_socket(self.handle);
|
||||
self.iface.common().release_port(self.port);
|
||||
self.iface.common().remove_bound_socket(self.weak_ref());
|
||||
}
|
||||
}
|
||||
|
||||
// For TCP
|
||||
pub const RECV_BUF_LEN: usize = 65536;
|
||||
pub const SEND_BUF_LEN: usize = 65536;
|
||||
|
||||
// For UDP
|
||||
const UDP_METADATA_LEN: usize = 256;
|
||||
const UDP_SEND_PAYLOAD_LEN: usize = 65536;
|
||||
const UDP_RECEIVE_PAYLOAD_LEN: usize = 65536;
|
@ -1,184 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicU64, Ordering};
|
||||
|
||||
use super::Ipv4Address;
|
||||
use crate::prelude::*;
|
||||
use alloc::collections::btree_map::Entry;
|
||||
use keyable_arc::KeyableWeak;
|
||||
use smoltcp::{
|
||||
iface::{SocketHandle, SocketSet},
|
||||
phy::Device,
|
||||
wire::IpCidr,
|
||||
};
|
||||
|
||||
use super::{
|
||||
any_socket::{AnyBoundSocket, AnyRawSocket, AnyUnboundSocket},
|
||||
time::get_network_timestamp,
|
||||
util::BindPortConfig,
|
||||
Iface,
|
||||
};
|
||||
|
||||
pub struct IfaceCommon {
|
||||
interface: SpinLock<smoltcp::iface::Interface>,
|
||||
sockets: SpinLock<SocketSet<'static>>,
|
||||
used_ports: RwLock<BTreeMap<u16, usize>>,
|
||||
/// The time should do next poll. We stores the total microseconds since system boots up.
|
||||
next_poll_at_ms: AtomicU64,
|
||||
bound_sockets: RwLock<BTreeSet<KeyableWeak<AnyBoundSocket>>>,
|
||||
}
|
||||
|
||||
impl IfaceCommon {
|
||||
pub(super) fn new(interface: smoltcp::iface::Interface) -> Self {
|
||||
let socket_set = SocketSet::new(Vec::new());
|
||||
let used_ports = BTreeMap::new();
|
||||
Self {
|
||||
interface: SpinLock::new(interface),
|
||||
sockets: SpinLock::new(socket_set),
|
||||
used_ports: RwLock::new(used_ports),
|
||||
next_poll_at_ms: AtomicU64::new(0),
|
||||
bound_sockets: RwLock::new(BTreeSet::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn interface(&self) -> SpinLockGuard<smoltcp::iface::Interface> {
|
||||
self.interface.lock_irq_disabled()
|
||||
}
|
||||
|
||||
pub(super) fn sockets(&self) -> SpinLockGuard<smoltcp::iface::SocketSet<'static>> {
|
||||
self.sockets.lock_irq_disabled()
|
||||
}
|
||||
|
||||
pub(super) fn ipv4_addr(&self) -> Option<Ipv4Address> {
|
||||
self.interface.lock_irq_disabled().ipv4_addr()
|
||||
}
|
||||
|
||||
pub(super) fn netmask(&self) -> Option<Ipv4Address> {
|
||||
let interface = self.interface.lock_irq_disabled();
|
||||
let ip_addrs = interface.ip_addrs();
|
||||
ip_addrs.first().map(|cidr| match cidr {
|
||||
IpCidr::Ipv4(ipv4_cidr) => ipv4_cidr.netmask(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Alloc an unused port range from 49152 ~ 65535 (According to smoltcp docs)
|
||||
fn alloc_ephemeral_port(&self) -> Result<u16> {
|
||||
let mut used_ports = self.used_ports.write();
|
||||
for port in IP_LOCAL_PORT_START..=IP_LOCAL_PORT_END {
|
||||
if let Entry::Vacant(e) = used_ports.entry(port) {
|
||||
e.insert(0);
|
||||
return Ok(port);
|
||||
}
|
||||
}
|
||||
return_errno_with_message!(Errno::EAGAIN, "cannot find unused high port");
|
||||
}
|
||||
|
||||
fn bind_port(&self, port: u16, can_reuse: bool) -> Result<()> {
|
||||
let mut used_ports = self.used_ports.write();
|
||||
if let Some(used_times) = used_ports.get_mut(&port) {
|
||||
if *used_times == 0 || can_reuse {
|
||||
*used_times += 1;
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EADDRINUSE, "cannot bind port");
|
||||
}
|
||||
} else {
|
||||
used_ports.insert(port, 1);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Release port number so the port can be used again. For reused port, the port may still be in use.
|
||||
pub(super) fn release_port(&self, port: u16) {
|
||||
let mut used_ports = self.used_ports.write();
|
||||
if let Some(used_times) = used_ports.remove(&port) {
|
||||
if used_times != 1 {
|
||||
used_ports.insert(port, used_times - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn bind_socket(
|
||||
&self,
|
||||
iface: Arc<dyn Iface>,
|
||||
socket: Box<AnyUnboundSocket>,
|
||||
config: BindPortConfig,
|
||||
) -> core::result::Result<Arc<AnyBoundSocket>, (Error, Box<AnyUnboundSocket>)> {
|
||||
let port = if let Some(port) = config.port() {
|
||||
port
|
||||
} else {
|
||||
match self.alloc_ephemeral_port() {
|
||||
Ok(port) => port,
|
||||
Err(e) => return Err((e, socket)),
|
||||
}
|
||||
};
|
||||
if let Some(e) = self.bind_port(port, config.can_reuse()).err() {
|
||||
return Err((e, socket));
|
||||
}
|
||||
let socket_family = socket.socket_family();
|
||||
let mut sockets = self.sockets.lock_irq_disabled();
|
||||
let handle = match socket.raw_socket_family() {
|
||||
AnyRawSocket::Tcp(tcp_socket) => sockets.add(tcp_socket),
|
||||
AnyRawSocket::Udp(udp_socket) => sockets.add(udp_socket),
|
||||
};
|
||||
let bound_socket = AnyBoundSocket::new(iface, handle, port, socket_family);
|
||||
self.insert_bound_socket(&bound_socket).unwrap();
|
||||
Ok(bound_socket)
|
||||
}
|
||||
|
||||
/// Remove a socket from the interface
|
||||
pub(super) fn remove_socket(&self, handle: SocketHandle) {
|
||||
self.sockets.lock_irq_disabled().remove(handle);
|
||||
}
|
||||
|
||||
pub(super) fn poll<D: Device + ?Sized>(&self, device: &mut D) {
|
||||
let mut interface = self.interface.lock_irq_disabled();
|
||||
let timestamp = get_network_timestamp();
|
||||
let has_events = {
|
||||
let mut sockets = self.sockets.lock_irq_disabled();
|
||||
interface.poll(timestamp, device, &mut sockets)
|
||||
// drop sockets here to avoid deadlock
|
||||
};
|
||||
if has_events {
|
||||
self.bound_sockets.read().iter().for_each(|bound_socket| {
|
||||
if let Some(bound_socket) = bound_socket.upgrade() {
|
||||
bound_socket.on_iface_events();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let sockets = self.sockets.lock_irq_disabled();
|
||||
if let Some(instant) = interface.poll_at(timestamp, &sockets) {
|
||||
self.next_poll_at_ms
|
||||
.store(instant.total_millis() as u64, Ordering::SeqCst);
|
||||
} else {
|
||||
self.next_poll_at_ms.store(0, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn next_poll_at_ms(&self) -> Option<u64> {
|
||||
let millis = self.next_poll_at_ms.load(Ordering::SeqCst);
|
||||
if millis == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(millis)
|
||||
}
|
||||
}
|
||||
|
||||
fn insert_bound_socket(&self, socket: &Arc<AnyBoundSocket>) -> Result<()> {
|
||||
let weak_ref = KeyableWeak::from(Arc::downgrade(socket));
|
||||
let mut bound_sockets = self.bound_sockets.write();
|
||||
if bound_sockets.contains(&weak_ref) {
|
||||
return_errno_with_message!(Errno::EINVAL, "the socket is already bound");
|
||||
}
|
||||
bound_sockets.insert(weak_ref);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn remove_bound_socket(&self, socket: Weak<AnyBoundSocket>) {
|
||||
let weak_ref = KeyableWeak::from(socket);
|
||||
self.bound_sockets.write().remove(&weak_ref);
|
||||
}
|
||||
}
|
||||
|
||||
const IP_LOCAL_PORT_START: u16 = 49152;
|
||||
const IP_LOCAL_PORT_END: u16 = 65535;
|
@ -1,72 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use super::{IpAddress, Ipv4Address};
|
||||
use crate::prelude::*;
|
||||
use smoltcp::{
|
||||
iface::{Config, Routes},
|
||||
phy::{Loopback, Medium},
|
||||
wire::IpCidr,
|
||||
};
|
||||
|
||||
use super::{common::IfaceCommon, internal::IfaceInternal, Iface};
|
||||
|
||||
pub const LOOPBACK_ADDRESS: IpAddress = {
|
||||
let ipv4_addr = Ipv4Address::new(127, 0, 0, 1);
|
||||
IpAddress::Ipv4(ipv4_addr)
|
||||
};
|
||||
pub const LOOPBACK_ADDRESS_PREFIX_LEN: u8 = 8; // mask: 255.0.0.0
|
||||
|
||||
pub struct IfaceLoopback {
|
||||
driver: Mutex<Loopback>,
|
||||
common: IfaceCommon,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl IfaceLoopback {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let mut loopback = Loopback::new(Medium::Ip);
|
||||
let interface = {
|
||||
let routes = Routes::new();
|
||||
let config = Config::new();
|
||||
let mut interface = smoltcp::iface::Interface::new(config, &mut loopback);
|
||||
interface.update_ip_addrs(|ip_addrs| {
|
||||
debug_assert!(ip_addrs.is_empty());
|
||||
let ip_addr = IpCidr::new(LOOPBACK_ADDRESS, LOOPBACK_ADDRESS_PREFIX_LEN);
|
||||
ip_addrs.push(ip_addr).unwrap();
|
||||
});
|
||||
interface
|
||||
};
|
||||
println!("Loopback ipaddr: {}", interface.ipv4_addr().unwrap());
|
||||
let common = IfaceCommon::new(interface);
|
||||
Arc::new_cyclic(|weak| Self {
|
||||
driver: Mutex::new(loopback),
|
||||
common,
|
||||
weak_self: weak.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IfaceInternal for IfaceLoopback {
|
||||
fn common(&self) -> &IfaceCommon {
|
||||
&self.common
|
||||
}
|
||||
|
||||
fn arc_self(&self) -> Arc<dyn Iface> {
|
||||
self.weak_self.upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iface for IfaceLoopback {
|
||||
fn name(&self) -> &str {
|
||||
"lo"
|
||||
}
|
||||
|
||||
fn mac_addr(&self) -> Option<smoltcp::wire::EthernetAddress> {
|
||||
None
|
||||
}
|
||||
|
||||
fn poll(&self) {
|
||||
let mut device = self.driver.lock();
|
||||
self.common.poll(&mut *device);
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use self::common::IfaceCommon;
|
||||
use crate::prelude::*;
|
||||
use smoltcp::iface::SocketSet;
|
||||
|
||||
mod any_socket;
|
||||
mod common;
|
||||
mod loopback;
|
||||
mod time;
|
||||
mod util;
|
||||
mod virtio;
|
||||
|
||||
pub use any_socket::{AnyBoundSocket, AnyUnboundSocket, RawTcpSocket, RawUdpSocket};
|
||||
pub use any_socket::{RECV_BUF_LEN, SEND_BUF_LEN};
|
||||
pub use loopback::IfaceLoopback;
|
||||
pub use smoltcp::wire::{EthernetAddress, IpAddress, IpEndpoint, IpListenEndpoint, Ipv4Address};
|
||||
pub use util::{spawn_background_poll_thread, BindPortConfig};
|
||||
pub use virtio::IfaceVirtio;
|
||||
|
||||
/// Network interface.
|
||||
///
|
||||
/// A network interface (abbreviated as iface) is a hardware or software component that connects a device or computer to a network.
|
||||
/// Network interfaces can be physical components like Ethernet ports or wireless adapters,
|
||||
/// or they can be virtual interfaces created by software such as virtual private network (VPN) connections.
|
||||
pub trait Iface: internal::IfaceInternal + Send + Sync {
|
||||
/// The iface name. For linux, usually the driver name followed by a unit number.
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// The optional mac address
|
||||
fn mac_addr(&self) -> Option<EthernetAddress>;
|
||||
|
||||
/// Transmit packets queued in the iface, and receive packets queued in the iface.
|
||||
/// It any event happens, this function will also update socket status.
|
||||
fn poll(&self);
|
||||
|
||||
/// Bind a socket to the iface. So the packet for this socket will be dealt with by the interface.
|
||||
/// If port is None, the iface will pick up an empheral port for the socket.
|
||||
/// FIXME: The reason for binding socket and interface together is because there are limitations inside smoltcp.
|
||||
/// See discussion at https://github.com/smoltcp-rs/smoltcp/issues/779.
|
||||
fn bind_socket(
|
||||
&self,
|
||||
socket: Box<AnyUnboundSocket>,
|
||||
config: BindPortConfig,
|
||||
) -> core::result::Result<Arc<AnyBoundSocket>, (Error, Box<AnyUnboundSocket>)> {
|
||||
let common = self.common();
|
||||
let socket_type_inner = socket.socket_family();
|
||||
common.bind_socket(self.arc_self(), socket, config)
|
||||
}
|
||||
|
||||
/// The optional ipv4 address
|
||||
/// FIXME: An interface indeed support multiple addresses
|
||||
fn ipv4_addr(&self) -> Option<Ipv4Address> {
|
||||
self.common().ipv4_addr()
|
||||
}
|
||||
|
||||
/// The netmask.
|
||||
/// FIXME: The netmask and IP address should be one-to-one if there are multiple ip address
|
||||
fn netmask(&self) -> Option<Ipv4Address> {
|
||||
self.common().netmask()
|
||||
}
|
||||
}
|
||||
|
||||
mod internal {
|
||||
use super::*;
|
||||
|
||||
/// A helper trait
|
||||
pub trait IfaceInternal {
|
||||
fn common(&self) -> &IfaceCommon;
|
||||
/// The inner socket set
|
||||
fn sockets(&self) -> SpinLockGuard<SocketSet<'static>> {
|
||||
self.common().sockets()
|
||||
}
|
||||
/// The inner iface.
|
||||
fn iface_inner(&self) -> SpinLockGuard<smoltcp::iface::Interface> {
|
||||
self.common().interface()
|
||||
}
|
||||
/// The time we should do another poll.
|
||||
fn next_poll_at_ms(&self) -> Option<u64> {
|
||||
self.common().next_poll_at_ms()
|
||||
}
|
||||
fn arc_self(&self) -> Arc<dyn Iface>;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_frame::timer::read_monotonic_milli_seconds;
|
||||
|
||||
pub(super) fn get_network_timestamp() -> smoltcp::time::Instant {
|
||||
let millis = read_monotonic_milli_seconds();
|
||||
smoltcp::time::Instant::from_millis(millis as i64)
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_frame::timer::read_monotonic_milli_seconds;
|
||||
|
||||
use crate::{
|
||||
prelude::*,
|
||||
thread::{
|
||||
kernel_thread::{KernelThreadExt, ThreadOptions},
|
||||
Thread,
|
||||
},
|
||||
};
|
||||
|
||||
use super::Iface;
|
||||
|
||||
pub enum BindPortConfig {
|
||||
CanReuse(u16),
|
||||
Specified(u16),
|
||||
Ephemeral,
|
||||
}
|
||||
|
||||
impl BindPortConfig {
|
||||
pub fn new(port: u16, can_reuse: bool) -> Result<Self> {
|
||||
let config = if port != 0 {
|
||||
if can_reuse {
|
||||
Self::CanReuse(port)
|
||||
} else {
|
||||
Self::Specified(port)
|
||||
}
|
||||
} else if can_reuse {
|
||||
return_errno_with_message!(Errno::EINVAL, "invalid bind port config");
|
||||
} else {
|
||||
Self::Ephemeral
|
||||
};
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
pub(super) fn can_reuse(&self) -> bool {
|
||||
matches!(self, Self::CanReuse(_))
|
||||
}
|
||||
|
||||
pub(super) fn port(&self) -> Option<u16> {
|
||||
match self {
|
||||
Self::CanReuse(port) | Self::Specified(port) => Some(*port),
|
||||
Self::Ephemeral => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spawn_background_poll_thread(iface: Arc<dyn Iface>) {
|
||||
// FIXME: use timer or wait_timeout when timer is enable.
|
||||
let task_fn = move || {
|
||||
debug!("spawn background poll thread");
|
||||
loop {
|
||||
let next_poll_time = if let Some(next_poll_time) = iface.next_poll_at_ms() {
|
||||
next_poll_time
|
||||
} else {
|
||||
Thread::yield_now();
|
||||
continue;
|
||||
};
|
||||
let now = read_monotonic_milli_seconds();
|
||||
if now > next_poll_time {
|
||||
// FIXME: now is later than next poll time. This may cause problem.
|
||||
iface.poll();
|
||||
continue;
|
||||
}
|
||||
let duration = next_poll_time - now;
|
||||
// FIXME: choose a suitable time interval
|
||||
if duration < 10 {
|
||||
iface.poll();
|
||||
} else {
|
||||
Thread::yield_now();
|
||||
}
|
||||
}
|
||||
};
|
||||
Thread::spawn_kernel_thread(ThreadOptions::new(task_fn));
|
||||
}
|
@ -1,128 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
use aster_frame::sync::SpinLock;
|
||||
use aster_network::AnyNetworkDevice;
|
||||
use aster_virtio::device::network::DEVICE_NAME;
|
||||
use smoltcp::{
|
||||
iface::{Config, Routes, SocketHandle, SocketSet},
|
||||
socket::dhcpv4,
|
||||
wire::{self, IpCidr},
|
||||
};
|
||||
|
||||
use super::{common::IfaceCommon, internal::IfaceInternal, Iface};
|
||||
|
||||
pub struct IfaceVirtio {
|
||||
driver: Arc<SpinLock<Box<dyn AnyNetworkDevice>>>,
|
||||
common: IfaceCommon,
|
||||
dhcp_handle: SocketHandle,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl IfaceVirtio {
|
||||
pub fn new() -> Arc<Self> {
|
||||
let virtio_net = aster_network::get_device(DEVICE_NAME).unwrap();
|
||||
let interface = {
|
||||
let mac_addr = virtio_net.lock().mac_addr();
|
||||
let ip_addr = IpCidr::new(wire::IpAddress::Ipv4(wire::Ipv4Address::UNSPECIFIED), 0);
|
||||
let routes = Routes::new();
|
||||
let config = {
|
||||
let mut config = Config::new();
|
||||
config.hardware_addr = Some(wire::HardwareAddress::Ethernet(
|
||||
wire::EthernetAddress(mac_addr.0),
|
||||
));
|
||||
config
|
||||
};
|
||||
let mut interface = smoltcp::iface::Interface::new(config, &mut **virtio_net.lock());
|
||||
interface.update_ip_addrs(|ip_addrs| {
|
||||
debug_assert!(ip_addrs.is_empty());
|
||||
ip_addrs.push(ip_addr).unwrap();
|
||||
});
|
||||
interface
|
||||
};
|
||||
let common = IfaceCommon::new(interface);
|
||||
let mut socket_set = common.sockets();
|
||||
let dhcp_handle = init_dhcp_client(&mut socket_set);
|
||||
drop(socket_set);
|
||||
Arc::new_cyclic(|weak| Self {
|
||||
driver: virtio_net,
|
||||
common,
|
||||
dhcp_handle,
|
||||
weak_self: weak.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// FIXME: Once we have user program dhcp client, we may remove dhcp logic from kernel.
|
||||
pub fn process_dhcp(&self) {
|
||||
let mut socket_set = self.common.sockets();
|
||||
let dhcp_socket: &mut dhcpv4::Socket = socket_set.get_mut(self.dhcp_handle);
|
||||
let config = if let Some(event) = dhcp_socket.poll() {
|
||||
debug!("event = {:?}", event);
|
||||
if let dhcpv4::Event::Configured(config) = event {
|
||||
config
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
let ip_addr = IpCidr::Ipv4(config.address);
|
||||
let mut interface = self.common.interface();
|
||||
interface.update_ip_addrs(|ipaddrs| {
|
||||
if let Some(addr) = ipaddrs.iter_mut().next() {
|
||||
// already has ipaddrs
|
||||
*addr = ip_addr
|
||||
} else {
|
||||
// does not has ip addr
|
||||
ipaddrs.push(ip_addr).unwrap();
|
||||
}
|
||||
});
|
||||
println!(
|
||||
"DHCP update IP address: {:?}",
|
||||
interface.ipv4_addr().unwrap()
|
||||
);
|
||||
if let Some(router) = config.router {
|
||||
println!("Default router address: {:?}", router);
|
||||
interface
|
||||
.routes_mut()
|
||||
.add_default_ipv4_route(router)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IfaceInternal for IfaceVirtio {
|
||||
fn common(&self) -> &IfaceCommon {
|
||||
&self.common
|
||||
}
|
||||
|
||||
fn arc_self(&self) -> Arc<dyn Iface> {
|
||||
self.weak_self.upgrade().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iface for IfaceVirtio {
|
||||
fn name(&self) -> &str {
|
||||
"virtio"
|
||||
}
|
||||
|
||||
fn mac_addr(&self) -> Option<smoltcp::wire::EthernetAddress> {
|
||||
let interface = self.common.interface();
|
||||
let hardware_addr = interface.hardware_addr();
|
||||
match hardware_addr {
|
||||
wire::HardwareAddress::Ethernet(ethe_address) => Some(ethe_address),
|
||||
}
|
||||
}
|
||||
|
||||
fn poll(&self) {
|
||||
let mut driver = self.driver.lock_irq_disabled();
|
||||
self.common.poll(&mut **driver);
|
||||
self.process_dhcp();
|
||||
}
|
||||
}
|
||||
|
||||
/// Register a dhcp socket.
|
||||
fn init_dhcp_client(socket_set: &mut SocketSet) -> SocketHandle {
|
||||
let dhcp_socket = dhcpv4::Socket::new();
|
||||
socket_set.add(dhcp_socket)
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::{
|
||||
net::iface::{Iface, IfaceLoopback, IfaceVirtio},
|
||||
prelude::*,
|
||||
};
|
||||
use spin::Once;
|
||||
|
||||
use self::iface::spawn_background_poll_thread;
|
||||
|
||||
pub static IFACES: Once<Vec<Arc<dyn Iface>>> = Once::new();
|
||||
|
||||
pub mod iface;
|
||||
pub mod socket;
|
||||
|
||||
pub fn init() {
|
||||
IFACES.call_once(|| {
|
||||
let iface_virtio = IfaceVirtio::new();
|
||||
let iface_loopback = IfaceLoopback::new();
|
||||
vec![iface_virtio, iface_loopback]
|
||||
});
|
||||
|
||||
for (name, _) in aster_network::all_devices() {
|
||||
aster_network::register_recv_callback(&name, || {
|
||||
// TODO: further check that the irq num is the same as iface's irq num
|
||||
let iface_virtio = &IFACES.get().unwrap()[0];
|
||||
iface_virtio.poll();
|
||||
})
|
||||
}
|
||||
poll_ifaces();
|
||||
}
|
||||
|
||||
/// Lazy init should be called after spawning init thread.
|
||||
pub fn lazy_init() {
|
||||
for iface in IFACES.get().unwrap() {
|
||||
spawn_background_poll_thread(iface.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll iface
|
||||
pub fn poll_ifaces() {
|
||||
let ifaces = IFACES.get().unwrap();
|
||||
for iface in ifaces.iter() {
|
||||
iface.poll();
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
|
||||
/// AlwaysSome is a wrapper for Option.
|
||||
///
|
||||
/// AlwaysSome should always be Some(T), so we can treat it as a smart pointer.
|
||||
/// If it becomes None, the AlwaysSome should be viewed invalid and cannot be used anymore.
|
||||
pub struct AlwaysSome<T>(Option<T>);
|
||||
|
||||
impl<T> AlwaysSome<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
AlwaysSome(Some(value))
|
||||
}
|
||||
|
||||
pub fn try_take_with<R, E: Into<Error>, F: FnOnce(T) -> core::result::Result<R, (E, T)>>(
|
||||
&mut self,
|
||||
f: F,
|
||||
) -> Result<R> {
|
||||
let value = if let Some(value) = self.0.take() {
|
||||
value
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EINVAL, "the take cell is none");
|
||||
};
|
||||
match f(value) {
|
||||
Ok(res) => Ok(res),
|
||||
Err((err, t)) => {
|
||||
self.0 = Some(t);
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes inner value
|
||||
pub fn take(&mut self) -> T {
|
||||
debug_assert!(self.0.is_some());
|
||||
self.0.take().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for AlwaysSome<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for AlwaysSome<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.0.as_mut().unwrap()
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::net::iface::BindPortConfig;
|
||||
use crate::net::iface::Iface;
|
||||
use crate::net::iface::{AnyBoundSocket, AnyUnboundSocket};
|
||||
use crate::net::iface::{IpAddress, IpEndpoint};
|
||||
use crate::net::IFACES;
|
||||
use crate::prelude::*;
|
||||
|
||||
pub fn get_iface_to_bind(ip_addr: &IpAddress) -> Option<Arc<dyn Iface>> {
|
||||
let ifaces = IFACES.get().unwrap();
|
||||
let IpAddress::Ipv4(ipv4_addr) = ip_addr;
|
||||
ifaces
|
||||
.iter()
|
||||
.find(|iface| {
|
||||
if let Some(iface_ipv4_addr) = iface.ipv4_addr() {
|
||||
iface_ipv4_addr == *ipv4_addr
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.map(Clone::clone)
|
||||
}
|
||||
|
||||
/// Get a suitable iface to deal with sendto/connect request if the socket is not bound to an iface.
|
||||
/// If the remote address is the same as that of some iface, we will use the iface.
|
||||
/// Otherwise, we will use a default interface.
|
||||
fn get_ephemeral_iface(remote_ip_addr: &IpAddress) -> Arc<dyn Iface> {
|
||||
let ifaces = IFACES.get().unwrap();
|
||||
let IpAddress::Ipv4(remote_ipv4_addr) = remote_ip_addr;
|
||||
if let Some(iface) = ifaces.iter().find(|iface| {
|
||||
if let Some(iface_ipv4_addr) = iface.ipv4_addr() {
|
||||
iface_ipv4_addr == *remote_ipv4_addr
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
return iface.clone();
|
||||
}
|
||||
// FIXME: use the virtio-net as the default interface
|
||||
ifaces[0].clone()
|
||||
}
|
||||
|
||||
pub(super) fn bind_socket(
|
||||
unbound_socket: Box<AnyUnboundSocket>,
|
||||
endpoint: IpEndpoint,
|
||||
can_reuse: bool,
|
||||
) -> core::result::Result<Arc<AnyBoundSocket>, (Error, Box<AnyUnboundSocket>)> {
|
||||
let iface = match get_iface_to_bind(&endpoint.addr) {
|
||||
Some(iface) => iface,
|
||||
None => {
|
||||
let err = Error::with_message(Errno::EADDRNOTAVAIL, "Request iface is not available");
|
||||
return Err((err, unbound_socket));
|
||||
}
|
||||
};
|
||||
let bind_port_config = match BindPortConfig::new(endpoint.port, can_reuse) {
|
||||
Ok(config) => config,
|
||||
Err(e) => return Err((e, unbound_socket)),
|
||||
};
|
||||
iface.bind_socket(unbound_socket, bind_port_config)
|
||||
}
|
||||
|
||||
pub fn get_ephemeral_endpoint(remote_endpoint: &IpEndpoint) -> IpEndpoint {
|
||||
let iface = get_ephemeral_iface(&remote_endpoint.addr);
|
||||
let ip_addr = iface.ipv4_addr().unwrap();
|
||||
IpEndpoint::new(IpAddress::Ipv4(ip_addr), 0)
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::{IoEvents, Observer};
|
||||
use crate::net::iface::IpEndpoint;
|
||||
|
||||
use crate::net::poll_ifaces;
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
use crate::{
|
||||
net::{
|
||||
iface::{AnyBoundSocket, RawUdpSocket},
|
||||
socket::util::send_recv_flags::SendRecvFlags,
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub struct BoundDatagram {
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
remote_endpoint: RwLock<Option<IpEndpoint>>,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
impl BoundDatagram {
|
||||
pub fn new(bound_socket: Arc<AnyBoundSocket>, pollee: Pollee) -> Arc<Self> {
|
||||
let bound = Arc::new(Self {
|
||||
bound_socket,
|
||||
remote_endpoint: RwLock::new(None),
|
||||
pollee,
|
||||
});
|
||||
bound.bound_socket.set_observer(Arc::downgrade(&bound) as _);
|
||||
bound
|
||||
}
|
||||
|
||||
pub fn remote_endpoint(&self) -> Result<IpEndpoint> {
|
||||
self.remote_endpoint
|
||||
.read()
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "remote endpoint is not specified"))
|
||||
}
|
||||
|
||||
pub fn set_remote_endpoint(&self, endpoint: IpEndpoint) {
|
||||
*self.remote_endpoint.write() = Some(endpoint);
|
||||
}
|
||||
|
||||
pub fn local_endpoint(&self) -> Result<IpEndpoint> {
|
||||
self.bound_socket.local_endpoint().ok_or_else(|| {
|
||||
Error::with_message(Errno::EINVAL, "socket does not bind to local endpoint")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_recvfrom(
|
||||
&self,
|
||||
buf: &mut [u8],
|
||||
flags: &SendRecvFlags,
|
||||
) -> Result<(usize, IpEndpoint)> {
|
||||
poll_ifaces();
|
||||
let recv_slice = |socket: &mut RawUdpSocket| {
|
||||
socket
|
||||
.recv_slice(buf)
|
||||
.map_err(|_| Error::with_message(Errno::EAGAIN, "recv buf is empty"))
|
||||
};
|
||||
self.bound_socket.raw_with(recv_slice)
|
||||
}
|
||||
|
||||
pub fn try_sendto(
|
||||
&self,
|
||||
buf: &[u8],
|
||||
remote: Option<IpEndpoint>,
|
||||
flags: SendRecvFlags,
|
||||
) -> Result<usize> {
|
||||
let remote_endpoint = remote
|
||||
.or_else(|| self.remote_endpoint().ok())
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "udp should provide remote addr"))?;
|
||||
let send_slice = |socket: &mut RawUdpSocket| {
|
||||
socket
|
||||
.send_slice(buf, remote_endpoint)
|
||||
.map(|_| buf.len())
|
||||
.map_err(|_| Error::with_message(Errno::EAGAIN, "send udp packet fails"))
|
||||
};
|
||||
let len = self.bound_socket.raw_with(send_slice)?;
|
||||
poll_ifaces();
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn update_io_events(&self) {
|
||||
self.bound_socket.raw_with(|socket: &mut RawUdpSocket| {
|
||||
let pollee = &self.pollee;
|
||||
|
||||
if socket.can_recv() {
|
||||
pollee.add_events(IoEvents::IN);
|
||||
} else {
|
||||
pollee.del_events(IoEvents::IN);
|
||||
}
|
||||
|
||||
if socket.can_send() {
|
||||
pollee.add_events(IoEvents::OUT);
|
||||
} else {
|
||||
pollee.del_events(IoEvents::OUT);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<()> for BoundDatagram {
|
||||
fn on_events(&self, _: &()) {
|
||||
self.update_io_events();
|
||||
}
|
||||
}
|
@ -1,211 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::file_handle::FileLike;
|
||||
use crate::fs::utils::StatusFlags;
|
||||
use crate::net::iface::IpEndpoint;
|
||||
use crate::net::socket::util::send_recv_flags::SendRecvFlags;
|
||||
use crate::net::socket::util::socket_addr::SocketAddr;
|
||||
use crate::net::socket::Socket;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
use self::bound::BoundDatagram;
|
||||
use self::unbound::UnboundDatagram;
|
||||
|
||||
use super::always_some::AlwaysSome;
|
||||
use super::common::get_ephemeral_endpoint;
|
||||
|
||||
mod bound;
|
||||
mod unbound;
|
||||
|
||||
pub struct DatagramSocket {
|
||||
nonblocking: AtomicBool,
|
||||
inner: RwLock<Inner>,
|
||||
}
|
||||
|
||||
enum Inner {
|
||||
Unbound(AlwaysSome<UnboundDatagram>),
|
||||
Bound(Arc<BoundDatagram>),
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn is_bound(&self) -> bool {
|
||||
matches!(self, Inner::Bound { .. })
|
||||
}
|
||||
|
||||
fn bind(&mut self, endpoint: IpEndpoint) -> Result<Arc<BoundDatagram>> {
|
||||
let unbound = match self {
|
||||
Inner::Unbound(unbound) => unbound,
|
||||
Inner::Bound(..) => return_errno_with_message!(
|
||||
Errno::EINVAL,
|
||||
"the socket is already bound to an address"
|
||||
),
|
||||
};
|
||||
let bound = unbound.try_take_with(|unbound| unbound.bind(endpoint))?;
|
||||
*self = Inner::Bound(bound.clone());
|
||||
Ok(bound)
|
||||
}
|
||||
|
||||
fn bind_to_ephemeral_endpoint(
|
||||
&mut self,
|
||||
remote_endpoint: &IpEndpoint,
|
||||
) -> Result<Arc<BoundDatagram>> {
|
||||
let endpoint = get_ephemeral_endpoint(remote_endpoint);
|
||||
self.bind(endpoint)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
match self {
|
||||
Inner::Unbound(unbound) => unbound.poll(mask, poller),
|
||||
Inner::Bound(bound) => bound.poll(mask, poller),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatagramSocket {
|
||||
pub fn new(nonblocking: bool) -> Self {
|
||||
let unbound = UnboundDatagram::new();
|
||||
Self {
|
||||
inner: RwLock::new(Inner::Unbound(AlwaysSome::new(unbound))),
|
||||
nonblocking: AtomicBool::new(nonblocking),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_bound(&self) -> bool {
|
||||
self.inner.read().is_bound()
|
||||
}
|
||||
|
||||
pub fn is_nonblocking(&self) -> bool {
|
||||
self.nonblocking.load(Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) {
|
||||
self.nonblocking.store(nonblocking, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn bound(&self) -> Result<Arc<BoundDatagram>> {
|
||||
if let Inner::Bound(bound) = &*self.inner.read() {
|
||||
Ok(bound.clone())
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EINVAL, "socket does not bind to local endpoint")
|
||||
}
|
||||
}
|
||||
|
||||
fn try_bind_empheral(&self, remote_endpoint: &IpEndpoint) -> Result<Arc<BoundDatagram>> {
|
||||
// Fast path
|
||||
if let Inner::Bound(bound) = &*self.inner.read() {
|
||||
return Ok(bound.clone());
|
||||
}
|
||||
|
||||
// Slow path
|
||||
let mut inner = self.inner.write();
|
||||
if let Inner::Bound(bound) = &*inner {
|
||||
return Ok(bound.clone());
|
||||
}
|
||||
inner.bind_to_ephemeral_endpoint(remote_endpoint)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for DatagramSocket {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
// FIXME: respect flags
|
||||
let flags = SendRecvFlags::empty();
|
||||
let (recv_len, _) = self.recvfrom(buf, flags)?;
|
||||
Ok(recv_len)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
// FIXME: set correct flags
|
||||
let flags = SendRecvFlags::empty();
|
||||
self.sendto(buf, None, flags)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.inner.read().poll(mask, poller)
|
||||
}
|
||||
|
||||
fn as_socket(self: Arc<Self>) -> Option<Arc<dyn Socket>> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
if self.is_nonblocking() {
|
||||
StatusFlags::O_NONBLOCK
|
||||
} else {
|
||||
StatusFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
if new_flags.contains(StatusFlags::O_NONBLOCK) {
|
||||
self.set_nonblocking(true);
|
||||
} else {
|
||||
self.set_nonblocking(false);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Socket for DatagramSocket {
|
||||
fn bind(&self, socket_addr: SocketAddr) -> Result<()> {
|
||||
let endpoint = socket_addr.try_into()?;
|
||||
self.inner.write().bind(endpoint)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn connect(&self, socket_addr: SocketAddr) -> Result<()> {
|
||||
let endpoint = socket_addr.try_into()?;
|
||||
let bound = self.try_bind_empheral(&endpoint)?;
|
||||
bound.set_remote_endpoint(endpoint);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn addr(&self) -> Result<SocketAddr> {
|
||||
self.bound()?.local_endpoint()?.try_into()
|
||||
}
|
||||
|
||||
fn peer_addr(&self) -> Result<SocketAddr> {
|
||||
self.bound()?.remote_endpoint()?.try_into()
|
||||
}
|
||||
|
||||
// FIXME: respect RecvFromFlags
|
||||
fn recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, SocketAddr)> {
|
||||
debug_assert!(flags.is_all_supported());
|
||||
let bound = self.bound()?;
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
if let Ok((recv_len, remote_endpoint)) = bound.try_recvfrom(buf, &flags) {
|
||||
let remote_addr = remote_endpoint.try_into()?;
|
||||
return Ok((recv_len, remote_addr));
|
||||
}
|
||||
let events = bound.poll(IoEvents::IN, Some(&poller));
|
||||
if !events.contains(IoEvents::IN) {
|
||||
if self.is_nonblocking() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "try to receive again");
|
||||
}
|
||||
// FIXME: deal with recvfrom timeout
|
||||
poller.wait()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sendto(
|
||||
&self,
|
||||
buf: &[u8],
|
||||
remote: Option<SocketAddr>,
|
||||
flags: SendRecvFlags,
|
||||
) -> Result<usize> {
|
||||
debug_assert!(flags.is_all_supported());
|
||||
let (bound, remote_endpoint) = if let Some(addr) = remote {
|
||||
let endpoint = addr.try_into()?;
|
||||
(self.try_bind_empheral(&endpoint)?, Some(endpoint))
|
||||
} else {
|
||||
let bound = self.bound()?;
|
||||
(bound, None)
|
||||
};
|
||||
bound.try_sendto(buf, remote_endpoint, flags)
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::net::iface::IpEndpoint;
|
||||
|
||||
use crate::net::socket::ip::common::bind_socket;
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
use crate::{
|
||||
net::iface::{AnyUnboundSocket, RawUdpSocket},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use super::bound::BoundDatagram;
|
||||
|
||||
pub struct UnboundDatagram {
|
||||
unbound_socket: Box<AnyUnboundSocket>,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
impl UnboundDatagram {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
unbound_socket: Box::new(AnyUnboundSocket::new_udp()),
|
||||
pollee: Pollee::new(IoEvents::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
pub fn bind(
|
||||
self,
|
||||
endpoint: IpEndpoint,
|
||||
) -> core::result::Result<Arc<BoundDatagram>, (Error, Self)> {
|
||||
let bound_socket = match bind_socket(self.unbound_socket, endpoint, false) {
|
||||
Ok(bound_socket) => bound_socket,
|
||||
Err((err, unbound_socket)) => {
|
||||
return Err((
|
||||
err,
|
||||
Self {
|
||||
unbound_socket,
|
||||
pollee: self.pollee,
|
||||
},
|
||||
))
|
||||
}
|
||||
};
|
||||
let bound_endpoint = bound_socket.local_endpoint().unwrap();
|
||||
bound_socket.raw_with(|socket: &mut RawUdpSocket| {
|
||||
socket.bind(bound_endpoint).unwrap();
|
||||
});
|
||||
Ok(BoundDatagram::new(bound_socket, self.pollee))
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod always_some;
|
||||
mod common;
|
||||
mod datagram;
|
||||
pub mod stream;
|
||||
|
||||
pub use datagram::DatagramSocket;
|
||||
pub use stream::StreamSocket;
|
@ -1,171 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::events::{IoEvents, Observer};
|
||||
use crate::net::iface::IpEndpoint;
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
use crate::{
|
||||
net::{
|
||||
iface::{AnyBoundSocket, RawTcpSocket},
|
||||
poll_ifaces,
|
||||
socket::util::{send_recv_flags::SendRecvFlags, shutdown_cmd::SockShutdownCmd},
|
||||
},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub struct ConnectedStream {
|
||||
nonblocking: AtomicBool,
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
remote_endpoint: IpEndpoint,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
impl ConnectedStream {
|
||||
pub fn new(
|
||||
is_nonblocking: bool,
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
remote_endpoint: IpEndpoint,
|
||||
pollee: Pollee,
|
||||
) -> Arc<Self> {
|
||||
let connected = Arc::new(Self {
|
||||
nonblocking: AtomicBool::new(is_nonblocking),
|
||||
bound_socket,
|
||||
remote_endpoint,
|
||||
pollee,
|
||||
});
|
||||
connected
|
||||
.bound_socket
|
||||
.set_observer(Arc::downgrade(&connected) as _);
|
||||
connected
|
||||
}
|
||||
|
||||
pub fn shutdown(&self, cmd: SockShutdownCmd) -> Result<()> {
|
||||
// TODO: deal with cmd
|
||||
self.bound_socket.raw_with(|socket: &mut RawTcpSocket| {
|
||||
socket.close();
|
||||
});
|
||||
poll_ifaces();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, IpEndpoint)> {
|
||||
debug_assert!(flags.is_all_supported());
|
||||
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
let recv_len = self.try_recvfrom(buf, flags)?;
|
||||
if recv_len > 0 {
|
||||
let remote_endpoint = self.remote_endpoint()?;
|
||||
return Ok((recv_len, remote_endpoint));
|
||||
}
|
||||
let events = self.poll(IoEvents::IN, Some(&poller));
|
||||
if events.contains(IoEvents::HUP) || events.contains(IoEvents::ERR) {
|
||||
return_errno_with_message!(Errno::ENOTCONN, "recv packet fails");
|
||||
}
|
||||
if !events.contains(IoEvents::IN) {
|
||||
if self.is_nonblocking() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "try to recv again");
|
||||
}
|
||||
// FIXME: deal with receive timeout
|
||||
poller.wait()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<usize> {
|
||||
poll_ifaces();
|
||||
let res = self.bound_socket.raw_with(|socket: &mut RawTcpSocket| {
|
||||
socket
|
||||
.recv_slice(buf)
|
||||
.map_err(|_| Error::with_message(Errno::ENOTCONN, "fail to recv packet"))
|
||||
});
|
||||
self.update_io_events();
|
||||
res
|
||||
}
|
||||
|
||||
pub fn sendto(&self, buf: &[u8], flags: SendRecvFlags) -> Result<usize> {
|
||||
debug_assert!(flags.is_all_supported());
|
||||
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
let sent_len = self.try_sendto(buf, flags)?;
|
||||
if sent_len > 0 {
|
||||
return Ok(sent_len);
|
||||
}
|
||||
let events = self.poll(IoEvents::OUT, Some(&poller));
|
||||
if events.contains(IoEvents::HUP) || events.contains(IoEvents::ERR) {
|
||||
return_errno_with_message!(Errno::ENOBUFS, "fail to send packets");
|
||||
}
|
||||
if !events.contains(IoEvents::OUT) {
|
||||
if self.is_nonblocking() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "try to send again");
|
||||
}
|
||||
// FIXME: deal with send timeout
|
||||
poller.wait()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn try_sendto(&self, buf: &[u8], flags: SendRecvFlags) -> Result<usize> {
|
||||
let res = self
|
||||
.bound_socket
|
||||
.raw_with(|socket: &mut RawTcpSocket| socket.send_slice(buf))
|
||||
.map_err(|_| Error::with_message(Errno::ENOBUFS, "cannot send packet"));
|
||||
match res {
|
||||
// We have to explicitly invoke `update_io_events` when the send buffer becomes
|
||||
// full. Note that smoltcp does not think it is an interface event, so calling
|
||||
// `poll_ifaces` alone is not enough.
|
||||
Ok(0) => self.update_io_events(),
|
||||
Ok(_) => poll_ifaces(),
|
||||
_ => (),
|
||||
};
|
||||
res
|
||||
}
|
||||
|
||||
pub fn local_endpoint(&self) -> Result<IpEndpoint> {
|
||||
self.bound_socket
|
||||
.local_endpoint()
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "does not has remote endpoint"))
|
||||
}
|
||||
|
||||
pub fn remote_endpoint(&self) -> Result<IpEndpoint> {
|
||||
Ok(self.remote_endpoint)
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn update_io_events(&self) {
|
||||
self.bound_socket.raw_with(|socket: &mut RawTcpSocket| {
|
||||
let pollee = &self.pollee;
|
||||
|
||||
if socket.can_recv() {
|
||||
pollee.add_events(IoEvents::IN);
|
||||
} else {
|
||||
pollee.del_events(IoEvents::IN);
|
||||
}
|
||||
|
||||
if socket.can_send() {
|
||||
pollee.add_events(IoEvents::OUT);
|
||||
} else {
|
||||
pollee.del_events(IoEvents::OUT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn is_nonblocking(&self) -> bool {
|
||||
self.nonblocking.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) {
|
||||
self.nonblocking.store(nonblocking, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<()> for ConnectedStream {
|
||||
fn on_events(&self, _: &()) {
|
||||
self.update_io_events();
|
||||
}
|
||||
}
|
@ -1,157 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
|
||||
use crate::events::{IoEvents, Observer};
|
||||
use crate::net::iface::RawTcpSocket;
|
||||
use crate::net::poll_ifaces;
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::net::iface::{AnyBoundSocket, IpEndpoint};
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
|
||||
use super::connected::ConnectedStream;
|
||||
use super::init::InitStream;
|
||||
|
||||
pub struct ConnectingStream {
|
||||
nonblocking: AtomicBool,
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
remote_endpoint: IpEndpoint,
|
||||
conn_result: RwLock<Option<ConnResult>>,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
enum ConnResult {
|
||||
Connected,
|
||||
Refused,
|
||||
}
|
||||
|
||||
impl ConnectingStream {
|
||||
pub fn new(
|
||||
nonblocking: bool,
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
remote_endpoint: IpEndpoint,
|
||||
pollee: Pollee,
|
||||
) -> Result<Arc<Self>> {
|
||||
bound_socket.do_connect(remote_endpoint)?;
|
||||
|
||||
let connecting = Arc::new(Self {
|
||||
nonblocking: AtomicBool::new(nonblocking),
|
||||
bound_socket,
|
||||
remote_endpoint,
|
||||
conn_result: RwLock::new(None),
|
||||
pollee,
|
||||
});
|
||||
connecting.pollee.reset_events();
|
||||
connecting
|
||||
.bound_socket
|
||||
.set_observer(Arc::downgrade(&connecting) as _);
|
||||
Ok(connecting)
|
||||
}
|
||||
|
||||
pub fn wait_conn(
|
||||
&self,
|
||||
) -> core::result::Result<Arc<ConnectedStream>, (Error, Arc<InitStream>)> {
|
||||
debug_assert!(!self.is_nonblocking());
|
||||
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
poll_ifaces();
|
||||
|
||||
match *self.conn_result.read() {
|
||||
Some(ConnResult::Connected) => {
|
||||
return Ok(ConnectedStream::new(
|
||||
self.is_nonblocking(),
|
||||
self.bound_socket.clone(),
|
||||
self.remote_endpoint,
|
||||
self.pollee.clone(),
|
||||
));
|
||||
}
|
||||
Some(ConnResult::Refused) => {
|
||||
return Err((
|
||||
Error::with_message(Errno::ECONNREFUSED, "connection refused"),
|
||||
InitStream::new_bound(
|
||||
self.is_nonblocking(),
|
||||
self.bound_socket.clone(),
|
||||
self.pollee.clone(),
|
||||
),
|
||||
));
|
||||
}
|
||||
None => (),
|
||||
};
|
||||
|
||||
let events = self.poll(IoEvents::OUT, Some(&poller));
|
||||
if !events.contains(IoEvents::OUT) {
|
||||
// FIXME: deal with nonblocking mode & connecting timeout
|
||||
poller.wait().expect("async connect() not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_endpoint(&self) -> Result<IpEndpoint> {
|
||||
self.bound_socket
|
||||
.local_endpoint()
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "no local endpoint"))
|
||||
}
|
||||
|
||||
pub fn remote_endpoint(&self) -> Result<IpEndpoint> {
|
||||
Ok(self.remote_endpoint)
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
pub fn is_nonblocking(&self) -> bool {
|
||||
self.nonblocking.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) {
|
||||
self.nonblocking.store(nonblocking, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn update_io_events(&self) {
|
||||
if self.conn_result.read().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let became_writable = self.bound_socket.raw_with(|socket: &mut RawTcpSocket| {
|
||||
let mut result = self.conn_result.write();
|
||||
if result.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Connected
|
||||
if socket.can_send() {
|
||||
*result = Some(ConnResult::Connected);
|
||||
return true;
|
||||
}
|
||||
// Connecting
|
||||
if socket.is_open() {
|
||||
return false;
|
||||
}
|
||||
// Refused
|
||||
*result = Some(ConnResult::Refused);
|
||||
true
|
||||
});
|
||||
|
||||
// Either when the connection is established, or when the connection fails, the socket
|
||||
// shall indicate that it is writable.
|
||||
//
|
||||
// TODO: Find a way to turn `ConnectingStream` into `ConnectedStream` or `InitStream`
|
||||
// here, so non-blocking `connect()` can work correctly. Meanwhile, the latter should
|
||||
// be responsible to initialize all the I/O events including `IoEvents::OUT`, so the
|
||||
// following hard-coded event addition can be removed.
|
||||
if became_writable {
|
||||
self.pollee.add_events(IoEvents::OUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<()> for ConnectingStream {
|
||||
fn on_events(&self, _: &()) {
|
||||
self.update_io_events();
|
||||
}
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::net::iface::Iface;
|
||||
use crate::net::iface::IpEndpoint;
|
||||
use crate::net::iface::{AnyBoundSocket, AnyUnboundSocket};
|
||||
use crate::net::socket::ip::always_some::AlwaysSome;
|
||||
use crate::net::socket::ip::common::{bind_socket, get_ephemeral_endpoint};
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Pollee;
|
||||
use crate::process::signal::Poller;
|
||||
|
||||
use super::connecting::ConnectingStream;
|
||||
use super::listen::ListenStream;
|
||||
|
||||
pub struct InitStream {
|
||||
inner: RwLock<Inner>,
|
||||
is_nonblocking: AtomicBool,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
enum Inner {
|
||||
Unbound(AlwaysSome<Box<AnyUnboundSocket>>),
|
||||
Bound(AlwaysSome<Arc<AnyBoundSocket>>),
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn new() -> Inner {
|
||||
let unbound_socket = Box::new(AnyUnboundSocket::new_tcp());
|
||||
Inner::Unbound(AlwaysSome::new(unbound_socket))
|
||||
}
|
||||
|
||||
fn is_bound(&self) -> bool {
|
||||
match self {
|
||||
Self::Unbound(_) => false,
|
||||
Self::Bound(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn bind(&mut self, endpoint: IpEndpoint) -> Result<()> {
|
||||
let unbound_socket = if let Inner::Unbound(unbound_socket) = self {
|
||||
unbound_socket
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EINVAL, "the socket is already bound to an address");
|
||||
};
|
||||
let bound_socket =
|
||||
unbound_socket.try_take_with(|raw_socket| bind_socket(raw_socket, endpoint, false))?;
|
||||
*self = Inner::Bound(AlwaysSome::new(bound_socket));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bind_to_ephemeral_endpoint(&mut self, remote_endpoint: &IpEndpoint) -> Result<()> {
|
||||
let endpoint = get_ephemeral_endpoint(remote_endpoint);
|
||||
self.bind(endpoint)
|
||||
}
|
||||
|
||||
fn bound_socket(&self) -> Option<&Arc<AnyBoundSocket>> {
|
||||
match self {
|
||||
Inner::Bound(bound_socket) => Some(bound_socket),
|
||||
Inner::Unbound(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn iface(&self) -> Option<Arc<dyn Iface>> {
|
||||
match self {
|
||||
Inner::Bound(bound_socket) => Some(bound_socket.iface().clone()),
|
||||
Inner::Unbound(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn local_endpoint(&self) -> Option<IpEndpoint> {
|
||||
self.bound_socket()
|
||||
.and_then(|socket| socket.local_endpoint())
|
||||
}
|
||||
}
|
||||
|
||||
impl InitStream {
|
||||
// FIXME: In Linux we have the `POLLOUT` event for a newly created socket, while calling
|
||||
// `write()` on it triggers `SIGPIPE`/`EPIPE`. No documentation found yet, but confirmed by
|
||||
// experimentation and Linux source code.
|
||||
pub fn new(nonblocking: bool) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
inner: RwLock::new(Inner::new()),
|
||||
is_nonblocking: AtomicBool::new(nonblocking),
|
||||
pollee: Pollee::new(IoEvents::empty()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_bound(
|
||||
nonblocking: bool,
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
pollee: Pollee,
|
||||
) -> Arc<Self> {
|
||||
bound_socket.set_observer(Weak::<()>::new());
|
||||
let inner = Inner::Bound(AlwaysSome::new(bound_socket));
|
||||
Arc::new(Self {
|
||||
is_nonblocking: AtomicBool::new(nonblocking),
|
||||
inner: RwLock::new(inner),
|
||||
pollee,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn bind(&self, endpoint: IpEndpoint) -> Result<()> {
|
||||
self.inner.write().bind(endpoint)
|
||||
}
|
||||
|
||||
pub fn connect(&self, remote_endpoint: &IpEndpoint) -> Result<Arc<ConnectingStream>> {
|
||||
if !self.inner.read().is_bound() {
|
||||
self.inner
|
||||
.write()
|
||||
.bind_to_ephemeral_endpoint(remote_endpoint)?
|
||||
}
|
||||
ConnectingStream::new(
|
||||
self.is_nonblocking(),
|
||||
self.inner.read().bound_socket().unwrap().clone(),
|
||||
*remote_endpoint,
|
||||
self.pollee.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn listen(&self, backlog: usize) -> Result<Arc<ListenStream>> {
|
||||
let bound_socket = if let Some(bound_socket) = self.inner.read().bound_socket() {
|
||||
bound_socket.clone()
|
||||
} else {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot listen without bound")
|
||||
};
|
||||
ListenStream::new(
|
||||
self.is_nonblocking(),
|
||||
bound_socket,
|
||||
backlog,
|
||||
self.pollee.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn local_endpoint(&self) -> Result<IpEndpoint> {
|
||||
self.inner
|
||||
.read()
|
||||
.local_endpoint()
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "does not has local endpoint"))
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
pub fn is_nonblocking(&self) -> bool {
|
||||
self.is_nonblocking.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) {
|
||||
self.is_nonblocking.store(nonblocking, Ordering::Relaxed);
|
||||
}
|
||||
}
|
@ -1,181 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
use crate::events::{IoEvents, Observer};
|
||||
use crate::net::iface::{AnyUnboundSocket, BindPortConfig, IpEndpoint};
|
||||
|
||||
use crate::net::iface::{AnyBoundSocket, RawTcpSocket};
|
||||
use crate::process::signal::{Pollee, Poller};
|
||||
use crate::{net::poll_ifaces, prelude::*};
|
||||
|
||||
use super::connected::ConnectedStream;
|
||||
|
||||
pub struct ListenStream {
|
||||
is_nonblocking: AtomicBool,
|
||||
backlog: usize,
|
||||
/// A bound socket held to ensure the TCP port cannot be released
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
/// Backlog sockets listening at the local endpoint
|
||||
backlog_sockets: RwLock<Vec<BacklogSocket>>,
|
||||
pollee: Pollee,
|
||||
}
|
||||
|
||||
impl ListenStream {
|
||||
pub fn new(
|
||||
nonblocking: bool,
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
backlog: usize,
|
||||
pollee: Pollee,
|
||||
) -> Result<Arc<Self>> {
|
||||
let listen_stream = Arc::new(Self {
|
||||
is_nonblocking: AtomicBool::new(nonblocking),
|
||||
backlog,
|
||||
bound_socket,
|
||||
backlog_sockets: RwLock::new(Vec::new()),
|
||||
pollee,
|
||||
});
|
||||
listen_stream.fill_backlog_sockets()?;
|
||||
listen_stream.pollee.reset_events();
|
||||
listen_stream
|
||||
.bound_socket
|
||||
.set_observer(Arc::downgrade(&listen_stream) as _);
|
||||
Ok(listen_stream)
|
||||
}
|
||||
|
||||
pub fn accept(&self) -> Result<(Arc<ConnectedStream>, IpEndpoint)> {
|
||||
// wait to accept
|
||||
let poller = Poller::new();
|
||||
loop {
|
||||
poll_ifaces();
|
||||
let accepted_socket = if let Some(accepted_socket) = self.try_accept() {
|
||||
accepted_socket
|
||||
} else {
|
||||
let events = self.poll(IoEvents::IN, Some(&poller));
|
||||
if !events.contains(IoEvents::IN) {
|
||||
if self.is_nonblocking() {
|
||||
return_errno_with_message!(Errno::EAGAIN, "try accept again");
|
||||
}
|
||||
// FIXME: deal with accept timeout
|
||||
poller.wait()?;
|
||||
}
|
||||
continue;
|
||||
};
|
||||
let remote_endpoint = accepted_socket.remote_endpoint().unwrap();
|
||||
let connected_stream = {
|
||||
let BacklogSocket {
|
||||
bound_socket: backlog_socket,
|
||||
} = accepted_socket;
|
||||
ConnectedStream::new(
|
||||
false,
|
||||
backlog_socket,
|
||||
remote_endpoint,
|
||||
Pollee::new(IoEvents::empty()),
|
||||
)
|
||||
};
|
||||
return Ok((connected_stream, remote_endpoint));
|
||||
}
|
||||
}
|
||||
|
||||
/// Append sockets listening at LocalEndPoint to support backlog
|
||||
fn fill_backlog_sockets(&self) -> Result<()> {
|
||||
let backlog = self.backlog;
|
||||
let mut backlog_sockets = self.backlog_sockets.write();
|
||||
let current_backlog_len = backlog_sockets.len();
|
||||
debug_assert!(backlog >= current_backlog_len);
|
||||
if backlog == current_backlog_len {
|
||||
return Ok(());
|
||||
}
|
||||
for _ in current_backlog_len..backlog {
|
||||
let backlog_socket = BacklogSocket::new(&self.bound_socket)?;
|
||||
backlog_sockets.push(backlog_socket);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn try_accept(&self) -> Option<BacklogSocket> {
|
||||
let backlog_socket = {
|
||||
let mut backlog_sockets = self.backlog_sockets.write();
|
||||
let index = backlog_sockets
|
||||
.iter()
|
||||
.position(|backlog_socket| backlog_socket.is_active())?;
|
||||
backlog_sockets.remove(index)
|
||||
};
|
||||
self.fill_backlog_sockets().unwrap();
|
||||
self.update_io_events();
|
||||
Some(backlog_socket)
|
||||
}
|
||||
|
||||
pub fn local_endpoint(&self) -> Result<IpEndpoint> {
|
||||
self.bound_socket
|
||||
.local_endpoint()
|
||||
.ok_or_else(|| Error::with_message(Errno::EINVAL, "does not has remote endpoint"))
|
||||
}
|
||||
|
||||
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
self.pollee.poll(mask, poller)
|
||||
}
|
||||
|
||||
fn update_io_events(&self) {
|
||||
// The lock should be held to avoid data races
|
||||
let backlog_sockets = self.backlog_sockets.read();
|
||||
|
||||
let can_accept = backlog_sockets.iter().any(|socket| socket.is_active());
|
||||
if can_accept {
|
||||
self.pollee.add_events(IoEvents::IN);
|
||||
} else {
|
||||
self.pollee.del_events(IoEvents::IN);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_nonblocking(&self) -> bool {
|
||||
self.is_nonblocking.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_nonblocking(&self, nonblocking: bool) {
|
||||
self.is_nonblocking.store(nonblocking, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl Observer<()> for ListenStream {
|
||||
fn on_events(&self, _: &()) {
|
||||
self.update_io_events();
|
||||
}
|
||||
}
|
||||
|
||||
struct BacklogSocket {
|
||||
bound_socket: Arc<AnyBoundSocket>,
|
||||
}
|
||||
|
||||
impl BacklogSocket {
|
||||
fn new(bound_socket: &Arc<AnyBoundSocket>) -> Result<Self> {
|
||||
let local_endpoint = bound_socket.local_endpoint().ok_or(Error::with_message(
|
||||
Errno::EINVAL,
|
||||
"the socket is not bound",
|
||||
))?;
|
||||
let unbound_socket = Box::new(AnyUnboundSocket::new_tcp());
|
||||
let bound_socket = {
|
||||
let iface = bound_socket.iface();
|
||||
let bind_port_config = BindPortConfig::new(local_endpoint.port, true)?;
|
||||
iface
|
||||
.bind_socket(unbound_socket, bind_port_config)
|
||||
.map_err(|(e, _)| e)?
|
||||
};
|
||||
bound_socket.raw_with(|raw_tcp_socket: &mut RawTcpSocket| {
|
||||
raw_tcp_socket
|
||||
.listen(local_endpoint)
|
||||
.map_err(|_| Error::with_message(Errno::EINVAL, "fail to listen"))
|
||||
})?;
|
||||
Ok(Self { bound_socket })
|
||||
}
|
||||
|
||||
fn is_active(&self) -> bool {
|
||||
self.bound_socket
|
||||
.raw_with(|socket: &mut RawTcpSocket| socket.is_active())
|
||||
}
|
||||
|
||||
fn remote_endpoint(&self) -> Option<IpEndpoint> {
|
||||
self.bound_socket
|
||||
.raw_with(|socket: &mut RawTcpSocket| socket.remote_endpoint())
|
||||
}
|
||||
}
|
@ -1,406 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::events::IoEvents;
|
||||
use crate::fs::file_handle::FileLike;
|
||||
use crate::fs::utils::StatusFlags;
|
||||
use crate::net::socket::options::{
|
||||
Error, Linger, RecvBuf, ReuseAddr, ReusePort, SendBuf, SocketOption,
|
||||
};
|
||||
use crate::net::socket::util::options::{SocketOptionSet, MIN_RECVBUF, MIN_SENDBUF};
|
||||
use crate::net::socket::util::{
|
||||
send_recv_flags::SendRecvFlags, shutdown_cmd::SockShutdownCmd, socket_addr::SocketAddr,
|
||||
};
|
||||
use crate::net::socket::Socket;
|
||||
use crate::prelude::*;
|
||||
use crate::process::signal::Poller;
|
||||
use crate::{match_sock_option_mut, match_sock_option_ref};
|
||||
use options::{Congestion, MaxSegment, WindowClamp};
|
||||
use util::{TcpOptionSet, DEFAULT_MAXSEG};
|
||||
|
||||
use connected::ConnectedStream;
|
||||
use connecting::ConnectingStream;
|
||||
use init::InitStream;
|
||||
use listen::ListenStream;
|
||||
use options::NoDelay;
|
||||
use smoltcp::wire::IpEndpoint;
|
||||
|
||||
mod connected;
|
||||
mod connecting;
|
||||
mod init;
|
||||
mod listen;
|
||||
pub mod options;
|
||||
mod util;
|
||||
|
||||
pub use self::util::CongestionControl;
|
||||
|
||||
pub struct StreamSocket {
|
||||
options: RwLock<OptionSet>,
|
||||
state: RwLock<State>,
|
||||
}
|
||||
|
||||
enum State {
|
||||
// Start state
|
||||
Init(Arc<InitStream>),
|
||||
// Intermediate state
|
||||
Connecting(Arc<ConnectingStream>),
|
||||
// Final State 1
|
||||
Connected(Arc<ConnectedStream>),
|
||||
// Final State 2
|
||||
Listen(Arc<ListenStream>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct OptionSet {
|
||||
socket: SocketOptionSet,
|
||||
tcp: TcpOptionSet,
|
||||
}
|
||||
|
||||
impl OptionSet {
|
||||
fn new() -> Self {
|
||||
let socket = SocketOptionSet::new_tcp();
|
||||
let tcp = TcpOptionSet::new();
|
||||
OptionSet { socket, tcp }
|
||||
}
|
||||
}
|
||||
|
||||
impl StreamSocket {
|
||||
pub fn new(nonblocking: bool) -> Self {
|
||||
let options = OptionSet::new();
|
||||
let state = State::Init(InitStream::new(nonblocking));
|
||||
Self {
|
||||
options: RwLock::new(options),
|
||||
state: RwLock::new(state),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_nonblocking(&self) -> bool {
|
||||
match &*self.state.read() {
|
||||
State::Init(init) => init.is_nonblocking(),
|
||||
State::Connecting(connecting) => connecting.is_nonblocking(),
|
||||
State::Connected(connected) => connected.is_nonblocking(),
|
||||
State::Listen(listen) => listen.is_nonblocking(),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_nonblocking(&self, nonblocking: bool) {
|
||||
match &*self.state.read() {
|
||||
State::Init(init) => init.set_nonblocking(nonblocking),
|
||||
State::Connecting(connecting) => connecting.set_nonblocking(nonblocking),
|
||||
State::Connected(connected) => connected.set_nonblocking(nonblocking),
|
||||
State::Listen(listen) => listen.set_nonblocking(nonblocking),
|
||||
}
|
||||
}
|
||||
|
||||
fn do_connect(&self, remote_endpoint: &IpEndpoint) -> Result<Arc<ConnectingStream>> {
|
||||
let mut state = self.state.write();
|
||||
let init_stream = match &*state {
|
||||
State::Init(init_stream) => init_stream,
|
||||
State::Listen(_) | State::Connecting(_) | State::Connected(_) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot connect")
|
||||
}
|
||||
};
|
||||
|
||||
let connecting = init_stream.connect(remote_endpoint)?;
|
||||
*state = State::Connecting(connecting.clone());
|
||||
Ok(connecting)
|
||||
}
|
||||
}
|
||||
|
||||
impl FileLike for StreamSocket {
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize> {
|
||||
// FIXME: set correct flags
|
||||
let flags = SendRecvFlags::empty();
|
||||
let (recv_len, _) = self.recvfrom(buf, flags)?;
|
||||
Ok(recv_len)
|
||||
}
|
||||
|
||||
fn write(&self, buf: &[u8]) -> Result<usize> {
|
||||
// FIXME: set correct flags
|
||||
let flags = SendRecvFlags::empty();
|
||||
self.sendto(buf, None, flags)
|
||||
}
|
||||
|
||||
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
|
||||
let state = self.state.read();
|
||||
match &*state {
|
||||
State::Init(init) => init.poll(mask, poller),
|
||||
State::Connecting(connecting) => connecting.poll(mask, poller),
|
||||
State::Connected(connected) => connected.poll(mask, poller),
|
||||
State::Listen(listen) => listen.poll(mask, poller),
|
||||
}
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> StatusFlags {
|
||||
if self.is_nonblocking() {
|
||||
StatusFlags::O_NONBLOCK
|
||||
} else {
|
||||
StatusFlags::empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> {
|
||||
if new_flags.contains(StatusFlags::O_NONBLOCK) {
|
||||
self.set_nonblocking(true);
|
||||
} else {
|
||||
self.set_nonblocking(false);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn as_socket(self: Arc<Self>) -> Option<Arc<dyn Socket>> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Socket for StreamSocket {
|
||||
fn bind(&self, socket_addr: SocketAddr) -> Result<()> {
|
||||
let endpoint = socket_addr.try_into()?;
|
||||
let state = self.state.read();
|
||||
match &*state {
|
||||
State::Init(init_stream) => init_stream.bind(endpoint),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "cannot bind"),
|
||||
}
|
||||
}
|
||||
|
||||
fn connect(&self, socket_addr: SocketAddr) -> Result<()> {
|
||||
let remote_endpoint = socket_addr.try_into()?;
|
||||
|
||||
let connecting_stream = self.do_connect(&remote_endpoint)?;
|
||||
match connecting_stream.wait_conn() {
|
||||
Ok(connected_stream) => {
|
||||
*self.state.write() = State::Connected(connected_stream);
|
||||
Ok(())
|
||||
}
|
||||
Err((err, init_stream)) => {
|
||||
*self.state.write() = State::Init(init_stream);
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn listen(&self, backlog: usize) -> Result<()> {
|
||||
let mut state = self.state.write();
|
||||
let init_stream = match &*state {
|
||||
State::Init(init_stream) => init_stream,
|
||||
State::Connecting(connecting_stream) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot listen for a connecting stream")
|
||||
}
|
||||
State::Listen(listen_stream) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "cannot listen for a listening stream")
|
||||
}
|
||||
State::Connected(_) => return_errno_with_message!(Errno::EINVAL, "cannot listen"),
|
||||
};
|
||||
|
||||
let listener = init_stream.listen(backlog)?;
|
||||
*state = State::Listen(listener);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn accept(&self) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
let listen_stream = match &*self.state.read() {
|
||||
State::Listen(listen_stream) => listen_stream.clone(),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "the socket is not listening"),
|
||||
};
|
||||
|
||||
let (connected_stream, remote_endpoint) = {
|
||||
let listen_stream = listen_stream.clone();
|
||||
listen_stream.accept()?
|
||||
};
|
||||
|
||||
let accepted_socket = {
|
||||
let state = RwLock::new(State::Connected(connected_stream));
|
||||
Arc::new(StreamSocket {
|
||||
options: RwLock::new(OptionSet::new()),
|
||||
state,
|
||||
})
|
||||
};
|
||||
|
||||
let socket_addr = remote_endpoint.try_into()?;
|
||||
Ok((accepted_socket, socket_addr))
|
||||
}
|
||||
|
||||
fn shutdown(&self, cmd: SockShutdownCmd) -> Result<()> {
|
||||
let state = self.state.read();
|
||||
match &*state {
|
||||
State::Connected(connected_stream) => connected_stream.shutdown(cmd),
|
||||
// TDOD: shutdown listening stream
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "cannot shutdown"),
|
||||
}
|
||||
}
|
||||
|
||||
fn addr(&self) -> Result<SocketAddr> {
|
||||
let state = self.state.read();
|
||||
let local_endpoint = match &*state {
|
||||
State::Init(init_stream) => init_stream.local_endpoint(),
|
||||
State::Connecting(connecting_stream) => connecting_stream.local_endpoint(),
|
||||
State::Listen(listen_stream) => listen_stream.local_endpoint(),
|
||||
State::Connected(connected_stream) => connected_stream.local_endpoint(),
|
||||
}?;
|
||||
local_endpoint.try_into()
|
||||
}
|
||||
|
||||
fn peer_addr(&self) -> Result<SocketAddr> {
|
||||
let state = self.state.read();
|
||||
let remote_endpoint = match &*state {
|
||||
State::Init(init_stream) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "init socket does not have peer")
|
||||
}
|
||||
State::Connecting(connecting_stream) => connecting_stream.remote_endpoint(),
|
||||
State::Listen(listen_stream) => {
|
||||
return_errno_with_message!(Errno::EINVAL, "listening socket does not have peer")
|
||||
}
|
||||
State::Connected(connected_stream) => connected_stream.remote_endpoint(),
|
||||
}?;
|
||||
remote_endpoint.try_into()
|
||||
}
|
||||
|
||||
fn recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, SocketAddr)> {
|
||||
let connected_stream = match &*self.state.read() {
|
||||
State::Connected(connected_stream) => connected_stream.clone(),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "the socket is not connected"),
|
||||
};
|
||||
|
||||
let (recv_size, remote_endpoint) = connected_stream.recvfrom(buf, flags)?;
|
||||
let socket_addr = remote_endpoint.try_into()?;
|
||||
Ok((recv_size, socket_addr))
|
||||
}
|
||||
|
||||
fn sendto(
|
||||
&self,
|
||||
buf: &[u8],
|
||||
remote: Option<SocketAddr>,
|
||||
flags: SendRecvFlags,
|
||||
) -> Result<usize> {
|
||||
debug_assert!(remote.is_none());
|
||||
if remote.is_some() {
|
||||
return_errno_with_message!(Errno::EINVAL, "tcp socked should not provide remote addr");
|
||||
}
|
||||
|
||||
let connected_stream = match &*self.state.read() {
|
||||
State::Connected(connected_stream) => connected_stream.clone(),
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "the socket is not connected"),
|
||||
};
|
||||
connected_stream.sendto(buf, flags)
|
||||
}
|
||||
|
||||
fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> {
|
||||
let options = self.options.read();
|
||||
match_sock_option_mut!(option, {
|
||||
// Socket Options
|
||||
socket_errors: Error => {
|
||||
let sock_errors = options.socket.sock_errors();
|
||||
socket_errors.set(sock_errors);
|
||||
},
|
||||
socket_reuse_addr: ReuseAddr => {
|
||||
let reuse_addr = options.socket.reuse_addr();
|
||||
socket_reuse_addr.set(reuse_addr);
|
||||
},
|
||||
socket_send_buf: SendBuf => {
|
||||
let send_buf = options.socket.send_buf();
|
||||
socket_send_buf.set(send_buf);
|
||||
},
|
||||
socket_recv_buf: RecvBuf => {
|
||||
let recv_buf = options.socket.recv_buf();
|
||||
socket_recv_buf.set(recv_buf);
|
||||
},
|
||||
socket_reuse_port: ReusePort => {
|
||||
let reuse_port = options.socket.reuse_port();
|
||||
socket_reuse_port.set(reuse_port);
|
||||
},
|
||||
// Tcp Options
|
||||
tcp_no_delay: NoDelay => {
|
||||
let no_delay = options.tcp.no_delay();
|
||||
tcp_no_delay.set(no_delay);
|
||||
},
|
||||
tcp_congestion: Congestion => {
|
||||
let congestion = options.tcp.congestion();
|
||||
tcp_congestion.set(congestion);
|
||||
},
|
||||
tcp_maxseg: MaxSegment => {
|
||||
// It will always return the default MSS value defined above for an unconnected socket
|
||||
// and always return the actual current MSS for a connected one.
|
||||
|
||||
// FIXME: how to get the current MSS?
|
||||
let maxseg = match &*self.state.read() {
|
||||
State::Init(_) | State::Listen(_) | State::Connecting(_) => DEFAULT_MAXSEG,
|
||||
State::Connected(_) => options.tcp.maxseg(),
|
||||
};
|
||||
tcp_maxseg.set(maxseg);
|
||||
},
|
||||
tcp_window_clamp: WindowClamp => {
|
||||
let window_clamp = options.tcp.window_clamp();
|
||||
tcp_window_clamp.set(window_clamp);
|
||||
},
|
||||
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "get unknown option")
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
|
||||
let mut options = self.options.write();
|
||||
// FIXME: here we have only set the value of the option, without actually
|
||||
// making any real modifications.
|
||||
match_sock_option_ref!(option, {
|
||||
// Socket options
|
||||
socket_recv_buf: RecvBuf => {
|
||||
let recv_buf = socket_recv_buf.get().unwrap();
|
||||
if *recv_buf <= MIN_RECVBUF {
|
||||
options.socket.set_recv_buf(MIN_RECVBUF);
|
||||
} else{
|
||||
options.socket.set_recv_buf(*recv_buf);
|
||||
}
|
||||
},
|
||||
socket_send_buf: SendBuf => {
|
||||
let send_buf = socket_send_buf.get().unwrap();
|
||||
if *send_buf <= MIN_SENDBUF {
|
||||
options.socket.set_send_buf(MIN_SENDBUF);
|
||||
} else {
|
||||
options.socket.set_send_buf(*send_buf);
|
||||
}
|
||||
},
|
||||
socket_reuse_addr: ReuseAddr => {
|
||||
let reuse_addr = socket_reuse_addr.get().unwrap();
|
||||
options.socket.set_reuse_addr(*reuse_addr);
|
||||
},
|
||||
socket_reuse_port: ReusePort => {
|
||||
let reuse_port = socket_reuse_port.get().unwrap();
|
||||
options.socket.set_reuse_port(*reuse_port);
|
||||
},
|
||||
socket_linger: Linger => {
|
||||
let linger = socket_linger.get().unwrap();
|
||||
options.socket.set_linger(*linger);
|
||||
},
|
||||
// Tcp options
|
||||
tcp_no_delay: NoDelay => {
|
||||
let no_delay = tcp_no_delay.get().unwrap();
|
||||
options.tcp.set_no_delay(*no_delay);
|
||||
},
|
||||
tcp_congestion: Congestion => {
|
||||
let congestion = tcp_congestion.get().unwrap();
|
||||
options.tcp.set_congestion(*congestion);
|
||||
},
|
||||
tcp_maxseg: MaxSegment => {
|
||||
const MIN_MAXSEG: u32 = 536;
|
||||
const MAX_MAXSEG: u32 = 65535;
|
||||
let maxseg = tcp_maxseg.get().unwrap();
|
||||
|
||||
if *maxseg < MIN_MAXSEG || *maxseg > MAX_MAXSEG {
|
||||
return_errno_with_message!(Errno::EINVAL, "New maxseg should be in allowed range.");
|
||||
}
|
||||
|
||||
options.tcp.set_maxseg(*maxseg);
|
||||
},
|
||||
tcp_window_clamp: WindowClamp => {
|
||||
let window_clamp = tcp_window_clamp.get().unwrap();
|
||||
let half_recv_buf = (options.socket.recv_buf()) / 2;
|
||||
if *window_clamp <= half_recv_buf {
|
||||
options.tcp.set_window_clamp(half_recv_buf);
|
||||
} else {
|
||||
options.tcp.set_window_clamp(*window_clamp);
|
||||
}
|
||||
},
|
||||
_ => return_errno_with_message!(Errno::ENOPROTOOPT, "set unknown option")
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::impl_socket_options;
|
||||
|
||||
use super::CongestionControl;
|
||||
|
||||
impl_socket_options!(
|
||||
pub struct NoDelay(bool);
|
||||
pub struct Congestion(CongestionControl);
|
||||
pub struct MaxSegment(u32);
|
||||
pub struct WindowClamp(u32);
|
||||
);
|
@ -1,61 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, CopyGetters, Setters)]
|
||||
#[get_copy = "pub"]
|
||||
#[set = "pub"]
|
||||
pub struct TcpOptionSet {
|
||||
no_delay: bool,
|
||||
congestion: CongestionControl,
|
||||
maxseg: u32,
|
||||
window_clamp: u32,
|
||||
}
|
||||
|
||||
pub const DEFAULT_MAXSEG: u32 = 536;
|
||||
pub const DEFAULT_WINDOW_CLAMP: u32 = 0x8000_0000;
|
||||
|
||||
impl TcpOptionSet {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
no_delay: false,
|
||||
congestion: CongestionControl::Reno,
|
||||
maxseg: DEFAULT_MAXSEG,
|
||||
window_clamp: DEFAULT_WINDOW_CLAMP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TcpOptionSet {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum CongestionControl {
|
||||
Reno,
|
||||
Cubic,
|
||||
}
|
||||
|
||||
impl CongestionControl {
|
||||
const RENO: &'static str = "reno";
|
||||
const CUBIC: &'static str = "cubic";
|
||||
|
||||
pub fn new(name: &str) -> Result<Self> {
|
||||
let congestion = match name {
|
||||
Self::RENO => Self::Reno,
|
||||
Self::CUBIC => Self::Cubic,
|
||||
_ => return_errno_with_message!(Errno::EINVAL, "unsupported congestion name"),
|
||||
};
|
||||
|
||||
Ok(congestion)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Reno => Self::RENO,
|
||||
Self::Cubic => Self::CUBIC,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::{fs::file_handle::FileLike, prelude::*};
|
||||
|
||||
use self::options::SocketOption;
|
||||
pub use self::util::options::LingerOption;
|
||||
pub use self::util::send_recv_flags::SendRecvFlags;
|
||||
pub use self::util::shutdown_cmd::SockShutdownCmd;
|
||||
pub use self::util::socket_addr::SocketAddr;
|
||||
|
||||
pub mod ip;
|
||||
pub mod options;
|
||||
pub mod unix;
|
||||
mod util;
|
||||
|
||||
/// Operations defined on a socket.
|
||||
pub trait Socket: FileLike + Send + Sync {
|
||||
/// Assign the address specified by socket_addr to the socket
|
||||
fn bind(&self, socket_addr: SocketAddr) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "bind not implemented");
|
||||
}
|
||||
|
||||
/// Build connection for a given address
|
||||
fn connect(&self, socket_addr: SocketAddr) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "connect not implemented");
|
||||
}
|
||||
|
||||
/// Listen for connections on a socket
|
||||
fn listen(&self, backlog: usize) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "connect not implemented");
|
||||
}
|
||||
|
||||
/// Accept a connection on a socket
|
||||
fn accept(&self) -> Result<(Arc<dyn FileLike>, SocketAddr)> {
|
||||
return_errno_with_message!(Errno::EINVAL, "accept not implemented");
|
||||
}
|
||||
|
||||
/// Shut down part of a full-duplex connection
|
||||
fn shutdown(&self, cmd: SockShutdownCmd) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "shutdown not implemented");
|
||||
}
|
||||
|
||||
/// Get address of this socket.
|
||||
fn addr(&self) -> Result<SocketAddr> {
|
||||
return_errno_with_message!(Errno::EINVAL, "getsockname not implemented");
|
||||
}
|
||||
|
||||
/// Get address of peer socket
|
||||
fn peer_addr(&self) -> Result<SocketAddr> {
|
||||
return_errno_with_message!(Errno::EINVAL, "getpeername not implemented");
|
||||
}
|
||||
|
||||
/// Get options on the socket. The resulted option will put in the `option` parameter, if
|
||||
/// this method returns success.
|
||||
fn get_option(&self, option: &mut dyn SocketOption) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "getsockopt not implemented");
|
||||
}
|
||||
|
||||
/// Set options on the socket.
|
||||
fn set_option(&self, option: &dyn SocketOption) -> Result<()> {
|
||||
return_errno_with_message!(Errno::EINVAL, "setsockopt not implemented");
|
||||
}
|
||||
|
||||
/// Receive a message from a socket
|
||||
fn recvfrom(&self, buf: &mut [u8], flags: SendRecvFlags) -> Result<(usize, SocketAddr)> {
|
||||
return_errno_with_message!(Errno::EINVAL, "recvfrom not implemented");
|
||||
}
|
||||
|
||||
/// Send a message on a socket
|
||||
fn sendto(
|
||||
&self,
|
||||
buf: &[u8],
|
||||
remote: Option<SocketAddr>,
|
||||
flags: SendRecvFlags,
|
||||
) -> Result<usize> {
|
||||
return_errno_with_message!(Errno::EINVAL, "recvfrom not implemented");
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_socket_options {
|
||||
($(
|
||||
$(#[$outer:meta])*
|
||||
pub struct $name: ident ( $value_ty:ty );
|
||||
)*) => {
|
||||
$(
|
||||
$(#[$outer])*
|
||||
#[derive(Debug)]
|
||||
pub struct $name (Option<$value_ty>);
|
||||
|
||||
impl $name {
|
||||
pub fn new() -> Self {
|
||||
Self (None)
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Option<&$value_ty> {
|
||||
self.0.as_ref()
|
||||
}
|
||||
|
||||
pub fn set(&mut self, value: $value_ty) {
|
||||
self.0 = Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
impl $crate::net::socket::SocketOption for $name {
|
||||
fn as_any(&self) -> &dyn core::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn core::any::Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for $name {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_sock_option_ref {
|
||||
(
|
||||
$option:expr, {
|
||||
$( $bind: ident : $ty:ty => $arm:expr ),*,
|
||||
_ => $default:expr
|
||||
}
|
||||
) => {{
|
||||
let __option : &dyn SocketOption = $option;
|
||||
$(
|
||||
if let Some($bind) = __option.as_any().downcast_ref::<$ty>() {
|
||||
$arm
|
||||
} else
|
||||
)*
|
||||
{
|
||||
$default
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_sock_option_mut {
|
||||
(
|
||||
$option:expr, {
|
||||
$( $bind: ident : $ty:ty => $arm:expr ),*,
|
||||
_ => $default:expr
|
||||
}
|
||||
) => {{
|
||||
let __option : &mut dyn SocketOption = $option;
|
||||
$(
|
||||
if let Some($bind) = __option.as_any_mut().downcast_mut::<$ty>() {
|
||||
$arm
|
||||
} else
|
||||
)*
|
||||
{
|
||||
$default
|
||||
}
|
||||
}};
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user