1
1
//! 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
2
46
3
47
#![ deny( unsafe_op_in_unsafe_fn) ]
4
48
#![ unstable( feature = "solid_ext" , issue = "none" ) ]
5
49
50
+ use crate :: fmt;
51
+ use crate :: marker:: PhantomData ;
52
+ use crate :: mem:: forget;
6
53
use crate :: net;
7
54
use crate :: sys;
8
55
use crate :: sys_common:: { self , AsInner , FromInner , IntoInner } ;
9
56
10
57
/// Raw file descriptors.
11
58
pub type RawFd = i32 ;
12
59
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
+
13
307
/// A trait to extract the raw SOLID Sockets file descriptor from an underlying
14
308
/// object.
15
309
pub trait AsRawFd {
@@ -84,7 +378,7 @@ macro_rules! impl_as_raw_fd {
84
378
impl AsRawFd for net:: $t {
85
379
#[ inline]
86
380
fn as_raw_fd( & self ) -> RawFd {
87
- * self . as_inner( ) . socket( ) . as_inner ( )
381
+ self . as_inner( ) . socket( ) . as_raw_fd ( )
88
382
}
89
383
}
90
384
) * } ;
@@ -97,7 +391,7 @@ macro_rules! impl_from_raw_fd {
97
391
impl FromRawFd for net:: $t {
98
392
#[ inline]
99
393
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) } ;
101
395
net:: $t:: from_inner( sys_common:: net:: $t:: from_inner( socket) )
102
396
}
103
397
}
@@ -111,7 +405,7 @@ macro_rules! impl_into_raw_fd {
111
405
impl IntoRawFd for net:: $t {
112
406
#[ inline]
113
407
fn into_raw_fd( self ) -> RawFd {
114
- self . into_inner( ) . into_socket( ) . into_inner ( )
408
+ self . into_inner( ) . into_socket( ) . into_raw_fd ( )
115
409
}
116
410
}
117
411
) * } ;
0 commit comments