diff --git a/src/chrdev.rs b/src/chrdev.rs index 9115fd9c..d6d10fbb 100644 --- a/src/chrdev.rs +++ b/src/chrdev.rs @@ -23,7 +23,7 @@ pub fn builder(name: &'static CStr, minors: Range) -> KernelResult pub struct Builder { name: &'static CStr, minors: Range, - file_ops: Vec<&'static file_operations::FileOperationsVtable>, + file_ops: Vec<&'static bindings::file_operations>, } impl Builder { @@ -31,7 +31,8 @@ impl Builder { if self.file_ops.len() >= self.minors.len() { panic!("More devices registered than minor numbers allocated.") } - self.file_ops.push(&T::VTABLE); + self.file_ops + .push(&file_operations::FileOperationsVtable::::VTABLE); self } @@ -54,7 +55,7 @@ impl Builder { let mut cdevs = vec![unsafe { mem::zeroed() }; self.file_ops.len()].into_boxed_slice(); for (i, file_op) in self.file_ops.iter().enumerate() { unsafe { - bindings::cdev_init(&mut cdevs[i], &file_op.0); + bindings::cdev_init(&mut cdevs[i], *file_op); cdevs[i].owner = &mut bindings::__this_module; let rc = bindings::cdev_add(&mut cdevs[i], dev + i as bindings::dev_t, 1); if rc != 0 { diff --git a/src/file_operations.rs b/src/file_operations.rs index 10de5b25..1f7d85cc 100644 --- a/src/file_operations.rs +++ b/src/file_operations.rs @@ -39,8 +39,6 @@ pub enum SeekFrom { Current(i64), } -pub struct FileOperationsVtable(pub(crate) bindings::file_operations); - unsafe extern "C" fn open_callback( _inode: *mut bindings::inode, file: *mut bindings::file, @@ -53,7 +51,7 @@ unsafe extern "C" fn open_callback( 0 } -unsafe extern "C" fn read_callback( +unsafe extern "C" fn read_callback( file: *mut bindings::file, buf: *mut c_types::c_char, len: c_types::c_size_t, @@ -70,7 +68,8 @@ unsafe extern "C" fn read_callback( Ok(v) => v, Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(), }; - match f.read(&File::from_ptr(file), &mut data, positive_offset) { + let read = T::READ.unwrap(); + match read(f, &File::from_ptr(file), &mut data, positive_offset) { Ok(()) => { let written = len - data.len(); (*offset) += bindings::loff_t::try_from(written).unwrap(); @@ -80,7 +79,7 @@ unsafe extern "C" fn read_callback( } } -unsafe extern "C" fn write_callback( +unsafe extern "C" fn write_callback( file: *mut bindings::file, buf: *const c_types::c_char, len: c_types::c_size_t, @@ -97,7 +96,8 @@ unsafe extern "C" fn write_callback( Ok(v) => v, Err(_) => return Error::EINVAL.to_kernel_errno().try_into().unwrap(), }; - match f.write(&mut data, positive_offset) { + let write = T::WRITE.unwrap(); + match write(f, &mut data, positive_offset) { Ok(()) => { let read = len - data.len(); (*offset) += bindings::loff_t::try_from(read).unwrap(); @@ -116,7 +116,7 @@ unsafe extern "C" fn release_callback( 0 } -unsafe extern "C" fn llseek_callback( +unsafe extern "C" fn llseek_callback( file: *mut bindings::file, offset: bindings::loff_t, whence: c_types::c_int, @@ -131,130 +131,101 @@ unsafe extern "C" fn llseek_callback( _ => return Error::EINVAL.to_kernel_errno().into(), }; let f = &*((*file).private_data as *const T); - match f.seek(&File::from_ptr(file), off) { + let seek = T::SEEK.unwrap(); + match seek(f, &File::from_ptr(file), off) { Ok(off) => off as bindings::loff_t, Err(e) => e.to_kernel_errno().into(), } } -impl FileOperationsVtable { - pub const fn builder() -> FileOperationsVtableBuilder { - FileOperationsVtableBuilder( - bindings::file_operations { - open: Some(open_callback::), - release: Some(release_callback::), - - #[cfg(not(kernel_4_9_0_or_greater))] - aio_fsync: None, - check_flags: None, - #[cfg(all(kernel_4_5_0_or_greater, not(kernel_4_20_0_or_greater)))] - clone_file_range: None, - compat_ioctl: None, - #[cfg(kernel_4_5_0_or_greater)] - copy_file_range: None, - #[cfg(all(kernel_4_5_0_or_greater, not(kernel_4_20_0_or_greater)))] - dedupe_file_range: None, - fallocate: None, - #[cfg(kernel_4_19_0_or_greater)] - fadvise: None, - fasync: None, - flock: None, - flush: None, - fsync: None, - get_unmapped_area: None, - iterate: None, - #[cfg(kernel_4_7_0_or_greater)] - iterate_shared: None, - #[cfg(kernel_5_1_0_or_greater)] - iopoll: None, - llseek: None, - lock: None, - mmap: None, - #[cfg(kernel_4_15_0_or_greater)] - mmap_supported_flags: 0, - owner: ptr::null_mut(), - poll: None, - read: None, - read_iter: None, - #[cfg(kernel_4_20_0_or_greater)] - remap_file_range: None, - sendpage: None, - #[cfg(kernel_aufs_setfl)] - setfl: None, - setlease: None, - show_fdinfo: None, - splice_read: None, - splice_write: None, - unlocked_ioctl: None, - write: None, - write_iter: None, - }, - marker::PhantomData, - ) - } -} - -pub struct FileOperationsVtableBuilder(bindings::file_operations, marker::PhantomData); +pub(crate) struct FileOperationsVtable(marker::PhantomData); -impl FileOperationsVtableBuilder { - pub const fn build(self) -> FileOperationsVtable { - FileOperationsVtable(self.0) - } -} - -impl FileOperationsVtableBuilder { - pub const fn read(mut self) -> FileOperationsVtableBuilder { - self.0.read = Some(read_callback::); - self - } -} +impl FileOperationsVtable { + pub(crate) const VTABLE: bindings::file_operations = bindings::file_operations { + open: Some(open_callback::), + release: Some(release_callback::), + read: if let Some(_) = T::READ { + Some(read_callback::) + } else { + None + }, + write: if let Some(_) = T::WRITE { + Some(write_callback::) + } else { + None + }, + llseek: if let Some(_) = T::SEEK { + Some(llseek_callback::) + } else { + None + }, -impl FileOperationsVtableBuilder { - pub const fn write(mut self) -> FileOperationsVtableBuilder { - self.0.write = Some(write_callback::); - self - } + #[cfg(not(kernel_4_9_0_or_greater))] + aio_fsync: None, + check_flags: None, + #[cfg(all(kernel_4_5_0_or_greater, not(kernel_4_20_0_or_greater)))] + clone_file_range: None, + compat_ioctl: None, + #[cfg(kernel_4_5_0_or_greater)] + copy_file_range: None, + #[cfg(all(kernel_4_5_0_or_greater, not(kernel_4_20_0_or_greater)))] + dedupe_file_range: None, + fallocate: None, + #[cfg(kernel_4_19_0_or_greater)] + fadvise: None, + fasync: None, + flock: None, + flush: None, + fsync: None, + get_unmapped_area: None, + iterate: None, + #[cfg(kernel_4_7_0_or_greater)] + iterate_shared: None, + #[cfg(kernel_5_1_0_or_greater)] + iopoll: None, + lock: None, + mmap: None, + #[cfg(kernel_4_15_0_or_greater)] + mmap_supported_flags: 0, + owner: ptr::null_mut(), + poll: None, + read_iter: None, + #[cfg(kernel_4_20_0_or_greater)] + remap_file_range: None, + sendpage: None, + #[cfg(kernel_aufs_setfl)] + setfl: None, + setlease: None, + show_fdinfo: None, + splice_read: None, + splice_write: None, + unlocked_ioctl: None, + write_iter: None, + }; } -impl FileOperationsVtableBuilder { - pub const fn seek(mut self) -> FileOperationsVtableBuilder { - self.0.llseek = Some(llseek_callback::); - self - } -} +pub type ReadFn = Option KernelResult<()>>; +pub type WriteFn = Option KernelResult<()>>; +pub type SeekFn = Option KernelResult>; /// `FileOperations` corresponds to the kernel's `struct file_operations`. You -/// implement this trait whenever you'd create a `struct file_operations`, and -/// also an additional trait for each function pointer in the -/// `struct file_operations`. File descriptors may be used from multiple threads -/// (or processes) concurrently, so your type must be `Sync`. +/// implement this trait whenever you'd create a `struct file_operations`. +/// File descriptors may be used from multiple threads (or processes) +/// concurrently, so your type must be `Sync`. pub trait FileOperations: Sync + Sized { - /// A container for the actual `file_operations` value. This will always be: - /// ``` - /// const VTABLE: linux_kernel_module::chrdev::FileOperationsVtable = - /// linux_kernel_module::chrdev::FileOperationsVtable::new::(); - /// ``` - const VTABLE: FileOperationsVtable; - /// Creates a new instance of this file. Corresponds to the `open` function /// pointer in `struct file_operations`. fn open() -> KernelResult; -} -pub trait Read { /// Reads data from this file to userspace. Corresponds to the `read` /// function pointer in `struct file_operations`. - fn read(&self, file: &File, buf: &mut UserSlicePtrWriter, offset: u64) -> KernelResult<()>; -} + const READ: ReadFn = None; -pub trait Write { /// Writes data from userspace o this file. Corresponds to the `write` /// function pointer in `struct file_operations`. - fn write(&self, buf: &mut UserSlicePtrReader, offset: u64) -> KernelResult<()>; -} + const WRITE: WriteFn = None; -pub trait Seek { /// Changes the position of the file. Corresponds to the `llseek` function /// pointer in `struct file_operations`. - fn seek(&self, file: &File, offset: SeekFrom) -> KernelResult; + const SEEK: SeekFn = None; } diff --git a/src/lib.rs b/src/lib.rs index 51852886..f4fa3581 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![no_std] -#![feature(allocator_api, alloc_error_handler, const_fn, const_raw_ptr_deref)] +#![feature(allocator_api, alloc_error_handler, const_raw_ptr_deref)] extern crate alloc; diff --git a/tests/chrdev/src/lib.rs b/tests/chrdev/src/lib.rs index be818bc0..71aa3333 100644 --- a/tests/chrdev/src/lib.rs +++ b/tests/chrdev/src/lib.rs @@ -10,55 +10,42 @@ use linux_kernel_module::{self, cstr}; struct CycleFile; impl linux_kernel_module::file_operations::FileOperations for CycleFile { - const VTABLE: linux_kernel_module::file_operations::FileOperationsVtable = - linux_kernel_module::file_operations::FileOperationsVtable::builder::() - .read() - .build(); - fn open() -> linux_kernel_module::KernelResult { Ok(CycleFile) } -} -impl linux_kernel_module::file_operations::Read for CycleFile { - fn read( - &self, - _file: &linux_kernel_module::file_operations::File, - buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter, - offset: u64, - ) -> linux_kernel_module::KernelResult<()> { - for c in b"123456789" - .iter() - .cycle() - .skip((offset % 9) as _) - .take(buf.len()) - { - buf.write(&[*c])?; - } - Ok(()) - } + + const READ: linux_kernel_module::file_operations::ReadFn = Some( + |_this: &Self, + _file: &linux_kernel_module::file_operations::File, + buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter, + offset: u64| + -> linux_kernel_module::KernelResult<()> { + for c in b"123456789" + .iter() + .cycle() + .skip((offset % 9) as _) + .take(buf.len()) + { + buf.write(&[*c])?; + } + Ok(()) + }, + ); } struct SeekFile; impl linux_kernel_module::file_operations::FileOperations for SeekFile { - const VTABLE: linux_kernel_module::file_operations::FileOperationsVtable = - linux_kernel_module::file_operations::FileOperationsVtable::builder::() - .seek() - .build(); - fn open() -> linux_kernel_module::KernelResult { Ok(SeekFile) } -} -impl linux_kernel_module::file_operations::Seek for SeekFile { - fn seek( - &self, - _file: &linux_kernel_module::file_operations::File, - _offset: linux_kernel_module::file_operations::SeekFrom, - ) -> linux_kernel_module::KernelResult { - Ok(1234) - } + const SEEK: linux_kernel_module::file_operations::SeekFn = Some( + |_this: &Self, + _file: &linux_kernel_module::file_operations::File, + _offset: linux_kernel_module::file_operations::SeekFrom| + -> linux_kernel_module::KernelResult { Ok(1234) }, + ); } struct WriteFile { @@ -66,42 +53,34 @@ struct WriteFile { } impl linux_kernel_module::file_operations::FileOperations for WriteFile { - const VTABLE: linux_kernel_module::file_operations::FileOperationsVtable = - linux_kernel_module::file_operations::FileOperationsVtable::builder::() - .read() - .write() - .build(); - fn open() -> linux_kernel_module::KernelResult { Ok(WriteFile { written: AtomicUsize::new(0), }) } -} - -impl linux_kernel_module::file_operations::Read for WriteFile { - fn read( - &self, - _file: &linux_kernel_module::file_operations::File, - buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter, - _offset: u64, - ) -> linux_kernel_module::KernelResult<()> { - let val = self.written.load(Ordering::SeqCst).to_string(); - buf.write(val.as_bytes())?; - Ok(()) - } -} -impl linux_kernel_module::file_operations::Write for WriteFile { - fn write( - &self, - buf: &mut linux_kernel_module::user_ptr::UserSlicePtrReader, - _offset: u64, - ) -> linux_kernel_module::KernelResult<()> { - let data = buf.read_all()?; - self.written.fetch_add(data.len(), Ordering::SeqCst); - Ok(()) - } + const READ: linux_kernel_module::file_operations::ReadFn = Some( + |this: &Self, + _file: &linux_kernel_module::file_operations::File, + buf: &mut linux_kernel_module::user_ptr::UserSlicePtrWriter, + _offset: u64| + -> linux_kernel_module::KernelResult<()> { + let val = this.written.load(Ordering::SeqCst).to_string(); + buf.write(val.as_bytes())?; + Ok(()) + }, + ); + + const WRITE: linux_kernel_module::file_operations::WriteFn = Some( + |this: &Self, + buf: &mut linux_kernel_module::user_ptr::UserSlicePtrReader, + _offset: u64| + -> linux_kernel_module::KernelResult<()> { + let data = buf.read_all()?; + this.written.fetch_add(data.len(), Ordering::SeqCst); + Ok(()) + }, + ); } struct ChrdevTestModule {