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