diff --git a/Cargo.lock b/Cargo.lock index 8202738..0958306 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,14 @@ dependencies = [ "unsafe_unwrap", ] +[[package]] +name = "fs-macros" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "kernel" version = "0.1.0" @@ -230,5 +238,6 @@ dependencies = [ name = "vfs" version = "0.1.0" dependencies = [ + "fs-macros", "libsys", ] diff --git a/Cargo.toml b/Cargo.toml index 4ae5460..94aff48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" [workspace] members = [ "fs/fat32", + "fs/macros", "fs/memfs", "fs/vfs", "kernel", diff --git a/fs/macros/Cargo.toml b/fs/macros/Cargo.toml new file mode 100644 index 0000000..f73f66c --- /dev/null +++ b/fs/macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "fs-macros" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +syn = { version = "^1.0.81", features = ["full"] } +quote = "^1.0.10" + +[lib] +proc-macro = true diff --git a/fs/macros/src/lib.rs b/fs/macros/src/lib.rs new file mode 100644 index 0000000..4ccd843 --- /dev/null +++ b/fs/macros/src/lib.rs @@ -0,0 +1,119 @@ +extern crate proc_macro; +extern crate syn; +#[macro_use] +extern crate quote; + +use proc_macro::TokenStream; +use quote::ToTokens; +use std::collections::HashSet; +use syn::{parse_macro_input, ImplItem, ItemImpl, Ident}; + +fn impl_inode_fn(name: &str, behavior: T) -> ImplItem { + ImplItem::Verbatim(match name { + "create" => quote! { + fn create(&mut self, _at: VnodeRef, _name: &str, kind: VnodeKind) -> Result { + #behavior + } + }, + "remove" => quote! { + fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> { + #behavior + } + }, + "lookup" => quote! { + fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { + #behavior + } + }, + "stat" => quote! { + fn stat(&mut self, _at: VnodeRef, _stat: &mut Stat) -> Result<(), Errno> { + #behavior + } + }, + "truncate" => quote! { + fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { + #behavior + } + }, + "size" => quote! { + fn size(&mut self, _node: VnodeRef) -> Result { + #behavior + } + }, + "read" => quote! { + fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result { + #behavior + } + }, + "write" => quote! { + fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { + #behavior + } + }, + "open" => quote! { + fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result { + #behavior + } + }, + "close" => quote! { + fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { + #behavior + } + }, + "ioctl" => quote! { + fn ioctl(&mut self, _node: VnodeRef, _cmd: IoctlCmd, _ptr: usize, _len: usize) -> Result { + #behavior + } + }, + _ => panic!("TODO implement {:?}", name), + }) +} + +#[proc_macro_attribute] +pub fn auto_inode(attr: TokenStream, input: TokenStream) -> TokenStream { + let mut impl_item = parse_macro_input!(input as ItemImpl); + let mut missing = HashSet::::new(); + let behavior = if attr.is_empty() { + "unimplemented".to_string() + } else { + parse_macro_input!(attr as Ident).to_string() + }; + let behavior = match behavior.as_str() { + "unimplemented" => quote! { unimplemented!() }, + "panic" => quote! { panic!() }, + "error" => quote! { Err(libsys::error::Errno::NotImplemented) }, + _ => panic!("Unknown #[auto_inode] behavior: {:?}", behavior) + }; + + missing.insert("create".to_string()); + missing.insert("remove".to_string()); + missing.insert("lookup".to_string()); + missing.insert("open".to_string()); + missing.insert("close".to_string()); + missing.insert("truncate".to_string()); + missing.insert("read".to_string()); + missing.insert("write".to_string()); + missing.insert("stat".to_string()); + missing.insert("size".to_string()); + missing.insert("ioctl".to_string()); + + for item in &impl_item.items { + match item { + ImplItem::Method(method) => { + let name = &method.sig.ident.to_string(); + if missing.contains(name) { + missing.remove(name); + } + } + _ => panic!("Unexpected impl item"), + } + } + + for item in &missing { + impl_item + .items + .push(impl_inode_fn(item, behavior.clone())); + } + + impl_item.to_token_stream().into() +} diff --git a/fs/vfs/Cargo.toml b/fs/vfs/Cargo.toml index d05ed41..24ade6d 100644 --- a/fs/vfs/Cargo.toml +++ b/fs/vfs/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] libsys = { path = "../../libsys" } +fs-macros = { path = "../macros" } diff --git a/fs/vfs/src/char.rs b/fs/vfs/src/char.rs index 7eae7de..fa143b0 100644 --- a/fs/vfs/src/char.rs +++ b/fs/vfs/src/char.rs @@ -25,19 +25,8 @@ pub struct CharDeviceWrapper { device: &'static dyn CharDevice, } +#[auto_inode(error)] impl VnodeImpl for CharDeviceWrapper { - fn create(&mut self, _at: VnodeRef, _name: &str, _kind: VnodeKind) -> Result { - panic!(); - } - - fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> { - panic!(); - } - - fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { - panic!(); - } - fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result { Ok(0) } @@ -54,18 +43,6 @@ impl VnodeImpl for CharDeviceWrapper { self.device.write(true, data) } - fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { - panic!(); - } - - fn size(&mut self, _node: VnodeRef) -> Result { - panic!(); - } - - fn stat(&mut self, _node: VnodeRef, _stat: &mut Stat) -> Result<(), Errno> { - todo!(); - } - fn ioctl(&mut self, _node: VnodeRef, cmd: IoctlCmd, ptr: usize, len: usize) -> Result { self.device.ioctl(cmd, ptr, len) } diff --git a/fs/vfs/src/file.rs b/fs/vfs/src/file.rs index af6bf77..e7e794d 100644 --- a/fs/vfs/src/file.rs +++ b/fs/vfs/src/file.rs @@ -3,8 +3,8 @@ use alloc::rc::Rc; use core::cell::RefCell; use core::cmp::min; use libsys::{ + error::Errno, traits::{Read, Seek, SeekDir, Write}, - error::Errno }; struct NormalFile { @@ -135,11 +135,13 @@ impl Drop for File { mod tests { use super::*; use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; + use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat}; use alloc::boxed::Box; use alloc::rc::Rc; struct DummyInode; + #[auto_inode] impl VnodeImpl for DummyInode { fn create( &mut self, @@ -152,25 +154,17 @@ mod tests { Ok(node) } - fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> { - Err(Errno::NotImplemented) - } - - fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { - todo!() - } - - fn open(&mut self, _node: VnodeRef) -> Result { + fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result { Ok(0) } fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { - Err(Errno::NotImplemented) + Ok(()) } fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result { #[cfg(test)] - println!("read {}", pos); + println!("read {} at {}", data.len(), pos); let len = 123; if pos >= len { return Ok(0); @@ -185,41 +179,24 @@ mod tests { fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { Err(Errno::NotImplemented) } - - fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { - Err(Errno::NotImplemented) - } - - fn size(&mut self, _node: VnodeRef) -> Result { - Err(Errno::NotImplemented) - } } #[test] fn test_normal_read() { let node = Vnode::new("", VnodeKind::Regular, 0); node.set_data(Box::new(DummyInode {})); - let mut file = node.open().unwrap(); - - match &file.inner { - FileInner::Normal(inner) => { - assert!(Rc::ptr_eq(&inner.vnode, &node)); - assert_eq!(inner.pos, 0); - } - _ => panic!("Invalid file.inner"), - } - + let mut file = node.open(OpenFlags::O_RDONLY).unwrap(); let mut buf = [0u8; 4096]; - assert_eq!(file.read(&mut buf[0..32]).unwrap(), 32); + assert_eq!(file.borrow_mut().read(&mut buf[0..32]).unwrap(), 32); for i in 0..32 { assert_eq!((i & 0xFF) as u8, buf[i]); } - assert_eq!(file.read(&mut buf[0..64]).unwrap(), 64); + assert_eq!(file.borrow_mut().read(&mut buf[0..64]).unwrap(), 64); for i in 0..64 { assert_eq!(((i + 32) & 0xFF) as u8, buf[i]); } - assert_eq!(file.read(&mut buf[0..64]).unwrap(), 27); + assert_eq!(file.borrow_mut().read(&mut buf[0..64]).unwrap(), 27); for i in 0..27 { assert_eq!(((i + 96) & 0xFF) as u8, buf[i]); } diff --git a/fs/vfs/src/ioctx.rs b/fs/vfs/src/ioctx.rs index 2980d20..a7650d9 100644 --- a/fs/vfs/src/ioctx.rs +++ b/fs/vfs/src/ioctx.rs @@ -1,7 +1,7 @@ use crate::{FileMode, FileRef, OpenFlags, VnodeKind, VnodeRef}; use libsys::{ error::Errno, - path::{path_component_left, path_component_right} + path::{path_component_left, path_component_right}, }; /// I/O context structure @@ -119,9 +119,11 @@ mod tests { use super::*; use crate::{Vnode, VnodeImpl, VnodeKind}; use alloc::{boxed::Box, rc::Rc}; + use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat}; pub struct DummyInode; + #[auto_inode] impl VnodeImpl for DummyInode { fn create( &mut self, @@ -134,37 +136,9 @@ mod tests { Ok(vnode) } - fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> { - todo!() - } - fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { Err(Errno::DoesNotExist) } - - fn open(&mut self, _node: VnodeRef) -> Result { - todo!() - } - - fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { - todo!() - } - - fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result { - todo!() - } - - fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { - todo!() - } - - fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { - todo!() - } - - fn size(&mut self, _node: VnodeRef) -> Result { - todo!() - } } #[test] diff --git a/fs/vfs/src/lib.rs b/fs/vfs/src/lib.rs index c8a74fe..374731a 100644 --- a/fs/vfs/src/lib.rs +++ b/fs/vfs/src/lib.rs @@ -7,6 +7,9 @@ #[macro_use] extern crate std; +#[macro_use] +extern crate fs_macros; + extern crate alloc; pub use libsys::stat::{FileMode, OpenFlags, Stat}; diff --git a/fs/vfs/src/node.rs b/fs/vfs/src/node.rs index 8df4eee..2893299 100644 --- a/fs/vfs/src/node.rs +++ b/fs/vfs/src/node.rs @@ -405,8 +405,10 @@ impl fmt::Debug for Vnode { mod tests { use super::*; + use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat}; pub struct DummyInode; + #[auto_inode] impl VnodeImpl for DummyInode { fn create( &mut self, @@ -426,30 +428,6 @@ mod tests { fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result { Err(Errno::DoesNotExist) } - - fn open(&mut self, _node: VnodeRef) -> Result { - Err(Errno::NotImplemented) - } - - fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { - Err(Errno::NotImplemented) - } - - fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result { - Err(Errno::NotImplemented) - } - - fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result { - Err(Errno::NotImplemented) - } - - fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> { - Err(Errno::NotImplemented) - } - - fn size(&mut self, _node: VnodeRef) -> Result { - Err(Errno::NotImplemented) - } } #[test] @@ -469,10 +447,10 @@ mod tests { root.set_data(Box::new(DummyInode {})); - let node = root.mkdir("test", FileMode::default_dir()).unwrap(); + let node = root.create("test", FileMode::default_dir(), VnodeKind::Directory).unwrap(); assert_eq!( - root.mkdir("test", FileMode::default_dir()).unwrap_err(), + root.create("test", FileMode::default_dir(), VnodeKind::Directory).unwrap_err(), Errno::AlreadyExists );