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