xref: /linux/rust/kernel/debugfs/entry.rs (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
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