1 // SPDX-License-Identifier: GPL-2.0 2 // Copyright (C) 2025 Google LLC. 3 4 //! DebugFS Abstraction 5 //! 6 //! C header: [`include/linux/debugfs.h`](srctree/include/linux/debugfs.h) 7 8 // When DebugFS is disabled, many parameters are dead. Linting for this isn't helpful. 9 #![cfg_attr(not(CONFIG_DEBUG_FS), allow(unused_variables))] 10 11 #[cfg(CONFIG_DEBUG_FS)] 12 use crate::sync::Arc; 13 use crate::{ 14 fmt, 15 prelude::*, 16 str::CStr, 17 uaccess::UserSliceReader, // 18 }; 19 20 #[cfg(CONFIG_DEBUG_FS)] 21 use core::mem::ManuallyDrop; 22 use core::{ 23 marker::{ 24 PhantomData, 25 PhantomPinned, // 26 }, 27 ops::Deref, 28 }; 29 30 mod traits; 31 pub use traits::{ 32 BinaryReader, 33 BinaryReaderMut, 34 BinaryWriter, 35 Reader, 36 Writer, // 37 }; 38 39 mod callback_adapters; 40 use callback_adapters::{ 41 FormatAdapter, 42 NoWriter, 43 WritableAdapter, // 44 }; 45 46 mod file_ops; 47 use file_ops::{ 48 BinaryReadFile, 49 BinaryReadWriteFile, 50 BinaryWriteFile, 51 FileOps, 52 ReadFile, 53 ReadWriteFile, 54 WriteFile, // 55 }; 56 57 #[cfg(CONFIG_DEBUG_FS)] 58 mod entry; 59 #[cfg(CONFIG_DEBUG_FS)] 60 use entry::Entry; 61 62 /// Owning handle to a DebugFS directory. 63 /// 64 /// The directory in the filesystem represented by [`Dir`] will be removed when handle has been 65 /// dropped *and* all children have been removed. 66 // If we have a parent, we hold a reference to it in the `Entry`. This prevents the `dentry` 67 // we point to from being cleaned up if our parent `Dir`/`Entry` is dropped before us. 68 // 69 // The `None` option indicates that the `Arc` could not be allocated, so our children would not be 70 // able to refer to us. In this case, we need to silently fail. All future child directories/files 71 // will silently fail as well. 72 #[derive(Clone)] 73 pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry<'static>>>); 74 75 impl Dir { 76 /// Create a new directory in DebugFS. If `parent` is [`None`], it will be created at the root. 77 fn create(name: &CStr, parent: Option<&Dir>) -> Self { 78 #[cfg(CONFIG_DEBUG_FS)] 79 { 80 let parent_entry = match parent { 81 // If the parent couldn't be allocated, just early-return 82 Some(Dir(None)) => return Self(None), 83 Some(Dir(Some(entry))) => Some(entry.clone()), 84 None => None, 85 }; 86 Self( 87 // If Arc creation fails, the `Entry` will be dropped, so the directory will be 88 // cleaned up. 89 Arc::new(Entry::dynamic_dir(name, parent_entry), GFP_KERNEL).ok(), 90 ) 91 } 92 #[cfg(not(CONFIG_DEBUG_FS))] 93 Self() 94 } 95 96 /// Creates a DebugFS file which will own the data produced by the initializer provided in 97 /// `data`. 98 fn create_file<'a, T, E: 'a>( 99 &'a self, 100 name: &'a CStr, 101 data: impl PinInit<T, E> + 'a, 102 file_ops: &'static FileOps<T>, 103 ) -> impl PinInit<File<T>, E> + 'a 104 where 105 T: Sync + 'static, 106 { 107 let scope = Scope::<T>::new(data, move |data| { 108 #[cfg(CONFIG_DEBUG_FS)] 109 if let Some(parent) = &self.0 { 110 // SAFETY: Because data derives from a scope, and our entry will be dropped before 111 // the data is dropped, it is guaranteed to outlive the entry we return. 112 unsafe { Entry::dynamic_file(name, parent.clone(), data, file_ops) } 113 } else { 114 Entry::empty() 115 } 116 }); 117 try_pin_init! { 118 File { 119 scope <- scope 120 } ? E 121 } 122 } 123 124 /// Create a new directory in DebugFS at the root. 125 /// 126 /// # Examples 127 /// 128 /// ``` 129 /// # use kernel::debugfs::Dir; 130 /// let debugfs = Dir::new(c"parent"); 131 /// ``` 132 pub fn new(name: &CStr) -> Self { 133 Dir::create(name, None) 134 } 135 136 /// Creates a subdirectory within this directory. 137 /// 138 /// # Examples 139 /// 140 /// ``` 141 /// # use kernel::debugfs::Dir; 142 /// let parent = Dir::new(c"parent"); 143 /// let child = parent.subdir(c"child"); 144 /// ``` 145 pub fn subdir(&self, name: &CStr) -> Self { 146 Dir::create(name, Some(self)) 147 } 148 149 /// Creates a read-only file in this directory. 150 /// 151 /// The file's contents are produced by invoking [`Writer::write`] on the value initialized by 152 /// `data`. 153 /// 154 /// # Examples 155 /// 156 /// ``` 157 /// # use kernel::debugfs::Dir; 158 /// # use kernel::prelude::*; 159 /// # let dir = Dir::new(c"my_debugfs_dir"); 160 /// let file = KBox::pin_init(dir.read_only_file(c"foo", 200), GFP_KERNEL)?; 161 /// // "my_debugfs_dir/foo" now contains the number 200. 162 /// // The file is removed when `file` is dropped. 163 /// # Ok::<(), Error>(()) 164 /// ``` 165 pub fn read_only_file<'a, T, E: 'a>( 166 &'a self, 167 name: &'a CStr, 168 data: impl PinInit<T, E> + 'a, 169 ) -> impl PinInit<File<T>, E> + 'a 170 where 171 T: Writer + Send + Sync + 'static, 172 { 173 let file_ops = &<T as ReadFile<_>>::FILE_OPS; 174 self.create_file(name, data, file_ops) 175 } 176 177 /// Creates a read-only binary file in this directory. 178 /// 179 /// The file's contents are produced by invoking [`BinaryWriter::write_to_slice`] on the value 180 /// initialized by `data`. 181 /// 182 /// # Examples 183 /// 184 /// ``` 185 /// # use kernel::debugfs::Dir; 186 /// # use kernel::prelude::*; 187 /// # let dir = Dir::new(c"my_debugfs_dir"); 188 /// let file = KBox::pin_init(dir.read_binary_file(c"foo", [0x1, 0x2]), GFP_KERNEL)?; 189 /// # Ok::<(), Error>(()) 190 /// ``` 191 pub fn read_binary_file<'a, T, E: 'a>( 192 &'a self, 193 name: &'a CStr, 194 data: impl PinInit<T, E> + 'a, 195 ) -> impl PinInit<File<T>, E> + 'a 196 where 197 T: BinaryWriter + Send + Sync + 'static, 198 { 199 self.create_file(name, data, &T::FILE_OPS) 200 } 201 202 /// Creates a read-only file in this directory, with contents from a callback. 203 /// 204 /// `f` must be a function item or a non-capturing closure. 205 /// This is statically asserted and not a safety requirement. 206 /// 207 /// # Examples 208 /// 209 /// ``` 210 /// # use core::sync::atomic::{AtomicU32, Ordering}; 211 /// # use kernel::debugfs::Dir; 212 /// # use kernel::prelude::*; 213 /// # let dir = Dir::new(c"foo"); 214 /// let file = KBox::pin_init( 215 /// dir.read_callback_file(c"bar", 216 /// AtomicU32::new(3), 217 /// &|val, f| { 218 /// let out = val.load(Ordering::Relaxed); 219 /// writeln!(f, "{out:#010x}") 220 /// }), 221 /// GFP_KERNEL)?; 222 /// // Reading "foo/bar" will show "0x00000003". 223 /// file.store(10, Ordering::Relaxed); 224 /// // Reading "foo/bar" will now show "0x0000000a". 225 /// # Ok::<(), Error>(()) 226 /// ``` 227 pub fn read_callback_file<'a, T, E: 'a, F>( 228 &'a self, 229 name: &'a CStr, 230 data: impl PinInit<T, E> + 'a, 231 _f: &'static F, 232 ) -> impl PinInit<File<T>, E> + 'a 233 where 234 T: Send + Sync + 'static, 235 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 236 { 237 let file_ops = <FormatAdapter<T, F>>::FILE_OPS.adapt(); 238 self.create_file(name, data, file_ops) 239 } 240 241 /// Creates a read-write file in this directory. 242 /// 243 /// Reading the file uses the [`Writer`] implementation. 244 /// Writing to the file uses the [`Reader`] implementation. 245 pub fn read_write_file<'a, T, E: 'a>( 246 &'a self, 247 name: &'a CStr, 248 data: impl PinInit<T, E> + 'a, 249 ) -> impl PinInit<File<T>, E> + 'a 250 where 251 T: Writer + Reader + Send + Sync + 'static, 252 { 253 let file_ops = &<T as ReadWriteFile<_>>::FILE_OPS; 254 self.create_file(name, data, file_ops) 255 } 256 257 /// Creates a read-write binary file in this directory. 258 /// 259 /// Reading the file uses the [`BinaryWriter`] implementation. 260 /// Writing to the file uses the [`BinaryReader`] implementation. 261 pub fn read_write_binary_file<'a, T, E: 'a>( 262 &'a self, 263 name: &'a CStr, 264 data: impl PinInit<T, E> + 'a, 265 ) -> impl PinInit<File<T>, E> + 'a 266 where 267 T: BinaryWriter + BinaryReader + Send + Sync + 'static, 268 { 269 let file_ops = &<T as BinaryReadWriteFile<_>>::FILE_OPS; 270 self.create_file(name, data, file_ops) 271 } 272 273 /// Creates a read-write file in this directory, with logic from callbacks. 274 /// 275 /// Reading from the file is handled by `f`. Writing to the file is handled by `w`. 276 /// 277 /// `f` and `w` must be function items or non-capturing closures. 278 /// This is statically asserted and not a safety requirement. 279 pub fn read_write_callback_file<'a, T, E: 'a, F, W>( 280 &'a self, 281 name: &'a CStr, 282 data: impl PinInit<T, E> + 'a, 283 _f: &'static F, 284 _w: &'static W, 285 ) -> impl PinInit<File<T>, E> + 'a 286 where 287 T: Send + Sync + 'static, 288 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 289 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 290 { 291 let file_ops = 292 <WritableAdapter<FormatAdapter<T, F>, W> as file_ops::ReadWriteFile<_>>::FILE_OPS 293 .adapt() 294 .adapt(); 295 self.create_file(name, data, file_ops) 296 } 297 298 /// Creates a write-only file in this directory. 299 /// 300 /// The file owns its backing data. Writing to the file uses the [`Reader`] 301 /// implementation. 302 /// 303 /// The file is removed when the returned [`File`] is dropped. 304 pub fn write_only_file<'a, T, E: 'a>( 305 &'a self, 306 name: &'a CStr, 307 data: impl PinInit<T, E> + 'a, 308 ) -> impl PinInit<File<T>, E> + 'a 309 where 310 T: Reader + Send + Sync + 'static, 311 { 312 self.create_file(name, data, &T::FILE_OPS) 313 } 314 315 /// Creates a write-only binary file in this directory. 316 /// 317 /// The file owns its backing data. Writing to the file uses the [`BinaryReader`] 318 /// implementation. 319 /// 320 /// The file is removed when the returned [`File`] is dropped. 321 pub fn write_binary_file<'a, T, E: 'a>( 322 &'a self, 323 name: &'a CStr, 324 data: impl PinInit<T, E> + 'a, 325 ) -> impl PinInit<File<T>, E> + 'a 326 where 327 T: BinaryReader + Send + Sync + 'static, 328 { 329 self.create_file(name, data, &T::FILE_OPS) 330 } 331 332 /// Creates a write-only file in this directory, with write logic from a callback. 333 /// 334 /// `w` must be a function item or a non-capturing closure. 335 /// This is statically asserted and not a safety requirement. 336 pub fn write_callback_file<'a, T, E: 'a, W>( 337 &'a self, 338 name: &'a CStr, 339 data: impl PinInit<T, E> + 'a, 340 _w: &'static W, 341 ) -> impl PinInit<File<T>, E> + 'a 342 where 343 T: Send + Sync + 'static, 344 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 345 { 346 let file_ops = <WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS 347 .adapt() 348 .adapt(); 349 self.create_file(name, data, file_ops) 350 } 351 352 // While this function is safe, it is intentionally not public because it's a bit of a 353 // footgun. 354 // 355 // Unless you also extract the `entry` later and schedule it for `Drop` at the appropriate 356 // time, a `ScopedDir` with a `Dir` parent will never be deleted. 357 fn scoped_dir<'data>(&self, name: &CStr) -> ScopedDir<'data, 'static> { 358 #[cfg(CONFIG_DEBUG_FS)] 359 { 360 let parent_entry = match &self.0 { 361 None => return ScopedDir::empty(), 362 Some(entry) => entry.clone(), 363 }; 364 ScopedDir { 365 entry: ManuallyDrop::new(Entry::dynamic_dir(name, Some(parent_entry))), 366 _phantom: PhantomData, 367 } 368 } 369 #[cfg(not(CONFIG_DEBUG_FS))] 370 ScopedDir::empty() 371 } 372 373 /// Creates a new scope, which is a directory associated with some data `T`. 374 /// 375 /// The created directory will be a subdirectory of `self`. The `init` closure is called to 376 /// populate the directory with files and subdirectories. These files can reference the data 377 /// stored in the scope. 378 /// 379 /// The entire directory tree created within the scope will be removed when the returned 380 /// `Scope` handle is dropped. 381 pub fn scope<'a, T: 'a, E: 'a, F>( 382 &'a self, 383 data: impl PinInit<T, E> + 'a, 384 name: &'a CStr, 385 init: F, 386 ) -> impl PinInit<Scope<T>, E> + 'a 387 where 388 F: for<'data, 'dir> FnOnce(&'data T, &'dir ScopedDir<'data, 'dir>) + 'a, 389 { 390 Scope::new(data, |data| { 391 let scoped = self.scoped_dir(name); 392 init(data, &scoped); 393 scoped.into_entry() 394 }) 395 } 396 } 397 398 #[pin_data] 399 /// Handle to a DebugFS scope, which ensures that attached `data` will outlive the DebugFS entry 400 /// without moving. 401 /// 402 /// This is internally used to back [`File`], and used in the API to represent the attachment 403 /// of a directory lifetime to a data structure which may be jointly accessed by a number of 404 /// different files. 405 /// 406 /// When dropped, a `Scope` will remove all directories and files in the filesystem backed by the 407 /// attached data structure prior to releasing the attached data. 408 pub struct Scope<T> { 409 // This order is load-bearing for drops - `_entry` must be dropped before `data`. 410 #[cfg(CONFIG_DEBUG_FS)] 411 _entry: Entry<'static>, 412 #[pin] 413 data: T, 414 // Even if `T` is `Unpin`, we still can't allow it to be moved. 415 #[pin] 416 _pin: PhantomPinned, 417 } 418 419 #[pin_data] 420 /// Handle to a DebugFS file, owning its backing data. 421 /// 422 /// When dropped, the DebugFS file will be removed and the attached data will be dropped. 423 pub struct File<T> { 424 #[pin] 425 scope: Scope<T>, 426 } 427 428 #[cfg(not(CONFIG_DEBUG_FS))] 429 impl<'b, T: 'b> Scope<T> { 430 fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b 431 where 432 F: for<'a> FnOnce(&'a T) + 'b, 433 { 434 try_pin_init! { 435 Self { 436 data <- data, 437 _pin: PhantomPinned 438 } ? E 439 } 440 .pin_chain(|scope| { 441 init(&scope.data); 442 Ok(()) 443 }) 444 } 445 } 446 447 #[cfg(CONFIG_DEBUG_FS)] 448 impl<'b, T: 'b> Scope<T> { 449 fn entry_mut(self: Pin<&mut Self>) -> &mut Entry<'static> { 450 // SAFETY: _entry is not structurally pinned. 451 unsafe { &mut Pin::into_inner_unchecked(self)._entry } 452 } 453 454 fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b 455 where 456 F: for<'a> FnOnce(&'a T) -> Entry<'static> + 'b, 457 { 458 try_pin_init! { 459 Self { 460 _entry: Entry::empty(), 461 data <- data, 462 _pin: PhantomPinned 463 } ? E 464 } 465 .pin_chain(|scope| { 466 *scope.entry_mut() = init(&scope.data); 467 Ok(()) 468 }) 469 } 470 } 471 472 impl<'a, T: 'a> Scope<T> { 473 /// Creates a new scope, which is a directory at the root of the debugfs filesystem, 474 /// associated with some data `T`. 475 /// 476 /// The `init` closure is called to populate the directory with files and subdirectories. These 477 /// files can reference the data stored in the scope. 478 /// 479 /// The entire directory tree created within the scope will be removed when the returned 480 /// `Scope` handle is dropped. 481 pub fn dir<E: 'a, F>( 482 data: impl PinInit<T, E> + 'a, 483 name: &'a CStr, 484 init: F, 485 ) -> impl PinInit<Self, E> + 'a 486 where 487 F: for<'data, 'dir> FnOnce(&'data T, &'dir ScopedDir<'data, 'dir>) + 'a, 488 { 489 Scope::new(data, |data| { 490 let scoped = ScopedDir::new(name); 491 init(data, &scoped); 492 scoped.into_entry() 493 }) 494 } 495 } 496 497 impl<T> Deref for Scope<T> { 498 type Target = T; 499 fn deref(&self) -> &T { 500 &self.data 501 } 502 } 503 504 impl<T> Deref for File<T> { 505 type Target = T; 506 fn deref(&self) -> &T { 507 &self.scope 508 } 509 } 510 511 /// A handle to a directory which will live at most `'dir`, accessing data that will live for at 512 /// least `'data`. 513 /// 514 /// Dropping a ScopedDir will not delete or clean it up, this is expected to occur through dropping 515 /// the `Scope` that created it. 516 pub struct ScopedDir<'data, 'dir> { 517 #[cfg(CONFIG_DEBUG_FS)] 518 entry: ManuallyDrop<Entry<'dir>>, 519 _phantom: PhantomData<fn(&'data ()) -> &'dir ()>, 520 } 521 522 impl<'data, 'dir> ScopedDir<'data, 'dir> { 523 /// Creates a subdirectory inside this `ScopedDir`. 524 /// 525 /// The returned directory handle cannot outlive this one. 526 pub fn dir<'dir2>(&'dir2 self, name: &CStr) -> ScopedDir<'data, 'dir2> { 527 #[cfg(not(CONFIG_DEBUG_FS))] 528 let _ = name; 529 ScopedDir { 530 #[cfg(CONFIG_DEBUG_FS)] 531 entry: ManuallyDrop::new(Entry::dir(name, Some(&*self.entry))), 532 _phantom: PhantomData, 533 } 534 } 535 536 fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static FileOps<T>) { 537 #[cfg(CONFIG_DEBUG_FS)] 538 core::mem::forget(Entry::file(name, &self.entry, data, vtable)); 539 } 540 541 /// Creates a read-only file in this directory. 542 /// 543 /// The file's contents are produced by invoking [`Writer::write`]. 544 /// 545 /// This function does not produce an owning handle to the file. The created 546 /// file is removed when the [`Scope`] that this directory belongs 547 /// to is dropped. 548 pub fn read_only_file<T: Writer + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) { 549 self.create_file(name, data, &T::FILE_OPS) 550 } 551 552 /// Creates a read-only binary file in this directory. 553 /// 554 /// The file's contents are produced by invoking [`BinaryWriter::write_to_slice`]. 555 /// 556 /// This function does not produce an owning handle to the file. The created file is removed 557 /// when the [`Scope`] that this directory belongs to is dropped. 558 pub fn read_binary_file<T: BinaryWriter + Send + Sync + 'static>( 559 &self, 560 name: &CStr, 561 data: &'data T, 562 ) { 563 self.create_file(name, data, &T::FILE_OPS) 564 } 565 566 /// Creates a read-only file in this directory, with contents from a callback. 567 /// 568 /// The file contents are generated by calling `f` with `data`. 569 /// 570 /// 571 /// `f` must be a function item or a non-capturing closure. 572 /// This is statically asserted and not a safety requirement. 573 /// 574 /// This function does not produce an owning handle to the file. The created 575 /// file is removed when the [`Scope`] that this directory belongs 576 /// to is dropped. 577 pub fn read_callback_file<T, F>(&self, name: &CStr, data: &'data T, _f: &'static F) 578 where 579 T: Send + Sync + 'static, 580 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 581 { 582 let vtable = <FormatAdapter<T, F> as ReadFile<_>>::FILE_OPS.adapt(); 583 self.create_file(name, data, vtable) 584 } 585 586 /// Creates a read-write file in this directory. 587 /// 588 /// Reading the file uses the [`Writer`] implementation on `data`. Writing to the file uses 589 /// the [`Reader`] implementation on `data`. 590 /// 591 /// This function does not produce an owning handle to the file. The created 592 /// file is removed when the [`Scope`] that this directory belongs 593 /// to is dropped. 594 pub fn read_write_file<T: Writer + Reader + Send + Sync + 'static>( 595 &self, 596 name: &CStr, 597 data: &'data T, 598 ) { 599 let vtable = &<T as ReadWriteFile<_>>::FILE_OPS; 600 self.create_file(name, data, vtable) 601 } 602 603 /// Creates a read-write binary file in this directory. 604 /// 605 /// Reading the file uses the [`BinaryWriter`] implementation on `data`. Writing to the file 606 /// uses the [`BinaryReader`] implementation on `data`. 607 /// 608 /// This function does not produce an owning handle to the file. The created file is removed 609 /// when the [`Scope`] that this directory belongs to is dropped. 610 pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync + 'static>( 611 &self, 612 name: &CStr, 613 data: &'data T, 614 ) { 615 let vtable = &<T as BinaryReadWriteFile<_>>::FILE_OPS; 616 self.create_file(name, data, vtable) 617 } 618 619 /// Creates a read-write file in this directory, with logic from callbacks. 620 /// 621 /// Reading from the file is handled by `f`. Writing to the file is handled by `w`. 622 /// 623 /// `f` and `w` must be function items or non-capturing closures. 624 /// This is statically asserted and not a safety requirement. 625 /// 626 /// This function does not produce an owning handle to the file. The created 627 /// file is removed when the [`Scope`] that this directory belongs 628 /// to is dropped. 629 pub fn read_write_callback_file<T, F, W>( 630 &self, 631 name: &CStr, 632 data: &'data T, 633 _f: &'static F, 634 _w: &'static W, 635 ) where 636 T: Send + Sync + 'static, 637 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 638 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 639 { 640 let vtable = <WritableAdapter<FormatAdapter<T, F>, W> as ReadWriteFile<_>>::FILE_OPS 641 .adapt() 642 .adapt(); 643 self.create_file(name, data, vtable) 644 } 645 646 /// Creates a write-only file in this directory. 647 /// 648 /// Writing to the file uses the [`Reader`] implementation on `data`. 649 /// 650 /// This function does not produce an owning handle to the file. The created 651 /// file is removed when the [`Scope`] that this directory belongs 652 /// to is dropped. 653 pub fn write_only_file<T: Reader + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) { 654 let vtable = &<T as WriteFile<_>>::FILE_OPS; 655 self.create_file(name, data, vtable) 656 } 657 658 /// Creates a write-only binary file in this directory. 659 /// 660 /// Writing to the file uses the [`BinaryReader`] implementation on `data`. 661 /// 662 /// This function does not produce an owning handle to the file. The created file is removed 663 /// when the [`Scope`] that this directory belongs to is dropped. 664 pub fn write_binary_file<T: BinaryReader + Send + Sync + 'static>( 665 &self, 666 name: &CStr, 667 data: &'data T, 668 ) { 669 self.create_file(name, data, &T::FILE_OPS) 670 } 671 672 /// Creates a write-only file in this directory, with write logic from a callback. 673 /// 674 /// Writing to the file is handled by `w`. 675 /// 676 /// `w` must be a function item or a non-capturing closure. 677 /// This is statically asserted and not a safety requirement. 678 /// 679 /// This function does not produce an owning handle to the file. The created 680 /// file is removed when the [`Scope`] that this directory belongs 681 /// to is dropped. 682 pub fn write_only_callback_file<T, W>(&self, name: &CStr, data: &'data T, _w: &'static W) 683 where 684 T: Send + Sync + 'static, 685 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 686 { 687 let vtable = &<WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS 688 .adapt() 689 .adapt(); 690 self.create_file(name, data, vtable) 691 } 692 693 fn empty() -> Self { 694 ScopedDir { 695 #[cfg(CONFIG_DEBUG_FS)] 696 entry: ManuallyDrop::new(Entry::empty()), 697 _phantom: PhantomData, 698 } 699 } 700 #[cfg(CONFIG_DEBUG_FS)] 701 fn into_entry(self) -> Entry<'dir> { 702 ManuallyDrop::into_inner(self.entry) 703 } 704 #[cfg(not(CONFIG_DEBUG_FS))] 705 fn into_entry(self) {} 706 } 707 708 impl<'data> ScopedDir<'data, 'static> { 709 // This is safe, but intentionally not exported due to footgun status. A ScopedDir with no 710 // parent will never be released by default, and needs to have its entry extracted and used 711 // somewhere. 712 fn new(name: &CStr) -> ScopedDir<'data, 'static> { 713 ScopedDir { 714 #[cfg(CONFIG_DEBUG_FS)] 715 entry: ManuallyDrop::new(Entry::dir(name, None)), 716 _phantom: PhantomData, 717 } 718 } 719 } 720