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