From 166fc19b7a1e08c451fd1e1766f047bd97729337 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Wed, 3 Nov 2021 17:35:51 +0200 Subject: [PATCH] feature(fs): O_CREAT option --- Cargo.lock | 3 ++ fs/memfs/src/dir.rs | 21 +++++--- fs/memfs/src/file.rs | 16 +++--- fs/memfs/src/lib.rs | 4 +- fs/vfs/src/char.rs | 4 +- fs/vfs/src/ioctx.rs | 55 ++++++++++++++++++--- fs/vfs/src/lib.rs | 4 +- fs/vfs/src/node.rs | 14 +++--- fs/vfs/src/stat.rs | 99 -------------------------------------- init/src/main.rs | 4 +- kernel/src/init.rs | 6 +-- kernel/src/proc/process.rs | 11 +++++ kernel/src/syscall.rs | 36 +++++++++----- libusr/src/lib.rs | 2 +- syscall/Cargo.toml | 1 + syscall/src/abi.rs | 3 +- syscall/src/calls.rs | 10 +++- syscall/src/lib.rs | 4 +- syscall/src/stat.rs | 43 +++++++++++++++++ 19 files changed, 182 insertions(+), 158 deletions(-) delete mode 100644 fs/vfs/src/stat.rs diff --git a/Cargo.lock b/Cargo.lock index be671b1..5ddad75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,6 +211,9 @@ dependencies = [ [[package]] name = "syscall" version = "0.1.0" +dependencies = [ + "bitflags", +] [[package]] name = "tock-registers" diff --git a/fs/memfs/src/dir.rs b/fs/memfs/src/dir.rs index a9a8510..b94ff4d 100644 --- a/fs/memfs/src/dir.rs +++ b/fs/memfs/src/dir.rs @@ -1,10 +1,13 @@ -use vfs::{VnodeImpl, VnodeKind, VnodeRef, Vnode, Stat}; +use crate::{BlockAllocator, Bvec, FileInode}; use alloc::boxed::Box; use error::Errno; +use vfs::{OpenFlags, Stat, Vnode, VnodeImpl, VnodeKind, VnodeRef}; -pub struct DirInode; +pub struct DirInode { + alloc: A, +} -impl VnodeImpl for DirInode { +impl VnodeImpl for DirInode { fn create( &mut self, _parent: VnodeRef, @@ -13,8 +16,9 @@ impl VnodeImpl for DirInode { ) -> Result { let vnode = Vnode::new(name, kind, Vnode::SEEKABLE); match kind { - VnodeKind::Directory => vnode.set_data(Box::new(DirInode {})), - _ => todo!() + VnodeKind::Directory => vnode.set_data(Box::new(DirInode { alloc: self.alloc })), + VnodeKind::Regular => vnode.set_data(Box::new(FileInode::new(Bvec::new(self.alloc)))), + _ => todo!(), } Ok(vnode) } @@ -27,7 +31,7 @@ impl VnodeImpl for DirInode { Ok(()) } - fn open(&mut self, _node: VnodeRef) -> Result { + fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result { todo!() } @@ -56,3 +60,8 @@ impl VnodeImpl for DirInode { } } +impl DirInode { + pub const fn new(alloc: A) -> Self { + Self { alloc } + } +} diff --git a/fs/memfs/src/file.rs b/fs/memfs/src/file.rs index b8b182e..b2d59b7 100644 --- a/fs/memfs/src/file.rs +++ b/fs/memfs/src/file.rs @@ -1,4 +1,4 @@ -use vfs::{VnodeImpl, VnodeKind, VnodeRef, Stat}; +use vfs::{VnodeImpl, VnodeKind, VnodeRef, Stat, OpenFlags}; use error::Errno; use crate::{BlockAllocator, Bvec}; @@ -6,12 +6,6 @@ pub struct FileInode<'a, A: BlockAllocator + Copy + 'static> { data: Bvec<'a, A>, } -impl<'a, A: BlockAllocator + Copy + 'static> FileInode<'a, A> { - pub fn new(data: Bvec<'a, A>) -> Self { - Self { data } - } -} - impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { fn create( &mut self, @@ -30,7 +24,7 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { panic!() } - fn open(&mut self, _node: VnodeRef) -> Result { + fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result { Ok(0) } @@ -61,3 +55,9 @@ impl<'a, A: BlockAllocator + Copy + 'static> VnodeImpl for FileInode<'a, A> { Ok(()) } } + +impl<'a, A: BlockAllocator + Copy + 'static> FileInode<'a, A> { + pub fn new(data: Bvec<'a, A>) -> Self { + Self { data } + } +} diff --git a/fs/memfs/src/lib.rs b/fs/memfs/src/lib.rs index 3f3388d..e4a3462 100644 --- a/fs/memfs/src/lib.rs +++ b/fs/memfs/src/lib.rs @@ -62,7 +62,7 @@ impl Ramfs { let node = Vnode::new(name, kind, Vnode::SEEKABLE); node.set_fs(self.clone()); match kind { - VnodeKind::Directory => node.set_data(Box::new(DirInode {})), + VnodeKind::Directory => node.set_data(Box::new(DirInode::new(self.alloc))), VnodeKind::Regular => {} VnodeKind::Char => todo!(), VnodeKind::Block => todo!(), @@ -90,7 +90,7 @@ impl Ramfs { return Err(Errno::DoesNotExist); } // TODO file modes - let node = at.mkdir(element, FileMode::default_dir())?; + let node = at.create(element, FileMode::default_dir(), VnodeKind::Directory)?; node } }; diff --git a/fs/vfs/src/char.rs b/fs/vfs/src/char.rs index af4e14e..a16b18e 100644 --- a/fs/vfs/src/char.rs +++ b/fs/vfs/src/char.rs @@ -1,4 +1,4 @@ -use crate::{VnodeImpl, VnodeKind, VnodeRef, Stat}; +use crate::{VnodeImpl, VnodeKind, VnodeRef, Stat, OpenFlags}; use error::Errno; /// Generic character device trait @@ -36,7 +36,7 @@ impl VnodeImpl for CharDeviceWrapper { panic!(); } - fn open(&mut self, _node: VnodeRef) -> Result { + fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result { Ok(0) } diff --git a/fs/vfs/src/ioctx.rs b/fs/vfs/src/ioctx.rs index f3d4e97..6a56f8d 100644 --- a/fs/vfs/src/ioctx.rs +++ b/fs/vfs/src/ioctx.rs @@ -1,4 +1,4 @@ -use crate::{FileMode, VnodeRef, VnodeKind}; +use crate::{FileMode, VnodeKind, VnodeRef, OpenFlags, File}; use error::Errno; use libcommon::{path_component_left, path_component_right}; @@ -83,7 +83,28 @@ impl Ioctx { mode: FileMode, ) -> Result { let (parent, name) = path_component_right(path); - self.find(at, parent, true)?.mkdir(name.trim_start_matches('/'), mode) + self.find(at, parent, true)? + .create(name.trim_start_matches('/'), mode, VnodeKind::Directory) + } + + /// + pub fn open( + &self, + at: Option, + path: &str, + mode: FileMode, + opts: OpenFlags, + ) -> Result { + let node = match self.find(at.clone(), path, true) { + Err(Errno::DoesNotExist) => { + let (parent, name) = path_component_right(path); + let at = self.find(at, parent, true)?; + at.create(name, mode, VnodeKind::Regular) + }, + o => o + }?; + + node.open(opts) } } @@ -271,14 +292,32 @@ mod tests { let ioctx = Ioctx::new(root_outer.clone()); - assert_eq!(ioctx.find(None, "/dir0/dir1", false).unwrap_err(), Errno::DoesNotExist); + assert_eq!( + ioctx.find(None, "/dir0/dir1", false).unwrap_err(), + Errno::DoesNotExist + ); dir0.mount(root_inner.clone()).unwrap(); - assert!(Rc::ptr_eq(&root_inner, &ioctx.find(None, "/dir0", false).unwrap())); - assert!(Rc::ptr_eq(&dir1, &ioctx.find(None, "/dir0/dir1", false).unwrap())); - assert!(Rc::ptr_eq(&root_inner, &ioctx.find(None, "/dir0/dir1/..", false).unwrap())); - assert!(Rc::ptr_eq(&dir0, &ioctx.find(None, "/dir0/dir1/../..", false).unwrap())); - assert!(Rc::ptr_eq(&root_outer, &ioctx.find(None, "/dir0/dir1/../../..", false).unwrap())); + assert!(Rc::ptr_eq( + &root_inner, + &ioctx.find(None, "/dir0", false).unwrap() + )); + assert!(Rc::ptr_eq( + &dir1, + &ioctx.find(None, "/dir0/dir1", false).unwrap() + )); + assert!(Rc::ptr_eq( + &root_inner, + &ioctx.find(None, "/dir0/dir1/..", false).unwrap() + )); + assert!(Rc::ptr_eq( + &dir0, + &ioctx.find(None, "/dir0/dir1/../..", false).unwrap() + )); + assert!(Rc::ptr_eq( + &root_outer, + &ioctx.find(None, "/dir0/dir1/../../..", false).unwrap() + )); } } diff --git a/fs/vfs/src/lib.rs b/fs/vfs/src/lib.rs index 407f937..0e55bf0 100644 --- a/fs/vfs/src/lib.rs +++ b/fs/vfs/src/lib.rs @@ -9,14 +9,12 @@ extern crate std; extern crate alloc; -pub use syscall::stat::Stat; +pub use syscall::stat::{Stat, OpenFlags, FileMode}; mod block; pub use block::BlockDevice; mod fs; pub use fs::Filesystem; -mod stat; -pub use stat::FileMode; mod node; pub use node::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; mod ioctx; diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index 27e9460..a7d56d8 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -1,4 +1,4 @@ -use crate::{File, FileMode, Filesystem, Stat}; +use crate::{File, FileMode, Filesystem, Stat, OpenFlags}; use alloc::{borrow::ToOwned, boxed::Box, rc::Rc, string::String, vec::Vec}; use core::cell::{RefCell, RefMut}; use core::fmt; @@ -58,7 +58,7 @@ pub trait VnodeImpl { fn lookup(&mut self, at: VnodeRef, name: &str) -> Result; /// Opens a vnode for access. Returns initial file position. - fn open(&mut self, node: VnodeRef /* TODO open mode */) -> Result; + fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result; /// Closes a vnode fn close(&mut self, node: VnodeRef) -> Result<(), Errno>; @@ -234,8 +234,8 @@ impl Vnode { } } - /// Creates a new directory `name` in `self` - pub fn mkdir(self: &VnodeRef, name: &str, mode: FileMode) -> Result { + /// Creates a new node `name` in `self` + pub fn create(self: &VnodeRef, name: &str, mode: FileMode, kind: VnodeKind) -> Result { if self.kind != VnodeKind::Directory { return Err(Errno::NotADirectory); } @@ -250,7 +250,7 @@ impl Vnode { }; if let Some(ref mut data) = *self.data() { - let vnode = data.create(self.clone(), name, VnodeKind::Directory)?; + let vnode = data.create(self.clone(), name, kind)?; if let Some(fs) = self.fs() { vnode.set_fs(fs); } @@ -282,13 +282,13 @@ impl Vnode { } /// Opens a vnode for access - pub fn open(self: &VnodeRef) -> Result { + pub fn open(self: &VnodeRef, flags: OpenFlags) -> Result { if self.kind == VnodeKind::Directory { return Err(Errno::IsADirectory); } if let Some(ref mut data) = *self.data() { - let pos = data.open(self.clone())?; + let pos = data.open(self.clone(), flags)?; Ok(File::normal(self.clone(), pos)) } else { Err(Errno::NotImplemented) diff --git a/fs/vfs/src/stat.rs b/fs/vfs/src/stat.rs deleted file mode 100644 index e02a92e..0000000 --- a/fs/vfs/src/stat.rs +++ /dev/null @@ -1,99 +0,0 @@ -use core::fmt; - -/// Wrapper type for file mode/permissions -#[derive(Clone, Copy, PartialEq)] -#[repr(transparent)] -pub struct FileMode(u16); - -impl FileMode { - /// File is user-readable - pub const USER_READ: u16 = 1 << 8; - /// File is user-writeable - pub const USER_WRITE: u16 = 1 << 7; - /// File is user-executable - pub const USER_EXEC: u16 = 1 << 6; - /// File is group-readable - pub const GROUP_READ: u16 = 1 << 5; - /// File is group-writeable - pub const GROUP_WRITE: u16 = 1 << 4; - /// File is group-executable - pub const GROUP_EXEC: u16 = 1 << 3; - /// File is readable by anyone - pub const OTHER_READ: u16 = 1 << 2; - /// File is writable by anyone - pub const OTHER_WRITE: u16 = 1 << 1; - /// File is executable by anyone - pub const OTHER_EXEC: u16 = 1 << 0; - - /// Returns an empty permission set - pub const fn empty() -> Self { - Self(0) - } - - /// Returns default permission set for directories - pub const fn default_dir() -> Self { - Self(0o755) - } - - /// Returns default permission set for regular files - pub const fn default_reg() -> Self { - Self(0o644) - } -} - -impl fmt::Debug for FileMode { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "FileMode (")?; - - if self.0 & Self::USER_READ != 0 { - write!(f, "r")?; - } else { - write!(f, "-")?; - } - if self.0 & Self::USER_WRITE != 0 { - write!(f, "w")?; - } else { - write!(f, "-")?; - } - if self.0 & Self::USER_EXEC != 0 { - write!(f, "x")?; - } else { - write!(f, "-")?; - } - - if self.0 & Self::GROUP_READ != 0 { - write!(f, "r")?; - } else { - write!(f, "-")?; - } - if self.0 & Self::GROUP_WRITE != 0 { - write!(f, "w")?; - } else { - write!(f, "-")?; - } - if self.0 & Self::GROUP_EXEC != 0 { - write!(f, "x")?; - } else { - write!(f, "-")?; - } - - if self.0 & Self::OTHER_READ != 0 { - write!(f, "r")?; - } else { - write!(f, "-")?; - } - if self.0 & Self::OTHER_WRITE != 0 { - write!(f, "w")?; - } else { - write!(f, "-")?; - } - if self.0 & Self::OTHER_EXEC != 0 { - write!(f, "x")?; - } else { - write!(f, "-")?; - } - - write!(f, ")")?; - Ok(()) - } -} diff --git a/init/src/main.rs b/init/src/main.rs index 502fbb0..6a51a6e 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -5,18 +5,16 @@ #[macro_use] extern crate libusr; +use libusr::sys::{OpenFlags, AT_FDCWD}; use libusr::io; #[no_mangle] fn main() -> i32 { let mut buf = [0; 128]; - let stat = io::stat("/test.txt").unwrap(); print!("\x1B[2J\x1B[1;1H"); println!("Hello!"); - println!("test.txt: {:?}", stat); - loop { print!("> "); diff --git a/kernel/src/init.rs b/kernel/src/init.rs index 323e00d..cef850e 100644 --- a/kernel/src/init.rs +++ b/kernel/src/init.rs @@ -3,7 +3,7 @@ use crate::fs::{devfs, MemfsBlockAlloc}; use crate::mem; use crate::proc::{elf, Process}; use memfs::Ramfs; -use vfs::{FileMode, Filesystem, Ioctx}; +use vfs::{FileMode, Filesystem, Ioctx, OpenFlags}; #[inline(never)] pub extern "C" fn init_fn(_arg: usize) -> ! { @@ -28,7 +28,7 @@ pub extern "C" fn init_fn(_arg: usize) -> ! { let ioctx = Ioctx::new(root); let node = ioctx.find(None, "/init", true).unwrap(); - let mut file = node.open().unwrap(); + let mut file = node.open(OpenFlags::O_RDONLY).unwrap(); proc.set_ioctx(ioctx); @@ -43,7 +43,7 @@ pub extern "C" fn init_fn(_arg: usize) -> ! { let mut io = proc.io.lock(); // TODO fd cloning? - io.place_file(tty_node.open().unwrap()).unwrap(); + io.place_file(tty_node.open(OpenFlags::O_RDWR).unwrap()).unwrap(); } drop(cfg); diff --git a/kernel/src/proc/process.rs b/kernel/src/proc/process.rs index 0212af5..b4cdfaa 100644 --- a/kernel/src/proc/process.rs +++ b/kernel/src/proc/process.rs @@ -161,6 +161,17 @@ impl ProcessIo { Err(Errno::TooManyDescriptors) } + /// + pub fn close_file(&mut self, idx: usize) -> Result<(), Errno> { + if self.file_bitmap & (1 << idx) == 0 { + return Err(Errno::InvalidFile); + } + + let res = self.files.remove(&idx); + assert!(res.is_some()); + Ok(()) + } + /// pub fn new() -> Self { Self { diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index fd4607d..15ba5de 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -5,10 +5,8 @@ use core::mem::size_of; use core::time::Duration; use error::Errno; use libcommon::{Read, Write}; -use syscall::{ - abi, - stat::{Stat, AT_FDCWD}, -}; +use syscall::{abi, stat::AT_FDCWD}; +use vfs::{FileMode, OpenFlags, Stat}; fn translate(virt: usize) -> Option { let mut res: usize; @@ -24,9 +22,7 @@ fn translate(virt: usize) -> Option { fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> { let bytes = validate_user_ptr(base, size_of::())?; - Ok(unsafe { - &mut *(bytes.as_mut_ptr() as *mut T) - }) + Ok(unsafe { &mut *(bytes.as_mut_ptr() as *mut T) }) } fn validate_user_ptr<'a>(base: usize, len: usize) -> Result<&'a mut [u8], Errno> { @@ -119,15 +115,23 @@ pub unsafe fn syscall(num: usize, args: &[usize]) -> Result { } // I/O system calls - abi::SYS_OPEN => { - let path = validate_user_str(args[0], 256)?; + abi::SYS_OPENAT => { + let at_fd = args[0]; + let path = validate_user_str(args[1], 256)?; + let mode = FileMode::from_bits(args[2] as u32).ok_or(Errno::InvalidArgument)?; + let opts = OpenFlags::from_bits(args[3] as u32).ok_or(Errno::InvalidArgument)?; + + let at = if at_fd as i32 == AT_FDCWD { + None + } else { + todo!(); + }; let proc = Process::current(); let mut io = proc.io.lock(); - let node = io.ioctx().find(None, path, true)?; - // TODO check access - io.place_file(node.open()?) + let file = io.ioctx().open(at, path, mode, opts)?; + io.place_file(file) } abi::SYS_READ => { let proc = Process::current(); @@ -160,6 +164,14 @@ pub unsafe fn syscall(num: usize, args: &[usize]) -> Result { node.stat(buf)?; Ok(0) } + abi::SYS_CLOSE => { + let proc = Process::current(); + let mut io = proc.io.lock(); + let fd = args[0]; + + io.close_file(fd)?; + Ok(0) + } // Extra system calls abi::SYS_EX_DEBUG_TRACE => { diff --git a/libusr/src/lib.rs b/libusr/src/lib.rs index 524a768..014814d 100644 --- a/libusr/src/lib.rs +++ b/libusr/src/lib.rs @@ -9,7 +9,7 @@ pub mod io; pub mod sys { pub use syscall::calls::*; - pub use syscall::stat::Stat; + pub use syscall::stat::{AT_FDCWD, Stat, OpenFlags}; } #[link_section = ".text._start"] diff --git a/syscall/Cargo.toml b/syscall/Cargo.toml index 666eb63..16a7f53 100644 --- a/syscall/Cargo.toml +++ b/syscall/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bitflags = "^1.3.0" [features] user = [] diff --git a/syscall/src/abi.rs b/syscall/src/abi.rs index c707b51..5dd734d 100644 --- a/syscall/src/abi.rs +++ b/syscall/src/abi.rs @@ -6,5 +6,6 @@ pub const SYS_EX_NANOSLEEP: usize = 129; pub const SYS_EXIT: usize = 1; pub const SYS_READ: usize = 2; pub const SYS_WRITE: usize = 3; -pub const SYS_OPEN: usize = 4; +pub const SYS_OPENAT: usize = 4; pub const SYS_FSTATAT: usize = 5; +pub const SYS_CLOSE: usize = 6; diff --git a/syscall/src/calls.rs b/syscall/src/calls.rs index e9f0f42..25e1e93 100644 --- a/syscall/src/calls.rs +++ b/syscall/src/calls.rs @@ -38,6 +38,11 @@ pub unsafe fn sys_exit(status: i32) -> ! { loop {} } +#[inline(always)] +pub unsafe fn sys_close(fd: i32) -> i32 { + syscall!(abi::SYS_CLOSE, fd as usize) as i32 +} + #[inline(always)] pub unsafe fn sys_ex_nanosleep(ns: u64, rem: *mut [u64; 2]) -> i32 { syscall!(abi::SYS_EX_NANOSLEEP, ns as usize, rem as usize) as i32 @@ -49,9 +54,10 @@ pub unsafe fn sys_ex_debug_trace(msg: *const u8, len: usize) -> usize { } #[inline(always)] -pub unsafe fn sys_open(pathname: *const u8, mode: u32, flags: u32) -> i32 { +pub unsafe fn sys_openat(at: i32, pathname: *const u8, mode: u32, flags: u32) -> i32 { syscall!( - abi::SYS_OPEN, + abi::SYS_OPENAT, + at as usize, pathname as usize, mode as usize, flags as usize diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index ce77afc..b515e0f 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -4,8 +4,10 @@ #[cfg(feature = "linux_compat")] compile_error!("Not yet implemented"); -pub mod abi; +#[macro_use] +extern crate bitflags; +pub mod abi; pub mod stat; #[cfg(feature = "user")] diff --git a/syscall/src/stat.rs b/syscall/src/stat.rs index 035ddd7..09c2e20 100644 --- a/syscall/src/stat.rs +++ b/syscall/src/stat.rs @@ -1,5 +1,32 @@ +use core::fmt; + pub const AT_FDCWD: i32 = -2; +bitflags! { + pub struct OpenFlags: u32 { + const O_RDONLY = 1 << 0; + const O_WRONLY = 2 << 0; + const O_RDWR = 3 << 0; + const O_ACCESS = 0xF; + + const O_CREAT = 1 << 4; + } +} + +bitflags! { + pub struct FileMode: u32 { + const USER_READ = 1 << 8; + const USER_WRITE = 1 << 7; + const USER_EXEC = 1 << 6; + const GROUP_READ = 1 << 5; + const GROUP_WRITE = 1 << 4; + const GROUP_EXEC = 1 << 3; + const OTHER_READ = 1 << 2; + const OTHER_WRITE = 1 << 1; + const OTHER_EXEC = 1 << 0; + } +} + #[derive(Clone, Copy, Debug)] #[repr(C)] pub struct Stat { @@ -7,3 +34,19 @@ pub struct Stat { pub size: u64, pub blksize: u32, } + +impl FileMode { + /// Returns default permission set for directories + pub const fn default_dir() -> Self { + unsafe { + Self::from_bits_unchecked(0o755) + } + } + + /// Returns default permission set for regular files + pub const fn default_reg() -> Self { + unsafe { + Self::from_bits_unchecked(0o644) + } + } +}