about summary refs log tree commit diff
path: root/library/std/src/sys
diff options
context:
space:
mode:
authorDan Gohman <dev@sunfishcode.online>2021-06-30 21:44:30 -0700
committerDan Gohman <dev@sunfishcode.online>2021-08-19 12:02:39 -0700
commitd15418586ca78ead4f87ad18fcffa3550c1b169e (patch)
tree93234d47e5eab3c9177ad31b9369abcd1c1ed5d1 /library/std/src/sys
parent2451f42c1deb9379d5e8e5fa86b0bf857ae048ec (diff)
downloadrust-d15418586ca78ead4f87ad18fcffa3550c1b169e.tar.gz
rust-d15418586ca78ead4f87ad18fcffa3550c1b169e.zip
I/O safety.
Introduce `OwnedFd` and `BorrowedFd`, and the `AsFd` trait, and
implementations of `AsFd`, `From<OwnedFd>` and `From<T> for OwnedFd`
for relevant types, along with Windows counterparts for handles and
sockets.

Tracking issue:
 - <https://github.com/rust-lang/rust/issues/87074>

RFC:
 - <https://github.com/rust-lang/rfcs/blob/master/text/3128-io-safety.md>
Diffstat (limited to 'library/std/src/sys')
-rw-r--r--library/std/src/sys/unix/fd.rs115
-rw-r--r--library/std/src/sys/unix/fd/tests.rs3
-rw-r--r--library/std/src/sys/unix/fs.rs77
-rw-r--r--library/std/src/sys/unix/net.rs92
-rw-r--r--library/std/src/sys/unix/pipe.rs63
-rw-r--r--library/std/src/sys/unix/process/process_common.rs15
-rw-r--r--library/std/src/sys/unix/stdio.rs65
-rw-r--r--library/std/src/sys/wasi/fd.rs138
-rw-r--r--library/std/src/sys/wasi/fs.rs49
-rw-r--r--library/std/src/sys/wasi/net.rs125
-rw-r--r--library/std/src/sys/wasi/stdio.rs19
-rw-r--r--library/std/src/sys/windows/fs.rs73
-rw-r--r--library/std/src/sys/windows/handle.rs122
-rw-r--r--library/std/src/sys/windows/net.rs106
-rw-r--r--library/std/src/sys/windows/os.rs2
-rw-r--r--library/std/src/sys/windows/pipe.rs17
-rw-r--r--library/std/src/sys/windows/process.rs41
-rw-r--r--library/std/src/sys/windows/stdio.rs21
-rw-r--r--library/std/src/sys/windows/thread.rs5
19 files changed, 771 insertions, 377 deletions
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 28e32681e15..0956726084e 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -5,21 +5,14 @@ mod tests;
 
 use crate::cmp;
 use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
-use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
 use crate::sys::cvt;
-use crate::sys_common::AsInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 
 use libc::{c_int, c_void};
 
 #[derive(Debug)]
-#[rustc_layout_scalar_valid_range_start(0)]
-// libstd/os/raw/mod.rs assures me that every libstd-supported platform has a
-// 32-bit c_int. Below is -2, in two's complement, but that only works out
-// because c_int is 32 bits.
-#[rustc_layout_scalar_valid_range_end(0xFF_FF_FF_FE)]
-pub struct FileDesc {
-    fd: c_int,
-}
+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
@@ -67,26 +60,13 @@ const fn max_iov() -> usize {
 }
 
 impl FileDesc {
-    pub fn new(fd: c_int) -> FileDesc {
-        assert_ne!(fd, -1i32);
-        // SAFETY: we just asserted that the value is in the valid range and isn't `-1` (the only value bigger than `0xFF_FF_FF_FE` unsigned)
-        unsafe { 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))
+            libc::read(
+                self.as_raw_fd(),
+                buf.as_mut_ptr() as *mut c_void,
+                cmp::min(buf.len(), READ_LIMIT),
+            )
         })?;
         Ok(ret as usize)
     }
@@ -95,7 +75,7 @@ impl FileDesc {
     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
         let ret = cvt(unsafe {
             libc::readv(
-                self.fd,
+                self.as_raw_fd(),
                 bufs.as_ptr() as *const libc::iovec,
                 cmp::min(bufs.len(), max_iov()) as c_int,
             )
@@ -138,7 +118,7 @@ impl FileDesc {
 
         unsafe {
             cvt_pread64(
-                self.fd,
+                self.as_raw_fd(),
                 buf.as_mut_ptr() as *mut c_void,
                 cmp::min(buf.len(), READ_LIMIT),
                 offset as i64,
@@ -149,7 +129,11 @@ impl FileDesc {
 
     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))
+            libc::write(
+                self.as_raw_fd(),
+                buf.as_ptr() as *const c_void,
+                cmp::min(buf.len(), READ_LIMIT),
+            )
         })?;
         Ok(ret as usize)
     }
@@ -158,7 +142,7 @@ impl FileDesc {
     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
         let ret = cvt(unsafe {
             libc::writev(
-                self.fd,
+                self.as_raw_fd(),
                 bufs.as_ptr() as *const libc::iovec,
                 cmp::min(bufs.len(), max_iov()) as c_int,
             )
@@ -196,7 +180,7 @@ impl FileDesc {
 
         unsafe {
             cvt_pwrite64(
-                self.fd,
+                self.as_raw_fd(),
                 buf.as_ptr() as *const c_void,
                 cmp::min(buf.len(), READ_LIMIT),
                 offset as i64,
@@ -207,7 +191,7 @@ impl FileDesc {
 
     #[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) }
+        unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
     }
 
     #[cfg(not(any(
@@ -224,7 +208,7 @@ impl FileDesc {
     )))]
     pub fn set_cloexec(&self) -> io::Result<()> {
         unsafe {
-            cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
+            cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
             Ok(())
         }
     }
@@ -242,10 +226,10 @@ impl FileDesc {
     ))]
     pub fn set_cloexec(&self) -> io::Result<()> {
         unsafe {
-            let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
+            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.fd, libc::F_SETFD, new))?;
+                cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
             }
             Ok(())
         }
@@ -261,7 +245,7 @@ impl FileDesc {
     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
         unsafe {
             let v = nonblocking as c_int;
-            cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
+            cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
             Ok(())
         }
     }
@@ -269,14 +253,14 @@ impl FileDesc {
     #[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 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.fd, libc::F_SETFL, new))?;
+                cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
             }
             Ok(())
         }
@@ -296,8 +280,8 @@ impl FileDesc {
         #[cfg(target_os = "espidf")]
         let cmd = libc::F_DUPFD;
 
-        let fd = cvt(unsafe { libc::fcntl(self.raw(), cmd, 0) })?;
-        Ok(FileDesc::new(fd))
+        let fd = cvt(unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) })?;
+        Ok(unsafe { FileDesc::from_raw_fd(fd) })
     }
 }
 
@@ -312,19 +296,44 @@ impl<'a> Read for &'a FileDesc {
     }
 }
 
-impl AsInner<c_int> for FileDesc {
-    fn as_inner(&self) -> &c_int {
-        &self.fd
+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 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) };
+impl FromRawFd for FileDesc {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        Self(FromRawFd::from_raw_fd(raw_fd))
     }
 }
diff --git a/library/std/src/sys/unix/fd/tests.rs b/library/std/src/sys/unix/fd/tests.rs
index c9520485c3c..5d17e46786c 100644
--- a/library/std/src/sys/unix/fd/tests.rs
+++ b/library/std/src/sys/unix/fd/tests.rs
@@ -1,9 +1,10 @@
 use super::{FileDesc, IoSlice};
+use crate::os::unix::io::FromRawFd;
 use core::mem::ManuallyDrop;
 
 #[test]
 fn limit_vector_count() {
-    let stdout = ManuallyDrop::new(unsafe { FileDesc { fd: 1 } });
+    let stdout = ManuallyDrop::new(unsafe { FileDesc::from_raw_fd(1) });
     let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
     assert!(stdout.write_vectored(&bufs).is_ok());
 }
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index fd4defd72eb..6075eb5c7c5 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -4,13 +4,14 @@ use crate::ffi::{CStr, CString, OsStr, OsString};
 use crate::fmt;
 use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
 use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
 use crate::path::{Path, PathBuf};
 use crate::ptr;
 use crate::sync::Arc;
 use crate::sys::fd::FileDesc;
 use crate::sys::time::SystemTime;
 use crate::sys::{cvt, cvt_r};
-use crate::sys_common::{AsInner, FromInner};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 
 #[cfg(any(
     all(target_os = "linux", target_env = "gnu"),
@@ -764,11 +765,11 @@ impl File {
         // However, since this is a variadic function, C integer promotion rules mean that on
         // the ABI level, this still gets passed as `c_int` (aka `u32` on Unix platforms).
         let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode as c_int) })?;
