diff --git a/Makefile b/Makefile index d0d3afa..bbfd889 100644 --- a/Makefile +++ b/Makefile @@ -95,6 +95,7 @@ initrd: mkdir -p $(O)/rootfs/bin cp target/$(ARCH)-osdev5/$(PROFILE)/init $(O)/rootfs/init cp target/$(ARCH)-osdev5/$(PROFILE)/shell $(O)/rootfs/bin + cp target/$(ARCH)-osdev5/$(PROFILE)/fuzzy $(O)/rootfs/bin cd $(O)/rootfs && tar cf ../initrd.img `find -type f -printf "%P\n"` ifeq ($(MACH),orangepi3) $(MKIMAGE) \ diff --git a/fs/memfs/src/lib.rs b/fs/memfs/src/lib.rs index 2e4a1b5..04be460 100644 --- a/fs/memfs/src/lib.rs +++ b/fs/memfs/src/lib.rs @@ -29,7 +29,7 @@ pub use block::{BlockAllocator, BlockRef}; mod bvec; use bvec::Bvec; mod tar; -use tar::TarIterator; +use tar::{TarIterator, Tar}; mod file; use file::FileInode; mod dir; @@ -67,8 +67,10 @@ impl Ramfs { Ok(res) } - fn create_node_initial(self: Rc, name: &str, kind: VnodeKind) -> VnodeRef { + fn create_node_initial(self: Rc, name: &str, tar: &Tar) -> VnodeRef { + let kind = tar.node_kind(); let node = Vnode::new(name, kind, Vnode::SEEKABLE); + node.props_mut().mode = tar.mode(); node.set_fs(self.clone()); match kind { VnodeKind::Directory => node.set_data(Box::new(DirInode::new(self.alloc))), @@ -111,7 +113,10 @@ impl Ramfs { } unsafe fn load_tar(self: Rc, base: *const u8, size: usize) -> Result { - let root = self.clone().create_node_initial("", VnodeKind::Directory); + let root = Vnode::new("", VnodeKind::Directory, Vnode::SEEKABLE); + root.set_fs(self.clone()); + root.set_data(Box::new(DirInode::new(self.alloc))); + root.props_mut().mode = FileMode::default_dir(); // 1. Create all the paths in TAR for block in TarIterator::new(base, base.add(size)) { @@ -120,7 +125,7 @@ impl Ramfs { let parent = self.clone().make_path(root.clone(), dirname, true)?; let node = self .clone() - .create_node_initial(basename, block.node_kind()); + .create_node_initial(basename, block); assert_eq!(node.kind(), block.node_kind()); parent.attach(node); } diff --git a/fs/memfs/src/tar.rs b/fs/memfs/src/tar.rs index 4f4984c..2211f65 100644 --- a/fs/memfs/src/tar.rs +++ b/fs/memfs/src/tar.rs @@ -1,4 +1,4 @@ -use libsys::error::Errno; +use libsys::{error::Errno, stat::FileMode}; use vfs::VnodeKind; #[repr(packed)] @@ -81,6 +81,10 @@ impl Tar { } } + pub fn mode(&self) -> FileMode { + FileMode::from_bits(from_octal(&self.mode) as u32).unwrap() + } + pub fn data(&self) -> &[u8] { unsafe { core::slice::from_raw_parts( diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index ad86b2f..8ff2185 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -1,11 +1,11 @@ -use crate::{File, FileRef, Filesystem}; +use crate::{Ioctx, File, FileRef, Filesystem}; use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec}; use core::cell::{RefCell, RefMut}; use core::fmt; use libsys::{ error::Errno, ioctl::IoctlCmd, - stat::{FileMode, OpenFlags, Stat}, + stat::{AccessMode, FileMode, OpenFlags, Stat}, }; /// Convenience type alias for [Rc] @@ -31,7 +31,7 @@ pub(crate) struct TreeNode { /// File property cache struct pub struct VnodeProps { - mode: FileMode, + pub mode: FileMode, } /// Virtual filesystem node struct, generalizes access to @@ -122,6 +122,11 @@ impl Vnode { &self.name } + /// Returns a borrowed reference to cached file properties + pub fn props_mut(&self) -> RefMut { + self.props.borrow_mut() + } + /// Sets an associated [VnodeImpl] for the [Vnode] pub fn set_data(&self, data: Box) { *self.data.borrow_mut() = Some(data); @@ -408,6 +413,38 @@ impl Vnode { Err(Errno::NotImplemented) } } + + pub fn check_access(&self, ioctx: &Ioctx, access: AccessMode) -> Result<(), Errno> { + let props = self.props.borrow(); + let mode = props.mode; + + if access.contains(AccessMode::F_OK) { + if access.intersects(AccessMode::R_OK | AccessMode::W_OK | AccessMode::X_OK) { + return Err(Errno::InvalidArgument); + } + return Ok(()); + } else { + if access.contains(AccessMode::F_OK) { + return Err(Errno::InvalidArgument); + } + + // Check user + if access.contains(AccessMode::R_OK) && !mode.contains(FileMode::USER_READ) { + return Err(Errno::PermissionDenied); + } + if access.contains(AccessMode::W_OK) && !mode.contains(FileMode::USER_WRITE) { + return Err(Errno::PermissionDenied); + } + if access.contains(AccessMode::X_OK) && !mode.contains(FileMode::USER_EXEC) { + return Err(Errno::PermissionDenied); + } + + // TODO check group + // TODO check other + + return Ok(()); + } + } } impl fmt::Debug for Vnode { diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 27fac7a..b24f58f 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -243,7 +243,9 @@ impl Process { if lock.threads.len() == 1 { // TODO call Process::exit instead? - todo!(); + drop(lock); + Process::exit(ExitCode::from(0)); + panic!(); } lock.threads.retain(|&e| e != tid); diff --git a/kernel/src/proc/thread.rs b/kernel/src/proc/thread.rs index 2c7d786..d848c19 100644 --- a/kernel/src/proc/thread.rs +++ b/kernel/src/proc/thread.rs @@ -283,7 +283,7 @@ impl Thread { } /// Switches process main thread to a signal handler - pub fn enter_signal(&self, signal: Signal, ttbr0: usize) { + pub fn enter_signal(self: ThreadRef, signal: Signal, ttbr0: usize) { if self .signal_pending .compare_exchange_weak(0, signal as u32, Ordering::SeqCst, Ordering::Relaxed) @@ -294,7 +294,9 @@ impl Thread { let mut lock = self.inner.lock(); if lock.signal_entry == 0 || lock.signal_stack == 0 { - todo!(); + drop(lock); + Process::exit_thread(self); + panic!(); } let signal_ctx = unsafe { &mut *self.signal_ctx.get() }; diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 9a5f701..b870833 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -12,7 +12,7 @@ use libsys::{ ioctl::IoctlCmd, proc::Pid, signal::{Signal, SignalDestination}, - stat::{FdSet, FileDescriptor, FileMode, OpenFlags, Stat, AT_EMPTY_PATH}, + stat::{FdSet, AccessMode, FileDescriptor, FileMode, OpenFlags, Stat, AT_EMPTY_PATH}, traits::{Read, Write}, }; use vfs::VnodeRef; @@ -233,6 +233,18 @@ pub fn syscall(num: usize, args: &[usize]) -> Result { wait::select(Thread::current(), rfds, wfds, timeout) } + abi::SYS_FACCESSAT => { + let at_fd = FileDescriptor::from_i32(args[0] as i32)?; + let path = validate_user_str(args[1], args[2])?; + let mode = AccessMode::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?; + let flags = args[4] as u32; + + let proc = Process::current(); + let mut io = proc.io.lock(); + + find_at_node(&mut io, at_fd, path, flags & AT_EMPTY_PATH != 0)?.check_access(io.ioctx(), mode)?; + Ok(0) + }, _ => { let thread = Thread::current(); diff --git a/libsys/src/abi.rs b/libsys/src/abi.rs index e5183b8..16ef3d7 100644 --- a/libsys/src/abi.rs +++ b/libsys/src/abi.rs @@ -20,3 +20,4 @@ pub const SYS_EXECVE: usize = 8; pub const SYS_WAITPID: usize = 9; pub const SYS_IOCTL: usize = 10; pub const SYS_SELECT: usize = 11; +pub const SYS_FACCESSAT: usize = 12; diff --git a/libsys/src/calls.rs b/libsys/src/calls.rs index 96de914..af430f6 100644 --- a/libsys/src/calls.rs +++ b/libsys/src/calls.rs @@ -4,7 +4,7 @@ use crate::{ ioctl::IoctlCmd, proc::{ExitCode, Pid}, signal::{Signal, SignalDestination}, - stat::{FdSet, FileDescriptor, FileMode, OpenFlags, Stat}, + stat::{AccessMode, FdSet, FileDescriptor, FileMode, OpenFlags, Stat}, }; // TODO document the syscall ABI @@ -325,3 +325,22 @@ pub fn sys_select( ) }) } + +#[inline(always)] +pub fn sys_faccessat( + fd: Option, + name: &str, + mode: AccessMode, + flags: u32, +) -> Result<(), Errno> { + Errno::from_syscall_unit(unsafe { + syscall!( + abi::SYS_FACCESSAT, + argn!(FileDescriptor::into_i32(fd)), + argp!(name.as_ptr()), + argn!(name.len()), + argn!(mode.bits()), + argn!(flags) + ) + }) +} diff --git a/libsys/src/error.rs b/libsys/src/error.rs index 450358f..1788d38 100644 --- a/libsys/src/error.rs +++ b/libsys/src/error.rs @@ -15,6 +15,7 @@ pub enum Errno { NotADirectory, NotImplemented, OutOfMemory, + PermissionDenied, ReadOnly, TimedOut, TooManyDescriptors, diff --git a/libsys/src/stat.rs b/libsys/src/stat.rs index f1d0e73..a59be78 100644 --- a/libsys/src/stat.rs +++ b/libsys/src/stat.rs @@ -31,6 +31,15 @@ bitflags! { } } +bitflags! { + pub struct AccessMode: u32 { + const R_OK = 1 << 0; + const W_OK = 1 << 1; + const X_OK = 1 << 2; + const F_OK = 1 << 3; + } +} + #[derive(Clone, Default)] pub struct FdSet { bits: [u64; 2] diff --git a/libusr/src/sys/mod.rs b/libusr/src/sys/mod.rs index 749c809..09c2212 100644 --- a/libusr/src/sys/mod.rs +++ b/libusr/src/sys/mod.rs @@ -2,7 +2,8 @@ pub use libsys::signal::{Signal, SignalDestination}; pub use libsys::proc::ExitCode; pub use libsys::termios; pub use libsys::calls::*; -pub use libsys::stat::{self, FileDescriptor}; +pub use libsys::stat::{self, AccessMode, FileDescriptor}; +pub use libsys::error::Errno; use core::sync::atomic::{Ordering, AtomicBool}; diff --git a/user/Cargo.toml b/user/Cargo.toml index 9196443..8cb69ab 100644 --- a/user/Cargo.toml +++ b/user/Cargo.toml @@ -13,6 +13,10 @@ path = "src/init/main.rs" name = "shell" path = "src/shell/main.rs" +[[bin]] +name = "fuzzy" +path = "src/fuzzy/main.rs" + [dependencies] libusr = { path = "../libusr" } lazy_static = { version = "*", features = ["spin_no_std"] } diff --git a/user/src/fuzzy/main.rs b/user/src/fuzzy/main.rs new file mode 100644 index 0000000..974561f --- /dev/null +++ b/user/src/fuzzy/main.rs @@ -0,0 +1,10 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate libusr; + +#[no_mangle] +fn main() -> i32 { + 0 +} diff --git a/user/src/shell/main.rs b/user/src/shell/main.rs index 2c5f85f..4425bd5 100644 --- a/user/src/shell/main.rs +++ b/user/src/shell/main.rs @@ -3,34 +3,56 @@ #[macro_use] extern crate libusr; -#[macro_use] -extern crate lazy_static; +extern crate alloc; -use libusr::thread; +use alloc::borrow::ToOwned; +use libusr::sys::{sys_faccessat, sys_exit, sys_execve, sys_waitpid, sys_fork, ExitCode, Errno, AccessMode}; use libusr::io::{self, Read}; -use libusr::sys::{Signal, SignalDestination}; -use libusr::sync::Mutex; -fn sleep(ns: u64) { - let mut rem = [0; 2]; - libusr::sys::sys_ex_nanosleep(ns, &mut rem).unwrap(); +fn readline<'a, F: Read>(f: &mut F, bytes: &'a mut [u8]) -> Result, io::Error> { + let size = f.read(bytes)?; + Ok(if size == 0 { + None + } else { + Some(core::str::from_utf8(&bytes[..size]).unwrap().trim_end_matches('\n')) + }) +} + +fn execvp(cmd: &str) -> ! { + sys_execve(&("/bin/".to_owned() + cmd)); + sys_exit(ExitCode::from(-1)); +} + +fn execute(line: &str) -> Result { + let mut words = line.split(' '); + let cmd = words.next().unwrap(); + + if let Some(pid) = sys_fork()? { + let mut status = 0; + sys_waitpid(pid, &mut status)?; + Ok(ExitCode::from(status)) + } else { + execvp(cmd); + } } #[no_mangle] fn main() -> i32 { - let value = 1234; - let thread = thread::spawn(move || { - trace!("Closure is alive: {}", value); - sleep(2_000_000_000); - trace!("Closure will now exit"); + let mut buf = [0; 256]; + let mut stdin = io::stdin(); - value - 100 - }); - sleep(1_000_000_000); - - trace!("???"); - - trace!("Thread joined: {:?}", thread.join()); + loop { + print!("> "); + let line = readline(&mut stdin, &mut buf).unwrap(); + if line.is_none() { + break; + } + let line = line.unwrap().trim_start_matches(' '); + if line.is_empty() { + continue; + } + execute(line); + } 0 }