diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs
index 694cc34cdc171..bffb9296e1156 100644
--- a/library/std/src/fs.rs
+++ b/library/std/src/fs.rs
@@ -14,10 +14,11 @@ mod tests;
 use crate::ffi::OsString;
 use crate::fmt;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write};
-use crate::path::{Path, PathBuf};
+use crate::path::{AsPath, Path, PathBuf};
 use crate::sealed::Sealed;
 use crate::sync::Arc;
-use crate::sys::fs as fs_imp;
+use crate::sys;
+use crate::sys::fs::fs_imp;
 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 use crate::time::SystemTime;
 
@@ -256,16 +257,16 @@ pub struct DirBuilder {
 /// }
 /// ```
 #[stable(feature = "fs_read_write_bytes", since = "1.26.0")]
-pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
-    fn inner(path: &Path) -> io::Result<Vec<u8>> {
-        let mut file = File::open(path)?;
+pub fn read<P: AsPath>(path: P) -> io::Result<Vec<u8>> {
+    fn inner(mut file: File) -> io::Result<Vec<u8>> {
         let size = file.metadata().map(|m| m.len() as usize).ok();
         let mut bytes = Vec::new();
         bytes.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
         io::default_read_to_end(&mut file, &mut bytes, size)?;
         Ok(bytes)
     }
-    inner(path.as_ref())
+    let file = File::open(path)?;
+    inner(file)
 }
 
 /// Read the entire contents of a file into a string.
@@ -299,16 +300,16 @@ pub fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
 /// }
 /// ```
 #[stable(feature = "fs_read_write", since = "1.26.0")]
-pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
-    fn inner(path: &Path) -> io::Result<String> {
-        let mut file = File::open(path)?;
+pub fn read_to_string<P: AsPath>(path: P) -> io::Result<String> {
+    fn inner(mut file: File) -> io::Result<String> {
         let size = file.metadata().map(|m| m.len() as usize).ok();
         let mut string = String::new();
         string.try_reserve_exact(size.unwrap_or(0)).map_err(|_| io::ErrorKind::OutOfMemory)?;
         io::default_read_to_string(&mut file, &mut string, size)?;
         Ok(string)
     }
-    inner(path.as_ref())
+    let file = File::open(path)?;
+    inner(file)
 }
 
 /// Write a slice as the entire contents of a file.
@@ -336,11 +337,12 @@ pub fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
 /// }
 /// ```
 #[stable(feature = "fs_read_write_bytes", since = "1.26.0")]
-pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
-    fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
-        File::create(path)?.write_all(contents)
+pub fn write<P: AsPath, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
+    fn inner(mut file: File, contents: &[u8]) -> io::Result<()> {
+        file.write_all(contents)
     }
-    inner(path.as_ref(), contents.as_ref())
+    let file = File::create(path)?;
+    inner(file, contents.as_ref())
 }
 
 impl File {
@@ -371,8 +373,8 @@ impl File {
     /// }
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
-        OpenOptions::new().read(true).open(path.as_ref())
+    pub fn open<P: AsPath>(path: P) -> io::Result<File> {
+        OpenOptions::new().read(true).open(path)
     }
 
     /// Opens a file in write-only mode.
@@ -400,8 +402,8 @@ impl File {
     /// }
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
-        OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref())
+    pub fn create<P: AsPath>(path: P) -> io::Result<File> {
+        OpenOptions::new().write(true).create(true).truncate(true).open(path)
     }
 
     /// Creates a new file in read-write mode; error if the file exists.
@@ -429,8 +431,8 @@ impl File {
     /// }
     /// ```
     #[stable(feature = "file_create_new", since = "1.77.0")]
-    pub fn create_new<P: AsRef<Path>>(path: P) -> io::Result<File> {
-        OpenOptions::new().read(true).write(true).create_new(true).open(path.as_ref())
+    pub fn create_new<P: AsPath>(path: P) -> io::Result<File> {
+        OpenOptions::new().read(true).write(true).create_new(true).open(path)
     }
 
     /// Returns a new OpenOptions object.
@@ -1127,12 +1129,12 @@ impl OpenOptions {
     /// [`NotFound`]: io::ErrorKind::NotFound
     /// [`PermissionDenied`]: io::ErrorKind::PermissionDenied
     #[stable(feature = "rust1", since = "1.0.0")]
-    pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
-        self._open(path.as_ref())
+    pub fn open<P: AsPath>(&self, path: P) -> io::Result<File> {
+        path.with_native_path(|path| self._open(path))
     }
 
-    fn _open(&self, path: &Path) -> io::Result<File> {
-        fs_imp::File::open(path, &self.0).map(|inner| File { inner })
+    fn _open(&self, path: &sys::path::NativePath) -> io::Result<File> {
+        fs_imp::File::open_native(path, &self.0).map(|inner| File { inner })
     }
 }
 
@@ -1884,8 +1886,8 @@ impl AsInner<fs_imp::DirEntry> for DirEntry {
 /// ```
 #[doc(alias = "rm", alias = "unlink", alias = "DeleteFile")]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
-    fs_imp::unlink(path.as_ref())
+pub fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
+    fs_imp::remove_file(path)
 }
 
 /// Given a path, query the file system to get information about a file,
@@ -1923,8 +1925,8 @@ pub fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// ```
 #[doc(alias = "stat")]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
-    fs_imp::stat(path.as_ref()).map(Metadata)
+pub fn metadata<P: AsPath>(path: P) -> io::Result<Metadata> {
+    fs_imp::metadata(path).map(Metadata)
 }
 
 /// Query the metadata about a file without following symlinks.
@@ -1958,8 +1960,8 @@ pub fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
 /// ```
 #[doc(alias = "lstat")]
 #[stable(feature = "symlink_metadata", since = "1.1.0")]
-pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
-    fs_imp::lstat(path.as_ref()).map(Metadata)
+pub fn symlink_metadata<P: AsPath>(path: P) -> io::Result<Metadata> {
+    fs_imp::symlink_metadata(path).map(Metadata)
 }
 
 /// Rename a file or directory to a new name, replacing the original file if
@@ -2002,8 +2004,8 @@ pub fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
 /// ```
 #[doc(alias = "mv", alias = "MoveFile", alias = "MoveFileEx")]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
-    fs_imp::rename(from.as_ref(), to.as_ref())
+pub fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
+    fs_imp::rename(from, to)
 }
 
 /// Copies the contents of one file to another. This function will also
@@ -2063,8 +2065,8 @@ pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()>
 #[doc(alias = "CopyFile", alias = "CopyFileEx")]
 #[doc(alias = "fclonefileat", alias = "fcopyfile")]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
-    fs_imp::copy(from.as_ref(), to.as_ref())
+pub fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
+    fs_imp::copy(from, to)
 }
 
 /// Creates a new hard link on the filesystem.
@@ -2108,8 +2110,8 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
 /// ```
 #[doc(alias = "CreateHardLink", alias = "linkat")]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
-    fs_imp::link(original.as_ref(), link.as_ref())
+pub fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+    fs_imp::hard_link(original, link)
 }
 
 /// Creates a new symbolic link on the filesystem.
@@ -2140,8 +2142,8 @@ pub fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Re
     note = "replaced with std::os::unix::fs::symlink and \
             std::os::windows::fs::{symlink_file, symlink_dir}"
 )]
-pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
-    fs_imp::symlink(original.as_ref(), link.as_ref())
+pub fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+    fs_imp::soft_link(original, link)
 }
 
 /// Reads a symbolic link, returning the file that the link points to.
@@ -2174,8 +2176,8 @@ pub fn soft_link<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Re
 /// }
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
-    fs_imp::readlink(path.as_ref())
+pub fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
+    fs_imp::read_link(path)
 }
 
 /// Returns the canonical, absolute form of a path with all intermediate