-        Ok(File(FileDesc::new(fd)))
+        Ok(File(unsafe { FileDesc::from_raw_fd(fd) }))
     }
 
     pub fn file_attr(&self) -> io::Result<FileAttr> {
-        let fd = self.0.raw();
+        let fd = self.as_raw_fd();
 
         cfg_has_statx! {
             if let Some(ret) = unsafe { try_statx(
@@ -787,7 +788,7 @@ impl File {
     }
 
     pub fn fsync(&self) -> io::Result<()> {
-        cvt_r(|| unsafe { os_fsync(self.0.raw()) })?;
+        cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
         return Ok(());
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -801,7 +802,7 @@ impl File {
     }
 
     pub fn datasync(&self) -> io::Result<()> {
-        cvt_r(|| unsafe { os_datasync(self.0.raw()) })?;
+        cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
         return Ok(());
 
         #[cfg(any(target_os = "macos", target_os = "ios"))]
@@ -834,14 +835,14 @@ impl File {
 
     pub fn truncate(&self, size: u64) -> io::Result<()> {
         #[cfg(target_os = "android")]
-        return crate::sys::android::ftruncate64(self.0.raw(), size);
+        return crate::sys::android::ftruncate64(self.as_raw_fd(), size);
 
         #[cfg(not(target_os = "android"))]
         {
             use crate::convert::TryInto;
             let size: off64_t =
                 size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
-            cvt_r(|| unsafe { ftruncate64(self.0.raw(), size) }).map(drop)
+            cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
         }
     }
 
@@ -891,7 +892,7 @@ impl File {
             SeekFrom::End(off) => (libc::SEEK_END, off),
             SeekFrom::Current(off) => (libc::SEEK_CUR, off),
         };
-        let n = cvt(unsafe { lseek64(self.0.raw(), pos, whence) })?;
+        let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?;
         Ok(n as u64)
     }
 
@@ -899,16 +900,8 @@ impl File {
         self.0.duplicate().map(File)
     }
 
-    pub fn fd(&self) -> &FileDesc {
-        &self.0
-    }
-
-    pub fn into_fd(self) -> FileDesc {
-        self.0
-    }
-
     pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
-        cvt_r(|| unsafe { libc::fchmod(self.0.raw(), perm.mode) })?;
+        cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
         Ok(())
     }
 }
@@ -933,9 +926,51 @@ fn cstr(path: &Path) -> io::Result<CString> {
     Ok(CString::new(path.as_os_str().as_bytes())?)
 }
 
-impl FromInner<c_int> for File {
-    fn from_inner(fd: c_int) -> File {
-        File(FileDesc::new(fd))
+impl AsInner<FileDesc> for File {
+    fn as_inner(&self) -> &FileDesc {
+        &self.0
+    }
+}
+
+impl AsInnerMut<FileDesc> for File {
+    fn as_inner_mut(&mut self) -> &mut FileDesc {
+        &mut self.0
+    }
+}
+
+impl IntoInner<FileDesc> for File {
+    fn into_inner(self) -> FileDesc {
+        self.0
+    }
+}
+
+impl FromInner<FileDesc> for File {
+    fn from_inner(file_desc: FileDesc) -> Self {
+        Self(file_desc)
+    }
+}
+
+impl AsFd for File {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
+    }
+}
+
+impl AsRawFd for File {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for File {
+    fn into_raw_fd(self) -> RawFd {
+        self.0.into_raw_fd()
+    }
+}
+
+impl FromRawFd for File {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        Self(FromRawFd::from_raw_fd(raw_fd))
     }
 }
 
@@ -1009,7 +1044,7 @@ impl fmt::Debug for File {
             None
         }
 
-        let fd = self.0.raw();
+        let fd = self.as_raw_fd();
         let mut b = f.debug_struct("File");
         b.field("fd", &fd);
         if let Some(path) = get_path(fd) {
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index 3f614fde08a..c2f5da1dbbb 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -3,6 +3,7 @@ use crate::ffi::CStr;
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::mem;
 use crate::net::{Shutdown, SocketAddr};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::str;
 use crate::sys::fd::FileDesc;
 use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
@@ -74,10 +75,10 @@ impl Socket {
                     // flag to atomically create the socket and set it as
                     // CLOEXEC. On Linux this was added in 2.6.27.
                     let fd = cvt(libc::socket(fam, ty | libc::SOCK_CLOEXEC, 0))?;
-                    Ok(Socket(FileDesc::new(fd)))
+                    Ok(Socket(FileDesc::from_raw_fd(fd)))
                 } else {
                     let fd = cvt(libc::socket(fam, ty, 0))?;
-                    let fd = FileDesc::new(fd);
+                    let fd = FileDesc::from_raw_fd(fd);
                     fd.set_cloexec()?;
                     let socket = Socket(fd);
 
@@ -109,11 +110,11 @@ impl Socket {
                 ))] {
                     // Like above, set cloexec atomically
                     cvt(libc::socketpair(fam, ty | libc::SOCK_CLOEXEC, 0, fds.as_mut_ptr()))?;
-                    Ok((Socket(FileDesc::new(fds[0])), Socket(FileDesc::new(fds[1]))))
+                    Ok((Socket(FileDesc::from_raw_fd(fds[0])), Socket(FileDesc::from_raw_fd(fds[1]))))
                 } else {
                     cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?;
-                    let a = FileDesc::new(fds[0]);
-                    let b = FileDesc::new(fds[1]);
+                    let a = FileDesc::from_raw_fd(fds[0]);
+                    let b = FileDesc::from_raw_fd(fds[1]);
                     a.set_cloexec()?;
                     b.set_cloexec()?;
                     Ok((Socket(a), Socket(b)))
@@ -131,7 +132,7 @@ impl Socket {
         self.set_nonblocking(true)?;
         let r = unsafe {
             let (addrp, len) = addr.into_inner();
-            cvt(libc::connect(self.0.raw(), addrp, len))
+            cvt(libc::connect(self.as_raw_fd(), addrp, len))
         };
         self.set_nonblocking(false)?;
 
@@ -142,7 +143,7 @@ impl Socket {
             Err(e) => return Err(e),
         }
 
-        let mut pollfd = libc::pollfd { fd: self.0.raw(), events: libc::POLLOUT, revents: 0 };
+        let mut pollfd = libc::pollfd { fd: self.as_raw_fd(), events: libc::POLLOUT, revents: 0 };
 
         if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 {
             return Err(io::Error::new_const(
@@ -212,15 +213,17 @@ impl Socket {
                 target_os = "netbsd",
                 target_os = "openbsd",
             ))] {
-                let fd = cvt_r(|| unsafe {
-                    libc::accept4(self.0.raw(), storage, len, libc::SOCK_CLOEXEC)
-                })?;
-                Ok(Socket(FileDesc::new(fd)))
+                unsafe {
+                    let fd = cvt_r(|| libc::accept4(self.as_raw_fd(), storage, len, libc::SOCK_CLOEXEC))?;
+                    Ok(Socket(FileDesc::from_raw_fd(fd)))
+                }
             } else {
-                let fd = cvt_r(|| unsafe { libc::accept(self.0.raw(), storage, len) })?;
-                let fd = FileDesc::new(fd);
-                fd.set_cloexec()?;
-                Ok(Socket(fd))
+                unsafe {
+                    let fd = cvt_r(|| libc::accept(self.as_raw_fd(), storage, len))?;
+                    let fd = FileDesc::from_raw_fd(fd);
+                    fd.set_cloexec()?;
+                    Ok(Socket(fd))
+                }
             }
         }
     }
@@ -231,7 +234,7 @@ impl Socket {
 
     fn recv_with_flags(&self, buf: &mut [u8], flags: c_int) -> io::Result<usize> {
         let ret = cvt(unsafe {
-            libc::recv(self.0.raw(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
+            libc::recv(self.as_raw_fd(), buf.as_mut_ptr() as *mut c_void, buf.len(), flags)
         })?;
         Ok(ret as usize)
     }
@@ -263,7 +266,7 @@ impl Socket {
 
         let n = cvt(unsafe {
             libc::recvfrom(
-                self.0.raw(),
+                self.as_raw_fd(),
                 buf.as_mut_ptr() as *mut c_void,
                 buf.len(),
                 flags,
@@ -288,7 +291,7 @@ impl Socket {
         target_os = "openbsd",
     ))]
     pub fn recv_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
-        let n = cvt(unsafe { libc::recvmsg(self.0.raw(), msg, libc::MSG_CMSG_CLOEXEC) })?;
+        let n = cvt(unsafe { libc::recvmsg(self.as_raw_fd(), msg, libc::MSG_CMSG_CLOEXEC) })?;
         Ok(n as usize)
     }
 
@@ -319,7 +322,7 @@ impl Socket {
         target_os = "openbsd",
     ))]
     pub fn send_msg(&self, msg: &mut libc::msghdr) -> io::Result<usize> {
-        let n = cvt(unsafe { libc::sendmsg(self.0.raw(), msg, 0) })?;
+        let n = cvt(unsafe { libc::sendmsg(self.as_raw_fd(), msg, 0) })?;
         Ok(n as usize)
     }
 
@@ -369,7 +372,7 @@ impl Socket {
             Shutdown::Read => libc::SHUT_RD,
             Shutdown::Both => libc::SHUT_RDWR,
         };
-        cvt(unsafe { libc::shutdown(self.0.raw(), how) })?;
+        cvt(unsafe { libc::shutdown(self.as_raw_fd(), how) })?;
         Ok(())
     }
 
@@ -396,7 +399,7 @@ impl Socket {
     #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
         let mut nonblocking = nonblocking as libc::c_int;
-        cvt(unsafe { libc::ioctl(*self.as_inner(), libc::FIONBIO, &mut nonblocking) }).map(drop)
+        cvt(unsafe { libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &mut nonblocking) }).map(drop)
     }
 
     #[cfg(any(target_os = "solaris", target_os = "illumos"))]
@@ -410,23 +413,52 @@ impl Socket {
         let raw: c_int = getsockopt(self, libc::SOL_SOCKET, libc::SO_ERROR)?;
         if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
     }
+
+    // This is used by sys_common code to abstract over Windows and Unix.
+    pub fn as_raw(&self) -> RawFd {
+        self.as_raw_fd()
+    }
+}
+
+impl AsInner<FileDesc> for Socket {
+    fn as_inner(&self) -> &FileDesc {
+        &self.0
+    }
+}
+
+impl IntoInner<FileDesc> for Socket {
+    fn into_inner(self) -> FileDesc {
+        self.0
+    }
+}
+
+impl FromInner<FileDesc> for Socket {
+    fn from_inner(file_desc: FileDesc) -> Self {
+        Self(file_desc)
+    }
+}
+
+impl AsFd for Socket {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
+    }
 }
 
-impl AsInner<c_int> for Socket {
-    fn as_inner(&self) -> &c_int {
-        self.0.as_inner()
+impl AsRawFd for Socket {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
     }
 }
 
-impl FromInner<c_int> for Socket {
-    fn from_inner(fd: c_int) -> Socket {
-        Socket(FileDesc::new(fd))
+impl IntoRawFd for Socket {
+    fn into_raw_fd(self) -> RawFd {
+        self.0.into_raw_fd()
     }
 }
 
-impl IntoInner<c_int> for Socket {
-    fn into_inner(self) -> c_int {
-        self.0.into_raw()
+impl FromRawFd for Socket {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        Self(FromRawFd::from_raw_fd(raw_fd))
     }
 }
 
diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs
index 7ae37bdda70..a56c275c942 100644
--- a/library/std/src/sys/unix/pipe.rs
+++ b/library/std/src/sys/unix/pipe.rs
@@ -1,7 +1,9 @@
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::mem;
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::sys::fd::FileDesc;
 use crate::sys::{cvt, cvt_r};
+use crate::sys_common::IntoInner;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Anonymous pipes
@@ -24,16 +26,20 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
             target_os = "openbsd",
             target_os = "redox"
         ))] {
-            cvt(unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC) })?;
-            Ok((AnonPipe(FileDesc::new(fds[0])), AnonPipe(FileDesc::new(fds[1]))))
+            unsafe {
+                cvt(libc::pipe2(fds.as_mut_ptr(), libc::O_CLOEXEC))?;
+                Ok((AnonPipe(FileDesc::from_raw_fd(fds[0])), AnonPipe(FileDesc::from_raw_fd(fds[1]))))
+            }
         } else {
-            cvt(unsafe { libc::pipe(fds.as_mut_ptr()) })?;
-
-            let fd0 = FileDesc::new(fds[0]);
-            let fd1 = FileDesc::new(fds[1]);
-            fd0.set_cloexec()?;
-            fd1.set_cloexec()?;
-            Ok((AnonPipe(fd0), AnonPipe(fd1)))
+            unsafe {
+                cvt(libc::pipe(fds.as_mut_ptr()))?;
+
+                let fd0 = FileDesc::from_raw_fd(fds[0]);
+                let fd1 = FileDesc::from_raw_fd(fds[1]);
+                fd0.set_cloexec()?;
+                fd1.set_cloexec()?;
+                Ok((AnonPipe(fd0), AnonPipe(fd1)))
+            }
         }
     }
 }
@@ -64,11 +70,10 @@ impl AnonPipe {
     pub fn is_write_vectored(&self) -> bool {
         self.0.is_write_vectored()
     }
+}
 
-    pub fn fd(&self) -> &FileDesc {
-        &self.0
-    }
-    pub fn into_fd(self) -> FileDesc {
+impl IntoInner<FileDesc> for AnonPipe {
+    fn into_inner(self) -> FileDesc {
         self.0
     }
 }
@@ -76,15 +81,15 @@ impl AnonPipe {
 pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> {
     // Set both pipes into nonblocking mode as we're gonna be reading from both
     // in the `select` loop below, and we wouldn't want one to block the other!
-    let p1 = p1.into_fd();
-    let p2 = p2.into_fd();
+    let p1 = p1.into_inner();
+    let p2 = p2.into_inner();
     p1.set_nonblocking(true)?;
     p2.set_nonblocking(true)?;
 
     let mut fds: [libc::pollfd; 2] = unsafe { mem::zeroed() };
-    fds[0].fd = p1.raw();
+    fds[0].fd = p1.as_raw_fd();
     fds[0].events = libc::POLLIN;
-    fds[1].fd = p2.raw();
+    fds[1].fd = p2.as_raw_fd();
     fds[1].events = libc::POLLIN;
     loop {
         // wait for either pipe to become readable using `poll`
@@ -120,3 +125,27 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) ->
         }
     }
 }
+
+impl AsRawFd for AnonPipe {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
+impl AsFd for AnonPipe {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
+    }
+}
+
+impl IntoRawFd for AnonPipe {
+    fn into_raw_fd(self) -> RawFd {
+        self.0.into_raw_fd()
+    }
+}
+
+impl FromRawFd for AnonPipe {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        Self(FromRawFd::from_raw_fd(raw_fd))
+    }
+}
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index a1972380a9f..7b261a302c3 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -13,6 +13,7 @@ use crate::sys::fd::FileDesc;
 use crate::sys::fs::File;
 use crate::sys::pipe::{self, AnonPipe};
 use crate::sys_common::process::{CommandEnv, CommandEnvs};
