summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/unix/fd.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/unix/fd.rs')
-rw-r--r--library/std/src/sys/unix/fd.rs330
1 files changed, 330 insertions, 0 deletions
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
new file mode 100644
index 000000000..30812dabb
--- /dev/null
+++ b/library/std/src/sys/unix/fd.rs
@@ -0,0 +1,330 @@
+#![unstable(reason = "not public", issue = "none", feature = "fd")]
+
+#[cfg(test)]
+mod tests;
+
+use crate::cmp;
+use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::sys::cvt;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re"
+))]
+use libc::off64_t;
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "android"
+)))]
+use libc::off_t as off64_t;
+
+#[derive(Debug)]
+pub struct FileDesc(OwnedFd);
+
+// The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
+// with the man page quoting that if the count of bytes to read is
+// greater than `SSIZE_MAX` the result is "unspecified".
+//
+// On macOS, however, apparently the 64-bit libc is either buggy or
+// intentionally showing odd behavior by rejecting any read with a size
+// larger than or equal to INT_MAX. To handle both of these the read
+// size is capped on both platforms.
+#[cfg(target_os = "macos")]
+const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
+#[cfg(not(target_os = "macos"))]
+const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "watchos",
+))]
+const fn max_iov() -> usize {
+ libc::IOV_MAX as usize
+}
+
+#[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
+const fn max_iov() -> usize {
+ libc::UIO_MAXIOV as usize
+}
+
+#[cfg(not(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "horizon",
+ target_os = "watchos",
+)))]
+const fn max_iov() -> usize {
+ 16 // The minimum value required by POSIX.
+}
+
+impl FileDesc {
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::read(
+ self.as_raw_fd(),
+ buf.as_mut_ptr() as *mut libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::readv(
+ self.as_raw_fd(),
+ bufs.as_ptr() as *const libc::iovec,
+ cmp::min(bufs.len(), max_iov()) as libc::c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ return crate::io::default_read_vectored(|b| self.read(b), bufs);
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ cfg!(not(any(target_os = "espidf", target_os = "horizon")))
+ }
+
+ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ let mut me = self;
+ (&mut me).read_to_end(buf)
+ }
+
+ pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ use libc::pread as pread64;
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ use libc::pread64;
+
+ unsafe {
+ cvt(pread64(
+ self.as_raw_fd(),
+ buf.as_mut_ptr() as *mut libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ offset as off64_t,
+ ))
+ .map(|n| n as usize)
+ }
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ let ret = cvt(unsafe {
+ libc::read(
+ self.as_raw_fd(),
+ buf.unfilled_mut().as_mut_ptr() as *mut libc::c_void,
+ cmp::min(buf.remaining(), READ_LIMIT),
+ )
+ })?;
+
+ // Safety: `ret` bytes were written to the initialized portion of the buffer
+ unsafe {
+ buf.assume_init(ret as usize);
+ }
+ buf.add_filled(ret as usize);
+ Ok(())
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::write(
+ self.as_raw_fd(),
+ buf.as_ptr() as *const libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ let ret = cvt(unsafe {
+ libc::writev(
+ self.as_raw_fd(),
+ bufs.as_ptr() as *const libc::iovec,
+ cmp::min(bufs.len(), max_iov()) as libc::c_int,
+ )
+ })?;
+ Ok(ret as usize)
+ }
+
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ return crate::io::default_write_vectored(|b| self.write(b), bufs);
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ cfg!(not(any(target_os = "espidf", target_os = "horizon")))
+ }
+
+ pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ use libc::pwrite as pwrite64;
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ use libc::pwrite64;
+
+ unsafe {
+ cvt(pwrite64(
+ self.as_raw_fd(),
+ buf.as_ptr() as *const libc::c_void,
+ cmp::min(buf.len(), READ_LIMIT),
+ offset as off64_t,
+ ))
+ .map(|n| n as usize)
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn get_cloexec(&self) -> io::Result<bool> {
+ unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
+ }
+
+ #[cfg(not(any(
+ target_env = "newlib",
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re",
+ target_os = "linux",
+ target_os = "haiku",
+ target_os = "redox",
+ target_os = "vxworks"
+ )))]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ unsafe {
+ cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
+ Ok(())
+ }
+ }
+ #[cfg(any(
+ all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))),
+ target_os = "solaris",
+ target_os = "illumos",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "l4re",
+ target_os = "linux",
+ target_os = "haiku",
+ target_os = "redox",
+ target_os = "vxworks"
+ ))]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ unsafe {
+ let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
+ let new = previous | libc::FD_CLOEXEC;
+ if new != previous {
+ cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
+ }
+ Ok(())
+ }
+ }
+ #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to,
+ // because neither supports spawning processes.
+ Ok(())
+ }
+
+ #[cfg(target_os = "linux")]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ let v = nonblocking as libc::c_int;
+ cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
+ Ok(())
+ }
+ }
+
+ #[cfg(not(target_os = "linux"))]
+ pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
+ unsafe {
+ let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
+ let new = if nonblocking {
+ previous | libc::O_NONBLOCK
+ } else {
+ previous & !libc::O_NONBLOCK
+ };
+ if new != previous {
+ cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
+ }
+ Ok(())
+ }
+ }
+
+ #[inline]
+ pub fn duplicate(&self) -> io::Result<FileDesc> {
+ Ok(Self(self.0.try_clone()?))
+ }
+}
+
+impl<'a> Read for &'a FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl AsInner<OwnedFd> for FileDesc {
+ fn as_inner(&self) -> &OwnedFd {
+ &self.0
+ }
+}
+
+impl IntoInner<OwnedFd> for FileDesc {
+ fn into_inner(self) -> OwnedFd {
+ self.0
+ }
+}
+
+impl FromInner<OwnedFd> for FileDesc {
+ fn from_inner(owned_fd: OwnedFd) -> Self {
+ Self(owned_fd)
+ }
+}
+
+impl AsFd for FileDesc {
+ fn as_fd(&self) -> BorrowedFd<'_> {
+ self.0.as_fd()
+ }
+}
+
+impl AsRawFd for FileDesc {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoRawFd for FileDesc {
+ fn into_raw_fd(self) -> RawFd {
+ self.0.into_raw_fd()
+ }
+}
+
+impl FromRawFd for FileDesc {
+ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+ Self(FromRawFd::from_raw_fd(raw_fd))
+ }
+}