@@ -2217,8 +2219,8 @@ pub fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
 #[doc(alias = "realpath")]
 #[doc(alias = "GetFinalPathNameByHandle")]
 #[stable(feature = "fs_canonicalize", since = "1.5.0")]
-pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
-    fs_imp::canonicalize(path.as_ref())
+pub fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
+    fs_imp::canonicalize(path)
 }
 
 /// Creates a new, empty directory at the provided path
@@ -2259,8 +2261,8 @@ pub fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
 #[doc(alias = "mkdir", alias = "CreateDirectory")]
 #[stable(feature = "rust1", since = "1.0.0")]
 #[cfg_attr(not(test), rustc_diagnostic_item = "fs_create_dir")]
-pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
-    DirBuilder::new().create(path.as_ref())
+pub fn create_dir<P: AsPath>(path: P) -> io::Result<()> {
+    DirBuilder::new().create(path)
 }
 
 /// Recursively create a directory and all of its parent components if they
@@ -2303,8 +2305,8 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// }
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
-    DirBuilder::new().recursive(true).create(path.as_ref())
+pub fn create_dir_all<P: AsPath>(path: P) -> io::Result<()> {
+    DirBuilder::new().recursive(true).create(path)
 }
 
 /// Removes an empty directory.
@@ -2339,8 +2341,8 @@ pub fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// ```
 #[doc(alias = "rmdir", alias = "RemoveDirectory")]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
-    fs_imp::rmdir(path.as_ref())
+pub fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
+    fs_imp::remove_dir(path)
 }
 
 /// Removes a directory at this path, after removing all its contents. Use
@@ -2386,8 +2388,8 @@ pub fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// }
 /// ```
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
-    fs_imp::remove_dir_all(path.as_ref())
+pub fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
+    fs_imp::remove_dir_all(path)
 }
 
 /// Returns an iterator over the entries within a directory.
@@ -2462,8 +2464,8 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
 /// ```
 #[doc(alias = "ls", alias = "opendir", alias = "FindFirstFile", alias = "FindNextFile")]
 #[stable(feature = "rust1", since = "1.0.0")]
-pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
-    fs_imp::readdir(path.as_ref()).map(ReadDir)
+pub fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
+    fs_imp::read_dir(path).map(ReadDir)
 }
 
 /// Changes the permissions found on a file or a directory.
@@ -2498,8 +2500,8 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
 /// ```
 #[doc(alias = "chmod", alias = "SetFileAttributes")]
 #[stable(feature = "set_permissions", since = "1.1.0")]
-pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
-    fs_imp::set_perm(path.as_ref(), perm.0)
+pub fn set_permissions<P: AsPath>(path: P, perm: Permissions) -> io::Result<()> {
+    fs_imp::set_permissions(path, perm.0)
 }
 
 impl DirBuilder {
@@ -2558,8 +2560,8 @@ impl DirBuilder {
     /// assert!(fs::metadata(path).unwrap().is_dir());
     /// ```
     #[stable(feature = "dir_builder", since = "1.6.0")]
-    pub fn create<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
-        self._create(path.as_ref())
+    pub fn create<P: AsPath>(&self, path: P) -> io::Result<()> {
+        path.with_path(|path| self._create(path))
     }
 
     fn _create(&self, path: &Path) -> io::Result<()> {
@@ -2630,6 +2632,6 @@ impl AsInnerMut<fs_imp::DirBuilder> for DirBuilder {
 // instead.
 #[unstable(feature = "fs_try_exists", issue = "83186")]
 #[inline]
-pub fn try_exists<P: AsRef<Path>>(path: P) -> io::Result<bool> {
-    fs_imp::try_exists(path.as_ref())
+pub fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
+    fs_imp::try_exists(path)
 }
diff --git a/library/std/src/os/unix/ffi/mod.rs b/library/std/src/os/unix/ffi/mod.rs
index 5b49f50763d74..45d07d2a98347 100644
--- a/library/std/src/os/unix/ffi/mod.rs
+++ b/library/std/src/os/unix/ffi/mod.rs
@@ -38,5 +38,24 @@
 
 mod os_str;
 
+use crate::ffi::CStr;
+use crate::path::NativePath;
+
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use self::os_str::{OsStrExt, OsStringExt};
+
+#[unstable(feature = "fs_native_path", issue = "108979")]
+pub trait NativePathExt: crate::sealed::Sealed {
+    fn from_cstr(cstr: &CStr) -> &NativePath;
+    fn into_cstr(&self) -> &CStr;
+}
+
+#[unstable(feature = "fs_native_path", issue = "108979")]
+impl NativePathExt for NativePath {
+    fn from_cstr(cstr: &CStr) -> &NativePath {
+        unsafe { &*(cstr as *const CStr as *const NativePath) }
+    }
+    fn into_cstr(&self) -> &CStr {
+        unsafe { &*(self as *const Self as *const CStr) }
+    }
+}
diff --git a/library/std/src/os/unix/fs.rs b/library/std/src/os/unix/fs.rs
index 058e9b90cc7a1..a065ed60ce13a 100644
--- a/library/std/src/os/unix/fs.rs
+++ b/library/std/src/os/unix/fs.rs
@@ -8,7 +8,7 @@ use super::platform::fs::MetadataExt as _;
 use crate::fs::{self, OpenOptions, Permissions};
 use crate::io;
 use crate::os::unix::io::{AsFd, AsRawFd};
-use crate::path::Path;
+use crate::path::{AsPath, Path};
 use crate::sys;
 use crate::sys_common::{AsInner, AsInnerMut, FromInner};
 // Used for `File::read` on intra-doc links
@@ -954,8 +954,8 @@ impl DirEntryExt2 for fs::DirEntry {
 /// }
 /// ```
 #[stable(feature = "symlink", since = "1.1.0")]
-pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
-    sys::fs::symlink(original.as_ref(), link.as_ref())
+pub fn symlink<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+    sys::fs::fs_imp::soft_link(original, link)
 }
 
 /// Unix-specific extensions to [`fs::DirBuilder`].
diff --git a/library/std/src/os/wasi/fs.rs b/library/std/src/os/wasi/fs.rs
index 4525c3aa91477..e8e79f4193743 100644
--- a/library/std/src/os/wasi/fs.rs
+++ b/library/std/src/os/wasi/fs.rs
@@ -563,7 +563,9 @@ pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
 /// This is a convenience API similar to `std::os::unix::fs::symlink` and
 /// `std::os::windows::fs::symlink_file` and `std::os::windows::fs::symlink_dir`.
 pub fn symlink_path<P: AsRef<Path>, U: AsRef<Path>>(old_path: P, new_path: U) -> io::Result<()> {
-    crate::sys::fs::symlink(old_path.as_ref(), new_path.as_ref())
+    crate::sys::common::small_c_string::run_path_with_cstr(new_path.as_ref(), &|new_path| {
+        crate::sys::fs::symlink(old_path.as_ref(), new_path)
+    })
 }
 
 fn osstr2str(f: &OsStr) -> io::Result<&str> {
diff --git a/library/std/src/os/windows/ffi.rs b/library/std/src/os/windows/ffi.rs
index 96bab59d3f8d7..1e20f1cbededf 100644
--- a/library/std/src/os/windows/ffi.rs
+++ b/library/std/src/os/windows/ffi.rs
@@ -54,6 +54,8 @@
 #![stable(feature = "rust1", since = "1.0.0")]
 
 use crate::ffi::{OsStr, OsString};
+#[cfg(not(target_os = "uefi"))]
+use crate::path::NativePath;
 use crate::sealed::Sealed;
 use crate::sys::os_str::Buf;
 use crate::sys_common::wtf8::Wtf8Buf;
@@ -134,3 +136,37 @@ impl OsStrExt for OsStr {
         self.as_inner().inner.encode_wide()
     }
 }
+
+/// On Windows `NativePath` wraps a wide string for use in filesystem function calls.
+/// These strings are `&[u16]` slices.
+///
+/// # Wide strings
+///
+/// Filesystem paths in Windows are encoded as UTF-16 strings.
+/// However, because the kernel does not verify validity this may contain invalid UTF-16.
+/// Therefore we use the term "wide string" for potentially invalid UTF-16.
+#[cfg(windows)]
+#[unstable(feature = "fs_native_path", issue = "108979")]
+pub trait NativePathExt: Sealed {
+    /// Wrap a Windows wide string as a `NativePath`.
+    /// The `wide` string must be null terminated and must not otherwise contain nulls.
+    fn from_wide(wide: &[u16]) -> &NativePath;
+    /// Wrap a Windows wide string as a `NativePath` without checking for null termination or internal nulls.
+    unsafe fn from_wide_unchecked(wide: &[u16]) -> &NativePath;
+    /// Unwrap the `NativePath` to return the inner wide string.
+    fn into_wide(&self) -> &[u16];
+}
+#[cfg(windows)]
+#[unstable(feature = "fs_native_path", issue = "108979")]
+impl NativePathExt for NativePath {
+    fn from_wide(wide: &[u16]) -> &NativePath {
+        assert_eq!(crate::sys::unrolled_find_u16s(0, wide), Some(wide.len().saturating_sub(1)));
+        unsafe { Self::from_wide_unchecked(wide) }
+    }
+    unsafe fn from_wide_unchecked(wide: &[u16]) -> &NativePath {
+        &*(wide as *const [u16] as *const Self)
+    }
+    fn into_wide(&self) -> &[u16] {
+        unsafe { &*(self as *const Self as *const [u16]) }
+    }
+}
diff --git a/library/std/src/os/windows/fs.rs b/library/std/src/os/windows/fs.rs
index e9d7a13e9d5b2..cff92e0944d2b 100644
--- a/library/std/src/os/windows/fs.rs
+++ b/library/std/src/os/windows/fs.rs
@@ -6,7 +6,7 @@
 
 use crate::fs::{self, Metadata, OpenOptions};
 use crate::io;
-use crate::path::Path;
+use crate::path::AsPath;
 use crate::sealed::Sealed;
 use crate::sys;
 use crate::sys_common::{AsInner, AsInnerMut, IntoInner};
@@ -578,8 +578,9 @@ impl FileTimesExt for fs::FileTimes {
 ///
 /// [symlink-security]: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
 #[stable(feature = "symlink", since = "1.1.0")]
-pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
-    sys::fs::symlink_inner(original.as_ref(), link.as_ref(), false)
+pub fn symlink_file<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+    original
+        .with_path(|original| link.with_path(|link| sys::fs::symlink_inner(original, link, false)))
 }
 
 /// Creates a new symlink to a directory on the filesystem.
@@ -617,6 +618,7 @@ pub fn symlink_file<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io:
 ///
 /// [symlink-security]: https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/create-symbolic-links
 #[stable(feature = "symlink", since = "1.1.0")]
-pub fn symlink_dir<P: AsRef<Path>, Q: AsRef<Path>>(original: P, link: Q) -> io::Result<()> {
-    sys::fs::symlink_inner(original.as_ref(), link.as_ref(), true)
+pub fn symlink_dir<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+    original
+        .with_path(|original| link.with_path(|link| sys::fs::symlink_inner(original, link, true)))
 }
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 56ea51226f9d5..f05f05a4b75a1 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -86,7 +86,9 @@ use crate::sync::Arc;
 
 use crate::ffi::{os_str, OsStr, OsString};
 use crate::sys;
-use crate::sys::path::{is_sep_byte, is_verbatim_sep, parse_prefix, MAIN_SEP_STR};
+use crate::sys::path::{
+    is_sep_byte, is_verbatim_sep, parse_prefix, NativePath as NativePathImpl, MAIN_SEP_STR,
+};
 
 ////////////////////////////////////////////////////////////////////////////////
 // GENERAL NOTES
@@ -1108,6 +1110,55 @@ impl FusedIterator for Ancestors<'_> {}
 // Basic types and traits
 ////////////////////////////////////////////////////////////////////////////////
 
+/// Represents a path in a form native to the OS.
+#[derive(Debug)]
+#[repr(transparent)]
+#[unstable(feature = "fs_native_path", issue = "108979")]
+pub struct NativePath(pub(crate) NativePathImpl);
+
+#[unstable(feature = "sealed", issue = "none")]
+impl crate::sealed::Sealed for NativePath {}
+#[unstable(feature = "sealed", issue = "none")]
+impl crate::sealed::Sealed for &NativePath {}
+
+/// # Stable use
+///
+/// Functions that need a filesystem path will often accept any type that implements [`AsRef<Path>`].
+///
+/// These types include [`Path`], [`OsStr`] and [`str`] as well as their owned
+/// counterparts [`PathBuf`], [`OsString`] and [`String`].
+///
+/// You can also implement you own [`AsRef<Path>`] for your own types and they'll
+/// automatically work for filesystem functions.
+///
+/// ## Example
+///
+/// ```no_run
+/// use std::ffi::OsStr;
+/// use std::path::Path;
+/// use std::fs;
+///
+/// // These are all equivalent.
+/// let metadata = fs::metadata("path/to/file")?;
+/// let metadata = fs::metadata(Path::new("path/to/file"))?;
+/// let metadata = fs::metadata(OsStr::new("path/to/file"))?;
+/// # Ok::<(), std::io::Error>(())
+/// ```
+///
+/// # Unstable use
+///
+/// The `AsPath` trait can also be used with [`NativePath`] to pass platform
+/// native paths more directly to system APIs.
+#[unstable(feature = "fs_native_path", issue = "108979")]
+pub trait AsPath {
+    #[doc(hidden)]
+    #[unstable(feature = "fs_native_path_internals", issue = "none")]
+    fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T>;
+    #[doc(hidden)]
+    #[unstable(feature = "fs_native_path_internals", issue = "none")]
+    fn with_native_path<T, F: Fn(&NativePathImpl) -> io::Result<T>>(self, f: F) -> io::Result<T>;
+}
+
 /// An owned, mutable path (akin to [`String`]).
 ///
 /// This type provides methods like [`push`] and [`set_extension`] that mutate
diff --git a/library/std/src/sys/pal/hermit/fs.rs b/library/std/src/sys/pal/hermit/fs.rs
index d4da53fd3df9e..ead14ed4cbb38 100644
--- a/library/std/src/sys/pal/hermit/fs.rs
+++ b/library/std/src/sys/pal/hermit/fs.rs
@@ -7,15 +7,66 @@ use crate::io::{self, Error, ErrorKind};
 use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
 use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
 use crate::path::{Path, PathBuf};
-use crate::sys::common::small_c_string::run_path_with_cstr;
 use crate::sys::cvt;
 use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 
-pub use crate::sys_common::fs::{copy, try_exists};
 //pub use crate::sys_common::fs::remove_dir_all;
 
+pub(crate) mod fs_imp {
+    pub(crate) use super::{
+        DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
+        ReadDir,
+    };
+    use crate::io;
+    use crate::path::{AsPath, PathBuf};
+    use crate::sys::unsupported;
+
+    pub(crate) fn remove_file<P: AsPath>(_path: P) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn symlink_metadata<P: AsPath>(_path: P) -> io::Result<FileAttr> {
+        unsupported()
+    }
+    pub(crate) fn metadata<P: AsPath>(_path: P) -> io::Result<FileAttr> {
+        unsupported()
+    }
+    pub(crate) fn rename<P: AsPath, Q: AsPath>(_from: P, _to: Q) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn hard_link<P: AsPath, Q: AsPath>(_original: P, _link: Q) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn soft_link<P: AsPath, Q: AsPath>(_original: P, _link: Q) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn remove_dir<P: AsPath>(_path: P) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn read_dir<P: AsPath>(_path: P) -> io::Result<ReadDir> {
+        unsupported()
+    }
+    pub(crate) fn set_permissions<P: AsPath>(_path: P, _perms: FilePermissions) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn copy<P: AsPath, Q: AsPath>(_from: P, _to: Q) -> io::Result<u64> {
+        unsupported()
+    }
+    pub(crate) fn canonicalize<P: AsPath>(_path: P) -> io::Result<PathBuf> {
+        unsupported()
+    }
+    pub(crate) fn remove_dir_all<P: AsPath>(_path: P) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn read_link<P: AsPath>(_path: P) -> io::Result<PathBuf> {
+        unsupported()
+    }
+    pub(crate) fn try_exists<P: AsPath>(_path: P) -> io::Result<bool> {
+        unsupported()
+    }
+}
+
 #[derive(Debug)]
 pub struct File(FileDesc);
 
@@ -268,11 +319,7 @@ impl OpenOptions {
 }
 
 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> {
+    pub fn open_native(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
         let mut flags = opts.get_access_mode()?;
         flags = flags | opts.get_creation_mode()?;
 
@@ -415,52 +462,3 @@ impl FromRawFd for File {
         Self(FromRawFd::from_raw_fd(raw_fd))
     }
 }
-
-pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
-    unsupported()
-}
-
-pub fn unlink(path: &Path) -> io::Result<()> {
-    run_path_with_cstr(path, &|path| cvt(unsafe { 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<()> {
-    match perm.0 {}
-}
-
-pub fn rmdir(_p: &Path) -> io::Result<()> {
-    unsupported()
-}
-
-pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
-    //unsupported()
-    Ok(())
-}
-
-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(_p: &Path) -> io::Result<FileAttr> {
-    unsupported()
-}
-
-pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
-    unsupported()
-}
-
-pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
-    unsupported()
-}
diff --git a/library/std/src/sys/pal/solid/fs.rs b/library/std/src/sys/pal/solid/fs.rs
index a6c1336109ad7..92ec2f024c6a7 100644
--- a/library/std/src/sys/pal/solid/fs.rs
+++ b/library/std/src/sys/pal/solid/fs.rs
@@ -14,6 +14,58 @@ use crate::{
 
 pub use crate::sys_common::fs::try_exists;
 
+pub(crate) mod fs_imp {
+    pub(crate) use super::{
+        DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
+        ReadDir,
+    };
+    use crate::io;
+    use crate::path::{AsPath, PathBuf};
+
+    pub(crate) fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(super::unlink)
+    }
+    pub(crate) fn symlink_metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_path(|path| super::lstat(path))
+    }
+    pub(crate) fn metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_path(|path| super::stat(path))
+    }
+    pub(crate) fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
+        from.with_path(|from| to.with_path(|to| super::rename(from, to)))
+    }
+    pub(crate) fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        original.with_path(|original| link.with_path(|link| super::link(original, link)))
+    }
+    pub(crate) fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        original.with_path(|original| link.with_path(|link| super::symlink(original, link)))
+    }
+    pub(crate) fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(super::rmdir)
+    }
+    pub(crate) fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
+        path.with_path(super::readdir)
+    }
+    pub(crate) fn set_permissions<P: AsPath>(path: P, perms: FilePermissions) -> io::Result<()> {
+        path.with_path(|path| super::set_perm(path, perms))
+    }
+    pub(crate) fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
+        from.with_path(|from| to.with_path(|to| super::copy(from, to)))
+    }
+    pub(crate) fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_path(super::canonicalize)
+    }
+    pub(crate) fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(super::remove_dir_all)
+    }
+    pub(crate) fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_path(super::readlink)
+    }
+    pub(crate) fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
+        path.with_path(super::try_exists)
+    }
+}
+
 /// A file descriptor.
 #[derive(Clone, Copy)]
 #[rustc_layout_scalar_valid_range_start(0)]
