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