diff --git a/filedescriptor/Cargo.toml b/filedescriptor/Cargo.toml index 9cb89bd59..130a4ad59 100644 --- a/filedescriptor/Cargo.toml +++ b/filedescriptor/Cargo.toml @@ -9,3 +9,10 @@ failure = "0.1" failure_derive = "0.1" libc = "0.2" +[target."cfg(windows)".dependencies] +winapi = { version = "0.3", features = [ + "winuser", + "handleapi", + "fileapi", + "namedpipeapi", +]} diff --git a/filedescriptor/src/lib.rs b/filedescriptor/src/lib.rs index 813fd9bd1..d7b9bf881 100644 --- a/filedescriptor/src/lib.rs +++ b/filedescriptor/src/lib.rs @@ -1,143 +1,14 @@ -use failure::{bail, Fallible}; -use std::os::unix::prelude::*; - #[cfg(unix)] -pub trait AsRawFileDescriptor: AsRawFd {} - +mod unix; #[cfg(unix)] -impl AsRawFileDescriptor for T {} +pub use crate::unix::*; #[cfg(windows)] -pub trait AsRawFileDescriptor: AsRawHandle {} - +mod windows; #[cfg(windows)] -impl AsRawFileDescriptor for T {} - -#[derive(Debug)] -pub struct FileDescriptor { - fd: RawFd, -} - -impl std::io::Read for FileDescriptor { - fn read(&mut self, buf: &mut [u8]) -> Result { - let size = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut _, buf.len()) }; - if size == -1 { - Err(std::io::Error::last_os_error()) - } else { - Ok(size as usize) - } - } -} - -impl std::io::Write for FileDescriptor { - fn write(&mut self, buf: &[u8]) -> Result { - let size = unsafe { libc::write(self.fd, buf.as_ptr() as *const _, buf.len()) }; - if size == -1 { - Err(std::io::Error::last_os_error()) - } else { - Ok(size as usize) - } - } - fn flush(&mut self) -> Result<(), std::io::Error> { - Ok(()) - } -} - -impl Drop for FileDescriptor { - fn drop(&mut self) { - unsafe { - libc::close(self.fd); - } - } -} - -impl AsRawFd for FileDescriptor { - fn as_raw_fd(&self) -> RawFd { - self.fd - } -} - -impl AsRawFd for &FileDescriptor { - fn as_raw_fd(&self) -> RawFd { - self.fd - } -} - -fn dup_fd(fd: RawFd) -> Fallible { - let duped = unsafe { libc::dup(fd) }; - if duped == -1 { - bail!( - "dup of fd {} failed: {:?}", - fd, - std::io::Error::last_os_error() - ) - } else { - let mut owned = FileDescriptor { fd: duped }; - owned.cloexec()?; - Ok(owned) - } -} +pub use crate::windows::*; pub struct Pipes { pub read: FileDescriptor, pub write: FileDescriptor, } - -pub fn dup(f: F) -> Fallible { - #[cfg(unix)] - dup_fd(f.as_raw_fd()) -} - -impl FileDescriptor { - pub fn dup(f: F) -> Fallible { - dup(f) - } - - pub fn clone(&self) -> Fallible { - dup(self) - } - - pub fn pipe() -> Fallible { - let mut fds = [-1i32; 2]; - let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; - if res == -1 { - bail!( - "failed to create a pipe: {:?}", - std::io::Error::last_os_error() - ) - } else { - let mut read = FileDescriptor { fd: fds[0] }; - let mut write = FileDescriptor { fd: fds[1] }; - read.cloexec()?; - write.cloexec()?; - Ok(Pipes { read, write }) - } - } - - /// Helper function to set the close-on-exec flag for a raw descriptor - fn cloexec(&mut self) -> Fallible<()> { - let flags = unsafe { libc::fcntl(self.fd, libc::F_GETFD) }; - if flags == -1 { - bail!( - "fcntl to read flags failed: {:?}", - std::io::Error::last_os_error() - ); - } - let result = unsafe { libc::fcntl(self.fd, libc::F_SETFD, flags | libc::FD_CLOEXEC) }; - if result == -1 { - bail!( - "fcntl to set CLOEXEC failed: {:?}", - std::io::Error::last_os_error() - ); - } - Ok(()) - } - - pub fn as_stdio(&self) -> Fallible { - let duped = dup_fd(self.fd)?; - let fd = duped.fd; - let stdio = unsafe { std::process::Stdio::from_raw_fd(fd) }; - std::mem::forget(duped); // don't drop; stdio now owns it - Ok(stdio) - } -} diff --git a/filedescriptor/src/unix.rs b/filedescriptor/src/unix.rs new file mode 100644 index 000000000..7fb65b8c9 --- /dev/null +++ b/filedescriptor/src/unix.rs @@ -0,0 +1,130 @@ +use crate::Pipes; +use failure::{bail, Fallible}; +use std::os::unix::prelude::*; + +pub trait AsRawFileDescriptor: AsRawFd {} + +impl AsRawFileDescriptor for T {} + +#[derive(Debug)] +pub struct FileDescriptor { + fd: RawFd, +} + +impl std::io::Read for FileDescriptor { + fn read(&mut self, buf: &mut [u8]) -> Result { + let size = unsafe { libc::read(self.fd, buf.as_mut_ptr() as *mut _, buf.len()) }; + if size == -1 { + Err(std::io::Error::last_os_error()) + } else { + Ok(size as usize) + } + } +} + +impl std::io::Write for FileDescriptor { + fn write(&mut self, buf: &[u8]) -> Result { + let size = unsafe { libc::write(self.fd, buf.as_ptr() as *const _, buf.len()) }; + if size == -1 { + Err(std::io::Error::last_os_error()) + } else { + Ok(size as usize) + } + } + fn flush(&mut self) -> Result<(), std::io::Error> { + Ok(()) + } +} + +impl Drop for FileDescriptor { + fn drop(&mut self) { + unsafe { + libc::close(self.fd); + } + } +} + +impl AsRawFd for FileDescriptor { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +impl AsRawFd for &FileDescriptor { + fn as_raw_fd(&self) -> RawFd { + self.fd + } +} + +fn dup_fd(fd: RawFd) -> Fallible { + let duped = unsafe { libc::dup(fd) }; + if duped == -1 { + bail!( + "dup of fd {} failed: {:?}", + fd, + std::io::Error::last_os_error() + ) + } else { + let mut owned = FileDescriptor { fd: duped }; + owned.cloexec()?; + Ok(owned) + } +} + +pub fn dup(f: F) -> Fallible { + dup_fd(f.as_raw_fd()) +} + +impl FileDescriptor { + pub fn dup(f: F) -> Fallible { + dup(f) + } + + pub fn try_clone(&self) -> Fallible { + dup(self) + } + + pub fn pipe() -> Fallible { + let mut fds = [-1i32; 2]; + let res = unsafe { libc::pipe(fds.as_mut_ptr()) }; + if res == -1 { + bail!( + "failed to create a pipe: {:?}", + std::io::Error::last_os_error() + ) + } else { + let mut read = FileDescriptor { fd: fds[0] }; + let mut write = FileDescriptor { fd: fds[1] }; + read.cloexec()?; + write.cloexec()?; + Ok(Pipes { read, write }) + } + } + + /// Helper function to set the close-on-exec flag for a raw descriptor + fn cloexec(&mut self) -> Fallible<()> { + let flags = unsafe { libc::fcntl(self.fd, libc::F_GETFD) }; + if flags == -1 { + bail!( + "fcntl to read flags failed: {:?}", + std::io::Error::last_os_error() + ); + } + let result = unsafe { libc::fcntl(self.fd, libc::F_SETFD, flags | libc::FD_CLOEXEC) }; + if result == -1 { + bail!( + "fcntl to set CLOEXEC failed: {:?}", + std::io::Error::last_os_error() + ); + } + Ok(()) + } + + pub fn as_stdio(&self) -> Fallible { + let duped = dup_fd(self.fd)?; + let fd = duped.fd; + let stdio = unsafe { std::process::Stdio::from_raw_fd(fd) }; + std::mem::forget(duped); // don't drop; stdio now owns it + Ok(stdio) + } +} diff --git a/filedescriptor/src/windows.rs b/filedescriptor/src/windows.rs new file mode 100644 index 000000000..b8ab0ea2c --- /dev/null +++ b/filedescriptor/src/windows.rs @@ -0,0 +1,134 @@ +use crate::Pipes; +use failure::{bail, Fallible}; +use std::io::{self, Error as IoError}; +use std::os::windows::prelude::*; +use std::os::windows::raw::HANDLE; +use std::ptr; +use winapi::um::fileapi::*; +use winapi::um::handleapi::*; +use winapi::um::namedpipeapi::CreatePipe; +use winapi::um::processthreadsapi::*; + +pub trait AsRawFileDescriptor: AsRawHandle {} + +impl AsRawFileDescriptor for T {} + +#[derive(Debug)] +pub struct FileDescriptor { + pub handle: HANDLE, +} + +unsafe impl Send for FileDescriptor {} + +impl Drop for FileDescriptor { + fn drop(&mut self) { + if self.handle != INVALID_HANDLE_VALUE && !self.handle.is_null() { + unsafe { CloseHandle(self.handle) }; + } + } +} + +fn dup_handle(handle: HANDLE) -> Fallible { + if handle == INVALID_HANDLE_VALUE || handle.is_null() { + return Ok(FileDescriptor { handle: handle }); + } + + let proc = unsafe { GetCurrentProcess() }; + let mut duped = INVALID_HANDLE_VALUE; + let ok = unsafe { + DuplicateHandle( + proc, + handle as *mut _, + proc, + &mut duped, + 0, + 0, + winapi::um::winnt::DUPLICATE_SAME_ACCESS, + ) + }; + if ok == 0 { + Err(IoError::last_os_error().into()) + } else { + Ok(FileDescriptor { + handle: duped as *mut _, + }) + } +} + +pub fn dup(f: F) -> Fallible { + dup_handle(f.as_raw_handle()) +} + +impl FileDescriptor { + pub fn new(handle: HANDLE) -> Self { + Self { handle } + } + + pub fn try_clone(&self) -> Fallible { + dup_handle(self.handle) + } + pub fn as_stdio(&self) -> Fallible { + let duped = dup_handle(self.handle)?; + let handle = duped.handle; + let stdio = unsafe { std::process::Stdio::from_raw_handle(handle) }; + std::mem::forget(duped); // don't drop; stdio now owns it + Ok(stdio) + } + pub fn pipe() -> Fallible { + let mut read: HANDLE = INVALID_HANDLE_VALUE; + let mut write: HANDLE = INVALID_HANDLE_VALUE; + if unsafe { CreatePipe(&mut read, &mut write, ptr::null_mut(), 0) } == 0 { + bail!("CreatePipe failed: {}", IoError::last_os_error()); + } + Ok(Pipes { + read: FileDescriptor { handle: read }, + write: FileDescriptor { handle: write }, + }) + } + pub fn dup(f: F) -> Fallible { + dup(f) + } +} + +impl io::Read for FileDescriptor { + fn read(&mut self, buf: &mut [u8]) -> Result { + let mut num_read = 0; + let ok = unsafe { + ReadFile( + self.handle as *mut _, + buf.as_mut_ptr() as *mut _, + buf.len() as u32, + &mut num_read, + ptr::null_mut(), + ) + }; + if ok == 0 { + Err(IoError::last_os_error()) + } else { + Ok(num_read as usize) + } + } +} + +impl io::Write for FileDescriptor { + fn write(&mut self, buf: &[u8]) -> Result { + let mut num_wrote = 0; + let ok = unsafe { + WriteFile( + self.handle as *mut _, + buf.as_ptr() as *const _, + buf.len() as u32, + &mut num_wrote, + ptr::null_mut(), + ) + }; + if ok == 0 { + Err(IoError::last_os_error()) + } else { + Ok(num_wrote as usize) + } + } + fn flush(&mut self) -> Result<(), io::Error> { + Ok(()) + } +}