Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/rust/kernel/debugfs/entry.rs
29266 views
1
// SPDX-License-Identifier: GPL-2.0
2
// Copyright (C) 2025 Google LLC.
3
4
use crate::debugfs::file_ops::FileOps;
5
use crate::ffi::c_void;
6
use crate::str::CStr;
7
use crate::sync::Arc;
8
use core::marker::PhantomData;
9
10
/// Owning handle to a DebugFS entry.
11
///
12
/// # Invariants
13
///
14
/// The wrapped pointer will always be `NULL`, an error, or an owned DebugFS `dentry`.
15
pub(crate) struct Entry<'a> {
16
entry: *mut bindings::dentry,
17
// If we were created with an owning parent, this is the keep-alive
18
_parent: Option<Arc<Entry<'static>>>,
19
// If we were created with a non-owning parent, this prevents us from outliving it
20
_phantom: PhantomData<&'a ()>,
21
}
22
23
// SAFETY: [`Entry`] is just a `dentry` under the hood, which the API promises can be transferred
24
// between threads.
25
unsafe impl Send for Entry<'_> {}
26
27
// SAFETY: All the C functions we call on the `dentry` pointer are threadsafe.
28
unsafe impl Sync for Entry<'_> {}
29
30
impl Entry<'static> {
31
pub(crate) fn dynamic_dir(name: &CStr, parent: Option<Arc<Self>>) -> Self {
32
let parent_ptr = match &parent {
33
Some(entry) => entry.as_ptr(),
34
None => core::ptr::null_mut(),
35
};
36
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
37
// * `name` is a valid C string by the invariants of `&CStr`.
38
// * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
39
// `dentry` by our invariant. `debugfs_create_dir` handles `NULL` pointers correctly.
40
let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };
41
42
Entry {
43
entry,
44
_parent: parent,
45
_phantom: PhantomData,
46
}
47
}
48
49
/// # Safety
50
///
51
/// * `data` must outlive the returned `Entry`.
52
pub(crate) unsafe fn dynamic_file<T>(
53
name: &CStr,
54
parent: Arc<Self>,
55
data: &T,
56
file_ops: &'static FileOps<T>,
57
) -> Self {
58
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
59
// * `name` is a valid C string by the invariants of `&CStr`.
60
// * `parent.as_ptr()` is a pointer to a valid `dentry` by invariant.
61
// * The caller guarantees that `data` will outlive the returned `Entry`.
62
// * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
63
// provided.
64
let entry = unsafe {
65
bindings::debugfs_create_file_full(
66
name.as_char_ptr(),
67
file_ops.mode(),
68
parent.as_ptr(),
69
core::ptr::from_ref(data) as *mut c_void,
70
core::ptr::null(),
71
&**file_ops,
72
)
73
};
74
75
Entry {
76
entry,
77
_parent: Some(parent),
78
_phantom: PhantomData,
79
}
80
}
81
}
82
83
impl<'a> Entry<'a> {
84
pub(crate) fn dir(name: &CStr, parent: Option<&'a Entry<'_>>) -> Self {
85
let parent_ptr = match &parent {
86
Some(entry) => entry.as_ptr(),
87
None => core::ptr::null_mut(),
88
};
89
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
90
// * `name` is a valid C string by the invariants of `&CStr`.
91
// * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
92
// `dentry` (because `parent` is a valid reference to an `Entry`). The lifetime `'a`
93
// ensures that the parent outlives this entry.
94
let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };
95
96
Entry {
97
entry,
98
_parent: None,
99
_phantom: PhantomData,
100
}
101
}
102
103
pub(crate) fn file<T>(
104
name: &CStr,
105
parent: &'a Entry<'_>,
106
data: &'a T,
107
file_ops: &FileOps<T>,
108
) -> Self {
109
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
110
// * `name` is a valid C string by the invariants of `&CStr`.
111
// * `parent.as_ptr()` is a pointer to a valid `dentry` because we have `&'a Entry`.
112
// * `data` is a valid pointer to `T` for lifetime `'a`.
113
// * The returned `Entry` has lifetime `'a`, so it cannot outlive `parent` or `data`.
114
// * The caller guarantees that `vtable` is compatible with `data`.
115
// * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
116
// provided.
117
let entry = unsafe {
118
bindings::debugfs_create_file_full(
119
name.as_char_ptr(),
120
file_ops.mode(),
121
parent.as_ptr(),
122
core::ptr::from_ref(data) as *mut c_void,
123
core::ptr::null(),
124
&**file_ops,
125
)
126
};
127
128
Entry {
129
entry,
130
_parent: None,
131
_phantom: PhantomData,
132
}
133
}
134
}
135
136
impl Entry<'_> {
137
/// Constructs a placeholder DebugFS [`Entry`].
138
pub(crate) fn empty() -> Self {
139
Self {
140
entry: core::ptr::null_mut(),
141
_parent: None,
142
_phantom: PhantomData,
143
}
144
}
145
146
/// Returns the pointer representation of the DebugFS directory.
147
///
148
/// # Guarantees
149
///
150
/// Due to the type invariant, the value returned from this function will always be an error
151
/// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as
152
/// long as this entry lives.
153
pub(crate) fn as_ptr(&self) -> *mut bindings::dentry {
154
self.entry
155
}
156
}
157
158
impl Drop for Entry<'_> {
159
fn drop(&mut self) {
160
// SAFETY: `debugfs_remove` can take `NULL`, error values, and legal DebugFS dentries.
161
// `as_ptr` guarantees that the pointer is of this form.
162
unsafe { bindings::debugfs_remove(self.as_ptr()) }
163
}
164
}
165
166