diff options
Diffstat (limited to 'ctr-std/src/sys/unix/fd.rs')
| -rw-r--r-- | ctr-std/src/sys/unix/fd.rs | 104 |
1 files changed, 88 insertions, 16 deletions
diff --git a/ctr-std/src/sys/unix/fd.rs b/ctr-std/src/sys/unix/fd.rs index 91adac1..e19cbb0 100644 --- a/ctr-std/src/sys/unix/fd.rs +++ b/ctr-std/src/sys/unix/fd.rs @@ -14,9 +14,9 @@ use cmp; use io::{self, Read}; use libc::{self, c_int, c_void, ssize_t}; use mem; +use sync::atomic::{AtomicBool, Ordering}; use sys::cvt; use sys_common::AsInner; -use sys_common::io::read_to_end_uninitialized; #[derive(Debug)] pub struct FileDesc { @@ -28,7 +28,7 @@ fn max_len() -> usize { // with the man page quoting that if the count of bytes to read is // greater than `SSIZE_MAX` the result is "unspecified". // - // On OSX, however, apparently the 64-bit libc is either buggy or + // 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. @@ -110,22 +110,98 @@ impl FileDesc { } } - // This is a unix-specific operation that the 3DS likely doesn't support. - // However, there's no reason to make calling this function an error either. + #[cfg(not(any(target_env = "newlib", + target_os = "solaris", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "haiku")))] pub fn set_cloexec(&self) -> io::Result<()> { - Ok(()) + unsafe { + cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + Ok(()) + } + } + #[cfg(any(target_env = "newlib", + target_os = "solaris", + target_os = "emscripten", + target_os = "fuchsia", + target_os = "l4re", + target_os = "haiku"))] + 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(()) + } } - // This is a unix-specific operation that the 3DS likely doesn't support. - // However, there's no reason to make calling this function an error either. - pub fn set_nonblocking(&self) -> io::Result<()> { - Ok(()) + 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(()) + } } - // The sdmc and romfs devoptabs definitely don't support this operation. - // Not sure if it will be needed for network support or not. pub fn duplicate(&self) -> io::Result<FileDesc> { - unimplemented!() + // We want to atomically duplicate this file descriptor and set the + // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This + // flag, however, isn't supported on older Linux kernels (earlier than + // 2.6.24). + // + // To detect this and ensure that CLOEXEC is still set, we + // follow a strategy similar to musl [1] where if passing + // F_DUPFD_CLOEXEC causes `fcntl` to return EINVAL it means it's not + // supported (the third parameter, 0, is always valid), so we stop + // trying that. + // + // Also note that Android doesn't have F_DUPFD_CLOEXEC, but get it to + // resolve so we at least compile this. + // + // [1]: http://comments.gmane.org/gmane.linux.lib.musl.general/2963 + #[cfg(any(target_os = "android", target_os = "haiku"))] + use libc::F_DUPFD as F_DUPFD_CLOEXEC; + #[cfg(not(any(target_os = "android", target_os="haiku")))] + use libc::F_DUPFD_CLOEXEC; + + let make_filedesc = |fd| { + let fd = FileDesc::new(fd); + fd.set_cloexec()?; + Ok(fd) + }; + static TRY_CLOEXEC: AtomicBool = + AtomicBool::new(!cfg!(target_os = "android")); + let fd = self.raw(); + if TRY_CLOEXEC.load(Ordering::Relaxed) { + match cvt(unsafe { libc::fcntl(fd, F_DUPFD_CLOEXEC, 0) }) { + // We *still* call the `set_cloexec` method as apparently some + // linux kernel at some point stopped setting CLOEXEC even + // though it reported doing so on F_DUPFD_CLOEXEC. + Ok(fd) => { + return Ok(if cfg!(target_os = "linux") { + make_filedesc(fd)? + } else { + FileDesc::new(fd) + }) + } + Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => { + TRY_CLOEXEC.store(false, Ordering::Relaxed); + } + Err(e) => return Err(e), + } + } + cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc) } } @@ -133,10 +209,6 @@ impl<'a> Read for &'a FileDesc { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { (**self).read(buf) } - - fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { - unsafe { read_to_end_uninitialized(self, buf) } - } } impl AsInner<c_int> for FileDesc { |