xref: /linux/rust/kernel/debugfs/file_ops.rs (revision 5e40b591cb46c0379d5406fa5548c9b2a3801353)
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