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
11use crate::fmt;
12use crate::prelude::*;
13use crate::str::CStr;
14#[cfg(CONFIG_DEBUG_FS)]
15use crate::sync::Arc;
16use crate::uaccess::UserSliceReader;
17use core::marker::PhantomData;
18use core::marker::PhantomPinned;
19#[cfg(CONFIG_DEBUG_FS)]
20use core::mem::ManuallyDrop;
21use core::ops::Deref;
22
23mod traits;
24pub use traits::{BinaryReader, BinaryReaderMut, BinaryWriter, Reader, Writer};
25
26mod callback_adapters;
27use callback_adapters::{FormatAdapter, NoWriter, WritableAdapter};
28mod file_ops;
29use file_ops::{
30    BinaryReadFile, BinaryReadWriteFile, BinaryWriteFile, FileOps, ReadFile, ReadWriteFile,
31    WriteFile,
32};
33#[cfg(CONFIG_DEBUG_FS)]
34mod entry;
35#[cfg(CONFIG_DEBUG_FS)]
36use 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)]
49pub struct Dir(#[cfg(CONFIG_DEBUG_FS)] Option<Arc<Entry<'static>>>);
50
51impl 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.
389pub 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.
404pub struct File<T> {
405    #[pin]
406    scope: Scope<T>,
407}
408
409#[cfg(not(CONFIG_DEBUG_FS))]
410impl<'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)]
429impl<'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
453impl<'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
478impl<T> Deref for Scope<T> {
479    type Target = T;
480    fn deref(&self) -> &T {
481        &self.data
482    }
483}
484
485impl<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.
497pub struct ScopedDir<'data, 'dir> {
498    #[cfg(CONFIG_DEBUG_FS)]
499    entry: ManuallyDrop<Entry<'dir>>,
500    _phantom: PhantomData<fn(&'data ()) -> &'dir ()>,
501}
502
503impl<'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
689impl<'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}