kernel/debugfs.rs
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)]
12use crate::sync::Arc;
13use crate::{
14 fmt,
15 prelude::*,
16 str::CStr,
17 uaccess::UserSliceReader, //
18};
19
20#[cfg(CONFIG_DEBUG_FS)]
21use core::mem::ManuallyDrop;
22use core::{
23 marker::{
24 PhantomData,
25 PhantomPinned, //
26 },
27 ops::Deref,
28};
29
30mod traits;
31pub use traits::{
32 BinaryReader,
33 BinaryReaderMut,
34 BinaryWriter,
35 Reader,
36 Writer, //
37};
38
39mod callback_adapters;
40use callback_adapters::{
41 FormatAdapter,
42 NoWriter,
43 WritableAdapter, //
44};
45
46mod file_ops;
47use file_ops::{
48 BinaryReadFile,
49 BinaryReadWriteFile,
50 BinaryWriteFile,
51 FileOps,
52 ReadFile,
53 ReadWriteFile,
54 WriteFile, //
55};
56
57#[cfg(CONFIG_DEBUG_FS)]
58mod entry;
59#[cfg(CONFIG_DEBUG_FS)]
60use 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)]
73pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry<'static>>>);
74
75impl 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.
413pub 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.
428pub struct File<T> {
429 #[pin]
430 scope: Scope<T>,
431}
432
433#[cfg(not(CONFIG_DEBUG_FS))]
434impl<'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)]
453impl<'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
477impl<'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
502impl<T> Deref for Scope<T> {
503 type Target = T;
504 fn deref(&self) -> &T {
505 &self.data
506 }
507}
508
509impl<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.
521pub struct ScopedDir<'data, 'dir> {
522 #[cfg(CONFIG_DEBUG_FS)]
523 entry: ManuallyDrop<Entry<'dir>>,
524 _phantom: PhantomData<fn(&'data ()) -> &'dir ()>,
525}
526
527impl<'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
713impl<'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}