xref: /linux/rust/kernel/debugfs/file_ops.rs (revision 839dc1d15b9ba5318ed145b20efffcfa91c02a3d)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (C) 2025 Google LLC.
3 
4 use super::{Reader, Writer};
5 use crate::prelude::*;
6 use crate::seq_file::SeqFile;
7 use crate::seq_print;
8 use crate::uaccess::UserSlice;
9 use core::fmt::{Display, Formatter, Result};
10 use core::marker::PhantomData;
11 
12 #[cfg(CONFIG_DEBUG_FS)]
13 use core::ops::Deref;
14 
15 /// # Invariant
16 ///
17 /// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed
18 /// off an inode which has a pointer to a `T` in its private data that is safe to convert
19 /// into a reference.
20 pub(super) struct FileOps<T> {
21     #[cfg(CONFIG_DEBUG_FS)]
22     operations: bindings::file_operations,
23     #[cfg(CONFIG_DEBUG_FS)]
24     mode: u16,
25     _phantom: PhantomData<T>,
26 }
27 
28 impl<T> FileOps<T> {
29     /// # Safety
30     ///
31     /// The caller asserts that the provided `operations` is safe to use for a file whose
32     /// inode has a pointer to `T` in its private data that is safe to convert into a reference.
33     const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self {
34         Self {
35             #[cfg(CONFIG_DEBUG_FS)]
36             operations,
37             #[cfg(CONFIG_DEBUG_FS)]
38             mode,
39             _phantom: PhantomData,
40         }
41     }
42 
43     #[cfg(CONFIG_DEBUG_FS)]
44     pub(crate) const fn mode(&self) -> u16 {
45         self.mode
46     }
47 }
48 
49 #[cfg(CONFIG_DEBUG_FS)]
50 impl<T> Deref for FileOps<T> {
51     type Target = bindings::file_operations;
52 
53     fn deref(&self) -> &Self::Target {
54         &self.operations
55     }
56 }
57 
58 struct WriterAdapter<T>(T);
59 
60 impl<'a, T: Writer> Display for WriterAdapter<&'a T> {
61     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
62         self.0.write(f)
63     }
64 }
65 
66 /// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.
67 ///
68 /// # Safety
69 ///
70 /// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode`
71 ///   and will not have any unique references alias it during the call.
72 /// * `file` must point to a live, not-yet-initialized file object.
73 unsafe extern "C" fn writer_open<T: Writer + Sync>(
74     inode: *mut bindings::inode,
75     file: *mut bindings::file,
76 ) -> c_int {
77     // SAFETY: The caller ensures that `inode` is a valid pointer.
78     let data = unsafe { (*inode).i_private };
79     // SAFETY:
80     // * `file` is acceptable by caller precondition.
81     // * `print_act` will be called on a `seq_file` with private data set to the third argument,
82     //   so we meet its safety requirements.
83     // * The `data` pointer passed in the third argument is a valid `T` pointer that outlives
84     //   this call by caller preconditions.
85     unsafe { bindings::single_open(file, Some(writer_act::<T>), data) }
86 }
87 
88 /// Prints private data stashed in a seq_file to that seq file.
89 ///
90 /// # Safety
91 ///
92 /// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may
93 /// not have any unique references alias it during the call.
94 unsafe extern "C" fn writer_act<T: Writer + Sync>(
95     seq: *mut bindings::seq_file,
96     _: *mut c_void,
97 ) -> c_int {
98     // SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and
99     // there are not and will not be any unique references until we are done.
100     let data = unsafe { &*((*seq).private.cast::<T>()) };
101     // SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift
102     // it.
103     let seq_file = unsafe { SeqFile::from_raw(seq) };
104     seq_print!(seq_file, "{}", WriterAdapter(data));
105     0
106 }
107 
108 // Work around lack of generic const items.
109 pub(crate) trait ReadFile<T> {
110     const FILE_OPS: FileOps<T>;
111 }
112 
113 impl<T: Writer + Sync> ReadFile<T> for T {
114     const FILE_OPS: FileOps<T> = {
115         let operations = bindings::file_operations {
116             read: Some(bindings::seq_read),
117             llseek: Some(bindings::seq_lseek),
118             release: Some(bindings::single_release),
119             open: Some(writer_open::<Self>),
120             // SAFETY: `file_operations` supports zeroes in all fields.
121             ..unsafe { core::mem::zeroed() }
122         };
123         // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`.
124         // `open`'s only requirement beyond what is provided to all open functions is that the
125         // inode's data pointer must point to a `T` that will outlive it, which matches the
126         // `FileOps` requirements.
127         unsafe { FileOps::new(operations, 0o400) }
128     };
129 }
130 
131 fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize {
132     let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader();
133 
134     if let Err(e) = data.read_from_slice(&mut reader) {
135         return e.to_errno() as isize;
136     }
137 
138     count as isize
139 }
140 
141 /// # Safety
142 ///
143 /// `file` must be a valid pointer to a `file` struct.
144 /// The `private_data` of the file must contain a valid pointer to a `seq_file` whose
145 /// `private` data in turn points to a `T` that implements `Reader`.
146 /// `buf` must be a valid user-space buffer.
147 pub(crate) unsafe extern "C" fn write<T: Reader + Sync>(
148     file: *mut bindings::file,
149     buf: *const c_char,
150     count: usize,
151     _ppos: *mut bindings::loff_t,
152 ) -> isize {
153     // SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`.
154     let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) };
155     // SAFETY: By caller precondition, this pointer is live and points to a value of type `T`.
156     let data = unsafe { &*(seq.private as *const T) };
157     read(data, buf, count)
158 }
159 
160 // A trait to get the file operations for a type.
161 pub(crate) trait ReadWriteFile<T> {
162     const FILE_OPS: FileOps<T>;
163 }
164 
165 impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {
166     const FILE_OPS: FileOps<T> = {
167         let operations = bindings::file_operations {
168             open: Some(writer_open::<T>),
169             read: Some(bindings::seq_read),
170             write: Some(write::<T>),
171             llseek: Some(bindings::seq_lseek),
172             release: Some(bindings::single_release),
173             // SAFETY: `file_operations` supports zeroes in all fields.
174             ..unsafe { core::mem::zeroed() }
175         };
176         // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`
177         // and `write`.
178         // `writer_open`'s only requirement beyond what is provided to all open functions is that
179         // the inode's data pointer must point to a `T` that will outlive it, which matches the
180         // `FileOps` requirements.
181         // `write` only requires that the file's private data pointer points to `seq_file`
182         // which points to a `T` that will outlive it, which matches what `writer_open`
183         // provides.
184         unsafe { FileOps::new(operations, 0o600) }
185     };
186 }
187 
188 /// # Safety
189 ///
190 /// `inode` must be a valid pointer to an `inode` struct.
191 /// `file` must be a valid pointer to a `file` struct.
192 unsafe extern "C" fn write_only_open(
193     inode: *mut bindings::inode,
194     file: *mut bindings::file,
195 ) -> c_int {
196     // SAFETY: The caller ensures that `inode` and `file` are valid pointers.
197     unsafe { (*file).private_data = (*inode).i_private };
198     0
199 }
200 
201 /// # Safety
202 ///
203 /// * `file` must be a valid pointer to a `file` struct.
204 /// * The `private_data` of the file must contain a valid pointer to a `T` that implements
205 ///   `Reader`.
206 /// * `buf` must be a valid user-space buffer.
207 pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>(
208     file: *mut bindings::file,
209     buf: *const c_char,
210     count: usize,
211     _ppos: *mut bindings::loff_t,
212 ) -> isize {
213     // SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a
214     // valid pointer to `T`.
215     let data = unsafe { &*((*file).private_data as *const T) };
216     read(data, buf, count)
217 }
218 
219 pub(crate) trait WriteFile<T> {
220     const FILE_OPS: FileOps<T>;
221 }
222 
223 impl<T: Reader + Sync> WriteFile<T> for T {
224     const FILE_OPS: FileOps<T> = {
225         let operations = bindings::file_operations {
226             open: Some(write_only_open),
227             write: Some(write_only_write::<T>),
228             llseek: Some(bindings::noop_llseek),
229             // SAFETY: `file_operations` supports zeroes in all fields.
230             ..unsafe { core::mem::zeroed() }
231         };
232         // SAFETY:
233         // * `write_only_open` populates the file private data with the inode private data
234         // * `write_only_write`'s only requirement is that the private data of the file point to
235         //   a `T` and be legal to convert to a shared reference, which `write_only_open`
236         //   satisfies.
237         unsafe { FileOps::new(operations, 0o200) }
238     };
239 }
240