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, 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    /// Maps an owner to a handle used as a token for the owner.  Handles do not change their koid
29    /// value while they are alive.  We will use the koid of a handle we receive later from the user
30    /// of the API to find the owner that has this particular handle associated with it.
31    ///
32    /// Every entry in owner_to_token will have a reverse mapping in token_to_owner.
33    ///
34    /// Owners must be wrapped in Tokenizable which will ensure tokens are unregistered when
35    /// Tokenizable is dropped.  They must be pinned since pointers are used.  They must also
36    /// implement the TokenInterface trait which extracts the information that `get_owner` returns.
37    owner_to_token: HashMap<*const (), Handle>,
38
39    /// Maps a koid of an owner to the owner.
40    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    /// Returns a token for the owner, creating one if one doesn't already exist.  Tokens will be
56    /// automatically removed when Tokenizable is dropped.
57    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    /// Returns the information provided by get_node_and_flags for the given token.  Returns None if
77    /// no such token exists (perhaps because the owner has been dropped).
78    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                // SAFETY: This is safe because Tokenizable's drop will ensure that unregister is
85                // called to avoid any dangling pointers.
86                Ok(Some(unsafe { (**owner).get_node() }))
87            }
88            None => Ok(None),
89        }
90    }
91
92    // Unregisters the token. This is done automatically by Tokenizable below.
93    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    /// Returns the node and flags that correspond with this token.  This information is returned by
105    /// the `get_owner` method.  For now this always returns Arc<dyn MutableDirectory> but it should
106    /// be possible to change this so that files can be represented in future if and when the need
107    /// arises.
108    fn get_node(&self) -> Arc<dyn MutableDirectory>;
109
110    /// Returns the token registry.
111    fn token_registry(&self) -> &TokenRegistry;
112}
113
114/// Tokenizable is to be used to wrap anything that might need to have tokens generated.  It will
115/// ensure that the token is unregistered when Tokenizable is dropped.
116#[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                // Note this ugly cast in place of `Arc::ptr_eq(&client, &res)` here is to ensure we
199                // don't compare vtable pointers, which are not strictly guaranteed to be the same
200                // across casts done in different code generation units at compilation time.
201                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}