Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/rust/kernel/debugfs.rs
48947 views
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
702