|
8 | 8 | use crate::{
|
9 | 9 | bindings,
|
10 | 10 | 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}, |
13 | 16 | };
|
14 | 17 | use alloc::boxed::Box;
|
15 | 18 | use core::{alloc::AllocError, marker::PhantomData, mem, ptr};
|
@@ -429,3 +432,291 @@ impl core::fmt::Debug for BadFdError {
|
429 | 432 | f.pad("EBADF")
|
430 | 433 | }
|
431 | 434 | }
|
| 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