xref: /linux/rust/kernel/debugfs/entry.rs (revision eb3289fc474f74105e0627bf508e3f9742fd3b63)
17f201ca1SMatthew Maurer // SPDX-License-Identifier: GPL-2.0
27f201ca1SMatthew Maurer // Copyright (C) 2025 Google LLC.
37f201ca1SMatthew Maurer 
45e40b591SMatthew Maurer use crate::debugfs::file_ops::FileOps;
55e40b591SMatthew Maurer use crate::ffi::c_void;
67f201ca1SMatthew Maurer use crate::str::CStr;
77f201ca1SMatthew Maurer use crate::sync::Arc;
8*5f094258SMatthew Maurer use core::marker::PhantomData;
97f201ca1SMatthew Maurer 
107f201ca1SMatthew Maurer /// Owning handle to a DebugFS entry.
117f201ca1SMatthew Maurer ///
127f201ca1SMatthew Maurer /// # Invariants
137f201ca1SMatthew Maurer ///
147f201ca1SMatthew Maurer /// The wrapped pointer will always be `NULL`, an error, or an owned DebugFS `dentry`.
15*5f094258SMatthew Maurer pub(crate) struct Entry<'a> {
167f201ca1SMatthew Maurer     entry: *mut bindings::dentry,
177f201ca1SMatthew Maurer     // If we were created with an owning parent, this is the keep-alive
18*5f094258SMatthew Maurer     _parent: Option<Arc<Entry<'static>>>,
19*5f094258SMatthew Maurer     // If we were created with a non-owning parent, this prevents us from outliving it
20*5f094258SMatthew Maurer     _phantom: PhantomData<&'a ()>,
217f201ca1SMatthew Maurer }
227f201ca1SMatthew Maurer 
237f201ca1SMatthew Maurer // SAFETY: [`Entry`] is just a `dentry` under the hood, which the API promises can be transferred
247f201ca1SMatthew Maurer // between threads.
25*5f094258SMatthew Maurer unsafe impl Send for Entry<'_> {}
267f201ca1SMatthew Maurer 
277f201ca1SMatthew Maurer // SAFETY: All the C functions we call on the `dentry` pointer are threadsafe.
28*5f094258SMatthew Maurer unsafe impl Sync for Entry<'_> {}
297f201ca1SMatthew Maurer 
30*5f094258SMatthew Maurer impl Entry<'static> {
317f201ca1SMatthew Maurer     pub(crate) fn dynamic_dir(name: &CStr, parent: Option<Arc<Self>>) -> Self {
327f201ca1SMatthew Maurer         let parent_ptr = match &parent {
337f201ca1SMatthew Maurer             Some(entry) => entry.as_ptr(),
347f201ca1SMatthew Maurer             None => core::ptr::null_mut(),
357f201ca1SMatthew Maurer         };
367f201ca1SMatthew Maurer         // SAFETY: The invariants of this function's arguments ensure the safety of this call.
377f201ca1SMatthew Maurer         // * `name` is a valid C string by the invariants of `&CStr`.
387f201ca1SMatthew Maurer         // * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
397f201ca1SMatthew Maurer         //   `dentry` by our invariant. `debugfs_create_dir` handles `NULL` pointers correctly.
407f201ca1SMatthew Maurer         let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };
417f201ca1SMatthew Maurer 
427f201ca1SMatthew Maurer         Entry {
437f201ca1SMatthew Maurer             entry,
447f201ca1SMatthew Maurer             _parent: parent,
45*5f094258SMatthew Maurer             _phantom: PhantomData,
467f201ca1SMatthew Maurer         }
477f201ca1SMatthew Maurer     }
487f201ca1SMatthew Maurer 
495e40b591SMatthew Maurer     /// # Safety
505e40b591SMatthew Maurer     ///
515e40b591SMatthew Maurer     /// * `data` must outlive the returned `Entry`.
525e40b591SMatthew Maurer     pub(crate) unsafe fn dynamic_file<T>(
535e40b591SMatthew Maurer         name: &CStr,
545e40b591SMatthew Maurer         parent: Arc<Self>,
555e40b591SMatthew Maurer         data: &T,
565e40b591SMatthew Maurer         file_ops: &'static FileOps<T>,
575e40b591SMatthew Maurer     ) -> Self {
585e40b591SMatthew Maurer         // SAFETY: The invariants of this function's arguments ensure the safety of this call.
595e40b591SMatthew Maurer         // * `name` is a valid C string by the invariants of `&CStr`.
605e40b591SMatthew Maurer         // * `parent.as_ptr()` is a pointer to a valid `dentry` by invariant.
615e40b591SMatthew Maurer         // * The caller guarantees that `data` will outlive the returned `Entry`.
625e40b591SMatthew Maurer         // * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
635e40b591SMatthew Maurer         //   provided.
645e40b591SMatthew Maurer         let entry = unsafe {
655e40b591SMatthew Maurer             bindings::debugfs_create_file_full(
665e40b591SMatthew Maurer                 name.as_char_ptr(),
675e40b591SMatthew Maurer                 file_ops.mode(),
685e40b591SMatthew Maurer                 parent.as_ptr(),
695e40b591SMatthew Maurer                 core::ptr::from_ref(data) as *mut c_void,
705e40b591SMatthew Maurer                 core::ptr::null(),
715e40b591SMatthew Maurer                 &**file_ops,
725e40b591SMatthew Maurer             )
735e40b591SMatthew Maurer         };
745e40b591SMatthew Maurer 
755e40b591SMatthew Maurer         Entry {
765e40b591SMatthew Maurer             entry,
775e40b591SMatthew Maurer             _parent: Some(parent),
78*5f094258SMatthew Maurer             _phantom: PhantomData,
79*5f094258SMatthew Maurer         }
805e40b591SMatthew Maurer     }
815e40b591SMatthew Maurer }
825e40b591SMatthew Maurer 
83*5f094258SMatthew Maurer impl<'a> Entry<'a> {
84*5f094258SMatthew Maurer     pub(crate) fn dir(name: &CStr, parent: Option<&'a Entry<'_>>) -> Self {
85*5f094258SMatthew Maurer         let parent_ptr = match &parent {
86*5f094258SMatthew Maurer             Some(entry) => entry.as_ptr(),
87*5f094258SMatthew Maurer             None => core::ptr::null_mut(),
88*5f094258SMatthew Maurer         };
89*5f094258SMatthew Maurer         // SAFETY: The invariants of this function's arguments ensure the safety of this call.
90*5f094258SMatthew Maurer         // * `name` is a valid C string by the invariants of `&CStr`.
91*5f094258SMatthew Maurer         // * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
92*5f094258SMatthew Maurer         //   `dentry` (because `parent` is a valid reference to an `Entry`). The lifetime `'a`
93*5f094258SMatthew Maurer         //   ensures that the parent outlives this entry.
94*5f094258SMatthew Maurer         let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };
95*5f094258SMatthew Maurer 
96*5f094258SMatthew Maurer         Entry {
97*5f094258SMatthew Maurer             entry,
98*5f094258SMatthew Maurer             _parent: None,
99*5f094258SMatthew Maurer             _phantom: PhantomData,
100*5f094258SMatthew Maurer         }
101*5f094258SMatthew Maurer     }
102*5f094258SMatthew Maurer 
103*5f094258SMatthew Maurer     pub(crate) fn file<T>(
104*5f094258SMatthew Maurer         name: &CStr,
105*5f094258SMatthew Maurer         parent: &'a Entry<'_>,
106*5f094258SMatthew Maurer         data: &'a T,
107*5f094258SMatthew Maurer         file_ops: &FileOps<T>,
108*5f094258SMatthew Maurer     ) -> Self {
109*5f094258SMatthew Maurer         // SAFETY: The invariants of this function's arguments ensure the safety of this call.
110*5f094258SMatthew Maurer         // * `name` is a valid C string by the invariants of `&CStr`.
111*5f094258SMatthew Maurer         // * `parent.as_ptr()` is a pointer to a valid `dentry` because we have `&'a Entry`.
112*5f094258SMatthew Maurer         // * `data` is a valid pointer to `T` for lifetime `'a`.
113*5f094258SMatthew Maurer         // * The returned `Entry` has lifetime `'a`, so it cannot outlive `parent` or `data`.
114*5f094258SMatthew Maurer         // * The caller guarantees that `vtable` is compatible with `data`.
115*5f094258SMatthew Maurer         // * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
116*5f094258SMatthew Maurer         //   provided.
117*5f094258SMatthew Maurer         let entry = unsafe {
118*5f094258SMatthew Maurer             bindings::debugfs_create_file_full(
119*5f094258SMatthew Maurer                 name.as_char_ptr(),
120*5f094258SMatthew Maurer                 file_ops.mode(),
121*5f094258SMatthew Maurer                 parent.as_ptr(),
122*5f094258SMatthew Maurer                 core::ptr::from_ref(data) as *mut c_void,
123*5f094258SMatthew Maurer                 core::ptr::null(),
124*5f094258SMatthew Maurer                 &**file_ops,
125*5f094258SMatthew Maurer             )
126*5f094258SMatthew Maurer         };
127*5f094258SMatthew Maurer 
128*5f094258SMatthew Maurer         Entry {
129*5f094258SMatthew Maurer             entry,
130*5f094258SMatthew Maurer             _parent: None,
131*5f094258SMatthew Maurer             _phantom: PhantomData,
132*5f094258SMatthew Maurer         }
133*5f094258SMatthew Maurer     }
134*5f094258SMatthew Maurer }
135*5f094258SMatthew Maurer 
136*5f094258SMatthew Maurer impl Entry<'_> {
1375e40b591SMatthew Maurer     /// Constructs a placeholder DebugFS [`Entry`].
1385e40b591SMatthew Maurer     pub(crate) fn empty() -> Self {
1395e40b591SMatthew Maurer         Self {
1405e40b591SMatthew Maurer             entry: core::ptr::null_mut(),
1415e40b591SMatthew Maurer             _parent: None,
142*5f094258SMatthew Maurer             _phantom: PhantomData,
1435e40b591SMatthew Maurer         }
1445e40b591SMatthew Maurer     }
1455e40b591SMatthew Maurer 
1467f201ca1SMatthew Maurer     /// Returns the pointer representation of the DebugFS directory.
1477f201ca1SMatthew Maurer     ///
1487f201ca1SMatthew Maurer     /// # Guarantees
1497f201ca1SMatthew Maurer     ///
1507f201ca1SMatthew Maurer     /// Due to the type invariant, the value returned from this function will always be an error
1517f201ca1SMatthew Maurer     /// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as
1527f201ca1SMatthew Maurer     /// long as this entry lives.
1537f201ca1SMatthew Maurer     pub(crate) fn as_ptr(&self) -> *mut bindings::dentry {
1547f201ca1SMatthew Maurer         self.entry
1557f201ca1SMatthew Maurer     }
1567f201ca1SMatthew Maurer }
1577f201ca1SMatthew Maurer 
158*5f094258SMatthew Maurer impl Drop for Entry<'_> {
1597f201ca1SMatthew Maurer     fn drop(&mut self) {
1607f201ca1SMatthew Maurer         // SAFETY: `debugfs_remove` can take `NULL`, error values, and legal DebugFS dentries.
1617f201ca1SMatthew Maurer         // `as_ptr` guarantees that the pointer is of this form.
1627f201ca1SMatthew Maurer         unsafe { bindings::debugfs_remove(self.as_ptr()) }
1637f201ca1SMatthew Maurer     }
1647f201ca1SMatthew Maurer }
165