diff --git a/src/elf/src/info.rs b/src/elf/src/info.rs index daa4ea2a7..1f9e04893 100644 --- a/src/elf/src/info.rs +++ b/src/elf/src/info.rs @@ -7,8 +7,8 @@ use thiserror::Error; /// An object that is initialized by `acquire_per_file_info_obj`. #[derive(Debug)] pub struct FileInfo { - data: Vec, - comment: Vec, + data: Box<[u8]>, + comment: Box<[u8]>, dynoff: usize, dynsize: usize, pltrelsz: usize, @@ -25,8 +25,8 @@ pub struct FileInfo { impl FileInfo { pub(super) fn parse( - data: Vec, - comment: Vec, + data: Box<[u8]>, + comment: Box<[u8]>, dynoff: usize, dynsize: usize, ) -> Result { diff --git a/src/elf/src/lib.rs b/src/elf/src/lib.rs index 675afc903..4011ad4e2 100644 --- a/src/elf/src/lib.rs +++ b/src/elf/src/lib.rs @@ -270,7 +270,12 @@ impl Elf { }; // Load info. - elf.info = match FileInfo::parse(dyndata, comment, dynoff, dynsize) { + elf.info = match FileInfo::parse( + dyndata.into_boxed_slice(), + comment.into_boxed_slice(), + dynoff, + dynsize, + ) { Ok(v) => Some(v), Err(e) => return Err(OpenError::ParseFileInfoFailed(e)), }; diff --git a/src/kernel/src/budget/mod.rs b/src/kernel/src/budget/mod.rs index c313d2fab..92d74d2c1 100644 --- a/src/kernel/src/budget/mod.rs +++ b/src/kernel/src/budget/mod.rs @@ -73,6 +73,7 @@ impl Budget { #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[allow(dead_code)] pub enum ProcType { BigApp, MiniApp, diff --git a/src/kernel/src/fs/dev/console.rs b/src/kernel/src/fs/dev/console.rs index 1eb5fe79b..788667bfb 100644 --- a/src/kernel/src/fs/dev/console.rs +++ b/src/kernel/src/fs/dev/console.rs @@ -1,23 +1,26 @@ -use crate::errno::Errno; +use crate::errno::{Errno, ENXIO}; use crate::fs::{IoctlCom, VFile, VFileOps, VPath}; -use crate::process::VThread; +use crate::process::{VProc, VThread}; +use crate::tty::Tty; use crate::ucred::Ucred; use macros::vpath; use std::fmt::{Display, Formatter}; use std::io::{self, Write}; +use std::num::NonZeroI32; +use std::sync::Arc; +use thiserror::Error; /// An implementation of `/dev/console`. #[derive(Debug)] -pub struct Console {} +pub struct Console { + tty: Arc, +} impl Console { pub const PATH: &VPath = vpath!("/dev/console"); - #[allow(dead_code)] - pub const TIOCSCTTY: IoctlCom = IoctlCom::io(b't', 97); - pub fn new() -> Self { - Self {} + Self { tty: Tty::new() } } } @@ -41,12 +44,19 @@ impl VFileOps for Console { fn ioctl( &self, _file: &VFile, - _com: IoctlCom, - _data: &mut [u8], + com: IoctlCom, + data: &mut [u8], _cred: &Ucred, - _td: &VThread, + td: &VThread, ) -> Result<(), Box> { - // TODO: Implement this. + if self.tty.is_gone() || !self.tty.is_open() { + return Err(Box::new(IoctlError::TtyNotAvailable)); + } + + //TODO: implement tty_wait_background and the rest of the checks here. + + self.tty.ioctl(com, data, td)?; + Ok(()) } } @@ -55,3 +65,17 @@ impl Display for Console { Self::PATH.fmt(f) } } + +#[derive(Debug, Error)] +enum IoctlError { + #[error("tty is not available")] + TtyNotAvailable, +} + +impl Errno for IoctlError { + fn errno(&self) -> NonZeroI32 { + match self { + Self::TtyNotAvailable => ENXIO, + } + } +} diff --git a/src/kernel/src/fs/dev/dmem1.rs b/src/kernel/src/fs/dev/dmem1.rs index efdc6149a..bd202725e 100644 --- a/src/kernel/src/fs/dev/dmem1.rs +++ b/src/kernel/src/fs/dev/dmem1.rs @@ -10,7 +10,6 @@ use thiserror::Error; #[derive(Debug)] pub struct Dmem1 { - vp: Arc, total_size: usize, number: usize, } @@ -22,9 +21,8 @@ impl Dmem1 { pub const COM10: IoctlCom = IoctlCom::ior::(Self::DMEM_GRP, 0xa); - pub fn new(vp: &Arc) -> Self { + pub fn new() -> Self { Self { - vp: vp.clone(), total_size: 0x13C_000_000, // TODO figure out the real value number: 1, } @@ -42,13 +40,13 @@ impl VFileOps for Dmem1 { com: IoctlCom, data: &mut [u8], cred: &Ucred, - _: &VThread, + td: &VThread, ) -> Result<(), Box> { if cred.is_unk1() || cred.is_unk2() { return Err(Box::new(IoctlErr::BadCredentials)); } - if self.number != 2 && self.number != self.vp.dmem_container() && !cred.is_system() { + if self.number != 2 && self.number != td.proc().dmem_container() && !cred.is_system() { return Err(Box::new(IoctlErr::BadCredentials)); } diff --git a/src/kernel/src/fs/dev/mod.rs b/src/kernel/src/fs/dev/mod.rs index 830fe8945..47365e39a 100644 --- a/src/kernel/src/fs/dev/mod.rs +++ b/src/kernel/src/fs/dev/mod.rs @@ -2,9 +2,10 @@ pub use self::cdev::*; use self::dirent::Dirent; use self::vnode::{CHARACTER_OPS, VNODE_OPS}; use super::{path_contains, DirentType, FsOps, Mount, MountFlags, Vnode, VnodeType}; -use crate::errno::{Errno, EEXIST, ENOENT, EOPNOTSUPP}; +use crate::errno::{Errno, EEXIST, EINVAL, ENAMETOOLONG, EOPNOTSUPP}; use crate::ucred::Ucred; use bitflags::bitflags; +use cdev::{Cdev, CdevSw, DeviceFlags, DriverFlags}; use std::any::Any; use std::collections::HashMap; use std::num::NonZeroI32; @@ -31,23 +32,18 @@ pub fn make_dev>( gid: i32, mode: u16, cred: Option>, - flags: MakeDev, + flags: MakeDevFlags, ) -> Result, MakeDevError> { if sw.flags().intersects(DriverFlags::D_NEEDMINOR) { todo!("make_dev_credv with D_NEEDMINOR"); } - // TODO: Implement prep_devname. - let name = name.into(); - - if dev_exists(&name) { - return Err(MakeDevError::AlreadyExist(name)); - } + let name = prepare_name(name.into())?; // Get device flags. let mut df = DeviceFlags::empty(); - if flags.intersects(MakeDev::MAKEDEV_ETERNAL) { + if flags.intersects(MakeDevFlags::MAKEDEV_ETERNAL) { df |= DeviceFlags::SI_ETERNAL; } @@ -70,6 +66,34 @@ pub fn make_dev>( Ok(dev) } +/// See `prep_devname` on the PS4 for a reference. +fn prepare_name(name: String) -> Result { + if name.len() > 63 { + return Err(MakeDevError::NameTooLong); + } + + let name = name.trim_start_matches('/').to_string(); + + //TODO: Deduplicate consecutive slashes. + + if name.is_empty() { + return Err(MakeDevError::NameInvalid); + } + + for char in name.split('/') { + match char { + "." | ".." => return Err(MakeDevError::NameInvalid), + _ => {} + } + } + + if dev_exists(&name) { + return Err(MakeDevError::AlreadyExist(name)); + } + + Ok(name) +} + /// See `devfs_dev_exists` on the PS4 for a reference. pub fn dev_exists>(name: N) -> bool { let name = name.as_ref(); @@ -198,8 +222,7 @@ impl DevFs { if children .iter() - .find(|&c| c.ty() == DirentType::Link && c.name() == name) - .is_some() + .any(|c| c.ty() == DirentType::Link && c.name() == name) { todo!("devfs_populate with DT_LNK children"); } @@ -292,7 +315,7 @@ impl DevFs { bitflags! { /// Flags for [`make_dev()`]. #[derive(Clone, Copy)] - pub struct MakeDev: u32 { + pub struct MakeDevFlags: u32 { const MAKEDEV_ETERNAL = 0x10; } } @@ -315,12 +338,18 @@ impl Devices { pub enum MakeDevError { #[error("the device with the same name already exist")] AlreadyExist(String), + #[error("the device name is too long")] + NameTooLong, + #[error("the device name is invalid")] + NameInvalid, } impl Errno for MakeDevError { fn errno(&self) -> NonZeroI32 { match self { Self::AlreadyExist(_) => EEXIST, + Self::NameTooLong => ENAMETOOLONG, + Self::NameInvalid => EINVAL, } } } diff --git a/src/kernel/src/fs/item.rs b/src/kernel/src/fs/item.rs index c8c1294e8..b1507547b 100644 --- a/src/kernel/src/fs/item.rs +++ b/src/kernel/src/fs/item.rs @@ -45,11 +45,11 @@ impl FsItem { } } - pub fn open(&self, vp: &Arc) -> Result, FsError> { + pub fn open(&self) -> Result, FsError> { match self { Self::Directory(_) => todo!("VFileOps for host directory"), Self::File(_) => todo!("VFileOps for host file"), - Self::Device(d) => d.open(vp), + Self::Device(d) => d.open(), } } } @@ -105,13 +105,13 @@ pub enum VDev { } impl VDev { - pub fn open(&self, vp: &Arc) -> Result, FsError> { + pub fn open(&self) -> Result, FsError> { let ops: Box = match self { Self::Console => Box::new(Console::new()), Self::Dipsw => Box::new(Dipsw::new()), Self::DeciTty6 => Box::new(DeciTty6::new()), Self::Dmem0 => Box::new(Dmem0::new()), - Self::Dmem1 => Box::new(Dmem1::new(vp)), + Self::Dmem1 => Box::new(Dmem1::new()), Self::Dmem2 => Box::new(Dmem2::new()), }; diff --git a/src/kernel/src/fs/mod.rs b/src/kernel/src/fs/mod.rs index f0cb2510d..139a7bf5c 100644 --- a/src/kernel/src/fs/mod.rs +++ b/src/kernel/src/fs/mod.rs @@ -312,7 +312,10 @@ impl Fs { }; *file.flags_mut() = flags.to_fflags(); - file.set_ops(Some(self.namei(&mut nd)?.open(td.proc())?)); + + let item = self.namei(&mut nd)?; + let ops = item.open()?; + file.set_ops(Some(ops)); // Install to descriptor table. let fd = td.proc().files().alloc(Arc::new(file)); diff --git a/src/kernel/src/fs/vnode.rs b/src/kernel/src/fs/vnode.rs index f0801d054..4002e26aa 100644 --- a/src/kernel/src/fs/vnode.rs +++ b/src/kernel/src/fs/vnode.rs @@ -10,6 +10,7 @@ use thiserror::Error; /// An implementation of `vnode`. #[derive(Debug)] +#[allow(dead_code)] pub struct Vnode { fs: Arc, // v_mount ty: VnodeType, // v_type diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index 494b0cf93..720df6912 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -43,6 +43,7 @@ mod rtld; mod signal; mod syscalls; mod sysctl; +mod tty; mod ucred; fn main() -> ExitCode { diff --git a/src/kernel/src/process/group.rs b/src/kernel/src/process/group.rs index 1d49626d9..05328e43b 100644 --- a/src/kernel/src/process/group.rs +++ b/src/kernel/src/process/group.rs @@ -1,19 +1,28 @@ -use super::VSession; -use std::num::NonZeroI32; +use super::{VProc, VSession}; +use std::{num::NonZeroI32, sync::Arc}; /// An implementation of `pgrp` struct. #[derive(Debug)] pub struct VProcGroup { - id: NonZeroI32, // pg_id - session: VSession, // pg_session + id: NonZeroI32, // pg_id + session: Option>, // pg_session + leader: Arc, // pg_leader } impl VProcGroup { - pub fn new(id: NonZeroI32, session: VSession) -> Self { - Self { id, session } + pub fn new(id: NonZeroI32, session: VSession, leader: &Arc) -> Self { + Self { + id, + session: Some(Arc::new(session)), + leader: leader.clone(), + } } - pub fn session_mut(&mut self) -> &mut VSession { - &mut self.session + pub fn session(&self) -> Option<&Arc> { + self.session.as_ref() + } + + pub fn leader(&self) -> &Arc { + &self.leader } } diff --git a/src/kernel/src/process/mod.rs b/src/kernel/src/process/mod.rs index f1207f87c..147257a98 100644 --- a/src/kernel/src/process/mod.rs +++ b/src/kernel/src/process/mod.rs @@ -17,7 +17,8 @@ use crate::signal::{ }; use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; use crate::ucred::{AuthInfo, Privilege, Ucred}; -use gmtx::{Gutex, GutexGroup, GutexWriteGuard}; +use bitflags::bitflags; +use gmtx::{Gutex, GutexGroup, GutexReadGuard, GutexWriteGuard}; use std::any::Any; use std::cmp::min; use std::ffi::c_char; @@ -48,12 +49,13 @@ pub struct VProc { id: NonZeroI32, // p_pid threads: Gutex>>, // p_threads cred: Ucred, // p_ucred - group: Gutex>, // p_pgrp + group: Gutex>>, // p_pgrp sigacts: Gutex, // p_sigacts files: FileDesc, // p_fd system_path: String, // p_randomized_path limits: [ResourceLimit; ResourceLimit::NLIMITS], // p_limit comm: Gutex>, // p_comm + flags: Gutex, // p_flag objects: Gutex>>, budget_id: usize, budget_ptype: ProcType, @@ -97,6 +99,7 @@ impl VProc { dmem_container, limits, comm: gg.spawn(None), //TODO: Find out how this is actually set + flags: gg.spawn(VProcFlags::empty()), app_info: AppInfo::new(), ptc: 0, uptc: AtomicPtr::new(null_mut()), @@ -128,6 +131,10 @@ impl VProc { &self.cred } + pub fn group(&self) -> GutexReadGuard<'_, Option>> { + self.group.read() + } + pub fn files(&self) -> &FileDesc { &self.files } @@ -136,6 +143,10 @@ impl VProc { self.limits.get(ty) } + pub fn flags_mut(&self) -> GutexWriteGuard<'_, VProcFlags> { + self.flags.write() + } + pub fn set_name(&self, name: Option<&str>) { *self.comm.write() = name.map(|n| n.to_owned()); } @@ -202,9 +213,11 @@ impl VProc { // Set login name. let mut group = self.group.write(); - let session = group.as_mut().unwrap().session_mut(); + let session = group.as_ref().and_then(|grp| grp.session()).unwrap(); - session.set_login(login); + let mut slogin = session.login_mut(); + + *slogin = login.to_owned(); info!("Login name was changed to '{login}'."); @@ -225,7 +238,7 @@ impl VProc { // TODO: Find out the correct login name for VSession. let session = VSession::new(self.id, String::from("root")); - *group = Some(VProcGroup::new(self.id, session)); + *group = Some(Arc::new(VProcGroup::new(self.id, session, &self))); info!("Virtual process now set as group leader."); Ok(self.id.into()) @@ -666,3 +679,10 @@ pub enum VProcError { } static NEXT_ID: AtomicI32 = AtomicI32::new(1); + +bitflags! { + #[derive(Debug)] + pub struct VProcFlags: i32 { + const P_CONTROLT = 0x00002; + } +} diff --git a/src/kernel/src/process/session.rs b/src/kernel/src/process/session.rs index 63cceb670..e7f9b7e6d 100644 --- a/src/kernel/src/process/session.rs +++ b/src/kernel/src/process/session.rs @@ -1,18 +1,41 @@ -use std::num::NonZeroI32; +use crate::{fs::Vnode, tty::Tty}; +use gmtx::*; +use std::{num::NonZeroI32, sync::Arc}; /// An implementation of `session` structure. #[derive(Debug)] pub struct VSession { - id: NonZeroI32, // s_sid - login: String, // s_login + id: NonZeroI32, // s_sid + login: Gutex, // s_login + vnode: Option, // s_ttyvp + tty: Gutex>>, // s_ttyp } impl VSession { pub fn new(id: NonZeroI32, login: String) -> Self { - Self { id, login } + let gg = GutexGroup::new(); + + Self { + id, + login: gg.spawn(login), + vnode: None, // TODO figure out how this is actually set + tty: gg.spawn(None), + } + } + + pub fn login_mut(&self) -> GutexWriteGuard<'_, String> { + self.login.write() + } + + pub fn vnode(&self) -> Option<&Vnode> { + self.vnode.as_ref() + } + + pub fn tty(&self) -> GutexReadGuard<'_, Option>> { + self.tty.read() } - pub fn set_login>(&mut self, v: V) { - self.login = v.into(); + pub fn tty_mut(&self) -> GutexWriteGuard<'_, Option>> { + self.tty.write() } } diff --git a/src/kernel/src/rtld/mod.rs b/src/kernel/src/rtld/mod.rs index 5a909a001..2a0d4cdbc 100644 --- a/src/kernel/src/rtld/mod.rs +++ b/src/kernel/src/rtld/mod.rs @@ -271,7 +271,7 @@ impl RuntimeLinker { // Search for TLS free slot. let names = vec![name]; let tls = elf.tls().map(|i| &elf.programs()[i]); - let tls = if tls.map(|p| p.memory_size()).unwrap_or(0) == 0 { + let tls = if tls.map_or(0, |p| p.memory_size()) == 0 { 0 } else { let mut alloc = self.tls.write(); @@ -1006,8 +1006,8 @@ impl RuntimeLinker { // Initialization and finalization functions. if !md.flags().contains(ModuleFlags::UNK5) { - info.init = md.init().map(|v| addr + v).unwrap_or(0); - info.fini = md.fini().map(|v| addr + v).unwrap_or(0); + info.init = md.init().map_or(0, |v| addr + v); + info.fini = md.fini().map_or(0, |v| addr + v); } // Exception handling. diff --git a/src/kernel/src/sysctl/mod.rs b/src/kernel/src/sysctl/mod.rs index e7a10c8f8..572efaf00 100644 --- a/src/kernel/src/sysctl/mod.rs +++ b/src/kernel/src/sysctl/mod.rs @@ -258,7 +258,7 @@ impl Sysctl { req: &mut SysctlReq, ) -> Result<(), SysErr> { // Check input size. - let newlen = req.new.as_ref().map(|b| b.len()).unwrap_or(0); + let newlen = req.new.as_ref().map_or(0, |b| b.len()); if newlen == 0 { return Err(SysErr::Raw(ENOENT)); @@ -339,7 +339,7 @@ impl Sysctl { req: &mut SysctlReq, ) -> Result<(), SysErr> { // Check the buffer. - let oldlen = req.old.as_ref().map(|b| b.len()).unwrap_or(0); + let oldlen = req.old.as_ref().map_or(0, |b| b.len()); if oldlen >= 73 { return Err(SysErr::Raw(EINVAL)); @@ -427,7 +427,7 @@ impl Sysctl { req: &mut SysctlReq, ) -> Result<(), SysErr> { let mut buf = [0; 256]; - let len = min(req.old.as_ref().map(|b| b.len()).unwrap_or(0), 256); + let len = min(req.old.as_ref().map_or(0, |b| b.len()), 256); self.arnd.rand_bytes(&mut buf[..len]); diff --git a/src/kernel/src/tty/mod.rs b/src/kernel/src/tty/mod.rs new file mode 100644 index 000000000..8d4477d76 --- /dev/null +++ b/src/kernel/src/tty/mod.rs @@ -0,0 +1,124 @@ +use crate::errno::{Errno, EPERM}; +use crate::fs::IoctlCom; +use crate::info; +use crate::process::{VProc, VProcFlags, VProcGroup, VSession, VThread}; +use bitflags::bitflags; +use gmtx::*; +use std::num::NonZeroI32; +use std::sync::Arc; +use thiserror::Error; + +#[derive(Debug)] +/// An implementation of `tty` structure. +pub struct Tty { + group: Gutex>>, //t_pgrp + session: Gutex>>, //t_session + session_count: Gutex, //t_sessioncnt + flags: TtyFlags, //t_flags +} + +impl Tty { + pub const TTY_GRP: u8 = b't'; // 0x74 + + pub const TIOCSCTTY: IoctlCom = IoctlCom::io(Self::TTY_GRP, 97); + + pub fn new() -> Arc { + let gg = GutexGroup::new(); + + Arc::new(Self { + group: gg.spawn(None), + session: gg.spawn(None), + session_count: gg.spawn(0), + flags: TtyFlags::TF_OPENED_CONS, // TODO: figure out the actual value + }) + } + + pub fn is_gone(&self) -> bool { + self.flags.intersects(TtyFlags::TF_GONE) + } + + pub fn is_open(&self) -> bool { + self.flags.intersects(TtyFlags::TF_OPENED) + } + + /// See `tty_generic_ioctl` on the PS4 for reference. + pub fn ioctl( + self: &Arc, + com: IoctlCom, + _data: &mut [u8], + td: &VThread, + ) -> Result<(), Box> { + match com { + Self::TIOCSCTTY => { + info!("Setting tty as controlling tty"); + + let grp_guard = td.proc().group(); + let proc_grp = grp_guard.as_ref().unwrap(); + + if !Arc::ptr_eq(td.proc(), proc_grp.leader()) { + return Err(Box::new(TtyError::NotSessionLeader)); + } + + match (self.session.read().as_ref(), proc_grp.session()) { + (Some(tsess), Some(gsess)) if Arc::ptr_eq(tsess, gsess) => { + //already the controlling tty + return Ok(()); + } + _ => {} + } + + if proc_grp.session().is_some_and(|s| s.tty().is_some()) || { + let sess = self.session.read(); + + sess.as_ref().is_some_and(|sess| sess.vnode().is_some()) + } { + return Err(Box::new(TtyError::BadState)); + } + + let sess = proc_grp.session().unwrap(); + + *sess.tty_mut() = Some(self.clone()); + *self.session.write() = Some(sess.clone()); + + let mut cnt = self.session_count.write(); + + *cnt += 1; + + self.group.write().replace(proc_grp.clone()); + + td.proc().flags_mut().insert(VProcFlags::P_CONTROLT); + } + _ => todo!("ioctl com {:?} is not implemented", com), + } + + Ok(()) + } +} + +bitflags! { + #[derive(Debug)] + struct TtyFlags: u32 { + const TF_OPENED_IN = 0x00008; + const TF_OPENED_OUT = 0x00010; + const TF_OPENED_CONS = 0x00020; + const TF_OPENED = Self::TF_OPENED_IN.bits() | Self::TF_OPENED_OUT.bits() | Self::TF_OPENED_CONS.bits(); + const TF_GONE = 0x00040; + } +} + +#[derive(Debug, Error)] +enum TtyError { + #[error("not session leader")] + NotSessionLeader, + #[error("bad tty state")] + BadState, +} + +impl Errno for TtyError { + fn errno(&self) -> NonZeroI32 { + match self { + Self::NotSessionLeader => EPERM, + Self::BadState => EPERM, + } + } +} diff --git a/src/kernel/src/ucred/auth.rs b/src/kernel/src/ucred/auth.rs index 503725f00..fd61bb399 100644 --- a/src/kernel/src/ucred/auth.rs +++ b/src/kernel/src/ucred/auth.rs @@ -82,6 +82,7 @@ impl AuthCaps { (self.0[0] & 0x1000000000000000) != 0 } + #[allow(dead_code)] pub fn is_user(&self) -> bool { (self.0[0] & 0x2000000000000000) != 0 }