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
View File

@ -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",
]

View File

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

13
fs/macros/Cargo.toml Normal file
View 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
View 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()
}

View File

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

View File

@ -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<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> {
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<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> {
self.device.ioctl(cmd, ptr, len)
}

View File

@ -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<VnodeRef, Errno> {
todo!()
}
fn open(&mut self, _node: VnodeRef) -> Result<usize, Errno> {
fn open(&mut self, _node: VnodeRef, _flags: OpenFlags) -> Result<usize, Errno> {
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<usize, Errno> {
#[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<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]
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]);
}

View File

@ -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<VnodeRef, Errno> {
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]

View File

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

View File

@ -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<VnodeRef, Errno> {
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]
@ -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
);