Rename aster-std to aster-nix

This commit is contained in:
Jianfeng Jiang
2024-02-05 07:58:10 +00:00
committed by Tate, Hongliang Tian
parent 8d456ebe8f
commit ab03ef0fe8
312 changed files with 60 additions and 60 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
mod fs;
mod inode;

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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