feature(fs): O_CREAT option

This commit is contained in:
2021-11-03 17:35:51 +02:00
parent 53a432bc05
commit 166fc19b7a
19 changed files with 182 additions and 158 deletions
Generated
+3
View File
@@ -211,6 +211,9 @@ dependencies = [
[[package]]
name = "syscall"
version = "0.1.0"
dependencies = [
"bitflags",
]
[[package]]
name = "tock-registers"
+15 -6
View File
@@ -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<A: BlockAllocator + Copy + 'static> {
alloc: A,
}
impl VnodeImpl for DirInode {
impl<A: BlockAllocator + Copy + 'static> VnodeImpl for DirInode<A> {
fn create(
&mut self,
_parent: VnodeRef,
@@ -13,8 +16,9 @@ impl VnodeImpl for DirInode {
) -> Result<VnodeRef, Errno> {
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<usize, Errno> {
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
todo!()
}
@@ -56,3 +60,8 @@ impl VnodeImpl for DirInode {
}
}
impl<A: BlockAllocator + Copy + 'static> DirInode<A> {
pub const fn new(alloc: A) -> Self {
Self { alloc }
}
}
+8 -8
View File
@@ -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<usize, Errno> {
fn open(&mut self, _node: VnodeRef, _mode: OpenFlags) -> Result<usize, Errno> {
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 }
}
}
+2 -2
View File
@@ -62,7 +62,7 @@ impl<A: BlockAllocator + Copy + 'static> Ramfs<A> {
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<A: BlockAllocator + Copy + 'static> Ramfs<A> {
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
}
};
+2 -2
View File
@@ -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<usize, Errno> {
fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result<usize, Errno> {
Ok(0)
}
+47 -8
View File
@@ -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<VnodeRef, Errno> {
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<VnodeRef>,
path: &str,
mode: FileMode,
opts: OpenFlags,
) -> Result<File, Errno> {
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()
));
}
}
+1 -3
View File
@@ -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;
+7 -7
View File
@@ -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<VnodeRef, Errno>;
/// Opens a vnode for access. Returns initial file position.
fn open(&mut self, node: VnodeRef /* TODO open mode */) -> Result<usize, Errno>;
fn open(&mut self, node: VnodeRef, opts: OpenFlags) -> Result<usize, Errno>;
/// 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<VnodeRef, Errno> {
/// Creates a new node `name` in `self`
pub fn create(self: &VnodeRef, name: &str, mode: FileMode, kind: VnodeKind) -> Result<VnodeRef, Errno> {
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<File, Errno> {
pub fn open(self: &VnodeRef, flags: OpenFlags) -> Result<File, Errno> {
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)
-99
View File
@@ -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(())
}
}
+1 -3
View File
@@ -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!("> ");
+3 -3
View File
@@ -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);
+11
View File
@@ -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 {
+24 -12
View File
@@ -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<usize> {
let mut res: usize;
@@ -24,9 +22,7 @@ fn translate(virt: usize) -> Option<usize> {
fn validate_user_ptr_struct<'a, T>(base: usize) -> Result<&'a mut T, Errno> {
let bytes = validate_user_ptr(base, size_of::<T>())?;
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<usize, Errno> {
}
// 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<usize, Errno> {
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 => {
+1 -1
View File
@@ -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"]
+1
View File
@@ -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 = []
+2 -1
View File
@@ -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;
+8 -2
View File
@@ -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
+3 -1
View File
@@ -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")]
+43
View File
@@ -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)
}
}
}