3
3
use crate :: cmp;
4
4
use crate :: io:: { self , Initializer , IoSlice , IoSliceMut , Read } ;
5
5
use crate :: mem;
6
- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
7
6
use crate :: sys:: {
8
7
cvt,
9
- net:: netc:: { self , c_int, c_void, ssize_t } ,
8
+ net:: netc:: { self , c_int, c_void} ,
10
9
} ;
11
10
use crate :: sys_common:: AsInner ;
12
11
13
12
#[ derive( Debug ) ]
13
+ #[ rustc_layout_scalar_valid_range_start( 0 ) ]
14
+ // libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
15
+ // 32-bit c_int. Below is -2, in two's complement, but that only works out
16
+ // because c_int is 32 bits.
17
+ #[ rustc_layout_scalar_valid_range_end( 0xFF_FF_FF_FE ) ]
14
18
pub struct NetFileDesc {
15
19
fd : c_int ,
16
20
}
17
21
18
- fn max_len ( ) -> usize {
19
- // The maximum read limit on most posix-like systems is `SSIZE_MAX`,
20
- // with the man page quoting that if the count of bytes to read is
21
- // greater than `SSIZE_MAX` the result is "unspecified".
22
- //
23
- // On macOS, however, apparently the 64-bit libc is either buggy or
24
- // intentionally showing odd behavior by rejecting any read with a size
25
- // larger than or equal to INT_MAX. To handle both of these the read
26
- // size is capped on both platforms.
27
- if cfg ! ( target_os = "macos" ) {
28
- <c_int >:: max_value ( ) as usize - 1
29
- } else {
30
- <ssize_t >:: max_value ( ) as usize
31
- }
22
+ // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
23
+ // with the man page quoting that if the count of bytes to read is
24
+ // greater than `SSIZE_MAX` the result is "unspecified".
25
+ //
26
+ // On macOS, however, apparently the 64-bit libc is either buggy or
27
+ // intentionally showing odd behavior by rejecting any read with a size
28
+ // larger than or equal to INT_MAX. To handle both of these the read
29
+ // size is capped on both platforms.
30
+ #[ cfg( target_os = "macos" ) ]
31
+ const READ_LIMIT : usize = c_int:: MAX as usize - 1 ;
32
+ #[ cfg( not( target_os = "macos" ) ) ]
33
+ const READ_LIMIT : usize = netc:: ssize_t:: MAX as usize ;
34
+
35
+ #[ cfg( any(
36
+ target_os = "dragonfly" ,
37
+ target_os = "freebsd" ,
38
+ target_os = "ios" ,
39
+ target_os = "macos" ,
40
+ target_os = "netbsd" ,
41
+ target_os = "openbsd" ,
42
+ ) ) ]
43
+ const fn max_iov ( ) -> usize {
44
+ netc:: IOV_MAX as usize
45
+ }
46
+
47
+ #[ cfg( any( target_os = "android" , target_os = "emscripten" , target_os = "linux" ) ) ]
48
+ const fn max_iov ( ) -> usize {
49
+ netc:: UIO_MAXIOV as usize
50
+ }
51
+
52
+ #[ cfg( not( any(
53
+ target_os = "android" ,
54
+ target_os = "dragonfly" ,
55
+ target_os = "emscripten" ,
56
+ target_os = "freebsd" ,
57
+ target_os = "ios" ,
58
+ target_os = "linux" ,
59
+ target_os = "macos" ,
60
+ target_os = "netbsd" ,
61
+ target_os = "openbsd" ,
62
+ ) ) ) ]
63
+ const fn max_iov ( ) -> usize {
64
+ 16 // The minimum value required by POSIX.
32
65
}
33
66
34
67
impl NetFileDesc {
35
68
pub fn new ( fd : c_int ) -> NetFileDesc {
36
- NetFileDesc { fd }
69
+ assert_ne ! ( fd, -1i32 ) ;
70
+ // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
71
+ unsafe { NetFileDesc { fd } }
37
72
}
38
73
39
74
pub fn raw ( & self ) -> c_int {
@@ -49,7 +84,7 @@ impl NetFileDesc {
49
84
50
85
pub fn read ( & self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
51
86
let ret = cvt ( unsafe {
52
- netc:: read ( self . fd , buf. as_mut_ptr ( ) as * mut c_void , cmp:: min ( buf. len ( ) , max_len ( ) ) )
87
+ netc:: read ( self . fd , buf. as_mut_ptr ( ) as * mut c_void , cmp:: min ( buf. len ( ) , READ_LIMIT ) )
53
88
} ) ?;
54
89
Ok ( ret as usize )
55
90
}
@@ -59,7 +94,7 @@ impl NetFileDesc {
59
94
netc:: readv (
60
95
self . fd ,
61
96
bufs. as_ptr ( ) as * const netc:: iovec ,
62
- cmp:: min ( bufs. len ( ) , c_int :: max_value ( ) as usize ) as c_int ,
97
+ cmp:: min ( bufs. len ( ) , max_iov ( ) ) as c_int ,
63
98
)
64
99
} ) ?;
65
100
Ok ( ret as usize )
@@ -75,9 +110,38 @@ impl NetFileDesc {
75
110
( & mut me) . read_to_end ( buf)
76
111
}
77
112
113
+ pub fn read_at ( & self , buf : & mut [ u8 ] , offset : u64 ) -> io:: Result < usize > {
114
+ #[ cfg( target_os = "android" ) ]
115
+ use super :: android:: cvt_pread64;
116
+
117
+ #[ cfg( not( target_os = "android" ) ) ]
118
+ unsafe fn cvt_pread64 (
119
+ fd : c_int ,
120
+ buf : * mut c_void ,
121
+ count : usize ,
122
+ offset : i64 ,
123
+ ) -> io:: Result < isize > {
124
+ #[ cfg( not( target_os = "linux" ) ) ]
125
+ use netc:: pread as pread64;
126
+ #[ cfg( target_os = "linux" ) ]
127
+ use netc:: pread64;
128
+ cvt ( pread64 ( fd, buf, count, offset) )
129
+ }
130
+
131
+ unsafe {
132
+ cvt_pread64 (
133
+ self . fd ,
134
+ buf. as_mut_ptr ( ) as * mut c_void ,
135
+ cmp:: min ( buf. len ( ) , READ_LIMIT ) ,
136
+ offset as i64 ,
137
+ )
138
+ . map ( |n| n as usize )
139
+ }
140
+ }
141
+
78
142
pub fn write ( & self , buf : & [ u8 ] ) -> io:: Result < usize > {
79
143
let ret = cvt ( unsafe {
80
- netc:: write ( self . fd , buf. as_ptr ( ) as * const c_void , cmp:: min ( buf. len ( ) , max_len ( ) ) )
144
+ netc:: write ( self . fd , buf. as_ptr ( ) as * const c_void , cmp:: min ( buf. len ( ) , READ_LIMIT ) )
81
145
} ) ?;
82
146
Ok ( ret as usize )
83
147
}
@@ -87,7 +151,7 @@ impl NetFileDesc {
87
151
netc:: writev (
88
152
self . fd ,
89
153
bufs. as_ptr ( ) as * const netc:: iovec ,
90
- cmp:: min ( bufs. len ( ) , c_int :: max_value ( ) as usize ) as c_int ,
154
+ cmp:: min ( bufs. len ( ) , max_iov ( ) ) as c_int ,
91
155
)
92
156
} ) ?;
93
157
Ok ( ret as usize )
@@ -98,10 +162,45 @@ impl NetFileDesc {
98
162
true
99
163
}
100
164
165
+ pub fn write_at ( & self , buf : & [ u8 ] , offset : u64 ) -> io:: Result < usize > {
166
+ #[ cfg( target_os = "android" ) ]
167
+ use super :: android:: cvt_pwrite64;
168
+
169
+ #[ cfg( not( target_os = "android" ) ) ]
170
+ unsafe fn cvt_pwrite64 (
171
+ fd : c_int ,
172
+ buf : * const c_void ,
173
+ count : usize ,
174
+ offset : i64 ,
175
+ ) -> io:: Result < isize > {
176
+ #[ cfg( not( target_os = "linux" ) ) ]
177
+ use netc:: pwrite as pwrite64;
178
+ #[ cfg( target_os = "linux" ) ]
179
+ use netc:: pwrite64;
180
+ cvt ( pwrite64 ( fd, buf, count, offset) )
181
+ }
182
+
183
+ unsafe {
184
+ cvt_pwrite64 (
185
+ self . fd ,
186
+ buf. as_ptr ( ) as * const c_void ,
187
+ cmp:: min ( buf. len ( ) , READ_LIMIT ) ,
188
+ offset as i64 ,
189
+ )
190
+ . map ( |n| n as usize )
191
+ }
192
+ }
193
+
101
194
#[ cfg( target_os = "linux" ) ]
102
195
pub fn get_cloexec ( & self ) -> io:: Result < bool > {
103
196
unsafe { Ok ( ( cvt ( netc:: fcntl ( self . fd , netc:: F_GETFD ) ) ? & netc:: FD_CLOEXEC ) != 0 ) }
104
197
}
198
+ // Setting `FD_CLOEXEC` is not supported on FreeRTOS
199
+ // since there is no `exec` functionality.
200
+ #[ cfg( target_os = "freertos" ) ]
201
+ pub fn set_cloexec ( & self ) -> io:: Result < ( ) > {
202
+ Ok ( ( ) )
203
+ }
105
204
106
205
#[ cfg( not( any(
107
206
target_env = "newlib" ,
@@ -112,27 +211,26 @@ impl NetFileDesc {
112
211
target_os = "l4re" ,
113
212
target_os = "linux" ,
114
213
target_os = "haiku" ,
115
- target_os = "redox"
214
+ target_os = "redox" ,
215
+ target_os = "vxworks"
116
216
) ) ) ]
117
217
pub fn set_cloexec ( & self ) -> io:: Result < ( ) > {
118
218
unsafe {
119
219
cvt ( netc:: ioctl ( self . fd , netc:: FIOCLEX ) ) ?;
120
220
Ok ( ( ) )
121
221
}
122
222
}
123
- #[ cfg( all(
124
- any(
125
- target_env = "newlib" ,
126
- target_os = "solaris" ,
127
- target_os = "illumos" ,
128
- target_os = "emscripten" ,
129
- target_os = "fuchsia" ,
130
- target_os = "l4re" ,
131
- target_os = "linux" ,
132
- target_os = "haiku" ,
133
- target_os = "redox"
134
- ) ,
135
- not( target_os = "freertos" )
223
+ #[ cfg( any(
224
+ all( target_env = "newlib" , not( target_os = "freertos" ) ) ,
225
+ target_os = "solaris" ,
226
+ target_os = "illumos" ,
227
+ target_os = "emscripten" ,
228
+ target_os = "fuchsia" ,
229
+ target_os = "l4re" ,
230
+ target_os = "linux" ,
231
+ target_os = "haiku" ,
232
+ target_os = "redox" ,
233
+ target_os = "vxworks"
136
234
) ) ]
137
235
pub fn set_cloexec ( & self ) -> io:: Result < ( ) > {
138
236
unsafe {
@@ -145,60 +243,37 @@ impl NetFileDesc {
145
243
}
146
244
}
147
245
148
- // Setting `FD_CLOEXEC` is not supported on FreeRTOS
149
- // since there is no `exec` functionality.
150
- #[ cfg( target_os = "freertos" ) ]
151
- pub fn set_cloexec ( & self ) -> io:: Result < ( ) > {
152
- Ok ( ( ) )
246
+ #[ cfg( target_os = "linux" ) ]
247
+ pub fn set_nonblocking ( & self , nonblocking : bool ) -> io:: Result < ( ) > {
248
+ unsafe {
249
+ let v = nonblocking as c_int ;
250
+ cvt ( netc:: ioctl ( self . fd , netc:: FIONBIO , & v) ) ?;
251
+ Ok ( ( ) )
252
+ }
253
+ }
254
+
255
+ #[ cfg( not( target_os = "linux" ) ) ]
256
+ pub fn set_nonblocking ( & self , nonblocking : bool ) -> io:: Result < ( ) > {
257
+ unsafe {
258
+ let previous = cvt ( netc:: fcntl ( self . fd , netc:: F_GETFL ) ) ?;
259
+ let new = if nonblocking {
260
+ previous | netc:: O_NONBLOCK
261
+ } else {
262
+ previous & !netc:: O_NONBLOCK
263
+ } ;
264
+ if new != previous {
265
+ cvt ( netc:: fcntl ( self . fd , netc:: F_SETFL , new) ) ?;
266
+ }
267
+ Ok ( ( ) )
268
+ }
153
269
}
154
270
155
271
pub fn duplicate ( & self ) -> io:: Result < NetFileDesc > {
156
272
// We want to atomically duplicate this file descriptor and set the
157
273
// CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
158
- // flag, however, isn't supported on older Linux kernels (earlier than
159
- // 2.6.24).
160
- //
161
- // To detect this and ensure that CLOEXEC is still set, we
162
- // follow a strategy similar to musl [1] where if passing
163
- // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not
164
- // supported (the third parameter, 0, is always valid), so we stop
165
- // trying that.
166
- //
167
- // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to
168
- // resolve so we at least compile this.
169
- //
170
- // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963
171
- #[ cfg( any( target_os = "android" , target_os = "haiku" ) ) ]
172
- use netc:: F_DUPFD as F_DUPFD_CLOEXEC ;
173
- #[ cfg( not( any( target_os = "android" , target_os = "haiku" ) ) ) ]
174
- use netc:: F_DUPFD_CLOEXEC ;
175
-
176
- let make_filedesc = |fd| {
177
- let fd = NetFileDesc :: new ( fd) ;
178
- fd. set_cloexec ( ) ?;
179
- Ok ( fd)
180
- } ;
181
- static TRY_CLOEXEC : AtomicBool = AtomicBool :: new ( !cfg ! ( target_os = "android" ) ) ;
182
- let fd = self . raw ( ) ;
183
- if TRY_CLOEXEC . load ( Ordering :: Relaxed ) {
184
- match cvt ( unsafe { netc:: fcntl ( fd, F_DUPFD_CLOEXEC , 0 ) } ) {
185
- // We *still* call the `set_cloexec` method as apparently some
186
- // linux kernel at some point stopped setting CLOEXEC even
187
- // though it reported doing so on F_DUPFD_CLOEXEC.
188
- Ok ( fd) => {
189
- return Ok ( if cfg ! ( target_os = "linux" ) {
190
- make_filedesc ( fd) ?
191
- } else {
192
- NetFileDesc :: new ( fd)
193
- } ) ;
194
- }
195
- Err ( ref e) if e. raw_os_error ( ) == Some ( netc:: EINVAL ) => {
196
- TRY_CLOEXEC . store ( false , Ordering :: Relaxed ) ;
197
- }
198
- Err ( e) => return Err ( e) ,
199
- }
200
- }
201
- cvt ( unsafe { netc:: fcntl ( fd, netc:: F_DUPFD , 0 ) } ) . and_then ( make_filedesc)
274
+ // is a POSIX flag that was added to Linux in 2.6.24.
275
+ let fd = cvt ( unsafe { netc:: fcntl ( self . raw ( ) , netc:: F_DUPFD_CLOEXEC , 0 ) } ) ?;
276
+ Ok ( NetFileDesc :: new ( fd) )
202
277
}
203
278
}
204
279
0 commit comments