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