vfs/
token_registry.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Implementation of [`TokenRegistry`].
6
7use 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    /// Maps an owner to a handle used as a token for the owner.  Handles do not change their koid
30    /// value while they are alive.  We will use the koid of a handle we receive later from the user
31    /// of the API to find the owner that has this particular handle associated with it.
32    ///
33    /// Every entry in owner_to_token will have a reverse mapping in token_to_owner.
34    ///
35    /// Owners must be wrapped in Tokenizable which will ensure tokens are unregistered when
36    /// Tokenizable is dropped.  They must be pinned since pointers are used.  They must also
37    /// implement the TokenInterface trait which extracts the information that `get_owner` returns.
38    owner_to_token: HashMap<*const (), NullableHandle>,
39
40    /// Maps a koid of an owner to the owner.
41    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    /// Returns a token for the owner, creating one if one doesn't already exist.  Tokens will be
57    /// automatically removed when Tokenizable is dropped.
58    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    /// Returns the information provided by get_node_and_flags for the given token.  Returns None if
80    /// no such token exists (perhaps because the owner has been dropped).
81    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                // SAFETY: This is safe because Tokenizable's drop will ensure that unregister is
91                // called to avoid any dangling pointers.
92                Ok(Some(unsafe { (**owner).get_node() }))
93            }
94            None => Ok(None),
95        }
96    }
97
98    // Unregisters the token. This is done automatically by Tokenizable below.
99    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    /// Returns the node and flags that correspond with this token.  This information is returned by
111    /// the `get_owner` method.  For now this always returns Arc<dyn MutableDirectory> but it should
112    /// be possible to change this so that files can be represented in future if and when the need
113    /// arises.
114    fn get_node(&self) -> Arc<dyn MutableDirectory>;
115
116    /// Returns the token registry.
117    fn token_registry(&self) -> &TokenRegistry;
118}
119
120/// Tokenizable is to be used to wrap anything that might need to have tokens generated.  It will
121/// ensure that the token is unregistered when Tokenizable is dropped.
122#[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                // Note this ugly cast in place of `Arc::ptr_eq(&client, &res)` here is to ensure we
205                // don't compare vtable pointers, which are not strictly guaranteed to be the same
206                // across casts done in different code generation units at compilation time.
207                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}