Skip to main content

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;
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    /// Returns the node that corresponds with this token.  This information is returned by the
20    /// `get_owner` method.  For now this always returns Arc<dyn MutableDirectory> but it should be
21    /// possible to change this so that files can be represented in future if and when the need
22    /// arises.
23    fn get_node(&self) -> Arc<dyn MutableDirectory>;
24
25    /// Returns the rights of the connection this token is associated with.
26    fn get_rights(&self) -> fio::Rights;
27
28    /// Returns the token registry.
29    fn token_registry(&self) -> &TokenRegistry;
30}
31
32/// Tokenizable is to be used to wrap anything that might need to have tokens generated.  It will
33/// ensure that the token is unregistered when Tokenizable is dropped.
34#[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        /// Maps an owner to a handle used as a token for the owner.  Handles do not change their koid
87        /// value while they are alive.  We will use the koid of a handle we receive later from the user
88        /// of the API to find the owner that has this particular handle associated with it.
89        ///
90        /// Every entry in owner_to_token will have a reverse mapping in token_to_owner.
91        ///
92        /// Owners must be wrapped in Tokenizable which will ensure tokens are unregistered when
93        /// Tokenizable is dropped.  They must be pinned since pointers are used.  They must also
94        /// implement the TokenInterface trait which extracts the information that `get_owner` returns.
95        owner_to_token: HashMap<*const (), NullableHandle>,
96
97        /// Maps a koid of an owner to the owner.
98        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        /// Returns a token for the owner, creating one if one doesn't already exist.  Tokens will be
114        /// automatically removed when Tokenizable is dropped.
115        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        /// Returns the information provided by get_node_and_flags for the given token.  Returns None if
137        /// no such token exists (perhaps because the owner has been dropped).
138        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                    // SAFETY: This is safe because Tokenizable's drop will ensure that unregister is
148                    // called to avoid any dangling pointers.
149                    let owner = unsafe { &**owner_ptr };
150                    Ok(Some((owner.get_node(), owner.get_rights())))
151                }
152                None => Ok(None),
153            }
154        }
155
156        // Unregisters the token. This is done automatically by Tokenizable below.
157        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;