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    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        /// Maps an owner to a handle used as a token for the owner.  Handles do not change their koid
84        /// value while they are alive.  We will use the koid of a handle we receive later from the user
85        /// of the API to find the owner that has this particular handle associated with it.
86        ///
87        /// Every entry in owner_to_token will have a reverse mapping in token_to_owner.
88        ///
89        /// Owners must be wrapped in Tokenizable which will ensure tokens are unregistered when
90        /// Tokenizable is dropped.  They must be pinned since pointers are used.  They must also
91        /// implement the TokenInterface trait which extracts the information that `get_owner` returns.
92        owner_to_token: HashMap<*const (), NullableHandle>,
93
94        /// Maps a koid of an owner to the owner.
95        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        /// Returns a token for the owner, creating one if one doesn't already exist.  Tokens will be
111        /// automatically removed when Tokenizable is dropped.
112        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        /// Returns the information provided by get_node_and_flags for the given token.  Returns None if
134        /// no such token exists (perhaps because the owner has been dropped).
135        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                    // SAFETY: This is safe because Tokenizable's drop will ensure that unregister is
145                    // called to avoid any dangling pointers.
146                    let owner = unsafe { &**owner_ptr };
147                    Ok(Some((owner.get_node(), owner.get_rights())))
148                }
149                None => Ok(None),
150            }
151        }
152
153        // Unregisters the token. This is done automatically by Tokenizable below.
154        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;