33use crate :: cmp;
44use crate :: io:: { self , Initializer , IoSlice , IoSliceMut , Read } ;
55use crate :: mem;
6- use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
76use crate :: sys:: {
87 cvt,
9- net:: netc:: { self , c_int, c_void, ssize_t } ,
8+ net:: netc:: { self , c_int, c_void} ,
109} ;
1110use crate :: sys_common:: AsInner ;
1211
1312#[ 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 ) ]
1418pub struct NetFileDesc {
1519 fd : c_int ,
1620}
1721
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.
3265}
3366
3467impl NetFileDesc {
3568 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 } }
3772 }
3873
3974 pub fn raw ( & self ) -> c_int {
@@ -49,7 +84,7 @@ impl NetFileDesc {
4984
5085 pub fn read ( & self , buf : & mut [ u8 ] ) -> io:: Result < usize > {
5186 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 ) )
5388 } ) ?;
5489 Ok ( ret as usize )
5590 }
@@ -59,7 +94,7 @@ impl NetFileDesc {
5994 netc:: readv (
6095 self . fd ,
6196 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 ,
6398 )
6499 } ) ?;
65100 Ok ( ret as usize )
@@ -75,9 +110,38 @@ impl NetFileDesc {
75110 ( & mut me) . read_to_end ( buf)
76111 }
77112
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+
78142 pub fn write ( & self , buf : & [ u8 ] ) -> io:: Result < usize > {
79143 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 ) )
81145 } ) ?;
82146 Ok ( ret as usize )
83147 }
@@ -87,7 +151,7 @@ impl NetFileDesc {
87151 netc:: writev (
88152 self . fd ,
89153 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 ,
91155 )
92156 } ) ?;
93157 Ok ( ret as usize )
@@ -98,10 +162,45 @@ impl NetFileDesc {
98162 true
99163 }
100164
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+
101194 #[ cfg( target_os = "linux" ) ]
102195 pub fn get_cloexec ( & self ) -> io:: Result < bool > {
103196 unsafe { Ok ( ( cvt ( netc:: fcntl ( self . fd , netc:: F_GETFD ) ) ? & netc:: FD_CLOEXEC ) != 0 ) }
104197 }
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+ }
105204
106205 #[ cfg( not( any(
107206 target_env = "newlib" ,
@@ -112,27 +211,26 @@ impl NetFileDesc {
112211 target_os = "l4re" ,
113212 target_os = "linux" ,
114213 target_os = "haiku" ,
115- target_os = "redox"
214+ target_os = "redox" ,
215+ target_os = "vxworks"
116216 ) ) ) ]
117217 pub fn set_cloexec ( & self ) -> io:: Result < ( ) > {
118218 unsafe {
119219 cvt ( netc:: ioctl ( self . fd , netc:: FIOCLEX ) ) ?;
120220 Ok ( ( ) )
121221 }
122222 }
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"
136234 ) ) ]
137235 pub fn set_cloexec ( & self ) -> io:: Result < ( ) > {
138236 unsafe {
@@ -145,60 +243,37 @@ impl NetFileDesc {
145243 }
146244 }
147245
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+ }
153269 }
154270
155271 pub fn duplicate ( & self ) -> io:: Result < NetFileDesc > {
156272 // We want to atomically duplicate this file descriptor and set the
157273 // 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) )
202277 }
203278}
204279
0 commit comments