Skip to content

Commit 54a3c8e

Browse files
committed
rust: port file::Operations and deps from rust branch
Port file::Operations and its dependencies from the `rust` branch. This is a trimmed version from what can be found in `rust` since I only need a subset of the functionality. Signed-off-by: Fabien Parent <[email protected]>
1 parent 033f05f commit 54a3c8e

File tree

7 files changed

+761
-2
lines changed

7 files changed

+761
-2
lines changed

rust/helpers.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,24 @@ void rust_helper_init_task_work(struct callback_head *twork,
251251
}
252252
EXPORT_SYMBOL_GPL(rust_helper_init_task_work);
253253

254+
unsigned long rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n)
255+
{
256+
return copy_from_user(to, from, n);
257+
}
258+
EXPORT_SYMBOL_GPL(rust_helper_copy_from_user);
259+
260+
unsigned long rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n)
261+
{
262+
return copy_to_user(to, from, n);
263+
}
264+
EXPORT_SYMBOL_GPL(rust_helper_copy_to_user);
265+
266+
unsigned long rust_helper_clear_user(void __user *to, unsigned long n)
267+
{
268+
return clear_user(to, n);
269+
}
270+
EXPORT_SYMBOL_GPL(rust_helper_clear_user);
271+
254272
/*
255273
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
256274
* use it in contexts where Rust expects a `usize` like slice (array) indices.

rust/kernel/file.rs

Lines changed: 293 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
use crate::{
99
bindings,
1010
cred::Credential,
11-
error::{code::*, Error, Result},
12-
types::{ARef, AlwaysRefCounted, NotThreadSafe, Opaque},
11+
error::{code::*, from_result, Error, Result},
12+
io_buffer::{IoBufferReader, IoBufferWriter},
13+
macros::vtable,
14+
types::{ARef, AlwaysRefCounted, ForeignOwnable, NotThreadSafe, Opaque},
15+
user_ptr::{UserSlicePtr, UserSlicePtrReader, UserSlicePtrWriter},
1316
};
1417
use alloc::boxed::Box;
1518
use core::{alloc::AllocError, marker::PhantomData, mem, ptr};
@@ -429,3 +432,291 @@ impl core::fmt::Debug for BadFdError {
429432
f.pad("EBADF")
430433
}
431434
}
435+
436+
/// Equivalent to [`std::io::SeekFrom`].
437+
///
438+
/// [`std::io::SeekFrom`]: https://doc.rust-lang.org/std/io/enum.SeekFrom.html
439+
pub enum SeekFrom {
440+
/// Equivalent to C's `SEEK_SET`.
441+
Start(u64),
442+
443+
/// Equivalent to C's `SEEK_END`.
444+
End(i64),
445+
446+
/// Equivalent to C's `SEEK_CUR`.
447+
Current(i64),
448+
}
449+
450+
pub(crate) struct OperationsVtable<A, T>(PhantomData<A>, PhantomData<T>);
451+
452+
impl<A: OpenAdapter<T::OpenData>, T: Operations> OperationsVtable<A, T> {
453+
/// Called by the VFS when an inode should be opened.
454+
///
455+
/// Calls `T::open` on the returned value of `A::convert`.
456+
///
457+
/// # Safety
458+
///
459+
/// The returned value of `A::convert` must be a valid non-null pointer and
460+
/// `T:open` must return a valid non-null pointer on an `Ok` result.
461+
unsafe extern "C" fn open_callback(
462+
inode: *mut bindings::inode,
463+
file: *mut bindings::file,
464+
) -> core::ffi::c_int {
465+
from_result(|| {
466+
// SAFETY: `A::convert` must return a valid non-null pointer that
467+
// should point to data in the inode or file that lives longer
468+
// than the following use of `T::open`.
469+
let arg = unsafe { A::convert(inode, file) };
470+
// SAFETY: The C contract guarantees that `file` is valid. Additionally,
471+
// `fileref` never outlives this function, so it is guaranteed to be
472+
// valid.
473+
let fileref = unsafe { File::from_ptr(file) };
474+
// SAFETY: `arg` was previously returned by `A::convert` and must
475+
// be a valid non-null pointer.
476+
let ptr = T::open(unsafe { &*arg }, fileref)?.into_foreign();
477+
// SAFETY: The C contract guarantees that `private_data` is available
478+
// for implementers of the file operations (no other C code accesses
479+
// it), so we know that there are no concurrent threads/CPUs accessing
480+
// it (it's not visible to any other Rust code).
481+
unsafe { (*file).private_data = ptr as *mut core::ffi::c_void };
482+
Ok(0)
483+
})
484+
}
485+
486+
unsafe extern "C" fn read_callback(
487+
file: *mut bindings::file,
488+
buf: *mut core::ffi::c_char,
489+
len: core::ffi::c_size_t,
490+
offset: *mut bindings::loff_t,
491+
) -> core::ffi::c_ssize_t {
492+
from_result(|| {
493+
let mut data =
494+
unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).writer() };
495+
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
496+
// `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
497+
// `release` callback, which the C API guarantees that will be called only when all
498+
// references to `file` have been released, so we know it can't be called while this
499+
// function is running.
500+
let f = unsafe { T::Data::borrow((*file).private_data) };
501+
// No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
502+
// See <https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113>.
503+
let read = T::read(
504+
f,
505+
unsafe { File::from_ptr(file) },
506+
&mut data,
507+
unsafe { *offset }.try_into()?,
508+
)?;
509+
unsafe { (*offset) += bindings::loff_t::try_from(read).unwrap() };
510+
Ok(read as _)
511+
})
512+
}
513+
514+
unsafe extern "C" fn write_callback(
515+
file: *mut bindings::file,
516+
buf: *const core::ffi::c_char,
517+
len: core::ffi::c_size_t,
518+
offset: *mut bindings::loff_t,
519+
) -> core::ffi::c_ssize_t {
520+
from_result(|| {
521+
let mut data =
522+
unsafe { UserSlicePtr::new(buf as *mut core::ffi::c_void, len).reader() };
523+
// SAFETY: `private_data` was initialised by `open_callback` with a value returned by
524+
// `T::Data::into_foreign`. `T::Data::from_foreign` is only called by the
525+
// `release` callback, which the C API guarantees that will be called only when all
526+
// references to `file` have been released, so we know it can't be called while this
527+
// function is running.
528+
let f = unsafe { T::Data::borrow((*file).private_data) };
529+
// No `FMODE_UNSIGNED_OFFSET` support, so `offset` must be in [0, 2^63).
530+
// See <https://github.com/fishinabarrel/linux-kernel-module-rust/pull/113>.
531+
let written = T::write(
532+
f,
533+
unsafe { File::from_ptr(file) },
534+
&mut data,
535+
unsafe { *offset }.try_into()?,
536+
)?;
537+
unsafe { (*offset) += bindings::loff_t::try_from(written).unwrap() };
538+
Ok(written as _)
539+
})
540+
}
541+
542+
unsafe extern "C" fn release_callback(
543+
_inode: *mut bindings::inode,
544+
file: *mut bindings::file,
545+
) -> core::ffi::c_int {
546+
let ptr = mem::replace(unsafe { &mut (*file).private_data }, ptr::null_mut());
547+
T::release(unsafe { T::Data::from_foreign(ptr as _) }, unsafe {
548+
File::from_ptr(file)
549+
});
550+
0
551+
}
552+
553+
const VTABLE: bindings::file_operations = bindings::file_operations {
554+
open: Some(Self::open_callback),
555+
release: Some(Self::release_callback),
556+
read: if T::HAS_READ {
557+
Some(Self::read_callback)
558+
} else {
559+
None
560+
},
561+
write: if T::HAS_WRITE {
562+
Some(Self::write_callback)
563+
} else {
564+
None
565+
},
566+
llseek: None,
567+
check_flags: None,
568+
compat_ioctl: None,
569+
copy_file_range: None,
570+
fallocate: None,
571+
fadvise: None,
572+
fasync: None,
573+
flock: None,
574+
flush: None,
575+
fsync: None,
576+
get_unmapped_area: None,
577+
iterate_shared: None,
578+
iopoll: None,
579+
lock: None,
580+
mmap: None,
581+
mmap_supported_flags: 0,
582+
owner: ptr::null_mut(),
583+
poll: None,
584+
read_iter: None,
585+
remap_file_range: None,
586+
setlease: None,
587+
show_fdinfo: None,
588+
splice_eof: None,
589+
splice_read: None,
590+
splice_write: None,
591+
unlocked_ioctl: None,
592+
uring_cmd: None,
593+
uring_cmd_iopoll: None,
594+
write_iter: None,
595+
};
596+
597+
/// Builds an instance of [`struct file_operations`].
598+
///
599+
/// # Safety
600+
///
601+
/// The caller must ensure that the adapter is compatible with the way the device is registered.
602+
pub(crate) const unsafe fn build() -> &'static bindings::file_operations {
603+
&Self::VTABLE
604+
}
605+
}
606+
607+
/// Allows the handling of ioctls defined with the `_IO`, `_IOR`, `_IOW`, and `_IOWR` macros.
608+
///
609+
/// For each macro, there is a handler function that takes the appropriate types as arguments.
610+
pub trait IoctlHandler: Sync {
611+
/// The type of the first argument to each associated function.
612+
type Target<'a>;
613+
614+
/// Handles ioctls defined with the `_IO` macro, that is, with no buffer as argument.
615+
fn pure(_this: Self::Target<'_>, _file: &File, _cmd: u32, _arg: usize) -> Result<i32> {
616+
Err(EINVAL)
617+
}
618+
619+
/// Handles ioctls defined with the `_IOR` macro, that is, with an output buffer provided as
620+
/// argument.
621+
fn read(
622+
_this: Self::Target<'_>,
623+
_file: &File,
624+
_cmd: u32,
625+
_writer: &mut UserSlicePtrWriter,
626+
) -> Result<i32> {
627+
Err(EINVAL)
628+
}
629+
630+
/// Handles ioctls defined with the `_IOW` macro, that is, with an input buffer provided as
631+
/// argument.
632+
fn write(
633+
_this: Self::Target<'_>,
634+
_file: &File,
635+
_cmd: u32,
636+
_reader: &mut UserSlicePtrReader,
637+
) -> Result<i32> {
638+
Err(EINVAL)
639+
}
640+
641+
/// Handles ioctls defined with the `_IOWR` macro, that is, with a buffer for both input and
642+
/// output provided as argument.
643+
fn read_write(
644+
_this: Self::Target<'_>,
645+
_file: &File,
646+
_cmd: u32,
647+
_data: UserSlicePtr,
648+
) -> Result<i32> {
649+
Err(EINVAL)
650+
}
651+
}
652+
653+
/// Trait for extracting file open arguments from kernel data structures.
654+
///
655+
/// This is meant to be implemented by registration managers.
656+
pub trait OpenAdapter<T: Sync> {
657+
/// Converts untyped data stored in [`struct inode`] and [`struct file`] (when [`struct
658+
/// file_operations::open`] is called) into the given type. For example, for `miscdev`
659+
/// devices, a pointer to the registered [`struct miscdev`] is stored in [`struct
660+
/// file::private_data`].
661+
///
662+
/// # Safety
663+
///
664+
/// This function must be called only when [`struct file_operations::open`] is being called for
665+
/// a file that was registered by the implementer. The returned pointer must be valid and
666+
/// not-null.
667+
unsafe fn convert(_inode: *mut bindings::inode, _file: *mut bindings::file) -> *const T;
668+
}
669+
670+
/// Corresponds to the kernel's `struct file_operations`.
671+
///
672+
/// You implement this trait whenever you would create a `struct file_operations`.
673+
///
674+
/// File descriptors may be used from multiple threads/processes concurrently, so your type must be
675+
/// [`Sync`]. It must also be [`Send`] because [`Operations::release`] will be called from the
676+
/// thread that decrements that associated file's refcount to zero.
677+
#[vtable]
678+
pub trait Operations {
679+
/// The type of the context data returned by [`Operations::open`] and made available to
680+
/// other methods.
681+
type Data: ForeignOwnable + Send + Sync = ();
682+
683+
/// The type of the context data passed to [`Operations::open`].
684+
type OpenData: Sync = ();
685+
686+
/// Creates a new instance of this file.
687+
///
688+
/// Corresponds to the `open` function pointer in `struct file_operations`.
689+
fn open(context: &Self::OpenData, file: &File) -> Result<Self::Data>;
690+
691+
/// Cleans up after the last reference to the file goes away.
692+
///
693+
/// Note that context data is moved, so it will be freed automatically unless the
694+
/// implementation moves it elsewhere.
695+
///
696+
/// Corresponds to the `release` function pointer in `struct file_operations`.
697+
fn release(_data: Self::Data, _file: &File) {}
698+
699+
/// Reads data from this file to the caller's buffer.
700+
///
701+
/// Corresponds to the `read` and `read_iter` function pointers in `struct file_operations`.
702+
fn read(
703+
_data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
704+
_file: &File,
705+
_writer: &mut impl IoBufferWriter,
706+
_offset: u64,
707+
) -> Result<usize> {
708+
Err(EINVAL)
709+
}
710+
711+
/// Writes data from the caller's buffer to this file.
712+
///
713+
/// Corresponds to the `write` and `write_iter` function pointers in `struct file_operations`.
714+
fn write(
715+
_data: <Self::Data as ForeignOwnable>::Borrowed<'_>,
716+
_file: &File,
717+
_reader: &mut impl IoBufferReader,
718+
_offset: u64,
719+
) -> Result<usize> {
720+
Err(EINVAL)
721+
}
722+
}

0 commit comments

Comments
 (0)