@@ -324,7 +376,7 @@ fn cstr(path: &Path) -> io::Result<CString> {
 }
 
 impl File {
-    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+    pub fn open_native(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
         let flags = opts.get_access_mode()?
             | opts.get_creation_mode()?
             | (opts.custom_flags as c_int & !abi::O_ACCMODE);
@@ -332,7 +384,7 @@ impl File {
             let mut fd = MaybeUninit::uninit();
             error::SolidError::err_if_negative(abi::SOLID_FS_Open(
                 fd.as_mut_ptr(),
-                cstr(path)?.as_ptr(),
+                path.as_ptr(),
                 flags,
             ))
             .map_err(|e| e.as_io_error())?;
diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs
index 422a99380cc59..efdd41865e9c8 100644
--- a/library/std/src/sys/pal/unix/fs.rs
+++ b/library/std/src/sys/pal/unix/fs.rs
@@ -109,6 +109,60 @@ use libc::{dirent64, fstat64, ftruncate64, lseek64, lstat64, off64_t, open64, st
 
 pub use crate::sys_common::fs::try_exists;
 
+pub(crate) mod fs_imp {
+    use crate::io;
+    use crate::path::AsPath;
+    use crate::path::PathBuf;
+    use crate::sys::fs;
+    pub(crate) use crate::sys::fs::{
+        DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
+        ReadDir,
+    };
+
+    pub(crate) fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(fs::unlink)
+    }
+    pub(crate) fn symlink_metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_path(|path| fs::lstat(path))
+    }
+    pub(crate) fn metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_path(|path| fs::stat(path))
+    }
+    pub(crate) fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
+        from.with_path(|from| to.with_path(|to| fs::rename(from, to)))
+    }
+    pub(crate) fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        original.with_path(|original| link.with_path(|link| fs::link(original, link)))
+    }
+    pub(crate) fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        original.with_path(|original| link.with_path(|link| fs::symlink(original, link)))
+    }
+    pub(crate) fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(fs::rmdir)
+    }
+    pub(crate) fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
+        path.with_path(fs::readdir)
+    }
+    pub(crate) fn set_permissions<P: AsPath>(path: P, perms: FilePermissions) -> io::Result<()> {
+        path.with_path(|path| fs::set_perm(path, perms))
+    }
+    pub(crate) fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
+        from.with_path(|from| to.with_path(|to| fs::copy(from, to)))
+    }
+    pub(crate) fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_path(fs::canonicalize)
+    }
+    pub(crate) fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(fs::remove_dir_all)
+    }
+    pub(crate) fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_path(fs::readlink)
+    }
+    pub(crate) fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
+        path.with_path(fs::try_exists)
+    }
+}
+
 pub struct File(FileDesc);
 
 // FIXME: This should be available on Linux with all `target_env`.
