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