// SPDX-License-Identifier: GPL-2.01// Copyright (C) 2025 Google LLC.23use super::{Reader, Writer};4use crate::debugfs::callback_adapters::Adapter;5use crate::prelude::*;6use crate::seq_file::SeqFile;7use crate::seq_print;8use crate::uaccess::UserSlice;9use core::fmt::{Display, Formatter, Result};10use core::marker::PhantomData;1112#[cfg(CONFIG_DEBUG_FS)]13use core::ops::Deref;1415/// # Invariant16///17/// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed18/// off an inode which has a pointer to a `T` in its private data that is safe to convert19/// into a reference.20pub(super) struct FileOps<T> {21#[cfg(CONFIG_DEBUG_FS)]22operations: bindings::file_operations,23#[cfg(CONFIG_DEBUG_FS)]24mode: u16,25_phantom: PhantomData<T>,26}2728impl<T> FileOps<T> {29/// # Safety30///31/// The caller asserts that the provided `operations` is safe to use for a file whose32/// inode has a pointer to `T` in its private data that is safe to convert into a reference.33const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self {34Self {35#[cfg(CONFIG_DEBUG_FS)]36operations,37#[cfg(CONFIG_DEBUG_FS)]38mode,39_phantom: PhantomData,40}41}4243#[cfg(CONFIG_DEBUG_FS)]44pub(crate) const fn mode(&self) -> u16 {45self.mode46}47}4849impl<T: Adapter> FileOps<T> {50pub(super) const fn adapt(&self) -> &FileOps<T::Inner> {51// SAFETY: `Adapter` asserts that `T` can be legally cast to `T::Inner`.52unsafe { core::mem::transmute(self) }53}54}5556#[cfg(CONFIG_DEBUG_FS)]57impl<T> Deref for FileOps<T> {58type Target = bindings::file_operations;5960fn deref(&self) -> &Self::Target {61&self.operations62}63}6465struct WriterAdapter<T>(T);6667impl<'a, T: Writer> Display for WriterAdapter<&'a T> {68fn fmt(&self, f: &mut Formatter<'_>) -> Result {69self.0.write(f)70}71}7273/// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.74///75/// # Safety76///77/// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode`78/// and will not have any unique references alias it during the call.79/// * `file` must point to a live, not-yet-initialized file object.80unsafe extern "C" fn writer_open<T: Writer + Sync>(81inode: *mut bindings::inode,82file: *mut bindings::file,83) -> c_int {84// SAFETY: The caller ensures that `inode` is a valid pointer.85let data = unsafe { (*inode).i_private };86// SAFETY:87// * `file` is acceptable by caller precondition.88// * `print_act` will be called on a `seq_file` with private data set to the third argument,89// so we meet its safety requirements.90// * The `data` pointer passed in the third argument is a valid `T` pointer that outlives91// this call by caller preconditions.92unsafe { bindings::single_open(file, Some(writer_act::<T>), data) }93}9495/// Prints private data stashed in a seq_file to that seq file.96///97/// # Safety98///99/// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may100/// not have any unique references alias it during the call.101unsafe extern "C" fn writer_act<T: Writer + Sync>(102seq: *mut bindings::seq_file,103_: *mut c_void,104) -> c_int {105// SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and106// there are not and will not be any unique references until we are done.107let data = unsafe { &*((*seq).private.cast::<T>()) };108// SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift109// it.110let seq_file = unsafe { SeqFile::from_raw(seq) };111seq_print!(seq_file, "{}", WriterAdapter(data));1120113}114115// Work around lack of generic const items.116pub(crate) trait ReadFile<T> {117const FILE_OPS: FileOps<T>;118}119120impl<T: Writer + Sync> ReadFile<T> for T {121const FILE_OPS: FileOps<T> = {122let operations = bindings::file_operations {123read: Some(bindings::seq_read),124llseek: Some(bindings::seq_lseek),125release: Some(bindings::single_release),126open: Some(writer_open::<Self>),127// SAFETY: `file_operations` supports zeroes in all fields.128..unsafe { core::mem::zeroed() }129};130// SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`.131// `open`'s only requirement beyond what is provided to all open functions is that the132// inode's data pointer must point to a `T` that will outlive it, which matches the133// `FileOps` requirements.134unsafe { FileOps::new(operations, 0o400) }135};136}137138fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize {139let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader();140141if let Err(e) = data.read_from_slice(&mut reader) {142return e.to_errno() as isize;143}144145count as isize146}147148/// # Safety149///150/// `file` must be a valid pointer to a `file` struct.151/// The `private_data` of the file must contain a valid pointer to a `seq_file` whose152/// `private` data in turn points to a `T` that implements `Reader`.153/// `buf` must be a valid user-space buffer.154pub(crate) unsafe extern "C" fn write<T: Reader + Sync>(155file: *mut bindings::file,156buf: *const c_char,157count: usize,158_ppos: *mut bindings::loff_t,159) -> isize {160// SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`.161let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) };162// SAFETY: By caller precondition, this pointer is live and points to a value of type `T`.163let data = unsafe { &*(seq.private as *const T) };164read(data, buf, count)165}166167// A trait to get the file operations for a type.168pub(crate) trait ReadWriteFile<T> {169const FILE_OPS: FileOps<T>;170}171172impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {173const FILE_OPS: FileOps<T> = {174let operations = bindings::file_operations {175open: Some(writer_open::<T>),176read: Some(bindings::seq_read),177write: Some(write::<T>),178llseek: Some(bindings::seq_lseek),179release: Some(bindings::single_release),180// SAFETY: `file_operations` supports zeroes in all fields.181..unsafe { core::mem::zeroed() }182};183// SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`184// and `write`.185// `writer_open`'s only requirement beyond what is provided to all open functions is that186// the inode's data pointer must point to a `T` that will outlive it, which matches the187// `FileOps` requirements.188// `write` only requires that the file's private data pointer points to `seq_file`189// which points to a `T` that will outlive it, which matches what `writer_open`190// provides.191unsafe { FileOps::new(operations, 0o600) }192};193}194195/// # Safety196///197/// `inode` must be a valid pointer to an `inode` struct.198/// `file` must be a valid pointer to a `file` struct.199unsafe extern "C" fn write_only_open(200inode: *mut bindings::inode,201file: *mut bindings::file,202) -> c_int {203// SAFETY: The caller ensures that `inode` and `file` are valid pointers.204unsafe { (*file).private_data = (*inode).i_private };2050206}207208/// # Safety209///210/// * `file` must be a valid pointer to a `file` struct.211/// * The `private_data` of the file must contain a valid pointer to a `T` that implements212/// `Reader`.213/// * `buf` must be a valid user-space buffer.214pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>(215file: *mut bindings::file,216buf: *const c_char,217count: usize,218_ppos: *mut bindings::loff_t,219) -> isize {220// SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a221// valid pointer to `T`.222let data = unsafe { &*((*file).private_data as *const T) };223read(data, buf, count)224}225226pub(crate) trait WriteFile<T> {227const FILE_OPS: FileOps<T>;228}229230impl<T: Reader + Sync> WriteFile<T> for T {231const FILE_OPS: FileOps<T> = {232let operations = bindings::file_operations {233open: Some(write_only_open),234write: Some(write_only_write::<T>),235llseek: Some(bindings::noop_llseek),236// SAFETY: `file_operations` supports zeroes in all fields.237..unsafe { core::mem::zeroed() }238};239// SAFETY:240// * `write_only_open` populates the file private data with the inode private data241// * `write_only_write`'s only requirement is that the private data of the file point to242// a `T` and be legal to convert to a shared reference, which `write_only_open`243// satisfies.244unsafe { FileOps::new(operations, 0o200) }245};246}247248249