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