17f201ca1SMatthew Maurer // SPDX-License-Identifier: GPL-2.0 27f201ca1SMatthew Maurer // Copyright (C) 2025 Google LLC. 37f201ca1SMatthew Maurer 4*5e40b591SMatthew Maurer use crate::debugfs::file_ops::FileOps; 5*5e40b591SMatthew Maurer use crate::ffi::c_void; 67f201ca1SMatthew Maurer use crate::str::CStr; 77f201ca1SMatthew Maurer use crate::sync::Arc; 87f201ca1SMatthew Maurer 97f201ca1SMatthew Maurer /// Owning handle to a DebugFS entry. 107f201ca1SMatthew Maurer /// 117f201ca1SMatthew Maurer /// # Invariants 127f201ca1SMatthew Maurer /// 137f201ca1SMatthew Maurer /// The wrapped pointer will always be `NULL`, an error, or an owned DebugFS `dentry`. 147f201ca1SMatthew Maurer pub(crate) struct Entry { 157f201ca1SMatthew Maurer entry: *mut bindings::dentry, 167f201ca1SMatthew Maurer // If we were created with an owning parent, this is the keep-alive 177f201ca1SMatthew Maurer _parent: Option<Arc<Entry>>, 187f201ca1SMatthew Maurer } 197f201ca1SMatthew Maurer 207f201ca1SMatthew Maurer // SAFETY: [`Entry`] is just a `dentry` under the hood, which the API promises can be transferred 217f201ca1SMatthew Maurer // between threads. 227f201ca1SMatthew Maurer unsafe impl Send for Entry {} 237f201ca1SMatthew Maurer 247f201ca1SMatthew Maurer // SAFETY: All the C functions we call on the `dentry` pointer are threadsafe. 257f201ca1SMatthew Maurer unsafe impl Sync for Entry {} 267f201ca1SMatthew Maurer 277f201ca1SMatthew Maurer impl Entry { 287f201ca1SMatthew Maurer pub(crate) fn dynamic_dir(name: &CStr, parent: Option<Arc<Self>>) -> Self { 297f201ca1SMatthew Maurer let parent_ptr = match &parent { 307f201ca1SMatthew Maurer Some(entry) => entry.as_ptr(), 317f201ca1SMatthew Maurer None => core::ptr::null_mut(), 327f201ca1SMatthew Maurer }; 337f201ca1SMatthew Maurer // SAFETY: The invariants of this function's arguments ensure the safety of this call. 347f201ca1SMatthew Maurer // * `name` is a valid C string by the invariants of `&CStr`. 357f201ca1SMatthew Maurer // * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid 367f201ca1SMatthew Maurer // `dentry` by our invariant. `debugfs_create_dir` handles `NULL` pointers correctly. 377f201ca1SMatthew Maurer let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) }; 387f201ca1SMatthew Maurer 397f201ca1SMatthew Maurer Entry { 407f201ca1SMatthew Maurer entry, 417f201ca1SMatthew Maurer _parent: parent, 427f201ca1SMatthew Maurer } 437f201ca1SMatthew Maurer } 447f201ca1SMatthew Maurer 45*5e40b591SMatthew Maurer /// # Safety 46*5e40b591SMatthew Maurer /// 47*5e40b591SMatthew Maurer /// * `data` must outlive the returned `Entry`. 48*5e40b591SMatthew Maurer pub(crate) unsafe fn dynamic_file<T>( 49*5e40b591SMatthew Maurer name: &CStr, 50*5e40b591SMatthew Maurer parent: Arc<Self>, 51*5e40b591SMatthew Maurer data: &T, 52*5e40b591SMatthew Maurer file_ops: &'static FileOps<T>, 53*5e40b591SMatthew Maurer ) -> Self { 54*5e40b591SMatthew Maurer // SAFETY: The invariants of this function's arguments ensure the safety of this call. 55*5e40b591SMatthew Maurer // * `name` is a valid C string by the invariants of `&CStr`. 56*5e40b591SMatthew Maurer // * `parent.as_ptr()` is a pointer to a valid `dentry` by invariant. 57*5e40b591SMatthew Maurer // * The caller guarantees that `data` will outlive the returned `Entry`. 58*5e40b591SMatthew Maurer // * The guarantees on `FileOps` assert the vtable will be compatible with the data we have 59*5e40b591SMatthew Maurer // provided. 60*5e40b591SMatthew Maurer let entry = unsafe { 61*5e40b591SMatthew Maurer bindings::debugfs_create_file_full( 62*5e40b591SMatthew Maurer name.as_char_ptr(), 63*5e40b591SMatthew Maurer file_ops.mode(), 64*5e40b591SMatthew Maurer parent.as_ptr(), 65*5e40b591SMatthew Maurer core::ptr::from_ref(data) as *mut c_void, 66*5e40b591SMatthew Maurer core::ptr::null(), 67*5e40b591SMatthew Maurer &**file_ops, 68*5e40b591SMatthew Maurer ) 69*5e40b591SMatthew Maurer }; 70*5e40b591SMatthew Maurer 71*5e40b591SMatthew Maurer Entry { 72*5e40b591SMatthew Maurer entry, 73*5e40b591SMatthew Maurer _parent: Some(parent), 74*5e40b591SMatthew Maurer } 75*5e40b591SMatthew Maurer } 76*5e40b591SMatthew Maurer 77*5e40b591SMatthew Maurer /// Constructs a placeholder DebugFS [`Entry`]. 78*5e40b591SMatthew Maurer pub(crate) fn empty() -> Self { 79*5e40b591SMatthew Maurer Self { 80*5e40b591SMatthew Maurer entry: core::ptr::null_mut(), 81*5e40b591SMatthew Maurer _parent: None, 82*5e40b591SMatthew Maurer } 83*5e40b591SMatthew Maurer } 84*5e40b591SMatthew Maurer 857f201ca1SMatthew Maurer /// Returns the pointer representation of the DebugFS directory. 867f201ca1SMatthew Maurer /// 877f201ca1SMatthew Maurer /// # Guarantees 887f201ca1SMatthew Maurer /// 897f201ca1SMatthew Maurer /// Due to the type invariant, the value returned from this function will always be an error 907f201ca1SMatthew Maurer /// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as 917f201ca1SMatthew Maurer /// long as this entry lives. 927f201ca1SMatthew Maurer pub(crate) fn as_ptr(&self) -> *mut bindings::dentry { 937f201ca1SMatthew Maurer self.entry 947f201ca1SMatthew Maurer } 957f201ca1SMatthew Maurer } 967f201ca1SMatthew Maurer 977f201ca1SMatthew Maurer impl Drop for Entry { 987f201ca1SMatthew Maurer fn drop(&mut self) { 997f201ca1SMatthew Maurer // SAFETY: `debugfs_remove` can take `NULL`, error values, and legal DebugFS dentries. 1007f201ca1SMatthew Maurer // `as_ptr` guarantees that the pointer is of this form. 1017f201ca1SMatthew Maurer unsafe { bindings::debugfs_remove(self.as_ptr()) } 1027f201ca1SMatthew Maurer } 1037f201ca1SMatthew Maurer } 104