1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2025 Google LLC. 3 4 use super::{BinaryReader, BinaryWriter, Reader, Writer}; 5 use crate::debugfs::callback_adapters::Adapter; 6 use crate::fmt; 7 use crate::fs::file; 8 use crate::prelude::*; 9 use crate::seq_file::SeqFile; 10 use crate::seq_print; 11 use crate::uaccess::UserSlice; 12 use core::marker::PhantomData; 13 14 #[cfg(CONFIG_DEBUG_FS)] 15 use core::ops::Deref; 16 17 /// # Invariant 18 /// 19 /// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed 20 /// off an inode which has a pointer to a `T` in its private data that is safe to convert 21 /// into a reference. 22 pub(super) struct FileOps<T> { 23 #[cfg(CONFIG_DEBUG_FS)] 24 operations: bindings::file_operations, 25 #[cfg(CONFIG_DEBUG_FS)] 26 mode: u16, 27 _phantom: PhantomData<T>, 28 } 29 30 impl<T> FileOps<T> { 31 /// # Safety 32 /// 33 /// The caller asserts that the provided `operations` is safe to use for a file whose 34 /// inode has a pointer to `T` in its private data that is safe to convert into a reference. 35 const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self { 36 Self { 37 #[cfg(CONFIG_DEBUG_FS)] 38 operations, 39 #[cfg(CONFIG_DEBUG_FS)] 40 mode, 41 _phantom: PhantomData, 42 } 43 } 44 45 #[cfg(CONFIG_DEBUG_FS)] 46 pub(crate) const fn mode(&self) -> u16 { 47 self.mode 48 } 49 } 50 51 impl<T: Adapter> FileOps<T> { 52 pub(super) const fn adapt(&self) -> &FileOps<T::Inner> { 53 // SAFETY: `Adapter` asserts that `T` can be legally cast to `T::Inner`. 54 unsafe { core::mem::transmute(self) } 55 } 56 } 57 58 #[cfg(CONFIG_DEBUG_FS)] 59 impl<T> Deref for FileOps<T> { 60 type Target = bindings::file_operations; 61 62 fn deref(&self) -> &Self::Target { 63 &self.operations 64 } 65 } 66 67 struct WriterAdapter<T>(T); 68 69 impl<'a, T: Writer> fmt::Display for WriterAdapter<&'a T> { 70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 71 self.0.write(f) 72 } 73 } 74 75 /// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`. 76 /// 77 /// # Safety 78 /// 79 /// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode` 80 /// and will not have any unique references alias it during the call. 81 /// * `file` must point to a live, not-yet-initialized file object. 82 unsafe extern "C" fn writer_open<T: Writer + Sync>( 83 inode: *mut bindings::inode, 84 file: *mut bindings::file, 85 ) -> c_int { 86 // SAFETY: The caller ensures that `inode` is a valid pointer. 87 let data = unsafe { (*inode).i_private }; 88 // SAFETY: 89 // * `file` is acceptable by caller precondition. 90 // * `print_act` will be called on a `seq_file` with private data set to the third argument, 91 // so we meet its safety requirements. 92 // * The `data` pointer passed in the third argument is a valid `T` pointer that outlives 93 // this call by caller preconditions. 94 unsafe { bindings::single_open(file, Some(writer_act::<T>), data) } 95 } 96 97 /// Prints private data stashed in a seq_file to that seq file. 98 /// 99 /// # Safety 100 /// 101 /// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may 102 /// not have any unique references alias it during the call. 103 unsafe extern "C" fn writer_act<T: Writer + Sync>( 104 seq: *mut bindings::seq_file, 105 _: *mut c_void, 106 ) -> c_int { 107 // SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and 108 // there are not and will not be any unique references until we are done. 109 let data = unsafe { &*((*seq).private.cast::<T>()) }; 110 // SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift 111 // it. 112 let seq_file = unsafe { SeqFile::from_raw(seq) }; 113 seq_print!(seq_file, "{}", WriterAdapter(data)); 114 0 115 } 116 117 // Work around lack of generic const items. 118 pub(crate) trait ReadFile<T> { 119 const FILE_OPS: FileOps<T>; 120 } 121 122 impl<T: Writer + Sync> ReadFile<T> for T { 123 const FILE_OPS: FileOps<T> = { 124 let operations = bindings::file_operations { 125 read: Some(bindings::seq_read), 126 llseek: Some(bindings::seq_lseek), 127 release: Some(bindings::single_release), 128 open: Some(writer_open::<Self>), 129 // SAFETY: `file_operations` supports zeroes in all fields. 130 ..unsafe { core::mem::zeroed() } 131 }; 132 // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`. 133 // `open`'s only requirement beyond what is provided to all open functions is that the 134 // inode's data pointer must point to a `T` that will outlive it, which matches the 135 // `FileOps` requirements. 136 unsafe { FileOps::new(operations, 0o400) } 137 }; 138 } 139 140 fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize { 141 let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader(); 142 143 if let Err(e) = data.read_from_slice(&mut reader) { 144 return e.to_errno() as isize; 145 } 146 147 count as isize 148 } 149 150 /// # Safety 151 /// 152 /// `file` must be a valid pointer to a `file` struct. 153 /// The `private_data` of the file must contain a valid pointer to a `seq_file` whose 154 /// `private` data in turn points to a `T` that implements `Reader`. 155 /// `buf` must be a valid user-space buffer. 156 pub(crate) unsafe extern "C" fn write<T: Reader + Sync>( 157 file: *mut bindings::file, 158 buf: *const c_char, 159 count: usize, 160 _ppos: *mut bindings::loff_t, 161 ) -> isize { 162 // SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`. 163 let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) }; 164 // SAFETY: By caller precondition, this pointer is live and points to a value of type `T`. 165 let data = unsafe { &*(seq.private as *const T) }; 166 read(data, buf, count) 167 } 168 169 // A trait to get the file operations for a type. 170 pub(crate) trait ReadWriteFile<T> { 171 const FILE_OPS: FileOps<T>; 172 } 173 174 impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T { 175 const FILE_OPS: FileOps<T> = { 176 let operations = bindings::file_operations { 177 open: Some(writer_open::<T>), 178 read: Some(bindings::seq_read), 179 write: Some(write::<T>), 180 llseek: Some(bindings::seq_lseek), 181 release: Some(bindings::single_release), 182 // SAFETY: `file_operations` supports zeroes in all fields. 183 ..unsafe { core::mem::zeroed() } 184 }; 185 // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open` 186 // and `write`. 187 // `writer_open`'s only requirement beyond what is provided to all open functions is that 188 // the inode's data pointer must point to a `T` that will outlive it, which matches the 189 // `FileOps` requirements. 190 // `write` only requires that the file's private data pointer points to `seq_file` 191 // which points to a `T` that will outlive it, which matches what `writer_open` 192 // provides. 193 unsafe { FileOps::new(operations, 0o600) } 194 }; 195 } 196 197 /// # Safety 198 /// 199 /// `inode` must be a valid pointer to an `inode` struct. 200 /// `file` must be a valid pointer to a `file` struct. 201 unsafe extern "C" fn write_only_open( 202 inode: *mut bindings::inode, 203 file: *mut bindings::file, 204 ) -> c_int { 205 // SAFETY: The caller ensures that `inode` and `file` are valid pointers. 206 unsafe { (*file).private_data = (*inode).i_private }; 207 0 208 } 209 210 /// # Safety 211 /// 212 /// * `file` must be a valid pointer to a `file` struct. 213 /// * The `private_data` of the file must contain a valid pointer to a `T` that implements 214 /// `Reader`. 215 /// * `buf` must be a valid user-space buffer. 216 pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>( 217 file: *mut bindings::file, 218 buf: *const c_char, 219 count: usize, 220 _ppos: *mut bindings::loff_t, 221 ) -> isize { 222 // SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a 223 // valid pointer to `T`. 224 let data = unsafe { &*((*file).private_data as *const T) }; 225 read(data, buf, count) 226 } 227 228 pub(crate) trait WriteFile<T> { 229 const FILE_OPS: FileOps<T>; 230 } 231 232 impl<T: Reader + Sync> WriteFile<T> for T { 233 const FILE_OPS: FileOps<T> = { 234 let operations = bindings::file_operations { 235 open: Some(write_only_open), 236 write: Some(write_only_write::<T>), 237 llseek: Some(bindings::noop_llseek), 238 // SAFETY: `file_operations` supports zeroes in all fields. 239 ..unsafe { core::mem::zeroed() } 240 }; 241 // SAFETY: 242 // * `write_only_open` populates the file private data with the inode private data 243 // * `write_only_write`'s only requirement is that the private data of the file point to 244 // a `T` and be legal to convert to a shared reference, which `write_only_open` 245 // satisfies. 246 unsafe { FileOps::new(operations, 0o200) } 247 }; 248 } 249 250 extern "C" fn blob_read<T: BinaryWriter>( 251 file: *mut bindings::file, 252 buf: *mut c_char, 253 count: usize, 254 ppos: *mut bindings::loff_t, 255 ) -> isize { 256 // SAFETY: 257 // - `file` is a valid pointer to a `struct file`. 258 // - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`. 259 let this = unsafe { &*((*file).private_data.cast::<T>()) }; 260 261 // SAFETY: 262 // - `ppos` is a valid `file::Offset` pointer. 263 // - We have exclusive access to `ppos`. 264 let pos: &mut file::Offset = unsafe { &mut *ppos }; 265 266 let mut writer = UserSlice::new(UserPtr::from_ptr(buf.cast()), count).writer(); 267 268 let ret = || -> Result<isize> { 269 let written = this.write_to_slice(&mut writer, pos)?; 270 271 Ok(written.try_into()?) 272 }(); 273 274 match ret { 275 Ok(n) => n, 276 Err(e) => e.to_errno() as isize, 277 } 278 } 279 280 /// Representation of [`FileOps`] for read only binary files. 281 pub(crate) trait BinaryReadFile<T> { 282 const FILE_OPS: FileOps<T>; 283 } 284 285 impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T { 286 const FILE_OPS: FileOps<T> = { 287 let operations = bindings::file_operations { 288 read: Some(blob_read::<T>), 289 llseek: Some(bindings::default_llseek), 290 open: Some(bindings::simple_open), 291 // SAFETY: `file_operations` supports zeroes in all fields. 292 ..unsafe { core::mem::zeroed() } 293 }; 294 295 // SAFETY: 296 // - The private data of `struct inode` does always contain a pointer to a valid `T`. 297 // - `simple_open()` stores the `struct inode`'s private data in the private data of the 298 // corresponding `struct file`. 299 // - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data. 300 // - `default_llseek()` does not access the `struct file`'s private data. 301 unsafe { FileOps::new(operations, 0o400) } 302 }; 303 } 304 305 extern "C" fn blob_write<T: BinaryReader>( 306 file: *mut bindings::file, 307 buf: *const c_char, 308 count: usize, 309 ppos: *mut bindings::loff_t, 310 ) -> isize { 311 // SAFETY: 312 // - `file` is a valid pointer to a `struct file`. 313 // - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`. 314 let this = unsafe { &*((*file).private_data.cast::<T>()) }; 315 316 // SAFETY: 317 // - `ppos` is a valid `file::Offset` pointer. 318 // - We have exclusive access to `ppos`. 319 let pos: &mut file::Offset = unsafe { &mut *ppos }; 320 321 let mut reader = UserSlice::new(UserPtr::from_ptr(buf.cast_mut().cast()), count).reader(); 322 323 let ret = || -> Result<isize> { 324 let read = this.read_from_slice(&mut reader, pos)?; 325 326 Ok(read.try_into()?) 327 }(); 328 329 match ret { 330 Ok(n) => n, 331 Err(e) => e.to_errno() as isize, 332 } 333 } 334 335 /// Representation of [`FileOps`] for write only binary files. 336 pub(crate) trait BinaryWriteFile<T> { 337 const FILE_OPS: FileOps<T>; 338 } 339 340 impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T { 341 const FILE_OPS: FileOps<T> = { 342 let operations = bindings::file_operations { 343 write: Some(blob_write::<T>), 344 llseek: Some(bindings::default_llseek), 345 open: Some(bindings::simple_open), 346 // SAFETY: `file_operations` supports zeroes in all fields. 347 ..unsafe { core::mem::zeroed() } 348 }; 349 350 // SAFETY: 351 // - The private data of `struct inode` does always contain a pointer to a valid `T`. 352 // - `simple_open()` stores the `struct inode`'s private data in the private data of the 353 // corresponding `struct file`. 354 // - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data. 355 // - `default_llseek()` does not access the `struct file`'s private data. 356 unsafe { FileOps::new(operations, 0o200) } 357 }; 358 } 359 360 /// Representation of [`FileOps`] for read/write binary files. 361 pub(crate) trait BinaryReadWriteFile<T> { 362 const FILE_OPS: FileOps<T>; 363 } 364 365 impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T { 366 const FILE_OPS: FileOps<T> = { 367 let operations = bindings::file_operations { 368 read: Some(blob_read::<T>), 369 write: Some(blob_write::<T>), 370 llseek: Some(bindings::default_llseek), 371 open: Some(bindings::simple_open), 372 // SAFETY: `file_operations` supports zeroes in all fields. 373 ..unsafe { core::mem::zeroed() } 374 }; 375 376 // SAFETY: 377 // - The private data of `struct inode` does always contain a pointer to a valid `T`. 378 // - `simple_open()` stores the `struct inode`'s private data in the private data of the 379 // corresponding `struct file`. 380 // - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data. 381 // - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data. 382 // - `default_llseek()` does not access the `struct file`'s private data. 383 unsafe { FileOps::new(operations, 0o600) } 384 }; 385 } 386