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 // SAFETY: `file_operations` supports zeroes in all fields. 139 ..unsafe { core::mem::zeroed() } 140 }; 141 // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`. 142 // `open`'s only requirement beyond what is provided to all open functions is that the 143 // inode's data pointer must point to a `T` that will outlive it, which matches the 144 // `FileOps` requirements. 145 unsafe { FileOps::new(operations, 0o400) } 146 }; 147 } 148 149 fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize { 150 let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader(); 151 152 if let Err(e) = data.read_from_slice(&mut reader) { 153 return e.to_errno() as isize; 154 } 155 156 count as isize 157 } 158 159 /// # Safety 160 /// 161 /// `file` must be a valid pointer to a `file` struct. 162 /// The `private_data` of the file must contain a valid pointer to a `seq_file` whose 163 /// `private` data in turn points to a `T` that implements `Reader`. 164 /// `buf` must be a valid user-space buffer. 165 pub(crate) unsafe extern "C" fn write<T: Reader + Sync>( 166 file: *mut bindings::file, 167 buf: *const c_char, 168 count: usize, 169 _ppos: *mut bindings::loff_t, 170 ) -> isize { 171 // SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`. 172 let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) }; 173 // SAFETY: By caller precondition, this pointer is live and points to a value of type `T`. 174 let data = unsafe { &*(seq.private as *const T) }; 175 read(data, buf, count) 176 } 177 178 // A trait to get the file operations for a type. 179 pub(crate) trait ReadWriteFile<T> { 180 const FILE_OPS: FileOps<T>; 181 } 182 183 impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T { 184 const FILE_OPS: FileOps<T> = { 185 let operations = bindings::file_operations { 186 open: Some(writer_open::<T>), 187 read: Some(bindings::seq_read), 188 write: Some(write::<T>), 189 llseek: Some(bindings::seq_lseek), 190 release: Some(bindings::single_release), 191 // SAFETY: `file_operations` supports zeroes in all fields. 192 ..unsafe { core::mem::zeroed() } 193 }; 194 // SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open` 195 // and `write`. 196 // `writer_open`'s only requirement beyond what is provided to all open functions is that 197 // the inode's data pointer must point to a `T` that will outlive it, which matches the 198 // `FileOps` requirements. 199 // `write` only requires that the file's private data pointer points to `seq_file` 200 // which points to a `T` that will outlive it, which matches what `writer_open` 201 // provides. 202 unsafe { FileOps::new(operations, 0o600) } 203 }; 204 } 205 206 /// # Safety 207 /// 208 /// `inode` must be a valid pointer to an `inode` struct. 209 /// `file` must be a valid pointer to a `file` struct. 210 unsafe extern "C" fn write_only_open( 211 inode: *mut bindings::inode, 212 file: *mut bindings::file, 213 ) -> c_int { 214 // SAFETY: The caller ensures that `inode` and `file` are valid pointers. 215 unsafe { (*file).private_data = (*inode).i_private }; 216 0 217 } 218 219 /// # Safety 220 /// 221 /// * `file` must be a valid pointer to a `file` struct. 222 /// * The `private_data` of the file must contain a valid pointer to a `T` that implements 223 /// `Reader`. 224 /// * `buf` must be a valid user-space buffer. 225 pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>( 226 file: *mut bindings::file, 227 buf: *const c_char, 228 count: usize, 229 _ppos: *mut bindings::loff_t, 230 ) -> isize { 231 // SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a 232 // valid pointer to `T`. 233 let data = unsafe { &*((*file).private_data as *const T) }; 234 read(data, buf, count) 235 } 236 237 pub(crate) trait WriteFile<T> { 238 const FILE_OPS: FileOps<T>; 239 } 240 241 impl<T: Reader + Sync> WriteFile<T> for T { 242 const FILE_OPS: FileOps<T> = { 243 let operations = bindings::file_operations { 244 open: Some(write_only_open), 245 write: Some(write_only_write::<T>), 246 llseek: Some(bindings::noop_llseek), 247 // SAFETY: `file_operations` supports zeroes in all fields. 248 ..unsafe { core::mem::zeroed() } 249 }; 250 // SAFETY: 251 // * `write_only_open` populates the file private data with the inode private data 252 // * `write_only_write`'s only requirement is that the private data of the file point to 253 // a `T` and be legal to convert to a shared reference, which `write_only_open` 254 // satisfies. 255 unsafe { FileOps::new(operations, 0o200) } 256 }; 257 } 258 259 extern "C" fn blob_read<T: BinaryWriter>( 260 file: *mut bindings::file, 261 buf: *mut c_char, 262 count: usize, 263 ppos: *mut bindings::loff_t, 264 ) -> isize { 265 // SAFETY: 266 // - `file` is a valid pointer to a `struct file`. 267 // - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`. 268 let this = unsafe { &*((*file).private_data.cast::<T>()) }; 269 270 // SAFETY: 271 // - `ppos` is a valid `file::Offset` pointer. 272 // - We have exclusive access to `ppos`. 273 let pos: &mut file::Offset = unsafe { &mut *ppos }; 274 275 let mut writer = UserSlice::new(UserPtr::from_ptr(buf.cast()), count).writer(); 276 277 let ret = || -> Result<isize> { 278 let written = this.write_to_slice(&mut writer, pos)?; 279 280 Ok(written.try_into()?) 281 }(); 282 283 match ret { 284 Ok(n) => n, 285 Err(e) => e.to_errno() as isize, 286 } 287 } 288 289 /// Representation of [`FileOps`] for read only binary files. 290 pub(crate) trait BinaryReadFile<T> { 291 const FILE_OPS: FileOps<T>; 292 } 293 294 impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T { 295 const FILE_OPS: FileOps<T> = { 296 let operations = bindings::file_operations { 297 read: Some(blob_read::<T>), 298 llseek: Some(bindings::default_llseek), 299 open: Some(bindings::simple_open), 300 // SAFETY: `file_operations` supports zeroes in all fields. 301 ..unsafe { core::mem::zeroed() } 302 }; 303 304 // SAFETY: 305 // - The private data of `struct inode` does always contain a pointer to a valid `T`. 306 // - `simple_open()` stores the `struct inode`'s private data in the private data of the 307 // corresponding `struct file`. 308 // - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data. 309 // - `default_llseek()` does not access the `struct file`'s private data. 310 unsafe { FileOps::new(operations, 0o400) } 311 }; 312 } 313 314 extern "C" fn blob_write<T: BinaryReader>( 315 file: *mut bindings::file, 316 buf: *const c_char, 317 count: usize, 318 ppos: *mut bindings::loff_t, 319 ) -> isize { 320 // SAFETY: 321 // - `file` is a valid pointer to a `struct file`. 322 // - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`. 323 let this = unsafe { &*((*file).private_data.cast::<T>()) }; 324 325 // SAFETY: 326 // - `ppos` is a valid `file::Offset` pointer. 327 // - We have exclusive access to `ppos`. 328 let pos: &mut file::Offset = unsafe { &mut *ppos }; 329 330 let mut reader = UserSlice::new(UserPtr::from_ptr(buf.cast_mut().cast()), count).reader(); 331 332 let ret = || -> Result<isize> { 333 let read = this.read_from_slice(&mut reader, pos)?; 334 335 Ok(read.try_into()?) 336 }(); 337 338 match ret { 339 Ok(n) => n, 340 Err(e) => e.to_errno() as isize, 341 } 342 } 343 344 /// Representation of [`FileOps`] for write only binary files. 345 pub(crate) trait BinaryWriteFile<T> { 346 const FILE_OPS: FileOps<T>; 347 } 348 349 impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T { 350 const FILE_OPS: FileOps<T> = { 351 let operations = bindings::file_operations { 352 write: Some(blob_write::<T>), 353 llseek: Some(bindings::default_llseek), 354 open: Some(bindings::simple_open), 355 // SAFETY: `file_operations` supports zeroes in all fields. 356 ..unsafe { core::mem::zeroed() } 357 }; 358 359 // SAFETY: 360 // - The private data of `struct inode` does always contain a pointer to a valid `T`. 361 // - `simple_open()` stores the `struct inode`'s private data in the private data of the 362 // corresponding `struct file`. 363 // - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data. 364 // - `default_llseek()` does not access the `struct file`'s private data. 365 unsafe { FileOps::new(operations, 0o200) } 366 }; 367 } 368 369 /// Representation of [`FileOps`] for read/write binary files. 370 pub(crate) trait BinaryReadWriteFile<T> { 371 const FILE_OPS: FileOps<T>; 372 } 373 374 impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T { 375 const FILE_OPS: FileOps<T> = { 376 let operations = bindings::file_operations { 377 read: Some(blob_read::<T>), 378 write: Some(blob_write::<T>), 379 llseek: Some(bindings::default_llseek), 380 open: Some(bindings::simple_open), 381 // SAFETY: `file_operations` supports zeroes in all fields. 382 ..unsafe { core::mem::zeroed() } 383 }; 384 385 // SAFETY: 386 // - The private data of `struct inode` does always contain a pointer to a valid `T`. 387 // - `simple_open()` stores the `struct inode`'s private data in the private data of the 388 // corresponding `struct file`. 389 // - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data. 390 // - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data. 391 // - `default_llseek()` does not access the `struct file`'s private data. 392 unsafe { FileOps::new(operations, 0o600) } 393 }; 394 } 395