Skip to main content

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}