@@ -1117,11 +1171,7 @@ impl OpenOptions {
 }
 
 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> {
+    pub fn open_native(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
         let flags = libc::O_CLOEXEC
             | opts.get_access_mode()?
             | opts.get_creation_mode()?
diff --git a/library/std/src/sys/pal/unix/process/process_common.rs b/library/std/src/sys/pal/unix/process/process_common.rs
index f615e8086dccf..7b43ad66589a9 100644
--- a/library/std/src/sys/pal/unix/process/process_common.rs
+++ b/library/std/src/sys/pal/unix/process/process_common.rs
@@ -481,7 +481,7 @@ impl Stdio {
                 let mut opts = OpenOptions::new();
                 opts.read(readable);
                 opts.write(!readable);
-                let fd = File::open_c(DEV_NULL, &opts)?;
+                let fd = File::open_native(DEV_NULL, &opts)?;
                 Ok((ChildStdio::Owned(fd.into_inner()), None))
             }
 
diff --git a/library/std/src/sys/pal/unsupported/fs.rs b/library/std/src/sys/pal/unsupported/fs.rs
index 6ac1b5d2bcfca..f0ec12c394516 100644
--- a/library/std/src/sys/pal/unsupported/fs.rs
+++ b/library/std/src/sys/pal/unsupported/fs.rs
@@ -1,4 +1,4 @@
-use crate::ffi::OsString;
+use crate::ffi::{CStr, OsString};
 use crate::fmt;
 use crate::hash::{Hash, Hasher};
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
@@ -14,6 +14,60 @@ pub struct ReadDir(!);
 
 pub struct DirEntry(!);
 
+#[allow(unused_variables)]
+pub(crate) mod fs_imp {
+    use super::unsupported;
+    pub(crate) use super::{
+        DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
+        ReadDir,
+    };
+    use crate::io;
+    use crate::path::{AsPath, PathBuf};
+
+    pub(crate) fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn symlink_metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        unsupported()
+    }
+    pub(crate) fn metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        unsupported()
+    }
+    pub(crate) fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
+        unsupported()
+    }
+    pub(crate) fn set_permissions<P: AsPath>(path: P, perms: FilePermissions) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
+        unsupported()
+    }
+    pub(crate) fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        unsupported()
+    }
+    pub(crate) fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
+        unsupported()
+    }
+    pub(crate) fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        unsupported()
+    }
+    pub(crate) fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
+        unsupported()
+    }
+}
+
 #[derive(Clone, Debug)]
 pub struct OpenOptions {}
 
@@ -182,7 +236,7 @@ impl OpenOptions {
 }
 
 impl File {
-    pub fn open(_path: &Path, _opts: &OpenOptions) -> io::Result<File> {
+    pub fn open_native(_path: &CStr, _opts: &OpenOptions) -> io::Result<File> {
         unsupported()
     }
 
@@ -266,59 +320,3 @@ impl fmt::Debug for File {
         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 try_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/pal/wasi/fs.rs b/library/std/src/sys/pal/wasi/fs.rs
index 529b82e019893..3db22df9021dd 100644
--- a/library/std/src/sys/pal/wasi/fs.rs
+++ b/library/std/src/sys/pal/wasi/fs.rs
@@ -17,7 +17,58 @@ use crate::sys::time::SystemTime;
 use crate::sys::unsupported;
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 
-pub use crate::sys_common::fs::try_exists;
+pub(crate) mod fs_imp {
+    pub(crate) use super::{
+        DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
+        ReadDir,
+    };
+    use crate::io;
+    use crate::path::AsPath;
+    use crate::path::PathBuf;
+
+    pub(crate) fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_native_path(super::unlink)
+    }
+    pub(crate) fn symlink_metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_native_path(|path| super::lstat(path))
+    }
+    pub(crate) fn metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_native_path(|path| super::stat(path))
+    }
+    pub(crate) fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
+        to.with_path(|to| from.with_native_path(|from| super::rename(from, to)))
+    }
+    pub(crate) fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        link.with_path(|link| original.with_native_path(|original| super::link(original, link)))
+    }
+    pub(crate) fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        original.with_path(|original| link.with_native_path(|link| super::symlink(original, link)))
+    }
+    pub(crate) fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_native_path(super::rmdir)
+    }
+    pub(crate) fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
+        path.with_path(super::readdir)
+    }
+    pub(crate) fn set_permissions<P: AsPath>(path: P, perms: FilePermissions) -> io::Result<()> {
+        path.with_path(|path| super::set_perm(path, perms))
+    }
+    pub(crate) fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
+        from.with_path(|from| to.with_path(|to| super::copy(from, to)))
+    }
+    pub(crate) fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_path(super::canonicalize)
+    }
+    pub(crate) fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(super::remove_dir_all)
+    }
+    pub(crate) fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_native_path(super::readlink)
+    }
+    pub(crate) fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
+        path.with_path(crate::sys_common::fs::try_exists)
+    }
+}
 
 pub struct File {
     fd: WasiFd,
@@ -398,7 +449,7 @@ impl OpenOptions {
 }
 
 impl File {
-    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+    pub fn open_native(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
         let (dir, file) = open_parent(path)?;
         open_at(&dir, &file, opts)
     }
@@ -548,8 +599,10 @@ impl DirBuilder {
     }
 
     pub fn mkdir(&self, p: &Path) -> io::Result<()> {
-        let (dir, file) = open_parent(p)?;
-        dir.create_directory(osstr2str(file.as_ref())?)
+        run_path_with_cstr(p, &|p| {
+            let (dir, file) = open_parent(p)?;
+            dir.create_directory(osstr2str(file.as_ref())?)
+        })
     }
 }
 
@@ -563,18 +616,18 @@ 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)?;
+    let dir = run_path_with_cstr(p, &|p| File::open_native(p, &opts))?;
     Ok(ReadDir::new(dir, p.to_path_buf()))
 }
 
