11//! SOLID-specific extensions to general I/O primitives
2+ //!
3+ //! Just like raw pointers, raw SOLID Sockets file descriptors point to
4+ //! resources with dynamic lifetimes, and they can dangle if they outlive their
5+ //! resources or be forged if they're created from invalid values.
6+ //!
7+ //! This module provides three types for representing raw file descriptors
8+ //! with different ownership properties: raw, borrowed, and owned, which are
9+ //! analogous to types used for representing pointers:
10+ //!
11+ //! | Type | Analogous to |
12+ //! | ------------------ | ------------ |
13+ //! | [`RawFd`] | `*const _` |
14+ //! | [`BorrowedFd<'a>`] | `&'a _` |
15+ //! | [`OwnedFd`] | `Box<_>` |
16+ //!
17+ //! Like raw pointers, `RawFd` values are primitive values. And in new code,
18+ //! they should be considered unsafe to do I/O on (analogous to dereferencing
19+ //! them). Rust did not always provide this guidance, so existing code in the
20+ //! Rust ecosystem often doesn't mark `RawFd` usage as unsafe. Once the
21+ //! `io_safety` feature is stable, libraries will be encouraged to migrate,
22+ //! either by adding `unsafe` to APIs that dereference `RawFd` values, or by
23+ //! using to `BorrowedFd` or `OwnedFd` instead.
24+ //!
25+ //! Like references, `BorrowedFd` values are tied to a lifetime, to ensure
26+ //! that they don't outlive the resource they point to. These are safe to
27+ //! use. `BorrowedFd` values may be used in APIs which provide safe access to
28+ //! any system call except for:
29+ //!
30+ //! - `close`, because that would end the dynamic lifetime of the resource
31+ //! without ending the lifetime of the file descriptor.
32+ //!
33+ //! - `dup2`/`dup3`, in the second argument, because this argument is
34+ //! closed and assigned a new resource, which may break the assumptions
35+ //! other code using that file descriptor.
36+ //!
37+ //! `BorrowedFd` values may be used in APIs which provide safe access to `dup`
38+ //! system calls, so types implementing `AsFd` or `From<OwnedFd>` should not
39+ //! assume they always have exclusive access to the underlying file
40+ //! description.
41+ //!
42+ //! Like boxes, `OwnedFd` values conceptually own the resource they point to,
43+ //! and free (close) it when they are dropped.
44+ //!
45+ //! [`BorrowedFd<'a>`]: crate::os::solid::io::BorrowedFd
246
347#![ deny( unsafe_op_in_unsafe_fn) ]
448#![ unstable( feature = "solid_ext" , issue = "none" ) ]
549
50+ use crate :: fmt;
51+ use crate :: marker:: PhantomData ;
52+ use crate :: mem:: forget;
653use crate :: net;
754use crate :: sys;
855use crate :: sys_common:: { self , AsInner , FromInner , IntoInner } ;
956
1057/// Raw file descriptors.
1158pub type RawFd = i32 ;
1259
60+ /// A borrowed SOLID Sockets file descriptor.
61+ ///
62+ /// This has a lifetime parameter to tie it to the lifetime of something that
63+ /// owns the socket.
64+ ///
65+ /// This uses `repr(transparent)` and has the representation of a host file
66+ /// descriptor, so it can be used in FFI in places where a socket is passed as
67+ /// an argument, it is not captured or consumed, and it never has the value
68+ /// `SOLID_NET_INVALID_FD`.
69+ ///
70+ /// This type's `.to_owned()` implementation returns another `BorrowedFd`
71+ /// rather than an `OwnedFd`. It just makes a trivial copy of the raw
72+ /// socket, which is then borrowed under the same lifetime.
73+ #[ derive( Copy , Clone ) ]
74+ #[ repr( transparent) ]
75+ #[ rustc_layout_scalar_valid_range_start( 0 ) ]
76+ // This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`.
77+ #[ rustc_layout_scalar_valid_range_end( 0xFF_FF_FF_FE ) ]
78+ #[ rustc_nonnull_optimization_guaranteed]
79+ pub struct BorrowedFd < ' socket > {
80+ fd : RawFd ,
81+ _phantom : PhantomData < & ' socket OwnedFd > ,
82+ }
83+
84+ /// An owned SOLID Sockets file descriptor.
85+ ///
86+ /// This closes the file descriptor on drop.
87+ ///
88+ /// This uses `repr(transparent)` and has the representation of a host file
89+ /// descriptor, so it can be used in FFI in places where a socket is passed as
90+ /// an argument, it is not captured or consumed, and it never has the value
91+ /// `SOLID_NET_INVALID_FD`.
92+ #[ repr( transparent) ]
93+ #[ rustc_layout_scalar_valid_range_start( 0 ) ]
94+ // This is -2, in two's complement. -1 is `SOLID_NET_INVALID_FD`.
95+ #[ rustc_layout_scalar_valid_range_end( 0xFF_FF_FF_FE ) ]
96+ #[ rustc_nonnull_optimization_guaranteed]
97+ pub struct OwnedFd {
98+ fd : RawFd ,
99+ }
100+
101+ impl BorrowedFd < ' _ > {
102+ /// Return a `BorrowedFd` holding the given raw file descriptor.
103+ ///
104+ /// # Safety
105+ ///
106+ /// The resource pointed to by `fd` must remain open for the duration of
107+ /// the returned `BorrowedFd`, and it must not have the value
108+ /// `SOLID_NET_INVALID_FD`.
109+ #[ inline]
110+ pub const unsafe fn borrow_raw ( fd : RawFd ) -> Self {
111+ assert ! ( fd != -1 as RawFd ) ;
112+ // SAFETY: we just asserted that the value is in the valid range and
113+ // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
114+ unsafe { Self { fd, _phantom : PhantomData } }
115+ }
116+ }
117+
118+ impl OwnedFd {
119+ /// Creates a new `OwnedFd` instance that shares the same underlying file
120+ /// description as the existing `OwnedFd` instance.
121+ pub fn try_clone ( & self ) -> crate :: io:: Result < Self > {
122+ self . as_fd ( ) . try_clone_to_owned ( )
123+ }
124+ }
125+
126+ impl BorrowedFd < ' _ > {
127+ /// Creates a new `OwnedFd` instance that shares the same underlying file
128+ /// description as the existing `BorrowedFd` instance.
129+ pub fn try_clone_to_owned ( & self ) -> crate :: io:: Result < OwnedFd > {
130+ let fd = sys:: net:: cvt ( unsafe { sys:: net:: netc:: dup ( self . as_raw_fd ( ) ) } ) ?;
131+ Ok ( unsafe { OwnedFd :: from_raw_fd ( fd) } )
132+ }
133+ }
134+
135+ impl AsRawFd for BorrowedFd < ' _ > {
136+ #[ inline]
137+ fn as_raw_fd ( & self ) -> RawFd {
138+ self . fd
139+ }
140+ }
141+
142+ impl AsRawFd for OwnedFd {
143+ #[ inline]
144+ fn as_raw_fd ( & self ) -> RawFd {
145+ self . fd
146+ }
147+ }
148+
149+ impl IntoRawFd for OwnedFd {
150+ #[ inline]
151+ fn into_raw_fd ( self ) -> RawFd {
152+ let fd = self . fd ;
153+ forget ( self ) ;
154+ fd
155+ }
156+ }
157+
158+ impl FromRawFd for OwnedFd {
159+ /// Constructs a new instance of `Self` from the given raw file descriptor.
160+ ///
161+ /// # Safety
162+ ///
163+ /// The resource pointed to by `fd` must be open and suitable for assuming
164+ /// ownership. The resource must not require any cleanup other than `close`.
165+ #[ inline]
166+ unsafe fn from_raw_fd ( fd : RawFd ) -> Self {
167+ assert_ne ! ( fd, -1 as RawFd ) ;
168+ // SAFETY: we just asserted that the value is in the valid range and
169+ // isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
170+ unsafe { Self { fd } }
171+ }
172+ }
173+
174+ impl Drop for OwnedFd {
175+ #[ inline]
176+ fn drop ( & mut self ) {
177+ unsafe { sys:: net:: netc:: close ( self . fd ) } ;
178+ }
179+ }
180+
181+ impl fmt:: Debug for BorrowedFd < ' _ > {
182+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
183+ f. debug_struct ( "BorrowedFd" ) . field ( "fd" , & self . fd ) . finish ( )
184+ }
185+ }
186+
187+ impl fmt:: Debug for OwnedFd {
188+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
189+ f. debug_struct ( "OwnedFd" ) . field ( "fd" , & self . fd ) . finish ( )
190+ }
191+ }
192+
193+ macro_rules! impl_is_terminal {
194+ ( $( $t: ty) ,* $( , ) ?) => { $(
195+ #[ unstable( feature = "sealed" , issue = "none" ) ]
196+ impl crate :: sealed:: Sealed for $t { }
197+
198+ #[ stable( feature = "is_terminal" , since = "1.70.0" ) ]
199+ impl crate :: io:: IsTerminal for $t {
200+ #[ inline]
201+ fn is_terminal( & self ) -> bool {
202+ crate :: sys:: io:: is_terminal( self )
203+ }
204+ }
205+ ) * }
206+ }
207+
208+ impl_is_terminal ! ( BorrowedFd <' _>, OwnedFd ) ;
209+
210+ /// A trait to borrow the SOLID Sockets file descriptor from an underlying
211+ /// object.
212+ pub trait AsFd {
213+ /// Borrows the file descriptor.
214+ fn as_fd ( & self ) -> BorrowedFd < ' _ > ;
215+ }
216+
217+ impl < T : AsFd > AsFd for & T {
218+ #[ inline]
219+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
220+ T :: as_fd ( self )
221+ }
222+ }
223+
224+ impl < T : AsFd > AsFd for & mut T {
225+ #[ inline]
226+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
227+ T :: as_fd ( self )
228+ }
229+ }
230+
231+ impl AsFd for BorrowedFd < ' _ > {
232+ #[ inline]
233+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
234+ * self
235+ }
236+ }
237+
238+ impl AsFd for OwnedFd {
239+ #[ inline]
240+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
241+ // Safety: `OwnedFd` and `BorrowedFd` have the same validity
242+ // invariants, and the `BorrowedFd` is bounded by the lifetime
243+ // of `&self`.
244+ unsafe { BorrowedFd :: borrow_raw ( self . as_raw_fd ( ) ) }
245+ }
246+ }
247+
248+ macro_rules! impl_owned_fd_traits {
249+ ( $( $t: ident) * ) => { $(
250+ impl AsFd for net:: $t {
251+ #[ inline]
252+ fn as_fd( & self ) -> BorrowedFd <' _> {
253+ self . as_inner( ) . socket( ) . as_fd( )
254+ }
255+ }
256+
257+ impl From <net:: $t> for OwnedFd {
258+ #[ inline]
259+ fn from( socket: net:: $t) -> OwnedFd {
260+ socket. into_inner( ) . into_socket( ) . into_inner( )
261+ }
262+ }
263+
264+ impl From <OwnedFd > for net:: $t {
265+ #[ inline]
266+ fn from( owned_fd: OwnedFd ) -> Self {
267+ Self :: from_inner( FromInner :: from_inner( FromInner :: from_inner( owned_fd) ) )
268+ }
269+ }
270+ ) * } ;
271+ }
272+ impl_owned_fd_traits ! { TcpStream TcpListener UdpSocket }
273+
274+ /// This impl allows implementing traits that require `AsFd` on Arc.
275+ /// ```
276+ /// # #[cfg(target_os = "solid_asp3")] mod group_cfg {
277+ /// # use std::os::solid::io::AsFd;
278+ /// use std::net::UdpSocket;
279+ /// use std::sync::Arc;
280+ ///
281+ /// trait MyTrait: AsFd {}
282+ /// impl MyTrait for Arc<UdpSocket> {}
283+ /// impl MyTrait for Box<UdpSocket> {}
284+ /// # }
285+ /// ```
286+ impl < T : AsFd > AsFd for crate :: sync:: Arc < T > {
287+ #[ inline]
288+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
289+ ( * * self ) . as_fd ( )
290+ }
291+ }
292+
293+ impl < T : AsFd > AsFd for crate :: rc:: Rc < T > {
294+ #[ inline]
295+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
296+ ( * * self ) . as_fd ( )
297+ }
298+ }
299+
300+ impl < T : AsFd > AsFd for Box < T > {
301+ #[ inline]
302+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
303+ ( * * self ) . as_fd ( )
304+ }
305+ }
306+
13307/// A trait to extract the raw SOLID Sockets file descriptor from an underlying
14308/// object.
15309pub trait AsRawFd {
@@ -84,7 +378,7 @@ macro_rules! impl_as_raw_fd {
84378 impl AsRawFd for net:: $t {
85379 #[ inline]
86380 fn as_raw_fd( & self ) -> RawFd {
87- * self . as_inner( ) . socket( ) . as_inner ( )
381+ self . as_inner( ) . socket( ) . as_raw_fd ( )
88382 }
89383 }
90384 ) * } ;
@@ -97,7 +391,7 @@ macro_rules! impl_from_raw_fd {
97391 impl FromRawFd for net:: $t {
98392 #[ inline]
99393 unsafe fn from_raw_fd( fd: RawFd ) -> net:: $t {
100- let socket = sys:: net:: Socket :: from_inner ( fd) ;
394+ let socket = unsafe { sys:: net:: Socket :: from_raw_fd ( fd) } ;
101395 net:: $t:: from_inner( sys_common:: net:: $t:: from_inner( socket) )
102396 }
103397 }
@@ -111,7 +405,7 @@ macro_rules! impl_into_raw_fd {
111405 impl IntoRawFd for net:: $t {
112406 #[ inline]
113407 fn into_raw_fd( self ) -> RawFd {
114- self . into_inner( ) . into_socket( ) . into_inner ( )
408+ self . into_inner( ) . into_socket( ) . into_raw_fd ( )
115409 }
116410 }
117411 ) * } ;
0 commit comments