diff options
author | mark <markm@cs.wisc.edu> | 2020-06-11 21:31:49 -0500 |
---|---|---|
committer | mark <markm@cs.wisc.edu> | 2020-07-27 19:51:13 -0500 |
commit | 2c31b45ae878b821975c4ebd94cc1e49f6073fd0 (patch) | |
tree | 14f64e683e3f64dcbcfb8c2c7cb45ac7592e6e09 /library/std/src/sys/unix/fd.rs | |
parent | 9be8ffcb0206fc1558069a7b4766090df7877659 (diff) | |
download | rust-2c31b45ae878b821975c4ebd94cc1e49f6073fd0.tar.gz |
mv std libs to library/
Diffstat (limited to 'library/std/src/sys/unix/fd.rs')
-rw-r--r-- | library/std/src/sys/unix/fd.rs | 258 |
1 files changed, 258 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 00000000000..84c4d662161 --- /dev/null +++ b/library/std/src/sys/unix/fd.rs @@ -0,0 +1,258 @@ +#![unstable(reason = "not public", issue = "none", feature = "fd")] + +use crate::cmp; +use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read}; +use crate::mem; +use crate::sys::cvt; +use crate::sys_common::AsInner; + +use libc::{c_int, c_void}; + +#[derive(Debug)] +pub struct FileDesc { + fd: c_int, +} + +// 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 = c_int::MAX as usize - 1; +#[cfg(not(target_os = "macos"))] +const READ_LIMIT: usize = libc::ssize_t::MAX as usize; + +impl FileDesc { + pub fn new(fd: c_int) -> FileDesc { + FileDesc { fd } + } + + pub fn raw(&self) -> c_int { + self.fd + } + + /// Extracts the actual file descriptor without closing it. + pub fn into_raw(self) -> c_int { + let fd = self.fd; + mem::forget(self); + fd + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::readv( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + true + } + + 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(target_os = "android")] + use super::android::cvt_pread64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pread64( + fd: c_int, + buf: *mut c_void, + count: usize, + offset: i64, + ) -> io::Result<isize> { + #[cfg(not(target_os = "linux"))] + use libc::pread as pread64; + #[cfg(target_os = "linux")] + use libc::pread64; + cvt(pread64(fd, buf, count, offset)) + } + + unsafe { + cvt_pread64( + self.fd, + buf.as_mut_ptr() as *mut c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + pub fn write(&self, buf: &[u8]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT)) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + let ret = cvt(unsafe { + libc::writev( + self.fd, + bufs.as_ptr() as *const libc::iovec, + cmp::min(bufs.len(), c_int::MAX as usize) as c_int, + ) + })?; + Ok(ret as usize) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + true + } + + pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> { + #[cfg(target_os = "android")] + use super::android::cvt_pwrite64; + + #[cfg(not(target_os = "android"))] + unsafe fn cvt_pwrite64( + fd: c_int, + buf: *const c_void, + count: usize, + offset: i64, + ) -> io::Result<isize> { + #[cfg(not(target_os = "linux"))] + use libc::pwrite as pwrite64; + #[cfg(target_os = "linux")] + use libc::pwrite64; + cvt(pwrite64(fd, buf, count, offset)) + } + + unsafe { + cvt_pwrite64( + self.fd, + buf.as_ptr() as *const c_void, + cmp::min(buf.len(), READ_LIMIT), + offset as i64, + ) + .map(|n| n as usize) + } + } + + #[cfg(target_os = "linux")] + pub fn get_cloexec(&self) -> io::Result<bool> { + unsafe { Ok((cvt(libc::fcntl(self.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" + )))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + Ok(()) + } + } + #[cfg(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" + ))] + pub fn set_cloexec(&self) -> io::Result<()> { + unsafe { + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + let new = previous | libc::FD_CLOEXEC; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?; + } + Ok(()) + } + } + + #[cfg(target_os = "linux")] + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + unsafe { + let v = nonblocking as c_int; + cvt(libc::ioctl(self.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.fd, libc::F_GETFL))?; + let new = if nonblocking { + previous | libc::O_NONBLOCK + } else { + previous & !libc::O_NONBLOCK + }; + if new != previous { + cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; + } + Ok(()) + } + } + + pub fn duplicate(&self) -> io::Result<FileDesc> { + // We want to atomically duplicate this file descriptor and set the + // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This + // is a POSIX flag that was added to Linux in 2.6.24. + let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?; + Ok(FileDesc::new(fd)) + } +} + +impl<'a> Read for &'a FileDesc { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + (**self).read(buf) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} + +impl AsInner<c_int> for FileDesc { + fn as_inner(&self) -> &c_int { + &self.fd + } +} + +impl Drop for FileDesc { + fn drop(&mut self) { + // Note that errors are ignored when closing a file descriptor. The + // reason for this is that if an error occurs we don't actually know if + // the file descriptor was closed or not, and if we retried (for + // something like EINTR), we might close another valid file descriptor + // opened after we closed ours. + let _ = unsafe { libc::close(self.fd) }; + } +} |