+use crate::sys_common::IntoInner;
 
 #[cfg(not(target_os = "fuchsia"))]
 use crate::sys::fs::OpenOptions;
@@ -388,17 +389,17 @@ impl Stdio {
             // stderr. No matter which we dup first, the second will get
             // overwritten prematurely.
             Stdio::Fd(ref fd) => {
-                if fd.raw() >= 0 && fd.raw() <= libc::STDERR_FILENO {
+                if fd.as_raw_fd() >= 0 && fd.as_raw_fd() <= libc::STDERR_FILENO {
                     Ok((ChildStdio::Owned(fd.duplicate()?), None))
                 } else {
-                    Ok((ChildStdio::Explicit(fd.raw()), None))
+                    Ok((ChildStdio::Explicit(fd.as_raw_fd()), None))
                 }
             }
 
             Stdio::MakePipe => {
                 let (reader, writer) = pipe::anon_pipe()?;
                 let (ours, theirs) = if readable { (writer, reader) } else { (reader, writer) };
-                Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours)))
+                Ok((ChildStdio::Owned(theirs.into_inner()), Some(ours)))
             }
 
             #[cfg(not(target_os = "fuchsia"))]
@@ -408,7 +409,7 @@ impl Stdio {
                 opts.write(!readable);
                 let path = unsafe { CStr::from_ptr(DEV_NULL.as_ptr() as *const _) };
                 let fd = File::open_c(&path, &opts)?;
-                Ok((ChildStdio::Owned(fd.into_fd()), None))
+                Ok((ChildStdio::Owned(fd.into_inner()), None))
             }
 
             #[cfg(target_os = "fuchsia")]
@@ -419,13 +420,13 @@ impl Stdio {
 
 impl From<AnonPipe> for Stdio {
     fn from(pipe: AnonPipe) -> Stdio {
-        Stdio::Fd(pipe.into_fd())
+        Stdio::Fd(pipe.into_inner())
     }
 }
 
 impl From<File> for Stdio {
     fn from(file: File) -> Stdio {
-        Stdio::Fd(file.into_fd())
+        Stdio::Fd(file.into_inner())
     }
 }
 
@@ -434,7 +435,7 @@ impl ChildStdio {
         match *self {
             ChildStdio::Inherit => None,
             ChildStdio::Explicit(fd) => Some(fd),
-            ChildStdio::Owned(ref fd) => Some(fd.raw()),
+            ChildStdio::Owned(ref fd) => Some(fd.as_raw_fd()),
 
             #[cfg(target_os = "fuchsia")]
             ChildStdio::Null => None,
diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs
index a05fe8165cf..b359987595d 100644
--- a/library/std/src/sys/unix/stdio.rs
+++ b/library/std/src/sys/unix/stdio.rs
@@ -1,5 +1,6 @@
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::mem::ManuallyDrop;
+use crate::os::unix::io::{AsFd, BorrowedFd, FromRawFd};
 use crate::sys::fd::FileDesc;
 
 pub struct Stdin(());
@@ -14,11 +15,11 @@ impl Stdin {
 
 impl io::Read for Stdin {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read(buf)
+        unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read(buf) }
     }
 
     fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(FileDesc::new(libc::STDIN_FILENO)).read_vectored(bufs)
+        unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDIN_FILENO)).read_vectored(bufs) }
     }
 
     #[inline]
@@ -35,11 +36,13 @@ impl Stdout {
 
 impl io::Write for Stdout {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write(buf)
+        unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write(buf) }
     }
 
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(FileDesc::new(libc::STDOUT_FILENO)).write_vectored(bufs)
+        unsafe {
+            ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDOUT_FILENO)).write_vectored(bufs)
+        }
     }
 
     #[inline]
@@ -60,11 +63,13 @@ impl Stderr {
 
 impl io::Write for Stderr {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write(buf)
+        unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write(buf) }
     }
 
     fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(FileDesc::new(libc::STDERR_FILENO)).write_vectored(bufs)
+        unsafe {
+            ManuallyDrop::new(FileDesc::from_raw_fd(libc::STDERR_FILENO)).write_vectored(bufs)
+        }
     }
 
     #[inline]
