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