1 #![unstable(reason = "not public", issue = "none", feature = "fd")]
7 use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
8 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
10 use crate::sys_common::{AsInner, FromInner, IntoInner};
12 use libc::{c_int, c_void};
15 pub struct FileDesc(OwnedFd);
17 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
18 // with the man page quoting that if the count of bytes to read is
19 // greater than `SSIZE_MAX` the result is "unspecified".
21 // On macOS, however, apparently the 64-bit libc is either buggy or
22 // intentionally showing odd behavior by rejecting any read with a size
23 // larger than or equal to INT_MAX. To handle both of these the read
24 // size is capped on both platforms.
25 #[cfg(target_os = "macos")]
26 const READ_LIMIT: usize = c_int::MAX as usize - 1;
27 #[cfg(not(target_os = "macos"))]
28 const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
31 target_os = "dragonfly",
32 target_os = "freebsd",
36 target_os = "openbsd",
38 const fn max_iov() -> usize {
39 libc::IOV_MAX as usize
42 #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
43 const fn max_iov() -> usize {
44 libc::UIO_MAXIOV as usize
48 target_os = "android",
49 target_os = "dragonfly",
50 target_os = "emscripten",
51 target_os = "freebsd",
56 target_os = "openbsd",
58 const fn max_iov() -> usize {
59 16 // The minimum value required by POSIX.
63 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
64 let ret = cvt(unsafe {
67 buf.as_mut_ptr() as *mut c_void,
68 cmp::min(buf.len(), READ_LIMIT),
74 #[cfg(not(target_os = "espidf"))]
75 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
76 let ret = cvt(unsafe {
79 bufs.as_ptr() as *const libc::iovec,
80 cmp::min(bufs.len(), max_iov()) as c_int,
86 #[cfg(target_os = "espidf")]
87 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
88 return crate::io::default_read_vectored(|b| self.read(b), bufs);
92 pub fn is_read_vectored(&self) -> bool {
93 cfg!(not(target_os = "espidf"))
96 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
98 (&mut me).read_to_end(buf)
101 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
102 #[cfg(target_os = "android")]
103 use super::android::cvt_pread64;
105 #[cfg(not(target_os = "android"))]
106 unsafe fn cvt_pread64(
111 ) -> io::Result<isize> {
112 #[cfg(not(target_os = "linux"))]
113 use libc::pread as pread64;
114 #[cfg(target_os = "linux")]
116 cvt(pread64(fd, buf, count, offset))
122 buf.as_mut_ptr() as *mut c_void,
123 cmp::min(buf.len(), READ_LIMIT),
130 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
131 let ret = cvt(unsafe {
134 buf.as_ptr() as *const c_void,
135 cmp::min(buf.len(), READ_LIMIT),
141 #[cfg(not(target_os = "espidf"))]
142 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
143 let ret = cvt(unsafe {
146 bufs.as_ptr() as *const libc::iovec,
147 cmp::min(bufs.len(), max_iov()) as c_int,
153 #[cfg(target_os = "espidf")]
154 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
155 return crate::io::default_write_vectored(|b| self.write(b), bufs);
159 pub fn is_write_vectored(&self) -> bool {
160 cfg!(not(target_os = "espidf"))
163 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
164 #[cfg(target_os = "android")]
165 use super::android::cvt_pwrite64;
167 #[cfg(not(target_os = "android"))]
168 unsafe fn cvt_pwrite64(
173 ) -> io::Result<isize> {
174 #[cfg(not(target_os = "linux"))]
175 use libc::pwrite as pwrite64;
176 #[cfg(target_os = "linux")]
178 cvt(pwrite64(fd, buf, count, offset))
184 buf.as_ptr() as *const c_void,
185 cmp::min(buf.len(), READ_LIMIT),
192 #[cfg(target_os = "linux")]
193 pub fn get_cloexec(&self) -> io::Result<bool> {
194 unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
198 target_env = "newlib",
199 target_os = "solaris",
200 target_os = "illumos",
201 target_os = "emscripten",
202 target_os = "fuchsia",
207 target_os = "vxworks"
209 pub fn set_cloexec(&self) -> io::Result<()> {
211 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
216 all(target_env = "newlib", not(target_os = "espidf")),
217 target_os = "solaris",
218 target_os = "illumos",
219 target_os = "emscripten",
220 target_os = "fuchsia",
225 target_os = "vxworks"
227 pub fn set_cloexec(&self) -> io::Result<()> {
229 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
230 let new = previous | libc::FD_CLOEXEC;
232 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
237 #[cfg(target_os = "espidf")]
238 pub fn set_cloexec(&self) -> io::Result<()> {
239 // FD_CLOEXEC is not supported in ESP-IDF but there's no need to,
240 // because ESP-IDF does not support spawning processes either.
244 #[cfg(target_os = "linux")]
245 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
247 let v = nonblocking as c_int;
248 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
253 #[cfg(not(target_os = "linux"))]
254 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
256 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
257 let new = if nonblocking {
258 previous | libc::O_NONBLOCK
260 previous & !libc::O_NONBLOCK
263 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
269 pub fn duplicate(&self) -> io::Result<FileDesc> {
270 // We want to atomically duplicate this file descriptor and set the
271 // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
272 // is a POSIX flag that was added to Linux in 2.6.24.
273 #[cfg(not(target_os = "espidf"))]
274 let cmd = libc::F_DUPFD_CLOEXEC;
276 // For ESP-IDF, F_DUPFD is used instead, because the CLOEXEC semantics
277 // will never be supported, as this is a bare metal framework with
278 // no capabilities for multi-process execution. While F_DUPFD is also
279 // not supported yet, it might be (currently it returns ENOSYS).
280 #[cfg(target_os = "espidf")]
281 let cmd = libc::F_DUPFD;
283 let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
284 Ok(unsafe { FileDesc::from_raw_fd(fd) })
288 impl<'a> Read for &'a FileDesc {
289 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
294 unsafe fn initializer(&self) -> Initializer {
299 impl AsInner<OwnedFd> for FileDesc {
300 fn as_inner(&self) -> &OwnedFd {
305 impl IntoInner<OwnedFd> for FileDesc {
306 fn into_inner(self) -> OwnedFd {
311 impl FromInner<OwnedFd> for FileDesc {
312 fn from_inner(owned_fd: OwnedFd) -> Self {
317 impl AsFd for FileDesc {
318 fn as_fd(&self) -> BorrowedFd<'_> {
323 impl AsRawFd for FileDesc {
324 fn as_raw_fd(&self) -> RawFd {
329 impl IntoRawFd for FileDesc {
330 fn into_raw_fd(self) -> RawFd {
335 impl FromRawFd for FileDesc {
336 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
337 Self(FromRawFd::from_raw_fd(raw_fd))