vfs/
token_registry.rs
1use crate::directory::entry_container::MutableDirectory;
8use fidl::{Event, Handle, HandleBased, Rights};
9use pin_project::{pin_project, pinned_drop};
10use std::collections::hash_map::{Entry, HashMap};
11use std::ops::{Deref, DerefMut};
12use std::pin::Pin;
13use std::sync::{Arc, Mutex};
14use zx_status::Status;
15
16#[cfg(not(target_os = "fuchsia"))]
17use fuchsia_async::emulated_handle::{AsHandleRef, Koid};
18#[cfg(target_os = "fuchsia")]
19use zx::{AsHandleRef, Koid};
20
21const DEFAULT_TOKEN_RIGHTS: Rights = Rights::BASIC;
22
23pub struct TokenRegistry {
24 inner: Mutex<Inner>,
25}
26
27struct Inner {
28 owner_to_token: HashMap<*const (), Handle>,
38
39 token_to_owner: HashMap<Koid, *const dyn TokenInterface>,
41}
42
43unsafe impl Send for Inner {}
44
45impl TokenRegistry {
46 pub fn new() -> Self {
47 Self {
48 inner: Mutex::new(Inner {
49 owner_to_token: HashMap::new(),
50 token_to_owner: HashMap::new(),
51 }),
52 }
53 }
54
55 pub fn get_token<T: TokenInterface>(owner: Pin<&Tokenizable<T>>) -> Result<Handle, Status> {
58 let ptr = owner.get_ref() as *const _ as *const ();
59 let mut this = owner.token_registry().inner.lock().unwrap();
60 let Inner { owner_to_token, token_to_owner, .. } = &mut *this;
61 match owner_to_token.entry(ptr) {
62 Entry::Occupied(o) => o.into_mut(),
63 Entry::Vacant(v) => {
64 let handle = Event::create().into_handle();
65 let koid = handle.get_koid()?;
66 assert!(
67 token_to_owner.insert(koid, &owner.0 as &dyn TokenInterface).is_none(),
68 "koid is a duplicate"
69 );
70 v.insert(handle)
71 }
72 }
73 .duplicate_handle(DEFAULT_TOKEN_RIGHTS)
74 }
75
76 pub fn get_owner(&self, token: Handle) -> Result<Option<Arc<dyn MutableDirectory>>, Status> {
79 let koid = token.get_koid()?;
80 let this = self.inner.lock().unwrap();
81
82 match this.token_to_owner.get(&koid) {
83 Some(owner) => {
84 Ok(Some(unsafe { (**owner).get_node() }))
87 }
88 None => Ok(None),
89 }
90 }
91
92 fn unregister<T: TokenInterface>(&self, owner: &Tokenizable<T>) {
94 let ptr = owner as *const _ as *const ();
95 let mut this = self.inner.lock().unwrap();
96
97 if let Some(handle) = this.owner_to_token.remove(&ptr) {
98 this.token_to_owner.remove(&handle.get_koid().unwrap()).unwrap();
99 }
100 }
101}
102
103pub trait TokenInterface: 'static {
104 fn get_node(&self) -> Arc<dyn MutableDirectory>;
109
110 fn token_registry(&self) -> &TokenRegistry;
112}
113
114#[pin_project(!Unpin, PinnedDrop)]
117pub struct Tokenizable<T: TokenInterface>(#[pin] T);
118
119impl<T: TokenInterface> Tokenizable<T> {
120 pub fn new(inner: T) -> Self {
121 Self(inner)
122 }
123
124 pub fn as_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
125 self.project().0
126 }
127}
128
129impl<T: TokenInterface> Deref for Tokenizable<T> {
130 type Target = T;
131
132 fn deref(&self) -> &T {
133 &self.0
134 }
135}
136
137impl<T: TokenInterface> DerefMut for Tokenizable<T> {
138 fn deref_mut(&mut self) -> &mut T {
139 &mut self.0
140 }
141}
142
143#[pinned_drop]
144impl<T: TokenInterface> PinnedDrop for Tokenizable<T> {
145 fn drop(self: Pin<&mut Self>) {
146 self.0.token_registry().unregister(&self);
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use self::mocks::{MockChannel, MockDirectory};
153 use super::{TokenRegistry, Tokenizable, DEFAULT_TOKEN_RIGHTS};
154 use fidl::{AsHandleRef, HandleBased, Rights};
155 use futures::pin_mut;
156 use std::sync::Arc;
157
158 #[test]
159 fn client_register_same_token() {
160 let registry = Arc::new(TokenRegistry::new());
161 let client = Tokenizable(MockChannel(registry.clone(), MockDirectory::new()));
162 pin_mut!(client);
163
164 let token1 = TokenRegistry::get_token(client.as_ref()).unwrap();
165 let token2 = TokenRegistry::get_token(client.as_ref()).unwrap();
166
167 let koid1 = token1.get_koid().unwrap();
168 let koid2 = token2.get_koid().unwrap();
169 assert_eq!(koid1, koid2);
170 }
171
172 #[test]
173 fn token_rights() {
174 let registry = Arc::new(TokenRegistry::new());
175 let client = Tokenizable(MockChannel(registry.clone(), MockDirectory::new()));
176 pin_mut!(client);
177
178 let token = TokenRegistry::get_token(client.as_ref()).unwrap();
179
180 assert_eq!(token.basic_info().unwrap().rights, DEFAULT_TOKEN_RIGHTS);
181 }
182
183 #[test]
184 fn client_unregister() {
185 let registry = Arc::new(TokenRegistry::new());
186
187 let token = {
188 let client = Tokenizable(MockChannel(registry.clone(), MockDirectory::new()));
189 pin_mut!(client);
190
191 let token = TokenRegistry::get_token(client.as_ref()).unwrap();
192
193 {
194 let res = registry
195 .get_owner(token.duplicate_handle(Rights::SAME_RIGHTS).unwrap())
196 .unwrap()
197 .unwrap();
198 assert_eq!(Arc::as_ptr(&client.1) as *const (), Arc::as_ptr(&res) as *const ());
202 }
203
204 token
205 };
206
207 assert!(
208 registry
209 .get_owner(token.duplicate_handle(Rights::SAME_RIGHTS).unwrap())
210 .unwrap()
211 .is_none(),
212 "`registry.get_owner() is not `None` after an connection dropped."
213 );
214 }
215
216 #[test]
217 fn client_get_token_twice_unregister() {
218 let registry = Arc::new(TokenRegistry::new());
219
220 let token = {
221 let client = Tokenizable(MockChannel(registry.clone(), MockDirectory::new()));
222 pin_mut!(client);
223
224 let token = TokenRegistry::get_token(client.as_ref()).unwrap();
225
226 {
227 let token2 = TokenRegistry::get_token(client.as_ref()).unwrap();
228
229 let koid1 = token.get_koid().unwrap();
230 let koid2 = token2.get_koid().unwrap();
231 assert_eq!(koid1, koid2);
232 }
233
234 token
235 };
236
237 assert!(
238 registry
239 .get_owner(token.duplicate_handle(Rights::SAME_RIGHTS).unwrap())
240 .unwrap()
241 .is_none(),
242 "`registry.get_owner() is not `None` after connection dropped."
243 );
244 }
245
246 mod mocks {
247 use crate::directory::dirents_sink;
248 use crate::directory::entry::{EntryInfo, GetEntryInfo};
249 use crate::directory::entry_container::{Directory, DirectoryWatcher, MutableDirectory};
250 use crate::directory::traversal_position::TraversalPosition;
251 use crate::execution_scope::ExecutionScope;
252 use crate::node::Node;
253 use crate::path::Path;
254 use crate::token_registry::{TokenInterface, TokenRegistry};
255 use crate::ObjectRequestRef;
256 use fidl::endpoints::ServerEnd;
257 use fidl_fuchsia_io as fio;
258 use std::sync::Arc;
259 use zx_status::Status;
260
261 pub(super) struct MockChannel(pub Arc<TokenRegistry>, pub Arc<MockDirectory>);
262
263 impl TokenInterface for MockChannel {
264 fn get_node(&self) -> Arc<dyn MutableDirectory> {
265 self.1.clone()
266 }
267
268 fn token_registry(&self) -> &TokenRegistry {
269 &self.0
270 }
271 }
272
273 pub(super) struct MockDirectory {}
274
275 impl MockDirectory {
276 pub(super) fn new() -> Arc<Self> {
277 Arc::new(Self {})
278 }
279 }
280
281 impl GetEntryInfo for MockDirectory {
282 fn entry_info(&self) -> EntryInfo {
283 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
284 }
285 }
286
287 impl Node for MockDirectory {
288 async fn get_attributes(
289 &self,
290 _query: fio::NodeAttributesQuery,
291 ) -> Result<fio::NodeAttributes2, Status> {
292 unimplemented!("Not implemented");
293 }
294 }
295
296 impl Directory for MockDirectory {
297 fn open(
298 self: Arc<Self>,
299 _scope: ExecutionScope,
300 _flags: fio::OpenFlags,
301 _path: Path,
302 _server_end: ServerEnd<fio::NodeMarker>,
303 ) {
304 }
305
306 fn open3(
307 self: Arc<Self>,
308 _scope: ExecutionScope,
309 _path: Path,
310 _flags: fio::Flags,
311 _object_request: ObjectRequestRef<'_>,
312 ) -> Result<(), Status> {
313 unimplemented!("Not implemented");
314 }
315
316 async fn read_dirents<'a>(
317 &'a self,
318 _pos: &'a TraversalPosition,
319 _sink: Box<dyn dirents_sink::Sink>,
320 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status> {
321 unimplemented!("Not implemented!")
322 }
323
324 fn register_watcher(
325 self: Arc<Self>,
326 _scope: ExecutionScope,
327 _mask: fio::WatchMask,
328 _watcher: DirectoryWatcher,
329 ) -> Result<(), Status> {
330 unimplemented!("Not implemented!")
331 }
332
333 fn unregister_watcher(self: Arc<Self>, _key: usize) {
334 unimplemented!("Not implemented!")
335 }
336 }
337
338 impl MutableDirectory for MockDirectory {
339 async fn unlink(
340 self: Arc<Self>,
341 _name: &str,
342 _must_be_directory: bool,
343 ) -> Result<(), Status> {
344 unimplemented!("Not implemented!")
345 }
346
347 async fn update_attributes(
348 &self,
349 _attributes: fio::MutableNodeAttributes,
350 ) -> Result<(), Status> {
351 unimplemented!("Not implemented!")
352 }
353
354 async fn sync(&self) -> Result<(), Status> {
355 unimplemented!("Not implemented!");
356 }
357 }
358 }
359}