15e40b591SMatthew Maurer // SPDX-License-Identifier: GPL-2.0 25e40b591SMatthew Maurer // Copyright (C) 2025 Google LLC. 35e40b591SMatthew Maurer 4839dc1d1SMatthew Maurer use super::{Reader, Writer}; 5*40ecc494SMatthew Maurer use crate::debugfs::callback_adapters::Adapter; 65e40b591SMatthew Maurer use crate::prelude::*; 75e40b591SMatthew Maurer use crate::seq_file::SeqFile; 85e40b591SMatthew Maurer use crate::seq_print; 9839dc1d1SMatthew Maurer use crate::uaccess::UserSlice; 105e40b591SMatthew Maurer use core::fmt::{Display, Formatter, Result}; 115e40b591SMatthew Maurer use core::marker::PhantomData; 125e40b591SMatthew Maurer 135e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 145e40b591SMatthew Maurer use core::ops::Deref; 155e40b591SMatthew Maurer 165e40b591SMatthew Maurer /// # Invariant 175e40b591SMatthew Maurer /// 185e40b591SMatthew Maurer /// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed 195e40b591SMatthew Maurer /// off an inode which has a pointer to a `T` in its private data that is safe to convert 205e40b591SMatthew Maurer /// into a reference. 215e40b591SMatthew Maurer pub(super) struct FileOps<T> { 225e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 235e40b591SMatthew Maurer operations: bindings::file_operations, 245e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 255e40b591SMatthew Maurer mode: u16, 265e40b591SMatthew Maurer _phantom: PhantomData<T>, 275e40b591SMatthew Maurer } 285e40b591SMatthew Maurer 295e40b591SMatthew Maurer impl<T> FileOps<T> { 305e40b591SMatthew Maurer /// # Safety 315e40b591SMatthew Maurer /// 325e40b591SMatthew Maurer /// The caller asserts that the provided `operations` is safe to use for a file whose 335e40b591SMatthew Maurer /// inode has a pointer to `T` in its private data that is safe to convert into a reference. 345e40b591SMatthew Maurer const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self { 355e40b591SMatthew Maurer Self { 365e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 375e40b591SMatthew Maurer operations, 385e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 395e40b591SMatthew Maurer mode, 405e40b591SMatthew Maurer _phantom: PhantomData, 415e40b591SMatthew Maurer } 425e40b591SMatthew Maurer } 435e40b591SMatthew Maurer 445e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 455e40b591SMatthew Maurer pub(crate) const fn mode(&self) -> u16 { 465e40b591SMatthew Maurer self.mode 475e40b591SMatthew Maurer } 485e40b591SMatthew Maurer } 495e40b591SMatthew Maurer 50*40ecc494SMatthew Maurer impl<T: Adapter> FileOps<T> { 51*40ecc494SMatthew Maurer pub(super) const fn adapt(&self) -> &FileOps<T::Inner> { 52*40ecc494SMatthew Maurer // SAFETY: `Adapter` asserts that `T` can be legally cast to `T::Inner`. 53*40ecc494SMatthew Maurer unsafe { core::mem::transmute(self) } 54*40ecc494SMatthew Maurer } 55*40ecc494SMatthew Maurer } 56*40ecc494SMatthew Maurer 575e40b591SMatthew Maurer #[cfg(CONFIG_DEBUG_FS)] 585e40b591SMatthew Maurer impl<T> Deref for FileOps<T> { 595e40b591SMatthew Maurer type Target = bindings::file_operations; 605e40b591SMatthew Maurer 615e40b591SMatthew Maurer fn deref(&self) -> &Self::Target { 625e40b591SMatthew Maurer &self.operations 635e40b591SMatthew Maurer } 645e40b591SMatthew Maurer } 655e40b591SMatthew Maurer 665e40b591SMatthew Maurer struct WriterAdapter<T>(T); 675e40b591SMatthew Maurer 685e40b591SMatthew Maurer impl<'a, T: Writer> Display for WriterAdapter<&'a T> { 695e40b591SMatthew Maurer fn fmt(&self, f: &mut Formatter<'_>) -> Result { 705e40b591SMatthew Maurer self.0.write(f) 715e40b591SMatthew Maurer } 725e40b591SMatthew Maurer } 735e40b591SMatthew Maurer 745e40b591SMatthew Maurer /// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`. 755e40b591SMatthew Maurer /// 765e40b591SMatthew Maurer /// # Safety 775e40b591SMatthew Maurer /// 785e40b591SMatthew Maurer /// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode` 795e40b591SMatthew Maurer /// and will not have any unique references alias it during the call. 805e40b591SMatthew Maurer /// * `file` must point to a live, not-yet-initialized file object. 815e40b591SMatthew Maurer unsafe extern "C" fn writer_open<T: Writer + Sync>( 825e40b591SMatthew Maurer inode: *mut bindings::inode, 835e40b591SMatthew Maurer file: *mut bindings::file, 845e40b591SMatthew Maurer ) -> c_int { 855e40b591SMatthew Maurer // SAFETY: The caller ensures that `inode` is a valid pointer. 865e40b591SMatthew Maurer let data = unsafe { (*inode).i_private }; 875e40b591SMatthew Maurer // SAFETY: 885e40b591SMatthew Maurer // * `file` is acceptable by caller precondition. 895e40b591SMatthew Maurer // * `print_act` will be called on a `seq_file` with private data set to the third argument, 905e40b591SMatthew Maurer // so we meet its safety requirements. 915e40b591SMatthew Maurer // * The `data` pointer passed in the third argument is a valid `T` pointer that outlives 925e40b591SMatthew Maurer // this call by caller preconditions. 935e40b591SMatthew Maurer unsafe { bindings::single_open(file, Some(writer_act::<T>), data) } 945e40b591SMatthew Maurer } 955e40b591SMatthew Maurer 965e40b591SMatthew Maurer /// Prints private data stashed in a seq_file to that seq file. 975e40b591SMatthew Maurer /// 985e40b591SMatthew Maurer /// # Safety 995e40b591SMatthew Maurer /// 1005e40b591SMatthew Maurer /// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may 1015e40b591SMatthew Maurer /// not have any unique references alias it during the call. 1025e40b591SMatthew Maurer unsafe extern "C" fn writer_act<T: Writer + Sync>( 1035e40b591SMatthew Maurer seq: *mut bindings::seq_file, 1045e40b591SMatthew Maurer _: *mut c_void, 1055e40b591SMatthew Maurer ) -> c_int { 1065e40b591SMatthew Maurer // SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and 1075e40b591SMatthew Maurer // there are not and will not be any unique references until we are done. 1085e40b591SMatthew Maurer let data = unsafe { &*((*seq).private.cast::<T>()) }; 1095e40b591SMatthew Maurer // SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift 1105e40b591SMatthew Maurer // it. 1115e40b591SMatthew Maurer let seq_file = unsafe { SeqFile::from_raw(seq) }; 1125e40b591SMatthew Maurer seq_print!(seq_file, "{}", WriterAdapter(data)); 1135e40b591SMatthew Maurer 0 1145e40b591SMatthew Maurer } 1155e40b591SMatthew Maurer 1165e40b591SMatthew Maurer // Work around lack of generic const items. 1175e40b591SMatthew Maurer pub(crate) trait ReadFile<T> { 1185e40b591SMatthew Maurer const FILE_OPS: FileOps<T>; 1195e40b591SMatthew Maurer } 1205e40b591SMatthew Maurer 1215e40b591SMatthew Maurer impl<T: Writer + Sync> ReadFile<T> for T { 1225e40b591SMatthew Maurer const FILE_OPS: FileOps<T> = { 1235e40b591SMatthew Maurer let operations = bindings::file_operations { 1245e40b591SMatthew Maurer read: Some(bindings::seq_read), 1255e40b591SMatthew Maurer llseek: Some(bindings::seq_lseek), 1265e40b591SMatthew Maurer release: Some(bindings::single_release), 1275e40b591SMatthew Maurer open: Some(writer_open::<Self>), 1285e40b591SMatthew Maurer // SAFETY: `file_operations` supports zeroes in all fields. 1295e40b591SMatthew Maurer ..unsafe { core::mem::zeroed() } 1305e40b591SMatthew Maurer }; 1315e40b591SMatthew Maurer // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`. 1325e40b591SMatthew Maurer // `open`'s only requirement beyond what is provided to all open functions is that the 1335e40b591SMatthew Maurer // inode's data pointer must point to a `T` that will outlive it, which matches the 1345e40b591SMatthew Maurer // `FileOps` requirements. 1355e40b591SMatthew Maurer unsafe { FileOps::new(operations, 0o400) } 1365e40b591SMatthew Maurer }; 1375e40b591SMatthew Maurer } 138839dc1d1SMatthew Maurer 139839dc1d1SMatthew Maurer fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize { 140839dc1d1SMatthew Maurer let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader(); 141839dc1d1SMatthew Maurer 142839dc1d1SMatthew Maurer if let Err(e) = data.read_from_slice(&mut reader) { 143839dc1d1SMatthew Maurer return e.to_errno() as isize; 144839dc1d1SMatthew Maurer } 145839dc1d1SMatthew Maurer 146839dc1d1SMatthew Maurer count as isize 147839dc1d1SMatthew Maurer } 148839dc1d1SMatthew Maurer 149839dc1d1SMatthew Maurer /// # Safety 150839dc1d1SMatthew Maurer /// 151839dc1d1SMatthew Maurer /// `file` must be a valid pointer to a `file` struct. 152839dc1d1SMatthew Maurer /// The `private_data` of the file must contain a valid pointer to a `seq_file` whose 153839dc1d1SMatthew Maurer /// `private` data in turn points to a `T` that implements `Reader`. 154839dc1d1SMatthew Maurer /// `buf` must be a valid user-space buffer. 155839dc1d1SMatthew Maurer pub(crate) unsafe extern "C" fn write<T: Reader + Sync>( 156839dc1d1SMatthew Maurer file: *mut bindings::file, 157839dc1d1SMatthew Maurer buf: *const c_char, 158839dc1d1SMatthew Maurer count: usize, 159839dc1d1SMatthew Maurer _ppos: *mut bindings::loff_t, 160839dc1d1SMatthew Maurer ) -> isize { 161839dc1d1SMatthew Maurer // SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`. 162839dc1d1SMatthew Maurer let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) }; 163839dc1d1SMatthew Maurer // SAFETY: By caller precondition, this pointer is live and points to a value of type `T`. 164839dc1d1SMatthew Maurer let data = unsafe { &*(seq.private as *const T) }; 165839dc1d1SMatthew Maurer read(data, buf, count) 166839dc1d1SMatthew Maurer } 167839dc1d1SMatthew Maurer 168839dc1d1SMatthew Maurer // A trait to get the file operations for a type. 169839dc1d1SMatthew Maurer pub(crate) trait ReadWriteFile<T> { 170839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T>; 171839dc1d1SMatthew Maurer } 172839dc1d1SMatthew Maurer 173839dc1d1SMatthew Maurer impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T { 174839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T> = { 175839dc1d1SMatthew Maurer let operations = bindings::file_operations { 176839dc1d1SMatthew Maurer open: Some(writer_open::<T>), 177839dc1d1SMatthew Maurer read: Some(bindings::seq_read), 178839dc1d1SMatthew Maurer write: Some(write::<T>), 179839dc1d1SMatthew Maurer llseek: Some(bindings::seq_lseek), 180839dc1d1SMatthew Maurer release: Some(bindings::single_release), 181839dc1d1SMatthew Maurer // SAFETY: `file_operations` supports zeroes in all fields. 182839dc1d1SMatthew Maurer ..unsafe { core::mem::zeroed() } 183839dc1d1SMatthew Maurer }; 184839dc1d1SMatthew Maurer // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open` 185839dc1d1SMatthew Maurer // and `write`. 186839dc1d1SMatthew Maurer // `writer_open`'s only requirement beyond what is provided to all open functions is that 187839dc1d1SMatthew Maurer // the inode's data pointer must point to a `T` that will outlive it, which matches the 188839dc1d1SMatthew Maurer // `FileOps` requirements. 189839dc1d1SMatthew Maurer // `write` only requires that the file's private data pointer points to `seq_file` 190839dc1d1SMatthew Maurer // which points to a `T` that will outlive it, which matches what `writer_open` 191839dc1d1SMatthew Maurer // provides. 192839dc1d1SMatthew Maurer unsafe { FileOps::new(operations, 0o600) } 193839dc1d1SMatthew Maurer }; 194839dc1d1SMatthew Maurer } 195839dc1d1SMatthew Maurer 196839dc1d1SMatthew Maurer /// # Safety 197839dc1d1SMatthew Maurer /// 198839dc1d1SMatthew Maurer /// `inode` must be a valid pointer to an `inode` struct. 199839dc1d1SMatthew Maurer /// `file` must be a valid pointer to a `file` struct. 200839dc1d1SMatthew Maurer unsafe extern "C" fn write_only_open( 201839dc1d1SMatthew Maurer inode: *mut bindings::inode, 202839dc1d1SMatthew Maurer file: *mut bindings::file, 203839dc1d1SMatthew Maurer ) -> c_int { 204839dc1d1SMatthew Maurer // SAFETY: The caller ensures that `inode` and `file` are valid pointers. 205839dc1d1SMatthew Maurer unsafe { (*file).private_data = (*inode).i_private }; 206839dc1d1SMatthew Maurer 0 207839dc1d1SMatthew Maurer } 208839dc1d1SMatthew Maurer 209839dc1d1SMatthew Maurer /// # Safety 210839dc1d1SMatthew Maurer /// 211839dc1d1SMatthew Maurer /// * `file` must be a valid pointer to a `file` struct. 212839dc1d1SMatthew Maurer /// * The `private_data` of the file must contain a valid pointer to a `T` that implements 213839dc1d1SMatthew Maurer /// `Reader`. 214839dc1d1SMatthew Maurer /// * `buf` must be a valid user-space buffer. 215839dc1d1SMatthew Maurer pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>( 216839dc1d1SMatthew Maurer file: *mut bindings::file, 217839dc1d1SMatthew Maurer buf: *const c_char, 218839dc1d1SMatthew Maurer count: usize, 219839dc1d1SMatthew Maurer _ppos: *mut bindings::loff_t, 220839dc1d1SMatthew Maurer ) -> isize { 221839dc1d1SMatthew Maurer // SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a 222839dc1d1SMatthew Maurer // valid pointer to `T`. 223839dc1d1SMatthew Maurer let data = unsafe { &*((*file).private_data as *const T) }; 224839dc1d1SMatthew Maurer read(data, buf, count) 225839dc1d1SMatthew Maurer } 226839dc1d1SMatthew Maurer 227839dc1d1SMatthew Maurer pub(crate) trait WriteFile<T> { 228839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T>; 229839dc1d1SMatthew Maurer } 230839dc1d1SMatthew Maurer 231839dc1d1SMatthew Maurer impl<T: Reader + Sync> WriteFile<T> for T { 232839dc1d1SMatthew Maurer const FILE_OPS: FileOps<T> = { 233839dc1d1SMatthew Maurer let operations = bindings::file_operations { 234839dc1d1SMatthew Maurer open: Some(write_only_open), 235839dc1d1SMatthew Maurer write: Some(write_only_write::<T>), 236839dc1d1SMatthew Maurer llseek: Some(bindings::noop_llseek), 237839dc1d1SMatthew Maurer // SAFETY: `file_operations` supports zeroes in all fields. 238839dc1d1SMatthew Maurer ..unsafe { core::mem::zeroed() } 239839dc1d1SMatthew Maurer }; 240839dc1d1SMatthew Maurer // SAFETY: 241839dc1d1SMatthew Maurer // * `write_only_open` populates the file private data with the inode private data 242839dc1d1SMatthew Maurer // * `write_only_write`'s only requirement is that the private data of the file point to 243839dc1d1SMatthew Maurer // a `T` and be legal to convert to a shared reference, which `write_only_open` 244839dc1d1SMatthew Maurer // satisfies. 245839dc1d1SMatthew Maurer unsafe { FileOps::new(operations, 0o200) } 246839dc1d1SMatthew Maurer }; 247839dc1d1SMatthew Maurer } 248