1use crate::directory::entry_container::MutableDirectory;
8#[cfg(not(feature = "fdomain"))]
9use fidl::{Event, NullableHandle, Rights};
10use flex_fuchsia_io as fio;
11#[cfg(not(feature = "fdomain"))]
12use fuchsia_sync::Mutex;
13use pin_project::{pin_project, pinned_drop};
14use std::ops::{Deref, DerefMut};
15use std::pin::Pin;
16use std::sync::Arc;
17
18pub trait TokenInterface: 'static {
19 fn get_node(&self) -> Arc<dyn MutableDirectory>;
24
25 fn get_rights(&self) -> fio::Rights;
27
28 fn token_registry(&self) -> &TokenRegistry;
30}
31
32#[pin_project(!Unpin, PinnedDrop)]
35pub struct Tokenizable<T: TokenInterface>(#[pin] T);
36
37impl<T: TokenInterface> Tokenizable<T> {
38 pub fn new(inner: T) -> Self {
39 Self(inner)
40 }
41
42 pub fn as_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
43 self.project().0
44 }
45}
46
47impl<T: TokenInterface> Deref for Tokenizable<T> {
48 type Target = T;
49
50 fn deref(&self) -> &T {
51 &self.0
52 }
53}
54
55impl<T: TokenInterface> DerefMut for Tokenizable<T> {
56 fn deref_mut(&mut self) -> &mut T {
57 &mut self.0
58 }
59}
60
61#[pinned_drop]
62impl<T: TokenInterface> PinnedDrop for Tokenizable<T> {
63 fn drop(self: Pin<&mut Self>) {
64 self.0.token_registry().unregister(&self);
65 }
66}
67
68#[cfg(not(feature = "fdomain"))]
69mod implementation {
70 use super::*;
71 use std::collections::hash_map::{Entry, HashMap};
72 use zx_status::Status;
73
74 #[cfg(not(target_os = "fuchsia"))]
75 use fuchsia_async::emulated_handle::Koid;
76 #[cfg(target_os = "fuchsia")]
77 use zx::Koid;
78
79 const DEFAULT_TOKEN_RIGHTS: Rights = Rights::BASIC;
80
81 pub struct TokenRegistry {
82 inner: Mutex<Inner>,
83 }
84
85 struct Inner {
86 owner_to_token: HashMap<*const (), NullableHandle>,
96
97 token_to_owner: HashMap<Koid, *const dyn TokenInterface>,
99 }
100
101 unsafe impl Send for Inner {}
102
103 impl TokenRegistry {
104 pub fn new() -> Self {
105 Self {
106 inner: Mutex::new(Inner {
107 owner_to_token: HashMap::new(),
108 token_to_owner: HashMap::new(),
109 }),
110 }
111 }
112
113 pub fn get_token<T: TokenInterface>(
116 owner: Pin<&Tokenizable<T>>,
117 ) -> Result<NullableHandle, Status> {
118 let ptr = owner.get_ref() as *const _ as *const ();
119 let mut this = owner.token_registry().inner.lock();
120 let Inner { owner_to_token, token_to_owner, .. } = &mut *this;
121 match owner_to_token.entry(ptr) {
122 Entry::Occupied(o) => o.into_mut(),
123 Entry::Vacant(v) => {
124 let handle = Event::create().into_handle();
125 let koid = handle.koid()?;
126 assert!(
127 token_to_owner.insert(koid, &owner.0 as &dyn TokenInterface).is_none(),
128 "koid is a duplicate"
129 );
130 v.insert(handle)
131 }
132 }
133 .duplicate_handle(DEFAULT_TOKEN_RIGHTS)
134 }
135
136 pub fn get_owner_and_rights(
139 &self,
140 token: NullableHandle,
141 ) -> Result<Option<(Arc<dyn MutableDirectory>, fio::Rights)>, Status> {
142 let koid = token.koid()?;
143 let this = self.inner.lock();
144
145 match this.token_to_owner.get(&koid) {
146 Some(owner_ptr) => {
147 let owner = unsafe { &**owner_ptr };
150 Ok(Some((owner.get_node(), owner.get_rights())))
151 }
152 None => Ok(None),
153 }
154 }
155
156 pub(super) fn unregister<T: TokenInterface>(&self, owner: &Tokenizable<T>) {
158 let ptr = owner as *const _ as *const ();
159 let mut this = self.inner.lock();
160
161 if let Some(handle) = this.owner_to_token.remove(&ptr) {
162 this.token_to_owner.remove(&handle.koid().unwrap()).unwrap();
163 }
164 }
165 }
166
167 #[cfg(test)]
168 mod tests {
169 use super::*;
170 use futures::pin_mut;
171
172 #[test]
173 fn client_register_same_token() {
174 let registry = Arc::new(TokenRegistry::new());
175 let client = Tokenizable(mocks::MockChannel(
176 registry.clone(),
177 mocks::MockDirectory::new(),
178 fio::Rights::empty(),
179 ));
180 pin_mut!(client);
181
182 let token1 = TokenRegistry::get_token(client.as_ref()).unwrap();
183 let token2 = TokenRegistry::get_token(client.as_ref()).unwrap();
184
185 let koid1 = token1.koid().unwrap();
186 let koid2 = token2.koid().unwrap();
187 assert_eq!(koid1, koid2);
188 }
189
190 #[test]
191 fn token_rights() {
192 let registry = Arc::new(TokenRegistry::new());
193 let client = Tokenizable(mocks::MockChannel(
194 registry.clone(),
195 mocks::MockDirectory::new(),
196 fio::Rights::empty(),
197 ));
198 pin_mut!(client);
199
200 let token = TokenRegistry::get_token(client.as_ref()).unwrap();
201
202 assert_eq!(token.basic_info().unwrap().rights, DEFAULT_TOKEN_RIGHTS);
203 }
204
205 #[test]
206 fn client_unregister() {
207 let registry = Arc::new(TokenRegistry::new());
208
209 let token = {
210 let client = Tokenizable(mocks::MockChannel(
211 registry.clone(),
212 mocks::MockDirectory::new(),
213 fio::Rights::READ_BYTES,
214 ));
215 pin_mut!(client);
216
217 let token = TokenRegistry::get_token(client.as_ref()).unwrap();
218
219 {
220 let (res, rights) = registry
221 .get_owner_and_rights(token.duplicate_handle(Rights::SAME_RIGHTS).unwrap())
222 .unwrap()
223 .unwrap();
224 assert_eq!(Arc::as_ptr(&client.1) as *const (), Arc::as_ptr(&res) as *const ());
225 assert_eq!(rights, fio::Rights::READ_BYTES)
226 }
227
228 token
229 };
230
231 assert!(
232 registry
233 .get_owner_and_rights(token.duplicate_handle(Rights::SAME_RIGHTS).unwrap())
234 .unwrap()
235 .is_none(),
236 "`registry.get_owner() is not `None` after an connection dropped."
237 );
238 }
239
240 #[test]
241 fn client_get_token_twice_unregister() {
242 let registry = Arc::new(TokenRegistry::new());
243
244 let token = {
245 let client = Tokenizable(mocks::MockChannel(
246 registry.clone(),
247 mocks::MockDirectory::new(),
248 fio::Rights::empty(),
249 ));
250 pin_mut!(client);
251
252 let token = TokenRegistry::get_token(client.as_ref()).unwrap();
253
254 {
255 let token2 = TokenRegistry::get_token(client.as_ref()).unwrap();
256
257 let koid1 = token.koid().unwrap();
258 let koid2 = token2.koid().unwrap();
259 assert_eq!(koid1, koid2);
260 }
261
262 token
263 };
264
265 assert!(
266 registry
267 .get_owner_and_rights(token.duplicate_handle(Rights::SAME_RIGHTS).unwrap())
268 .unwrap()
269 .is_none(),
270 "`registry.get_owner() is not `None` after connection dropped."
271 );
272 }
273
274 mod mocks {
275 use super::*;
276 use crate::ObjectRequestRef;
277 use crate::directory::dirents_sink;
278 use crate::directory::entry::{EntryInfo, GetEntryInfo};
279 use crate::directory::entry_container::{
280 Directory, DirectoryWatcher, MutableDirectory,
281 };
282 use crate::directory::traversal_position::TraversalPosition;
283 use crate::execution_scope::ExecutionScope;
284 use crate::node::Node;
285 use crate::path::Path;
286
287 pub(super) struct MockChannel(
288 pub Arc<TokenRegistry>,
289 pub Arc<MockDirectory>,
290 pub fio::Rights,
291 );
292
293 impl TokenInterface for MockChannel {
294 fn get_node(&self) -> Arc<dyn MutableDirectory> {
295 self.1.clone()
296 }
297
298 fn get_rights(&self) -> fio::Rights {
299 self.2
300 }
301
302 fn token_registry(&self) -> &TokenRegistry {
303 &self.0
304 }
305 }
306
307 pub(super) struct MockDirectory {}
308
309 impl MockDirectory {
310 pub(super) fn new() -> Arc<Self> {
311 Arc::new(Self {})
312 }
313 }
314
315 impl GetEntryInfo for MockDirectory {
316 fn entry_info(&self) -> EntryInfo {
317 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
318 }
319 }
320
321 impl Node for MockDirectory {
322 async fn get_attributes(
323 &self,
324 _query: fio::NodeAttributesQuery,
325 ) -> Result<fio::NodeAttributes2, Status> {
326 unimplemented!("Not implemented");
327 }
328 }
329
330 impl Directory for MockDirectory {
331 fn open(
332 self: Arc<Self>,
333 _scope: ExecutionScope,
334 _path: Path,
335 _flags: fio::Flags,
336 _object_request: ObjectRequestRef<'_>,
337 ) -> Result<(), Status> {
338 unimplemented!("Not implemented");
339 }
340
341 async fn read_dirents(
342 &self,
343 _pos: &TraversalPosition,
344 _sink: Box<dyn dirents_sink::Sink>,
345 ) -> Result<(TraversalPosition, Box<dyn dirents_sink::Sealed>), Status>
346 {
347 unimplemented!("Not implemented!")
348 }
349
350 fn register_watcher(
351 self: Arc<Self>,
352 _scope: ExecutionScope,
353 _mask: fio::WatchMask,
354 _watcher: DirectoryWatcher,
355 ) -> Result<(), Status> {
356 unimplemented!("Not implemented!")
357 }
358
359 fn unregister_watcher(self: Arc<Self>, _key: usize) {
360 unimplemented!("Not implemented!")
361 }
362 }
363
364 impl MutableDirectory for MockDirectory {
365 async fn unlink(
366 self: Arc<Self>,
367 _name: &str,
368 _must_be_directory: bool,
369 ) -> Result<(), Status> {
370 unimplemented!("Not implemented!")
371 }
372
373 async fn update_attributes(
374 &self,
375 _attributes: fio::MutableNodeAttributes,
376 ) -> Result<(), Status> {
377 unimplemented!("Not implemented!")
378 }
379
380 async fn sync(&self) -> Result<(), Status> {
381 unimplemented!("Not implemented!");
382 }
383 }
384 }
385 }
386}
387
388#[cfg(feature = "fdomain")]
389mod implementation {
390 use super::*;
391 use flex_client::NullableHandle;
392 use zx_status::Status;
393
394 pub struct TokenRegistry;
395
396 impl TokenRegistry {
397 pub fn new() -> Self {
398 Self
399 }
400
401 pub fn get_token<T: TokenInterface>(
402 _owner: Pin<&Tokenizable<T>>,
403 ) -> Result<NullableHandle, Status> {
404 Err(Status::NOT_SUPPORTED)
405 }
406
407 pub fn get_owner_and_rights(
408 &self,
409 _token: NullableHandle,
410 ) -> Result<Option<(Arc<dyn MutableDirectory>, fio::Rights)>, Status> {
411 Err(Status::NOT_SUPPORTED)
412 }
413
414 pub(super) fn unregister<T: TokenInterface>(&self, _owner: &Tokenizable<T>) {}
415 }
416}
417
418pub use implementation::TokenRegistry;