1*7f201ca1SMatthew Maurer // SPDX-License-Identifier: GPL-2.0 2*7f201ca1SMatthew Maurer // Copyright (C) 2025 Google LLC. 3*7f201ca1SMatthew Maurer 4*7f201ca1SMatthew Maurer use crate::str::CStr; 5*7f201ca1SMatthew Maurer use crate::sync::Arc; 6*7f201ca1SMatthew Maurer 7*7f201ca1SMatthew Maurer /// Owning handle to a DebugFS entry. 8*7f201ca1SMatthew Maurer /// 9*7f201ca1SMatthew Maurer /// # Invariants 10*7f201ca1SMatthew Maurer /// 11*7f201ca1SMatthew Maurer /// The wrapped pointer will always be `NULL`, an error, or an owned DebugFS `dentry`. 12*7f201ca1SMatthew Maurer pub(crate) struct Entry { 13*7f201ca1SMatthew Maurer entry: *mut bindings::dentry, 14*7f201ca1SMatthew Maurer // If we were created with an owning parent, this is the keep-alive 15*7f201ca1SMatthew Maurer _parent: Option<Arc<Entry>>, 16*7f201ca1SMatthew Maurer } 17*7f201ca1SMatthew Maurer 18*7f201ca1SMatthew Maurer // SAFETY: [`Entry`] is just a `dentry` under the hood, which the API promises can be transferred 19*7f201ca1SMatthew Maurer // between threads. 20*7f201ca1SMatthew Maurer unsafe impl Send for Entry {} 21*7f201ca1SMatthew Maurer 22*7f201ca1SMatthew Maurer // SAFETY: All the C functions we call on the `dentry` pointer are threadsafe. 23*7f201ca1SMatthew Maurer unsafe impl Sync for Entry {} 24*7f201ca1SMatthew Maurer 25*7f201ca1SMatthew Maurer impl Entry { 26*7f201ca1SMatthew Maurer pub(crate) fn dynamic_dir(name: &CStr, parent: Option<Arc<Self>>) -> Self { 27*7f201ca1SMatthew Maurer let parent_ptr = match &parent { 28*7f201ca1SMatthew Maurer Some(entry) => entry.as_ptr(), 29*7f201ca1SMatthew Maurer None => core::ptr::null_mut(), 30*7f201ca1SMatthew Maurer }; 31*7f201ca1SMatthew Maurer // SAFETY: The invariants of this function's arguments ensure the safety of this call. 32*7f201ca1SMatthew Maurer // * `name` is a valid C string by the invariants of `&CStr`. 33*7f201ca1SMatthew Maurer // * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid 34*7f201ca1SMatthew Maurer // `dentry` by our invariant. `debugfs_create_dir` handles `NULL` pointers correctly. 35*7f201ca1SMatthew Maurer let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) }; 36*7f201ca1SMatthew Maurer 37*7f201ca1SMatthew Maurer Entry { 38*7f201ca1SMatthew Maurer entry, 39*7f201ca1SMatthew Maurer _parent: parent, 40*7f201ca1SMatthew Maurer } 41*7f201ca1SMatthew Maurer } 42*7f201ca1SMatthew Maurer 43*7f201ca1SMatthew Maurer /// Returns the pointer representation of the DebugFS directory. 44*7f201ca1SMatthew Maurer /// 45*7f201ca1SMatthew Maurer /// # Guarantees 46*7f201ca1SMatthew Maurer /// 47*7f201ca1SMatthew Maurer /// Due to the type invariant, the value returned from this function will always be an error 48*7f201ca1SMatthew Maurer /// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as 49*7f201ca1SMatthew Maurer /// long as this entry lives. 50*7f201ca1SMatthew Maurer pub(crate) fn as_ptr(&self) -> *mut bindings::dentry { 51*7f201ca1SMatthew Maurer self.entry 52*7f201ca1SMatthew Maurer } 53*7f201ca1SMatthew Maurer } 54*7f201ca1SMatthew Maurer 55*7f201ca1SMatthew Maurer impl Drop for Entry { 56*7f201ca1SMatthew Maurer fn drop(&mut self) { 57*7f201ca1SMatthew Maurer // SAFETY: `debugfs_remove` can take `NULL`, error values, and legal DebugFS dentries. 58*7f201ca1SMatthew Maurer // `as_ptr` guarantees that the pointer is of this form. 59*7f201ca1SMatthew Maurer unsafe { bindings::debugfs_remove(self.as_ptr()) } 60*7f201ca1SMatthew Maurer } 61*7f201ca1SMatthew Maurer } 62