1 #![unstable(reason = "not public", issue = "none", feature = "fd")]
7 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
8 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
10 use crate::sys_common::{AsInner, FromInner, IntoInner};
13 target_os = "android",
15 target_os = "emscripten",
21 target_os = "emscripten",
25 use libc::off_t as off64_t;
28 pub struct FileDesc(OwnedFd);
30 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
31 // with the man page quoting that if the count of bytes to read is
32 // greater than `SSIZE_MAX` the result is "unspecified".
34 // On macOS, however, apparently the 64-bit libc is either buggy or
35 // intentionally showing odd behavior by rejecting any read with a size
36 // larger than or equal to INT_MAX. To handle both of these the read
37 // size is capped on both platforms.
38 #[cfg(target_os = "macos")]
39 const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
40 #[cfg(not(target_os = "macos"))]
41 const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
44 target_os = "dragonfly",
45 target_os = "freebsd",
49 target_os = "openbsd",
50 target_os = "watchos",
52 const fn max_iov() -> usize {
53 libc::IOV_MAX as usize
56 #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
57 const fn max_iov() -> usize {
58 libc::UIO_MAXIOV as usize
62 target_os = "android",
63 target_os = "dragonfly",
64 target_os = "emscripten",
65 target_os = "freebsd",
70 target_os = "openbsd",
71 target_os = "horizon",
72 target_os = "watchos",
74 const fn max_iov() -> usize {
75 16 // The minimum value required by POSIX.
79 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
80 let ret = cvt(unsafe {
83 buf.as_mut_ptr() as *mut libc::c_void,
84 cmp::min(buf.len(), READ_LIMIT),
90 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
91 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
92 let ret = cvt(unsafe {
95 bufs.as_ptr() as *const libc::iovec,
96 cmp::min(bufs.len(), max_iov()) as libc::c_int,
102 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
103 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
104 return crate::io::default_read_vectored(|b| self.read(b), bufs);
108 pub fn is_read_vectored(&self) -> bool {
109 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
112 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
114 (&mut me).read_to_end(buf)
117 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
118 #[cfg(not(any(target_os = "linux", target_os = "android")))]
119 use libc::pread as pread64;
120 #[cfg(any(target_os = "linux", target_os = "android"))]
126 buf.as_mut_ptr() as *mut libc::c_void,
127 cmp::min(buf.len(), READ_LIMIT),
134 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
135 let ret = cvt(unsafe {
138 cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
139 cmp::min(cursor.capacity(), READ_LIMIT),
143 // Safety: `ret` bytes were written to the initialized portion of the buffer
145 cursor.advance(ret as usize);
150 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
151 let ret = cvt(unsafe {
154 buf.as_ptr() as *const libc::c_void,
155 cmp::min(buf.len(), READ_LIMIT),
161 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
162 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
163 let ret = cvt(unsafe {
166 bufs.as_ptr() as *const libc::iovec,
167 cmp::min(bufs.len(), max_iov()) as libc::c_int,
173 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
174 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
175 return crate::io::default_write_vectored(|b| self.write(b), bufs);
179 pub fn is_write_vectored(&self) -> bool {
180 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
183 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
184 #[cfg(not(any(target_os = "linux", target_os = "android")))]
185 use libc::pwrite as pwrite64;
186 #[cfg(any(target_os = "linux", target_os = "android"))]
192 buf.as_ptr() as *const libc::c_void,
193 cmp::min(buf.len(), READ_LIMIT),
200 #[cfg(target_os = "linux")]
201 pub fn get_cloexec(&self) -> io::Result<bool> {
202 unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
206 target_env = "newlib",
207 target_os = "solaris",
208 target_os = "illumos",
209 target_os = "emscripten",
210 target_os = "fuchsia",
215 target_os = "vxworks"
217 pub fn set_cloexec(&self) -> io::Result<()> {
219 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
224 all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))),
225 target_os = "solaris",
226 target_os = "illumos",
227 target_os = "emscripten",
228 target_os = "fuchsia",
233 target_os = "vxworks"
235 pub fn set_cloexec(&self) -> io::Result<()> {
237 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
238 let new = previous | libc::FD_CLOEXEC;
240 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
245 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
246 pub fn set_cloexec(&self) -> io::Result<()> {
247 // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to,
248 // because neither supports spawning processes.
252 #[cfg(target_os = "linux")]
253 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
255 let v = nonblocking as libc::c_int;
256 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
261 #[cfg(not(target_os = "linux"))]
262 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
264 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
265 let new = if nonblocking {
266 previous | libc::O_NONBLOCK
268 previous & !libc::O_NONBLOCK
271 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
278 pub fn duplicate(&self) -> io::Result<FileDesc> {
279 Ok(Self(self.0.try_clone()?))
283 impl<'a> Read for &'a FileDesc {
284 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
289 impl AsInner<OwnedFd> for FileDesc {
290 fn as_inner(&self) -> &OwnedFd {
295 impl IntoInner<OwnedFd> for FileDesc {
296 fn into_inner(self) -> OwnedFd {
301 impl FromInner<OwnedFd> for FileDesc {
302 fn from_inner(owned_fd: OwnedFd) -> Self {
307 impl AsFd for FileDesc {
308 fn as_fd(&self) -> BorrowedFd<'_> {
313 impl AsRawFd for FileDesc {
314 fn as_raw_fd(&self) -> RawFd {
319 impl IntoRawFd for FileDesc {
320 fn into_raw_fd(self) -> RawFd {
325 impl FromRawFd for FileDesc {
326 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
327 Self(FromRawFd::from_raw_fd(raw_fd))