1*5e40b591SMatthew Maurer // SPDX-License-Identifier: GPL-2.0 2*5e40b591SMatthew Maurer // Copyright (C) 2025 Google LLC. 3*5e40b591SMatthew Maurer 4*5e40b591SMatthew Maurer use super::Writer; 5*5e40b591SMatthew Maurer use crate::prelude::*; 6*5e40b591SMatthew Maurer use crate::seq_file::SeqFile; 7*5e40b591SMatthew Maurer use crate::seq_print; 8*5e40b591SMatthew Maurer use core::fmt::{Display, Formatter, Result}; 9*5e40b591SMatthew Maurer use core::marker::PhantomData; 10*5e40b591SMatthew Maurer 11*5e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 12*5e40b591SMatthew Maurer use core::ops::Deref; 13*5e40b591SMatthew Maurer 14*5e40b591SMatthew Maurer /// # Invariant 15*5e40b591SMatthew Maurer /// 16*5e40b591SMatthew Maurer /// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed 17*5e40b591SMatthew Maurer /// off an inode which has a pointer to a `T` in its private data that is safe to convert 18*5e40b591SMatthew Maurer /// into a reference. 19*5e40b591SMatthew Maurer pub(super) struct FileOps<T> { 20*5e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 21*5e40b591SMatthew Maurer operations: bindings::file_operations, 22*5e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 23*5e40b591SMatthew Maurer mode: u16, 24*5e40b591SMatthew Maurer _phantom: PhantomData<T>, 25*5e40b591SMatthew Maurer } 26*5e40b591SMatthew Maurer 27*5e40b591SMatthew Maurer impl<T> FileOps<T> { 28*5e40b591SMatthew Maurer /// # Safety 29*5e40b591SMatthew Maurer /// 30*5e40b591SMatthew Maurer /// The caller asserts that the provided `operations` is safe to use for a file whose 31*5e40b591SMatthew Maurer /// inode has a pointer to `T` in its private data that is safe to convert into a reference. 32*5e40b591SMatthew Maurer const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self { 33*5e40b591SMatthew Maurer Self { 34*5e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 35*5e40b591SMatthew Maurer operations, 36*5e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 37*5e40b591SMatthew Maurer mode, 38*5e40b591SMatthew Maurer _phantom: PhantomData, 39*5e40b591SMatthew Maurer } 40*5e40b591SMatthew Maurer } 41*5e40b591SMatthew Maurer 42*5e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 43*5e40b591SMatthew Maurer pub(crate) const fn mode(&self) -> u16 { 44*5e40b591SMatthew Maurer self.mode 45*5e40b591SMatthew Maurer } 46*5e40b591SMatthew Maurer } 47*5e40b591SMatthew Maurer 48*5e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 49*5e40b591SMatthew Maurer impl<T> Deref for FileOps<T> { 50*5e40b591SMatthew Maurer type Target = bindings::file_operations; 51*5e40b591SMatthew Maurer 52*5e40b591SMatthew Maurer fn deref(&self) -> &Self::Target { 53*5e40b591SMatthew Maurer &self.operations 54*5e40b591SMatthew Maurer } 55*5e40b591SMatthew Maurer } 56*5e40b591SMatthew Maurer 57*5e40b591SMatthew Maurer struct WriterAdapter<T>(T); 58*5e40b591SMatthew Maurer 59*5e40b591SMatthew Maurer impl<'a, T: Writer> Display for WriterAdapter<&'a T> { 60*5e40b591SMatthew Maurer fn fmt(&self, f: &mut Formatter<'_>) -> Result { 61*5e40b591SMatthew Maurer self.0.write(f) 62*5e40b591SMatthew Maurer } 63*5e40b591SMatthew Maurer } 64*5e40b591SMatthew Maurer 65*5e40b591SMatthew Maurer /// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`. 66*5e40b591SMatthew Maurer /// 67*5e40b591SMatthew Maurer /// # Safety 68*5e40b591SMatthew Maurer /// 69*5e40b591SMatthew Maurer /// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode` 70*5e40b591SMatthew Maurer /// and will not have any unique references alias it during the call. 71*5e40b591SMatthew Maurer /// * `file` must point to a live, not-yet-initialized file object. 72*5e40b591SMatthew Maurer unsafe extern "C" fn writer_open<T: Writer + Sync>( 73*5e40b591SMatthew Maurer inode: *mut bindings::inode, 74*5e40b591SMatthew Maurer file: *mut bindings::file, 75*5e40b591SMatthew Maurer ) -> c_int { 76*5e40b591SMatthew Maurer // SAFETY: The caller ensures that `inode` is a valid pointer. 77*5e40b591SMatthew Maurer let data = unsafe { (*inode).i_private }; 78*5e40b591SMatthew Maurer // SAFETY: 79*5e40b591SMatthew Maurer // * `file` is acceptable by caller precondition. 80*5e40b591SMatthew Maurer // * `print_act` will be called on a `seq_file` with private data set to the third argument, 81*5e40b591SMatthew Maurer // so we meet its safety requirements. 82*5e40b591SMatthew Maurer // * The `data` pointer passed in the third argument is a valid `T` pointer that outlives 83*5e40b591SMatthew Maurer // this call by caller preconditions. 84*5e40b591SMatthew Maurer unsafe { bindings::single_open(file, Some(writer_act::<T>), data) } 85*5e40b591SMatthew Maurer } 86*5e40b591SMatthew Maurer 87*5e40b591SMatthew Maurer /// Prints private data stashed in a seq_file to that seq file. 88*5e40b591SMatthew Maurer /// 89*5e40b591SMatthew Maurer /// # Safety 90*5e40b591SMatthew Maurer /// 91*5e40b591SMatthew Maurer /// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may 92*5e40b591SMatthew Maurer /// not have any unique references alias it during the call. 93*5e40b591SMatthew Maurer unsafe extern "C" fn writer_act<T: Writer + Sync>( 94*5e40b591SMatthew Maurer seq: *mut bindings::seq_file, 95*5e40b591SMatthew Maurer _: *mut c_void, 96*5e40b591SMatthew Maurer ) -> c_int { 97*5e40b591SMatthew Maurer // SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and 98*5e40b591SMatthew Maurer // there are not and will not be any unique references until we are done. 99*5e40b591SMatthew Maurer let data = unsafe { &*((*seq).private.cast::<T>()) }; 100*5e40b591SMatthew Maurer // SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift 101*5e40b591SMatthew Maurer // it. 102*5e40b591SMatthew Maurer let seq_file = unsafe { SeqFile::from_raw(seq) }; 103*5e40b591SMatthew Maurer seq_print!(seq_file, "{}", WriterAdapter(data)); 104*5e40b591SMatthew Maurer 0 105*5e40b591SMatthew Maurer } 106*5e40b591SMatthew Maurer 107*5e40b591SMatthew Maurer // Work around lack of generic const items. 108*5e40b591SMatthew Maurer pub(crate) trait ReadFile<T> { 109*5e40b591SMatthew Maurer const FILE_OPS: FileOps<T>; 110*5e40b591SMatthew Maurer } 111*5e40b591SMatthew Maurer 112*5e40b591SMatthew Maurer impl<T: Writer + Sync> ReadFile<T> for T { 113*5e40b591SMatthew Maurer const FILE_OPS: FileOps<T> = { 114*5e40b591SMatthew Maurer let operations = bindings::file_operations { 115*5e40b591SMatthew Maurer read: Some(bindings::seq_read), 116*5e40b591SMatthew Maurer llseek: Some(bindings::seq_lseek), 117*5e40b591SMatthew Maurer release: Some(bindings::single_release), 118*5e40b591SMatthew Maurer open: Some(writer_open::<Self>), 119*5e40b591SMatthew Maurer // SAFETY: `file_operations` supports zeroes in all fields. 120*5e40b591SMatthew Maurer ..unsafe { core::mem::zeroed() } 121*5e40b591SMatthew Maurer }; 122*5e40b591SMatthew Maurer // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`. 123*5e40b591SMatthew Maurer // `open`'s only requirement beyond what is provided to all open functions is that the 124*5e40b591SMatthew Maurer // inode's data pointer must point to a `T` that will outlive it, which matches the 125*5e40b591SMatthew Maurer // `FileOps` requirements. 126*5e40b591SMatthew Maurer unsafe { FileOps::new(operations, 0o400) } 127*5e40b591SMatthew Maurer }; 128*5e40b591SMatthew Maurer } 129