about summary refs log tree commit diff
path: root/library/std/src/sys/fs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/fs')
-rw-r--r--library/std/src/sys/fs/common.rs60
-rw-r--r--library/std/src/sys/fs/hermit.rs602
-rw-r--r--library/std/src/sys/fs/mod.rs28
-rw-r--r--library/std/src/sys/fs/solid.rs611
-rw-r--r--library/std/src/sys/fs/uefi.rs348
-rw-r--r--library/std/src/sys/fs/unix.rs2319
-rw-r--r--library/std/src/sys/fs/unix/tests.rs71
-rw-r--r--library/std/src/sys/fs/unsupported.rs348
-rw-r--r--library/std/src/sys/fs/wasi.rs870
-rw-r--r--library/std/src/sys/fs/windows.rs1662
-rw-r--r--library/std/src/sys/fs/windows/remove_dir_all.rs205
11 files changed, 7124 insertions, 0 deletions
diff --git a/library/std/src/sys/fs/common.rs b/library/std/src/sys/fs/common.rs
new file mode 100644
index 00000000000..bfd684d295b
--- /dev/null
+++ b/library/std/src/sys/fs/common.rs
@@ -0,0 +1,60 @@
+#![allow(dead_code)] // not used on all platforms
+
+use crate::fs;
+use crate::io::{self, Error, ErrorKind};
+use crate::path::Path;
+use crate::sys_common::ignore_notfound;
+
+pub(crate) const NOT_FILE_ERROR: Error = io::const_error!(
+    ErrorKind::InvalidInput,
+    "the source path is neither a regular file nor a symlink to a regular file",
+);
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    let mut reader = fs::File::open(from)?;
+    let metadata = reader.metadata()?;
+
+    if !metadata.is_file() {
+        return Err(NOT_FILE_ERROR);
+    }
+
+    let mut writer = fs::File::create(to)?;
+    let perm = metadata.permissions();
+
+    let ret = io::copy(&mut reader, &mut writer)?;
+    writer.set_permissions(perm)?;
+    Ok(ret)
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    let filetype = fs::symlink_metadata(path)?.file_type();
+    if filetype.is_symlink() { fs::remove_file(path) } else { remove_dir_all_recursive(path) }
+}
+
+fn remove_dir_all_recursive(path: &Path) -> io::Result<()> {
+    for child in fs::read_dir(path)? {
+        let result: io::Result<()> = try {
+            let child = child?;
+            if child.file_type()?.is_dir() {
+                remove_dir_all_recursive(&child.path())?;
+            } else {
+                fs::remove_file(&child.path())?;
+            }
+        };
+        // ignore internal NotFound errors to prevent race conditions
+        if let Err(err) = &result
+            && err.kind() != io::ErrorKind::NotFound
+        {
+            return result;
+        }
+    }
+    ignore_notfound(fs::remove_dir(path))
+}
+
+pub fn exists(path: &Path) -> io::Result<bool> {
+    match fs::metadata(path) {
+        Ok(_) => Ok(true),
+        Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
+        Err(error) => Err(error),
+    }
+}
diff --git a/library/std/src/sys/fs/hermit.rs b/library/std/src/sys/fs/hermit.rs
new file mode 100644
index 00000000000..e9339ff261c
--- /dev/null
+++ b/library/std/src/sys/fs/hermit.rs
@@ -0,0 +1,602 @@
+use crate::ffi::{CStr, OsStr, OsString, c_char};
+use crate::io::{self, BorrowedCursor, Error, ErrorKind, IoSlice, IoSliceMut, SeekFrom};
+use crate::os::hermit::ffi::OsStringExt;
+use crate::os::hermit::hermit_abi::{
+    self, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT, O_DIRECTORY, O_EXCL, O_RDONLY,
+    O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG, dirent64, stat as stat_struct,
+};
+use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
+use crate::path::{Path, PathBuf};
+use crate::sync::Arc;
+use crate::sys::common::small_c_string::run_path_with_cstr;
+pub use crate::sys::fs::common::{copy, exists};
+use crate::sys::pal::fd::FileDesc;
+use crate::sys::time::SystemTime;
+use crate::sys::{cvt, unsupported};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
+use crate::{fmt, mem};
+
+#[derive(Debug)]
+pub struct File(FileDesc);
+#[derive(Clone)]
+pub struct FileAttr {
+    stat_val: stat_struct,
+}
+
+impl FileAttr {
+    fn from_stat(stat_val: stat_struct) -> Self {
+        Self { stat_val }
+    }
+}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+    root: PathBuf,
+    dir: Vec<u8>,
+}
+
+impl InnerReadDir {
+    pub fn new(root: PathBuf, dir: Vec<u8>) -> Self {
+        Self { root, dir }
+    }
+}
+
+pub struct ReadDir {
+    inner: Arc<InnerReadDir>,
+    pos: usize,
+}
+
+impl ReadDir {
+    fn new(inner: InnerReadDir) -> Self {
+        Self { inner: Arc::new(inner), pos: 0 }
+    }
+}
+
+pub struct DirEntry {
+    /// path to the entry
+    root: PathBuf,
+    /// 64-bit inode number
+    ino: u64,
+    /// File type
+    type_: u8,
+    /// name of the entry
+    name: OsString,
+}
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+    // generic
+    read: bool,
+    write: bool,
+    append: bool,
+    truncate: bool,
+    create: bool,
+    create_new: bool,
+    // system-specific
+    mode: i32,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions {
+    mode: u32,
+}
+
+#[derive(Copy, Clone, Eq, Debug)]
+pub struct FileType {
+    mode: u8,
+}
+
+impl PartialEq for FileType {
+    fn eq(&self, other: &Self) -> bool {
+        self.mode == other.mode
+    }
+}
+
+impl core::hash::Hash for FileType {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        self.mode.hash(state);
+    }
+}
+
+#[derive(Debug)]
+pub struct DirBuilder {
+    mode: u32,
+}
+
+impl FileAttr {
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::new(self.stat_val.st_mtim.tv_sec, self.stat_val.st_mtim.tv_nsec))
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::new(self.stat_val.st_atim.tv_sec, self.stat_val.st_atim.tv_nsec))
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::new(self.stat_val.st_ctim.tv_sec, self.stat_val.st_ctim.tv_nsec))
+    }
+
+    pub fn size(&self) -> u64 {
+        self.stat_val.st_size as u64
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        FilePermissions { mode: self.stat_val.st_mode }
+    }
+
+    pub fn file_type(&self) -> FileType {
+        let masked_mode = self.stat_val.st_mode & S_IFMT;
+        let mode = match masked_mode {
+            S_IFDIR => DT_DIR,
+            S_IFLNK => DT_LNK,
+            S_IFREG => DT_REG,
+            _ => DT_UNKNOWN,
+        };
+        FileType { mode }
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        // check if any class (owner, group, others) has write permission
+        self.mode & 0o222 == 0
+    }
+
+    pub fn set_readonly(&mut self, _readonly: bool) {
+        unimplemented!()
+    }
+
+    #[allow(dead_code)]
+    pub fn mode(&self) -> u32 {
+        self.mode as u32
+    }
+}
+
+impl FileTimes {
+    pub fn set_accessed(&mut self, _t: SystemTime) {}
+    pub fn set_modified(&mut self, _t: SystemTime) {}
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        self.mode == DT_DIR
+    }
+    pub fn is_file(&self) -> bool {
+        self.mode == DT_REG
+    }
+    pub fn is_symlink(&self) -> bool {
+        self.mode == DT_LNK
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
+        // Thus the result will be e.g. 'ReadDir("/home")'
+        fmt::Debug::fmt(&*self.inner.root, f)
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        let mut counter: usize = 0;
+        let mut offset: usize = 0;
+
+        // loop over all directory entries and search the entry for the current position
+        loop {
+            // leave function, if the loop reaches the of the buffer (with all entries)
+            if offset >= self.inner.dir.len() {
+                return None;
+            }
+
+            let dir = unsafe { &*(self.inner.dir.as_ptr().add(offset) as *const dirent64) };
+
+            if counter == self.pos {
+                self.pos += 1;
+
+                // After dirent64, the file name is stored. d_reclen represents the length of the dirent64
+                // plus the length of the file name. Consequently, file name has a size of d_reclen minus
+                // the size of dirent64. The file name is always a C string and terminated by `\0`.
+                // Consequently, we are able to ignore the last byte.
+                let name_bytes =
+                    unsafe { CStr::from_ptr(&dir.d_name as *const _ as *const c_char).to_bytes() };
+                let entry = DirEntry {
+                    root: self.inner.root.clone(),
+                    ino: dir.d_ino,
+                    type_: dir.d_type,
+                    name: OsString::from_vec(name_bytes.to_vec()),
+                };
+
+                return Some(Ok(entry));
+            }
+
+            counter += 1;
+
+            // move to the next dirent64, which is directly stored after the previous one
+            offset = offset + usize::from(dir.d_reclen);
+        }
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        self.root.join(self.file_name_os_str())
+    }
+
+    pub fn file_name(&self) -> OsString {
+        self.file_name_os_str().to_os_string()
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        let mut path = self.path();
+        path.set_file_name(self.file_name_os_str());
+        lstat(&path)
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        Ok(FileType { mode: self.type_ })
+    }
+
+    #[allow(dead_code)]
+    pub fn ino(&self) -> u64 {
+        self.ino
+    }
+
+    pub fn file_name_os_str(&self) -> &OsStr {
+        self.name.as_os_str()
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions {
+            // generic
+            read: false,
+            write: false,
+            append: false,
+            truncate: false,
+            create: false,
+            create_new: false,
+            // system-specific
+            mode: 0o777,
+        }
+    }
+
+    pub fn read(&mut self, read: bool) {
+        self.read = read;
+    }
+    pub fn write(&mut self, write: bool) {
+        self.write = write;
+    }
+    pub fn append(&mut self, append: bool) {
+        self.append = append;
+    }
+    pub fn truncate(&mut self, truncate: bool) {
+        self.truncate = truncate;
+    }
+    pub fn create(&mut self, create: bool) {
+        self.create = create;
+    }
+    pub fn create_new(&mut self, create_new: bool) {
+        self.create_new = create_new;
+    }
+
+    fn get_access_mode(&self) -> io::Result<i32> {
+        match (self.read, self.write, self.append) {
+            (true, false, false) => Ok(O_RDONLY),
+            (false, true, false) => Ok(O_WRONLY),
+            (true, true, false) => Ok(O_RDWR),
+            (false, _, true) => Ok(O_WRONLY | O_APPEND),
+            (true, _, true) => Ok(O_RDWR | O_APPEND),
+            (false, false, false) => {
+                Err(io::const_error!(ErrorKind::InvalidInput, "invalid access mode"))
+            }
+        }
+    }
+
+    fn get_creation_mode(&self) -> io::Result<i32> {
+        match (self.write, self.append) {
+            (true, false) => {}
+            (false, false) => {
+                if self.truncate || self.create || self.create_new {
+                    return Err(io::const_error!(ErrorKind::InvalidInput, "invalid creation mode"));
+                }
+            }
+            (_, true) => {
+                if self.truncate && !self.create_new {
+                    return Err(io::const_error!(ErrorKind::InvalidInput, "invalid creation mode"));
+                }
+            }
+        }
+
+        Ok(match (self.create, self.truncate, self.create_new) {
+            (false, false, false) => 0,
+            (true, false, false) => O_CREAT,
+            (false, true, false) => O_TRUNC,
+            (true, true, false) => O_CREAT | O_TRUNC,
+            (_, _, true) => O_CREAT | O_EXCL,
+        })
+    }
+}
+
+impl File {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        run_path_with_cstr(path, &|path| File::open_c(&path, opts))
+    }
+
+    pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
+        let mut flags = opts.get_access_mode()?;
+        flags = flags | opts.get_creation_mode()?;
+
+        let mode;
+        if flags & O_CREAT == O_CREAT {
+            mode = opts.mode;
+        } else {
+            mode = 0;
+        }
+
+        let fd = unsafe { cvt(hermit_abi::open(path.as_ptr(), flags, mode))? };
+        Ok(File(unsafe { FileDesc::from_raw_fd(fd as i32) }))
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        let mut stat_val: stat_struct = unsafe { mem::zeroed() };
+        self.0.fstat(&mut stat_val)?;
+        Ok(FileAttr::from_stat(stat_val))
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        Err(Error::from_raw_os_error(22))
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        self.fsync()
+    }
+
+    pub fn lock(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn lock_shared(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn try_lock(&self) -> io::Result<bool> {
+        unsupported()
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        unsupported()
+    }
+
+    pub fn unlock(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn truncate(&self, _size: u64) -> io::Result<()> {
+        Err(Error::from_raw_os_error(22))
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.0.read(buf)
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
+    pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        crate::io::default_read_buf(|buf| self.read(buf), cursor)
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        self.0.write(buf)
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
+    #[inline]
+    pub fn flush(&self) -> io::Result<()> {
+        Ok(())
+    }
+
+    pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+        Err(Error::from_raw_os_error(22))
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        self.seek(SeekFrom::Current(0))
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        Err(Error::from_raw_os_error(22))
+    }
+
+    pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+        Err(Error::from_raw_os_error(22))
+    }
+
+    pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
+        Err(Error::from_raw_os_error(22))
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder { mode: 0o777 }
+    }
+
+    pub fn mkdir(&self, path: &Path) -> io::Result<()> {
+        run_path_with_cstr(path, &|path| {
+            cvt(unsafe { hermit_abi::mkdir(path.as_ptr().cast(), self.mode.into()) }).map(|_| ())
+        })
+    }
+
+    #[allow(dead_code)]
+    pub fn set_mode(&mut self, mode: u32) {
+        self.mode = mode;
+    }
+}
+
+impl AsInner<FileDesc> for File {
+    #[inline]
+    fn as_inner(&self) -> &FileDesc {
+        &self.0
+    }
+}
+
+impl AsInnerMut<FileDesc> for File {
+    #[inline]
+    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 {
+    #[inline]
+    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 {
+        let file_desc = unsafe { FileDesc::from_raw_fd(raw_fd) };
+        Self(file_desc)
+    }
+}
+
+pub fn readdir(path: &Path) -> io::Result<ReadDir> {
+    let fd_raw = run_path_with_cstr(path, &|path| {
+        cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) })
+    })?;
+    let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) };
+    let root = path.to_path_buf();
+
+    // read all director entries
+    let mut vec: Vec<u8> = Vec::new();
+    let mut sz = 512;
+    loop {
+        // reserve memory to receive all directory entries
+        vec.resize(sz, 0);
+
+        let readlen = unsafe {
+            hermit_abi::getdents64(fd.as_raw_fd(), vec.as_mut_ptr() as *mut dirent64, sz)
+        };
+        if readlen > 0 {
+            // shrink down to the minimal size
+            vec.resize(readlen.try_into().unwrap(), 0);
+            break;
+        }
+
+        // if the buffer is too small, getdents64 returns EINVAL
+        // otherwise, getdents64 returns an error number
+        if readlen != (-hermit_abi::errno::EINVAL).into() {
+            return Err(Error::from_raw_os_error(readlen.try_into().unwrap()));
+        }
+
+        // we don't have enough memory => try to increase the vector size
+        sz = sz * 2;
+
+        // 1 MB for directory entries should be enough
+        // stop here to avoid an endless loop
+        if sz > 0x100000 {
+            return Err(Error::from(ErrorKind::Uncategorized));
+        }
+    }
+
+    Ok(ReadDir::new(InnerReadDir::new(root, vec)))
+}
+
+pub fn unlink(path: &Path) -> io::Result<()> {
+    run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::unlink(path.as_ptr()) }).map(|_| ()))
+}
+
+pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
+    Err(Error::from_raw_os_error(22))
+}
+
+pub fn rmdir(path: &Path) -> io::Result<()> {
+    run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::rmdir(path.as_ptr()) }).map(|_| ()))
+}
+
+pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn stat(path: &Path) -> io::Result<FileAttr> {
+    run_path_with_cstr(path, &|path| {
+        let mut stat_val: stat_struct = unsafe { mem::zeroed() };
+        cvt(unsafe { hermit_abi::stat(path.as_ptr(), &mut stat_val) })?;
+        Ok(FileAttr::from_stat(stat_val))
+    })
+}
+
+pub fn lstat(path: &Path) -> io::Result<FileAttr> {
+    run_path_with_cstr(path, &|path| {
+        let mut stat_val: stat_struct = unsafe { mem::zeroed() };
+        cvt(unsafe { hermit_abi::lstat(path.as_ptr(), &mut stat_val) })?;
+        Ok(FileAttr::from_stat(stat_val))
+    })
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
diff --git a/library/std/src/sys/fs/mod.rs b/library/std/src/sys/fs/mod.rs
new file mode 100644
index 00000000000..c2e19eb393a
--- /dev/null
+++ b/library/std/src/sys/fs/mod.rs
@@ -0,0 +1,28 @@
+#![deny(unsafe_op_in_unsafe_fn)]
+
+pub mod common;
+
+cfg_if::cfg_if! {
+    if #[cfg(target_family = "unix")] {
+        mod unix;
+        pub use unix::*;
+    } else if #[cfg(target_os = "windows")] {
+        mod windows;
+        pub use windows::*;
+    } else if #[cfg(target_os = "hermit")] {
+        mod hermit;
+        pub use hermit::*;
+    } else if #[cfg(target_os = "solid_asp3")] {
+        mod solid;
+        pub use solid::*;
+    } else if #[cfg(target_os = "uefi")] {
+        mod uefi;
+        pub use uefi::*;
+    } else if #[cfg(target_os = "wasi")] {
+        mod wasi;
+        pub use wasi::*;
+    } else {
+        mod unsupported;
+        pub use unsupported::*;
+    }
+}
diff --git a/library/std/src/sys/fs/solid.rs b/library/std/src/sys/fs/solid.rs
new file mode 100644
index 00000000000..39de933b724
--- /dev/null
+++ b/library/std/src/sys/fs/solid.rs
@@ -0,0 +1,611 @@
+#![allow(dead_code)]
+
+use crate::ffi::{CStr, CString, OsStr, OsString};
+use crate::fmt;
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
+use crate::mem::MaybeUninit;
+use crate::os::raw::{c_int, c_short};
+use crate::os::solid::ffi::OsStrExt;
+use crate::path::{Path, PathBuf};
+use crate::sync::Arc;
+pub use crate::sys::fs::common::exists;
+use crate::sys::pal::{abi, error};
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+use crate::sys_common::ignore_notfound;
+
+type CIntNotMinusOne = core::num::niche_types::NotAllOnes<c_int>;
+
+/// A file descriptor.
+#[derive(Clone, Copy)]
+struct FileDesc {
+    fd: CIntNotMinusOne,
+}
+
+impl FileDesc {
+    #[inline]
+    #[track_caller]
+    fn new(fd: c_int) -> FileDesc {
+        FileDesc { fd: CIntNotMinusOne::new(fd).expect("fd != -1") }
+    }
+
+    #[inline]
+    fn raw(&self) -> c_int {
+        self.fd.as_inner()
+    }
+}
+
+pub struct File {
+    fd: FileDesc,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+    stat: abi::stat,
+}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+    dirp: abi::S_DIR,
+    root: PathBuf,
+}
+
+pub struct ReadDir {
+    inner: Arc<InnerReadDir>,
+}
+
+pub struct DirEntry {
+    entry: abi::dirent,
+    inner: Arc<InnerReadDir>,
+}
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+    // generic
+    read: bool,
+    write: bool,
+    append: bool,
+    truncate: bool,
+    create: bool,
+    create_new: bool,
+    // system-specific
+    custom_flags: i32,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions(c_short);
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType(c_short);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        self.stat.st_size as u64
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        FilePermissions(self.stat.st_mode)
+    }
+
+    pub fn file_type(&self) -> FileType {
+        FileType(self.stat.st_mode)
+    }
+
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_time_t(self.stat.st_mtime))
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_time_t(self.stat.st_atime))
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_time_t(self.stat.st_ctime))
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        (self.0 & abi::S_IWRITE) == 0
+    }
+
+    pub fn set_readonly(&mut self, readonly: bool) {
+        if readonly {
+            self.0 &= !abi::S_IWRITE;
+        } else {
+            self.0 |= abi::S_IWRITE;
+        }
+    }
+}
+
+impl FileTimes {
+    pub fn set_accessed(&mut self, _t: SystemTime) {}
+    pub fn set_modified(&mut self, _t: SystemTime) {}
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        self.is(abi::S_IFDIR)
+    }
+    pub fn is_file(&self) -> bool {
+        self.is(abi::S_IFREG)
+    }
+    pub fn is_symlink(&self) -> bool {
+        false
+    }
+
+    pub fn is(&self, mode: c_short) -> bool {
+        self.0 & abi::S_IFMT == mode
+    }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+    unsafe {
+        let mut dir = MaybeUninit::uninit();
+        error::SolidError::err_if_negative(abi::SOLID_FS_OpenDir(
+            cstr(p)?.as_ptr(),
+            dir.as_mut_ptr(),
+        ))
+        .map_err(|e| e.as_io_error())?;
+        let inner = Arc::new(InnerReadDir { dirp: dir.assume_init(), root: p.to_owned() });
+        Ok(ReadDir { inner })
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
+        // Thus the result will be e g 'ReadDir("/home")'
+        fmt::Debug::fmt(&*self.inner.root, f)
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        let entry = unsafe {
+            let mut out_entry = MaybeUninit::uninit();
+            match error::SolidError::err_if_negative(abi::SOLID_FS_ReadDir(
+                self.inner.dirp,
+                out_entry.as_mut_ptr(),
+            )) {
+                Ok(_) => out_entry.assume_init(),
+                Err(e) if e.as_raw() == abi::SOLID_ERR_NOTFOUND => return None,
+                Err(e) => return Some(Err(e.as_io_error())),
+            }
+        };
+
+        (entry.d_name[0] != 0).then(|| Ok(DirEntry { entry, inner: Arc::clone(&self.inner) }))
+    }
+}
+
+impl Drop for InnerReadDir {
+    fn drop(&mut self) {
+        unsafe { abi::SOLID_FS_CloseDir(self.dirp) };
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        self.inner.root.join(OsStr::from_bytes(
+            unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes(),
+        ))
+    }
+
+    pub fn file_name(&self) -> OsString {
+        OsStr::from_bytes(unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }.to_bytes())
+            .to_os_string()
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        lstat(&self.path())
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        match self.entry.d_type {
+            abi::DT_CHR => Ok(FileType(abi::S_IFCHR)),
+            abi::DT_FIFO => Ok(FileType(abi::S_IFIFO)),
+            abi::DT_REG => Ok(FileType(abi::S_IFREG)),
+            abi::DT_DIR => Ok(FileType(abi::S_IFDIR)),
+            abi::DT_BLK => Ok(FileType(abi::S_IFBLK)),
+            _ => lstat(&self.path()).map(|m| m.file_type()),
+        }
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions {
+            // generic
+            read: false,
+            write: false,
+            append: false,
+            truncate: false,
+            create: false,
+            create_new: false,
+            // system-specific
+            custom_flags: 0,
+        }
+    }
+
+    pub fn read(&mut self, read: bool) {
+        self.read = read;
+    }
+    pub fn write(&mut self, write: bool) {
+        self.write = write;
+    }
+    pub fn append(&mut self, append: bool) {
+        self.append = append;
+    }
+    pub fn truncate(&mut self, truncate: bool) {
+        self.truncate = truncate;
+    }
+    pub fn create(&mut self, create: bool) {
+        self.create = create;
+    }
+    pub fn create_new(&mut self, create_new: bool) {
+        self.create_new = create_new;
+    }
+
+    pub fn custom_flags(&mut self, flags: i32) {
+        self.custom_flags = flags;
+    }
+    pub fn mode(&mut self, _mode: u32) {}
+
+    fn get_access_mode(&self) -> io::Result<c_int> {
+        match (self.read, self.write, self.append) {
+            (true, false, false) => Ok(abi::O_RDONLY),
+            (false, true, false) => Ok(abi::O_WRONLY),
+            (true, true, false) => Ok(abi::O_RDWR),
+            (false, _, true) => Ok(abi::O_WRONLY | abi::O_APPEND),
+            (true, _, true) => Ok(abi::O_RDWR | abi::O_APPEND),
+            (false, false, false) => Err(io::Error::from_raw_os_error(libc::EINVAL)),
+        }
+    }
+
+    fn get_creation_mode(&self) -> io::Result<c_int> {
+        match (self.write, self.append) {
+            (true, false) => {}
+            (false, false) => {
+                if self.truncate || self.create || self.create_new {
+                    return Err(io::Error::from_raw_os_error(libc::EINVAL));
+                }
+            }
+            (_, true) => {
+                if self.truncate && !self.create_new {
+                    return Err(io::Error::from_raw_os_error(libc::EINVAL));
+                }
+            }
+        }
+
+        Ok(match (self.create, self.truncate, self.create_new) {
+            (false, false, false) => 0,
+            (true, false, false) => abi::O_CREAT,
+            (false, true, false) => abi::O_TRUNC,
+            (true, true, false) => abi::O_CREAT | abi::O_TRUNC,
+            (_, _, true) => abi::O_CREAT | abi::O_EXCL,
+        })
+    }
+}
+
+fn cstr(path: &Path) -> io::Result<CString> {
+    let path = path.as_os_str().as_bytes();
+
+    if !path.starts_with(br"\") {
+        // Relative paths aren't supported
+        return Err(crate::io::const_error!(
+            crate::io::ErrorKind::Unsupported,
+            "relative path is not supported on this platform",
+        ));
+    }
+
+    // Apply the thread-safety wrapper
+    const SAFE_PREFIX: &[u8] = br"\TS";
+    let wrapped_path = [SAFE_PREFIX, &path, &[0]].concat();
+
+    CString::from_vec_with_nul(wrapped_path).map_err(|_| {
+        crate::io::const_error!(io::ErrorKind::InvalidInput, "path provided contains a nul byte")
+    })
+}
+
+impl File {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        let flags = opts.get_access_mode()?
+            | opts.get_creation_mode()?
+            | (opts.custom_flags as c_int & !abi::O_ACCMODE);
+        unsafe {
+            let mut fd = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Open(
+                fd.as_mut_ptr(),
+                cstr(path)?.as_ptr(),
+                flags,
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(File { fd: FileDesc::new(fd.assume_init()) })
+        }
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        unsupported()
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        self.flush()
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        self.flush()
+    }
+
+    pub fn lock(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn lock_shared(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn try_lock(&self) -> io::Result<bool> {
+        unsupported()
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        unsupported()
+    }
+
+    pub fn unlock(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn truncate(&self, _size: u64) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        unsafe {
+            let mut out_num_bytes = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Read(
+                self.fd.raw(),
+                buf.as_mut_ptr(),
+                buf.len(),
+                out_num_bytes.as_mut_ptr(),
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(out_num_bytes.assume_init())
+        }
+    }
+
+    pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        unsafe {
+            let len = cursor.capacity();
+            let mut out_num_bytes = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Read(
+                self.fd.raw(),
+                cursor.as_mut().as_mut_ptr() as *mut u8,
+                len,
+                out_num_bytes.as_mut_ptr(),
+            ))
+            .map_err(|e| e.as_io_error())?;
+
+            // Safety: `out_num_bytes` is filled by the successful call to
+            // `SOLID_FS_Read`
+            let num_bytes_read = out_num_bytes.assume_init();
+
+            // Safety: `num_bytes_read` bytes were written to the unfilled
+            // portion of the buffer
+            cursor.advance_unchecked(num_bytes_read);
+
+            Ok(())
+        }
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        crate::io::default_read_vectored(|buf| self.read(buf), bufs)
+    }
+
+    pub fn is_read_vectored(&self) -> bool {
+        false
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        unsafe {
+            let mut out_num_bytes = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Write(
+                self.fd.raw(),
+                buf.as_ptr(),
+                buf.len(),
+                out_num_bytes.as_mut_ptr(),
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(out_num_bytes.assume_init())
+        }
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        crate::io::default_write_vectored(|buf| self.write(buf), bufs)
+    }
+
+    pub fn is_write_vectored(&self) -> bool {
+        false
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Sync(self.fd.raw()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    }
+
+    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+        let (whence, pos) = match pos {
+            // Casting to `i64` is fine, too large values will end up as
+            // negative which will cause an error in `SOLID_FS_Lseek`.
+            SeekFrom::Start(off) => (abi::SEEK_SET, off as i64),
+            SeekFrom::End(off) => (abi::SEEK_END, off),
+            SeekFrom::Current(off) => (abi::SEEK_CUR, off),
+        };
+        error::SolidError::err_if_negative(unsafe {
+            abi::SOLID_FS_Lseek(self.fd.raw(), pos, whence)
+        })
+        .map_err(|e| e.as_io_error())?;
+        // Get the new offset
+        self.tell()
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        unsafe {
+            let mut out_offset = MaybeUninit::uninit();
+            error::SolidError::err_if_negative(abi::SOLID_FS_Ftell(
+                self.fd.raw(),
+                out_offset.as_mut_ptr(),
+            ))
+            .map_err(|e| e.as_io_error())?;
+            Ok(out_offset.assume_init() as u64)
+        }
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        unsupported()
+    }
+
+    pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
+        unsupported()
+    }
+}
+
+impl Drop for File {
+    fn drop(&mut self) {
+        unsafe { abi::SOLID_FS_Close(self.fd.raw()) };
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder {}
+    }
+
+    pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Mkdir(cstr(p)?.as_ptr()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    }
+}
+
+impl fmt::Debug for File {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("File").field("fd", &self.fd.raw()).finish()
+    }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+    if stat(p)?.file_type().is_dir() {
+        Err(io::const_error!(io::ErrorKind::IsADirectory, "is a directory"))
+    } else {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    }
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+    error::SolidError::err_if_negative(unsafe {
+        abi::SOLID_FS_Rename(cstr(old)?.as_ptr(), cstr(new)?.as_ptr())
+    })
+    .map_err(|e| e.as_io_error())?;
+    Ok(())
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+    error::SolidError::err_if_negative(unsafe {
+        abi::SOLID_FS_Chmod(cstr(p)?.as_ptr(), perm.0.into())
+    })
+    .map_err(|e| e.as_io_error())?;
+    Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+    if stat(p)?.file_type().is_dir() {
+        error::SolidError::err_if_negative(unsafe { abi::SOLID_FS_Unlink(cstr(p)?.as_ptr()) })
+            .map_err(|e| e.as_io_error())?;
+        Ok(())
+    } else {
+        Err(io::const_error!(io::ErrorKind::NotADirectory, "not a directory"))
+    }
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    for child in readdir(path)? {
+        let result: io::Result<()> = try {
+            let child = child?;
+            let child_type = child.file_type()?;
+            if child_type.is_dir() {
+                remove_dir_all(&child.path())?;
+            } else {
+                unlink(&child.path())?;
+            }
+        };
+        // ignore internal NotFound errors
+        if let Err(err) = &result
+            && err.kind() != io::ErrorKind::NotFound
+        {
+            return result;
+        }
+    }
+    ignore_notfound(rmdir(path))
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+    // This target doesn't support symlinks
+    stat(p)?;
+    Err(io::const_error!(io::ErrorKind::InvalidInput, "not a symbolic link"))
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+    // This target doesn't support symlinks
+    unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+    // This target doesn't support symlinks
+    unsupported()
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+    // This target doesn't support symlinks
+    lstat(p)
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+    unsafe {
+        let mut out_stat = MaybeUninit::uninit();
+        error::SolidError::err_if_negative(abi::SOLID_FS_Stat(
+            cstr(p)?.as_ptr(),
+            out_stat.as_mut_ptr(),
+        ))
+        .map_err(|e| e.as_io_error())?;
+        Ok(FileAttr { stat: out_stat.assume_init() })
+    }
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    use crate::fs::File;
+
+    let mut reader = File::open(from)?;
+    let mut writer = File::create(to)?;
+
+    io::copy(&mut reader, &mut writer)
+}
diff --git a/library/std/src/sys/fs/uefi.rs b/library/std/src/sys/fs/uefi.rs
new file mode 100644
index 00000000000..45e93deffa3
--- /dev/null
+++ b/library/std/src/sys/fs/uefi.rs
@@ -0,0 +1,348 @@
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
+use crate::path::{Path, PathBuf};
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+
+pub struct File(!);
+
+pub struct FileAttr(!);
+
+pub struct ReadDir(!);
+
+pub struct DirEntry(!);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
+pub struct FilePermissions(!);
+
+pub struct FileType(!);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        self.0
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        self.0
+    }
+
+    pub fn file_type(&self) -> FileType {
+        self.0
+    }
+
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        self.0
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        self.0
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        self.0
+    }
+}
+
+impl Clone for FileAttr {
+    fn clone(&self) -> FileAttr {
+        self.0
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        self.0
+    }
+
+    pub fn set_readonly(&mut self, _readonly: bool) {
+        self.0
+    }
+}
+
+impl Clone for FilePermissions {
+    fn clone(&self) -> FilePermissions {
+        self.0
+    }
+}
+
+impl PartialEq for FilePermissions {
+    fn eq(&self, _other: &FilePermissions) -> bool {
+        self.0
+    }
+}
+
+impl Eq for FilePermissions {}
+
+impl fmt::Debug for FilePermissions {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+impl FileTimes {
+    pub fn set_accessed(&mut self, _t: SystemTime) {}
+    pub fn set_modified(&mut self, _t: SystemTime) {}
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        self.0
+    }
+
+    pub fn is_file(&self) -> bool {
+        self.0
+    }
+
+    pub fn is_symlink(&self) -> bool {
+        self.0
+    }
+}
+
+impl Clone for FileType {
+    fn clone(&self) -> FileType {
+        self.0
+    }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+    fn eq(&self, _other: &FileType) -> bool {
+        self.0
+    }
+}
+
+impl Eq for FileType {}
+
+impl Hash for FileType {
+    fn hash<H: Hasher>(&self, _h: &mut H) {
+        self.0
+    }
+}
+
+impl fmt::Debug for FileType {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        self.0
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        self.0
+    }
+
+    pub fn file_name(&self) -> OsString {
+        self.0
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        self.0
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        self.0
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions {}
+    }
+
+    pub fn read(&mut self, _read: bool) {}
+    pub fn write(&mut self, _write: bool) {}
+    pub fn append(&mut self, _append: bool) {}
+    pub fn truncate(&mut self, _truncate: bool) {}
+    pub fn create(&mut self, _create: bool) {}
+    pub fn create_new(&mut self, _create_new: bool) {}
+}
+
+impl File {
+    pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
+        unsupported()
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        self.0
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn lock(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn lock_shared(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn try_lock(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn unlock(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn truncate(&self, _size: u64) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn is_read_vectored(&self) -> bool {
+        self.0
+    }
+
+    pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn is_write_vectored(&self) -> bool {
+        self.0
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+        self.0
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        self.0
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        self.0
+    }
+
+    pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
+        self.0
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder {}
+    }
+
+    pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
+        unsupported()
+    }
+}
+
+impl fmt::Debug for File {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
+    unsupported()
+}
+
+pub fn unlink(_p: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
+    match perm.0 {}
+}
+
+pub fn rmdir(_p: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn exists(_path: &Path) -> io::Result<bool> {
+    unsupported()
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn stat(_p: &Path) -> io::Result<FileAttr> {
+    unsupported()
+}
+
+pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
+    unsupported()
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
+    unsupported()
+}
diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs
new file mode 100644
index 00000000000..914971934bf
--- /dev/null
+++ b/library/std/src/sys/fs/unix.rs
@@ -0,0 +1,2319 @@
+#![allow(nonstandard_style)]
+#![allow(unsafe_op_in_unsafe_fn)]
+// miri has some special hacks here that make things unused.
+#![cfg_attr(miri, allow(unused))]
+
+#[cfg(test)]
+mod tests;
+
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+use libc::c_char;
+#[cfg(any(
+    all(target_os = "linux", not(target_env = "musl")),
+    target_os = "android",
+    target_os = "fuchsia",
+    target_os = "hurd"
+))]
+use libc::dirfd;
+#[cfg(target_os = "fuchsia")]
+use libc::fstatat as fstatat64;
+#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
+use libc::fstatat64;
+#[cfg(any(
+    target_os = "android",
+    target_os = "solaris",
+    target_os = "fuchsia",
+    target_os = "redox",
+    target_os = "illumos",
+    target_os = "aix",
+    target_os = "nto",
+    target_os = "vita",
+    all(target_os = "linux", target_env = "musl"),
+))]
+use libc::readdir as readdir64;
+#[cfg(not(any(
+    target_os = "android",
+    target_os = "linux",
+    target_os = "solaris",
+    target_os = "illumos",
+    target_os = "l4re",
+    target_os = "fuchsia",
+    target_os = "redox",
+    target_os = "aix",
+    target_os = "nto",
+    target_os = "vita",
+    target_os = "hurd",
+)))]
+use libc::readdir_r as readdir64_r;
+#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
+use libc::readdir64;
+#[cfg(target_os = "l4re")]
+use libc::readdir64_r;
+use libc::{c_int, mode_t};
+#[cfg(target_os = "android")]
+use libc::{
+    dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
+    lstat as lstat64, off64_t, open as open64, stat as stat64,
+};
+#[cfg(not(any(
+    all(target_os = "linux", not(target_env = "musl")),
+    target_os = "l4re",
+    target_os = "android",
+    target_os = "hurd",
+)))]
+use libc::{
+    dirent as dirent64, fstat as fstat64, ftruncate as ftruncate64, lseek as lseek64,
+    lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
+};
+#[cfg(any(
+    all(target_os = "linux", not(target_env = "musl")),
+    target_os = "l4re",
+    target_os = "hurd"
+))]
+use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, stat64};
+
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::fmt::{self, Write as _};
+use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
+use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd};
+use crate::os::unix::prelude::*;
+use crate::path::{Path, PathBuf};
+use crate::sync::Arc;
+use crate::sys::common::small_c_string::run_path_with_cstr;
+use crate::sys::fd::FileDesc;
+pub use crate::sys::fs::common::exists;
+use crate::sys::time::SystemTime;
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+use crate::sys::weak::syscall;
+#[cfg(target_os = "android")]
+use crate::sys::weak::weak;
+use crate::sys::{cvt, cvt_r};
+use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
+use crate::{mem, ptr};
+
+pub struct File(FileDesc);
+
+// FIXME: This should be available on Linux with all `target_env`.
+// But currently only glibc exposes `statx` fn and structs.
+// We don't want to import unverified raw C structs here directly.
+// https://github.com/rust-lang/rust/pull/67774
+macro_rules! cfg_has_statx {
+    ({ $($then_tt:tt)* } else { $($else_tt:tt)* }) => {
+        cfg_if::cfg_if! {
+            if #[cfg(all(target_os = "linux", target_env = "gnu"))] {
+                $($then_tt)*
+            } else {
+                $($else_tt)*
+            }
+        }
+    };
+    ($($block_inner:tt)*) => {
+        #[cfg(all(target_os = "linux", target_env = "gnu"))]
+        {
+            $($block_inner)*
+        }
+    };
+}
+
+cfg_has_statx! {{
+    #[derive(Clone)]
+    pub struct FileAttr {
+        stat: stat64,
+        statx_extra_fields: Option<StatxExtraFields>,
+    }
+
+    #[derive(Clone)]
+    struct StatxExtraFields {
+        // This is needed to check if btime is supported by the filesystem.
+        stx_mask: u32,
+        stx_btime: libc::statx_timestamp,
+        // With statx, we can overcome 32-bit `time_t` too.
+        #[cfg(target_pointer_width = "32")]
+        stx_atime: libc::statx_timestamp,
+        #[cfg(target_pointer_width = "32")]
+        stx_ctime: libc::statx_timestamp,
+        #[cfg(target_pointer_width = "32")]
+        stx_mtime: libc::statx_timestamp,
+
+    }
+
+    // We prefer `statx` on Linux if available, which contains file creation time,
+    // as well as 64-bit timestamps of all kinds.
+    // Default `stat64` contains no creation time and may have 32-bit `time_t`.
+    unsafe fn try_statx(
+        fd: c_int,
+        path: *const c_char,
+        flags: i32,
+        mask: u32,
+    ) -> Option<io::Result<FileAttr>> {
+        use crate::sync::atomic::{AtomicU8, Ordering};
+
+        // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
+        // We check for it on first failure and remember availability to avoid having to
+        // do it again.
+        #[repr(u8)]
+        enum STATX_STATE{ Unknown = 0, Present, Unavailable }
+        static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8);
+
+        syscall! {
+            fn statx(
+                fd: c_int,
+                pathname: *const c_char,
+                flags: c_int,
+                mask: libc::c_uint,
+                statxbuf: *mut libc::statx
+            ) -> c_int
+        }
+
+        let statx_availability = STATX_SAVED_STATE.load(Ordering::Relaxed);
+        if statx_availability == STATX_STATE::Unavailable as u8 {
+            return None;
+        }
+
+        let mut buf: libc::statx = mem::zeroed();
+        if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
+            if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
+                return Some(Err(err));
+            }
+
+            // We're not yet entirely sure whether `statx` is usable on this kernel
+            // or not. Syscalls can return errors from things other than the kernel
+            // per se, e.g. `EPERM` can be returned if seccomp is used to block the
+            // syscall, or `ENOSYS` might be returned from a faulty FUSE driver.
+            //
+            // Availability is checked by performing a call which expects `EFAULT`
+            // if the syscall is usable.
+            //
+            // See: https://github.com/rust-lang/rust/issues/65662
+            //
+            // FIXME what about transient conditions like `ENOMEM`?
+            let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_BASIC_STATS | libc::STATX_BTIME, ptr::null_mut()))
+                .err()
+                .and_then(|e| e.raw_os_error());
+            if err2 == Some(libc::EFAULT) {
+                STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
+                return Some(Err(err));
+            } else {
+                STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+                return None;
+            }
+        }
+        if statx_availability == STATX_STATE::Unknown as u8 {
+            STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
+        }
+
+        // We cannot fill `stat64` exhaustively because of private padding fields.
+        let mut stat: stat64 = mem::zeroed();
+        // `c_ulong` on gnu-mips, `dev_t` otherwise
+        stat.st_dev = libc::makedev(buf.stx_dev_major, buf.stx_dev_minor) as _;
+        stat.st_ino = buf.stx_ino as libc::ino64_t;
+        stat.st_nlink = buf.stx_nlink as libc::nlink_t;
+        stat.st_mode = buf.stx_mode as libc::mode_t;
+        stat.st_uid = buf.stx_uid as libc::uid_t;
+        stat.st_gid = buf.stx_gid as libc::gid_t;
+        stat.st_rdev = libc::makedev(buf.stx_rdev_major, buf.stx_rdev_minor) as _;
+        stat.st_size = buf.stx_size as off64_t;
+        stat.st_blksize = buf.stx_blksize as libc::blksize_t;
+        stat.st_blocks = buf.stx_blocks as libc::blkcnt64_t;
+        stat.st_atime = buf.stx_atime.tv_sec as libc::time_t;
+        // `i64` on gnu-x86_64-x32, `c_ulong` otherwise.
+        stat.st_atime_nsec = buf.stx_atime.tv_nsec as _;
+        stat.st_mtime = buf.stx_mtime.tv_sec as libc::time_t;
+        stat.st_mtime_nsec = buf.stx_mtime.tv_nsec as _;
+        stat.st_ctime = buf.stx_ctime.tv_sec as libc::time_t;
+        stat.st_ctime_nsec = buf.stx_ctime.tv_nsec as _;
+
+        let extra = StatxExtraFields {
+            stx_mask: buf.stx_mask,
+            stx_btime: buf.stx_btime,
+            // Store full times to avoid 32-bit `time_t` truncation.
+            #[cfg(target_pointer_width = "32")]
+            stx_atime: buf.stx_atime,
+            #[cfg(target_pointer_width = "32")]
+            stx_ctime: buf.stx_ctime,
+            #[cfg(target_pointer_width = "32")]
+            stx_mtime: buf.stx_mtime,
+        };
+
+        Some(Ok(FileAttr { stat, statx_extra_fields: Some(extra) }))
+    }
+
+} else {
+    #[derive(Clone)]
+    pub struct FileAttr {
+        stat: stat64,
+    }
+}}
+
+// all DirEntry's will have a reference to this struct
+struct InnerReadDir {
+    dirp: Dir,
+    root: PathBuf,
+}
+
+pub struct ReadDir {
+    inner: Arc<InnerReadDir>,
+    end_of_stream: bool,
+}
+
+impl ReadDir {
+    fn new(inner: InnerReadDir) -> Self {
+        Self { inner: Arc::new(inner), end_of_stream: false }
+    }
+}
+
+struct Dir(*mut libc::DIR);
+
+unsafe impl Send for Dir {}
+unsafe impl Sync for Dir {}
+
+#[cfg(any(
+    target_os = "android",
+    target_os = "linux",
+    target_os = "solaris",
+    target_os = "illumos",
+    target_os = "fuchsia",
+    target_os = "redox",
+    target_os = "aix",
+    target_os = "nto",
+    target_os = "vita",
+    target_os = "hurd",
+))]
+pub struct DirEntry {
+    dir: Arc<InnerReadDir>,
+    entry: dirent64_min,
+    // We need to store an owned copy of the entry name on platforms that use
+    // readdir() (not readdir_r()), because a) struct dirent may use a flexible
+    // array to store the name, b) it lives only until the next readdir() call.
+    name: crate::ffi::CString,
+}
+
+// Define a minimal subset of fields we need from `dirent64`, especially since
+// we're not using the immediate `d_name` on these targets. Keeping this as an
+// `entry` field in `DirEntry` helps reduce the `cfg` boilerplate elsewhere.
+#[cfg(any(
+    target_os = "android",
+    target_os = "linux",
+    target_os = "solaris",
+    target_os = "illumos",
+    target_os = "fuchsia",
+    target_os = "redox",
+    target_os = "aix",
+    target_os = "nto",
+    target_os = "vita",
+    target_os = "hurd",
+))]
+struct dirent64_min {
+    d_ino: u64,
+    #[cfg(not(any(
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "vita",
+    )))]
+    d_type: u8,
+}
+
+#[cfg(not(any(
+    target_os = "android",
+    target_os = "linux",
+    target_os = "solaris",
+    target_os = "illumos",
+    target_os = "fuchsia",
+    target_os = "redox",
+    target_os = "aix",
+    target_os = "nto",
+    target_os = "vita",
+    target_os = "hurd",
+)))]
+pub struct DirEntry {
+    dir: Arc<InnerReadDir>,
+    // The full entry includes a fixed-length `d_name`.
+    entry: dirent64,
+}
+
+#[derive(Clone)]
+pub struct OpenOptions {
+    // generic
+    read: bool,
+    write: bool,
+    append: bool,
+    truncate: bool,
+    create: bool,
+    create_new: bool,
+    // system-specific
+    custom_flags: i32,
+    mode: mode_t,
+}
+
+#[derive(Clone, PartialEq, Eq)]
+pub struct FilePermissions {
+    mode: mode_t,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {
+    accessed: Option<SystemTime>,
+    modified: Option<SystemTime>,
+    #[cfg(target_vendor = "apple")]
+    created: Option<SystemTime>,
+}
+
+#[derive(Copy, Clone, Eq)]
+pub struct FileType {
+    mode: mode_t,
+}
+
+impl PartialEq for FileType {
+    fn eq(&self, other: &Self) -> bool {
+        self.masked() == other.masked()
+    }
+}
+
+impl core::hash::Hash for FileType {
+    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+        self.masked().hash(state);
+    }
+}
+
+pub struct DirBuilder {
+    mode: mode_t,
+}
+
+#[derive(Copy, Clone)]
+struct Mode(mode_t);
+
+cfg_has_statx! {{
+    impl FileAttr {
+        fn from_stat64(stat: stat64) -> Self {
+            Self { stat, statx_extra_fields: None }
+        }
+
+        #[cfg(target_pointer_width = "32")]
+        pub fn stx_mtime(&self) -> Option<&libc::statx_timestamp> {
+            if let Some(ext) = &self.statx_extra_fields {
+                if (ext.stx_mask & libc::STATX_MTIME) != 0 {
+                    return Some(&ext.stx_mtime);
+                }
+            }
+            None
+        }
+
+        #[cfg(target_pointer_width = "32")]
+        pub fn stx_atime(&self) -> Option<&libc::statx_timestamp> {
+            if let Some(ext) = &self.statx_extra_fields {
+                if (ext.stx_mask & libc::STATX_ATIME) != 0 {
+                    return Some(&ext.stx_atime);
+                }
+            }
+            None
+        }
+
+        #[cfg(target_pointer_width = "32")]
+        pub fn stx_ctime(&self) -> Option<&libc::statx_timestamp> {
+            if let Some(ext) = &self.statx_extra_fields {
+                if (ext.stx_mask & libc::STATX_CTIME) != 0 {
+                    return Some(&ext.stx_ctime);
+                }
+            }
+            None
+        }
+    }
+} else {
+    impl FileAttr {
+        fn from_stat64(stat: stat64) -> Self {
+            Self { stat }
+        }
+    }
+}}
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        self.stat.st_size as u64
+    }
+    pub fn perm(&self) -> FilePermissions {
+        FilePermissions { mode: (self.stat.st_mode as mode_t) }
+    }
+
+    pub fn file_type(&self) -> FileType {
+        FileType { mode: self.stat.st_mode as mode_t }
+    }
+}
+
+#[cfg(target_os = "netbsd")]
+impl FileAttr {
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)
+    }
+}
+
+#[cfg(target_os = "aix")]
+impl FileAttr {
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64)
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64)
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64)
+    }
+}
+
+#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))]
+impl FileAttr {
+    #[cfg(not(any(
+        target_os = "vxworks",
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "hurd",
+        target_os = "rtems",
+        target_os = "nuttx",
+    )))]
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        #[cfg(target_pointer_width = "32")]
+        cfg_has_statx! {
+            if let Some(mtime) = self.stx_mtime() {
+                return SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64);
+            }
+        }
+
+        SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64)
+    }
+
+    #[cfg(any(
+        target_os = "vxworks",
+        target_os = "espidf",
+        target_os = "vita",
+        target_os = "rtems",
+    ))]
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_mtime as i64, 0)
+    }
+
+    #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64)
+    }
+
+    #[cfg(not(any(
+        target_os = "vxworks",
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "hurd",
+        target_os = "rtems",
+        target_os = "nuttx",
+    )))]
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        #[cfg(target_pointer_width = "32")]
+        cfg_has_statx! {
+            if let Some(atime) = self.stx_atime() {
+                return SystemTime::new(atime.tv_sec, atime.tv_nsec as i64);
+            }
+        }
+
+        SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64)
+    }
+
+    #[cfg(any(
+        target_os = "vxworks",
+        target_os = "espidf",
+        target_os = "vita",
+        target_os = "rtems"
+    ))]
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_atime as i64, 0)
+    }
+
+    #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))]
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64)
+    }
+
+    #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_vendor = "apple"))]
+    pub fn created(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)
+    }
+
+    #[cfg(not(any(
+        target_os = "freebsd",
+        target_os = "openbsd",
+        target_os = "vita",
+        target_vendor = "apple",
+    )))]
+    pub fn created(&self) -> io::Result<SystemTime> {
+        cfg_has_statx! {
+            if let Some(ext) = &self.statx_extra_fields {
+                return if (ext.stx_mask & libc::STATX_BTIME) != 0 {
+                    SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64)
+                } else {
+                    Err(io::const_error!(
+                        io::ErrorKind::Unsupported,
+                        "creation time is not available for the filesystem",
+                    ))
+                };
+            }
+        }
+
+        Err(io::const_error!(
+            io::ErrorKind::Unsupported,
+            "creation time is not available on this platform currently",
+        ))
+    }
+
+    #[cfg(target_os = "vita")]
+    pub fn created(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_ctime as i64, 0)
+    }
+}
+
+#[cfg(target_os = "nto")]
+impl FileAttr {
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)
+    }
+}
+
+impl AsInner<stat64> for FileAttr {
+    #[inline]
+    fn as_inner(&self) -> &stat64 {
+        &self.stat
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        // check if any class (owner, group, others) has write permission
+        self.mode & 0o222 == 0
+    }
+
+    pub fn set_readonly(&mut self, readonly: bool) {
+        if readonly {
+            // remove write permission for all classes; equivalent to `chmod a-w <file>`
+            self.mode &= !0o222;
+        } else {
+            // add write permission for all classes; equivalent to `chmod a+w <file>`
+            self.mode |= 0o222;
+        }
+    }
+    pub fn mode(&self) -> u32 {
+        self.mode as u32
+    }
+}
+
+impl FileTimes {
+    pub fn set_accessed(&mut self, t: SystemTime) {
+        self.accessed = Some(t);
+    }
+
+    pub fn set_modified(&mut self, t: SystemTime) {
+        self.modified = Some(t);
+    }
+
+    #[cfg(target_vendor = "apple")]
+    pub fn set_created(&mut self, t: SystemTime) {
+        self.created = Some(t);
+    }
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        self.is(libc::S_IFDIR)
+    }
+    pub fn is_file(&self) -> bool {
+        self.is(libc::S_IFREG)
+    }
+    pub fn is_symlink(&self) -> bool {
+        self.is(libc::S_IFLNK)
+    }
+
+    pub fn is(&self, mode: mode_t) -> bool {
+        self.masked() == mode
+    }
+
+    fn masked(&self) -> mode_t {
+        self.mode & libc::S_IFMT
+    }
+}
+
+impl fmt::Debug for FileType {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let FileType { mode } = self;
+        f.debug_struct("FileType").field("mode", &Mode(*mode)).finish()
+    }
+}
+
+impl FromInner<u32> for FilePermissions {
+    fn from_inner(mode: u32) -> FilePermissions {
+        FilePermissions { mode: mode as mode_t }
+    }
+}
+
+impl fmt::Debug for FilePermissions {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let FilePermissions { mode } = self;
+        f.debug_struct("FilePermissions").field("mode", &Mode(*mode)).finish()
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
+        // Thus the result will be e g 'ReadDir("/home")'
+        fmt::Debug::fmt(&*self.inner.root, f)
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    #[cfg(any(
+        target_os = "android",
+        target_os = "linux",
+        target_os = "solaris",
+        target_os = "fuchsia",
+        target_os = "redox",
+        target_os = "illumos",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "vita",
+        target_os = "hurd",
+    ))]
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        use crate::sys::os::{errno, set_errno};
+
+        if self.end_of_stream {
+            return None;
+        }
+
+        unsafe {
+            loop {
+                // As of POSIX.1-2017, readdir() is not required to be thread safe; only
+                // readdir_r() is. However, readdir_r() cannot correctly handle platforms
+                // with unlimited or variable NAME_MAX. Many modern platforms guarantee
+                // thread safety for readdir() as long an individual DIR* is not accessed
+                // concurrently, which is sufficient for Rust.
+                set_errno(0);
+                let entry_ptr: *const dirent64 = readdir64(self.inner.dirp.0);
+                if entry_ptr.is_null() {
+                    // We either encountered an error, or reached the end. Either way,
+                    // the next call to next() should return None.
+                    self.end_of_stream = true;
+
+                    // To distinguish between errors and end-of-directory, we had to clear
+                    // errno beforehand to check for an error now.
+                    return match errno() {
+                        0 => None,
+                        e => Some(Err(Error::from_raw_os_error(e))),
+                    };
+                }
+
+                // The dirent64 struct is a weird imaginary thing that isn't ever supposed
+                // to be worked with by value. Its trailing d_name field is declared
+                // variously as [c_char; 256] or [c_char; 1] on different systems but
+                // either way that size is meaningless; only the offset of d_name is
+                // meaningful. The dirent64 pointers that libc returns from readdir64 are
+                // allowed to point to allocations smaller _or_ LARGER than implied by the
+                // definition of the struct.
+                //
+                // As such, we need to be even more careful with dirent64 than if its
+                // contents were "simply" partially initialized data.
+                //
+                // Like for uninitialized contents, converting entry_ptr to `&dirent64`
+                // would not be legal. However, we can use `&raw const (*entry_ptr).d_name`
+                // to refer the fields individually, because that operation is equivalent
+                // to `byte_offset` and thus does not require the full extent of `*entry_ptr`
+                // to be in bounds of the same allocation, only the offset of the field
+                // being referenced.
+
+                // d_name is guaranteed to be null-terminated.
+                let name = CStr::from_ptr((&raw const (*entry_ptr).d_name).cast());
+                let name_bytes = name.to_bytes();
+                if name_bytes == b"." || name_bytes == b".." {
+                    continue;
+                }
+
+                // When loading from a field, we can skip the `&raw const`; `(*entry_ptr).d_ino` as
+                // a value expression will do the right thing: `byte_offset` to the field and then
+                // only access those bytes.
+                #[cfg(not(target_os = "vita"))]
+                let entry = dirent64_min {
+                    d_ino: (*entry_ptr).d_ino as u64,
+                    #[cfg(not(any(
+                        target_os = "solaris",
+                        target_os = "illumos",
+                        target_os = "aix",
+                        target_os = "nto",
+                    )))]
+                    d_type: (*entry_ptr).d_type as u8,
+                };
+
+                #[cfg(target_os = "vita")]
+                let entry = dirent64_min { d_ino: 0u64 };
+
+                return Some(Ok(DirEntry {
+                    entry,
+                    name: name.to_owned(),
+                    dir: Arc::clone(&self.inner),
+                }));
+            }
+        }
+    }
+
+    #[cfg(not(any(
+        target_os = "android",
+        target_os = "linux",
+        target_os = "solaris",
+        target_os = "fuchsia",
+        target_os = "redox",
+        target_os = "illumos",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "vita",
+        target_os = "hurd",
+    )))]
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        if self.end_of_stream {
+            return None;
+        }
+
+        unsafe {
+            let mut ret = DirEntry { entry: mem::zeroed(), dir: Arc::clone(&self.inner) };
+            let mut entry_ptr = ptr::null_mut();
+            loop {
+                let err = readdir64_r(self.inner.dirp.0, &mut ret.entry, &mut entry_ptr);
+                if err != 0 {
+                    if entry_ptr.is_null() {
+                        // We encountered an error (which will be returned in this iteration), but
+                        // we also reached the end of the directory stream. The `end_of_stream`
+                        // flag is enabled to make sure that we return `None` in the next iteration
+                        // (instead of looping forever)
+                        self.end_of_stream = true;
+                    }
+                    return Some(Err(Error::from_raw_os_error(err)));
+                }
+                if entry_ptr.is_null() {
+                    return None;
+                }
+                if ret.name_bytes() != b"." && ret.name_bytes() != b".." {
+                    return Some(Ok(ret));
+                }
+            }
+        }
+    }
+}
+
+/// Aborts the process if a file desceriptor is not open, if debug asserts are enabled
+///
+/// Many IO syscalls can't be fully trusted about EBADF error codes because those
+/// might get bubbled up from a remote FUSE server rather than the file descriptor
+/// in the current process being invalid.
+///
+/// So we check file flags instead which live on the file descriptor and not the underlying file.
+/// The downside is that it costs an extra syscall, so we only do it for debug.
+#[inline]
+pub(crate) fn debug_assert_fd_is_open(fd: RawFd) {
+    use crate::sys::os::errno;
+
+    // this is similar to assert_unsafe_precondition!() but it doesn't require const
+    if core::ub_checks::check_library_ub() {
+        if unsafe { libc::fcntl(fd, libc::F_GETFD) } == -1 && errno() == libc::EBADF {
+            rtabort!("IO Safety violation: owned file descriptor already closed");
+        }
+    }
+}
+
+impl Drop for Dir {
+    fn drop(&mut self) {
+        // dirfd isn't supported everywhere
+        #[cfg(not(any(
+            miri,
+            target_os = "redox",
+            target_os = "nto",
+            target_os = "vita",
+            target_os = "hurd",
+            target_os = "espidf",
+            target_os = "horizon",
+            target_os = "vxworks",
+            target_os = "rtems",
+            target_os = "nuttx",
+        )))]
+        {
+            let fd = unsafe { libc::dirfd(self.0) };
+            debug_assert_fd_is_open(fd);
+        }
+        let r = unsafe { libc::closedir(self.0) };
+        assert!(
+            r == 0 || crate::io::Error::last_os_error().is_interrupted(),
+            "unexpected error during closedir: {:?}",
+            crate::io::Error::last_os_error()
+        );
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        self.dir.root.join(self.file_name_os_str())
+    }
+
+    pub fn file_name(&self) -> OsString {
+        self.file_name_os_str().to_os_string()
+    }
+
+    #[cfg(all(
+        any(
+            all(target_os = "linux", not(target_env = "musl")),
+            target_os = "android",
+            target_os = "fuchsia",
+            target_os = "hurd"
+        ),
+        not(miri) // no dirfd on Miri
+    ))]
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        let fd = cvt(unsafe { dirfd(self.dir.dirp.0) })?;
+        let name = self.name_cstr().as_ptr();
+
+        cfg_has_statx! {
+            if let Some(ret) = unsafe { try_statx(
+                fd,
+                name,
+                libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
+                libc::STATX_BASIC_STATS | libc::STATX_BTIME,
+            ) } {
+                return ret;
+            }
+        }
+
+        let mut stat: stat64 = unsafe { mem::zeroed() };
+        cvt(unsafe { fstatat64(fd, name, &mut stat, libc::AT_SYMLINK_NOFOLLOW) })?;
+        Ok(FileAttr::from_stat64(stat))
+    }
+
+    #[cfg(any(
+        not(any(
+            all(target_os = "linux", not(target_env = "musl")),
+            target_os = "android",
+            target_os = "fuchsia",
+            target_os = "hurd",
+        )),
+        miri
+    ))]
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        lstat(&self.path())
+    }
+
+    #[cfg(any(
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "haiku",
+        target_os = "vxworks",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "vita",
+    ))]
+    pub fn file_type(&self) -> io::Result<FileType> {
+        self.metadata().map(|m| m.file_type())
+    }
+
+    #[cfg(not(any(
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "haiku",
+        target_os = "vxworks",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "vita",
+    )))]
+    pub fn file_type(&self) -> io::Result<FileType> {
+        match self.entry.d_type {
+            libc::DT_CHR => Ok(FileType { mode: libc::S_IFCHR }),
+            libc::DT_FIFO => Ok(FileType { mode: libc::S_IFIFO }),
+            libc::DT_LNK => Ok(FileType { mode: libc::S_IFLNK }),
+            libc::DT_REG => Ok(FileType { mode: libc::S_IFREG }),
+            libc::DT_SOCK => Ok(FileType { mode: libc::S_IFSOCK }),
+            libc::DT_DIR => Ok(FileType { mode: libc::S_IFDIR }),
+            libc::DT_BLK => Ok(FileType { mode: libc::S_IFBLK }),
+            _ => self.metadata().map(|m| m.file_type()),
+        }
+    }
+
+    #[cfg(any(
+        target_os = "linux",
+        target_os = "emscripten",
+        target_os = "android",
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "haiku",
+        target_os = "l4re",
+        target_os = "fuchsia",
+        target_os = "redox",
+        target_os = "vxworks",
+        target_os = "espidf",
+        target_os = "horizon",
+        target_os = "vita",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "hurd",
+        target_os = "rtems",
+        target_vendor = "apple",
+    ))]
+    pub fn ino(&self) -> u64 {
+        self.entry.d_ino as u64
+    }
+
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "openbsd",
+        target_os = "netbsd",
+        target_os = "dragonfly"
+    ))]
+    pub fn ino(&self) -> u64 {
+        self.entry.d_fileno as u64
+    }
+
+    #[cfg(target_os = "nuttx")]
+    pub fn ino(&self) -> u64 {
+        // Leave this 0 for now, as NuttX does not provide an inode number
+        // in its directory entries.
+        0
+    }
+
+    #[cfg(any(
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_vendor = "apple",
+    ))]
+    fn name_bytes(&self) -> &[u8] {
+        use crate::slice;
+        unsafe {
+            slice::from_raw_parts(
+                self.entry.d_name.as_ptr() as *const u8,
+                self.entry.d_namlen as usize,
+            )
+        }
+    }
+    #[cfg(not(any(
+        target_os = "netbsd",
+        target_os = "openbsd",
+        target_os = "freebsd",
+        target_os = "dragonfly",
+        target_vendor = "apple",
+    )))]
+    fn name_bytes(&self) -> &[u8] {
+        self.name_cstr().to_bytes()
+    }
+
+    #[cfg(not(any(
+        target_os = "android",
+        target_os = "linux",
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "fuchsia",
+        target_os = "redox",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "vita",
+        target_os = "hurd",
+    )))]
+    fn name_cstr(&self) -> &CStr {
+        unsafe { CStr::from_ptr(self.entry.d_name.as_ptr()) }
+    }
+    #[cfg(any(
+        target_os = "android",
+        target_os = "linux",
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "fuchsia",
+        target_os = "redox",
+        target_os = "aix",
+        target_os = "nto",
+        target_os = "vita",
+        target_os = "hurd",
+    ))]
+    fn name_cstr(&self) -> &CStr {
+        &self.name
+    }
+
+    pub fn file_name_os_str(&self) -> &OsStr {
+        OsStr::from_bytes(self.name_bytes())
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions {
+            // generic
+            read: false,
+            write: false,
+            append: false,
+            truncate: false,
+            create: false,
+            create_new: false,
+            // system-specific
+            custom_flags: 0,
+            mode: 0o666,
+        }
+    }
+
+    pub fn read(&mut self, read: bool) {
+        self.read = read;
+    }
+    pub fn write(&mut self, write: bool) {
+        self.write = write;
+    }
+    pub fn append(&mut self, append: bool) {
+        self.append = append;
+    }
+    pub fn truncate(&mut self, truncate: bool) {
+        self.truncate = truncate;
+    }
+    pub fn create(&mut self, create: bool) {
+        self.create = create;
+    }
+    pub fn create_new(&mut self, create_new: bool) {
+        self.create_new = create_new;
+    }
+
+    pub fn custom_flags(&mut self, flags: i32) {
+        self.custom_flags = flags;
+    }
+    pub fn mode(&mut self, mode: u32) {
+        self.mode = mode as mode_t;
+    }
+
+    fn get_access_mode(&self) -> io::Result<c_int> {
+        match (self.read, self.write, self.append) {
+            (true, false, false) => Ok(libc::O_RDONLY),
+            (false, true, false) => Ok(libc::O_WRONLY),
+            (true, true, false) => Ok(libc::O_RDWR),
+            (false, _, true) => Ok(libc::O_WRONLY | libc::O_APPEND),
+            (true, _, true) => Ok(libc::O_RDWR | libc::O_APPEND),
+            (false, false, false) => Err(Error::from_raw_os_error(libc::EINVAL)),
+        }
+    }
+
+    fn get_creation_mode(&self) -> io::Result<c_int> {
+        match (self.write, self.append) {
+            (true, false) => {}
+            (false, false) => {
+                if self.truncate || self.create || self.create_new {
+                    return Err(Error::from_raw_os_error(libc::EINVAL));
+                }
+            }
+            (_, true) => {
+                if self.truncate && !self.create_new {
+                    return Err(Error::from_raw_os_error(libc::EINVAL));
+                }
+            }
+        }
+
+        Ok(match (self.create, self.truncate, self.create_new) {
+            (false, false, false) => 0,
+            (true, false, false) => libc::O_CREAT,
+            (false, true, false) => libc::O_TRUNC,
+            (true, true, false) => libc::O_CREAT | libc::O_TRUNC,
+            (_, _, true) => libc::O_CREAT | libc::O_EXCL,
+        })
+    }
+}
+
+impl fmt::Debug for OpenOptions {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let OpenOptions { read, write, append, truncate, create, create_new, custom_flags, mode } =
+            self;
+        f.debug_struct("OpenOptions")
+            .field("read", read)
+            .field("write", write)
+            .field("append", append)
+            .field("truncate", truncate)
+            .field("create", create)
+            .field("create_new", create_new)
+            .field("custom_flags", custom_flags)
+            .field("mode", &Mode(*mode))
+            .finish()
+    }
+}
+
+impl File {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        run_path_with_cstr(path, &|path| File::open_c(path, opts))
+    }
+
+    pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
+        let flags = libc::O_CLOEXEC
+            | opts.get_access_mode()?
+            | opts.get_creation_mode()?
+            | (opts.custom_flags as c_int & !libc::O_ACCMODE);
+        // The third argument of `open64` is documented to have type `mode_t`. On
+        // some platforms (like macOS, where `open64` is actually `open`), `mode_t` is `u16`.
+        // 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(unsafe { FileDesc::from_raw_fd(fd) }))
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        let fd = self.as_raw_fd();
+
+        cfg_has_statx! {
+            if let Some(ret) = unsafe { try_statx(
+                fd,
+                c"".as_ptr() as *const c_char,
+                libc::AT_EMPTY_PATH | libc::AT_STATX_SYNC_AS_STAT,
+                libc::STATX_BASIC_STATS | libc::STATX_BTIME,
+            ) } {
+                return ret;
+            }
+        }
+
+        let mut stat: stat64 = unsafe { mem::zeroed() };
+        cvt(unsafe { fstat64(fd, &mut stat) })?;
+        Ok(FileAttr::from_stat64(stat))
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?;
+        return Ok(());
+
+        #[cfg(target_vendor = "apple")]
+        unsafe fn os_fsync(fd: c_int) -> c_int {
+            libc::fcntl(fd, libc::F_FULLFSYNC)
+        }
+        #[cfg(not(target_vendor = "apple"))]
+        unsafe fn os_fsync(fd: c_int) -> c_int {
+            libc::fsync(fd)
+        }
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?;
+        return Ok(());
+
+        #[cfg(target_vendor = "apple")]
+        unsafe fn os_datasync(fd: c_int) -> c_int {
+            libc::fcntl(fd, libc::F_FULLFSYNC)
+        }
+        #[cfg(any(
+            target_os = "freebsd",
+            target_os = "fuchsia",
+            target_os = "linux",
+            target_os = "android",
+            target_os = "netbsd",
+            target_os = "openbsd",
+            target_os = "nto",
+            target_os = "hurd",
+        ))]
+        unsafe fn os_datasync(fd: c_int) -> c_int {
+            libc::fdatasync(fd)
+        }
+        #[cfg(not(any(
+            target_os = "android",
+            target_os = "fuchsia",
+            target_os = "freebsd",
+            target_os = "linux",
+            target_os = "netbsd",
+            target_os = "openbsd",
+            target_os = "nto",
+            target_os = "hurd",
+            target_vendor = "apple",
+        )))]
+        unsafe fn os_datasync(fd: c_int) -> c_int {
+            libc::fsync(fd)
+        }
+    }
+
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    ))]
+    pub fn lock(&self) -> io::Result<()> {
+        cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX) })?;
+        return Ok(());
+    }
+
+    #[cfg(not(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    )))]
+    pub fn lock(&self) -> io::Result<()> {
+        Err(io::const_error!(io::ErrorKind::Unsupported, "lock() not supported"))
+    }
+
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    ))]
+    pub fn lock_shared(&self) -> io::Result<()> {
+        cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH) })?;
+        return Ok(());
+    }
+
+    #[cfg(not(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    )))]
+    pub fn lock_shared(&self) -> io::Result<()> {
+        Err(io::const_error!(io::ErrorKind::Unsupported, "lock_shared() not supported"))
+    }
+
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    ))]
+    pub fn try_lock(&self) -> io::Result<bool> {
+        let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) });
+        if let Err(ref err) = result {
+            if err.kind() == io::ErrorKind::WouldBlock {
+                return Ok(false);
+            }
+        }
+        result?;
+        return Ok(true);
+    }
+
+    #[cfg(not(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    )))]
+    pub fn try_lock(&self) -> io::Result<bool> {
+        Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock() not supported"))
+    }
+
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    ))]
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        let result = cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_SH | libc::LOCK_NB) });
+        if let Err(ref err) = result {
+            if err.kind() == io::ErrorKind::WouldBlock {
+                return Ok(false);
+            }
+        }
+        result?;
+        return Ok(true);
+    }
+
+    #[cfg(not(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    )))]
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        Err(io::const_error!(io::ErrorKind::Unsupported, "try_lock_shared() not supported"))
+    }
+
+    #[cfg(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    ))]
+    pub fn unlock(&self) -> io::Result<()> {
+        cvt(unsafe { libc::flock(self.as_raw_fd(), libc::LOCK_UN) })?;
+        return Ok(());
+    }
+
+    #[cfg(not(any(
+        target_os = "freebsd",
+        target_os = "fuchsia",
+        target_os = "linux",
+        target_os = "netbsd",
+        target_vendor = "apple",
+    )))]
+    pub fn unlock(&self) -> io::Result<()> {
+        Err(io::const_error!(io::ErrorKind::Unsupported, "unlock() not supported"))
+    }
+
+    pub fn truncate(&self, size: u64) -> io::Result<()> {
+        let size: off64_t =
+            size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
+        cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.0.read(buf)
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.0.read_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.0.is_read_vectored()
+    }
+
+    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+        self.0.read_at(buf, offset)
+    }
+
+    pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        self.0.read_buf(cursor)
+    }
+
+    pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
+        self.0.read_vectored_at(bufs, offset)
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        self.0.write(buf)
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.0.write_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.0.is_write_vectored()
+    }
+
+    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+        self.0.write_at(buf, offset)
+    }
+
+    pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
+        self.0.write_vectored_at(bufs, offset)
+    }
+
+    #[inline]
+    pub fn flush(&self) -> io::Result<()> {
+        Ok(())
+    }
+
+    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+        let (whence, pos) = match pos {
+            // Casting to `i64` is fine, too large values will end up as
+            // negative which will cause an error in `lseek64`.
+            SeekFrom::Start(off) => (libc::SEEK_SET, off as i64),
+            SeekFrom::End(off) => (libc::SEEK_END, off),
+            SeekFrom::Current(off) => (libc::SEEK_CUR, off),
+        };
+        let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
+        Ok(n as u64)
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        self.seek(SeekFrom::Current(0))
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        self.0.duplicate().map(File)
+    }
+
+    pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
+        cvt_r(|| unsafe { libc::fchmod(self.as_raw_fd(), perm.mode) })?;
+        Ok(())
+    }
+
+    pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+        #[cfg(not(any(
+            target_os = "redox",
+            target_os = "espidf",
+            target_os = "horizon",
+            target_os = "vxworks",
+            target_os = "nuttx",
+        )))]
+        let to_timespec = |time: Option<SystemTime>| match time {
+            Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts),
+            Some(time) if time > crate::sys::time::UNIX_EPOCH => Err(io::const_error!(
+                io::ErrorKind::InvalidInput,
+                "timestamp is too large to set as a file time",
+            )),
+            Some(_) => Err(io::const_error!(
+                io::ErrorKind::InvalidInput,
+                "timestamp is too small to set as a file time",
+            )),
+            None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }),
+        };
+        cfg_if::cfg_if! {
+            if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] {
+                // Redox doesn't appear to support `UTIME_OMIT`.
+                // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore
+                // the same as for Redox.
+                // `futimens` and `UTIME_OMIT` are a work in progress for vxworks.
+                let _ = times;
+                Err(io::const_error!(
+                    io::ErrorKind::Unsupported,
+                    "setting file times not supported",
+                ))
+            } else if #[cfg(target_vendor = "apple")] {
+                let mut buf = [mem::MaybeUninit::<libc::timespec>::uninit(); 3];
+                let mut num_times = 0;
+                let mut attrlist: libc::attrlist = unsafe { mem::zeroed() };
+                attrlist.bitmapcount = libc::ATTR_BIT_MAP_COUNT;
+                if times.created.is_some() {
+                    buf[num_times].write(to_timespec(times.created)?);
+                    num_times += 1;
+                    attrlist.commonattr |= libc::ATTR_CMN_CRTIME;
+                }
+                if times.modified.is_some() {
+                    buf[num_times].write(to_timespec(times.modified)?);
+                    num_times += 1;
+                    attrlist.commonattr |= libc::ATTR_CMN_MODTIME;
+                }
+                if times.accessed.is_some() {
+                    buf[num_times].write(to_timespec(times.accessed)?);
+                    num_times += 1;
+                    attrlist.commonattr |= libc::ATTR_CMN_ACCTIME;
+                }
+                cvt(unsafe { libc::fsetattrlist(
+                    self.as_raw_fd(),
+                    (&raw const attrlist).cast::<libc::c_void>().cast_mut(),
+                    buf.as_ptr().cast::<libc::c_void>().cast_mut(),
+                    num_times * size_of::<libc::timespec>(),
+                    0
+                ) })?;
+                Ok(())
+            } else if #[cfg(target_os = "android")] {
+                let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
+                // futimens requires Android API level 19
+                cvt(unsafe {
+                    weak!(fn futimens(c_int, *const libc::timespec) -> c_int);
+                    match futimens.get() {
+                        Some(futimens) => futimens(self.as_raw_fd(), times.as_ptr()),
+                        None => return Err(io::const_error!(
+                            io::ErrorKind::Unsupported,
+                            "setting file times requires Android API level >= 19",
+                        )),
+                    }
+                })?;
+                Ok(())
+            } else {
+                #[cfg(all(target_os = "linux", target_env = "gnu", target_pointer_width = "32", not(target_arch = "riscv32")))]
+                {
+                    use crate::sys::{time::__timespec64, weak::weak};
+
+                    // Added in glibc 2.34
+                    weak!(fn __futimens64(libc::c_int, *const __timespec64) -> libc::c_int);
+
+                    if let Some(futimens64) = __futimens64.get() {
+                        let to_timespec = |time: Option<SystemTime>| time.map(|time| time.t.to_timespec64())
+                            .unwrap_or(__timespec64::new(0, libc::UTIME_OMIT as _));
+                        let times = [to_timespec(times.accessed), to_timespec(times.modified)];
+                        cvt(unsafe { futimens64(self.as_raw_fd(), times.as_ptr()) })?;
+                        return Ok(());
+                    }
+                }
+                let times = [to_timespec(times.accessed)?, to_timespec(times.modified)?];
+                cvt(unsafe { libc::futimens(self.as_raw_fd(), times.as_ptr()) })?;
+                Ok(())
+            }
+        }
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder { mode: 0o777 }
+    }
+
+    pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+        run_path_with_cstr(p, &|p| cvt(unsafe { libc::mkdir(p.as_ptr(), self.mode) }).map(|_| ()))
+    }
+
+    pub fn set_mode(&mut self, mode: u32) {
+        self.mode = mode as mode_t;
+    }
+}
+
+impl fmt::Debug for DirBuilder {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let DirBuilder { mode } = self;
+        f.debug_struct("DirBuilder").field("mode", &Mode(*mode)).finish()
+    }
+}
+
+impl AsInner<FileDesc> for File {
+    #[inline]
+    fn as_inner(&self) -> &FileDesc {
+        &self.0
+    }
+}
+
+impl AsInnerMut<FileDesc> for File {
+    #[inline]
+    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 {
+    #[inline]
+    fn as_fd(&self) -> BorrowedFd<'_> {
+        self.0.as_fd()
+    }
+}
+
+impl AsRawFd for File {
+    #[inline]
+    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))
+    }
+}
+
+impl fmt::Debug for File {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        #[cfg(any(target_os = "linux", target_os = "illumos", target_os = "solaris"))]
+        fn get_path(fd: c_int) -> Option<PathBuf> {
+            let mut p = PathBuf::from("/proc/self/fd");
+            p.push(&fd.to_string());
+            readlink(&p).ok()
+        }
+
+        #[cfg(any(target_vendor = "apple", target_os = "netbsd"))]
+        fn get_path(fd: c_int) -> Option<PathBuf> {
+            // FIXME: The use of PATH_MAX is generally not encouraged, but it
+            // is inevitable in this case because Apple targets and NetBSD define `fcntl`
+            // with `F_GETPATH` in terms of `MAXPATHLEN`, and there are no
+            // alternatives. If a better method is invented, it should be used
+            // instead.
+            let mut buf = vec![0; libc::PATH_MAX as usize];
+            let n = unsafe { libc::fcntl(fd, libc::F_GETPATH, buf.as_ptr()) };
+            if n == -1 {
+                cfg_if::cfg_if! {
+                    if #[cfg(target_os = "netbsd")] {
+                        // fallback to procfs as last resort
+                        let mut p = PathBuf::from("/proc/self/fd");
+                        p.push(&fd.to_string());
+                        return readlink(&p).ok();
+                    } else {
+                        return None;
+                    }
+                }
+            }
+            let l = buf.iter().position(|&c| c == 0).unwrap();
+            buf.truncate(l as usize);
+            buf.shrink_to_fit();
+            Some(PathBuf::from(OsString::from_vec(buf)))
+        }
+
+        #[cfg(target_os = "freebsd")]
+        fn get_path(fd: c_int) -> Option<PathBuf> {
+            let info = Box::<libc::kinfo_file>::new_zeroed();
+            let mut info = unsafe { info.assume_init() };
+            info.kf_structsize = size_of::<libc::kinfo_file>() as libc::c_int;
+            let n = unsafe { libc::fcntl(fd, libc::F_KINFO, &mut *info) };
+            if n == -1 {
+                return None;
+            }
+            let buf = unsafe { CStr::from_ptr(info.kf_path.as_mut_ptr()).to_bytes().to_vec() };
+            Some(PathBuf::from(OsString::from_vec(buf)))
+        }
+
+        #[cfg(target_os = "vxworks")]
+        fn get_path(fd: c_int) -> Option<PathBuf> {
+            let mut buf = vec![0; libc::PATH_MAX as usize];
+            let n = unsafe { libc::ioctl(fd, libc::FIOGETNAME, buf.as_ptr()) };
+            if n == -1 {
+                return None;
+            }
+            let l = buf.iter().position(|&c| c == 0).unwrap();
+            buf.truncate(l as usize);
+            Some(PathBuf::from(OsString::from_vec(buf)))
+        }
+
+        #[cfg(not(any(
+            target_os = "linux",
+            target_os = "vxworks",
+            target_os = "freebsd",
+            target_os = "netbsd",
+            target_os = "illumos",
+            target_os = "solaris",
+            target_vendor = "apple",
+        )))]
+        fn get_path(_fd: c_int) -> Option<PathBuf> {
+            // FIXME(#24570): implement this for other Unix platforms
+            None
+        }
+
+        fn get_mode(fd: c_int) -> Option<(bool, bool)> {
+            let mode = unsafe { libc::fcntl(fd, libc::F_GETFL) };
+            if mode == -1 {
+                return None;
+            }
+            match mode & libc::O_ACCMODE {
+                libc::O_RDONLY => Some((true, false)),
+                libc::O_RDWR => Some((true, true)),
+                libc::O_WRONLY => Some((false, true)),
+                _ => None,
+            }
+        }
+
+        let fd = self.as_raw_fd();
+        let mut b = f.debug_struct("File");
+        b.field("fd", &fd);
+        if let Some(path) = get_path(fd) {
+            b.field("path", &path);
+        }
+        if let Some((read, write)) = get_mode(fd) {
+            b.field("read", &read).field("write", &write);
+        }
+        b.finish()
+    }
+}
+
+// Format in octal, followed by the mode format used in `ls -l`.
+//
+// References:
+//   https://pubs.opengroup.org/onlinepubs/009696899/utilities/ls.html
+//   https://www.gnu.org/software/libc/manual/html_node/Testing-File-Type.html
+//   https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
+//
+// Example:
+//   0o100664 (-rw-rw-r--)
+impl fmt::Debug for Mode {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let Self(mode) = *self;
+        write!(f, "0o{mode:06o}")?;
+
+        let entry_type = match mode & libc::S_IFMT {
+            libc::S_IFDIR => 'd',
+            libc::S_IFBLK => 'b',
+            libc::S_IFCHR => 'c',
+            libc::S_IFLNK => 'l',
+            libc::S_IFIFO => 'p',
+            libc::S_IFREG => '-',
+            _ => return Ok(()),
+        };
+
+        f.write_str(" (")?;
+        f.write_char(entry_type)?;
+
+        // Owner permissions
+        f.write_char(if mode & libc::S_IRUSR != 0 { 'r' } else { '-' })?;
+        f.write_char(if mode & libc::S_IWUSR != 0 { 'w' } else { '-' })?;
+        let owner_executable = mode & libc::S_IXUSR != 0;
+        let setuid = mode as c_int & libc::S_ISUID as c_int != 0;
+        f.write_char(match (owner_executable, setuid) {
+            (true, true) => 's',  // executable and setuid
+            (false, true) => 'S', // setuid
+            (true, false) => 'x', // executable
+            (false, false) => '-',
+        })?;
+
+        // Group permissions
+        f.write_char(if mode & libc::S_IRGRP != 0 { 'r' } else { '-' })?;
+        f.write_char(if mode & libc::S_IWGRP != 0 { 'w' } else { '-' })?;
+        let group_executable = mode & libc::S_IXGRP != 0;
+        let setgid = mode as c_int & libc::S_ISGID as c_int != 0;
+        f.write_char(match (group_executable, setgid) {
+            (true, true) => 's',  // executable and setgid
+            (false, true) => 'S', // setgid
+            (true, false) => 'x', // executable
+            (false, false) => '-',
+        })?;
+
+        // Other permissions
+        f.write_char(if mode & libc::S_IROTH != 0 { 'r' } else { '-' })?;
+        f.write_char(if mode & libc::S_IWOTH != 0 { 'w' } else { '-' })?;
+        let other_executable = mode & libc::S_IXOTH != 0;
+        let sticky = mode as c_int & libc::S_ISVTX as c_int != 0;
+        f.write_char(match (entry_type, other_executable, sticky) {
+            ('d', true, true) => 't',  // searchable and restricted deletion
+            ('d', false, true) => 'T', // restricted deletion
+            (_, true, _) => 'x',       // executable
+            (_, false, _) => '-',
+        })?;
+
+        f.write_char(')')
+    }
+}
+
+pub fn readdir(path: &Path) -> io::Result<ReadDir> {
+    let ptr = run_path_with_cstr(path, &|p| unsafe { Ok(libc::opendir(p.as_ptr())) })?;
+    if ptr.is_null() {
+        Err(Error::last_os_error())
+    } else {
+        let root = path.to_path_buf();
+        let inner = InnerReadDir { dirp: Dir(ptr), root };
+        Ok(ReadDir::new(inner))
+    }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+    run_path_with_cstr(p, &|p| cvt(unsafe { libc::unlink(p.as_ptr()) }).map(|_| ()))
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+    run_path_with_cstr(old, &|old| {
+        run_path_with_cstr(new, &|new| {
+            cvt(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) }).map(|_| ())
+        })
+    })
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+    run_path_with_cstr(p, &|p| cvt_r(|| unsafe { libc::chmod(p.as_ptr(), perm.mode) }).map(|_| ()))
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+    run_path_with_cstr(p, &|p| cvt(unsafe { libc::rmdir(p.as_ptr()) }).map(|_| ()))
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+    run_path_with_cstr(p, &|c_path| {
+        let p = c_path.as_ptr();
+
+        let mut buf = Vec::with_capacity(256);
+
+        loop {
+            let buf_read =
+                cvt(unsafe { libc::readlink(p, buf.as_mut_ptr() as *mut _, buf.capacity()) })?
+                    as usize;
+
+            unsafe {
+                buf.set_len(buf_read);
+            }
+
+            if buf_read != buf.capacity() {
+                buf.shrink_to_fit();
+
+                return Ok(PathBuf::from(OsString::from_vec(buf)));
+            }
+
+            // Trigger the internal buffer resizing logic of `Vec` by requiring
+            // more space than the current capacity. The length is guaranteed to be
+            // the same as the capacity due to the if statement above.
+            buf.reserve(1);
+        }
+    })
+}
+
+pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
+    run_path_with_cstr(original, &|original| {
+        run_path_with_cstr(link, &|link| {
+            cvt(unsafe { libc::symlink(original.as_ptr(), link.as_ptr()) }).map(|_| ())
+        })
+    })
+}
+
+pub fn link(original: &Path, link: &Path) -> io::Result<()> {
+    run_path_with_cstr(original, &|original| {
+        run_path_with_cstr(link, &|link| {
+            cfg_if::cfg_if! {
+                if #[cfg(any(target_os = "vxworks", target_os = "redox", target_os = "android", target_os = "espidf", target_os = "horizon", target_os = "vita", target_env = "nto70"))] {
+                    // VxWorks, Redox and ESP-IDF lack `linkat`, so use `link` instead. POSIX leaves
+                    // it implementation-defined whether `link` follows symlinks, so rely on the
+                    // `symlink_hard_link` test in library/std/src/fs/tests.rs to check the behavior.
+                    // Android has `linkat` on newer versions, but we happen to know `link`
+                    // always has the correct behavior, so it's here as well.
+                    cvt(unsafe { libc::link(original.as_ptr(), link.as_ptr()) })?;
+                } else {
+                    // Where we can, use `linkat` instead of `link`; see the comment above
+                    // this one for details on why.
+                    cvt(unsafe { libc::linkat(libc::AT_FDCWD, original.as_ptr(), libc::AT_FDCWD, link.as_ptr(), 0) })?;
+                }
+            }
+            Ok(())
+        })
+    })
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+    run_path_with_cstr(p, &|p| {
+        cfg_has_statx! {
+            if let Some(ret) = unsafe { try_statx(
+                libc::AT_FDCWD,
+                p.as_ptr(),
+                libc::AT_STATX_SYNC_AS_STAT,
+                libc::STATX_BASIC_STATS | libc::STATX_BTIME,
+            ) } {
+                return ret;
+            }
+        }
+
+        let mut stat: stat64 = unsafe { mem::zeroed() };
+        cvt(unsafe { stat64(p.as_ptr(), &mut stat) })?;
+        Ok(FileAttr::from_stat64(stat))
+    })
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+    run_path_with_cstr(p, &|p| {
+        cfg_has_statx! {
+            if let Some(ret) = unsafe { try_statx(
+                libc::AT_FDCWD,
+                p.as_ptr(),
+                libc::AT_SYMLINK_NOFOLLOW | libc::AT_STATX_SYNC_AS_STAT,
+                libc::STATX_BASIC_STATS | libc::STATX_BTIME,
+            ) } {
+                return ret;
+            }
+        }
+
+        let mut stat: stat64 = unsafe { mem::zeroed() };
+        cvt(unsafe { lstat64(p.as_ptr(), &mut stat) })?;
+        Ok(FileAttr::from_stat64(stat))
+    })
+}
+
+pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
+    let r = run_path_with_cstr(p, &|path| unsafe {
+        Ok(libc::realpath(path.as_ptr(), ptr::null_mut()))
+    })?;
+    if r.is_null() {
+        return Err(io::Error::last_os_error());
+    }
+    Ok(PathBuf::from(OsString::from_vec(unsafe {
+        let buf = CStr::from_ptr(r).to_bytes().to_vec();
+        libc::free(r as *mut _);
+        buf
+    })))
+}
+
+fn open_from(from: &Path) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
+    use crate::fs::File;
+    use crate::sys::fs::common::NOT_FILE_ERROR;
+
+    let reader = File::open(from)?;
+    let metadata = reader.metadata()?;
+    if !metadata.is_file() {
+        return Err(NOT_FILE_ERROR);
+    }
+    Ok((reader, metadata))
+}
+
+#[cfg(target_os = "espidf")]
+fn open_to_and_set_permissions(
+    to: &Path,
+    _reader_metadata: &crate::fs::Metadata,
+) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
+    use crate::fs::OpenOptions;
+    let writer = OpenOptions::new().open(to)?;
+    let writer_metadata = writer.metadata()?;
+    Ok((writer, writer_metadata))
+}
+
+#[cfg(not(target_os = "espidf"))]
+fn open_to_and_set_permissions(
+    to: &Path,
+    reader_metadata: &crate::fs::Metadata,
+) -> io::Result<(crate::fs::File, crate::fs::Metadata)> {
+    use crate::fs::OpenOptions;
+    use crate::os::unix::fs::{OpenOptionsExt, PermissionsExt};
+
+    let perm = reader_metadata.permissions();
+    let writer = OpenOptions::new()
+        // create the file with the correct mode right away
+        .mode(perm.mode())
+        .write(true)
+        .create(true)
+        .truncate(true)
+        .open(to)?;
+    let writer_metadata = writer.metadata()?;
+    // fchmod is broken on vita
+    #[cfg(not(target_os = "vita"))]
+    if writer_metadata.is_file() {
+        // Set the correct file permissions, in case the file already existed.
+        // Don't set the permissions on already existing non-files like
+        // pipes/FIFOs or device nodes.
+        writer.set_permissions(perm)?;
+    }
+    Ok((writer, writer_metadata))
+}
+
+mod cfm {
+    use crate::fs::{File, Metadata};
+    use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, Read, Result, Write};
+
+    #[allow(dead_code)]
+    pub struct CachedFileMetadata(pub File, pub Metadata);
+
+    impl Read for CachedFileMetadata {
+        fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
+            self.0.read(buf)
+        }
+        fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
+            self.0.read_vectored(bufs)
+        }
+        fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> Result<()> {
+            self.0.read_buf(cursor)
+        }
+        #[inline]
+        fn is_read_vectored(&self) -> bool {
+            self.0.is_read_vectored()
+        }
+        fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
+            self.0.read_to_end(buf)
+        }
+        fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
+            self.0.read_to_string(buf)
+        }
+    }
+    impl Write for CachedFileMetadata {
+        fn write(&mut self, buf: &[u8]) -> Result<usize> {
+            self.0.write(buf)
+        }
+        fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
+            self.0.write_vectored(bufs)
+        }
+        #[inline]
+        fn is_write_vectored(&self) -> bool {
+            self.0.is_write_vectored()
+        }
+        #[inline]
+        fn flush(&mut self) -> Result<()> {
+            self.0.flush()
+        }
+    }
+}
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub(crate) use cfm::CachedFileMetadata;
+
+#[cfg(not(target_vendor = "apple"))]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    let (reader, reader_metadata) = open_from(from)?;
+    let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
+
+    io::copy(
+        &mut cfm::CachedFileMetadata(reader, reader_metadata),
+        &mut cfm::CachedFileMetadata(writer, writer_metadata),
+    )
+}
+
+#[cfg(target_vendor = "apple")]
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    const COPYFILE_ALL: libc::copyfile_flags_t = libc::COPYFILE_METADATA | libc::COPYFILE_DATA;
+
+    struct FreeOnDrop(libc::copyfile_state_t);
+    impl Drop for FreeOnDrop {
+        fn drop(&mut self) {
+            // The code below ensures that `FreeOnDrop` is never a null pointer
+            unsafe {
+                // `copyfile_state_free` returns -1 if the `to` or `from` files
+                // cannot be closed. However, this is not considered an error.
+                libc::copyfile_state_free(self.0);
+            }
+        }
+    }
+
+    let (reader, reader_metadata) = open_from(from)?;
+
+    let clonefile_result = run_path_with_cstr(to, &|to| {
+        cvt(unsafe { libc::fclonefileat(reader.as_raw_fd(), libc::AT_FDCWD, to.as_ptr(), 0) })
+    });
+    match clonefile_result {
+        Ok(_) => return Ok(reader_metadata.len()),
+        Err(e) => match e.raw_os_error() {
+            // `fclonefileat` will fail on non-APFS volumes, if the
+            // destination already exists, or if the source and destination
+            // are on different devices. In all these cases `fcopyfile`
+            // should succeed.
+            Some(libc::ENOTSUP) | Some(libc::EEXIST) | Some(libc::EXDEV) => (),
+            _ => return Err(e),
+        },
+    }
+
+    // Fall back to using `fcopyfile` if `fclonefileat` does not succeed.
+    let (writer, writer_metadata) = open_to_and_set_permissions(to, &reader_metadata)?;
+
+    // We ensure that `FreeOnDrop` never contains a null pointer so it is
+    // always safe to call `copyfile_state_free`
+    let state = unsafe {
+        let state = libc::copyfile_state_alloc();
+        if state.is_null() {
+            return Err(crate::io::Error::last_os_error());
+        }
+        FreeOnDrop(state)
+    };
+
+    let flags = if writer_metadata.is_file() { COPYFILE_ALL } else { libc::COPYFILE_DATA };
+
+    cvt(unsafe { libc::fcopyfile(reader.as_raw_fd(), writer.as_raw_fd(), state.0, flags) })?;
+
+    let mut bytes_copied: libc::off_t = 0;
+    cvt(unsafe {
+        libc::copyfile_state_get(
+            state.0,
+            libc::COPYFILE_STATE_COPIED as u32,
+            (&raw mut bytes_copied) as *mut libc::c_void,
+        )
+    })?;
+    Ok(bytes_copied as u64)
+}
+
+pub fn chown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
+    run_path_with_cstr(path, &|path| {
+        cvt(unsafe { libc::chown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
+            .map(|_| ())
+    })
+}
+
+pub fn fchown(fd: c_int, uid: u32, gid: u32) -> io::Result<()> {
+    cvt(unsafe { libc::fchown(fd, uid as libc::uid_t, gid as libc::gid_t) })?;
+    Ok(())
+}
+
+#[cfg(not(target_os = "vxworks"))]
+pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
+    run_path_with_cstr(path, &|path| {
+        cvt(unsafe { libc::lchown(path.as_ptr(), uid as libc::uid_t, gid as libc::gid_t) })
+            .map(|_| ())
+    })
+}
+
+#[cfg(target_os = "vxworks")]
+pub fn lchown(path: &Path, uid: u32, gid: u32) -> io::Result<()> {
+    let (_, _, _) = (path, uid, gid);
+    Err(io::const_error!(io::ErrorKind::Unsupported, "lchown not supported by vxworks"))
+}
+
+#[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))]
+pub fn chroot(dir: &Path) -> io::Result<()> {
+    run_path_with_cstr(dir, &|dir| cvt(unsafe { libc::chroot(dir.as_ptr()) }).map(|_| ()))
+}
+
+#[cfg(target_os = "vxworks")]
+pub fn chroot(dir: &Path) -> io::Result<()> {
+    let _ = dir;
+    Err(io::const_error!(io::ErrorKind::Unsupported, "chroot not supported by vxworks"))
+}
+
+pub use remove_dir_impl::remove_dir_all;
+
+// Fallback for REDOX, ESP-ID, Horizon, Vita, Vxworks and Miri
+#[cfg(any(
+    target_os = "redox",
+    target_os = "espidf",
+    target_os = "horizon",
+    target_os = "vita",
+    target_os = "nto",
+    target_os = "vxworks",
+    miri
+))]
+mod remove_dir_impl {
+    pub use crate::sys::fs::common::remove_dir_all;
+}
+
+// Modern implementation using openat(), unlinkat() and fdopendir()
+#[cfg(not(any(
+    target_os = "redox",
+    target_os = "espidf",
+    target_os = "horizon",
+    target_os = "vita",
+    target_os = "nto",
+    target_os = "vxworks",
+    miri
+)))]
+mod remove_dir_impl {
+    #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
+    use libc::{fdopendir, openat, unlinkat};
+    #[cfg(all(target_os = "linux", target_env = "gnu"))]
+    use libc::{fdopendir, openat64 as openat, unlinkat};
+
+    use super::{Dir, DirEntry, InnerReadDir, ReadDir, lstat};
+    use crate::ffi::CStr;
+    use crate::io;
+    use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
+    use crate::os::unix::prelude::{OwnedFd, RawFd};
+    use crate::path::{Path, PathBuf};
+    use crate::sys::common::small_c_string::run_path_with_cstr;
+    use crate::sys::{cvt, cvt_r};
+    use crate::sys_common::ignore_notfound;
+
+    pub fn openat_nofollow_dironly(parent_fd: Option<RawFd>, p: &CStr) -> io::Result<OwnedFd> {
+        let fd = cvt_r(|| unsafe {
+            openat(
+                parent_fd.unwrap_or(libc::AT_FDCWD),
+                p.as_ptr(),
+                libc::O_CLOEXEC | libc::O_RDONLY | libc::O_NOFOLLOW | libc::O_DIRECTORY,
+            )
+        })?;
+        Ok(unsafe { OwnedFd::from_raw_fd(fd) })
+    }
+
+    fn fdreaddir(dir_fd: OwnedFd) -> io::Result<(ReadDir, RawFd)> {
+        let ptr = unsafe { fdopendir(dir_fd.as_raw_fd()) };
+        if ptr.is_null() {
+            return Err(io::Error::last_os_error());
+        }
+        let dirp = Dir(ptr);
+        // file descriptor is automatically closed by libc::closedir() now, so give up ownership
+        let new_parent_fd = dir_fd.into_raw_fd();
+        // a valid root is not needed because we do not call any functions involving the full path
+        // of the `DirEntry`s.
+        let dummy_root = PathBuf::new();
+        let inner = InnerReadDir { dirp, root: dummy_root };
+        Ok((ReadDir::new(inner), new_parent_fd))
+    }
+
+    #[cfg(any(
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "haiku",
+        target_os = "vxworks",
+        target_os = "aix",
+    ))]
+    fn is_dir(_ent: &DirEntry) -> Option<bool> {
+        None
+    }
+
+    #[cfg(not(any(
+        target_os = "solaris",
+        target_os = "illumos",
+        target_os = "haiku",
+        target_os = "vxworks",
+        target_os = "aix",
+    )))]
+    fn is_dir(ent: &DirEntry) -> Option<bool> {
+        match ent.entry.d_type {
+            libc::DT_UNKNOWN => None,
+            libc::DT_DIR => Some(true),
+            _ => Some(false),
+        }
+    }
+
+    fn is_enoent(result: &io::Result<()>) -> bool {
+        if let Err(err) = result
+            && matches!(err.raw_os_error(), Some(libc::ENOENT))
+        {
+            true
+        } else {
+            false
+        }
+    }
+
+    fn remove_dir_all_recursive(parent_fd: Option<RawFd>, path: &CStr) -> io::Result<()> {
+        // try opening as directory
+        let fd = match openat_nofollow_dironly(parent_fd, &path) {
+            Err(err) if matches!(err.raw_os_error(), Some(libc::ENOTDIR | libc::ELOOP)) => {
+                // not a directory - don't traverse further
+                // (for symlinks, older Linux kernels may return ELOOP instead of ENOTDIR)
+                return match parent_fd {
+                    // unlink...
+                    Some(parent_fd) => {
+                        cvt(unsafe { unlinkat(parent_fd, path.as_ptr(), 0) }).map(drop)
+                    }
+                    // ...unless this was supposed to be the deletion root directory
+                    None => Err(err),
+                };
+            }
+            result => result?,
+        };
+
+        // open the directory passing ownership of the fd
+        let (dir, fd) = fdreaddir(fd)?;
+        for child in dir {
+            let child = child?;
+            let child_name = child.name_cstr();
+            // we need an inner try block, because if one of these
+            // directories has already been deleted, then we need to
+            // continue the loop, not return ok.
+            let result: io::Result<()> = try {
+                match is_dir(&child) {
+                    Some(true) => {
+                        remove_dir_all_recursive(Some(fd), child_name)?;
+                    }
+                    Some(false) => {
+                        cvt(unsafe { unlinkat(fd, child_name.as_ptr(), 0) })?;
+                    }
+                    None => {
+                        // POSIX specifies that calling unlink()/unlinkat(..., 0) on a directory can succeed
+                        // if the process has the appropriate privileges. This however can causing orphaned
+                        // directories requiring an fsck e.g. on Solaris and Illumos. So we try recursing
+                        // into it first instead of trying to unlink() it.
+                        remove_dir_all_recursive(Some(fd), child_name)?;
+                    }
+                }
+            };
+            if result.is_err() && !is_enoent(&result) {
+                return result;
+            }
+        }
+
+        // unlink the directory after removing its contents
+        ignore_notfound(cvt(unsafe {
+            unlinkat(parent_fd.unwrap_or(libc::AT_FDCWD), path.as_ptr(), libc::AT_REMOVEDIR)
+        }))?;
+        Ok(())
+    }
+
+    fn remove_dir_all_modern(p: &Path) -> io::Result<()> {
+        // We cannot just call remove_dir_all_recursive() here because that would not delete a passed
+        // symlink. No need to worry about races, because remove_dir_all_recursive() does not recurse
+        // into symlinks.
+        let attr = lstat(p)?;
+        if attr.file_type().is_symlink() {
+            crate::fs::remove_file(p)
+        } else {
+            run_path_with_cstr(p, &|p| remove_dir_all_recursive(None, &p))
+        }
+    }
+
+    pub fn remove_dir_all(p: &Path) -> io::Result<()> {
+        remove_dir_all_modern(p)
+    }
+}
diff --git a/library/std/src/sys/fs/unix/tests.rs b/library/std/src/sys/fs/unix/tests.rs
new file mode 100644
index 00000000000..8875a318db7
--- /dev/null
+++ b/library/std/src/sys/fs/unix/tests.rs
@@ -0,0 +1,71 @@
+use crate::sys::fs::FilePermissions;
+
+#[test]
+fn test_debug_permissions() {
+    for (expected, mode) in [
+        // typical directory
+        ("FilePermissions { mode: 0o040775 (drwxrwxr-x) }", 0o04_0775),
+        // typical text file
+        ("FilePermissions { mode: 0o100664 (-rw-rw-r--) }", 0o10_0664),
+        // setuid executable (/usr/bin/doas)
+        ("FilePermissions { mode: 0o104755 (-rwsr-xr-x) }", 0o10_4755),
+        // char device (/dev/zero)
+        ("FilePermissions { mode: 0o020666 (crw-rw-rw-) }", 0o02_0666),
+        // block device (/dev/vda)
+        ("FilePermissions { mode: 0o060660 (brw-rw----) }", 0o06_0660),
+        // symbolic link
+        ("FilePermissions { mode: 0o120777 (lrwxrwxrwx) }", 0o12_0777),
+        // fifo
+        ("FilePermissions { mode: 0o010664 (prw-rw-r--) }", 0o01_0664),
+        // none
+        ("FilePermissions { mode: 0o100000 (----------) }", 0o10_0000),
+        // unrecognized
+        ("FilePermissions { mode: 0o000001 }", 1),
+    ] {
+        assert_eq!(format!("{:?}", FilePermissions { mode }), expected);
+    }
+
+    for (expected, mode) in [
+        // owner readable
+        ("FilePermissions { mode: 0o100400 (-r--------) }", libc::S_IRUSR),
+        // owner writable
+        ("FilePermissions { mode: 0o100200 (--w-------) }", libc::S_IWUSR),
+        // owner executable
+        ("FilePermissions { mode: 0o100100 (---x------) }", libc::S_IXUSR),
+        // setuid
+        ("FilePermissions { mode: 0o104000 (---S------) }", libc::S_ISUID),
+        // owner executable and setuid
+        ("FilePermissions { mode: 0o104100 (---s------) }", libc::S_IXUSR | libc::S_ISUID),
+        // group readable
+        ("FilePermissions { mode: 0o100040 (----r-----) }", libc::S_IRGRP),
+        // group writable
+        ("FilePermissions { mode: 0o100020 (-----w----) }", libc::S_IWGRP),
+        // group executable
+        ("FilePermissions { mode: 0o100010 (------x---) }", libc::S_IXGRP),
+        // setgid
+        ("FilePermissions { mode: 0o102000 (------S---) }", libc::S_ISGID),
+        // group executable and setgid
+        ("FilePermissions { mode: 0o102010 (------s---) }", libc::S_IXGRP | libc::S_ISGID),
+        // other readable
+        ("FilePermissions { mode: 0o100004 (-------r--) }", libc::S_IROTH),
+        // other writeable
+        ("FilePermissions { mode: 0o100002 (--------w-) }", libc::S_IWOTH),
+        // other executable
+        ("FilePermissions { mode: 0o100001 (---------x) }", libc::S_IXOTH),
+        // sticky
+        ("FilePermissions { mode: 0o101000 (----------) }", libc::S_ISVTX),
+        // other executable and sticky
+        ("FilePermissions { mode: 0o101001 (---------x) }", libc::S_IXOTH | libc::S_ISVTX),
+    ] {
+        assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFREG | mode }), expected);
+    }
+
+    for (expected, mode) in [
+        // restricted deletion ("sticky") flag is set, and search permission is not granted to others
+        ("FilePermissions { mode: 0o041000 (d--------T) }", libc::S_ISVTX),
+        // sticky and searchable
+        ("FilePermissions { mode: 0o041001 (d--------t) }", libc::S_ISVTX | libc::S_IXOTH),
+    ] {
+        assert_eq!(format!("{:?}", FilePermissions { mode: libc::S_IFDIR | mode }), expected);
+    }
+}
diff --git a/library/std/src/sys/fs/unsupported.rs b/library/std/src/sys/fs/unsupported.rs
new file mode 100644
index 00000000000..45e93deffa3
--- /dev/null
+++ b/library/std/src/sys/fs/unsupported.rs
@@ -0,0 +1,348 @@
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
+use crate::path::{Path, PathBuf};
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+
+pub struct File(!);
+
+pub struct FileAttr(!);
+
+pub struct ReadDir(!);
+
+pub struct DirEntry(!);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {}
+
+pub struct FilePermissions(!);
+
+pub struct FileType(!);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        self.0
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        self.0
+    }
+
+    pub fn file_type(&self) -> FileType {
+        self.0
+    }
+
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        self.0
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        self.0
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        self.0
+    }
+}
+
+impl Clone for FileAttr {
+    fn clone(&self) -> FileAttr {
+        self.0
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        self.0
+    }
+
+    pub fn set_readonly(&mut self, _readonly: bool) {
+        self.0
+    }
+}
+
+impl Clone for FilePermissions {
+    fn clone(&self) -> FilePermissions {
+        self.0
+    }
+}
+
+impl PartialEq for FilePermissions {
+    fn eq(&self, _other: &FilePermissions) -> bool {
+        self.0
+    }
+}
+
+impl Eq for FilePermissions {}
+
+impl fmt::Debug for FilePermissions {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+impl FileTimes {
+    pub fn set_accessed(&mut self, _t: SystemTime) {}
+    pub fn set_modified(&mut self, _t: SystemTime) {}
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        self.0
+    }
+
+    pub fn is_file(&self) -> bool {
+        self.0
+    }
+
+    pub fn is_symlink(&self) -> bool {
+        self.0
+    }
+}
+
+impl Clone for FileType {
+    fn clone(&self) -> FileType {
+        self.0
+    }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+    fn eq(&self, _other: &FileType) -> bool {
+        self.0
+    }
+}
+
+impl Eq for FileType {}
+
+impl Hash for FileType {
+    fn hash<H: Hasher>(&self, _h: &mut H) {
+        self.0
+    }
+}
+
+impl fmt::Debug for FileType {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        self.0
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        self.0
+    }
+
+    pub fn file_name(&self) -> OsString {
+        self.0
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        self.0
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        self.0
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions {}
+    }
+
+    pub fn read(&mut self, _read: bool) {}
+    pub fn write(&mut self, _write: bool) {}
+    pub fn append(&mut self, _append: bool) {}
+    pub fn truncate(&mut self, _truncate: bool) {}
+    pub fn create(&mut self, _create: bool) {}
+    pub fn create_new(&mut self, _create_new: bool) {}
+}
+
+impl File {
+    pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
+        unsupported()
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        self.0
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn lock(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn lock_shared(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn try_lock(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        self.0
+    }
+
+    pub fn unlock(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn truncate(&self, _size: u64) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn read(&self, _buf: &mut [u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn read_vectored(&self, _bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn is_read_vectored(&self) -> bool {
+        self.0
+    }
+
+    pub fn read_buf(&self, _cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn write_vectored(&self, _bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.0
+    }
+
+    pub fn is_write_vectored(&self) -> bool {
+        self.0
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+        self.0
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        self.0
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        self.0
+    }
+
+    pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+        self.0
+    }
+
+    pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
+        self.0
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder {}
+    }
+
+    pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
+        unsupported()
+    }
+}
+
+impl fmt::Debug for File {
+    fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0
+    }
+}
+
+pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
+    unsupported()
+}
+
+pub fn unlink(_p: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
+    match perm.0 {}
+}
+
+pub fn rmdir(_p: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn exists(_path: &Path) -> io::Result<bool> {
+    unsupported()
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn link(_src: &Path, _dst: &Path) -> io::Result<()> {
+    unsupported()
+}
+
+pub fn stat(_p: &Path) -> io::Result<FileAttr> {
+    unsupported()
+}
+
+pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
+    unsupported()
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+    unsupported()
+}
+
+pub fn copy(_from: &Path, _to: &Path) -> io::Result<u64> {
+    unsupported()
+}
diff --git a/library/std/src/sys/fs/wasi.rs b/library/std/src/sys/fs/wasi.rs
new file mode 100644
index 00000000000..773040571bc
--- /dev/null
+++ b/library/std/src/sys/fs/wasi.rs
@@ -0,0 +1,870 @@
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
+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::sync::Arc;
+use crate::sys::common::small_c_string::run_path_with_cstr;
+use crate::sys::fd::WasiFd;
+pub use crate::sys::fs::common::exists;
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+use crate::sys_common::{AsInner, FromInner, IntoInner, ignore_notfound};
+use crate::{fmt, iter, ptr};
+
+pub struct File {
+    fd: WasiFd,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+    meta: wasi::Filestat,
+}
+
+pub struct ReadDir {
+    inner: Arc<ReadDirInner>,
+    state: ReadDirState,
+}
+
+enum ReadDirState {
+    /// Fill `buf` with `buf.len()` bytes starting from `next_read_offset`.
+    FillBuffer {
+        next_read_offset: wasi::Dircookie,
+        buf: Vec<u8>,
+    },
+    ProcessEntry {
+        buf: Vec<u8>,
+        next_read_offset: Option<wasi::Dircookie>,
+        offset: usize,
+    },
+    /// There is no more data to get in [`Self::FillBuffer`]; keep returning
+    /// entries via ProcessEntry until `buf` is exhausted.
+    RunUntilExhaustion {
+        buf: Vec<u8>,
+        offset: usize,
+    },
+    Done,
+}
+
+struct ReadDirInner {
+    root: PathBuf,
+    dir: File,
+}
+
+pub struct DirEntry {
+    meta: wasi::Dirent,
+    name: Vec<u8>,
+    inner: Arc<ReadDirInner>,
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct OpenOptions {
+    read: bool,
+    write: bool,
+    append: bool,
+    dirflags: wasi::Lookupflags,
+    fdflags: wasi::Fdflags,
+    oflags: wasi::Oflags,
+    rights_base: Option<wasi::Rights>,
+    rights_inheriting: Option<wasi::Rights>,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions {
+    readonly: bool,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {
+    accessed: Option<SystemTime>,
+    modified: Option<SystemTime>,
+}
+
+#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
+pub struct FileType {
+    bits: wasi::Filetype,
+}
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        self.meta.size
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        // not currently implemented in wasi yet
+        FilePermissions { readonly: false }
+    }
+
+    pub fn file_type(&self) -> FileType {
+        FileType { bits: self.meta.filetype }
+    }
+
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_wasi_timestamp(self.meta.mtim))
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_wasi_timestamp(self.meta.atim))
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from_wasi_timestamp(self.meta.ctim))
+    }
+
+    pub(crate) fn as_wasi(&self) -> &wasi::Filestat {
+        &self.meta
+    }
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        self.readonly
+    }
+
+    pub fn set_readonly(&mut self, readonly: bool) {
+        self.readonly = readonly;
+    }
+}
+
+impl FileTimes {
+    pub fn set_accessed(&mut self, t: SystemTime) {
+        self.accessed = Some(t);
+    }
+
+    pub fn set_modified(&mut self, t: SystemTime) {
+        self.modified = Some(t);
+    }
+}
+
+impl FileType {
+    pub fn is_dir(&self) -> bool {
+        self.bits == wasi::FILETYPE_DIRECTORY
+    }
+
+    pub fn is_file(&self) -> bool {
+        self.bits == wasi::FILETYPE_REGULAR_FILE
+    }
+
+    pub fn is_symlink(&self) -> bool {
+        self.bits == wasi::FILETYPE_SYMBOLIC_LINK
+    }
+
+    pub(crate) fn bits(&self) -> wasi::Filetype {
+        self.bits
+    }
+}
+
+impl ReadDir {
+    fn new(dir: File, root: PathBuf) -> ReadDir {
+        ReadDir {
+            inner: Arc::new(ReadDirInner { dir, root }),
+            state: ReadDirState::FillBuffer { next_read_offset: 0, buf: vec![0; 128] },
+        }
+    }
+}
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("ReadDir").finish_non_exhaustive()
+    }
+}
+
+impl core::iter::FusedIterator for ReadDir {}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        match &mut self.state {
+            ReadDirState::FillBuffer { next_read_offset, buf } => {
+                let result = self.inner.dir.fd.readdir(buf, *next_read_offset);
+                match result {
+                    Ok(read_bytes) => {
+                        if read_bytes < buf.len() {
+                            buf.truncate(read_bytes);
+                            self.state =
+                                ReadDirState::RunUntilExhaustion { buf: mem::take(buf), offset: 0 };
+                        } else {
+                            debug_assert_eq!(read_bytes, buf.len());
+                            self.state = ReadDirState::ProcessEntry {
+                                buf: mem::take(buf),
+                                offset: 0,
+                                next_read_offset: Some(*next_read_offset),
+                            };
+                        }
+                        self.next()
+                    }
+                    Err(e) => {
+                        self.state = ReadDirState::Done;
+                        return Some(Err(e));
+                    }
+                }
+            }
+            ReadDirState::ProcessEntry { buf, next_read_offset, offset } => {
+                let contents = &buf[*offset..];
+                const DIRENT_SIZE: usize = size_of::<wasi::Dirent>();
+                if contents.len() >= DIRENT_SIZE {
+                    let (dirent, data) = contents.split_at(DIRENT_SIZE);
+                    let dirent =
+                        unsafe { ptr::read_unaligned(dirent.as_ptr() as *const wasi::Dirent) };
+                    // If the file name was truncated, then we need to reinvoke
+                    // `readdir` so we truncate our buffer to start over and reread this
+                    // descriptor.
+                    if data.len() < dirent.d_namlen as usize {
+                        if buf.len() < dirent.d_namlen as usize + DIRENT_SIZE {
+                            buf.resize(dirent.d_namlen as usize + DIRENT_SIZE, 0);
+                        }
+                        if let Some(next_read_offset) = *next_read_offset {
+                            self.state =
+                                ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) };
+                        } else {
+                            self.state = ReadDirState::Done;
+                        }
+
+                        return self.next();
+                    }
+                    next_read_offset.as_mut().map(|cookie| {
+                        *cookie = dirent.d_next;
+                    });
+                    *offset = *offset + DIRENT_SIZE + dirent.d_namlen as usize;
+
+                    let name = &data[..(dirent.d_namlen as usize)];
+
+                    // These names are skipped on all other platforms, so let's skip
+                    // them here too
+                    if name == b"." || name == b".." {
+                        return self.next();
+                    }
+
+                    return Some(Ok(DirEntry {
+                        meta: dirent,
+                        name: name.to_vec(),
+                        inner: self.inner.clone(),
+                    }));
+                } else if let Some(next_read_offset) = *next_read_offset {
+                    self.state = ReadDirState::FillBuffer { next_read_offset, buf: mem::take(buf) };
+                } else {
+                    self.state = ReadDirState::Done;
+                }
+                self.next()
+            }
+            ReadDirState::RunUntilExhaustion { buf, offset } => {
+                if *offset >= buf.len() {
+                    self.state = ReadDirState::Done;
+                } else {
+                    self.state = ReadDirState::ProcessEntry {
+                        buf: mem::take(buf),
+                        offset: *offset,
+                        next_read_offset: None,
+                    };
+                }
+
+                self.next()
+            }
+            ReadDirState::Done => None,
+        }
+    }
+}
+
+impl DirEntry {
+    pub fn path(&self) -> PathBuf {
+        let name = OsStr::from_bytes(&self.name);
+        self.inner.root.join(name)
+    }
+
+    pub fn file_name(&self) -> OsString {
+        OsString::from_vec(self.name.clone())
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        metadata_at(&self.inner.dir.fd, 0, OsStr::from_bytes(&self.name).as_ref())
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        Ok(FileType { bits: self.meta.d_type })
+    }
+
+    pub fn ino(&self) -> wasi::Inode {
+        self.meta.d_ino
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        let mut base = OpenOptions::default();
+        base.dirflags = wasi::LOOKUPFLAGS_SYMLINK_FOLLOW;
+        base
+    }
+
+    pub fn read(&mut self, read: bool) {
+        self.read = read;
+    }
+
+    pub fn write(&mut self, write: bool) {
+        self.write = write;
+    }
+
+    pub fn truncate(&mut self, truncate: bool) {
+        self.oflag(wasi::OFLAGS_TRUNC, truncate);
+    }
+
+    pub fn create(&mut self, create: bool) {
+        self.oflag(wasi::OFLAGS_CREAT, create);
+    }
+
+    pub fn create_new(&mut self, create_new: bool) {
+        self.oflag(wasi::OFLAGS_EXCL, create_new);
+        self.oflag(wasi::OFLAGS_CREAT, create_new);
+    }
+
+    pub fn directory(&mut self, directory: bool) {
+        self.oflag(wasi::OFLAGS_DIRECTORY, directory);
+    }
+
+    fn oflag(&mut self, bit: wasi::Oflags, set: bool) {
+        if set {
+            self.oflags |= bit;
+        } else {
+            self.oflags &= !bit;
+        }
+    }
+
+    pub fn append(&mut self, append: bool) {
+        self.append = append;
+        self.fdflag(wasi::FDFLAGS_APPEND, append);
+    }
+
+    pub fn dsync(&mut self, set: bool) {
+        self.fdflag(wasi::FDFLAGS_DSYNC, set);
+    }
+
+    pub fn nonblock(&mut self, set: bool) {
+        self.fdflag(wasi::FDFLAGS_NONBLOCK, set);
+    }
+
+    pub fn rsync(&mut self, set: bool) {
+        self.fdflag(wasi::FDFLAGS_RSYNC, set);
+    }
+
+    pub fn sync(&mut self, set: bool) {
+        self.fdflag(wasi::FDFLAGS_SYNC, set);
+    }
+
+    fn fdflag(&mut self, bit: wasi::Fdflags, set: bool) {
+        if set {
+            self.fdflags |= bit;
+        } else {
+            self.fdflags &= !bit;
+        }
+    }
+
+    pub fn fs_rights_base(&mut self, rights: wasi::Rights) {
+        self.rights_base = Some(rights);
+    }
+
+    pub fn fs_rights_inheriting(&mut self, rights: wasi::Rights) {
+        self.rights_inheriting = Some(rights);
+    }
+
+    fn rights_base(&self) -> wasi::Rights {
+        if let Some(rights) = self.rights_base {
+            return rights;
+        }
+
+        // If rights haven't otherwise been specified try to pick a reasonable
+        // set. This can always be overridden by users via extension traits, and
+        // implementations may give us fewer rights silently than we ask for. So
+        // given that, just look at `read` and `write` and bucket permissions
+        // based on that.
+        let mut base = 0;
+        if self.read {
+            base |= wasi::RIGHTS_FD_READ;
+            base |= wasi::RIGHTS_FD_READDIR;
+        }
+        if self.write || self.append {
+            base |= wasi::RIGHTS_FD_WRITE;
+            base |= wasi::RIGHTS_FD_DATASYNC;
+            base |= wasi::RIGHTS_FD_ALLOCATE;
+            base |= wasi::RIGHTS_FD_FILESTAT_SET_SIZE;
+        }
+
+        // FIXME: some of these should probably be read-only or write-only...
+        base |= wasi::RIGHTS_FD_ADVISE;
+        base |= wasi::RIGHTS_FD_FDSTAT_SET_FLAGS;
+        base |= wasi::RIGHTS_FD_FILESTAT_GET;
+        base |= wasi::RIGHTS_FD_FILESTAT_SET_TIMES;
+        base |= wasi::RIGHTS_FD_SEEK;
+        base |= wasi::RIGHTS_FD_SYNC;
+        base |= wasi::RIGHTS_FD_TELL;
+        base |= wasi::RIGHTS_PATH_CREATE_DIRECTORY;
+        base |= wasi::RIGHTS_PATH_CREATE_FILE;
+        base |= wasi::RIGHTS_PATH_FILESTAT_GET;
+        base |= wasi::RIGHTS_PATH_LINK_SOURCE;
+        base |= wasi::RIGHTS_PATH_LINK_TARGET;
+        base |= wasi::RIGHTS_PATH_OPEN;
+        base |= wasi::RIGHTS_PATH_READLINK;
+        base |= wasi::RIGHTS_PATH_REMOVE_DIRECTORY;
+        base |= wasi::RIGHTS_PATH_RENAME_SOURCE;
+        base |= wasi::RIGHTS_PATH_RENAME_TARGET;
+        base |= wasi::RIGHTS_PATH_SYMLINK;
+        base |= wasi::RIGHTS_PATH_UNLINK_FILE;
+        base |= wasi::RIGHTS_POLL_FD_READWRITE;
+
+        base
+    }
+
+    fn rights_inheriting(&self) -> wasi::Rights {
+        self.rights_inheriting.unwrap_or_else(|| self.rights_base())
+    }
+
+    pub fn lookup_flags(&mut self, flags: wasi::Lookupflags) {
+        self.dirflags = flags;
+    }
+}
+
+impl File {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        let (dir, file) = open_parent(path)?;
+        open_at(&dir, &file, opts)
+    }
+
+    pub fn open_at(&self, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        open_at(&self.fd, path, opts)
+    }
+
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        self.fd.filestat_get().map(|meta| FileAttr { meta })
+    }
+
+    pub fn metadata_at(&self, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
+        metadata_at(&self.fd, flags, path)
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        self.fd.sync()
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        self.fd.datasync()
+    }
+
+    pub fn lock(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn lock_shared(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn try_lock(&self) -> io::Result<bool> {
+        unsupported()
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        unsupported()
+    }
+
+    pub fn unlock(&self) -> io::Result<()> {
+        unsupported()
+    }
+
+    pub fn truncate(&self, size: u64) -> io::Result<()> {
+        self.fd.filestat_set_size(size)
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.read_vectored(&mut [IoSliceMut::new(buf)])
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.fd.read(bufs)
+    }
+
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        true
+    }
+
+    pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        self.fd.read_buf(cursor)
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        self.write_vectored(&[IoSlice::new(buf)])
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.fd.write(bufs)
+    }
+
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        true
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        Ok(())
+    }
+
+    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+        self.fd.seek(pos)
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        self.fd.tell()
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        // https://github.com/CraneStation/wasmtime/blob/master/docs/WASI-rationale.md#why-no-dup
+        unsupported()
+    }
+
+    pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+        // Permissions haven't been fully figured out in wasi yet, so this is
+        // likely temporary
+        unsupported()
+    }
+
+    pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+        let to_timestamp = |time: Option<SystemTime>| match time {
+            Some(time) if let Some(ts) = time.to_wasi_timestamp() => Ok(ts),
+            Some(_) => Err(io::const_error!(
+                io::ErrorKind::InvalidInput,
+                "timestamp is too large to set as a file time",
+            )),
+            None => Ok(0),
+        };
+        self.fd.filestat_set_times(
+            to_timestamp(times.accessed)?,
+            to_timestamp(times.modified)?,
+            times.accessed.map_or(0, |_| wasi::FSTFLAGS_ATIM)
+                | times.modified.map_or(0, |_| wasi::FSTFLAGS_MTIM),
+        )
+    }
+
+    pub fn read_link(&self, file: &Path) -> io::Result<PathBuf> {
+        read_link(&self.fd, file)
+    }
+}
+
+impl AsInner<WasiFd> for File {
+    #[inline]
+    fn as_inner(&self) -> &WasiFd {
+        &self.fd
+    }
+}
+
+impl IntoInner<WasiFd> for File {
+    fn into_inner(self) -> WasiFd {
+        self.fd
+    }
+}
+
+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 {
+    #[inline]
+    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 FromRawFd for File {
+    unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
+        unsafe { Self { fd: FromRawFd::from_raw_fd(raw_fd) } }
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder {}
+    }
+
+    pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+        let (dir, file) = open_parent(p)?;
+        dir.create_directory(osstr2str(file.as_ref())?)
+    }
+}
+
+impl fmt::Debug for File {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("File").field("fd", &self.as_raw_fd()).finish()
+    }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+    let mut opts = OpenOptions::new();
+    opts.directory(true);
+    opts.read(true);
+    let dir = File::open(p, &opts)?;
+    Ok(ReadDir::new(dir, p.to_path_buf()))
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+    let (dir, file) = open_parent(p)?;
+    dir.unlink_file(osstr2str(file.as_ref())?)
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+    let (old, old_file) = open_parent(old)?;
+    let (new, new_file) = open_parent(new)?;
+    old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?)
+}
+
+pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
+    // Permissions haven't been fully figured out in wasi yet, so this is
+    // likely temporary
+    unsupported()
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+    let (dir, file) = open_parent(p)?;
+    dir.remove_directory(osstr2str(file.as_ref())?)
+}
+
+pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+    let (dir, file) = open_parent(p)?;
+    read_link(&dir, &file)
+}
+
+fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
+    // Try to get a best effort initial capacity for the vector we're going to
+    // fill. Note that if it's not a symlink we don't use a file to avoid
+    // allocating gigabytes if you read_link a huge movie file by accident.
+    // Additionally we add 1 to the initial size so if it doesn't change until
+    // when we call `readlink` the returned length will be less than the
+    // capacity, guaranteeing that we got all the data.
+    let meta = metadata_at(fd, 0, file)?;
+    let initial_size = if meta.file_type().is_symlink() {
+        (meta.size() as usize).saturating_add(1)
+    } else {
+        1 // this'll fail in just a moment
+    };
+
+    // Now that we have an initial guess of how big to make our buffer, call
+    // `readlink` in a loop until it fails or reports it filled fewer bytes than
+    // we asked for, indicating we got everything.
+    let file = osstr2str(file.as_ref())?;
+    let mut destination = vec![0u8; initial_size];
+    loop {
+        let len = fd.readlink(file, &mut destination)?;
+        if len < destination.len() {
+            destination.truncate(len);
+            destination.shrink_to_fit();
+            return Ok(PathBuf::from(OsString::from_vec(destination)));
+        }
+        let amt_to_add = destination.len();
+        destination.extend(iter::repeat(0).take(amt_to_add));
+    }
+}
+
+pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
+    let (link, link_file) = open_parent(link)?;
+    link.symlink(osstr2str(original.as_ref())?, osstr2str(link_file.as_ref())?)
+}
+
+pub fn link(original: &Path, link: &Path) -> io::Result<()> {
+    let (original, original_file) = open_parent(original)?;
+    let (link, link_file) = open_parent(link)?;
+    // Pass 0 as the flags argument, meaning don't follow symlinks.
+    original.link(0, osstr2str(original_file.as_ref())?, &link, osstr2str(link_file.as_ref())?)
+}
+
+pub fn stat(p: &Path) -> io::Result<FileAttr> {
+    let (dir, file) = open_parent(p)?;
+    metadata_at(&dir, wasi::LOOKUPFLAGS_SYMLINK_FOLLOW, &file)
+}
+
+pub fn lstat(p: &Path) -> io::Result<FileAttr> {
+    let (dir, file) = open_parent(p)?;
+    metadata_at(&dir, 0, &file)
+}
+
+fn metadata_at(fd: &WasiFd, flags: wasi::Lookupflags, path: &Path) -> io::Result<FileAttr> {
+    let meta = fd.path_filestat_get(flags, osstr2str(path.as_ref())?)?;
+    Ok(FileAttr { meta })
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+    // This seems to not be in wasi's API yet, and we may need to end up
+    // emulating it ourselves. For now just return an error.
+    unsupported()
+}
+
+fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
+    let fd = fd.open(
+        opts.dirflags,
+        osstr2str(path.as_ref())?,
+        opts.oflags,
+        opts.rights_base(),
+        opts.rights_inheriting(),
+        opts.fdflags,
+    )?;
+    Ok(File { fd })
+}
+
+/// Attempts to open a bare path `p`.
+///
+/// WASI has no fundamental capability to do this. All syscalls and operations
+/// are relative to already-open file descriptors. The C library, however,
+/// manages a map of pre-opened file descriptors to their path, and then the C
+/// library provides an API to look at this. In other words, when you want to
+/// open a path `p`, you have to find a previously opened file descriptor in a
+/// global table and then see if `p` is relative to that file descriptor.
+///
+/// This function, if successful, will return two items:
+///
+/// * The first is a `ManuallyDrop<WasiFd>`. This represents a pre-opened file
+///   descriptor which we don't have ownership of, but we can use. You shouldn't
+///   actually drop the `fd`.
+///
+/// * The second is a path that should be a part of `p` and represents a
+///   relative traversal from the file descriptor specified to the desired
+///   location `p`.
+///
+/// If successful you can use the returned file descriptor to perform
+/// file-descriptor-relative operations on the path returned as well. The
+/// `rights` argument indicates what operations are desired on the returned file
+/// descriptor, and if successful the returned file descriptor should have the
+/// appropriate rights for performing `rights` actions.
+///
+/// Note that this can fail if `p` doesn't look like it can be opened relative
+/// to any pre-opened file descriptor.
+fn open_parent(p: &Path) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
+    run_path_with_cstr(p, &|p| {
+        let mut buf = Vec::<u8>::with_capacity(512);
+        loop {
+            unsafe {
+                let mut relative_path = buf.as_ptr().cast();
+                let mut abs_prefix = ptr::null();
+                let fd = __wasilibc_find_relpath(
+                    p.as_ptr(),
+                    &mut abs_prefix,
+                    &mut relative_path,
+                    buf.capacity(),
+                );
+                if fd == -1 {
+                    if io::Error::last_os_error().raw_os_error() == Some(libc::ENOMEM) {
+                        // Trigger the internal buffer resizing logic of `Vec` by requiring
+                        // more space than the current capacity.
+                        let cap = buf.capacity();
+                        buf.set_len(cap);
+                        buf.reserve(1);
+                        continue;
+                    }
+                    let msg = format!(
+                        "failed to find a pre-opened file descriptor \
+                        through which {p:?} could be opened",
+                    );
+                    return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
+                }
+                let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
+
+                return Ok((
+                    ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
+                    PathBuf::from(OsString::from_vec(relative)),
+                ));
+            }
+        }
+
+        unsafe extern "C" {
+            pub fn __wasilibc_find_relpath(
+                path: *const libc::c_char,
+                abs_prefix: *mut *const libc::c_char,
+                relative_path: *mut *const libc::c_char,
+                relative_path_len: libc::size_t,
+            ) -> libc::c_int;
+        }
+    })
+}
+
+pub fn osstr2str(f: &OsStr) -> io::Result<&str> {
+    f.to_str().ok_or_else(|| io::const_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    use crate::fs::File;
+
+    let mut reader = File::open(from)?;
+    let mut writer = File::create(to)?;
+
+    io::copy(&mut reader, &mut writer)
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    let (parent, path) = open_parent(path)?;
+    remove_dir_all_recursive(&parent, &path)
+}
+
+fn remove_dir_all_recursive(parent: &WasiFd, path: &Path) -> io::Result<()> {
+    // Open up a file descriptor for the directory itself. Note that we don't
+    // follow symlinks here and we specifically open directories.
+    //
+    // At the root invocation of this function this will correctly handle
+    // symlinks passed to the top-level `remove_dir_all`. At the recursive
+    // level this will double-check that after the `readdir` call deduced this
+    // was a directory it's still a directory by the time we open it up.
+    //
+    // If the opened file was actually a symlink then the symlink is deleted,
+    // not the directory recursively.
+    let mut opts = OpenOptions::new();
+    opts.lookup_flags(0);
+    opts.directory(true);
+    opts.read(true);
+    let fd = open_at(parent, path, &opts)?;
+    if fd.file_attr()?.file_type().is_symlink() {
+        return parent.unlink_file(osstr2str(path.as_ref())?);
+    }
+
+    // this "root" is only used by `DirEntry::path` which we don't use below so
+    // it's ok for this to be a bogus value
+    let dummy_root = PathBuf::new();
+
+    // Iterate over all the entries in this directory, and travel recursively if
+    // necessary
+    for entry in ReadDir::new(fd, dummy_root) {
+        let entry = entry?;
+        let path = crate::str::from_utf8(&entry.name).map_err(|_| {
+            io::const_error!(io::ErrorKind::Uncategorized, "invalid utf-8 file name found")
+        })?;
+
+        let result: io::Result<()> = try {
+            if entry.file_type()?.is_dir() {
+                remove_dir_all_recursive(&entry.inner.dir.fd, path.as_ref())?;
+            } else {
+                entry.inner.dir.fd.unlink_file(path)?;
+            }
+        };
+        // ignore internal NotFound errors
+        if let Err(err) = &result
+            && err.kind() != io::ErrorKind::NotFound
+        {
+            return result;
+        }
+    }
+
+    // Once all this directory's contents are deleted it should be safe to
+    // delete the directory tiself.
+    ignore_notfound(parent.remove_directory(osstr2str(path.as_ref())?))
+}
diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs
new file mode 100644
index 00000000000..362e64abf1a
--- /dev/null
+++ b/library/std/src/sys/fs/windows.rs
@@ -0,0 +1,1662 @@
+#![allow(nonstandard_style)]
+
+use crate::alloc::{Layout, alloc, dealloc};
+use crate::borrow::Cow;
+use crate::ffi::{OsStr, OsString, c_void};
+use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
+use crate::mem::{self, MaybeUninit, offset_of};
+use crate::os::windows::io::{AsHandle, BorrowedHandle};
+use crate::os::windows::prelude::*;
+use crate::path::{Path, PathBuf};
+use crate::sync::Arc;
+use crate::sys::handle::Handle;
+use crate::sys::pal::api::{self, WinError, set_file_information_by_handle};
+use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul};
+use crate::sys::path::maybe_verbatim;
+use crate::sys::time::SystemTime;
+use crate::sys::{Align8, c, cvt};
+use crate::sys_common::{AsInner, FromInner, IntoInner};
+use crate::{fmt, ptr, slice};
+
+mod remove_dir_all;
+use remove_dir_all::remove_dir_all_iterative;
+
+pub struct File {
+    handle: Handle,
+}
+
+#[derive(Clone)]
+pub struct FileAttr {
+    attributes: u32,
+    creation_time: c::FILETIME,
+    last_access_time: c::FILETIME,
+    last_write_time: c::FILETIME,
+    change_time: Option<c::FILETIME>,
+    file_size: u64,
+    reparse_tag: u32,
+    volume_serial_number: Option<u32>,
+    number_of_links: Option<u32>,
+    file_index: Option<u64>,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct FileType {
+    attributes: u32,
+    reparse_tag: u32,
+}
+
+pub struct ReadDir {
+    handle: Option<FindNextFileHandle>,
+    root: Arc<PathBuf>,
+    first: Option<c::WIN32_FIND_DATAW>,
+}
+
+struct FindNextFileHandle(c::HANDLE);
+
+unsafe impl Send for FindNextFileHandle {}
+unsafe impl Sync for FindNextFileHandle {}
+
+pub struct DirEntry {
+    root: Arc<PathBuf>,
+    data: c::WIN32_FIND_DATAW,
+}
+
+unsafe impl Send for OpenOptions {}
+unsafe impl Sync for OpenOptions {}
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+    // generic
+    read: bool,
+    write: bool,
+    append: bool,
+    truncate: bool,
+    create: bool,
+    create_new: bool,
+    // system-specific
+    custom_flags: u32,
+    access_mode: Option<u32>,
+    attributes: u32,
+    share_mode: u32,
+    security_qos_flags: u32,
+    security_attributes: *mut c::SECURITY_ATTRIBUTES,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug)]
+pub struct FilePermissions {
+    attrs: u32,
+}
+
+#[derive(Copy, Clone, Debug, Default)]
+pub struct FileTimes {
+    accessed: Option<c::FILETIME>,
+    modified: Option<c::FILETIME>,
+    created: Option<c::FILETIME>,
+}
+
+impl fmt::Debug for c::FILETIME {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let time = ((self.dwHighDateTime as u64) << 32) | self.dwLowDateTime as u64;
+        f.debug_tuple("FILETIME").field(&time).finish()
+    }
+}
+
+#[derive(Debug)]
+pub struct DirBuilder;
+
+impl fmt::Debug for ReadDir {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
+        // Thus the result will be e g 'ReadDir("C:\")'
+        fmt::Debug::fmt(&*self.root, f)
+    }
+}
+
+impl Iterator for ReadDir {
+    type Item = io::Result<DirEntry>;
+    fn next(&mut self) -> Option<io::Result<DirEntry>> {
+        let Some(handle) = self.handle.as_ref() else {
+            // This iterator was initialized with an `INVALID_HANDLE_VALUE` as its handle.
+            // Simply return `None` because this is only the case when `FindFirstFileExW` in
+            // the construction of this iterator returns `ERROR_FILE_NOT_FOUND` which means
+            // no matchhing files can be found.
+            return None;
+        };
+        if let Some(first) = self.first.take() {
+            if let Some(e) = DirEntry::new(&self.root, &first) {
+                return Some(Ok(e));
+            }
+        }
+        unsafe {
+            let mut wfd = mem::zeroed();
+            loop {
+                if c::FindNextFileW(handle.0, &mut wfd) == 0 {
+                    match api::get_last_error() {
+                        WinError::NO_MORE_FILES => return None,
+                        WinError { code } => {
+                            return Some(Err(Error::from_raw_os_error(code as i32)));
+                        }
+                    }
+                }
+                if let Some(e) = DirEntry::new(&self.root, &wfd) {
+                    return Some(Ok(e));
+                }
+            }
+        }
+    }
+}
+
+impl Drop for FindNextFileHandle {
+    fn drop(&mut self) {
+        let r = unsafe { c::FindClose(self.0) };
+        debug_assert!(r != 0);
+    }
+}
+
+impl DirEntry {
+    fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> {
+        match &wfd.cFileName[0..3] {
+            // check for '.' and '..'
+            &[46, 0, ..] | &[46, 46, 0, ..] => return None,
+            _ => {}
+        }
+
+        Some(DirEntry { root: root.clone(), data: *wfd })
+    }
+
+    pub fn path(&self) -> PathBuf {
+        self.root.join(self.file_name())
+    }
+
+    pub fn file_name(&self) -> OsString {
+        let filename = truncate_utf16_at_nul(&self.data.cFileName);
+        OsString::from_wide(filename)
+    }
+
+    pub fn file_type(&self) -> io::Result<FileType> {
+        Ok(FileType::new(
+            self.data.dwFileAttributes,
+            /* reparse_tag = */ self.data.dwReserved0,
+        ))
+    }
+
+    pub fn metadata(&self) -> io::Result<FileAttr> {
+        Ok(self.data.into())
+    }
+}
+
+impl OpenOptions {
+    pub fn new() -> OpenOptions {
+        OpenOptions {
+            // generic
+            read: false,
+            write: false,
+            append: false,
+            truncate: false,
+            create: false,
+            create_new: false,
+            // system-specific
+            custom_flags: 0,
+            access_mode: None,
+            share_mode: c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE,
+            attributes: 0,
+            security_qos_flags: 0,
+            security_attributes: ptr::null_mut(),
+        }
+    }
+
+    pub fn read(&mut self, read: bool) {
+        self.read = read;
+    }
+    pub fn write(&mut self, write: bool) {
+        self.write = write;
+    }
+    pub fn append(&mut self, append: bool) {
+        self.append = append;
+    }
+    pub fn truncate(&mut self, truncate: bool) {
+        self.truncate = truncate;
+    }
+    pub fn create(&mut self, create: bool) {
+        self.create = create;
+    }
+    pub fn create_new(&mut self, create_new: bool) {
+        self.create_new = create_new;
+    }
+
+    pub fn custom_flags(&mut self, flags: u32) {
+        self.custom_flags = flags;
+    }
+    pub fn access_mode(&mut self, access_mode: u32) {
+        self.access_mode = Some(access_mode);
+    }
+    pub fn share_mode(&mut self, share_mode: u32) {
+        self.share_mode = share_mode;
+    }
+    pub fn attributes(&mut self, attrs: u32) {
+        self.attributes = attrs;
+    }
+    pub fn security_qos_flags(&mut self, flags: u32) {
+        // We have to set `SECURITY_SQOS_PRESENT` here, because one of the valid flags we can
+        // receive is `SECURITY_ANONYMOUS = 0x0`, which we can't check for later on.
+        self.security_qos_flags = flags | c::SECURITY_SQOS_PRESENT;
+    }
+    pub fn security_attributes(&mut self, attrs: *mut c::SECURITY_ATTRIBUTES) {
+        self.security_attributes = attrs;
+    }
+
+    fn get_access_mode(&self) -> io::Result<u32> {
+        match (self.read, self.write, self.append, self.access_mode) {
+            (.., Some(mode)) => Ok(mode),
+            (true, false, false, None) => Ok(c::GENERIC_READ),
+            (false, true, false, None) => Ok(c::GENERIC_WRITE),
+            (true, true, false, None) => Ok(c::GENERIC_READ | c::GENERIC_WRITE),
+            (false, _, true, None) => Ok(c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA),
+            (true, _, true, None) => {
+                Ok(c::GENERIC_READ | (c::FILE_GENERIC_WRITE & !c::FILE_WRITE_DATA))
+            }
+            (false, false, false, None) => {
+                Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32))
+            }
+        }
+    }
+
+    fn get_creation_mode(&self) -> io::Result<u32> {
+        match (self.write, self.append) {
+            (true, false) => {}
+            (false, false) => {
+                if self.truncate || self.create || self.create_new {
+                    return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
+                }
+            }
+            (_, true) => {
+                if self.truncate && !self.create_new {
+                    return Err(Error::from_raw_os_error(c::ERROR_INVALID_PARAMETER as i32));
+                }
+            }
+        }
+
+        Ok(match (self.create, self.truncate, self.create_new) {
+            (false, false, false) => c::OPEN_EXISTING,
+            (true, false, false) => c::OPEN_ALWAYS,
+            (false, true, false) => c::TRUNCATE_EXISTING,
+            // `CREATE_ALWAYS` has weird semantics so we emulate it using
+            // `OPEN_ALWAYS` and a manual truncation step. See #115745.
+            (true, true, false) => c::OPEN_ALWAYS,
+            (_, _, true) => c::CREATE_NEW,
+        })
+    }
+
+    fn get_flags_and_attributes(&self) -> u32 {
+        self.custom_flags
+            | self.attributes
+            | self.security_qos_flags
+            | if self.create_new { c::FILE_FLAG_OPEN_REPARSE_POINT } else { 0 }
+    }
+}
+
+impl File {
+    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+        let path = maybe_verbatim(path)?;
+        Self::open_native(&path, opts)
+    }
+
+    fn open_native(path: &[u16], opts: &OpenOptions) -> io::Result<File> {
+        let creation = opts.get_creation_mode()?;
+        let handle = unsafe {
+            c::CreateFileW(
+                path.as_ptr(),
+                opts.get_access_mode()?,
+                opts.share_mode,
+                opts.security_attributes,
+                creation,
+                opts.get_flags_and_attributes(),
+                ptr::null_mut(),
+            )
+        };
+        let handle = unsafe { HandleOrInvalid::from_raw_handle(handle) };
+        if let Ok(handle) = OwnedHandle::try_from(handle) {
+            // Manual truncation. See #115745.
+            if opts.truncate
+                && creation == c::OPEN_ALWAYS
+                && api::get_last_error() == WinError::ALREADY_EXISTS
+            {
+                // This first tries `FileAllocationInfo` but falls back to
+                // `FileEndOfFileInfo` in order to support WINE.
+                // If WINE gains support for FileAllocationInfo, we should
+                // remove the fallback.
+                let alloc = c::FILE_ALLOCATION_INFO { AllocationSize: 0 };
+                set_file_information_by_handle(handle.as_raw_handle(), &alloc)
+                    .or_else(|_| {
+                        let eof = c::FILE_END_OF_FILE_INFO { EndOfFile: 0 };
+                        set_file_information_by_handle(handle.as_raw_handle(), &eof)
+                    })
+                    .io_result()?;
+            }
+            Ok(File { handle: Handle::from_inner(handle) })
+        } else {
+            Err(Error::last_os_error())
+        }
+    }
+
+    pub fn fsync(&self) -> io::Result<()> {
+        cvt(unsafe { c::FlushFileBuffers(self.handle.as_raw_handle()) })?;
+        Ok(())
+    }
+
+    pub fn datasync(&self) -> io::Result<()> {
+        self.fsync()
+    }
+
+    fn acquire_lock(&self, flags: c::LOCK_FILE_FLAGS) -> io::Result<()> {
+        unsafe {
+            let mut overlapped: c::OVERLAPPED = mem::zeroed();
+            let event = c::CreateEventW(ptr::null_mut(), c::FALSE, c::FALSE, ptr::null());
+            if event.is_null() {
+                return Err(io::Error::last_os_error());
+            }
+            overlapped.hEvent = event;
+            let lock_result = cvt(c::LockFileEx(
+                self.handle.as_raw_handle(),
+                flags,
+                0,
+                u32::MAX,
+                u32::MAX,
+                &mut overlapped,
+            ));
+
+            let final_result = match lock_result {
+                Ok(_) => Ok(()),
+                Err(err) => {
+                    if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32) {
+                        // Wait for the lock to be acquired, and get the lock operation status.
+                        // This can happen asynchronously, if the file handle was opened for async IO
+                        let mut bytes_transferred = 0;
+                        cvt(c::GetOverlappedResult(
+                            self.handle.as_raw_handle(),
+                            &mut overlapped,
+                            &mut bytes_transferred,
+                            c::TRUE,
+                        ))
+                        .map(|_| ())
+                    } else {
+                        Err(err)
+                    }
+                }
+            };
+            c::CloseHandle(overlapped.hEvent);
+            final_result
+        }
+    }
+
+    pub fn lock(&self) -> io::Result<()> {
+        self.acquire_lock(c::LOCKFILE_EXCLUSIVE_LOCK)
+    }
+
+    pub fn lock_shared(&self) -> io::Result<()> {
+        self.acquire_lock(0)
+    }
+
+    pub fn try_lock(&self) -> io::Result<bool> {
+        let result = cvt(unsafe {
+            let mut overlapped = mem::zeroed();
+            c::LockFileEx(
+                self.handle.as_raw_handle(),
+                c::LOCKFILE_EXCLUSIVE_LOCK | c::LOCKFILE_FAIL_IMMEDIATELY,
+                0,
+                u32::MAX,
+                u32::MAX,
+                &mut overlapped,
+            )
+        });
+
+        match result {
+            Ok(_) => Ok(true),
+            Err(err)
+                if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
+                    || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) =>
+            {
+                Ok(false)
+            }
+            Err(err) => Err(err),
+        }
+    }
+
+    pub fn try_lock_shared(&self) -> io::Result<bool> {
+        let result = cvt(unsafe {
+            let mut overlapped = mem::zeroed();
+            c::LockFileEx(
+                self.handle.as_raw_handle(),
+                c::LOCKFILE_FAIL_IMMEDIATELY,
+                0,
+                u32::MAX,
+                u32::MAX,
+                &mut overlapped,
+            )
+        });
+
+        match result {
+            Ok(_) => Ok(true),
+            Err(err)
+                if err.raw_os_error() == Some(c::ERROR_IO_PENDING as i32)
+                    || err.raw_os_error() == Some(c::ERROR_LOCK_VIOLATION as i32) =>
+            {
+                Ok(false)
+            }
+            Err(err) => Err(err),
+        }
+    }
+
+    pub fn unlock(&self) -> io::Result<()> {
+        // Unlock the handle twice because LockFileEx() allows a file handle to acquire
+        // both an exclusive and shared lock, in which case the documentation states that:
+        // "...two unlock operations are necessary to unlock the region; the first unlock operation
+        // unlocks the exclusive lock, the second unlock operation unlocks the shared lock"
+        cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) })?;
+        let result =
+            cvt(unsafe { c::UnlockFile(self.handle.as_raw_handle(), 0, 0, u32::MAX, u32::MAX) });
+        match result {
+            Ok(_) => Ok(()),
+            Err(err) if err.raw_os_error() == Some(c::ERROR_NOT_LOCKED as i32) => Ok(()),
+            Err(err) => Err(err),
+        }
+    }
+
+    pub fn truncate(&self, size: u64) -> io::Result<()> {
+        let info = c::FILE_END_OF_FILE_INFO { EndOfFile: size as i64 };
+        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
+    }
+
+    #[cfg(not(target_vendor = "uwp"))]
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        unsafe {
+            let mut info: c::BY_HANDLE_FILE_INFORMATION = mem::zeroed();
+            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 attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
+                cvt(c::GetFileInformationByHandleEx(
+                    self.handle.as_raw_handle(),
+                    c::FileAttributeTagInfo,
+                    (&raw mut attr_tag).cast(),
+                    size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
+                ))?;
+                if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+                    reparse_tag = attr_tag.ReparseTag;
+                }
+            }
+            Ok(FileAttr {
+                attributes: info.dwFileAttributes,
+                creation_time: info.ftCreationTime,
+                last_access_time: info.ftLastAccessTime,
+                last_write_time: info.ftLastWriteTime,
+                change_time: None, // Only available in FILE_BASIC_INFO
+                file_size: (info.nFileSizeLow as u64) | ((info.nFileSizeHigh as u64) << 32),
+                reparse_tag,
+                volume_serial_number: Some(info.dwVolumeSerialNumber),
+                number_of_links: Some(info.nNumberOfLinks),
+                file_index: Some(
+                    (info.nFileIndexLow as u64) | ((info.nFileIndexHigh as u64) << 32),
+                ),
+            })
+        }
+    }
+
+    #[cfg(target_vendor = "uwp")]
+    pub fn file_attr(&self) -> io::Result<FileAttr> {
+        unsafe {
+            let mut info: c::FILE_BASIC_INFO = mem::zeroed();
+            let size = size_of_val(&info);
+            cvt(c::GetFileInformationByHandleEx(
+                self.handle.as_raw_handle(),
+                c::FileBasicInfo,
+                (&raw mut info) as *mut c_void,
+                size as u32,
+            ))?;
+            let mut attr = FileAttr {
+                attributes: info.FileAttributes,
+                creation_time: c::FILETIME {
+                    dwLowDateTime: info.CreationTime as u32,
+                    dwHighDateTime: (info.CreationTime >> 32) as u32,
+                },
+                last_access_time: c::FILETIME {
+                    dwLowDateTime: info.LastAccessTime as u32,
+                    dwHighDateTime: (info.LastAccessTime >> 32) as u32,
+                },
+                last_write_time: c::FILETIME {
+                    dwLowDateTime: info.LastWriteTime as u32,
+                    dwHighDateTime: (info.LastWriteTime >> 32) as u32,
+                },
+                change_time: Some(c::FILETIME {
+                    dwLowDateTime: info.ChangeTime as u32,
+                    dwHighDateTime: (info.ChangeTime >> 32) as u32,
+                }),
+                file_size: 0,
+                reparse_tag: 0,
+                volume_serial_number: None,
+                number_of_links: None,
+                file_index: None,
+            };
+            let mut info: c::FILE_STANDARD_INFO = mem::zeroed();
+            let size = size_of_val(&info);
+            cvt(c::GetFileInformationByHandleEx(
+                self.handle.as_raw_handle(),
+                c::FileStandardInfo,
+                (&raw mut info) as *mut c_void,
+                size as u32,
+            ))?;
+            attr.file_size = info.AllocationSize as u64;
+            attr.number_of_links = Some(info.NumberOfLinks);
+            if attr.file_type().is_reparse_point() {
+                let mut attr_tag: c::FILE_ATTRIBUTE_TAG_INFO = mem::zeroed();
+                cvt(c::GetFileInformationByHandleEx(
+                    self.handle.as_raw_handle(),
+                    c::FileAttributeTagInfo,
+                    (&raw mut attr_tag).cast(),
+                    size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(),
+                ))?;
+                if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+                    attr.reparse_tag = attr_tag.ReparseTag;
+                }
+            }
+            Ok(attr)
+        }
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+        self.handle.read(buf)
+    }
+
+    pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+        self.handle.read_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_read_vectored(&self) -> bool {
+        self.handle.is_read_vectored()
+    }
+
+    pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
+        self.handle.read_at(buf, offset)
+    }
+
+    pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
+        self.handle.read_buf(cursor)
+    }
+
+    pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+        self.handle.write(buf)
+    }
+
+    pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+        self.handle.write_vectored(bufs)
+    }
+
+    #[inline]
+    pub fn is_write_vectored(&self) -> bool {
+        self.handle.is_write_vectored()
+    }
+
+    pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
+        self.handle.write_at(buf, offset)
+    }
+
+    pub fn flush(&self) -> io::Result<()> {
+        Ok(())
+    }
+
+    pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
+        let (whence, pos) = match pos {
+            // Casting to `i64` is fine, `SetFilePointerEx` reinterprets this
+            // integer as `u64`.
+            SeekFrom::Start(n) => (c::FILE_BEGIN, n as i64),
+            SeekFrom::End(n) => (c::FILE_END, n),
+            SeekFrom::Current(n) => (c::FILE_CURRENT, n),
+        };
+        let pos = pos as i64;
+        let mut newpos = 0;
+        cvt(unsafe { c::SetFilePointerEx(self.handle.as_raw_handle(), pos, &mut newpos, whence) })?;
+        Ok(newpos as u64)
+    }
+
+    pub fn tell(&self) -> io::Result<u64> {
+        self.seek(SeekFrom::Current(0))
+    }
+
+    pub fn duplicate(&self) -> io::Result<File> {
+        Ok(Self { handle: self.handle.try_clone()? })
+    }
+
+    // NB: returned pointer is derived from `space`, and has provenance to
+    // match. A raw pointer is returned rather than a reference in order to
+    // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
+    fn reparse_point(
+        &self,
+        space: &mut Align8<[MaybeUninit<u8>]>,
+    ) -> io::Result<(u32, *mut c::REPARSE_DATA_BUFFER)> {
+        unsafe {
+            let mut bytes = 0;
+            cvt({
+                // Grab this in advance to avoid it invalidating the pointer
+                // we get from `space.0.as_mut_ptr()`.
+                let len = space.0.len();
+                c::DeviceIoControl(
+                    self.handle.as_raw_handle(),
+                    c::FSCTL_GET_REPARSE_POINT,
+                    ptr::null_mut(),
+                    0,
+                    space.0.as_mut_ptr().cast(),
+                    len as u32,
+                    &mut bytes,
+                    ptr::null_mut(),
+                )
+            })?;
+            const _: () = assert!(align_of::<c::REPARSE_DATA_BUFFER>() <= 8);
+            Ok((bytes, space.0.as_mut_ptr().cast::<c::REPARSE_DATA_BUFFER>()))
+        }
+    }
+
+    fn readlink(&self) -> io::Result<PathBuf> {
+        let mut space =
+            Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize]);
+        let (_bytes, buf) = self.reparse_point(&mut space)?;
+        unsafe {
+            let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
+                c::IO_REPARSE_TAG_SYMLINK => {
+                    let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
+                    assert!(info.is_aligned());
+                    (
+                        (&raw mut (*info).PathBuffer).cast::<u16>(),
+                        (*info).SubstituteNameOffset / 2,
+                        (*info).SubstituteNameLength / 2,
+                        (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0,
+                    )
+                }
+                c::IO_REPARSE_TAG_MOUNT_POINT => {
+                    let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast();
+                    assert!(info.is_aligned());
+                    (
+                        (&raw mut (*info).PathBuffer).cast::<u16>(),
+                        (*info).SubstituteNameOffset / 2,
+                        (*info).SubstituteNameLength / 2,
+                        false,
+                    )
+                }
+                _ => {
+                    return Err(io::const_error!(
+                        io::ErrorKind::Uncategorized,
+                        "Unsupported reparse point type",
+                    ));
+                }
+            };
+            let subst_ptr = path_buffer.add(subst_off.into());
+            let subst = slice::from_raw_parts_mut(subst_ptr, subst_len as usize);
+            // Absolute paths start with an NT internal namespace prefix `\??\`
+            // We should not let it leak through.
+            if !relative && subst.starts_with(&[92u16, 63u16, 63u16, 92u16]) {
+                // Turn `\??\` into `\\?\` (a verbatim path).
+                subst[1] = b'\\' as u16;
+                // Attempt to convert to a more user-friendly path.
+                let user = crate::sys::args::from_wide_to_user_path(
+                    subst.iter().copied().chain([0]).collect(),
+                )?;
+                Ok(PathBuf::from(OsString::from_wide(user.strip_suffix(&[0]).unwrap_or(&user))))
+            } else {
+                Ok(PathBuf::from(OsString::from_wide(subst)))
+            }
+        }
+    }
+
+    pub fn set_permissions(&self, perm: FilePermissions) -> io::Result<()> {
+        let info = c::FILE_BASIC_INFO {
+            CreationTime: 0,
+            LastAccessTime: 0,
+            LastWriteTime: 0,
+            ChangeTime: 0,
+            FileAttributes: perm.attrs,
+        };
+        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info).io_result()
+    }
+
+    pub fn set_times(&self, times: FileTimes) -> io::Result<()> {
+        let is_zero = |t: c::FILETIME| t.dwLowDateTime == 0 && t.dwHighDateTime == 0;
+        if times.accessed.map_or(false, is_zero)
+            || times.modified.map_or(false, is_zero)
+            || times.created.map_or(false, is_zero)
+        {
+            return Err(io::const_error!(
+                io::ErrorKind::InvalidInput,
+                "cannot set file timestamp to 0",
+            ));
+        }
+        let is_max = |t: c::FILETIME| t.dwLowDateTime == u32::MAX && t.dwHighDateTime == u32::MAX;
+        if times.accessed.map_or(false, is_max)
+            || times.modified.map_or(false, is_max)
+            || times.created.map_or(false, is_max)
+        {
+            return Err(io::const_error!(
+                io::ErrorKind::InvalidInput,
+                "cannot set file timestamp to 0xFFFF_FFFF_FFFF_FFFF",
+            ));
+        }
+        cvt(unsafe {
+            let created =
+                times.created.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
+            let accessed =
+                times.accessed.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
+            let modified =
+                times.modified.as_ref().map(|a| a as *const c::FILETIME).unwrap_or(ptr::null());
+            c::SetFileTime(self.as_raw_handle(), created, accessed, modified)
+        })?;
+        Ok(())
+    }
+
+    /// Gets only basic file information such as attributes and file times.
+    fn basic_info(&self) -> io::Result<c::FILE_BASIC_INFO> {
+        unsafe {
+            let mut info: c::FILE_BASIC_INFO = mem::zeroed();
+            let size = size_of_val(&info);
+            cvt(c::GetFileInformationByHandleEx(
+                self.handle.as_raw_handle(),
+                c::FileBasicInfo,
+                (&raw mut info) as *mut c_void,
+                size as u32,
+            ))?;
+            Ok(info)
+        }
+    }
+
+    /// Deletes the file, consuming the file handle to ensure the delete occurs
+    /// as immediately as possible.
+    /// This attempts to use `posix_delete` but falls back to `win32_delete`
+    /// if that is not supported by the filesystem.
+    #[allow(unused)]
+    fn delete(self) -> Result<(), WinError> {
+        // If POSIX delete is not supported for this filesystem then fallback to win32 delete.
+        match self.posix_delete() {
+            Err(WinError::INVALID_PARAMETER)
+            | Err(WinError::NOT_SUPPORTED)
+            | Err(WinError::INVALID_FUNCTION) => self.win32_delete(),
+            result => result,
+        }
+    }
+
+    /// Delete using POSIX semantics.
+    ///
+    /// Files will be deleted as soon as the handle is closed. This is supported
+    /// for Windows 10 1607 (aka RS1) and later. However some filesystem
+    /// drivers will not support it even then, e.g. FAT32.
+    ///
+    /// If the operation is not supported for this filesystem or OS version
+    /// then errors will be `ERROR_NOT_SUPPORTED` or `ERROR_INVALID_PARAMETER`.
+    #[allow(unused)]
+    fn posix_delete(&self) -> Result<(), WinError> {
+        let info = c::FILE_DISPOSITION_INFO_EX {
+            Flags: c::FILE_DISPOSITION_FLAG_DELETE
+                | c::FILE_DISPOSITION_FLAG_POSIX_SEMANTICS
+                | c::FILE_DISPOSITION_FLAG_IGNORE_READONLY_ATTRIBUTE,
+        };
+        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)
+    }
+
+    /// Delete a file using win32 semantics. The file won't actually be deleted
+    /// until all file handles are closed. However, marking a file for deletion
+    /// will prevent anyone from opening a new handle to the file.
+    #[allow(unused)]
+    fn win32_delete(&self) -> Result<(), WinError> {
+        let info = c::FILE_DISPOSITION_INFO { DeleteFile: true };
+        api::set_file_information_by_handle(self.handle.as_raw_handle(), &info)
+    }
+
+    /// Fill the given buffer with as many directory entries as will fit.
+    /// This will remember its position and continue from the last call unless
+    /// `restart` is set to `true`.
+    ///
+    /// The returned bool indicates if there are more entries or not.
+    /// It is an error if `self` is not a directory.
+    ///
+    /// # Symlinks and other reparse points
+    ///
+    /// On Windows a file is either a directory or a non-directory.
+    /// A symlink directory is simply an empty directory with some "reparse" metadata attached.
+    /// So if you open a link (not its target) and iterate the directory,
+    /// you will always iterate an empty directory regardless of the target.
+    #[allow(unused)]
+    fn fill_dir_buff(&self, buffer: &mut DirBuff, restart: bool) -> Result<bool, WinError> {
+        let class =
+            if restart { c::FileIdBothDirectoryRestartInfo } else { c::FileIdBothDirectoryInfo };
+
+        unsafe {
+            let result = c::GetFileInformationByHandleEx(
+                self.as_raw_handle(),
+                class,
+                buffer.as_mut_ptr().cast(),
+                buffer.capacity() as _,
+            );
+            if result == 0 {
+                let err = api::get_last_error();
+                if err.code == c::ERROR_NO_MORE_FILES { Ok(false) } else { Err(err) }
+            } else {
+                Ok(true)
+            }
+        }
+    }
+}
+
+/// A buffer for holding directory entries.
+struct DirBuff {
+    buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
+}
+impl DirBuff {
+    const BUFFER_SIZE: usize = 1024;
+    fn new() -> Self {
+        Self {
+            // Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
+            // initialization.
+            buffer: unsafe { Box::new_uninit().assume_init() },
+        }
+    }
+    fn capacity(&self) -> usize {
+        self.buffer.0.len()
+    }
+    fn as_mut_ptr(&mut self) -> *mut u8 {
+        self.buffer.0.as_mut_ptr().cast()
+    }
+    /// Returns a `DirBuffIter`.
+    fn iter(&self) -> DirBuffIter<'_> {
+        DirBuffIter::new(self)
+    }
+}
+impl AsRef<[MaybeUninit<u8>]> for DirBuff {
+    fn as_ref(&self) -> &[MaybeUninit<u8>] {
+        &self.buffer.0
+    }
+}
+
+/// An iterator over entries stored in a `DirBuff`.
+///
+/// Currently only returns file names (UTF-16 encoded).
+struct DirBuffIter<'a> {
+    buffer: Option<&'a [MaybeUninit<u8>]>,
+    cursor: usize,
+}
+impl<'a> DirBuffIter<'a> {
+    fn new(buffer: &'a DirBuff) -> Self {
+        Self { buffer: Some(buffer.as_ref()), cursor: 0 }
+    }
+}
+impl<'a> Iterator for DirBuffIter<'a> {
+    type Item = (Cow<'a, [u16]>, bool);
+    fn next(&mut self) -> Option<Self::Item> {
+        let buffer = &self.buffer?[self.cursor..];
+
+        // Get the name and next entry from the buffer.
+        // SAFETY:
+        // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
+        //   field (the file name) is unsized. So an offset has to be used to
+        //   get the file name slice.
+        // - The OS has guaranteed initialization of the fields of
+        //   `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
+        //   `FileNameLength` bytes)
+        let (name, is_directory, next_entry) = unsafe {
+            let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
+            // While this is guaranteed to be aligned in documentation for
+            // https://docs.microsoft.com/en-us/windows/win32/api/winbase/ns-winbase-file_id_both_dir_info
+            // it does not seem that reality is so kind, and assuming this
+            // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530)
+            // presumably, this can be blamed on buggy filesystem drivers, but who knows.
+            let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize;
+            let length = (&raw const (*info).FileNameLength).read_unaligned() as usize;
+            let attrs = (&raw const (*info).FileAttributes).read_unaligned();
+            let name = from_maybe_unaligned(
+                (&raw const (*info).FileName).cast::<u16>(),
+                length / size_of::<u16>(),
+            );
+            let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0;
+
+            (name, is_directory, next_entry)
+        };
+
+        if next_entry == 0 {
+            self.buffer = None
+        } else {
+            self.cursor += next_entry
+        }
+
+        // Skip `.` and `..` pseudo entries.
+        const DOT: u16 = b'.' as u16;
+        match &name[..] {
+            [DOT] | [DOT, DOT] => self.next(),
+            _ => Some((name, is_directory)),
+        }
+    }
+}
+
+unsafe fn from_maybe_unaligned<'a>(p: *const u16, len: usize) -> Cow<'a, [u16]> {
+    unsafe {
+        if p.is_aligned() {
+            Cow::Borrowed(crate::slice::from_raw_parts(p, len))
+        } else {
+            Cow::Owned((0..len).map(|i| p.add(i).read_unaligned()).collect())
+        }
+    }
+}
+
+impl AsInner<Handle> for File {
+    #[inline]
+    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 }
+    }
+}
+
+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 {
+        unsafe {
+            Self { handle: FromInner::from_inner(FromRawHandle::from_raw_handle(raw_handle)) }
+        }
+    }
+}
+
+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.as_raw_handle());
+        if let Ok(path) = get_path(self) {
+            b.field("path", &path);
+        }
+        b.finish()
+    }
+}
+
+impl FileAttr {
+    pub fn size(&self) -> u64 {
+        self.file_size
+    }
+
+    pub fn perm(&self) -> FilePermissions {
+        FilePermissions { attrs: self.attributes }
+    }
+
+    pub fn attrs(&self) -> u32 {
+        self.attributes
+    }
+
+    pub fn file_type(&self) -> FileType {
+        FileType::new(self.attributes, self.reparse_tag)
+    }
+
+    pub fn modified(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from(self.last_write_time))
+    }
+
+    pub fn accessed(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from(self.last_access_time))
+    }
+
+    pub fn created(&self) -> io::Result<SystemTime> {
+        Ok(SystemTime::from(self.creation_time))
+    }
+
+    pub fn modified_u64(&self) -> u64 {
+        to_u64(&self.last_write_time)
+    }
+
+    pub fn accessed_u64(&self) -> u64 {
+        to_u64(&self.last_access_time)
+    }
+
+    pub fn created_u64(&self) -> u64 {
+        to_u64(&self.creation_time)
+    }
+
+    pub fn changed_u64(&self) -> Option<u64> {
+        self.change_time.as_ref().map(|c| to_u64(c))
+    }
+
+    pub fn volume_serial_number(&self) -> Option<u32> {
+        self.volume_serial_number
+    }
+
+    pub fn number_of_links(&self) -> Option<u32> {
+        self.number_of_links
+    }
+
+    pub fn file_index(&self) -> Option<u64> {
+        self.file_index
+    }
+}
+impl From<c::WIN32_FIND_DATAW> for FileAttr {
+    fn from(wfd: c::WIN32_FIND_DATAW) -> Self {
+        FileAttr {
+            attributes: wfd.dwFileAttributes,
+            creation_time: wfd.ftCreationTime,
+            last_access_time: wfd.ftLastAccessTime,
+            last_write_time: wfd.ftLastWriteTime,
+            change_time: None,
+            file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64),
+            reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
+                // reserved unless this is a reparse point
+                wfd.dwReserved0
+            } else {
+                0
+            },
+            volume_serial_number: None,
+            number_of_links: None,
+            file_index: None,
+        }
+    }
+}
+
+fn to_u64(ft: &c::FILETIME) -> u64 {
+    (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32)
+}
+
+impl FilePermissions {
+    pub fn readonly(&self) -> bool {
+        self.attrs & c::FILE_ATTRIBUTE_READONLY != 0
+    }
+
+    pub fn set_readonly(&mut self, readonly: bool) {
+        if readonly {
+            self.attrs |= c::FILE_ATTRIBUTE_READONLY;
+        } else {
+            self.attrs &= !c::FILE_ATTRIBUTE_READONLY;
+        }
+    }
+}
+
+impl FileTimes {
+    pub fn set_accessed(&mut self, t: SystemTime) {
+        self.accessed = Some(t.into_inner());
+    }
+
+    pub fn set_modified(&mut self, t: SystemTime) {
+        self.modified = Some(t.into_inner());
+    }
+
+    pub fn set_created(&mut self, t: SystemTime) {
+        self.created = Some(t.into_inner());
+    }
+}
+
+impl FileType {
+    fn new(attrs: u32, reparse_tag: u32) -> FileType {
+        FileType { attributes: attrs, reparse_tag }
+    }
+    pub fn is_dir(&self) -> bool {
+        !self.is_symlink() && self.is_directory()
+    }
+    pub fn is_file(&self) -> bool {
+        !self.is_symlink() && !self.is_directory()
+    }
+    pub fn is_symlink(&self) -> bool {
+        self.is_reparse_point() && self.is_reparse_tag_name_surrogate()
+    }
+    pub fn is_symlink_dir(&self) -> bool {
+        self.is_symlink() && self.is_directory()
+    }
+    pub fn is_symlink_file(&self) -> bool {
+        self.is_symlink() && !self.is_directory()
+    }
+    fn is_directory(&self) -> bool {
+        self.attributes & c::FILE_ATTRIBUTE_DIRECTORY != 0
+    }
+    fn is_reparse_point(&self) -> bool {
+        self.attributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0
+    }
+    fn is_reparse_tag_name_surrogate(&self) -> bool {
+        self.reparse_tag & 0x20000000 != 0
+    }
+}
+
+impl DirBuilder {
+    pub fn new() -> DirBuilder {
+        DirBuilder
+    }
+
+    pub fn mkdir(&self, p: &Path) -> io::Result<()> {
+        let p = maybe_verbatim(p)?;
+        cvt(unsafe { c::CreateDirectoryW(p.as_ptr(), ptr::null_mut()) })?;
+        Ok(())
+    }
+}
+
+pub fn readdir(p: &Path) -> io::Result<ReadDir> {
+    // We push a `*` to the end of the path which cause the empty path to be
+    // treated as the current directory. So, for consistency with other platforms,
+    // we explicitly error on the empty path.
+    if p.as_os_str().is_empty() {
+        // Return an error code consistent with other ways of opening files.
+        // E.g. fs::metadata or File::open.
+        return Err(io::Error::from_raw_os_error(c::ERROR_PATH_NOT_FOUND as i32));
+    }
+    let root = p.to_path_buf();
+    let star = p.join("*");
+    let path = maybe_verbatim(&star)?;
+
+    unsafe {
+        let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();
+        // this is like FindFirstFileW (see https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirstfileexw),
+        // but with FindExInfoBasic it should skip filling WIN32_FIND_DATAW.cAlternateFileName
+        // (see https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw)
+        // (which will be always null string value and currently unused) and should be faster.
+        //
+        // We can pass FIND_FIRST_EX_LARGE_FETCH to dwAdditionalFlags to speed up things more,
+        // but as we don't know user's use profile of this function, lets be conservative.
+        let find_handle = c::FindFirstFileExW(
+            path.as_ptr(),
+            c::FindExInfoBasic,
+            &mut wfd as *mut _ as _,
+            c::FindExSearchNameMatch,
+            ptr::null(),
+            0,
+        );
+
+        if find_handle != c::INVALID_HANDLE_VALUE {
+            Ok(ReadDir {
+                handle: Some(FindNextFileHandle(find_handle)),
+                root: Arc::new(root),
+                first: Some(wfd),
+            })
+        } else {
+            // The status `ERROR_FILE_NOT_FOUND` is returned by the `FindFirstFileExW` function
+            // if no matching files can be found, but not necessarily that the path to find the
+            // files in does not exist.
+            //
+            // Hence, a check for whether the path to search in exists is added when the last
+            // os error returned by Windows is `ERROR_FILE_NOT_FOUND` to handle this scenario.
+            // If that is the case, an empty `ReadDir` iterator is returned as it returns `None`
+            // in the initial `.next()` invocation because `ERROR_NO_MORE_FILES` would have been
+            // returned by the `FindNextFileW` function.
+            //
+            // See issue #120040: https://github.com/rust-lang/rust/issues/120040.
+            let last_error = api::get_last_error();
+            if last_error == WinError::FILE_NOT_FOUND {
+                return Ok(ReadDir { handle: None, root: Arc::new(root), first: None });
+            }
+
+            // Just return the error constructed from the raw OS error if the above is not the case.
+            //
+            // Note: `ERROR_PATH_NOT_FOUND` would have been returned by the `FindFirstFileExW` function
+            // when the path to search in does not exist in the first place.
+            Err(Error::from_raw_os_error(last_error.code as i32))
+        }
+    }
+}
+
+pub fn unlink(p: &Path) -> io::Result<()> {
+    let p_u16s = maybe_verbatim(p)?;
+    if unsafe { c::DeleteFileW(p_u16s.as_ptr()) } == 0 {
+        let err = api::get_last_error();
+        // if `DeleteFileW` fails with ERROR_ACCESS_DENIED then try to remove
+        // the file while ignoring the readonly attribute.
+        // This is accomplished by calling the `posix_delete` function on an open file handle.
+        if err == WinError::ACCESS_DENIED {
+            let mut opts = OpenOptions::new();
+            opts.access_mode(c::DELETE);
+            opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT);
+            if let Ok(f) = File::open_native(&p_u16s, &opts) {
+                if f.posix_delete().is_ok() {
+                    return Ok(());
+                }
+            }
+        }
+        // return the original error if any of the above fails.
+        Err(io::Error::from_raw_os_error(err.code as i32))
+    } else {
+        Ok(())
+    }
+}
+
+pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+    let old = maybe_verbatim(old)?;
+    let new = maybe_verbatim(new)?;
+
+    if unsafe { c::MoveFileExW(old.as_ptr(), new.as_ptr(), c::MOVEFILE_REPLACE_EXISTING) } == 0 {
+        let err = api::get_last_error();
+        // if `MoveFileExW` fails with ERROR_ACCESS_DENIED then try to move
+        // the file while ignoring the readonly attribute.
+        // This is accomplished by calling `SetFileInformationByHandle` with `FileRenameInfoEx`.
+        if err == WinError::ACCESS_DENIED {
+            let mut opts = OpenOptions::new();
+            opts.access_mode(c::DELETE);
+            opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
+            let Ok(f) = File::open_native(&old, &opts) else { return Err(err).io_result() };
+
+            // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
+            // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
+            let Ok(new_len_without_nul_in_bytes): Result<u32, _> = ((new.len() - 1) * 2).try_into()
+            else {
+                return Err(err).io_result();
+            };
+            let offset: u32 = offset_of!(c::FILE_RENAME_INFO, FileName).try_into().unwrap();
+            let struct_size = offset + new_len_without_nul_in_bytes + 2;
+            let layout =
+                Layout::from_size_align(struct_size as usize, align_of::<c::FILE_RENAME_INFO>())
+                    .unwrap();
+
+            // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
+            let file_rename_info;
+            unsafe {
+                file_rename_info = alloc(layout).cast::<c::FILE_RENAME_INFO>();
+                if file_rename_info.is_null() {
+                    return Err(io::ErrorKind::OutOfMemory.into());
+                }
+
+                (&raw mut (*file_rename_info).Anonymous).write(c::FILE_RENAME_INFO_0 {
+                    Flags: c::FILE_RENAME_FLAG_REPLACE_IF_EXISTS
+                        | c::FILE_RENAME_FLAG_POSIX_SEMANTICS,
+                });
+
+                (&raw mut (*file_rename_info).RootDirectory).write(ptr::null_mut());
+                // Don't include the NULL in the size
+                (&raw mut (*file_rename_info).FileNameLength).write(new_len_without_nul_in_bytes);
+
+                new.as_ptr().copy_to_nonoverlapping(
+                    (&raw mut (*file_rename_info).FileName).cast::<u16>(),
+                    new.len(),
+                );
+            }
+
+            let result = unsafe {
+                c::SetFileInformationByHandle(
+                    f.as_raw_handle(),
+                    c::FileRenameInfoEx,
+                    file_rename_info.cast::<c_void>(),
+                    struct_size,
+                )
+            };
+            unsafe { dealloc(file_rename_info.cast::<u8>(), layout) };
+            if result == 0 {
+                if api::get_last_error() == WinError::DIR_NOT_EMPTY {
+                    return Err(WinError::DIR_NOT_EMPTY).io_result();
+                } else {
+                    return Err(err).io_result();
+                }
+            }
+        } else {
+            return Err(err).io_result();
+        }
+    }
+    Ok(())
+}
+
+pub fn rmdir(p: &Path) -> io::Result<()> {
+    let p = maybe_verbatim(p)?;
+    cvt(unsafe { c::RemoveDirectoryW(p.as_ptr()) })?;
+    Ok(())
+}
+
+pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+    // Open a file or directory without following symlinks.
+    let mut opts = OpenOptions::new();
+    opts.access_mode(c::FILE_LIST_DIRECTORY);
+    // `FILE_FLAG_BACKUP_SEMANTICS` allows opening directories.
+    // `FILE_FLAG_OPEN_REPARSE_POINT` opens a link instead of its target.
+    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT);
+    let file = File::open(path, &opts)?;
+
+    // Test if the file is not a directory or a symlink to a directory.
+    if (file.basic_info()?.FileAttributes & c::FILE_ATTRIBUTE_DIRECTORY) == 0 {
+        return Err(io::Error::from_raw_os_error(c::ERROR_DIRECTORY as _));
+    }
+
+    // Remove the directory and all its contents.
+    remove_dir_all_iterative(file).io_result()
+}
+
+pub fn readlink(path: &Path) -> io::Result<PathBuf> {
+    // Open the link with no access mode, instead of generic read.
+    // By default FILE_LIST_DIRECTORY is denied for the junction "C:\Documents and Settings", so
+    // this is needed for a common case.
+    let mut opts = OpenOptions::new();
+    opts.access_mode(0);
+    opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
+    let file = File::open(path, &opts)?;
+    file.readlink()
+}
+
+pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
+    symlink_inner(original, link, false)
+}
+
+pub fn symlink_inner(original: &Path, link: &Path, dir: bool) -> io::Result<()> {
+    let original = to_u16s(original)?;
+    let link = maybe_verbatim(link)?;
+    let flags = if dir { c::SYMBOLIC_LINK_FLAG_DIRECTORY } else { 0 };
+    // Formerly, symlink creation required the SeCreateSymbolicLink privilege. For the Windows 10
+    // Creators Update, Microsoft loosened this to allow unprivileged symlink creation if the
+    // computer is in Developer Mode, but SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE must be
+    // added to dwFlags to opt into this behavior.
+    let result = cvt(unsafe {
+        c::CreateSymbolicLinkW(
+            link.as_ptr(),
+            original.as_ptr(),
+            flags | c::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
+        ) as c::BOOL
+    });
+    if let Err(err) = result {
+        if err.raw_os_error() == Some(c::ERROR_INVALID_PARAMETER as i32) {
+            // Older Windows objects to SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE,
+            // so if we encounter ERROR_INVALID_PARAMETER, retry without that flag.
+            cvt(unsafe {
+                c::CreateSymbolicLinkW(link.as_ptr(), original.as_ptr(), flags) as c::BOOL
+            })?;
+        } else {
+            return Err(err);
+        }
+    }
+    Ok(())
+}
+
+#[cfg(not(target_vendor = "uwp"))]
+pub fn link(original: &Path, link: &Path) -> io::Result<()> {
+    let original = maybe_verbatim(original)?;
+    let link = maybe_verbatim(link)?;
+    cvt(unsafe { c::CreateHardLinkW(link.as_ptr(), original.as_ptr(), ptr::null_mut()) })?;
+    Ok(())
+}
+
+#[cfg(target_vendor = "uwp")]
+pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
+    return Err(io::const_error!(io::ErrorKind::Unsupported, "hard link are not supported on UWP"));
+}
+
+pub fn stat(path: &Path) -> io::Result<FileAttr> {
+    match metadata(path, ReparsePoint::Follow) {
+        Err(err) if err.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => {
+            if let Ok(attrs) = lstat(path) {
+                if !attrs.file_type().is_symlink() {
+                    return Ok(attrs);
+                }
+            }
+            Err(err)
+        }
+        result => result,
+    }
+}
+
+pub fn lstat(path: &Path) -> io::Result<FileAttr> {
+    metadata(path, ReparsePoint::Open)
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum ReparsePoint {
+    Follow = 0,
+    Open = c::FILE_FLAG_OPEN_REPARSE_POINT,
+}
+impl ReparsePoint {
+    fn as_flag(self) -> u32 {
+        self as u32
+    }
+}
+
+fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
+    let mut opts = OpenOptions::new();
+    // No read or write permissions are necessary
+    opts.access_mode(0);
+    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag());
+
+    // Attempt to open the file normally.
+    // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileExW`.
+    // If the fallback fails for any reason we return the original error.
+    match File::open(path, &opts) {
+        Ok(file) => file.file_attr(),
+        Err(e)
+            if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)]
+                .contains(&e.raw_os_error()) =>
+        {
+            // `ERROR_ACCESS_DENIED` is returned when the user doesn't have permission for the resource.
+            // One such example is `System Volume Information` as default but can be created as well
+            // `ERROR_SHARING_VIOLATION` will almost never be returned.
+            // Usually if a file is locked you can still read some metadata.
+            // However, there are special system files, such as
+            // `C:\hiberfil.sys`, that are locked in a way that denies even that.
+            unsafe {
+                let path = maybe_verbatim(path)?;
+
+                // `FindFirstFileExW` accepts wildcard file names.
+                // Fortunately wildcards are not valid file names and
+                // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
+                // therefore it's safe to assume the file name given does not
+                // include wildcards.
+                let mut wfd: c::WIN32_FIND_DATAW = mem::zeroed();
+                let handle = c::FindFirstFileExW(
+                    path.as_ptr(),
+                    c::FindExInfoBasic,
+                    &mut wfd as *mut _ as _,
+                    c::FindExSearchNameMatch,
+                    ptr::null(),
+                    0,
+                );
+
+                if handle == c::INVALID_HANDLE_VALUE {
+                    // This can fail if the user does not have read access to the
+                    // directory.
+                    Err(e)
+                } else {
+                    // We no longer need the find handle.
+                    c::FindClose(handle);
+
+                    // `FindFirstFileExW` reads the cached file information from the
+                    // directory. The downside is that this metadata may be outdated.
+                    let attrs = FileAttr::from(wfd);
+                    if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() {
+                        Err(e)
+                    } else {
+                        Ok(attrs)
+                    }
+                }
+            }
+        }
+        Err(e) => Err(e),
+    }
+}
+
+pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> {
+    let p = maybe_verbatim(p)?;
+    unsafe {
+        cvt(c::SetFileAttributesW(p.as_ptr(), perm.attrs))?;
+        Ok(())
+    }
+}
+
+fn get_path(f: &File) -> io::Result<PathBuf> {
+    fill_utf16_buf(
+        |buf, sz| unsafe {
+            c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
+        },
+        |buf| PathBuf::from(OsString::from_wide(buf)),
+    )
+}
+
+pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
+    let mut opts = OpenOptions::new();
+    // No read or write permissions are necessary
+    opts.access_mode(0);
+    // This flag is so we can open directories too
+    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
+    let f = File::open(p, &opts)?;
+    get_path(&f)
+}
+
+pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
+    unsafe extern "system" fn callback(
+        _TotalFileSize: i64,
+        _TotalBytesTransferred: i64,
+        _StreamSize: i64,
+        StreamBytesTransferred: i64,
+        dwStreamNumber: u32,
+        _dwCallbackReason: u32,
+        _hSourceFile: c::HANDLE,
+        _hDestinationFile: c::HANDLE,
+        lpData: *const c_void,
+    ) -> u32 {
+        unsafe {
+            if dwStreamNumber == 1 {
+                *(lpData as *mut i64) = StreamBytesTransferred;
+            }
+            c::PROGRESS_CONTINUE
+        }
+    }
+    let pfrom = maybe_verbatim(from)?;
+    let pto = maybe_verbatim(to)?;
+    let mut size = 0i64;
+    cvt(unsafe {
+        c::CopyFileExW(
+            pfrom.as_ptr(),
+            pto.as_ptr(),
+            Some(callback),
+            (&raw mut size) as *mut _,
+            ptr::null_mut(),
+            0,
+        )
+    })?;
+    Ok(size as u64)
+}
+
+pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> {
+    // Create and open a new directory in one go.
+    let mut opts = OpenOptions::new();
+    opts.create_new(true);
+    opts.write(true);
+    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_POSIX_SEMANTICS);
+    opts.attributes(c::FILE_ATTRIBUTE_DIRECTORY);
+
+    let d = File::open(link, &opts)?;
+
+    // We need to get an absolute, NT-style path.
+    let path_bytes = original.as_os_str().as_encoded_bytes();
+    let abs_path: Vec<u16> = if path_bytes.starts_with(br"\\?\") || path_bytes.starts_with(br"\??\")
+    {
+        // It's already an absolute path, we just need to convert the prefix to `\??\`
+        let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&path_bytes[4..]) };
+        r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()
+    } else {
+        // Get an absolute path and then convert the prefix to `\??\`
+        let abs_path = crate::path::absolute(original)?.into_os_string().into_encoded_bytes();
+        if abs_path.len() > 0 && abs_path[1..].starts_with(br":\") {
+            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path) };
+            r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()
+        } else if abs_path.starts_with(br"\\.\") {
+            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[4..]) };
+            r"\??\".encode_utf16().chain(bytes.encode_wide()).collect()
+        } else if abs_path.starts_with(br"\\") {
+            let bytes = unsafe { OsStr::from_encoded_bytes_unchecked(&abs_path[2..]) };
+            r"\??\UNC\".encode_utf16().chain(bytes.encode_wide()).collect()
+        } else {
+            return Err(io::const_error!(io::ErrorKind::InvalidInput, "path is not valid"));
+        }
+    };
+    // Defined inline so we don't have to mess about with variable length buffer.
+    #[repr(C)]
+    pub struct MountPointBuffer {
+        ReparseTag: u32,
+        ReparseDataLength: u16,
+        Reserved: u16,
+        SubstituteNameOffset: u16,
+        SubstituteNameLength: u16,
+        PrintNameOffset: u16,
+        PrintNameLength: u16,
+        PathBuffer: [MaybeUninit<u16>; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
+    }
+    let data_len = 12 + (abs_path.len() * 2);
+    if data_len > u16::MAX as usize {
+        return Err(io::const_error!(io::ErrorKind::InvalidInput, "`original` path is too long"));
+    }
+    let data_len = data_len as u16;
+    let mut header = MountPointBuffer {
+        ReparseTag: c::IO_REPARSE_TAG_MOUNT_POINT,
+        ReparseDataLength: data_len,
+        Reserved: 0,
+        SubstituteNameOffset: 0,
+        SubstituteNameLength: (abs_path.len() * 2) as u16,
+        PrintNameOffset: ((abs_path.len() + 1) * 2) as u16,
+        PrintNameLength: 0,
+        PathBuffer: [MaybeUninit::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE as usize],
+    };
+    unsafe {
+        let ptr = header.PathBuffer.as_mut_ptr();
+        ptr.copy_from(abs_path.as_ptr().cast::<MaybeUninit<u16>>(), abs_path.len());
+
+        let mut ret = 0;
+        cvt(c::DeviceIoControl(
+            d.as_raw_handle(),
+            c::FSCTL_SET_REPARSE_POINT,
+            (&raw const header).cast::<c_void>(),
+            data_len as u32 + 8,
+            ptr::null_mut(),
+            0,
+            &mut ret,
+            ptr::null_mut(),
+        ))
+        .map(drop)
+    }
+}
+
+// Try to see if a file exists but, unlike `exists`, report I/O errors.
+pub fn exists(path: &Path) -> io::Result<bool> {
+    // Open the file to ensure any symlinks are followed to their target.
+    let mut opts = OpenOptions::new();
+    // No read, write, etc access rights are needed.
+    opts.access_mode(0);
+    // Backup semantics enables opening directories as well as files.
+    opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
+    match File::open(path, &opts) {
+        Err(e) => match e.kind() {
+            // The file definitely does not exist
+            io::ErrorKind::NotFound => Ok(false),
+
+            // `ERROR_SHARING_VIOLATION` means that the file has been locked by
+            // another process. This is often temporary so we simply report it
+            // as the file existing.
+            _ if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as i32) => Ok(true),
+
+            // `ERROR_CANT_ACCESS_FILE` means that a file exists but that the
+            // reparse point could not be handled by `CreateFile`.
+            // This can happen for special files such as:
+            // * Unix domain sockets which you need to `connect` to
+            // * App exec links which require using `CreateProcess`
+            _ if e.raw_os_error() == Some(c::ERROR_CANT_ACCESS_FILE as i32) => Ok(true),
+
+            // Other errors such as `ERROR_ACCESS_DENIED` may indicate that the
+            // file exists. However, these types of errors are usually more
+            // permanent so we report them here.
+            _ => Err(e),
+        },
+        // The file was opened successfully therefore it must exist,
+        Ok(_) => Ok(true),
+    }
+}
diff --git a/library/std/src/sys/fs/windows/remove_dir_all.rs b/library/std/src/sys/fs/windows/remove_dir_all.rs
new file mode 100644
index 00000000000..f51eced8416
--- /dev/null
+++ b/library/std/src/sys/fs/windows/remove_dir_all.rs
@@ -0,0 +1,205 @@
+//! The Windows implementation of std::fs::remove_dir_all.
+//!
+//! This needs to address two issues:
+//!
+//! - It must not be possible to trick this into deleting files outside of
+//!   the parent directory (see CVE-2022-21658).
+//! - It should not fail if many threads or processes call `remove_dir_all`
+//!   on the same path.
+//!
+//! The first is handled by using the low-level `NtOpenFile` API to open a file
+//! relative to a parent directory.
+//!
+//! The second is trickier. Deleting a file works by setting its "disposition"
+//! to delete. However, it isn't actually deleted until the file is closed.
+//! During the gap between these two events, the file is in a kind of limbo
+//! state where it still exists in the filesystem but anything trying to open
+//! it fails with an error.
+//!
+//! The mitigations we use here are:
+//!
+//! - When attempting to open the file, we treat ERROR_DELETE_PENDING as a
+//!   successful delete.
+//! - If the file still hasn't been removed from the filesystem by the time we
+//!   attempt to delete the parent directory, we try to wait for it to finish.
+//!   We can't wait indefinitely though so after some number of spins, we give
+//!   up and return an error.
+//!
+//! In short, we can't guarantee this will always succeed in the event of a
+//! race but we do make a best effort such that it *should* do so.
+
+use core::ptr;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+use super::{AsRawHandle, DirBuff, File, FromRawHandle};
+use crate::sys::c;
+use crate::sys::pal::api::WinError;
+use crate::thread;
+
+// The maximum number of times to spin when waiting for deletes to complete.
+const MAX_RETRIES: usize = 50;
+
+/// A wrapper around a raw NtOpenFile call.
+///
+/// This isn't completely safe because `OBJECT_ATTRIBUTES` contains raw pointers.
+unsafe fn nt_open_file(
+    access: u32,
+    object_attribute: &c::OBJECT_ATTRIBUTES,
+    share: u32,
+    options: u32,
+) -> Result<File, WinError> {
+    unsafe {
+        let mut handle = ptr::null_mut();
+        let mut io_status = c::IO_STATUS_BLOCK::PENDING;
+        let status =
+            c::NtOpenFile(&mut handle, access, object_attribute, &mut io_status, share, options);
+        if c::nt_success(status) {
+            Ok(File::from_raw_handle(handle))
+        } else {
+            // Convert an NTSTATUS to the more familiar Win32 error code (aka "DosError")
+            let win_error = if status == c::STATUS_DELETE_PENDING {
+                // We make a special exception for `STATUS_DELETE_PENDING` because
+                // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
+                // very unhelpful because that can also mean a permission error.
+                WinError::DELETE_PENDING
+            } else {
+                WinError::new(c::RtlNtStatusToDosError(status))
+            };
+            Err(win_error)
+        }
+    }
+}
+
+/// Open the file `path` in the directory `parent`, requesting the given `access` rights.
+/// `options` will be OR'd with `FILE_OPEN_REPARSE_POINT`.
+fn open_link_no_reparse(
+    parent: &File,
+    path: &[u16],
+    access: u32,
+    options: u32,
+) -> Result<Option<File>, WinError> {
+    // This is implemented using the lower level `NtOpenFile` function as
+    // unfortunately opening a file relative to a parent is not supported by
+    // win32 functions.
+    //
+    // See https://learn.microsoft.com/windows/win32/api/winternl/nf-winternl-ntopenfile
+
+    // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been
+    // tricked into following a symlink. However, it may not be available in
+    // earlier versions of Windows.
+    static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE);
+
+    let result = unsafe {
+        let mut path_str = c::UNICODE_STRING::from_ref(path);
+        let mut object = c::OBJECT_ATTRIBUTES {
+            ObjectName: &mut path_str,
+            RootDirectory: parent.as_raw_handle(),
+            Attributes: ATTRIBUTES.load(Ordering::Relaxed),
+            ..c::OBJECT_ATTRIBUTES::default()
+        };
+        let share = c::FILE_SHARE_DELETE | c::FILE_SHARE_READ | c::FILE_SHARE_WRITE;
+        let options = c::FILE_OPEN_REPARSE_POINT | options;
+        let result = nt_open_file(access, &object, share, options);
+
+        // Retry without OBJ_DONT_REPARSE if it's not supported.
+        if matches!(result, Err(WinError::INVALID_PARAMETER))
+            && ATTRIBUTES.load(Ordering::Relaxed) == c::OBJ_DONT_REPARSE
+        {
+            ATTRIBUTES.store(0, Ordering::Relaxed);
+            object.Attributes = 0;
+            nt_open_file(access, &object, share, options)
+        } else {
+            result
+        }
+    };
+
+    // Ignore not found errors
+    match result {
+        Ok(f) => Ok(Some(f)),
+        Err(
+            WinError::FILE_NOT_FOUND
+            | WinError::PATH_NOT_FOUND
+            | WinError::BAD_NETPATH
+            | WinError::BAD_NET_NAME
+            // `DELETE_PENDING` means something else is already trying to delete it
+            // so we assume that will eventually succeed.
+            | WinError::DELETE_PENDING,
+        ) => Ok(None),
+        Err(e) => Err(e),
+    }
+}
+
+fn open_dir(parent: &File, name: &[u16]) -> Result<Option<File>, WinError> {
+    // Open the directory for synchronous directory listing.
+    open_link_no_reparse(
+        parent,
+        name,
+        c::SYNCHRONIZE | c::FILE_LIST_DIRECTORY,
+        // "_IO_NONALERT" means that a synchronous call won't be interrupted.
+        c::FILE_SYNCHRONOUS_IO_NONALERT,
+    )
+}
+
+fn delete(parent: &File, name: &[u16]) -> Result<(), WinError> {
+    // Note that the `delete` function consumes the opened file to ensure it's
+    // dropped immediately. See module comments for why this is important.
+    match open_link_no_reparse(parent, name, c::DELETE, 0) {
+        Ok(Some(f)) => f.delete(),
+        Ok(None) => Ok(()),
+        Err(e) => Err(e),
+    }
+}
+
+/// A simple retry loop that keeps running `f` while it fails with the given
+/// error code or until `MAX_RETRIES` is reached.
+fn retry<T: PartialEq>(
+    mut f: impl FnMut() -> Result<T, WinError>,
+    ignore: WinError,
+) -> Result<T, WinError> {
+    let mut i = MAX_RETRIES;
+    loop {
+        i -= 1;
+        if i == 0 {
+            return f();
+        } else {
+            let result = f();
+            if result != Err(ignore) {
+                return result;
+            }
+        }
+        thread::yield_now();
+    }
+}
+
+pub fn remove_dir_all_iterative(dir: File) -> Result<(), WinError> {
+    let mut buffer = DirBuff::new();
+    let mut dirlist = vec![dir];
+
+    let mut restart = true;
+    'outer: while let Some(dir) = dirlist.pop() {
+        let more_data = dir.fill_dir_buff(&mut buffer, restart)?;
+        for (name, is_directory) in buffer.iter() {
+            if is_directory {
+                let Some(subdir) = open_dir(&dir, &name)? else { continue };
+                dirlist.push(dir);
+                dirlist.push(subdir);
+                continue 'outer;
+            } else {
+                // Attempt to delete, retrying on sharing violation errors as these
+                // can often be very temporary. E.g. if something takes just a
+                // bit longer than expected to release a file handle.
+                retry(|| delete(&dir, &name), WinError::SHARING_VIOLATION)?;
+            }
+        }
+        if more_data {
+            dirlist.push(dir);
+            restart = false;
+        } else {
+            // Attempt to delete, retrying on not empty errors because we may
+            // need to wait some time for files to be removed from the filesystem.
+            retry(|| delete(&dir, &[]), WinError::DIR_NOT_EMPTY)?;
+            restart = true;
+        }
+    }
+    Ok(())
+}