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