Skip to content
This repository has been archived by the owner on Mar 7, 2021. It is now read-only.

Commit

Permalink
Change the FileOperations API to reduce the use of nightly features (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
alex authored Aug 30, 2020
1 parent 3ad884e commit 079f100
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 180 deletions.
7 changes: 4 additions & 3 deletions src/chrdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@ pub fn builder(name: &'static CStr, minors: Range<u16>) -> KernelResult<Builder>
pub struct Builder {
name: &'static CStr,
minors: Range<u16>,
file_ops: Vec<&'static file_operations::FileOperationsVtable>,
file_ops: Vec<&'static bindings::file_operations>,
}

impl Builder {
pub fn register_device<T: file_operations::FileOperations>(mut self) -> 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::<T>::VTABLE);
self
}

Expand All @@ -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 {
Expand Down
189 changes: 80 additions & 109 deletions src/file_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ pub enum SeekFrom {
Current(i64),
}

pub struct FileOperationsVtable(pub(crate) bindings::file_operations);

unsafe extern "C" fn open_callback<T: FileOperations>(
_inode: *mut bindings::inode,
file: *mut bindings::file,
Expand All @@ -53,7 +51,7 @@ unsafe extern "C" fn open_callback<T: FileOperations>(
0
}

unsafe extern "C" fn read_callback<T: Read>(
unsafe extern "C" fn read_callback<T: FileOperations>(
file: *mut bindings::file,
buf: *mut c_types::c_char,
len: c_types::c_size_t,
Expand All @@ -70,7 +68,8 @@ unsafe extern "C" fn read_callback<T: Read>(
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();
Expand All @@ -80,7 +79,7 @@ unsafe extern "C" fn read_callback<T: Read>(
}
}

unsafe extern "C" fn write_callback<T: Write>(
unsafe extern "C" fn write_callback<T: FileOperations>(
file: *mut bindings::file,
buf: *const c_types::c_char,
len: c_types::c_size_t,
Expand All @@ -97,7 +96,8 @@ unsafe extern "C" fn write_callback<T: Write>(
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();
Expand All @@ -116,7 +116,7 @@ unsafe extern "C" fn release_callback<T: FileOperations>(
0
}

unsafe extern "C" fn llseek_callback<T: Seek>(
unsafe extern "C" fn llseek_callback<T: FileOperations>(
file: *mut bindings::file,
offset: bindings::loff_t,
whence: c_types::c_int,
Expand All @@ -131,130 +131,101 @@ unsafe extern "C" fn llseek_callback<T: Seek>(
_ => 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<T: FileOperations>() -> FileOperationsVtableBuilder<T> {
FileOperationsVtableBuilder(
bindings::file_operations {
open: Some(open_callback::<T>),
release: Some(release_callback::<T>),

#[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<T>(bindings::file_operations, marker::PhantomData<T>);
pub(crate) struct FileOperationsVtable<T>(marker::PhantomData<T>);

impl<T> FileOperationsVtableBuilder<T> {
pub const fn build(self) -> FileOperationsVtable {
FileOperationsVtable(self.0)
}
}

impl<T: Read> FileOperationsVtableBuilder<T> {
pub const fn read(mut self) -> FileOperationsVtableBuilder<T> {
self.0.read = Some(read_callback::<T>);
self
}
}
impl<T: FileOperations> FileOperationsVtable<T> {
pub(crate) const VTABLE: bindings::file_operations = bindings::file_operations {
open: Some(open_callback::<T>),
release: Some(release_callback::<T>),
read: if let Some(_) = T::READ {
Some(read_callback::<T>)
} else {
None
},
write: if let Some(_) = T::WRITE {
Some(write_callback::<T>)
} else {
None
},
llseek: if let Some(_) = T::SEEK {
Some(llseek_callback::<T>)
} else {
None
},

impl<T: Write> FileOperationsVtableBuilder<T> {
pub const fn write(mut self) -> FileOperationsVtableBuilder<T> {
self.0.write = Some(write_callback::<T>);
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<T: Seek> FileOperationsVtableBuilder<T> {
pub const fn seek(mut self) -> FileOperationsVtableBuilder<T> {
self.0.llseek = Some(llseek_callback::<T>);
self
}
}
pub type ReadFn<T> = Option<fn(&T, &File, &mut UserSlicePtrWriter, u64) -> KernelResult<()>>;
pub type WriteFn<T> = Option<fn(&T, &mut UserSlicePtrReader, u64) -> KernelResult<()>>;
pub type SeekFn<T> = Option<fn(&T, &File, SeekFrom) -> KernelResult<u64>>;

/// `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::<Self>();
/// ```
const VTABLE: FileOperationsVtable;

/// Creates a new instance of this file. Corresponds to the `open` function
/// pointer in `struct file_operations`.
fn open() -> KernelResult<Self>;
}

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<Self> = 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<Self> = 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<u64>;
const SEEK: SeekFn<Self> = None;
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down
Loading

0 comments on commit 079f100

Please sign in to comment.