Skip to main content

fdomain_container/
handles.rs

1// Copyright 2024 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
5use fidl::{AsHandleRef, Peered, SocketOpts};
6use fidl_fuchsia_fdomain as proto;
7
8/// This is implemented on the `fidl::*` objects for every type of handle FDomain
9/// supports. It essentially makes the handle object a responder to a stream of
10/// [`HandleOperation`]s.
11pub trait HandleType:
12    Sync + Sized + Into<fidl::NullableHandle> + fidl::AsHandleRef + 'static
13{
14    /// This should be the handle type corresponding to the implementing handle.
15    /// We use this to generalize some of the error reporting in this trait.
16    fn object_type(&self) -> fidl::ObjectType;
17
18    /// Returns `Ok` if this handle is the given type, `Err` otherwise.
19    fn expected_type(&self, expected_type: fidl::ObjectType) -> Result<(), proto::Error> {
20        if expected_type == self.object_type() {
21            Ok(())
22        } else {
23            Err(proto::Error::WrongHandleType(proto::WrongHandleType {
24                expected: expected_type,
25                got: self.object_type(),
26            }))
27        }
28    }
29
30    /// Implements [`HandleOperation::SocketDisposition`].
31    fn socket_disposition(
32        &self,
33        _disposition: proto::SocketDisposition,
34        _disposition_peer: proto::SocketDisposition,
35    ) -> Result<(), proto::Error> {
36        self.expected_type(fidl::ObjectType::SOCKET)
37    }
38
39    /// Implements [`HandleOperation::ReadSocket`].
40    fn read_socket(&self, _max_bytes: u64) -> Result<Option<Vec<u8>>, proto::Error> {
41        Err(self.expected_type(fidl::ObjectType::SOCKET).unwrap_err())
42    }
43
44    /// Implements [`HandleOperation::ReadChannel`].
45    fn read_channel(&self) -> Result<Option<fidl::MessageBufEtc>, proto::Error> {
46        Err(self.expected_type(fidl::ObjectType::CHANNEL).unwrap_err())
47    }
48
49    /// Implements [`HandleOperation::WriteSocket`].
50    fn write_socket(&self, _data: &[u8]) -> Result<usize, proto::Error> {
51        Err(self.expected_type(fidl::ObjectType::SOCKET).unwrap_err())
52    }
53
54    /// Implements [`HandleOperation::WriteChannel`].
55    fn write_channel(
56        &self,
57        _data: &[u8],
58        _handles: &mut Vec<fidl::HandleDisposition<'static>>,
59    ) -> Option<Result<(), proto::WriteChannelError>> {
60        Some(Err(proto::WriteChannelError::Error(
61            self.expected_type(fidl::ObjectType::CHANNEL).unwrap_err(),
62        )))
63    }
64}
65
66impl HandleType for fidl::Socket {
67    fn object_type(&self) -> fidl::ObjectType {
68        fidl::ObjectType::SOCKET
69    }
70
71    #[cfg(not(target_os = "fuchsia"))]
72    fn socket_disposition(
73        &self,
74        _disposition: proto::SocketDisposition,
75        _disposition_peer: proto::SocketDisposition,
76    ) -> Result<(), proto::Error> {
77        Err(proto::Error::TargetError(fidl::Status::NOT_SUPPORTED.into_raw()))
78    }
79
80    #[cfg(target_os = "fuchsia")]
81    fn socket_disposition(
82        &self,
83        disposition: proto::SocketDisposition,
84        disposition_peer: proto::SocketDisposition,
85    ) -> Result<(), proto::Error> {
86        fn map_disposition(
87            disposition: proto::SocketDisposition,
88        ) -> Result<Option<zx::SocketWriteDisposition>, proto::Error> {
89            match disposition {
90                proto::SocketDisposition::NoChange => Ok(None),
91                proto::SocketDisposition::WriteDisabled => {
92                    Ok(Some(zx::SocketWriteDisposition::Disabled))
93                }
94                proto::SocketDisposition::WriteEnabled => {
95                    Ok(Some(zx::SocketWriteDisposition::Enabled))
96                }
97                disposition => {
98                    Err(proto::Error::SocketDispositionUnknown(proto::SocketDispositionUnknown {
99                        disposition,
100                    }))
101                }
102            }
103        }
104
105        self.set_disposition(map_disposition(disposition)?, map_disposition(disposition_peer)?)
106            .map_err(|e| proto::Error::TargetError(e.into_raw()))
107    }
108
109    fn read_socket(&self, max_bytes: u64) -> Result<Option<Vec<u8>>, proto::Error> {
110        let max_bytes = max_bytes.try_into().unwrap_or(usize::MAX);
111        let mut buf = Vec::with_capacity(max_bytes);
112        // SAFETY: We've derived this pointer and length from a vec in a way
113        // that should guarantee they are valid.
114        #[cfg(target_os = "fuchsia")]
115        let read = unsafe {
116            let buf_ptr = buf.spare_capacity_mut();
117            let buf_ptr = buf_ptr.as_mut_ptr().cast::<u8>();
118            self.read_raw(buf_ptr, max_bytes)
119        };
120        #[cfg(not(target_os = "fuchsia"))]
121        let read = {
122            buf.resize(max_bytes, 0);
123            self.read(&mut buf)
124        };
125        match read {
126            Ok(size) => {
127                // SAFETY: The `read_raw` call should guarantee that this many
128                // bytes in the capacity have now been initialized.
129                #[cfg(target_os = "fuchsia")]
130                unsafe {
131                    buf.set_len(size);
132                }
133                #[cfg(not(target_os = "fuchsia"))]
134                buf.truncate(size);
135                Ok(Some(buf))
136            }
137            Err(fidl::Status::SHOULD_WAIT) => Ok(None),
138            Err(other) => Err(proto::Error::TargetError(other.into_raw())),
139        }
140    }
141
142    fn write_socket(&self, data: &[u8]) -> Result<usize, proto::Error> {
143        let mut wrote = 0;
144        loop {
145            match self.write(&data[wrote..]) {
146                Ok(count) => {
147                    wrote += count;
148
149                    if wrote >= data.len() {
150                        break Ok(wrote);
151                    }
152                }
153                Err(fidl::Status::SHOULD_WAIT) => break Ok(wrote),
154
155                Err(other) => break Err(proto::Error::TargetError(other.into_raw())),
156            }
157        }
158    }
159}
160
161impl HandleType for fidl::Channel {
162    fn object_type(&self) -> fidl::ObjectType {
163        fidl::ObjectType::CHANNEL
164    }
165
166    fn read_channel(&self) -> Result<Option<fidl::MessageBufEtc>, proto::Error> {
167        let mut buf = fidl::MessageBufEtc::new();
168        match self.read_etc(&mut buf) {
169            Err(fidl::Status::SHOULD_WAIT) => Ok(None),
170            other => other.map(|_| Some(buf)).map_err(|e| proto::Error::TargetError(e.into_raw())),
171        }
172    }
173
174    fn write_channel(
175        &self,
176        data: &[u8],
177        handles: &mut Vec<fidl::HandleDisposition<'static>>,
178    ) -> Option<Result<(), proto::WriteChannelError>> {
179        match self.write_etc(data, handles) {
180            Ok(()) => Some(Ok(())),
181            Err(fidl::Status::SHOULD_WAIT) => None,
182            Err(other) => {
183                if handles.iter().any(|x| x.result != fidl::Status::OK) {
184                    Some(Err(proto::WriteChannelError::OpErrors(
185                        handles
186                            .iter_mut()
187                            .map(|x| {
188                                Result::from(x.result)
189                                    .err()
190                                    .map(|e| Box::new(proto::Error::TargetError(e.into_raw())))
191                            })
192                            .collect(),
193                    )))
194                } else {
195                    Some(Err(proto::WriteChannelError::Error(proto::Error::TargetError(
196                        other.into_raw(),
197                    ))))
198                }
199            }
200        }
201    }
202}
203
204impl HandleType for fidl::EventPair {
205    fn object_type(&self) -> fidl::ObjectType {
206        fidl::ObjectType::EVENTPAIR
207    }
208}
209
210impl HandleType for fidl::Event {
211    fn object_type(&self) -> fidl::ObjectType {
212        fidl::ObjectType::EVENT
213    }
214}
215
216pub struct Unknown(pub fidl::NullableHandle, pub fidl::ObjectType);
217
218impl Into<fidl::NullableHandle> for Unknown {
219    fn into(self) -> fidl::NullableHandle {
220        self.0
221    }
222}
223
224impl fidl::AsHandleRef for Unknown {
225    fn as_handle_ref(&self) -> fidl::HandleRef<'_> {
226        self.0.as_handle_ref()
227    }
228}
229
230impl HandleType for Unknown {
231    fn object_type(&self) -> fidl::ObjectType {
232        self.1
233    }
234}
235
236pub enum AnyHandle {
237    Socket(fidl::Socket),
238    EventPair(fidl::EventPair),
239    Event(fidl::Event),
240    Channel(fidl::Channel),
241    Unknown(Unknown),
242}
243
244/// Whether a socket is a datagram socket.
245#[derive(Debug, Copy, Clone, PartialEq, Eq)]
246pub(crate) enum IsDatagramSocket {
247    NotDatagram,
248    Datagram,
249    Unknown,
250}
251
252impl IsDatagramSocket {
253    pub fn is_datagram(&self) -> bool {
254        matches!(self, IsDatagramSocket::Datagram)
255    }
256}
257
258impl AnyHandle {
259    /// Signals that indicate we should do write processing on a handle.
260    pub(crate) fn write_signals(&self) -> fidl::Signals {
261        let sock_signals = if let AnyHandle::Socket(_) = self {
262            fidl::Signals::SOCKET_WRITE_DISABLED
263        } else {
264            fidl::Signals::empty()
265        };
266
267        sock_signals | fidl::Signals::OBJECT_WRITABLE | fidl::Signals::OBJECT_PEER_CLOSED
268    }
269
270    /// Signals that indicate we should do write processing on a handle.
271    pub(crate) fn read_signals(&self) -> fidl::Signals {
272        let sock_signals = if let AnyHandle::Socket(_) = self {
273            fidl::Signals::SOCKET_PEER_WRITE_DISABLED
274        } else {
275            fidl::Signals::empty()
276        };
277
278        sock_signals | fidl::Signals::OBJECT_READABLE | fidl::Signals::OBJECT_PEER_CLOSED
279    }
280
281    /// Whether this is a datagram socket.
282    pub(crate) fn is_datagram_socket(&self) -> IsDatagramSocket {
283        let AnyHandle::Socket(socket) = self else {
284            return IsDatagramSocket::NotDatagram;
285        };
286
287        let Ok(info) = socket.info() else {
288            return IsDatagramSocket::Unknown;
289        };
290
291        if info.options == SocketOpts::DATAGRAM {
292            IsDatagramSocket::Datagram
293        } else {
294            IsDatagramSocket::NotDatagram
295        }
296    }
297
298    /// zx_handle_duplicate but preserving our metadata.
299    pub fn duplicate(&self, rights: fidl::Rights) -> Result<AnyHandle, proto::Error> {
300        let handle = self
301            .as_handle_ref()
302            .duplicate_handle(rights)
303            .map_err(|e| proto::Error::TargetError(e.into_raw()))?;
304
305        Ok(match self {
306            AnyHandle::Socket(_) => AnyHandle::Socket(fidl::Socket::from(handle)),
307            AnyHandle::EventPair(_) => AnyHandle::EventPair(fidl::EventPair::from(handle)),
308            AnyHandle::Event(_) => AnyHandle::Event(fidl::Event::from(handle)),
309            AnyHandle::Channel(_) => AnyHandle::Channel(fidl::Channel::from(handle)),
310            AnyHandle::Unknown(Unknown(_, ty)) => AnyHandle::Unknown(Unknown(handle, *ty)),
311        })
312    }
313
314    /// zx_handle_replace but preserving our metadata.
315    #[cfg(not(target_os = "fuchsia"))]
316    pub fn replace(self, _rights: fidl::Rights) -> Result<AnyHandle, proto::Error> {
317        Err(proto::Error::TargetError(fidl::Status::NOT_SUPPORTED.into_raw()))
318    }
319
320    /// zx_handle_replace but preserving our metadata.
321    #[cfg(target_os = "fuchsia")]
322    pub fn replace(self, rights: fidl::Rights) -> Result<AnyHandle, proto::Error> {
323        Ok(match self {
324            AnyHandle::Socket(h) => AnyHandle::Socket(
325                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
326            ),
327            AnyHandle::EventPair(h) => AnyHandle::EventPair(
328                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
329            ),
330            AnyHandle::Event(h) => AnyHandle::Event(
331                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
332            ),
333            AnyHandle::Channel(h) => AnyHandle::Channel(
334                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
335            ),
336            AnyHandle::Unknown(Unknown(h, ty)) => AnyHandle::Unknown(Unknown(
337                h.replace_handle(rights).map_err(|e| proto::Error::TargetError(e.into_raw()))?,
338                ty,
339            )),
340        })
341    }
342
343    pub fn signal(&self, clear: fidl::Signals, set: fidl::Signals) -> Result<(), proto::Error> {
344        match self {
345            AnyHandle::Socket(s) => s.signal(clear, set),
346            AnyHandle::EventPair(e) => e.signal(clear, set),
347            AnyHandle::Event(e) => e.signal(clear, set),
348            AnyHandle::Channel(c) => c.signal(clear, set),
349            AnyHandle::Unknown(u) => u.0.signal(clear, set),
350        }
351        .map_err(|e| proto::Error::TargetError(e.into_raw()))
352    }
353
354    pub fn signal_peer(
355        &self,
356        clear: fidl::Signals,
357        set: fidl::Signals,
358    ) -> Result<(), proto::Error> {
359        // TODO: Rust is being helpful and only letting us signal peers for
360        // things we know have them, but we'd really like to just make the
361        // syscall and get it to report whatever error it will. Especially for
362        // `Unknown`, which may well have peers the type system just isn't aware
363        // of.
364        match self {
365            AnyHandle::Socket(h) => h.signal_peer(clear, set),
366            AnyHandle::EventPair(h) => h.signal_peer(clear, set),
367            AnyHandle::Event(_) => Err(fidl::Status::INVALID_ARGS),
368            AnyHandle::Channel(h) => h.signal_peer(clear, set),
369            AnyHandle::Unknown(_) => Err(fidl::Status::INVALID_ARGS),
370        }
371        .map_err(|e| proto::Error::TargetError(e.into_raw()))
372    }
373}
374
375impl From<fidl::Channel> for AnyHandle {
376    fn from(other: fidl::Channel) -> AnyHandle {
377        AnyHandle::Channel(other)
378    }
379}
380
381impl From<fidl::Socket> for AnyHandle {
382    fn from(other: fidl::Socket) -> AnyHandle {
383        AnyHandle::Socket(other)
384    }
385}
386
387impl From<fidl::EventPair> for AnyHandle {
388    fn from(other: fidl::EventPair) -> AnyHandle {
389        AnyHandle::EventPair(other)
390    }
391}
392
393impl From<fidl::Event> for AnyHandle {
394    fn from(other: fidl::Event) -> AnyHandle {
395        AnyHandle::Event(other)
396    }
397}
398
399macro_rules! impl_method {
400    ($this:ident => $h:ident . $meth:ident ( $($args:tt)* )) => {
401        match $this {
402            AnyHandle::Socket($h) => $h.$meth($($args)*),
403            AnyHandle::EventPair($h) => $h.$meth($($args)*),
404            AnyHandle::Event($h) => $h.$meth($($args)*),
405            AnyHandle::Channel($h) => $h.$meth($($args)*),
406            AnyHandle::Unknown($h) => $h.$meth($($args)*),
407        }
408    };
409}
410
411impl HandleType for AnyHandle {
412    fn object_type(&self) -> fidl::ObjectType {
413        impl_method!(self => h.object_type())
414    }
415
416    fn socket_disposition(
417        &self,
418        disposition: proto::SocketDisposition,
419        disposition_peer: proto::SocketDisposition,
420    ) -> Result<(), proto::Error> {
421        impl_method!(self => h.socket_disposition(disposition, disposition_peer))
422    }
423
424    fn read_socket(&self, max_bytes: u64) -> Result<Option<Vec<u8>>, proto::Error> {
425        impl_method!(self => h.read_socket(max_bytes))
426    }
427
428    fn read_channel(&self) -> Result<Option<fidl::MessageBufEtc>, proto::Error> {
429        impl_method!(self => h.read_channel())
430    }
431
432    fn write_socket(&self, data: &[u8]) -> Result<usize, proto::Error> {
433        impl_method!(self => h.write_socket(data))
434    }
435
436    fn write_channel(
437        &self,
438        data: &[u8],
439        handles: &mut Vec<fidl::HandleDisposition<'static>>,
440    ) -> Option<Result<(), proto::WriteChannelError>> {
441        impl_method!(self => h.write_channel(data, handles))
442    }
443}
444
445impl fidl::AsHandleRef for AnyHandle {
446    fn as_handle_ref(&self) -> fidl::HandleRef<'_> {
447        impl_method!(self => h.as_handle_ref())
448    }
449}
450
451impl Into<fidl::NullableHandle> for AnyHandle {
452    fn into(self) -> fidl::NullableHandle {
453        impl_method!(self => h.into())
454    }
455}