summaryrefslogtreecommitdiff
path: root/rust/kernel/debugfs/entry.rs
blob: f99402cd3ba0ca12f62d3699e4d6e460d0085d26 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.

use crate::debugfs::file_ops::FileOps;
use crate::ffi::c_void;
use crate::str::CStr;
use crate::sync::Arc;
use core::marker::PhantomData;

/// Owning handle to a DebugFS entry.
///
/// # Invariants
///
/// The wrapped pointer will always be `NULL`, an error, or an owned DebugFS `dentry`.
pub(crate) struct Entry<'a> {
    entry: *mut bindings::dentry,
    // If we were created with an owning parent, this is the keep-alive
    _parent: Option<Arc<Entry<'static>>>,
    // If we were created with a non-owning parent, this prevents us from outliving it
    _phantom: PhantomData<&'a ()>,
}

// SAFETY: [`Entry`] is just a `dentry` under the hood, which the API promises can be transferred
// between threads.
unsafe impl Send for Entry<'_> {}

// SAFETY: All the C functions we call on the `dentry` pointer are threadsafe.
unsafe impl Sync for Entry<'_> {}

impl Entry<'static> {
    pub(crate) fn dynamic_dir(name: &CStr, parent: Option<Arc<Self>>) -> Self {
        let parent_ptr = match &parent {
            Some(entry) => entry.as_ptr(),
            None => core::ptr::null_mut(),
        };
        // SAFETY: The invariants of this function's arguments ensure the safety of this call.
        // * `name` is a valid C string by the invariants of `&CStr`.
        // * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
        //   `dentry` by our invariant. `debugfs_create_dir` handles `NULL` pointers correctly.
        let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };

        Entry {
            entry,
            _parent: parent,
            _phantom: PhantomData,
        }
    }

    /// # Safety
    ///
    /// * `data` must outlive the returned `Entry`.
    pub(crate) unsafe fn dynamic_file<T>(
        name: &CStr,
        parent: Arc<Self>,
        data: &T,
        file_ops: &'static FileOps<T>,
    ) -> Self {
        // SAFETY: The invariants of this function's arguments ensure the safety of this call.
        // * `name` is a valid C string by the invariants of `&CStr`.
        // * `parent.as_ptr()` is a pointer to a valid `dentry` by invariant.
        // * The caller guarantees that `data` will outlive the returned `Entry`.
        // * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
        //   provided.
        let entry = unsafe {
            bindings::debugfs_create_file_full(
                name.as_char_ptr(),
                file_ops.mode(),
                parent.as_ptr(),
                core::ptr::from_ref(data) as *mut c_void,
                core::ptr::null(),
                &**file_ops,
            )
        };

        Entry {
            entry,
            _parent: Some(parent),
            _phantom: PhantomData,
        }
    }
}

impl<'a> Entry<'a> {
    pub(crate) fn dir(name: &CStr, parent: Option<&'a Entry<'_>>) -> Self {
        let parent_ptr = match &parent {
            Some(entry) => entry.as_ptr(),
            None => core::ptr::null_mut(),
        };
        // SAFETY: The invariants of this function's arguments ensure the safety of this call.
        // * `name` is a valid C string by the invariants of `&CStr`.
        // * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
        //   `dentry` (because `parent` is a valid reference to an `Entry`). The lifetime `'a`
        //   ensures that the parent outlives this entry.
        let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };

        Entry {
            entry,
            _parent: None,
            _phantom: PhantomData,
        }
    }

    pub(crate) fn file<T>(
        name: &CStr,
        parent: &'a Entry<'_>,
        data: &'a T,
        file_ops: &FileOps<T>,
    ) -> Self {
        // SAFETY: The invariants of this function's arguments ensure the safety of this call.
        // * `name` is a valid C string by the invariants of `&CStr`.
        // * `parent.as_ptr()` is a pointer to a valid `dentry` because we have `&'a Entry`.
        // * `data` is a valid pointer to `T` for lifetime `'a`.
        // * The returned `Entry` has lifetime `'a`, so it cannot outlive `parent` or `data`.
        // * The caller guarantees that `vtable` is compatible with `data`.
        // * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
        //   provided.
        let entry = unsafe {
            bindings::debugfs_create_file_full(
                name.as_char_ptr(),
                file_ops.mode(),
                parent.as_ptr(),
                core::ptr::from_ref(data) as *mut c_void,
                core::ptr::null(),
                &**file_ops,
            )
        };

        Entry {
            entry,
            _parent: None,
            _phantom: PhantomData,
        }
    }
}

impl Entry<'_> {
    /// Constructs a placeholder DebugFS [`Entry`].
    pub(crate) fn empty() -> Self {
        Self {
            entry: core::ptr::null_mut(),
            _parent: None,
            _phantom: PhantomData,
        }
    }

    /// Returns the pointer representation of the DebugFS directory.
    ///
    /// # Guarantees
    ///
    /// Due to the type invariant, the value returned from this function will always be an error
    /// code, NULL, or a live DebugFS directory. If it is live, it will remain live at least as
    /// long as this entry lives.
    pub(crate) fn as_ptr(&self) -> *mut bindings::dentry {
        self.entry
    }
}

impl Drop for Entry<'_> {
    fn drop(&mut self) {
        // SAFETY: `debugfs_remove` can take `NULL`, error values, and legal DebugFS dentries.
        // `as_ptr` guarantees that the pointer is of this form.
        unsafe { bindings::debugfs_remove(self.as_ptr()) }
    }
}