feature: simple macro to auto-impl VnodeImpl items

This commit is contained in:
Mark Poliakov 2021-11-11 22:08:55 +02:00
parent 47ef7e29fe
commit dd8033b51c
10 changed files with 164 additions and 112 deletions

9
Cargo.lock generated

@ -64,6 +64,14 @@ dependencies = [
"unsafe_unwrap", "unsafe_unwrap",
] ]
[[package]]
name = "fs-macros"
version = "0.1.0"
dependencies = [
"quote",
"syn",
]
[[package]] [[package]]
name = "kernel" name = "kernel"
version = "0.1.0" version = "0.1.0"
@ -230,5 +238,6 @@ dependencies = [
name = "vfs" name = "vfs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"fs-macros",
"libsys", "libsys",
] ]

@ -10,6 +10,7 @@ edition = "2018"
[workspace] [workspace]
members = [ members = [
"fs/fat32", "fs/fat32",
"fs/macros",
"fs/memfs", "fs/memfs",
"fs/vfs", "fs/vfs",
"kernel", "kernel",

13
fs/macros/Cargo.toml Normal file

@ -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

119
fs/macros/src/lib.rs Normal file

@ -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<T: ToTokens>(name: &str, behavior: T) -> ImplItem {
ImplItem::Verbatim(match name {
"create" => quote! {
fn create(&mut self, _at: VnodeRef, _name: &str, kind: VnodeKind) -> Result<VnodeRef, Errno> {
#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<VnodeRef, Errno> {
#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<usize, Errno> {
#behavior
}
},
"read" => quote! {
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result<usize, Errno> {
#behavior
}
},
"write" => quote! {
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
#behavior
}
},
"open" => quote! {
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
#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<usize, Errno> {
#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::<String>::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()
}

@ -7,3 +7,4 @@ edition = "2021"
[dependencies] [dependencies]
libsys = { path = "../../libsys" } libsys = { path = "../../libsys" }
fs-macros = { path = "../macros" }

@ -25,19 +25,8 @@ pub struct CharDeviceWrapper {
device: &'static dyn CharDevice, device: &'static dyn CharDevice,
} }
#[auto_inode(error)]
impl VnodeImpl for CharDeviceWrapper { impl VnodeImpl for CharDeviceWrapper {
fn create(&mut self, _at: VnodeRef, _name: &str, _kind: VnodeKind) -> Result<VnodeRef, Errno> {
panic!();
}
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
panic!();
}
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
panic!();
}
fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result<usize, Errno> { fn open(&mut self, _node: VnodeRef, _opts: OpenFlags) -> Result<usize, Errno> {
Ok(0) Ok(0)
} }
@ -54,18 +43,6 @@ impl VnodeImpl for CharDeviceWrapper {
self.device.write(true, data) self.device.write(true, data)
} }
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
panic!();
}
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
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<usize, Errno> { fn ioctl(&mut self, _node: VnodeRef, cmd: IoctlCmd, ptr: usize, len: usize) -> Result<usize, Errno> {
self.device.ioctl(cmd, ptr, len) self.device.ioctl(cmd, ptr, len)
} }

@ -3,8 +3,8 @@ use alloc::rc::Rc;
use core::cell::RefCell; use core::cell::RefCell;
use core::cmp::min; use core::cmp::min;
use libsys::{ use libsys::{
error::Errno,
traits::{Read, Seek, SeekDir, Write}, traits::{Read, Seek, SeekDir, Write},
error::Errno
}; };
struct NormalFile { struct NormalFile {
@ -135,11 +135,13 @@ impl Drop for File {
mod tests { mod tests {
use super::*; use super::*;
use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef}; use crate::{Vnode, VnodeImpl, VnodeKind, VnodeRef};
use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat};
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::rc::Rc; use alloc::rc::Rc;
struct DummyInode; struct DummyInode;
#[auto_inode]
impl VnodeImpl for DummyInode { impl VnodeImpl for DummyInode {
fn create( fn create(
&mut self, &mut self,
@ -152,25 +154,17 @@ mod tests {
Ok(node) Ok(node)
} }
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> { fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
Err(Errno::NotImplemented)
}
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
todo!()
}
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
Ok(0) Ok(0)
} }
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> { fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
Err(Errno::NotImplemented) Ok(())
} }
fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> { fn read(&mut self, _node: VnodeRef, pos: usize, data: &mut [u8]) -> Result<usize, Errno> {
#[cfg(test)] #[cfg(test)]
println!("read {}", pos); println!("read {} at {}", data.len(), pos);
let len = 123; let len = 123;
if pos >= len { if pos >= len {
return Ok(0); return Ok(0);
@ -185,41 +179,24 @@ mod tests {
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> { fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
Err(Errno::NotImplemented) Err(Errno::NotImplemented)
} }
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
Err(Errno::NotImplemented)
}
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
Err(Errno::NotImplemented)
}
} }
#[test] #[test]
fn test_normal_read() { fn test_normal_read() {
let node = Vnode::new("", VnodeKind::Regular, 0); let node = Vnode::new("", VnodeKind::Regular, 0);
node.set_data(Box::new(DummyInode {})); node.set_data(Box::new(DummyInode {}));
let mut file = node.open().unwrap(); let mut file = node.open(OpenFlags::O_RDONLY).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 buf = [0u8; 4096]; 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 { for i in 0..32 {
assert_eq!((i & 0xFF) as u8, buf[i]); 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 { for i in 0..64 {
assert_eq!(((i + 32) & 0xFF) as u8, buf[i]); 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 { for i in 0..27 {
assert_eq!(((i + 96) & 0xFF) as u8, buf[i]); assert_eq!(((i + 96) & 0xFF) as u8, buf[i]);
} }

@ -1,7 +1,7 @@
use crate::{FileMode, FileRef, OpenFlags, VnodeKind, VnodeRef}; use crate::{FileMode, FileRef, OpenFlags, VnodeKind, VnodeRef};
use libsys::{ use libsys::{
error::Errno, error::Errno,
path::{path_component_left, path_component_right} path::{path_component_left, path_component_right},
}; };
/// I/O context structure /// I/O context structure
@ -119,9 +119,11 @@ mod tests {
use super::*; use super::*;
use crate::{Vnode, VnodeImpl, VnodeKind}; use crate::{Vnode, VnodeImpl, VnodeKind};
use alloc::{boxed::Box, rc::Rc}; use alloc::{boxed::Box, rc::Rc};
use libsys::{ioctl::IoctlCmd, stat::OpenFlags, stat::Stat};
pub struct DummyInode; pub struct DummyInode;
#[auto_inode]
impl VnodeImpl for DummyInode { impl VnodeImpl for DummyInode {
fn create( fn create(
&mut self, &mut self,
@ -134,37 +136,9 @@ mod tests {
Ok(vnode) Ok(vnode)
} }
fn remove(&mut self, _at: VnodeRef, _name: &str) -> Result<(), Errno> {
todo!()
}
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> { fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
Err(Errno::DoesNotExist) Err(Errno::DoesNotExist)
} }
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
fn close(&mut self, _node: VnodeRef) -> Result<(), Errno> {
todo!()
}
fn read(&mut self, _node: VnodeRef, _pos: usize, _data: &mut [u8]) -> Result<usize, Errno> {
todo!()
}
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
todo!()
}
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
todo!()
}
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
todo!()
}
} }
#[test] #[test]

@ -7,6 +7,9 @@
#[macro_use] #[macro_use]
extern crate std; extern crate std;
#[macro_use]
extern crate fs_macros;
extern crate alloc; extern crate alloc;
pub use libsys::stat::{FileMode, OpenFlags, Stat}; pub use libsys::stat::{FileMode, OpenFlags, Stat};

@ -405,8 +405,10 @@ impl fmt::Debug for Vnode {
mod tests { mod tests {
use super::*; use super::*;
use libsys::{stat::OpenFlags, ioctl::IoctlCmd, stat::Stat};
pub struct DummyInode; pub struct DummyInode;
#[auto_inode]
impl VnodeImpl for DummyInode { impl VnodeImpl for DummyInode {
fn create( fn create(
&mut self, &mut self,
@ -426,30 +428,6 @@ mod tests {
fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> { fn lookup(&mut self, _at: VnodeRef, _name: &str) -> Result<VnodeRef, Errno> {
Err(Errno::DoesNotExist) Err(Errno::DoesNotExist)
} }
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
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<usize, Errno> {
Err(Errno::NotImplemented)
}
fn write(&mut self, _node: VnodeRef, _pos: usize, _data: &[u8]) -> Result<usize, Errno> {
Err(Errno::NotImplemented)
}
fn truncate(&mut self, _node: VnodeRef, _size: usize) -> Result<(), Errno> {
Err(Errno::NotImplemented)
}
fn size(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
Err(Errno::NotImplemented)
}
} }
#[test] #[test]
@ -469,10 +447,10 @@ mod tests {
root.set_data(Box::new(DummyInode {})); 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!( assert_eq!(
root.mkdir("test", FileMode::default_dir()).unwrap_err(), root.create("test", FileMode::default_dir(), VnodeKind::Directory).unwrap_err(),
Errno::AlreadyExists Errno::AlreadyExists
); );