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}