1#![unstable(reason = "not public", issue = "none", feature = "fd")]
2
3#[cfg(test)]
4mod tests;
5
6use crate::cmp;
7use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
8use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
9use crate::sys::cvt;
10use crate::sys_common::{AsInner, FromInner, IntoInner};
11
12#[cfg(any(
13 target_os = "android",
14 target_os = "linux",
15 target_os = "emscripten",
16 target_os = "l4re",
17 target_os = "hurd",
18))]
19use libc::off64_t;
20#[cfg(not(any(
21 target_os = "linux",
22 target_os = "emscripten",
23 target_os = "l4re",
24 target_os = "android",
25 target_os = "hurd",
26)))]
27use libc::off_t as off64_t;
28
29#[derive(Debug)]
30pub struct FileDesc(OwnedFd);
31
32// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
33// with the man page quoting that if the count of bytes to read is
34// greater than `SSIZE_MAX` the result is "unspecified".
35//
36// On macOS, however, apparently the 64-bit libc is either buggy or
37// intentionally showing odd behavior by rejecting any read with a size
38// larger than or equal to INT_MAX. To handle both of these the read
39// size is capped on both platforms.
40#[cfg(target_os = "macos")]
41const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
42#[cfg(not(target_os = "macos"))]
43const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
44
45#[cfg(any(
46 target_os = "dragonfly",
47 target_os = "freebsd",
48 target_os = "ios",
49 target_os = "tvos",
50 target_os = "macos",
51 target_os = "netbsd",
52 target_os = "openbsd",
53 target_os = "watchos",
54))]
55const fn max_iov() -> usize {
56 libc::IOV_MAX as usize
57}
58
59#[cfg(any(
60 target_os = "android",
61 target_os = "emscripten",
62 target_os = "linux",
63 target_os = "nto",
64))]
65const fn max_iov() -> usize {
66 libc::UIO_MAXIOV as usize
67}
68
69#[cfg(not(any(
70 target_os = "android",
71 target_os = "dragonfly",
72 target_os = "emscripten",
73 target_os = "freebsd",
74 target_os = "ios",
75 target_os = "tvos",
76 target_os = "linux",
77 target_os = "macos",
78 target_os = "netbsd",
79 target_os = "nto",
80 target_os = "openbsd",
81 target_os = "horizon",
82 target_os = "vita",
83 target_os = "watchos",
84)))]
85const fn max_iov() -> usize {
86 16 // The minimum value required by POSIX.
87}
88
89impl FileDesc {
90 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
91 let ret = cvt(unsafe {
92 libc::read(
93 self.as_raw_fd(),
94 buf.as_mut_ptr() as *mut libc::c_void,
95 cmp::min(buf.len(), READ_LIMIT),
96 )
97 })?;
98 Ok(ret as usize)
99 }
100
101 #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))]
102 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
103 let ret = cvt(unsafe {
104 libc::readv(
105 self.as_raw_fd(),
106 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
107 cmp::min(bufs.len(), max_iov()) as libc::c_int,
108 )
109 })?;
110 Ok(ret as usize)
111 }
112
113 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
114 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
115 io::default_read_vectored(|b| self.read(b), bufs)
116 }
117
118 #[inline]
119 pub fn is_read_vectored(&self) -> bool {
120 cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))
121 }
122
123 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
124 let mut me = self;
125 (&mut me).read_to_end(buf)
126 }
127
128 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
129 #[cfg(not(any(
130 all(target_os = "linux", not(target_env = "musl")),
131 target_os = "android",
132 target_os = "hurd"
133 )))]
134 use libc::pread as pread64;
135 #[cfg(any(
136 all(target_os = "linux", not(target_env = "musl")),
137 target_os = "android",
138 target_os = "hurd"
139 ))]
140 use libc::pread64;
141
142 unsafe {
143 cvt(pread64(
144 self.as_raw_fd(),
145 buf.as_mut_ptr() as *mut libc::c_void,
146 cmp::min(buf.len(), READ_LIMIT),
147 offset as off64_t,
148 ))
149 .map(|n| n as usize)
150 }
151 }
152
153 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
154 let ret = cvt(unsafe {
155 libc::read(
156 self.as_raw_fd(),
157 cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
158 cmp::min(cursor.capacity(), READ_LIMIT),
159 )
160 })?;
161
162 // Safety: `ret` bytes were written to the initialized portion of the buffer
163 unsafe {
164 cursor.advance(ret as usize);
165 }
166 Ok(())
167 }
168
169 #[cfg(any(
170 target_os = "emscripten",
171 target_os = "freebsd",
172 target_os = "fuchsia",
173 target_os = "hurd",
174 target_os = "illumos",
175 target_os = "linux",
176 target_os = "netbsd",
177 ))]
178 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
179 let ret = cvt(unsafe {
180 libc::preadv(
181 self.as_raw_fd(),
182 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
183 cmp::min(bufs.len(), max_iov()) as libc::c_int,
184 offset as _,
185 )
186 })?;
187 Ok(ret as usize)
188 }
189
190 #[cfg(not(any(
191 target_os = "android",
192 target_os = "emscripten",
193 target_os = "freebsd",
194 target_os = "fuchsia",
195 target_os = "hurd",
196 target_os = "illumos",
197 target_os = "ios",
198 target_os = "tvos",
199 target_os = "linux",
200 target_os = "macos",
201 target_os = "netbsd",
202 )))]
203 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
204 io::default_read_vectored(|b| self.read_at(b, offset), bufs)
205 }
206
207 // We support some old Android versions that do not have `preadv` in libc,
208 // so we use weak linkage and fallback to a direct syscall if not available.
209 //
210 // On 32-bit targets, we don't want to deal with weird ABI issues around
211 // passing 64-bits parameters to syscalls, so we fallback to the default
212 // implementation if `preadv` is not available.
213 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
214 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
215 super::weak::syscall! {
216 fn preadv(
217 fd: libc::c_int,
218 iovec: *const libc::iovec,
219 n_iovec: libc::c_int,
220 offset: off64_t
221 ) -> isize
222 }
223
224 let ret = cvt(unsafe {
225 preadv(
226 self.as_raw_fd(),
227 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
228 cmp::min(bufs.len(), max_iov()) as libc::c_int,
229 offset as _,
230 )
231 })?;
232 Ok(ret as usize)
233 }
234
235 // We support old MacOS and iOS versions that do not have `preadv`. There is
236 // no `syscall` possible in these platform.
237 #[cfg(any(
238 all(target_os = "android", target_pointer_width = "32"),
239 target_os = "ios",
240 target_os = "tvos",
241 target_os = "macos",
242 ))]
243 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
244 super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
245
246 match preadv64.get() {
247 Some(preadv) => {
248 let ret = cvt(unsafe {
249 preadv(
250 self.as_raw_fd(),
251 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
252 cmp::min(bufs.len(), max_iov()) as libc::c_int,
253 offset as _,
254 )
255 })?;
256 Ok(ret as usize)
257 }
258 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
259 }
260 }
261
262 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
263 let ret = cvt(unsafe {
264 libc::write(
265 self.as_raw_fd(),
266 buf.as_ptr() as *const libc::c_void,
267 cmp::min(buf.len(), READ_LIMIT),
268 )
269 })?;
270 Ok(ret as usize)
271 }
272
273 #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))]
274 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
275 let ret = cvt(unsafe {
276 libc::writev(
277 self.as_raw_fd(),
278 bufs.as_ptr() as *const libc::iovec,
279 cmp::min(bufs.len(), max_iov()) as libc::c_int,
280 )
281 })?;
282 Ok(ret as usize)
283 }
284
285 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
286 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
287 io::default_write_vectored(|b| self.write(b), bufs)
288 }
289
290 #[inline]
291 pub fn is_write_vectored(&self) -> bool {
292 cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))
293 }
294
295 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
296 #[cfg(not(any(
297 all(target_os = "linux", not(target_env = "musl")),
298 target_os = "android",
299 target_os = "hurd"
300 )))]
301 use libc::pwrite as pwrite64;
302 #[cfg(any(
303 all(target_os = "linux", not(target_env = "musl")),
304 target_os = "android",
305 target_os = "hurd"
306 ))]
307 use libc::pwrite64;
308
309 unsafe {
310 cvt(pwrite64(
311 self.as_raw_fd(),
312 buf.as_ptr() as *const libc::c_void,
313 cmp::min(buf.len(), READ_LIMIT),
314 offset as off64_t,
315 ))
316 .map(|n| n as usize)
317 }
318 }
319
320 #[cfg(any(
321 target_os = "emscripten",
322 target_os = "freebsd",
323 target_os = "fuchsia",
324 target_os = "hurd",
325 target_os = "illumos",
326 target_os = "linux",
327 target_os = "netbsd",
328 ))]
329 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
330 let ret = cvt(unsafe {
331 libc::pwritev(
332 self.as_raw_fd(),
333 bufs.as_ptr() as *const libc::iovec,
334 cmp::min(bufs.len(), max_iov()) as libc::c_int,
335 offset as _,
336 )
337 })?;
338 Ok(ret as usize)
339 }
340
341 #[cfg(not(any(
342 target_os = "android",
343 target_os = "emscripten",
344 target_os = "freebsd",
345 target_os = "fuchsia",
346 target_os = "hurd",
347 target_os = "illumos",
348 target_os = "ios",
349 target_os = "tvos",
350 target_os = "linux",
351 target_os = "macos",
352 target_os = "netbsd",
353 )))]
354 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
355 io::default_write_vectored(|b| self.write_at(b, offset), bufs)
356 }
357
358 // We support some old Android versions that do not have `pwritev` in libc,
359 // so we use weak linkage and fallback to a direct syscall if not available.
360 //
361 // On 32-bit targets, we don't want to deal with weird ABI issues around
362 // passing 64-bits parameters to syscalls, so we fallback to the default
363 // implementation if `pwritev` is not available.
364 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
365 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
366 super::weak::syscall! {
367 fn pwritev(
368 fd: libc::c_int,
369 iovec: *const libc::iovec,
370 n_iovec: libc::c_int,
371 offset: off64_t
372 ) -> isize
373 }
374
375 let ret = cvt(unsafe {
376 pwritev(
377 self.as_raw_fd(),
378 bufs.as_ptr() as *const libc::iovec,
379 cmp::min(bufs.len(), max_iov()) as libc::c_int,
380 offset as _,
381 )
382 })?;
383 Ok(ret as usize)
384 }
385
386 // We support old MacOS and iOS versions that do not have `pwritev`. There is
387 // no `syscall` possible in these platform.
388 #[cfg(any(
389 all(target_os = "android", target_pointer_width = "32"),
390 target_os = "ios",
391 target_os = "tvos",
392 target_os = "macos",
393 ))]
394 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
395 super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
396
397 match pwritev64.get() {
398 Some(pwritev) => {
399 let ret = cvt(unsafe {
400 pwritev(
401 self.as_raw_fd(),
402 bufs.as_ptr() as *const libc::iovec,
403 cmp::min(bufs.len(), max_iov()) as libc::c_int,
404 offset as _,
405 )
406 })?;
407 Ok(ret as usize)
408 }
409 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
410 }
411 }
412
413 #[cfg(not(any(
414 target_env = "newlib",
415 target_os = "solaris",
416 target_os = "illumos",
417 target_os = "emscripten",
418 target_os = "fuchsia",
419 target_os = "l4re",
420 target_os = "linux",
421 target_os = "haiku",
422 target_os = "redox",
423 target_os = "vxworks",
424 target_os = "nto",
425 )))]
426 pub fn set_cloexec(&self) -> io::Result<()> {
427 unsafe {
428 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
429 Ok(())
430 }
431 }
432 #[cfg(any(
433 all(
434 target_env = "newlib",
435 not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))
436 ),
437 target_os = "solaris",
438 target_os = "illumos",
439 target_os = "emscripten",
440 target_os = "fuchsia",
441 target_os = "l4re",
442 target_os = "linux",
443 target_os = "haiku",
444 target_os = "redox",
445 target_os = "vxworks",
446 target_os = "nto",
447 ))]
448 pub fn set_cloexec(&self) -> io::Result<()> {
449 unsafe {
450 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
451 let new = previous | libc::FD_CLOEXEC;
452 if new != previous {
453 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
454 }
455 Ok(())
456 }
457 }
458 #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
459 pub fn set_cloexec(&self) -> io::Result<()> {
460 // FD_CLOEXEC is not supported in ESP-IDF, Horizon OS and Vita but there's no need to,
461 // because none of them supports spawning processes.
462 Ok(())
463 }
464
465 #[cfg(target_os = "linux")]
466 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
467 unsafe {
468 let v = nonblocking as libc::c_int;
469 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
470 Ok(())
471 }
472 }
473
474 #[cfg(not(target_os = "linux"))]
475 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
476 unsafe {
477 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
478 let new = if nonblocking {
479 previous | libc::O_NONBLOCK
480 } else {
481 previous & !libc::O_NONBLOCK
482 };
483 if new != previous {
484 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
485 }
486 Ok(())
487 }
488 }
489
490 #[inline]
491 pub fn duplicate(&self) -> io::Result<FileDesc> {
492 Ok(Self(self.0.try_clone()?))
493 }
494}
495
496impl<'a> Read for &'a FileDesc {
497 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
498 (**self).read(buf)
499 }
500
501 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
502 (**self).read_buf(cursor)
503 }
504
505 fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
506 (**self).read_vectored(bufs)
507 }
508
509 #[inline]
510 fn is_read_vectored(&self) -> bool {
511 (**self).is_read_vectored()
512 }
513}
514
515impl AsInner<OwnedFd> for FileDesc {
516 #[inline]
517 fn as_inner(&self) -> &OwnedFd {
518 &self.0
519 }
520}
521
522impl IntoInner<OwnedFd> for FileDesc {
523 fn into_inner(self) -> OwnedFd {
524 self.0
525 }
526}
527
528impl FromInner<OwnedFd> for FileDesc {
529 fn from_inner(owned_fd: OwnedFd) -> Self {
530 Self(owned_fd)
531 }
532}
533
534impl AsFd for FileDesc {
535 fn as_fd(&self) -> BorrowedFd<'_> {
536 self.0.as_fd()
537 }
538}
539
540impl AsRawFd for FileDesc {
541 #[inline]
542 fn as_raw_fd(&self) -> RawFd {
543 self.0.as_raw_fd()
544 }
545}
546
547impl IntoRawFd for FileDesc {
548 fn into_raw_fd(self) -> RawFd {
549 self.0.into_raw_fd()
550 }
551}
552
553impl FromRawFd for FileDesc {
554 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
555 Self(FromRawFd::from_raw_fd(raw_fd))
556 }
557}
558