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}