@@ -86,3 +91,51 @@ pub const STDIN_BUF_SIZE: usize = crate::sys_common::io::DEFAULT_BUF_SIZE;
 pub fn panic_output() -> Option<impl io::Write> {
     Some(Stderr::new())
 }
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for io::Stdin {
+    #[inline]
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) }
+    }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<'a> AsFd for io::StdinLock<'a> {
+    #[inline]
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        unsafe { BorrowedFd::borrow_raw_fd(libc::STDIN_FILENO) }
+    }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for io::Stdout {
+    #[inline]
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) }
+    }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<'a> AsFd for io::StdoutLock<'a> {
+    #[inline]
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        unsafe { BorrowedFd::borrow_raw_fd(libc::STDOUT_FILENO) }
+    }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl AsFd for io::Stderr {
+    #[inline]
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) }
+    }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl<'a> AsFd for io::StderrLock<'a> {
+    #[inline]
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        unsafe { BorrowedFd::borrow_raw_fd(libc::STDERR_FILENO) }
+    }
+}
diff --git a/library/std/src/sys/wasi/fd.rs b/library/std/src/sys/wasi/fd.rs
index 1f6ea8d6e8d..461afd2316f 100644
--- a/library/std/src/sys/wasi/fd.rs
+++ b/library/std/src/sys/wasi/fd.rs
@@ -6,10 +6,12 @@ use crate::io::{self, IoSlice, IoSliceMut, SeekFrom};
 use crate::mem;
 use crate::net::Shutdown;
 use crate::os::raw::c_int;
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 
 #[derive(Debug)]
 pub struct WasiFd {
-    fd: c_int,
+    fd: OwnedFd,
 }
 
 fn iovec<'a>(a: &'a mut [IoSliceMut<'_>]) -> &'a [wasi::Iovec] {
@@ -27,38 +29,24 @@ fn ciovec<'a>(a: &'a [IoSlice<'_>]) -> &'a [wasi::Ciovec] {
 }
 
 impl WasiFd {
-    pub unsafe fn from_raw(fd: c_int) -> WasiFd {
-        WasiFd { fd }
-    }
-
-    pub fn into_raw(self) -> c_int {
-        let ret = self.fd;
-        mem::forget(self);
-        ret
-    }
-
-    pub fn as_raw(&self) -> c_int {
-        self.fd
-    }
-
     pub fn datasync(&self) -> io::Result<()> {
-        unsafe { wasi::fd_datasync(self.fd as wasi::Fd).map_err(err2io) }
+        unsafe { wasi::fd_datasync(self.as_raw_fd()).map_err(err2io) }
     }
 
     pub fn pread(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
-        unsafe { wasi::fd_pread(self.fd as wasi::Fd, iovec(bufs), offset).map_err(err2io) }
+        unsafe { wasi::fd_pread(self.as_raw_fd(), iovec(bufs), offset).map_err(err2io) }
     }
 
     pub fn pwrite(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
-        unsafe { wasi::fd_pwrite(self.fd as wasi::Fd, ciovec(bufs), offset).map_err(err2io) }
+        unsafe { wasi::fd_pwrite(self.as_raw_fd(), ciovec(bufs), offset).map_err(err2io) }
     }
 
     pub fn read(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        unsafe { wasi::fd_read(self.fd as wasi::Fd, iovec(bufs)).map_err(err2io) }
+        unsafe { wasi::fd_read(self.as_raw_fd(), iovec(bufs)).map_err(err2io) }
     }
 
     pub fn write(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
-        unsafe { wasi::fd_write(self.fd as wasi::Fd, ciovec(bufs)).map_err(err2io) }
+        unsafe { wasi::fd_write(self.as_raw_fd(), ciovec(bufs)).map_err(err2io) }
     }
 
     pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
@@ -67,37 +55,37 @@ impl WasiFd {
             SeekFrom::End(pos) => (wasi::WHENCE_END, pos),
             SeekFrom::Current(pos) => (wasi::WHENCE_CUR, pos),
         };
-        unsafe { wasi::fd_seek(self.fd as wasi::Fd, offset, whence).map_err(err2io) }
+        unsafe { wasi::fd_seek(self.as_raw_fd(), offset, whence).map_err(err2io) }
     }
 
     pub fn tell(&self) -> io::Result<u64> {
-        unsafe { wasi::fd_tell(self.fd as wasi::Fd).map_err(err2io) }
+        unsafe { wasi::fd_tell(self.as_raw_fd()).map_err(err2io) }
     }
 
     // FIXME: __wasi_fd_fdstat_get
 
     pub fn set_flags(&self, flags: wasi::Fdflags) -> io::Result<()> {
-        unsafe { wasi::fd_fdstat_set_flags(self.fd as wasi::Fd, flags).map_err(err2io) }
+        unsafe { wasi::fd_fdstat_set_flags(self.as_raw_fd(), flags).map_err(err2io) }
     }
 
     pub fn set_rights(&self, base: wasi::Rights, inheriting: wasi::Rights) -> io::Result<()> {
-        unsafe { wasi::fd_fdstat_set_rights(self.fd as wasi::Fd, base, inheriting).map_err(err2io) }
+        unsafe { wasi::fd_fdstat_set_rights(self.as_raw_fd(), base, inheriting).map_err(err2io) }
     }
 
     pub fn sync(&self) -> io::Result<()> {
-        unsafe { wasi::fd_sync(self.fd as wasi::Fd).map_err(err2io) }
+        unsafe { wasi::fd_sync(self.as_raw_fd()).map_err(err2io) }
     }
 
     pub fn advise(&self, offset: u64, len: u64, advice: wasi::Advice) -> io::Result<()> {
-        unsafe { wasi::fd_advise(self.fd as wasi::Fd, offset, len, advice).map_err(err2io) }
+        unsafe { wasi::fd_advise(self.as_raw_fd(), offset, len, advice).map_err(err2io) }
     }
 
     pub fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
-        unsafe { wasi::fd_allocate(self.fd as wasi::Fd, offset, len).map_err(err2io) }
+        unsafe { wasi::fd_allocate(self.as_raw_fd(), offset, len).map_err(err2io) }
     }
 
     pub fn create_directory(&self, path: &str) -> io::Result<()> {
-        unsafe { wasi::path_create_directory(self.fd as wasi::Fd, path).map_err(err2io) }
+        unsafe { wasi::path_create_directory(self.as_raw_fd(), path).map_err(err2io) }
     }
 
     pub fn link(
@@ -108,14 +96,8 @@ impl WasiFd {
         new_path: &str,
     ) -> io::Result<()> {
         unsafe {
-            wasi::path_link(
-                self.fd as wasi::Fd,
-                old_flags,
-                old_path,
-                new_fd.fd as wasi::Fd,
-                new_path,
-            )
-            .map_err(err2io)
+            wasi::path_link(self.as_raw_fd(), old_flags, old_path, new_fd.as_raw_fd(), new_path)
+                .map_err(err2io)
         }
     }
 
@@ -130,7 +112,7 @@ impl WasiFd {
     ) -> io::Result<WasiFd> {
         unsafe {
             wasi::path_open(
-                self.fd as wasi::Fd,
+                self.as_raw_fd(),
                 dirflags,
                 path,
                 oflags,
@@ -138,34 +120,32 @@ impl WasiFd {
                 fs_rights_inheriting,
                 fs_flags,
             )
-            .map(|fd| WasiFd::from_raw(fd as c_int))
+            .map(|fd| WasiFd::from_raw_fd(fd))
             .map_err(err2io)
         }
     }
 
     pub fn readdir(&self, buf: &mut [u8], cookie: wasi::Dircookie) -> io::Result<usize> {
         unsafe {
-            wasi::fd_readdir(self.fd as wasi::Fd, buf.as_mut_ptr(), buf.len(), cookie)
-                .map_err(err2io)
+            wasi::fd_readdir(self.as_raw_fd(), buf.as_mut_ptr(), buf.len(), cookie).map_err(err2io)
         }
     }
 
     pub fn readlink(&self, path: &str, buf: &mut [u8]) -> io::Result<usize> {
         unsafe {
-            wasi::path_readlink(self.fd as wasi::Fd, path, buf.as_mut_ptr(), buf.len())
-                .map_err(err2io)
+            wasi::path_readlink(self.as_raw_fd(), path, buf.as_mut_ptr(), buf.len()).map_err(err2io)
         }
     }
 
     pub fn rename(&self, old_path: &str, new_fd: &WasiFd, new_path: &str) -> io::Result<()> {
         unsafe {
-            wasi::path_rename(self.fd as wasi::Fd, old_path, new_fd.fd as wasi::Fd, new_path)
+            wasi::path_rename(self.as_raw_fd(), old_path, new_fd.as_raw_fd(), new_path)
                 .map_err(err2io)
         }
     }
 
     pub fn filestat_get(&self) -> io::Result<wasi::Filestat> {
-        unsafe { wasi::fd_filestat_get(self.fd as wasi::Fd).map_err(err2io) }
+        unsafe { wasi::fd_filestat_get(self.as_raw_fd()).map_err(err2io) }
     }
 
     pub fn filestat_set_times(
@@ -175,12 +155,12 @@ impl WasiFd {
         fstflags: wasi::Fstflags,
     ) -> io::Result<()> {
         unsafe {
-            wasi::fd_filestat_set_times(self.fd as wasi::Fd, atim, mtim, fstflags).map_err(err2io)
+            wasi::fd_filestat_set_times(self.as_raw_fd(), atim, mtim, fstflags).map_err(err2io)
         }
     }
 
     pub fn filestat_set_size(&self, size: u64) -> io::Result<()> {
-        unsafe { wasi::fd_filestat_set_size(self.fd as wasi::Fd, size).map_err(err2io) }
+        unsafe { wasi::fd_filestat_set_size(self.as_raw_fd(), size).map_err(err2io) }
     }
 
     pub fn path_filestat_get(
@@ -188,7 +168,7 @@ impl WasiFd {
         flags: wasi::Lookupflags,
         path: &str,
     ) -> io::Result<wasi::Filestat> {
-        unsafe { wasi::path_filestat_get(self.fd as wasi::Fd, flags, path).map_err(err2io) }
+        unsafe { wasi::path_filestat_get(self.as_raw_fd(), flags, path).map_err(err2io) }
     }
 
     pub fn path_filestat_set_times(
@@ -200,21 +180,21 @@ impl WasiFd {
         fstflags: wasi::Fstflags,
     ) -> io::Result<()> {
         unsafe {
-            wasi::path_filestat_set_times(self.fd as wasi::Fd, flags, path, atim, mtim, fstflags)
+            wasi::path_filestat_set_times(self.as_raw_fd(), flags, path, atim, mtim, fstflags)
                 .map_err(err2io)
         }
     }
 
     pub fn symlink(&self, old_path: &str, new_path: &str) -> io::Result<()> {
-        unsafe { wasi::path_symlink(old_path, self.fd as wasi::Fd, new_path).map_err(err2io) }
+        unsafe { wasi::path_symlink(old_path, self.as_raw_fd(), new_path).map_err(err2io) }
     }
 
     pub fn unlink_file(&self, path: &str) -> io::Result<()> {
-        unsafe { wasi::path_unlink_file(self.fd as wasi::Fd, path).map_err(err2io) }
+        unsafe { wasi::path_unlink_file(self.as_raw_fd(), path).map_err(err2io) }
     }
 
     pub fn remove_directory(&self, path: &str) -> io::Result<()> {
-        unsafe { wasi::path_remove_directory(self.fd as wasi::Fd, path).map_err(err2io) }
+        unsafe { wasi::path_remove_directory(self.as_raw_fd(), path).map_err(err2io) }
     }
 
     pub fn sock_recv(
@@ -222,11 +202,11 @@ impl WasiFd {
         ri_data: &mut [IoSliceMut<'_>],
         ri_flags: wasi::Riflags,
     ) -> io::Result<(usize, wasi::Roflags)> {
-        unsafe { wasi::sock_recv(self.fd as wasi::Fd, iovec(ri_data), ri_flags).map_err(err2io) }
+        unsafe { wasi::sock_recv(self.as_raw_fd(), iovec(ri_data), ri_flags).map_err(err2io) }
     }
 
     pub fn sock_send(&self, si_data: &[IoSlice<'_>], si_flags: wasi::Siflags) -> io::Result<usize> {
-        unsafe { wasi::sock_send(self.fd as wasi::Fd, ciovec(si_data), si_flags).map_err(err2io) }
+        unsafe { wasi::sock_send(self.as_raw_fd(), ciovec(si_data), si_flags).map_err(err2io) }
     }
 
     pub fn sock_shutdown(&self, how: Shutdown) -> io::Result<()> {
@@ -235,14 +215,54 @@ impl WasiFd {
             Shutdown::Write => wasi::SDFLAGS_WR,
             Shutdown::Both => wasi::SDFLAGS_WR | wasi::SDFLAGS_RD,
         };
-        unsafe { wasi::sock_shutdown(self.fd as wasi::Fd, how).map_err(err2io) }
+        unsafe { wasi::sock_shutdown(self.as_raw_fd(), how).map_err(err2io) }
+    }
+}
+
+impl AsInner<OwnedFd> for WasiFd {
+    fn as_inner(&self) -> &OwnedFd {
+        &self.fd
+    }
+}
+
+impl AsInnerMut<OwnedFd> for WasiFd {
+    fn as_inner_mut(&mut self) -> &mut OwnedFd {
+        &mut self.fd
+    }
+}
+
+impl IntoInner<OwnedFd> for WasiFd {
+    fn into_inner(self) -> OwnedFd {
+        self.fd
+    }
+}
+
+impl FromInner<OwnedFd> for WasiFd {
+    fn from_inner(owned_fd: OwnedFd) -> Self {
+        Self { fd: owned_fd }
+    }
+}
+
+impl AsFd for WasiFd {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.fd.as_fd()
+    }
+}
+
+impl AsRawFd for WasiFd {
+    fn as_raw_fd(&self) -> RawFd {
+        self.fd.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for WasiFd {
+    fn into_raw_fd(self) -> RawFd {
+        self.fd.into_raw_fd()
     }
 }
 
-impl Drop for WasiFd {
-    fn drop(&mut self) {
-        // FIXME: can we handle the return code here even though we can't on
-        // unix?
-        let _ = unsafe { wasi::fd_close(self.fd as wasi::Fd) };
+impl FromRawFd for WasiFd {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
     }
 }
diff --git a/library/std/src/sys/wasi/fs.rs b/library/std/src/sys/wasi/fs.rs
index 55c9c652a8b..984dda8dc0b 100644
--- a/library/std/src/sys/wasi/fs.rs
+++ b/library/std/src/sys/wasi/fs.rs
@@ -8,12 +8,13 @@ use crate::iter;
 use crate::mem::{self, ManuallyDrop};
 use crate::os::raw::c_int;
 use crate::os::wasi::ffi::{OsStrExt, OsStringExt};
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::path::{Path, PathBuf};
 use crate::ptr;
 use crate::sync::Arc;
 use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
-use crate::sys_common::FromInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 
 pub use crate::sys_common::fs::{remove_dir_all, try_exists};
 
@@ -442,22 +443,50 @@ impl File {
         unsupported()
     }
 
-    pub fn fd(&self) -> &WasiFd {
+    pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
+        read_link(&self.fd, file)
+    }
+}
+
+impl AsInner<WasiFd> for File {
+    fn as_inner(&self) -> &WasiFd {
         &self.fd
     }
+}
 
-    pub fn into_fd(self) -> WasiFd {
+impl IntoInner<WasiFd> for File {
+    fn into_inner(self) -> WasiFd {
         self.fd
     }
+}
 
-    pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
-        read_link(&self.fd, file)
+impl FromInner<WasiFd> for File {
+    fn from_inner(fd: WasiFd) -> File {
+        File { fd }
+    }
+}
+
+impl AsFd for File {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.fd.as_fd()
+    }
+}
+
+impl AsRawFd for File {
+    fn as_raw_fd(&self) -> RawFd {
+        self.fd.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for File {
+    fn into_raw_fd(self) -> RawFd {
+        self.fd.into_raw_fd()
     }
 }
 
-impl FromInner<c_int> for File {
-    fn from_inner(fd: c_int) -> File {
-        unsafe { File { fd: WasiFd::from_raw(fd) } }
+impl FromRawFd for File {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
     }
 }
 
@@ -474,7 +503,7 @@ impl DirBuilder {
 
 impl fmt::Debug for File {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("File").field("fd", &self.fd.as_raw()).finish()
+        f.debug_struct("File").field("fd", &self.as_raw_fd()).finish()
     }
 }
 
@@ -654,7 +683,7 @@ fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
             let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
 
             return Ok((
-                ManuallyDrop::new(WasiFd::from_raw(fd as c_int)),
+                ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
                 PathBuf::from(OsString::from_vec(relative)),
             ));
         }
diff --git a/library/std/src/sys/wasi/net.rs b/library/std/src/sys/wasi/net.rs
index 50b7352933e..1bd33f1e50e 100644
--- a/library/std/src/sys/wasi/net.rs
+++ b/library/std/src/sys/wasi/net.rs
@@ -6,12 +6,57 @@ use crate::fmt;
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
 use crate::os::raw::c_int;
+use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::sys::unsupported;
-use crate::sys_common::FromInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::time::Duration;
 
+pub struct Socket(WasiFd);
+
 pub struct TcpStream {
-    fd: WasiFd,
+    inner: Socket,
+}
+
+impl AsInner<WasiFd> for Socket {
+    fn as_inner(&self) -> &WasiFd {
+        &self.0
+    }
+}
+
+impl IntoInner<WasiFd> for Socket {
+    fn into_inner(self) -> WasiFd {
+        self.0
+    }
+}
+
+impl FromInner<WasiFd> for Socket {
+    fn from_inner(inner: WasiFd) -> Socket {
+        Socket(inner)
+    }
+}
+
+impl AsFd for Socket {
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
+    }
+}
+
+impl AsRawFd for Socket {
+    fn as_raw_fd(&self) -> RawFd {
+        self.0.as_raw_fd()
+    }
+}
+
+impl IntoRawFd for Socket {
+    fn into_raw_fd(self) -> RawFd {
+        self.0.into_raw_fd()
+    }
+}
+
+impl FromRawFd for Socket {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) }
+    }
 }
 
 impl TcpStream {
@@ -107,29 +152,29 @@ impl TcpStream {
         unsupported()
     }
 
-    pub fn fd(&self) -> &WasiFd {
-        &self.fd
+    pub fn socket(&self) -> &Socket {
+        &self.inner
     }
 
-    pub fn into_fd(self) -> WasiFd {
-        self.fd
+    pub fn into_socket(self) -> Socket {
+        self.inner
     }
 }
 
-impl FromInner<c_int> for TcpStream {
-    fn from_inner(fd: c_int) -> TcpStream {
-        unsafe { TcpStream { fd: WasiFd::from_raw(fd) } }
+impl FromInner<Socket> for TcpStream {
+    fn from_inner(socket: Socket) -> TcpStream {
+        TcpStream { inner: socket }
     }
 }
 
 impl fmt::Debug for TcpStream {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("TcpStream").field("fd", &self.fd.as_raw()).finish()
+        f.debug_struct("TcpStream").field("fd", &self.inner.as_raw_fd()).finish()
     }
 }
 
 pub struct TcpListener {
-    fd: WasiFd,
+    inner: Socket,
 }
 
 impl TcpListener {
@@ -173,29 +218,41 @@ impl TcpListener {
         unsupported()
     }
 
-    pub fn fd(&self) -> &WasiFd {
-        &self.fd
+    pub fn socket(&self) -> &Socket {
+        &self.inner
     }
 
-    pub fn into_fd(self) -> WasiFd {
-        self.fd
+    pub fn into_socket(self) -> Socket {
+        self.inner
     }
 }
 
-impl FromInner<c_int> for TcpListener {
-    fn from_inner(fd: c_int) -> TcpListener {
-        unsafe { TcpListener { fd: WasiFd::from_raw(fd) } }
+impl AsInner<Socket> for TcpListener {
+    fn as_inner(&self) -> &Socket {
+        &self.inner
+    }
+}
+
+impl IntoInner<Socket> for TcpListener {
+    fn into_inner(self) -> Socket {
+        self.inner
+    }
+}
+
+impl FromInner<Socket> for TcpListener {
+    fn from_inner(inner: Socket) -> TcpListener {
+        TcpListener { inner }
     }
 }
 
 impl fmt::Debug for TcpListener {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("TcpListener").field("fd", &self.fd.as_raw()).finish()
+        f.debug_struct("TcpListener").field("fd", &self.inner.as_raw_fd()).finish()
     }
 }
 
 pub struct UdpSocket {
-    fd: WasiFd,
+    inner: Socket,
 }
 
 impl UdpSocket {
@@ -323,24 +380,36 @@ impl UdpSocket {
         unsupported()
     }
 
-    pub fn fd(&self) -> &WasiFd {
-        &self.fd
+    pub fn socket(&self) -> &Socket {
+        &self.inner
+    }
+
+    pub fn into_socket(self) -> Socket {
+        self.inner
     }
+}
+
+impl AsInner<Socket> for UdpSocket {
+    fn as_inner(&self) -> &Socket {
+        &self.inner
+    }
+}
 
-    pub fn into_fd(self) -> WasiFd {
-        self.fd
+impl IntoInner<Socket> for UdpSocket {
+    fn into_inner(self) -> Socket {
+        self.inner
     }
 }
 
-impl FromInner<c_int> for UdpSocket {
-    fn from_inner(fd: c_int) -> UdpSocket {
-        unsafe { UdpSocket { fd: WasiFd::from_raw(fd) } }
+impl FromInner<Socket> for UdpSocket {
+    fn from_inner(inner: Socket) -> UdpSocket {
+        UdpSocket { inner }
     }
 }
 
 impl fmt::Debug for UdpSocket {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.debug_struct("UdpSocket").field("fd", &self.fd.as_raw()).finish()
+        f.debug_struct("UdpSocket").field("fd", &self.inner.as_raw_fd()).finish()
     }
 }
 
diff --git a/library/std/src/sys/wasi/stdio.rs b/library/std/src/sys/wasi/stdio.rs
index 8782f333a1f..2c8f394cd47 100644
--- a/library/std/src/sys/wasi/stdio.rs
+++ b/library/std/src/sys/wasi/stdio.rs
@@ -4,6 +4,7 @@ use super::fd::WasiFd;
 use crate::io::{self, IoSlice, IoSliceMut};
 use crate::mem::ManuallyDrop;
 use crate::os::raw;
+use crate::os::wasi::io::{AsRawFd, FromRawFd};
 
 pub struct Stdin;
 pub struct Stdout;
@@ -13,9 +14,11 @@ impl Stdin {
     pub const fn new() -> Stdin {
         Stdin
     }
+}
 
+impl AsRawFd for Stdin {
     #[inline]
-    pub fn as_raw_fd(&self) -> raw::c_int {
+    fn as_raw_fd(&self) -> raw::c_int {
         0
     }
 }
@@ -26,7 +29,7 @@ impl io::Read for Stdin {
     }
 
     fn read_vectored(&mut self, data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).read(data)
+        ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).read(data)
     }
 
     #[inline]
@@ -39,9 +42,11 @@ impl Stdout {
     pub const fn new() -> Stdout {
         Stdout
     }
+}
 
+impl AsRawFd for Stdout {
     #[inline]
-    pub fn as_raw_fd(&self) -> raw::c_int {
+    fn as_raw_fd(&self) -> raw::c_int {
         1
     }
 }
@@ -52,7 +57,7 @@ impl io::Write for Stdout {
     }
 
     fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data)
+        ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
     }
 
     #[inline]
@@ -68,9 +73,11 @@ impl Stderr {
     pub const fn new() -> Stderr {
         Stderr
     }
+}
 
+impl AsRawFd for Stderr {
     #[inline]
-    pub fn as_raw_fd(&self) -> raw::c_int {
+    fn as_raw_fd(&self) -> raw::c_int {
         2
     }
 }
@@ -81,7 +88,7 @@ impl io::Write for Stderr {
     }
 
     fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
-        ManuallyDrop::new(unsafe { WasiFd::from_raw(self.as_raw_fd()) }).write(data)
+        ManuallyDrop::new(unsafe { WasiFd::from_raw_fd(self.as_raw_fd()) }).write(data)
     }
 
     #[inline]
diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs
index c677adae688..0c1a50e231c 100644
--- a/library/std/src/sys/windows/fs.rs
+++ b/library/std/src/sys/windows/fs.rs
@@ -4,6 +4,7 @@ use crate::ffi::OsString;
 use crate::fmt;
 use crate::io::{self, Error, IoSlice, IoSliceMut, SeekFrom};
 use crate::mem;
+use crate::os::windows::io::{AsHandle, BorrowedHandle};
 use crate::path::{Path, PathBuf};
 use crate::ptr;
 use crate::slice;
@@ -11,7 +12,7 @@ use crate::sync::Arc;
 use crate::sys::handle::Handle;
 use crate::sys::time::SystemTime;
 use crate::sys::{c, cvt};
-use crate::sys_common::FromInner;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 
 use super::to_u16s;
 
@@ -295,12 +296,12 @@ impl File {
         if handle == c::INVALID_HANDLE_VALUE {
             Err(Error::last_os_error())
         } else {
-            Ok(File { handle: Handle::new(handle) })
+            unsafe { Ok(File { handle: Handle::from_raw_handle(handle) }) }
         }
     }
 
     pub fn fsync(&self) -> io::Result<()> {
-        cvt(unsafe { c::FlushFileBuffers(self.handle.raw()) })?;
+        cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
         Ok(())
     }
 
@@ -313,7 +314,7 @@ impl File {
         let size = mem::size_of_val(&info);
         cvt(unsafe {
             c::SetFileInformationByHandle(
-                self.handle.raw(),
+                self.handle.as_raw_handle(),
                 c::FileEndOfFileInfo,
                 &mut info as *mut _ as *mut _,
                 size as c::DWORD,
@@ -326,7 +327,7 @@ impl File {
     pub fn file_attr(&self) -> io::Result<FileAttr> {
         unsafe {
             let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
-            cvt(c::GetFileInformationByHandle(self.handle.raw(), &mut info))?;
+            cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
             let mut reparse_tag = 0;
             if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
                 let mut b = [0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
@@ -449,7 +450,7 @@ impl File {
         };
         let pos = pos as c::LARGE_INTEGER;
         let mut newpos = 0;
-        cvt(unsafe { c::SetFilePointerEx(self.handle.raw(), pos, &mut newpos, whence) })?;
+        cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;
         Ok(newpos as u64)
     }
 
@@ -457,14 +458,6 @@ impl File {
         Ok(File { handle: self.handle.duplicate(0, false, c::DUPLICATE_SAME_ACCESS)? })
     }
 
-    pub fn handle(&self) -> &Handle {
-        &self.handle
-    }
-
-    pub fn into_handle(self) -> Handle {
-        self.handle
-    }
-
     fn reparse_point<'a>(
         &self,
         space: &'a mut [u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE],
@@ -473,7 +466,7 @@ impl File {
             let mut bytes = 0;
             cvt({
                 c::DeviceIoControl(
-                    self.handle.raw(),
+                    self.handle.as_raw_handle(),
                     c::FSCTL_GET_REPARSE_POINT,
                     ptr::null_mut(),
                     0,
@@ -541,7 +534,7 @@ impl File {
         let size = mem::size_of_val(&info);
         cvt(unsafe {
             c::SetFileInformationByHandle(
-                self.handle.raw(),
+                self.handle.as_raw_handle(),
                 c::FileBasicInfo,
                 &mut info as *mut _ as *mut _,
                 size as c::DWORD,
@@ -551,9 +544,45 @@ impl File {
     }
 }
 
-impl FromInner<c::HANDLE> for File {
-    fn from_inner(handle: c::HANDLE) -> File {
-        File { handle: Handle::new(handle) }
+impl AsInner<Handle> for File {
+    fn as_inner(&self) -> &Handle {
+        &self.handle
+    }
+}
+
+impl IntoInner<Handle> for File {
+    fn into_inner(self) -> Handle {
+        self.handle
+    }
+}
+
+impl FromInner<Handle> for File {
+    fn from_inner(handle: Handle) -> File {
+        File { handle: handle }
+    }
+}
+
+impl AsHandle for File {
+    fn as_handle(&self) -> BorrowedHandle<'_> {
+        self.as_inner().as_handle()
+    }
+}
+
+impl AsRawHandle for File {
+    fn as_raw_handle(&self) -> RawHandle {
+        self.as_inner().as_raw_handle()
+    }
+}
+
+impl IntoRawHandle for File {
+    fn into_raw_handle(self) -> RawHandle {
+        self.into_inner().into_raw_handle()
+    }
+}
+
+impl FromRawHandle for File {
+    unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
+        Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) }
     }
 }
 
@@ -561,7 +590,7 @@ impl fmt::Debug for File {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // FIXME(#24570): add more info here (e.g., mode)
         let mut b = f.debug_struct("File");
-        b.field("handle", &self.handle.raw());
+        b.field("handle", &self.handle.as_raw_handle());
         if let Ok(path) = get_path(&self) {
             b.field("path", &path);
         }
@@ -838,7 +867,7 @@ pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
 fn get_path(f: &File) -> io::Result<PathBuf> {
     super::fill_utf16_buf(
         |buf, sz| unsafe {
-            c::GetFinalPathNameByHandleW(f.handle.raw(), buf, sz, c::VOLUME_NAME_DOS)
+            c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
         },
         |buf| PathBuf::from(OsString::from_wide(buf)),
     )
@@ -909,7 +938,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
     opts.write(true);
     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
     let f = File::open(junction, &opts)?;
-    let h = f.handle().raw();
+    let h = f.as_inner().as_raw_handle();
 
     unsafe {
         let mut data = [0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
diff --git a/library/std/src/sys/windows/handle.rs b/library/std/src/sys/windows/handle.rs
index 0d4baa3b340..21d86b00226 100644
--- a/library/std/src/sys/windows/handle.rs
+++ b/library/std/src/sys/windows/handle.rs
@@ -3,76 +3,87 @@
 use crate::cmp;
 use crate::io::{self, ErrorKind, IoSlice, IoSliceMut, Read};
 use crate::mem;
-use crate::ops::Deref;
+use crate::os::windows::io::{
+    AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
+};
 use crate::ptr;
 use crate::sys::c;
 use crate::sys::cvt;
+use crate::sys_common::{AsInner, FromInner, IntoInner};
 
 /// An owned container for `HANDLE` object, closing them on Drop.
 ///
 /// All methods are inherited through a `Deref` impl to `RawHandle`
-pub struct Handle(RawHandle);
-
-/// A wrapper type for `HANDLE` objects to give them proper Send/Sync inference
-/// as well as Rust-y methods.
-///
-/// This does **not** drop the handle when it goes out of scope, use `Handle`
-/// instead for that.
-#[derive(Copy, Clone)]
-pub struct RawHandle(c::HANDLE);
-
-unsafe impl Send for RawHandle {}
-unsafe impl Sync for RawHandle {}
+pub struct Handle(OwnedHandle);
 
 impl Handle {
-    pub fn new(handle: c::HANDLE) -> Handle {
-        Handle(RawHandle::new(handle))
-    }
-
     pub fn new_event(manual: bool, init: bool) -> io::Result<Handle> {
         unsafe {
             let event =
                 c::CreateEventW(ptr::null_mut(), manual as c::BOOL, init as c::BOOL, ptr::null());
-            if event.is_null() { Err(io::Error::last_os_error()) } else { Ok(Handle::new(event)) }
+            if event.is_null() {
+                Err(io::Error::last_os_error())
+            } else {
+                Ok(Handle::from_raw_handle(event))
+            }
         }
     }
+}
 
-    pub fn into_raw(self) -> c::HANDLE {
-        let ret = self.raw();
-        mem::forget(self);
-        ret
+impl AsInner<OwnedHandle> for Handle {
+    fn as_inner(&self) -> &OwnedHandle {
+        &self.0
     }
 }
 
-impl Deref for Handle {
-    type Target = RawHandle;
-    fn deref(&self) -> &RawHandle {
-        &self.0
+impl IntoInner<OwnedHandle> for Handle {
+    fn into_inner(self) -> OwnedHandle {
+        self.0
     }
 }
 
-impl Drop for Handle {
-    fn drop(&mut self) {
-        unsafe {
-            let _ = c::CloseHandle(self.raw());
-        }
+impl FromInner<OwnedHandle> for Handle {
+    fn from_inner(file_desc: OwnedHandle) -> Self {
+        Self(file_desc)
     }
 }
 
-impl RawHandle {
-    pub fn new(handle: c::HANDLE) -> RawHandle {
-        RawHandle(handle)
+impl AsHandle for Handle {
+    fn as_handle(&self) -> BorrowedHandle<'_> {
+        self.0.as_handle()
     }
+}
 
-    pub fn raw(&self) -> c::HANDLE {
-        self.0
+impl AsRawHandle for Handle {
+    fn as_raw_handle(&self) -> RawHandle {
+        self.0.as_raw_handle()
     }
+}
 
+impl IntoRawHandle for Handle {
+    fn into_raw_handle(self) -> RawHandle {
+        self.0.into_raw_handle()
+    }
+}
+
+impl FromRawHandle for Handle {
+    unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
+        Self(FromRawHandle::from_raw_handle(raw_handle))
+    }
+}
+
+impl Handle {
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
         let mut read = 0;
         let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
         let res = cvt(unsafe {
-            c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, ptr::null_mut())
+            c::ReadFile(
+                self.as_raw_handle(),
+                buf.as_mut_ptr() as c::LPVOID,
+                len,
+                &mut read,
+                ptr::null_mut(),
+            )
         });
 
         match res {
@@ -104,7 +115,13 @@ impl RawHandle {
             let mut overlapped: c::OVERLAPPED = mem::zeroed();
             overlapped.Offset = offset as u32;
             overlapped.OffsetHigh = (offset >> 32) as u32;
-            cvt(c::ReadFile(self.0, buf.as_mut_ptr() as c::LPVOID, len, &mut read, &mut overlapped))
+            cvt(c::ReadFile(
+                self.as_raw_handle(),
+                buf.as_mut_ptr() as c::LPVOID,
+                len,
+                &mut read,
+                &mut overlapped,
+            ))
         };
         match res {
             Ok(_) => Ok(read as usize),
@@ -120,7 +137,13 @@ impl RawHandle {
     ) -> io::Result<Option<usize>> {
         let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
         let mut amt = 0;
-        let res = cvt(c::ReadFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, overlapped));
+        let res = cvt(c::ReadFile(
+            self.as_raw_handle(),
+            buf.as_ptr() as c::LPVOID,
+            len,
+            &mut amt,
+            overlapped,
+        ));
         match res {
             Ok(_) => Ok(Some(amt as usize)),
             Err(e) => {
@@ -143,7 +166,8 @@ impl RawHandle {
         unsafe {
             let mut bytes = 0;
             let wait = if wait { c::TRUE } else { c::FALSE };
-            let res = cvt(c::GetOverlappedResult(self.raw(), overlapped, &mut bytes, wait));
+            let res =
+                cvt(c::GetOverlappedResult(self.as_raw_handle(), overlapped, &mut bytes, wait));
             match res {
                 Ok(_) => Ok(bytes as usize),
                 Err(e) => {
@@ -160,14 +184,20 @@ impl RawHandle {
     }
 
     pub fn cancel_io(&self) -> io::Result<()> {
-        unsafe { cvt(c::CancelIo(self.raw())).map(drop) }
+        unsafe { cvt(c::CancelIo(self.as_raw_handle())).map(drop) }
     }
 
     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
         let mut amt = 0;
         let len = cmp::min(buf.len(), <c::DWORD>::MAX as usize) as c::DWORD;
         cvt(unsafe {
-            c::WriteFile(self.0, buf.as_ptr() as c::LPVOID, len, &mut amt, ptr::null_mut())
+            c::WriteFile(
+                self.as_raw_handle(),
+                buf.as_ptr() as c::LPVOID,
+                len,
+                &mut amt,
+                ptr::null_mut(),
+            )
         })?;
         Ok(amt as usize)
     }
@@ -189,7 +219,7 @@ impl RawHandle {
             overlapped.Offset = offset as u32;
             overlapped.OffsetHigh = (offset >> 32) as u32;
             cvt(c::WriteFile(
-                self.0,
+                self.as_raw_handle(),
                 buf.as_ptr() as c::LPVOID,
                 len,
                 &mut written,
@@ -210,7 +240,7 @@ impl RawHandle {
             let cur_proc = c::GetCurrentProcess();
             c::DuplicateHandle(
                 cur_proc,
-                self.0,
+                self.as_raw_handle(),
                 cur_proc,
                 &mut ret,
                 access,
@@ -218,11 +248,11 @@ impl RawHandle {
                 options,
             )
         })?;
-        Ok(Handle::new(ret))
+        unsafe { Ok(Handle::from_raw_handle(ret)) }
     }
 }
 
-impl<'a> Read for &'a RawHandle {
+impl<'a> Read for &'a Handle {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         (**self).read(buf)
     }
diff --git a/library/std/src/sys/windows/net.rs b/library/std/src/sys/windows/net.rs
index 9cea5c5e63a..55aacb38c6f 100644
--- a/library/std/src/sys/windows/net.rs
+++ b/library/std/src/sys/windows/net.rs
@@ -4,6 +4,9 @@ use crate::cmp;
 use crate::io::{self, IoSlice, IoSliceMut, Read};
 use crate::mem;
 use crate::net::{Shutdown, SocketAddr};
+use crate::os::windows::io::{
+    AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
+};
 use crate::ptr;
 use crate::sync::Once;
 use crate::sys;
@@ -24,7 +27,7 @@ pub mod netc {
     pub use crate::sys::c::*;
 }
 
-pub struct Socket(c::SOCKET);
+pub struct Socket(OwnedSocket);
 
 static INIT: Once = Once::new();
 
@@ -109,7 +112,7 @@ impl Socket {
         };
 
         if socket != c::INVALID_SOCKET {
-            Ok(Self(socket))
+            unsafe { Ok(Self::from_raw_socket(socket)) }
         } else {
             let error = unsafe { c::WSAGetLastError() };
 
@@ -124,9 +127,11 @@ impl Socket {
                 return Err(last_error());
             }
 
-            let socket = Self(socket);
-            socket.set_no_inherit()?;
-            Ok(socket)
+            unsafe {
+                let socket = Self::from_raw_socket(socket);
+                socket.set_no_inherit()?;
+                Ok(socket)
+            }
         }
     }
 
@@ -134,7 +139,7 @@ impl Socket {
         self.set_nonblocking(true)?;
         let result = {
             let (addrp, len) = addr.into_inner();
-            let result = unsafe { c::connect(self.0, addrp, len) };
+            let result = unsafe { c::connect(self.as_raw_socket(), addrp, len) };
             cvt(result).map(drop)
         };
         self.set_nonblocking(false)?;
@@ -160,7 +165,7 @@ impl Socket {
                 let fds = {
                     let mut fds = unsafe { mem::zeroed::<c::fd_set>() };
                     fds.fd_count = 1;
-                    fds.fd_array[0] = self.0;
+                    fds.fd_array[0] = self.as_raw_socket();
                     fds
                 };
 
@@ -194,17 +199,19 @@ impl Socket {
     }
 
     pub fn accept(&self, storage: *mut c::SOCKADDR, len: *mut c_int) -> io::Result<Socket> {
-        let socket = unsafe { c::accept(self.0, storage, len) };
+        let socket = unsafe { c::accept(self.as_raw_socket(), storage, len) };
 
         match socket {
             c::INVALID_SOCKET => Err(last_error()),
-            _ => Ok(Self(socket)),
+            _ => unsafe { Ok(Self::from_raw_socket(socket)) },
         }
     }
 
     pub fn duplicate(&self) -> io::Result<Socket> {
         let mut info = unsafe { mem::zeroed::<c::WSAPROTOCOL_INFO>() };
-        let result = unsafe { c::WSADuplicateSocketW(self.0, c::GetCurrentProcessId(), &mut info) };
+        let result = unsafe {
+            c::WSADuplicateSocketW(self.as_raw_socket(), c::GetCurrentProcessId(), &mut info)
+        };
         cvt(result)?;
         let socket = unsafe {
             c::WSASocketW(
@@ -218,7 +225,7 @@ impl Socket {
         };
 
         if socket != c::INVALID_SOCKET {
-            Ok(Self(socket))
+            unsafe { Ok(Self::from_inner(OwnedSocket::from_raw_socket(socket))) }
         } else {
             let error = unsafe { c::WSAGetLastError() };
 
@@ -241,9 +248,11 @@ impl Socket {
                 return Err(last_error());
             }
 
-            let socket = Self(socket);
-            socket.set_no_inherit()?;
-            Ok(socket)
+            unsafe {
+                let socket = Self::from_inner(OwnedSocket::from_raw_socket(socket));
+                socket.set_no_inherit()?;
+                Ok(socket)
+            }
         }
     }
 
@@ -251,7 +260,8 @@ impl Socket {
         // On unix when a socket is shut down all further reads return 0, so we
         // do the same on windows to map a shut down socket to returning EOF.
         let length = cmp::min(buf.len(), i32::MAX as usize) as i32;
-        let result = unsafe { c::recv(self.0, buf.as_mut_ptr() as *mut _, length, flags) };
+        let result =
+            unsafe { c::recv(self.as_raw_socket(), buf.as_mut_ptr() as *mut _, length, flags) };
 
         match result {
             c::SOCKET_ERROR => {
@@ -279,7 +289,7 @@ impl Socket {
         let mut flags = 0;
         let result = unsafe {
             c::WSARecv(
-                self.0,
+                self.as_raw_socket(),
                 bufs.as_mut_ptr() as *mut c::WSABUF,
                 length,
                 &mut nread,
@@ -325,7 +335,7 @@ impl Socket {
         // do the same on windows to map a shut down socket to returning EOF.
         let result = unsafe {
             c::recvfrom(
-                self.0,
+                self.as_raw_socket(),
                 buf.as_mut_ptr() as *mut _,
                 length,
                 flags,
@@ -361,7 +371,7 @@ impl Socket {
         let mut nwritten = 0;
         let result = unsafe {
             c::WSASend(
-                self.0,
+                self.as_raw_socket(),
                 bufs.as_ptr() as *const c::WSABUF as *mut _,
                 length,
                 &mut nwritten,
@@ -408,8 +418,10 @@ impl Socket {
 
     #[cfg(not(target_vendor = "uwp"))]
     fn set_no_inherit(&self) -> io::Result<()> {
-        sys::cvt(unsafe { c::SetHandleInformation(self.0 as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0) })
-            .map(drop)
+        sys::cvt(unsafe {
+            c::SetHandleInformation(self.as_raw_socket() as c::HANDLE, c::HANDLE_FLAG_INHERIT, 0)
+        })
+        .map(drop)
     }
 
     #[cfg(target_vendor = "uwp")]
@@ -423,13 +435,14 @@ impl Socket {
             Shutdown::Read => c::SD_RECEIVE,
             Shutdown::Both => c::SD_BOTH,
         };
-        let result = unsafe { c::shutdown(self.0, how) };
+        let result = unsafe { c::shutdown(self.as_raw_socket(), how) };
         cvt(result).map(drop)
     }
 
     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
         let mut nonblocking = nonblocking as c_ulong;
-        let result = unsafe { c::ioctlsocket(self.0, c::FIONBIO as c_int, &mut nonblocking) };
+        let result =
+            unsafe { c::ioctlsocket(self.as_raw_socket(), c::FIONBIO as c_int, &mut nonblocking) };
         cvt(result).map(drop)
     }
 
@@ -446,6 +459,11 @@ impl Socket {
         let raw: c_int = net::getsockopt(self, c::SOL_SOCKET, c::SO_ERROR)?;
         if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) }
     }
+
+    // This is used by sys_common code to abstract over Windows and Unix.
+    pub fn as_raw(&self) -> RawSocket {
+        self.as_inner().as_raw_socket()
+    }
 }
 
 #[unstable(reason = "not public", issue = "none", feature = "fd_read")]
@@ -455,28 +473,44 @@ impl<'a> Read for &'a Socket {
     }
 }
 
-impl Drop for Socket {
-    fn drop(&mut self) {
-        let _ = unsafe { c::closesocket(self.0) };
+impl AsInner<OwnedSocket> for Socket {
+    fn as_inner(&self) -> &OwnedSocket {
+        &self.0
     }
 }
 
-impl AsInner<c::SOCKET> for Socket {
-    fn as_inner(&self) -> &c::SOCKET {
-        &self.0
+impl FromInner<OwnedSocket> for Socket {
+    fn from_inner(sock: OwnedSocket) -> Socket {
+        Socket(sock)
     }
 }
 
-impl FromInner<c::SOCKET> for Socket {
-    fn from_inner(sock: c::SOCKET) -> Socket {
-        Socket(sock)
+impl IntoInner<OwnedSocket> for Socket {
+    fn into_inner(self) -> OwnedSocket {
+        self.0
+    }
+}
+
+impl AsSocket for Socket {
+    fn as_socket(&self) -> BorrowedSocket<'_> {
+        self.0.as_socket()
+    }
+}
+
+impl AsRawSocket for Socket {
+    fn as_raw_socket(&self) -> RawSocket {
+        self.0.as_raw_socket()
+    }
+}
+
+impl IntoRawSocket for Socket {
+    fn into_raw_socket(self) -> RawSocket {
+        self.0.into_raw_socket()
     }
 }
 
-impl IntoInner<c::SOCKET> for Socket {
-    fn into_inner(self) -> c::SOCKET {
-        let ret = self.0;
-        mem::forget(self);
-        ret
+impl FromRawSocket for Socket {
+    unsafe fn from_raw_socket(raw_socket: RawSocket) -> Self {
+        Self(FromRawSocket::from_raw_socket(raw_socket))
     }
 }
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index 8db97ba50a8..883690c4831 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -288,7 +288,7 @@ fn home_dir_crt() -> Option<PathBuf> {
         if c::OpenProcessToken(me, c::TOKEN_READ, &mut token) == 0 {
             return None;
         }
-        let _handle = Handle::new(token);
+        let _handle = Handle::from_raw_handle(token);
         super::fill_utf16_buf(
             |buf, mut sz| {
                 match c::GetUserProfileDirectoryW(token, buf, &mut sz) {
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
index 104a8db4659..63d3d6c5ed4 100644
--- a/library/std/src/sys/windows/pipe.rs
+++ b/library/std/src/sys/windows/pipe.rs
@@ -12,6 +12,7 @@ use crate::sys::c;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::handle::Handle;
 use crate::sys::hashmap_random_keys;
+use crate::sys_common::IntoInner;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Anonymous pipes
@@ -21,6 +22,12 @@ pub struct AnonPipe {
     inner: Handle,
 }
 
+impl IntoInner<Handle> for AnonPipe {
+    fn into_inner(self) -> Handle {
+        self.inner
+    }
+}
+
 pub struct Pipes {
     pub ours: AnonPipe,
     pub theirs: AnonPipe,
@@ -123,7 +130,7 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
                 }
                 return Err(err);
             }
-            ours = Handle::new(handle);
+            ours = Handle::from_raw_handle(handle);
             break;
         }
 
@@ -146,11 +153,11 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
         };
         opts.security_attributes(&mut sa);
         let theirs = File::open(Path::new(&name), &opts)?;
-        let theirs = AnonPipe { inner: theirs.into_handle() };
+        let theirs = AnonPipe { inner: theirs.into_inner() };
 
         Ok(Pipes {
             ours: AnonPipe { inner: ours },
-            theirs: AnonPipe { inner: theirs.into_handle() },
+            theirs: AnonPipe { inner: theirs.into_inner() },
         })
     }
 }
@@ -207,7 +214,7 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) ->
 
     let mut p1 = AsyncPipe::new(p1, v1)?;
     let mut p2 = AsyncPipe::new(p2, v2)?;
-    let objs = [p1.event.raw(), p2.event.raw()];
+    let objs = [p1.event.as_raw_handle(), p2.event.as_raw_handle()];
 
     // In a loop we wait for either pipe's scheduled read operation to complete.
     // If the operation completes with 0 bytes, that means EOF was reached, in
@@ -262,7 +269,7 @@ impl<'a> AsyncPipe<'a> {
         // I/O operation is successfully scheduled (what we want).
         let event = Handle::new_event(true, true)?;
         let mut overlapped: Box<c::OVERLAPPED> = unsafe { Box::new(mem::zeroed()) };
-        overlapped.hEvent = event.raw();
+        overlapped.hEvent = event.as_raw_handle();
         Ok(AsyncPipe { pipe, overlapped, event, dst, state: State::NotReading })
     }
 
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index ae193b82e91..5cfde16faf0 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -15,6 +15,7 @@ use crate::io::{self, Error, ErrorKind};
 use crate::mem;
 use crate::num::NonZeroI32;
 use crate::os::windows::ffi::OsStrExt;
+use crate::os::windows::io::{AsRawHandle, FromRawHandle};
 use crate::path::Path;
 use crate::ptr;
 use crate::sys::c;
@@ -26,7 +27,7 @@ use crate::sys::pipe::{self, AnonPipe};
 use crate::sys::stdio;
 use crate::sys_common::mutex::StaticMutex;
 use crate::sys_common::process::{CommandEnv, CommandEnvs};
-use crate::sys_common::AsInner;
+use crate::sys_common::{AsInner, IntoInner};
 
 use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
 
@@ -316,9 +317,9 @@ impl Command {
         let stdin = stdin.to_handle(c::STD_INPUT_HANDLE, &mut pipes.stdin)?;
         let stdout = stdout.to_handle(c::STD_OUTPUT_HANDLE, &mut pipes.stdout)?;
         let stderr = stderr.to_handle(c::STD_ERROR_HANDLE, &mut pipes.stderr)?;
-        si.hStdInput = stdin.raw();
-        si.hStdOutput = stdout.raw();
-        si.hStdError = stderr.raw();
+        si.hStdInput = stdin.as_raw_handle();
+        si.hStdOutput = stdout.as_raw_handle();
+        si.hStdError = stderr.as_raw_handle();
 
         unsafe {
             cvt(c::CreateProcessW(
@@ -338,9 +339,11 @@ impl Command {
         // We close the thread handle because we don't care about keeping
         // the thread id valid, and we aren't keeping the thread handle
         // around to be able to close it later.
-        drop(Handle::new(pi.hThread));
+        unsafe {
+            drop(Handle::from_raw_handle(pi.hThread));
 
-        Ok((Process { handle: Handle::new(pi.hProcess) }, pipes))
+            Ok((Process { handle: Handle::from_raw_handle(pi.hProcess) }, pipes))
+        }
     }
 }
 
@@ -365,13 +368,13 @@ impl Stdio {
             // should still be unavailable so propagate the
             // INVALID_HANDLE_VALUE.
             Stdio::Inherit => match stdio::get_handle(stdio_id) {
-                Ok(io) => {
-                    let io = Handle::new(io);
+                Ok(io) => unsafe {
+                    let io = Handle::from_raw_handle(io);
                     let ret = io.duplicate(0, true, c::DUPLICATE_SAME_ACCESS);
-                    io.into_raw();
+                    io.into_inner();
                     ret
-                }
-                Err(..) => Ok(Handle::new(c::INVALID_HANDLE_VALUE)),
+                },
+                Err(..) => unsafe { Ok(Handle::from_raw_handle(c::INVALID_HANDLE_VALUE)) },
             },
 
             Stdio::MakePipe => {
@@ -397,7 +400,7 @@ impl Stdio {
                 opts.read(stdio_id == c::STD_INPUT_HANDLE);
                 opts.write(stdio_id != c::STD_INPUT_HANDLE);
                 opts.security_attributes(&mut sa);
-                File::open(Path::new("NUL"), &opts).map(|file| file.into_handle())
+                File::open(Path::new("NUL"), &opts).map(|file| file.into_inner())
             }
         }
     }
@@ -411,7 +414,7 @@ impl From<AnonPipe> for Stdio {
 
 impl From<File> for Stdio {
     fn from(file: File) -> Stdio {
-        Stdio::Handle(file.into_handle())
+        Stdio::Handle(file.into_inner())
     }
 }
 
@@ -430,29 +433,29 @@ pub struct Process {
 
 impl Process {
     pub fn kill(&mut self) -> io::Result<()> {
-        cvt(unsafe { c::TerminateProcess(self.handle.raw(), 1) })?;
+        cvt(unsafe { c::TerminateProcess(self.handle.as_raw_handle(), 1) })?;
         Ok(())
     }
 
     pub fn id(&self) -> u32 {
-        unsafe { c::GetProcessId(self.handle.raw()) as u32 }
+        unsafe { c::GetProcessId(self.handle.as_raw_handle()) as u32 }
     }
 
     pub fn wait(&mut self) -> io::Result<ExitStatus> {
         unsafe {
-            let res = c::WaitForSingleObject(self.handle.raw(), c::INFINITE);
+            let res = c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE);
             if res != c::WAIT_OBJECT_0 {
                 return Err(Error::last_os_error());
             }
             let mut status = 0;
-            cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?;
+            cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?;
             Ok(ExitStatus(status))
         }
     }
 
     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
         unsafe {
-            match c::WaitForSingleObject(self.handle.raw(), 0) {
+            match c::WaitForSingleObject(self.handle.as_raw_handle(), 0) {
                 c::WAIT_OBJECT_0 => {}
                 c::WAIT_TIMEOUT => {
                     return Ok(None);
@@ -460,7 +463,7 @@ impl Process {
                 _ => return Err(io::Error::last_os_error()),
             }
             let mut status = 0;
-            cvt(c::GetExitCodeProcess(self.handle.raw(), &mut status))?;
+            cvt(c::GetExitCodeProcess(self.handle.as_raw_handle(), &mut status))?;
             Ok(Some(ExitStatus(status)))
         }
     }
diff --git a/library/std/src/sys/windows/stdio.rs b/library/std/src/sys/windows/stdio.rs
index 2973951fe90..1cf0e9f0cf1 100644
--- a/library/std/src/sys/windows/stdio.rs
+++ b/library/std/src/sys/windows/stdio.rs
@@ -3,6 +3,7 @@
 use crate::char::decode_utf16;
 use crate::cmp;
 use crate::io;
+use crate::os::windows::io::{FromRawHandle, IntoRawHandle};
 use crate::ptr;
 use crate::str;
 use crate::sys::c;
@@ -53,10 +54,12 @@ fn is_console(handle: c::HANDLE) -> bool {
 fn write(handle_id: c::DWORD, data: &[u8]) -> io::Result<usize> {
     let handle = get_handle(handle_id)?;
     if !is_console(handle) {
-        let handle = Handle::new(handle);
-        let ret = handle.write(data);
-        handle.into_raw(); // Don't close the handle
-        return ret;
+        unsafe {
+            let handle = Handle::from_raw_handle(handle);
+            let ret = handle.write(data);
+            handle.into_raw_handle(); // Don't close the handle
+            return ret;
+        }
     }
 
     // As the console is meant for presenting text, we assume bytes of `data` come from a string
@@ -140,10 +143,12 @@ impl io::Read for Stdin {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
         let handle = get_handle(c::STD_INPUT_HANDLE)?;
         if !is_console(handle) {
-            let handle = Handle::new(handle);
-            let ret = handle.read(buf);
-            handle.into_raw(); // Don't close the handle
-            return ret;
+            unsafe {
+                let handle = Handle::from_raw_handle(handle);
+                let ret = handle.read(buf);
+                handle.into_raw_handle(); // Don't close the handle
+                return ret;
+            }
         }
 
         if buf.len() == 0 {
diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs
index ef7a9733fd8..a5293133b3a 100644
--- a/library/std/src/sys/windows/thread.rs
+++ b/library/std/src/sys/windows/thread.rs
@@ -1,6 +1,7 @@
 use crate::ffi::CStr;
 use crate::io;
 use crate::num::NonZeroUsize;
+use crate::os::windows::io::{AsRawHandle, FromRawHandle};
 use crate::ptr;
 use crate::sys::c;
 use crate::sys::handle::Handle;
@@ -45,7 +46,7 @@ impl Thread {
             drop(Box::from_raw(p));
             Err(io::Error::last_os_error())
         } else {
-            Ok(Thread { handle: Handle::new(ret) })
+            Ok(Thread { handle: Handle::from_raw_handle(ret) })
         };
 
         extern "system" fn thread_start(main: *mut c_void) -> c::DWORD {
@@ -71,7 +72,7 @@ impl Thread {
     }
 
     pub fn join(self) {
-        let rc = unsafe { c::WaitForSingleObject(self.handle.raw(), c::INFINITE) };
+        let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
         if rc == c::WAIT_FAILED {
             panic!("failed to join on thread: {}", io::Error::last_os_error());
         }