-pub fn unlink(p: &Path) -> io::Result<()> {
+pub fn unlink(p: &CStr) -> io::Result<()> {
     let (dir, file) = open_parent(p)?;
     dir.unlink_file(osstr2str(file.as_ref())?)
 }
 
-pub fn rename(old: &Path, new: &Path) -> io::Result<()> {
+pub fn rename(old: &CStr, new: &Path) -> io::Result<()> {
     let (old, old_file) = open_parent(old)?;
-    let (new, new_file) = open_parent(new)?;
+    let (new, new_file) = run_path_with_cstr(new, &|new| open_parent(new))?;
     old.rename(osstr2str(old_file.as_ref())?, &new, osstr2str(new_file.as_ref())?)
 }
 
@@ -584,12 +637,12 @@ pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
     unsupported()
 }
 
-pub fn rmdir(p: &Path) -> io::Result<()> {
+pub fn rmdir(p: &CStr) -> io::Result<()> {
     let (dir, file) = open_parent(p)?;
     dir.remove_directory(osstr2str(file.as_ref())?)
 }
 
-pub fn readlink(p: &Path) -> io::Result<PathBuf> {
+pub fn readlink(p: &CStr) -> io::Result<PathBuf> {
     let (dir, file) = open_parent(p)?;
     read_link(&dir, &file)
 }
@@ -625,24 +678,24 @@ fn read_link(fd: &WasiFd, file: &Path) -> io::Result<PathBuf> {
     }
 }
 
-pub fn symlink(original: &Path, link: &Path) -> io::Result<()> {
+pub fn symlink(original: &Path, link: &CStr) -> 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<()> {
+pub fn link(original: &CStr, link: &Path) -> io::Result<()> {
     let (original, original_file) = open_parent(original)?;
-    let (link, link_file) = open_parent(link)?;
+    let (link, link_file) = run_path_with_cstr(link, &|link| 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> {
+pub fn stat(p: &CStr) -> 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> {
+pub fn lstat(p: &CStr) -> io::Result<FileAttr> {
     let (dir, file) = open_parent(p)?;
     metadata_at(&dir, 0, &file)
 }
@@ -697,53 +750,51 @@ fn open_at(fd: &WasiFd, path: &Path, opts: &OpenOptions) -> io::Result<File> {
 ///
 /// 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 {:?} could be opened",
-                        p
-                    );
-                    return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
+fn open_parent(p: &CStr) -> io::Result<(ManuallyDrop<WasiFd>, PathBuf)> {
+    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 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)),
-                ));
+                let msg = format!(
+                    "failed to find a pre-opened file descriptor \
+                    through which {:?} could be opened",
+                    p
+                );
+                return Err(io::Error::new(io::ErrorKind::Uncategorized, msg));
             }
-        }
+            let relative = CStr::from_ptr(relative_path).to_bytes().to_vec();
 
-        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;
+            return Ok((
+                ManuallyDrop::new(WasiFd::from_raw_fd(fd as c_int)),
+                PathBuf::from(OsString::from_vec(relative)),
+            ));
         }
-    })
+    }
+
+    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> {
@@ -761,7 +812,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
 }
 
 pub fn remove_dir_all(path: &Path) -> io::Result<()> {
-    let (parent, path) = open_parent(path)?;
+    let (parent, path) = run_path_with_cstr(path, &|path| open_parent(path))?;
     remove_dir_all_recursive(&parent, &path)
 }
 
diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index 3a9e7b4660b36..fbac36f2edf23 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -11,6 +11,7 @@ use crate::ptr;
 use crate::slice;
 use crate::sync::Arc;
 use crate::sys::handle::Handle;
+use crate::sys::path::NativePath;
 use crate::sys::time::SystemTime;
 use crate::sys::{c, cvt, Align8};
 use crate::sys_common::{AsInner, FromInner, IntoInner};
@@ -19,6 +20,60 @@ use crate::thread;
 use super::{api, to_u16s, IoResult};
 use crate::sys::path::maybe_verbatim;
 
+/// The crate-public interface
+pub(crate) mod fs_imp {
+    use crate::io;
+    use crate::path::AsPath;
+    use crate::path::PathBuf;
+    use crate::sys::fs;
+    pub(crate) use crate::sys::fs::{
+        DirBuilder, DirEntry, File, FileAttr, FilePermissions, FileTimes, FileType, OpenOptions,
+        ReadDir,
+    };
+    pub(crate) fn remove_file<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(fs::unlink)
+    }
+    pub(crate) fn symlink_metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_native_path(fs::lstat)
+    }
+    pub(crate) fn metadata<P: AsPath>(path: P) -> io::Result<FileAttr> {
+        path.with_native_path(fs::stat)
+    }
+    pub(crate) fn rename<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<()> {
+        from.with_path(|from| to.with_path(|to| fs::rename(from, to)))
+    }
+    pub(crate) fn hard_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        original.with_path(|original| link.with_path(|link| fs::link(original, link)))
+    }
+    pub(crate) fn soft_link<P: AsPath, Q: AsPath>(original: P, link: Q) -> io::Result<()> {
+        original.with_path(|original| link.with_path(|link| fs::symlink(original, link)))
+    }
+    pub(crate) fn remove_dir<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_path(fs::rmdir)
+    }
+    pub(crate) fn read_dir<P: AsPath>(path: P) -> io::Result<ReadDir> {
+        path.with_path(fs::readdir)
+    }
+    pub(crate) fn set_permissions<P: AsPath>(path: P, perms: FilePermissions) -> io::Result<()> {
+        path.with_path(|path| fs::set_perm(path, perms))
+    }
+    pub(crate) fn copy<P: AsPath, Q: AsPath>(from: P, to: Q) -> io::Result<u64> {
+        from.with_path(|from| to.with_path(|to| fs::copy(from, to)))
+    }
+    pub(crate) fn canonicalize<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_native_path(fs::canonicalize)
+    }
+    pub(crate) fn remove_dir_all<P: AsPath>(path: P) -> io::Result<()> {
+        path.with_native_path(fs::remove_dir_all)
+    }
+    pub(crate) fn read_link<P: AsPath>(path: P) -> io::Result<PathBuf> {
+        path.with_native_path(fs::readlink)
+    }
+    pub(crate) fn try_exists<P: AsPath>(path: P) -> io::Result<bool> {
+        path.with_native_path(fs::try_exists)
+    }
+}
+
 pub struct File {
     handle: Handle,
 }
