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 kernel::{ 211 /// # debugfs::Dir, 212 /// # prelude::*, 213 /// # sync::atomic::{ 214 /// # Atomic, 215 /// # Relaxed, 216 /// # }, 217 /// # }; 218 /// # let dir = Dir::new(c"foo"); 219 /// let file = KBox::pin_init( 220 /// dir.read_callback_file(c"bar", 221 /// Atomic::<u32>::new(3), 222 /// &|val, f| { 223 /// let out = val.load(Relaxed); 224 /// writeln!(f, "{out:#010x}") 225 /// }), 226 /// GFP_KERNEL)?; 227 /// // Reading "foo/bar" will show "0x00000003". 228 /// file.store(10, Relaxed); 229 /// // Reading "foo/bar" will now show "0x0000000a". 230 /// # Ok::<(), Error>(()) 231 /// ``` 232 pub fn read_callback_file<'a, T, E: 'a, F>( 233 &'a self, 234 name: &'a CStr, 235 data: impl PinInit<T, E> + 'a, 236 _f: &'static F, 237 ) -> impl PinInit<File<T>, E> + 'a 238 where 239 T: Send + Sync + 'static, 240 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 241 { 242 let file_ops = <FormatAdapter<T, F>>::FILE_OPS.adapt(); 243 self.create_file(name, data, file_ops) 244 } 245 246 /// Creates a read-write file in this directory. 247 /// 248 /// Reading the file uses the [`Writer`] implementation. 249 /// Writing to the file uses the [`Reader`] implementation. 250 pub fn read_write_file<'a, T, E: 'a>( 251 &'a self, 252 name: &'a CStr, 253 data: impl PinInit<T, E> + 'a, 254 ) -> impl PinInit<File<T>, E> + 'a 255 where 256 T: Writer + Reader + Send + Sync + 'static, 257 { 258 let file_ops = &<T as ReadWriteFile<_>>::FILE_OPS; 259 self.create_file(name, data, file_ops) 260 } 261 262 /// Creates a read-write binary file in this directory. 263 /// 264 /// Reading the file uses the [`BinaryWriter`] implementation. 265 /// Writing to the file uses the [`BinaryReader`] implementation. 266 pub fn read_write_binary_file<'a, T, E: 'a>( 267 &'a self, 268 name: &'a CStr, 269 data: impl PinInit<T, E> + 'a, 270 ) -> impl PinInit<File<T>, E> + 'a 271 where 272 T: BinaryWriter + BinaryReader + Send + Sync + 'static, 273 { 274 let file_ops = &<T as BinaryReadWriteFile<_>>::FILE_OPS; 275 self.create_file(name, data, file_ops) 276 } 277 278 /// Creates a read-write file in this directory, with logic from callbacks. 279 /// 280 /// Reading from the file is handled by `f`. Writing to the file is handled by `w`. 281 /// 282 /// `f` and `w` must be function items or non-capturing closures. 283 /// This is statically asserted and not a safety requirement. 284 pub fn read_write_callback_file<'a, T, E: 'a, F, W>( 285 &'a self, 286 name: &'a CStr, 287 data: impl PinInit<T, E> + 'a, 288 _f: &'static F, 289 _w: &'static W, 290 ) -> impl PinInit<File<T>, E> + 'a 291 where 292 T: Send + Sync + 'static, 293 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 294 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 295 { 296 let file_ops = 297 <WritableAdapter<FormatAdapter<T, F>, W> as file_ops::ReadWriteFile<_>>::FILE_OPS 298 .adapt() 299 .adapt(); 300 self.create_file(name, data, file_ops) 301 } 302 303 /// Creates a write-only file in this directory. 304 /// 305 /// The file owns its backing data. Writing to the file uses the [`Reader`] 306 /// implementation. 307 /// 308 /// The file is removed when the returned [`File`] is dropped. 309 pub fn write_only_file<'a, T, E: 'a>( 310 &'a self, 311 name: &'a CStr, 312 data: impl PinInit<T, E> + 'a, 313 ) -> impl PinInit<File<T>, E> + 'a 314 where 315 T: Reader + Send + Sync + 'static, 316 { 317 self.create_file(name, data, &T::FILE_OPS) 318 } 319 320 /// Creates a write-only binary file in this directory. 321 /// 322 /// The file owns its backing data. Writing to the file uses the [`BinaryReader`] 323 /// implementation. 324 /// 325 /// The file is removed when the returned [`File`] is dropped. 326 pub fn write_binary_file<'a, T, E: 'a>( 327 &'a self, 328 name: &'a CStr, 329 data: impl PinInit<T, E> + 'a, 330 ) -> impl PinInit<File<T>, E> + 'a 331 where 332 T: BinaryReader + Send + Sync + 'static, 333 { 334 self.create_file(name, data, &T::FILE_OPS) 335 } 336 337 /// Creates a write-only file in this directory, with write logic from a callback. 338 /// 339 /// `w` must be a function item or a non-capturing closure. 340 /// This is statically asserted and not a safety requirement. 341 pub fn write_callback_file<'a, T, E: 'a, W>( 342 &'a self, 343 name: &'a CStr, 344 data: impl PinInit<T, E> + 'a, 345 _w: &'static W, 346 ) -> impl PinInit<File<T>, E> + 'a 347 where 348 T: Send + Sync + 'static, 349 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 350 { 351 let file_ops = <WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS 352 .adapt() 353 .adapt(); 354 self.create_file(name, data, file_ops) 355 } 356 357 // While this function is safe, it is intentionally not public because it's a bit of a 358 // footgun. 359 // 360 // Unless you also extract the `entry` later and schedule it for `Drop` at the appropriate 361 // time, a `ScopedDir` with a `Dir` parent will never be deleted. 362 fn scoped_dir<'data>(&self, name: &CStr) -> ScopedDir<'data, 'static> { 363 #[cfg(CONFIG_DEBUG_FS)] 364 { 365 let parent_entry = match &self.0 { 366 None => return ScopedDir::empty(), 367 Some(entry) => entry.clone(), 368 }; 369 ScopedDir { 370 entry: ManuallyDrop::new(Entry::dynamic_dir(name, Some(parent_entry))), 371 _phantom: PhantomData, 372 } 373 } 374 #[cfg(not(CONFIG_DEBUG_FS))] 375 ScopedDir::empty() 376 } 377 378 /// Creates a new scope, which is a directory associated with some data `T`. 379 /// 380 /// The created directory will be a subdirectory of `self`. The `init` closure is called to 381 /// populate the directory with files and subdirectories. These files can reference the data 382 /// stored in the scope. 383 /// 384 /// The entire directory tree created within the scope will be removed when the returned 385 /// `Scope` handle is dropped. 386 pub fn scope<'a, T: 'a, E: 'a, F>( 387 &'a self, 388 data: impl PinInit<T, E> + 'a, 389 name: &'a CStr, 390 init: F, 391 ) -> impl PinInit<Scope<T>, E> + 'a 392 where 393 F: for<'data, 'dir> FnOnce(&'data T, &'dir ScopedDir<'data, 'dir>) + 'a, 394 { 395 Scope::new(data, |data| { 396 let scoped = self.scoped_dir(name); 397 init(data, &scoped); 398 scoped.into_entry() 399 }) 400 } 401 } 402 403 #[pin_data] 404 /// Handle to a DebugFS scope, which ensures that attached `data` will outlive the DebugFS entry 405 /// without moving. 406 /// 407 /// This is internally used to back [`File`], and used in the API to represent the attachment 408 /// of a directory lifetime to a data structure which may be jointly accessed by a number of 409 /// different files. 410 /// 411 /// When dropped, a `Scope` will remove all directories and files in the filesystem backed by the 412 /// attached data structure prior to releasing the attached data. 413 pub struct Scope<T> { 414 // This order is load-bearing for drops - `_entry` must be dropped before `data`. 415 #[cfg(CONFIG_DEBUG_FS)] 416 _entry: Entry<'static>, 417 #[pin] 418 data: T, 419 // Even if `T` is `Unpin`, we still can't allow it to be moved. 420 #[pin] 421 _pin: PhantomPinned, 422 } 423 424 #[pin_data] 425 /// Handle to a DebugFS file, owning its backing data. 426 /// 427 /// When dropped, the DebugFS file will be removed and the attached data will be dropped. 428 pub struct File<T> { 429 #[pin] 430 scope: Scope<T>, 431 } 432 433 #[cfg(not(CONFIG_DEBUG_FS))] 434 impl<'b, T: 'b> Scope<T> { 435 fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b 436 where 437 F: for<'a> FnOnce(&'a T) + 'b, 438 { 439 try_pin_init! { 440 Self { 441 data <- data, 442 _pin: PhantomPinned 443 } ? E 444 } 445 .pin_chain(|scope| { 446 init(&scope.data); 447 Ok(()) 448 }) 449 } 450 } 451 452 #[cfg(CONFIG_DEBUG_FS)] 453 impl<'b, T: 'b> Scope<T> { 454 fn entry_mut(self: Pin<&mut Self>) -> &mut Entry<'static> { 455 // SAFETY: _entry is not structurally pinned. 456 unsafe { &mut Pin::into_inner_unchecked(self)._entry } 457 } 458 459 fn new<E: 'b, F>(data: impl PinInit<T, E> + 'b, init: F) -> impl PinInit<Self, E> + 'b 460 where 461 F: for<'a> FnOnce(&'a T) -> Entry<'static> + 'b, 462 { 463 try_pin_init! { 464 Self { 465 _entry: Entry::empty(), 466 data <- data, 467 _pin: PhantomPinned 468 } ? E 469 } 470 .pin_chain(|scope| { 471 *scope.entry_mut() = init(&scope.data); 472 Ok(()) 473 }) 474 } 475 } 476 477 impl<'a, T: 'a> Scope<T> { 478 /// Creates a new scope, which is a directory at the root of the debugfs filesystem, 479 /// associated with some data `T`. 480 /// 481 /// The `init` closure is called to populate the directory with files and subdirectories. These 482 /// files can reference the data stored in the scope. 483 /// 484 /// The entire directory tree created within the scope will be removed when the returned 485 /// `Scope` handle is dropped. 486 pub fn dir<E: 'a, F>( 487 data: impl PinInit<T, E> + 'a, 488 name: &'a CStr, 489 init: F, 490 ) -> impl PinInit<Self, E> + 'a 491 where 492 F: for<'data, 'dir> FnOnce(&'data T, &'dir ScopedDir<'data, 'dir>) + 'a, 493 { 494 Scope::new(data, |data| { 495 let scoped = ScopedDir::new(name); 496 init(data, &scoped); 497 scoped.into_entry() 498 }) 499 } 500 } 501 502 impl<T> Deref for Scope<T> { 503 type Target = T; 504 fn deref(&self) -> &T { 505 &self.data 506 } 507 } 508 509 impl<T> Deref for File<T> { 510 type Target = T; 511 fn deref(&self) -> &T { 512 &self.scope 513 } 514 } 515 516 /// A handle to a directory which will live at most `'dir`, accessing data that will live for at 517 /// least `'data`. 518 /// 519 /// Dropping a ScopedDir will not delete or clean it up, this is expected to occur through dropping 520 /// the `Scope` that created it. 521 pub struct ScopedDir<'data, 'dir> { 522 #[cfg(CONFIG_DEBUG_FS)] 523 entry: ManuallyDrop<Entry<'dir>>, 524 _phantom: PhantomData<fn(&'data ()) -> &'dir ()>, 525 } 526 527 impl<'data, 'dir> ScopedDir<'data, 'dir> { 528 /// Creates a subdirectory inside this `ScopedDir`. 529 /// 530 /// The returned directory handle cannot outlive this one. 531 pub fn dir<'dir2>(&'dir2 self, name: &CStr) -> ScopedDir<'data, 'dir2> { 532 #[cfg(not(CONFIG_DEBUG_FS))] 533 let _ = name; 534 ScopedDir { 535 #[cfg(CONFIG_DEBUG_FS)] 536 entry: ManuallyDrop::new(Entry::dir(name, Some(&*self.entry))), 537 _phantom: PhantomData, 538 } 539 } 540 541 fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static FileOps<T>) { 542 #[cfg(CONFIG_DEBUG_FS)] 543 core::mem::forget(Entry::file(name, &self.entry, data, vtable)); 544 } 545 546 /// Creates a read-only file in this directory. 547 /// 548 /// The file's contents are produced by invoking [`Writer::write`]. 549 /// 550 /// This function does not produce an owning handle to the file. The created 551 /// file is removed when the [`Scope`] that this directory belongs 552 /// to is dropped. 553 pub fn read_only_file<T: Writer + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) { 554 self.create_file(name, data, &T::FILE_OPS) 555 } 556 557 /// Creates a read-only binary file in this directory. 558 /// 559 /// The file's contents are produced by invoking [`BinaryWriter::write_to_slice`]. 560 /// 561 /// This function does not produce an owning handle to the file. The created file is removed 562 /// when the [`Scope`] that this directory belongs to is dropped. 563 pub fn read_binary_file<T: BinaryWriter + Send + Sync + 'static>( 564 &self, 565 name: &CStr, 566 data: &'data T, 567 ) { 568 self.create_file(name, data, &T::FILE_OPS) 569 } 570 571 /// Creates a read-only file in this directory, with contents from a callback. 572 /// 573 /// The file contents are generated by calling `f` with `data`. 574 /// 575 /// 576 /// `f` must be a function item or a non-capturing closure. 577 /// This is statically asserted and not a safety requirement. 578 /// 579 /// This function does not produce an owning handle to the file. The created 580 /// file is removed when the [`Scope`] that this directory belongs 581 /// to is dropped. 582 pub fn read_callback_file<T, F>(&self, name: &CStr, data: &'data T, _f: &'static F) 583 where 584 T: Send + Sync + 'static, 585 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 586 { 587 let vtable = <FormatAdapter<T, F> as ReadFile<_>>::FILE_OPS.adapt(); 588 self.create_file(name, data, vtable) 589 } 590 591 /// Creates a read-write file in this directory. 592 /// 593 /// Reading the file uses the [`Writer`] implementation on `data`. Writing to the file uses 594 /// the [`Reader`] implementation on `data`. 595 /// 596 /// This function does not produce an owning handle to the file. The created 597 /// file is removed when the [`Scope`] that this directory belongs 598 /// to is dropped. 599 pub fn read_write_file<T: Writer + Reader + Send + Sync + 'static>( 600 &self, 601 name: &CStr, 602 data: &'data T, 603 ) { 604 let vtable = &<T as ReadWriteFile<_>>::FILE_OPS; 605 self.create_file(name, data, vtable) 606 } 607 608 /// Creates a read-write binary file in this directory. 609 /// 610 /// Reading the file uses the [`BinaryWriter`] implementation on `data`. Writing to the file 611 /// uses the [`BinaryReader`] implementation on `data`. 612 /// 613 /// This function does not produce an owning handle to the file. The created file is removed 614 /// when the [`Scope`] that this directory belongs to is dropped. 615 pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync + 'static>( 616 &self, 617 name: &CStr, 618 data: &'data T, 619 ) { 620 let vtable = &<T as BinaryReadWriteFile<_>>::FILE_OPS; 621 self.create_file(name, data, vtable) 622 } 623 624 /// Creates a read-write file in this directory, with logic from callbacks. 625 /// 626 /// Reading from the file is handled by `f`. Writing to the file is handled by `w`. 627 /// 628 /// `f` and `w` must be function items or non-capturing closures. 629 /// This is statically asserted and not a safety requirement. 630 /// 631 /// This function does not produce an owning handle to the file. The created 632 /// file is removed when the [`Scope`] that this directory belongs 633 /// to is dropped. 634 pub fn read_write_callback_file<T, F, W>( 635 &self, 636 name: &CStr, 637 data: &'data T, 638 _f: &'static F, 639 _w: &'static W, 640 ) where 641 T: Send + Sync + 'static, 642 F: Fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result + Send + Sync, 643 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 644 { 645 let vtable = <WritableAdapter<FormatAdapter<T, F>, W> as ReadWriteFile<_>>::FILE_OPS 646 .adapt() 647 .adapt(); 648 self.create_file(name, data, vtable) 649 } 650 651 /// Creates a write-only file in this directory. 652 /// 653 /// Writing to the file uses the [`Reader`] implementation on `data`. 654 /// 655 /// This function does not produce an owning handle to the file. The created 656 /// file is removed when the [`Scope`] that this directory belongs 657 /// to is dropped. 658 pub fn write_only_file<T: Reader + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) { 659 let vtable = &<T as WriteFile<_>>::FILE_OPS; 660 self.create_file(name, data, vtable) 661 } 662 663 /// Creates a write-only binary file in this directory. 664 /// 665 /// Writing to the file uses the [`BinaryReader`] implementation on `data`. 666 /// 667 /// This function does not produce an owning handle to the file. The created file is removed 668 /// when the [`Scope`] that this directory belongs to is dropped. 669 pub fn write_binary_file<T: BinaryReader + Send + Sync + 'static>( 670 &self, 671 name: &CStr, 672 data: &'data T, 673 ) { 674 self.create_file(name, data, &T::FILE_OPS) 675 } 676 677 /// Creates a write-only file in this directory, with write logic from a callback. 678 /// 679 /// Writing to the file is handled by `w`. 680 /// 681 /// `w` must be a function item or a non-capturing closure. 682 /// This is statically asserted and not a safety requirement. 683 /// 684 /// This function does not produce an owning handle to the file. The created 685 /// file is removed when the [`Scope`] that this directory belongs 686 /// to is dropped. 687 pub fn write_only_callback_file<T, W>(&self, name: &CStr, data: &'data T, _w: &'static W) 688 where 689 T: Send + Sync + 'static, 690 W: Fn(&T, &mut UserSliceReader) -> Result + Send + Sync, 691 { 692 let vtable = &<WritableAdapter<NoWriter<T>, W> as WriteFile<_>>::FILE_OPS 693 .adapt() 694 .adapt(); 695 self.create_file(name, data, vtable) 696 } 697 698 fn empty() -> Self { 699 ScopedDir { 700 #[cfg(CONFIG_DEBUG_FS)] 701 entry: ManuallyDrop::new(Entry::empty()), 702 _phantom: PhantomData, 703 } 704 } 705 #[cfg(CONFIG_DEBUG_FS)] 706 fn into_entry(self) -> Entry<'dir> { 707 ManuallyDrop::into_inner(self.entry) 708 } 709 #[cfg(not(CONFIG_DEBUG_FS))] 710 fn into_entry(self) {} 711 } 712 713 impl<'data> ScopedDir<'data, 'static> { 714 // This is safe, but intentionally not exported due to footgun status. A ScopedDir with no 715 // parent will never be released by default, and needs to have its entry extracted and used 716 // somewhere. 717 fn new(name: &CStr) -> ScopedDir<'data, 'static> { 718 ScopedDir { 719 #[cfg(CONFIG_DEBUG_FS)] 720 entry: ManuallyDrop::new(Entry::dir(name, None)), 721 _phantom: PhantomData, 722 } 723 } 724 } 725