@@ -294,8 +349,7 @@ impl OpenOptions {
 }
 
 impl File {
-    pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
-        let path = maybe_verbatim(path)?;
+    pub fn open_native(path: &NativePath, opts: &OpenOptions) -> io::Result<File> {
         let creation = opts.get_creation_mode()?;
         let handle = unsafe {
             c::CreateFileW(
@@ -1132,16 +1186,16 @@ pub fn rmdir(p: &Path) -> io::Result<()> {
 }
 
 /// Open a file or directory without following symlinks.
-fn open_link(path: &Path, access_mode: u32) -> io::Result<File> {
+fn open_link(path: &NativePath, access_mode: u32) -> io::Result<File> {
     let mut opts = OpenOptions::new();
     opts.access_mode(access_mode);
     // `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);
-    File::open(path, &opts)
+    File::open_native(path, &opts)
 }
 
-pub fn remove_dir_all(path: &Path) -> io::Result<()> {
+pub fn remove_dir_all(path: &NativePath) -> io::Result<()> {
     let file = open_link(path, c::DELETE | c::FILE_LIST_DIRECTORY)?;
 
     // Test if the file is not a directory or a symlink to a directory.
@@ -1241,14 +1295,14 @@ fn remove_dir_all_iterative(f: &File, delete: fn(&File) -> io::Result<()>) -> io
     Ok(())
 }
 
-pub fn readlink(path: &Path) -> io::Result<PathBuf> {
+pub fn readlink(path: &NativePath) -> 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)?;
+    let file = File::open_native(path, &opts)?;
     file.readlink()
 }
 
@@ -1301,7 +1355,7 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
     ));
 }
 
-pub fn stat(path: &Path) -> io::Result<FileAttr> {
+pub fn stat(path: &NativePath) -> 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) {
@@ -1315,7 +1369,7 @@ pub fn stat(path: &Path) -> io::Result<FileAttr> {
     }
 }
 
-pub fn lstat(path: &Path) -> io::Result<FileAttr> {
+pub fn lstat(path: &NativePath) -> io::Result<FileAttr> {
     metadata(path, ReparsePoint::Open)
 }
 
@@ -1331,7 +1385,7 @@ impl ReparsePoint {
     }
 }
 
-fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
+fn metadata(path: &NativePath, reparse: ReparsePoint) -> io::Result<FileAttr> {
     let mut opts = OpenOptions::new();
     // No read or write permissions are necessary
     opts.access_mode(0);
@@ -1340,7 +1394,7 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
     // Attempt to open the file normally.
     // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`.
     // If the fallback fails for any reason we return the original error.
-    match File::open(path, &opts) {
+    match File::open_native(path, &opts) {
         Ok(file) => file.file_attr(),
         Err(e)
             if [Some(c::ERROR_SHARING_VIOLATION as _), Some(c::ERROR_ACCESS_DENIED as _)]
@@ -1353,8 +1407,6 @@ fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> {
             // 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)?;
-
                 // `FindFirstFileW` accepts wildcard file names.
                 // Fortunately wildcards are not valid file names and
                 // `ERROR_SHARING_VIOLATION` means the file exists (but is locked)
@@ -1403,13 +1455,13 @@ fn get_path(f: &File) -> io::Result<PathBuf> {
     )
 }
 
-pub fn canonicalize(p: &Path) -> io::Result<PathBuf> {
+pub fn canonicalize(p: &NativePath) -> 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)?;
+    let f = File::open_native(p, &opts)?;
     get_path(&f)
 }
 
@@ -1467,7 +1519,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
     let mut opts = OpenOptions::new();
     opts.write(true);
     opts.custom_flags(c::FILE_FLAG_OPEN_REPARSE_POINT | c::FILE_FLAG_BACKUP_SEMANTICS);
-    let f = File::open(junction, &opts)?;
+    let f = File::open_native(&maybe_verbatim(junction)?, &opts)?;
     let h = f.as_inner().as_raw_handle();
     unsafe {
         let mut data =
@@ -1525,14 +1577,14 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
 }
 
 // Try to see if a file exists but, unlike `exists`, report I/O errors.
-pub fn try_exists(path: &Path) -> io::Result<bool> {
+pub fn try_exists(path: &NativePath) -> 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) {
+    match File::open_native(path, &opts) {
         Err(e) => match e.kind() {
             // The file definitely does not exist
             io::ErrorKind::NotFound => Ok(false),
diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs
index 013f588676ae8..99b5a91960ac8 100644
--- a/library/std/src/sys/pal/windows/pipe.rs
+++ b/library/std/src/sys/pal/windows/pipe.rs
@@ -3,7 +3,6 @@ use crate::os::windows::prelude::*;
 use crate::ffi::OsStr;
 use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
 use crate::mem;
-use crate::path::Path;
 use crate::ptr;
 use crate::slice;
 use crate::sync::atomic::AtomicUsize;
@@ -12,6 +11,7 @@ use crate::sys::c;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::handle::Handle;
 use crate::sys::hashmap_random_keys;
+use crate::sys::path::NativePathBuf;
 use crate::sys_common::{FromInner, IntoInner};
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -161,7 +161,8 @@ pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Res
             bInheritHandle: their_handle_inheritable as i32,
         };
         opts.security_attributes(&mut sa);
-        let theirs = File::open(Path::new(&name), &opts)?;
+        let path = NativePathBuf::from_os_str(&name)?;
+        let theirs = File::open_native(&path, &opts)?;
         let theirs = AnonPipe { inner: theirs.into_inner() };
 
         Ok(Pipes {
diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs
index e4ab2ca7da1ce..c3ecf87024290 100644
--- a/library/std/src/sys/pal/windows/process.rs
+++ b/library/std/src/sys/pal/windows/process.rs
@@ -23,7 +23,7 @@ use crate::sys::c::{self, NonZeroDWORD, EXIT_FAILURE, EXIT_SUCCESS};
 use crate::sys::cvt;
 use crate::sys::fs::{File, OpenOptions};
 use crate::sys::handle::Handle;
-use crate::sys::path;
+use crate::sys::path::{self, NativePathBuf};
 use crate::sys::pipe::{self, AnonPipe};
 use crate::sys::stdio;
 use crate::sys_common::process::{CommandEnv, CommandEnvs};
@@ -597,7 +597,8 @@ impl Stdio {
                 opts.read(stdio_id == c::STD_INPUT_HANDLE);
                 opts.write(stdio_id != c::STD_INPUT_HANDLE);
                 opts.security_attributes(&mut sa);
-                File::open(Path::new(r"\\.\NUL"), &opts).map(|file| file.into_inner())
+                File::open_native(&NativePathBuf::from_os_str(r"\\.\NUL")?, &opts)
+                    .map(|file| file.into_inner())
             }
         }
     }
diff --git a/library/std/src/sys/path/cstr_native.rs b/library/std/src/sys/path/cstr_native.rs
new file mode 100644
index 0000000000000..f983bd1434277
--- /dev/null
+++ b/library/std/src/sys/path/cstr_native.rs
@@ -0,0 +1,35 @@
+use crate::ffi::CStr;
+use crate::io;
+use crate::mem;
+use crate::path::{AsPath, Path};
+use crate::sys::common::small_c_string::run_path_with_cstr;
+
+pub type NativePath = CStr;
+
+#[unstable(feature = "fs_native_path_internals", issue = "none")]
+impl<P: AsRef<Path>> AsPath for P {
+    #[doc(hidden)]
+    fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        f(self.as_ref())
+    }
+    #[doc(hidden)]
+    fn with_native_path<T, F: Fn(&NativePath) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        run_path_with_cstr(self.as_ref(), &f)
+    }
+}
+
+#[unstable(feature = "fs_native_path_internals", issue = "none")]
+impl AsPath for &crate::path::NativePath {
+    #[doc(hidden)]
+    fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        // SAFETY: OsStr is a byte slice on platforms with CStr paths.
+        // Note: We can't use os::unix::OsStrExt because that isn't necessarily
+        // available for all platforms that use CStr paths.
+        let osstr: &Path = unsafe { mem::transmute(self.0.to_bytes()) };
+        f(osstr)
+    }
+    #[doc(hidden)]
+    fn with_native_path<T, F: Fn(&NativePath) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        f(&self.0)
+    }
+}
diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs
index 24a94ec782824..000e706db70f9 100644
--- a/library/std/src/sys/path/mod.rs
+++ b/library/std/src/sys/path/mod.rs
@@ -1,3 +1,10 @@
+cfg_if::cfg_if! {
+    if #[cfg(not(target_os = "windows"))] {
+        mod cstr_native;
+        pub use cstr_native::NativePath;
+    }
+}
+
 cfg_if::cfg_if! {
     if #[cfg(target_os = "windows")] {
         mod windows;
diff --git a/library/std/src/sys/path/windows.rs b/library/std/src/sys/path/windows.rs
index cebc791023115..91269057cb51b 100644
--- a/library/std/src/sys/path/windows.rs
+++ b/library/std/src/sys/path/windows.rs
@@ -1,6 +1,6 @@
 use crate::ffi::{OsStr, OsString};
 use crate::io;
-use crate::path::{Path, PathBuf, Prefix};
+use crate::path::{AsPath, Path, PathBuf, Prefix};
 use crate::ptr;
 use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s};
 
@@ -10,6 +10,59 @@ mod tests;
 pub const MAIN_SEP_STR: &str = "\\";
 pub const MAIN_SEP: char = '\\';
 
+pub(crate) struct NativePathBuf(pub Vec<u16>);
+impl NativePathBuf {
+    pub fn from_os_str<S: AsRef<OsStr>>(path: S) -> io::Result<Self> {
+        to_u16s(path).map(Self)
+    }
+}
+impl core::ops::Deref for NativePathBuf {
+    type Target = NativePath;
+    fn deref(&self) -> &Self::Target {
+        unsafe { NativePath::new_unchecked(&self.0) }
+    }
+}
+
+/// A null-terminated `[u16]` string.
+#[unstable(feature = "fs_native_path_internals", issue = "none")]
+#[repr(transparent)]
+#[derive(Debug)]
+pub struct NativePath(pub [u16]);
+impl NativePath {
+    pub(crate) unsafe fn new_unchecked(native: &[u16]) -> &Self {
+        unsafe { &*(native as *const [u16] as *const Self) }
+    }
+    pub(crate) fn as_ptr(&self) -> *const u16 {
+        self.0.as_ptr()
+    }
+}
+
+#[unstable(feature = "fs_native_path_internals", issue = "none")]
+impl<P: AsRef<Path>> AsPath for P {
+    #[doc(hidden)]
+    fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        f(self.as_ref())
+    }
+    #[doc(hidden)]
+    fn with_native_path<T, F: Fn(&NativePath) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        let path = maybe_verbatim(self.as_ref())?;
+        f(&path)
+    }
+}
+
+#[unstable(feature = "fs_native_path_internals", issue = "none")]
+impl AsPath for &crate::path::NativePath {
+    #[doc(hidden)]
+    fn with_path<T, F: FnOnce(&Path) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        let path = os2path(&self.0.0[..self.0.0.len().saturating_sub(1)]);
+        f(&path)
+    }
+    #[doc(hidden)]
+    fn with_native_path<T, F: Fn(&NativePath) -> io::Result<T>>(self, f: F) -> io::Result<T> {
+        f(&self.0)
+    }
+}
+
 #[inline]
 pub fn is_sep_byte(b: u8) -> bool {
     b == b'/' || b == b'\\'
@@ -213,9 +266,9 @@ fn parse_next_component(path: &OsStr, verbatim: bool) -> (&OsStr, &OsStr) {
 /// Returns a UTF-16 encoded path capable of bypassing the legacy `MAX_PATH` limits.
 ///
 /// This path may or may not have a verbatim prefix.
-pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<Vec<u16>> {
+pub(crate) fn maybe_verbatim(path: &Path) -> io::Result<NativePathBuf> {
     let path = to_u16s(path)?;
-    get_long_path(path, true)
+    get_long_path(path, true).map(NativePathBuf)
 }
 
 /// Get a normalized absolute path that can bypass path length limits.
diff --git a/library/std/src/sys/path/windows/tests.rs b/library/std/src/sys/path/windows/tests.rs
index 623c6236166da..05c459536d636 100644
--- a/library/std/src/sys/path/windows/tests.rs
+++ b/library/std/src/sys/path/windows/tests.rs
@@ -38,7 +38,7 @@ fn verbatim() {
     use crate::path::Path;
     fn check(path: &str, expected: &str) {
         let verbatim = maybe_verbatim(Path::new(path)).unwrap();
-        let verbatim = String::from_utf16_lossy(verbatim.strip_suffix(&[0]).unwrap());
+        let verbatim = String::from_utf16_lossy(verbatim.0.strip_suffix(&[0]).unwrap());
         assert_eq!(&verbatim, expected, "{}", path);
     }
 
diff --git a/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr b/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr
index 1f08649428a4c..1b0e74912e61d 100644
--- a/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr
+++ b/src/tools/miri/tests/fail/shims/fs/isolated_file.stderr
@@ -8,15 +8,15 @@ LL |         let fd = cvt_r(|| unsafe { open64(path.as_ptr(), flags, opts.mode a
    = help: or pass `-Zmiri-isolation-error=warn` to configure Miri to return an error code from isolated operations (if supported for that operation) and continue with a warning
    = note: BACKTRACE:
    = note: inside closure at RUSTLIB/std/src/sys/pal/PLATFORM/fs.rs:LL:CC
-   = note: inside `std::sys::pal::PLATFORM::cvt_r::<i32, {closure@std::sys::pal::PLATFORM::fs::File::open_c::{closure#0}}>` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::sys::pal::PLATFORM::fs::File::open_c` at RUSTLIB/std/src/sys/pal/PLATFORM/fs.rs:LL:CC
-   = note: inside closure at RUSTLIB/std/src/sys/pal/PLATFORM/fs.rs:LL:CC
-   = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::<std::sys::pal::PLATFORM::fs::File>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
-   = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::<std::sys::pal::PLATFORM::fs::File>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
-   = note: inside `std::sys::pal::PLATFORM::small_c_string::run_path_with_cstr::<std::sys::pal::PLATFORM::fs::File>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
-   = note: inside `std::sys::pal::PLATFORM::fs::File::open` at RUSTLIB/std/src/sys/pal/PLATFORM/fs.rs:LL:CC
+   = note: inside `std::sys::pal::PLATFORM::cvt_r::<i32, {closure@std::sys::pal::PLATFORM::fs::File::open_native::{closure#0}}>` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+   = note: inside `std::sys::pal::PLATFORM::fs::File::open_native` at RUSTLIB/std/src/sys/pal/PLATFORM/fs.rs:LL:CC
    = note: inside `std::fs::OpenOptions::_open` at RUSTLIB/std/src/fs.rs:LL:CC
-   = note: inside `std::fs::OpenOptions::open::<&std::path::Path>` at RUSTLIB/std/src/fs.rs:LL:CC
+   = note: inside closure at RUSTLIB/std/src/fs.rs:LL:CC
+   = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::<std::fs::File>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
+   = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::<std::fs::File>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
+   = note: inside `std::sys::pal::PLATFORM::small_c_string::run_path_with_cstr::<std::fs::File>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
+   = note: inside `std::sys::path::cstr_native::<impl std::path::AsPath for &str>::with_native_path::<std::fs::File, {closure@std::fs::OpenOptions::open<&str>::{closure#0}}>` at RUSTLIB/std/src/sys/path/cstr_native.rs:LL:CC
+   = note: inside `std::fs::OpenOptions::open::<&str>` at RUSTLIB/std/src/fs.rs:LL:CC
    = note: inside `std::fs::File::open::<&str>` at RUSTLIB/std/src/fs.rs:LL:CC
 note: inside `main`
   --> $DIR/isolated_file.rs:LL:CC
diff --git a/tests/ui/async-await/issue-72442.stderr b/tests/ui/async-await/issue-72442.stderr
index e68f7a299908f..f559b72f68330 100644
--- a/tests/ui/async-await/issue-72442.stderr
+++ b/tests/ui/async-await/issue-72442.stderr
@@ -1,11 +1,13 @@
-error[E0277]: the trait bound `Option<&str>: AsRef<Path>` is not satisfied
+error[E0277]: the trait bound `Option<&str>: AsPath` is not satisfied
   --> $DIR/issue-72442.rs:12:36
    |
 LL |             let mut f = File::open(path.to_str())?;
-   |                         ---------- ^^^^^^^^^^^^^ the trait `AsRef<Path>` is not implemented for `Option<&str>`
+   |                         ---------- ^^^^^^^^^^^^^ the trait `AsRef<Path>` is not implemented for `Option<&str>`, which is required by `Option<&str>: AsPath`
    |                         |
    |                         required by a bound introduced by this call
    |
+   = help: the trait `AsPath` is implemented for `&NativePath`
+   = note: required for `Option<&str>` to implement `AsPath`
 note: required by a bound in `File::open`
   --> $SRC_DIR/std/src/fs.rs:LL:COL
 help: consider removing this method call, as the receiver has type `&Path` and `&Path: AsRef<Path>` trivially holds