From 6a264e26d4e3072d85af0b99e9ff789ef9cdfbef Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 10 Jan 2024 21:02:18 -0800 Subject: [PATCH] Use more custom traits. (#346) Use custom traits instead of libstd's `OpenOptionsExt`, `FileExt`, and `DirBuilderExt`. --- cap-async-std/src/fs/mod.rs | 4 +- cap-async-std/src/fs_utf8/mod.rs | 4 +- cap-fs-ext/src/dir_ext.rs | 6 +- cap-primitives/build.rs | 1 + cap-primitives/src/fs/dir_builder.rs | 10 +- cap-primitives/src/fs/dir_options.rs | 6 +- cap-primitives/src/fs/file.rs | 187 ++++++++++++++++++ cap-primitives/src/fs/mod.rs | 6 +- cap-primitives/src/fs/open_options.rs | 105 +++++++++- cap-primitives/src/rustix/fs/copy_impl.rs | 2 +- .../src/rustix/fs/dir_options_ext.rs | 2 +- cap-primitives/src/rustix/fs/mod.rs | 2 +- .../src/rustix/fs/open_options_ext.rs | 4 +- .../src/rustix/linux/fs/canonicalize_impl.rs | 2 +- cap-primitives/src/rustix/linux/fs/procfs.rs | 3 +- .../src/rustix/linux/fs/stat_impl.rs | 2 +- .../src/windows/fs/dir_entry_inner.rs | 2 +- cap-primitives/src/windows/fs/dir_utils.rs | 4 +- cap-primitives/src/windows/fs/oflags.rs | 5 +- .../src/windows/fs/open_options_ext.rs | 8 +- .../src/windows/fs/read_link_impl.rs | 3 +- .../src/windows/fs/set_times_impl.rs | 3 +- .../src/windows/fs/stat_unchecked.rs | 2 +- cap-std/src/fs/dir.rs | 4 +- cap-std/src/fs/file.rs | 46 ++--- cap-std/src/fs/mod.rs | 4 +- cap-std/src/fs_utf8/file.rs | 6 +- cap-std/src/fs_utf8/mod.rs | 4 +- tests/fs.rs | 4 +- tests/fs_additional.rs | 14 +- tests/fs_utf8.rs | 4 +- tests/reopen.rs | 2 +- tests/sys_common/symlink_junction.rs | 6 +- 33 files changed, 375 insertions(+), 92 deletions(-) create mode 100644 cap-primitives/src/fs/file.rs diff --git a/cap-async-std/src/fs/mod.rs b/cap-async-std/src/fs/mod.rs index b04c5bce..11e00162 100644 --- a/cap-async-std/src/fs/mod.rs +++ b/cap-async-std/src/fs/mod.rs @@ -40,9 +40,9 @@ pub use cap_primitives::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permis // Re-export conditional types from `cap_primitives`. #[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))] pub use cap_primitives::fs::FileTypeExt; -pub use cap_primitives::fs::MetadataExt; +pub use cap_primitives::fs::{FileExt, OpenOptionsExt, MetadataExt}; #[cfg(unix)] -pub use cap_primitives::fs::PermissionsExt; +pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt}; // Re-export things from `async_std` that we can use as-is. #[cfg(target_os = "wasi")] diff --git a/cap-async-std/src/fs_utf8/mod.rs b/cap-async-std/src/fs_utf8/mod.rs index c656ee5e..8503f6cd 100644 --- a/cap-async-std/src/fs_utf8/mod.rs +++ b/cap-async-std/src/fs_utf8/mod.rs @@ -25,9 +25,9 @@ pub use crate::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permissions}; // Re-export conditional types from `cap_primitives`. #[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))] pub use cap_primitives::fs::FileTypeExt; -pub use cap_primitives::fs::MetadataExt; +pub use cap_primitives::fs::{FileExt, OpenOptionsExt, MetadataExt}; #[cfg(unix)] -pub use cap_primitives::fs::PermissionsExt; +pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt}; // Re-export `camino` to make it easy for users to depend on the same // version we do, because we use its types in our public API. diff --git a/cap-fs-ext/src/dir_ext.rs b/cap-fs-ext/src/dir_ext.rs index a8c80701..0b52478c 100644 --- a/cap-fs-ext/src/dir_ext.rs +++ b/cap-fs-ext/src/dir_ext.rs @@ -595,8 +595,7 @@ impl DirExt for cap_std::fs::Dir { fn remove_file_or_symlink>(&self, path: P) -> io::Result<()> { use crate::OpenOptionsFollowExt; use cap_primitives::fs::_WindowsByHandle; - use cap_std::fs::OpenOptions; - use std::os::windows::fs::OpenOptionsExt; + use cap_std::fs::{OpenOptions, OpenOptionsExt}; use windows_sys::Win32::Storage::FileSystem::{ DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, @@ -1092,8 +1091,7 @@ impl DirExtUtf8 for cap_std::fs_utf8::Dir { fn remove_file_or_symlink>(&self, path: P) -> io::Result<()> { use crate::OpenOptionsFollowExt; use cap_primitives::fs::_WindowsByHandle; - use cap_std::fs::OpenOptions; - use std::os::windows::fs::OpenOptionsExt; + use cap_std::fs::{OpenOptions, OpenOptionsExt}; use windows_sys::Win32::Storage::FileSystem::{ DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, diff --git a/cap-primitives/build.rs b/cap-primitives/build.rs index 7fbba9a5..7c33bc9a 100644 --- a/cap-primitives/build.rs +++ b/cap-primitives/build.rs @@ -2,6 +2,7 @@ use std::env::var; use std::io::Write; fn main() { + use_feature_or_nothing("unix_file_vectored_at"); use_feature_or_nothing("windows_by_handle"); // https://github.com/rust-lang/rust/issues/63010 // https://doc.rust-lang.org/unstable-book/library-features/windows-file-type-ext.html use_feature_or_nothing("windows_file_type_ext"); diff --git a/cap-primitives/src/fs/dir_builder.rs b/cap-primitives/src/fs/dir_builder.rs index 6dd59cce..53b5174f 100644 --- a/cap-primitives/src/fs/dir_builder.rs +++ b/cap-primitives/src/fs/dir_builder.rs @@ -57,8 +57,16 @@ impl DirBuilder { } } +/// Unix-specific extensions to [`fs::DirBuilder`]. #[cfg(unix)] -impl std::os::unix::fs::DirBuilderExt for DirBuilder { +pub trait DirBuilderExt { + /// Sets the mode to create new directories with. This option defaults to + /// 0o777. + fn mode(&mut self, mode: u32) -> &mut Self; +} + +#[cfg(unix)] +impl DirBuilderExt for DirBuilder { #[inline] fn mode(&mut self, mode: u32) -> &mut Self { self.options.mode(mode); diff --git a/cap-primitives/src/fs/dir_options.rs b/cap-primitives/src/fs/dir_options.rs index 358492eb..d7dd8129 100644 --- a/cap-primitives/src/fs/dir_options.rs +++ b/cap-primitives/src/fs/dir_options.rs @@ -25,7 +25,7 @@ impl DirOptions { } #[cfg(unix)] -impl std::os::unix::fs::DirBuilderExt for DirOptions { +impl crate::fs::DirBuilderExt for DirOptions { #[inline] fn mode(&mut self, mode: u32) -> &mut Self { self.ext.mode(mode); @@ -34,7 +34,7 @@ impl std::os::unix::fs::DirBuilderExt for DirOptions { } #[cfg(target_os = "vxworks")] -impl std::os::vxworks::fs::DirBuilderExt for DirOptions { +impl crate::fs::DirBuilderExt for DirOptions { #[inline] fn mode(&mut self, mode: u32) -> &mut Self { self.ext.mode(mode); @@ -46,7 +46,7 @@ impl std::os::vxworks::fs::DirBuilderExt for DirOptions { impl arbitrary::Arbitrary<'_> for DirOptions { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { #[cfg(any(unix, target_os = "vxworks"))] - use std::os::unix::fs::DirBuilderExt; + use crate::fs::DirBuilderExt; #[allow(unused_mut)] let mut dir_options = Self::new(); diff --git a/cap-primitives/src/fs/file.rs b/cap-primitives/src/fs/file.rs new file mode 100644 index 00000000..aaedbde0 --- /dev/null +++ b/cap-primitives/src/fs/file.rs @@ -0,0 +1,187 @@ +use std::io; + +/// Unix-specific extensions to [`fs::File`]. +#[cfg(unix)] +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; + + /// Like `read_at`, except that it reads into a slice of buffers. + #[cfg(feature = "unix_file_vectored_at")] + fn read_vectored_at( + &self, + bufs: &mut [std::io::IoSliceMut<'_>], + offset: u64, + ) -> io::Result { + io::default_read_vectored(|b| self.read_at(b, offset), bufs) + } + + /// Reads the exact number of bytes required to fill `buf` from the given offset. + fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.read_at(buf, offset) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + offset += n as u64; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "failed to fill whole buffer", + )) + } else { + Ok(()) + } + } + + /// Writes a number of bytes starting from a given offset. + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; + + /// Like `write_at`, except that it writes from a slice of buffers. + #[cfg(feature = "unix_file_vectored_at")] + fn write_vectored_at(&self, bufs: &[std::io::IoSlice<'_>], offset: u64) -> io::Result { + io::default_write_vectored(|b| self.write_at(b, offset), bufs) + } + + /// Attempts to write an entire buffer starting from a given offset. + fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.write_at(buf, offset) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } +} + +/// WASI-specific extensions to [`fs::File`]. +#[cfg(target_os = "wasi")] +pub trait FileExt { + /// Reads a number of bytes starting from a given offset. + fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { + let bufs = &mut [std::io::IoSliceMut::new(buf)]; + self.read_vectored_at(bufs, offset) + } + + /// Reads a number of bytes starting from a given offset. + fn read_vectored_at( + &self, + bufs: &mut [std::io::IoSliceMut<'_>], + offset: u64, + ) -> io::Result; + + /// Reads the exact number of byte required to fill `buf` from the given offset. + fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.read_at(buf, offset) { + Ok(0) => break, + Ok(n) => { + let tmp = buf; + buf = &mut tmp[n..]; + offset += n as u64; + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + if !buf.is_empty() { + Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "failed to fill whole buffer", + )) + } else { + Ok(()) + } + } + + /// Writes a number of bytes starting from a given offset. + fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { + let bufs = &[std::io::IoSlice::new(buf)]; + self.write_vectored_at(bufs, offset) + } + + /// Writes a number of bytes starting from a given offset. + fn write_vectored_at(&self, bufs: &[std::io::IoSlice<'_>], offset: u64) -> io::Result; + + /// Attempts to write an entire buffer starting from a given offset. + fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { + while !buf.is_empty() { + match self.write_at(buf, offset) { + Ok(0) => { + return Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write whole buffer", + )); + } + Ok(n) => { + buf = &buf[n..]; + offset += n as u64 + } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Returns the current position within the file. + fn tell(&self) -> io::Result; + + /// Adjust the flags associated with this file. + fn fdstat_set_flags(&self, flags: u16) -> std::io::Result<()>; + + /// Adjust the rights associated with this file. + fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> std::io::Result<()>; + + /// Provide file advisory information on a file descriptor. + fn advise(&self, offset: u64, len: u64, advice: u8) -> std::io::Result<()>; + + /// Force the allocation of space in a file. + fn allocate(&self, offset: u64, len: u64) -> std::io::Result<()>; + + /// Create a directory. + fn create_directory>(&self, dir: P) -> std::io::Result<()>; + + /// Read the contents of a symbolic link. + fn read_link>(&self, path: P) -> io::Result; + + /// Return the attributes of a file or directory. + fn metadata_at>( + &self, + lookup_flags: u32, + path: P, + ) -> io::Result; + + /// Unlink a file. + fn remove_file>(&self, path: P) -> io::Result<()>; + + /// Remove a directory. + fn remove_directory>(&self, path: P) -> io::Result<()>; +} + +/// Windows-specific extensions to [`fs::File`]. +#[cfg(windows)] +pub trait FileExt { + /// Seeks to a given position and reads a number of bytes. + fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result; + + /// Seeks to a given position and writes a number of bytes. + fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result; +} diff --git a/cap-primitives/src/fs/mod.rs b/cap-primitives/src/fs/mod.rs index 63a6cf41..62ef8c9c 100644 --- a/cap-primitives/src/fs/mod.rs +++ b/cap-primitives/src/fs/mod.rs @@ -11,6 +11,7 @@ mod create_dir; mod dir_builder; mod dir_entry; mod dir_options; +mod file; #[cfg(not(any(target_os = "android", target_os = "linux", windows)))] mod file_path_by_searching; mod file_type; @@ -62,11 +63,12 @@ pub use access::{access, AccessModes, AccessType}; pub use canonicalize::canonicalize; pub use copy::copy; pub use create_dir::create_dir; -pub use dir_builder::DirBuilder; +pub use dir_builder::*; pub use dir_entry::DirEntry; #[cfg(windows)] pub use dir_entry::_WindowsDirEntryExt; pub use dir_options::DirOptions; +pub use file::FileExt; pub use file_type::FileType; #[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))] pub use file_type::FileTypeExt; @@ -81,7 +83,7 @@ pub use metadata::{Metadata, MetadataExt}; pub use open::open; pub use open_ambient::open_ambient; pub use open_dir::*; -pub use open_options::OpenOptions; +pub use open_options::*; pub use permissions::Permissions; #[cfg(unix)] pub use permissions::PermissionsExt; diff --git a/cap-primitives/src/fs/open_options.rs b/cap-primitives/src/fs/open_options.rs index 9d81965b..3faec096 100644 --- a/cap-primitives/src/fs/open_options.rs +++ b/cap-primitives/src/fs/open_options.rs @@ -1,4 +1,4 @@ -use crate::fs::{FollowSymlinks, OpenOptionsExt}; +use crate::fs::{FollowSymlinks, ImplOpenOptionsExt}; /// Options and flags which can be used to configure how a file is opened. /// @@ -33,7 +33,7 @@ pub struct OpenOptions { pub(crate) follow: FollowSymlinks, #[cfg(any(unix, windows, target_os = "vxworks"))] - pub(crate) ext: OpenOptionsExt, + pub(crate) ext: ImplOpenOptionsExt, } impl OpenOptions { @@ -60,7 +60,7 @@ impl OpenOptions { follow: FollowSymlinks::Yes, #[cfg(any(unix, windows, target_os = "vxworks"))] - ext: OpenOptionsExt::new(), + ext: ImplOpenOptionsExt::new(), } } @@ -250,8 +250,99 @@ impl OpenOptions { } } +/// Unix-specific extensions to [`fs::OpenOptions`]. #[cfg(unix)] -impl std::os::unix::fs::OpenOptionsExt for OpenOptions { +pub trait OpenOptionsExt { + /// Sets the mode bits that a new file will be created with. + fn mode(&mut self, mode: u32) -> &mut Self; + + /// Pass custom flags to the `flags` argument of `open`. + fn custom_flags(&mut self, flags: i32) -> &mut Self; +} + +/// WASI-specific extensions to [`fs::OpenOptions`]. +#[cfg(target_os = "wasi")] +pub trait OpenOptionsExt { + /// Pass custom `dirflags` argument to `path_open`. + fn lookup_flags(&mut self, flags: u32) -> &mut Self; + + /// Indicates whether `OpenOptions` must open a directory or not. + fn directory(&mut self, dir: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_DSYNC` is passed in the `fs_flags` + /// field of `path_open`. + fn dsync(&mut self, dsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_NONBLOCK` is passed in the `fs_flags` + /// field of `path_open`. + fn nonblock(&mut self, nonblock: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_RSYNC` is passed in the `fs_flags` + /// field of `path_open`. + fn rsync(&mut self, rsync: bool) -> &mut Self; + + /// Indicates whether `__WASI_FDFLAG_SYNC` is passed in the `fs_flags` + /// field of `path_open`. + fn sync(&mut self, sync: bool) -> &mut Self; + + /// Indicates the value that should be passed in for the `fs_rights_base` + /// parameter of `path_open`. + fn fs_rights_base(&mut self, rights: u64) -> &mut Self; + + /// Indicates the value that should be passed in for the + /// `fs_rights_inheriting` parameter of `path_open`. + fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self; + + /// Open a file or directory. + fn open_at>( + &self, + file: &std::fs::File, + path: P, + ) -> std::io::Result; +} + +/// Windows-specific extensions to [`fs::OpenOptions`]. +#[cfg(windows)] +pub trait OpenOptionsExt { + /// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`] + /// with the specified value. + fn access_mode(&mut self, access: u32) -> &mut Self; + + /// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with + /// the specified value. + fn share_mode(&mut self, val: u32) -> &mut Self; + + /// Sets extra flags for the `dwFileFlags` argument to the call to + /// [`CreateFile2`] to the specified value (or combines it with + /// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes` + /// for [`CreateFile`]). + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn custom_flags(&mut self, flags: u32) -> &mut Self; + + /// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and + /// `security_qos_flags` to set the `dwFlagsAndAttributes` for + /// [`CreateFile`]). + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + fn attributes(&mut self, val: u32) -> &mut Self; + + /// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to + /// the specified value (or combines it with `custom_flags` and `attributes` + /// to set the `dwFlagsAndAttributes` for [`CreateFile`]). + /// + /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea + /// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2 + /// [Impersonation Levels]: + /// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level + fn security_qos_flags(&mut self, flags: u32) -> &mut Self; +} + +#[cfg(unix)] +impl OpenOptionsExt for OpenOptions { #[inline] fn mode(&mut self, mode: u32) -> &mut Self { self.ext.mode(mode); @@ -266,7 +357,7 @@ impl std::os::unix::fs::OpenOptionsExt for OpenOptions { } #[cfg(target_os = "wasi")] -impl std::os::wasi::fs::OpenOptionsExt for OpenOptions { +impl OpenOptionsExt for OpenOptions { fn lookup_flags(&mut self, _: u32) -> &mut Self { todo!() } @@ -309,7 +400,7 @@ impl std::os::wasi::fs::OpenOptionsExt for OpenOptions { } #[cfg(target_os = "vxworks")] -impl std::os::vxworks::fs::OpenOptionsExt for OpenOptions { +impl OpenOptionsExt for OpenOptions { #[inline] fn mode(&mut self, mode: u32) -> &mut Self { self.ext.mode(mode); @@ -324,7 +415,7 @@ impl std::os::vxworks::fs::OpenOptionsExt for OpenOptions { } #[cfg(windows)] -impl std::os::windows::fs::OpenOptionsExt for OpenOptions { +impl OpenOptionsExt for OpenOptions { #[inline] fn access_mode(&mut self, access: u32) -> &mut Self { self.ext.access_mode(access); diff --git a/cap-primitives/src/rustix/fs/copy_impl.rs b/cap-primitives/src/rustix/fs/copy_impl.rs index 0edfde04..ee86961d 100644 --- a/cap-primitives/src/rustix/fs/copy_impl.rs +++ b/cap-primitives/src/rustix/fs/copy_impl.rs @@ -31,7 +31,7 @@ fn open_to_and_set_permissions( path: &Path, reader_metadata: fs::Metadata, ) -> io::Result<(fs::File, fs::Metadata)> { - use rustix::fs::OpenOptionsExt; + use crate::fs::OpenOptionsExt; use std::os::unix::fs::PermissionsExt; let perm = reader_metadata.permissions(); diff --git a/cap-primitives/src/rustix/fs/dir_options_ext.rs b/cap-primitives/src/rustix/fs/dir_options_ext.rs index dace0c51..07b56bcf 100644 --- a/cap-primitives/src/rustix/fs/dir_options_ext.rs +++ b/cap-primitives/src/rustix/fs/dir_options_ext.rs @@ -13,7 +13,7 @@ impl DirOptionsExt { } } -impl std::os::unix::fs::DirBuilderExt for DirOptionsExt { +impl crate::fs::DirBuilderExt for DirOptionsExt { fn mode(&mut self, mode: u32) -> &mut Self { self.mode = mode; self diff --git a/cap-primitives/src/rustix/fs/mod.rs b/cap-primitives/src/rustix/fs/mod.rs index 1ef574b9..1dd74412 100644 --- a/cap-primitives/src/rustix/fs/mod.rs +++ b/cap-primitives/src/rustix/fs/mod.rs @@ -114,7 +114,7 @@ pub(crate) use is_root_dir::is_root_dir; #[allow(unused_imports)] pub(crate) use is_same_file::{is_different_file, is_different_file_metadata, is_same_file}; pub(crate) use metadata_ext::ImplMetadataExt; -pub(crate) use open_options_ext::OpenOptionsExt; +pub(crate) use open_options_ext::ImplOpenOptionsExt; pub(crate) use open_unchecked::{open_ambient_impl, open_unchecked}; pub(crate) use permissions_ext::ImplPermissionsExt; pub(crate) use read_dir_inner::ReadDirInner; diff --git a/cap-primitives/src/rustix/fs/open_options_ext.rs b/cap-primitives/src/rustix/fs/open_options_ext.rs index 759cd7cf..dc8ca59f 100644 --- a/cap-primitives/src/rustix/fs/open_options_ext.rs +++ b/cap-primitives/src/rustix/fs/open_options_ext.rs @@ -1,10 +1,10 @@ #[derive(Debug, Clone)] -pub(crate) struct OpenOptionsExt { +pub(crate) struct ImplOpenOptionsExt { pub(crate) mode: u32, pub(crate) custom_flags: i32, } -impl OpenOptionsExt { +impl ImplOpenOptionsExt { pub(crate) const fn new() -> Self { Self { mode: 0o666, diff --git a/cap-primitives/src/rustix/linux/fs/canonicalize_impl.rs b/cap-primitives/src/rustix/linux/fs/canonicalize_impl.rs index 0b539f04..87dd9ebe 100644 --- a/cap-primitives/src/rustix/linux/fs/canonicalize_impl.rs +++ b/cap-primitives/src/rustix/linux/fs/canonicalize_impl.rs @@ -1,9 +1,9 @@ //! Path canonicalization using `/proc/self/fd`. use super::procfs::get_path_from_proc_self_fd; +use crate::fs::OpenOptionsExt; use crate::fs::{manually, open_beneath, FollowSymlinks, OpenOptions}; use rustix::fs::OFlags; -use std::os::unix::fs::OpenOptionsExt; use std::path::{Component, Path, PathBuf}; use std::{fs, io}; diff --git a/cap-primitives/src/rustix/linux/fs/procfs.rs b/cap-primitives/src/rustix/linux/fs/procfs.rs index 15922a06..342f8a17 100644 --- a/cap-primitives/src/rustix/linux/fs/procfs.rs +++ b/cap-primitives/src/rustix/linux/fs/procfs.rs @@ -6,6 +6,7 @@ //! is mounted, with actual `procfs`, and without any additional mount points //! on top of the paths we open. +use crate::fs::OpenOptionsExt; use crate::fs::{ errors, open, read_link_unchecked, set_times_follow_unchecked, OpenOptions, SystemTimeSpec, }; @@ -13,7 +14,7 @@ use io_lifetimes::{AsFd, AsFilelike}; use rustix::fs::{chmodat, AtFlags, Mode, OFlags, RawMode}; use rustix::path::DecInt; use rustix::procfs::proc_self_fd; -use std::os::unix::fs::{OpenOptionsExt, PermissionsExt}; +use std::os::unix::fs::PermissionsExt; use std::path::{Path, PathBuf}; use std::{fs, io}; diff --git a/cap-primitives/src/rustix/linux/fs/stat_impl.rs b/cap-primitives/src/rustix/linux/fs/stat_impl.rs index 813ba6e2..93d37e9f 100644 --- a/cap-primitives/src/rustix/linux/fs/stat_impl.rs +++ b/cap-primitives/src/rustix/linux/fs/stat_impl.rs @@ -15,7 +15,7 @@ pub(crate) fn stat_impl( path: &Path, follow: FollowSymlinks, ) -> io::Result { - use std::os::unix::fs::OpenOptionsExt; + use crate::fs::OpenOptionsExt; // Open the path with `O_PATH`. Use `read(true)` even though we don't need // `read` permissions, because Rust's libstd requires an access mode, and diff --git a/cap-primitives/src/windows/fs/dir_entry_inner.rs b/cap-primitives/src/windows/fs/dir_entry_inner.rs index 0dce0898..1b93c848 100644 --- a/cap-primitives/src/windows/fs/dir_entry_inner.rs +++ b/cap-primitives/src/windows/fs/dir_entry_inner.rs @@ -1,11 +1,11 @@ use super::open_options_to_std; use crate::ambient_authority; +use crate::fs::OpenOptionsExt; use crate::fs::{ open, open_ambient_dir, FileType, FollowSymlinks, ImplFileTypeExt, Metadata, OpenOptions, ReadDir, ReadDirInner, }; use std::ffi::OsString; -use std::os::windows::fs::OpenOptionsExt; use std::{fmt, fs, io}; use windows_sys::Win32::Storage::FileSystem::{ FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, diff --git a/cap-primitives/src/windows/fs/dir_utils.rs b/cap-primitives/src/windows/fs/dir_utils.rs index eb8f282d..5a0241bf 100644 --- a/cap-primitives/src/windows/fs/dir_utils.rs +++ b/cap-primitives/src/windows/fs/dir_utils.rs @@ -1,9 +1,9 @@ +use crate::fs::OpenOptionsExt; use crate::fs::{errors, OpenOptions}; use crate::AmbientAuthority; use std::ffi::OsString; use std::ops::Deref; use std::os::windows::ffi::{OsStrExt, OsStringExt}; -use std::os::windows::fs::OpenOptionsExt; use std::path::{Path, PathBuf}; use std::{fs, io}; use windows_sys::Win32::Storage::FileSystem::{ @@ -96,6 +96,8 @@ pub(crate) fn open_ambient_dir_impl( path: &Path, ambient_authority: AmbientAuthority, ) -> io::Result { + use std::os::windows::fs::OpenOptionsExt; + let _ = ambient_authority; // Set `FILE_FLAG_BACKUP_SEMANTICS` so that we can open directories. Unset diff --git a/cap-primitives/src/windows/fs/oflags.rs b/cap-primitives/src/windows/fs/oflags.rs index ea4e6889..b78133c0 100644 --- a/cap-primitives/src/windows/fs/oflags.rs +++ b/cap-primitives/src/windows/fs/oflags.rs @@ -1,6 +1,5 @@ -use crate::fs::{FollowSymlinks, OpenOptions}; +use crate::fs::{FollowSymlinks, OpenOptions, OpenOptionsExt}; use std::fs; -use std::os::windows::fs::OpenOptionsExt; use windows_sys::Win32::Storage::FileSystem::{ FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_FLAG_WRITE_THROUGH, FILE_SHARE_DELETE, @@ -53,6 +52,8 @@ pub(in super::super) fn prepare_open_options_for_open(opts: &mut OpenOptions) -> /// indicating that the `trunc` flag was requested but could not be set, /// so the file should be truncated manually after opening. pub(in super::super) fn open_options_to_std(opts: &OpenOptions) -> (fs::OpenOptions, bool) { + use std::os::windows::fs::OpenOptionsExt; + let mut opts = opts.clone(); let manually_trunc = prepare_open_options_for_open(&mut opts); diff --git a/cap-primitives/src/windows/fs/open_options_ext.rs b/cap-primitives/src/windows/fs/open_options_ext.rs index fe39cb7b..8eb57917 100644 --- a/cap-primitives/src/windows/fs/open_options_ext.rs +++ b/cap-primitives/src/windows/fs/open_options_ext.rs @@ -12,7 +12,7 @@ use windows_sys::Win32::Storage::FileSystem::{ }; #[derive(Debug, Clone)] -pub(crate) struct OpenOptionsExt { +pub(crate) struct ImplOpenOptionsExt { pub(super) access_mode: Option, pub(super) share_mode: u32, pub(super) custom_flags: u32, @@ -21,10 +21,10 @@ pub(crate) struct OpenOptionsExt { pub(super) security_qos_flags: u32, } -unsafe impl Send for OpenOptionsExt {} -unsafe impl Sync for OpenOptionsExt {} +unsafe impl Send for ImplOpenOptionsExt {} +unsafe impl Sync for ImplOpenOptionsExt {} -impl OpenOptionsExt { +impl ImplOpenOptionsExt { pub(crate) const fn new() -> Self { Self { access_mode: None, diff --git a/cap-primitives/src/windows/fs/read_link_impl.rs b/cap-primitives/src/windows/fs/read_link_impl.rs index a3edb4dd..d63e8135 100644 --- a/cap-primitives/src/windows/fs/read_link_impl.rs +++ b/cap-primitives/src/windows/fs/read_link_impl.rs @@ -1,5 +1,4 @@ -use crate::fs::{open, FollowSymlinks, OpenOptions}; -use std::os::windows::fs::OpenOptionsExt; +use crate::fs::{open, FollowSymlinks, OpenOptions, OpenOptionsExt}; use std::path::{Path, PathBuf}; use std::{fs, io}; use windows_sys::Win32::Storage::FileSystem::{ diff --git a/cap-primitives/src/windows/fs/set_times_impl.rs b/cap-primitives/src/windows/fs/set_times_impl.rs index 04363f41..83d0e338 100644 --- a/cap-primitives/src/windows/fs/set_times_impl.rs +++ b/cap-primitives/src/windows/fs/set_times_impl.rs @@ -1,5 +1,4 @@ -use crate::fs::{open, OpenOptions, SystemTimeSpec}; -use std::os::windows::fs::OpenOptionsExt; +use crate::fs::{open, OpenOptions, OpenOptionsExt, SystemTimeSpec}; use std::path::Path; use std::{fs, io}; use windows_sys::Win32::Storage::FileSystem::{ diff --git a/cap-primitives/src/windows/fs/stat_unchecked.rs b/cap-primitives/src/windows/fs/stat_unchecked.rs index d43da614..e0b3d415 100644 --- a/cap-primitives/src/windows/fs/stat_unchecked.rs +++ b/cap-primitives/src/windows/fs/stat_unchecked.rs @@ -1,5 +1,5 @@ +use crate::fs::OpenOptionsExt; use crate::fs::{open_unchecked, FollowSymlinks, Metadata, OpenOptions}; -use std::os::windows::fs::OpenOptionsExt; use std::path::Path; use std::{fs, io}; use windows_sys::Win32::Storage::FileSystem::{ diff --git a/cap-std/src/fs/dir.rs b/cap-std/src/fs/dir.rs index cc656fb9..92ce1f5c 100644 --- a/cap-std/src/fs/dir.rs +++ b/cap-std/src/fs/dir.rs @@ -1,3 +1,5 @@ +#[cfg(target_os = "wasi")] +use crate::fs::OpenOptionsExt; use crate::fs::{DirBuilder, File, Metadata, OpenOptions, ReadDir}; #[cfg(unix)] use crate::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; @@ -14,8 +16,6 @@ use io_lifetimes::AsFilelike; use io_lifetimes::{AsFd, BorrowedFd, OwnedFd}; #[cfg(windows)] use io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle}; -#[cfg(target_os = "wasi")] -use rustix::fs::OpenOptionsExt; use std::io::{self, Read, Write}; use std::path::{Path, PathBuf}; use std::{fmt, fs}; diff --git a/cap-std/src/fs/file.rs b/cap-std/src/fs/file.rs index e49ba007..a1c13b80 100644 --- a/cap-std/src/fs/file.rs +++ b/cap-std/src/fs/file.rs @@ -479,58 +479,58 @@ impl From for process::Stdio { } #[cfg(unix)] -impl std::os::unix::fs::FileExt for File { +impl crate::fs::FileExt for File { #[inline] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.std.read_at(buf, offset) + std::os::unix::fs::FileExt::read_at(&self.std, buf, offset) } #[inline] fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { - self.std.write_at(buf, offset) + std::os::unix::fs::FileExt::write_at(&self.std, buf, offset) } #[inline] fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> io::Result<()> { - self.std.read_exact_at(buf, offset) + std::os::unix::fs::FileExt::read_exact_at(&self.std, buf, offset) } #[inline] fn write_all_at(&self, buf: &[u8], offset: u64) -> io::Result<()> { - self.std.write_all_at(buf, offset) + std::os::unix::fs::FileExt::write_all_at(&self.std, buf, offset) } } #[cfg(target_os = "wasi")] -impl std::os::wasi::fs::FileExt for File { +impl crate::fs::FileExt for File { #[inline] fn read_at(&self, bufs: &mut [u8], offset: u64) -> io::Result { - self.std.read_at(bufs, offset) + std::os::wasi::fs::FileExt::read_at(&self.std, bufs, offset) } #[inline] fn write_at(&self, bufs: &[u8], offset: u64) -> io::Result { - self.std.write_at(bufs, offset) + std::os::wasi::fs::FileExt::write_at(&self.std, bufs, offset) } #[inline] fn read_vectored_at(&self, bufs: &mut [IoSliceMut], offset: u64) -> io::Result { - self.std.read_vectored_at(bufs, offset) + std::os::wasi::fs::FileExt::read_vectored_at(&self.std, bufs, offset) } #[inline] fn write_vectored_at(&self, bufs: &[IoSlice], offset: u64) -> io::Result { - self.std.write_vectored_at(bufs, offset) + std::os::wasi::fs::FileExt::write_vectored_at(&self.std, bufs, offset) } #[inline] fn tell(&self) -> std::result::Result { - self.std.tell() + std::os::wasi::fs::FileExt::tell(&self.std) } #[inline] fn fdstat_set_flags(&self, flags: u16) -> std::result::Result<(), std::io::Error> { - self.std.fdstat_set_flags(flags) + std::os::wasi::fs::FileExt::fdstat_set_flags(&self.std, flags) } #[inline] @@ -539,22 +539,22 @@ impl std::os::wasi::fs::FileExt for File { rights: u64, inheriting: u64, ) -> std::result::Result<(), std::io::Error> { - self.std.fdstat_set_rights(rights, inheriting) + std::os::wasi::fs::FileExt::fdstat_set_rights(&self.std, rights, inheriting) } #[inline] fn advise(&self, offset: u64, len: u64, advice: u8) -> std::result::Result<(), std::io::Error> { - self.std.advise(offset, len, advice) + std::os::wasi::fs::FileExt::advise(&self.std, offset, len, advice) } #[inline] fn allocate(&self, offset: u64, len: u64) -> std::result::Result<(), std::io::Error> { - self.std.allocate(offset, len) + std::os::wasi::fs::FileExt::allocate(&self.std, offset, len) } #[inline] fn create_directory>(&self, dir: P) -> std::result::Result<(), std::io::Error> { - self.std.create_directory(dir) + std::os::wasi::fs::FileExt::create_directory(&self.std, dir) } #[inline] @@ -562,7 +562,7 @@ impl std::os::wasi::fs::FileExt for File { &self, path: P, ) -> std::result::Result { - self.std.read_link(path) + std::os::wasi::fs::FileExt::read_link(&self.std, path) } #[inline] @@ -571,30 +571,30 @@ impl std::os::wasi::fs::FileExt for File { lookup_flags: u32, path: P, ) -> std::result::Result { - self.std.metadata_at(lookup_flags, path) + std::os::wasi::fs::FileExt::metadata_at(&self.std, lookup_flags, path) } #[inline] fn remove_file>(&self, path: P) -> std::result::Result<(), std::io::Error> { - self.std.remove_file(path) + std::os::wasi::fs::FileExt::remove_file(&self.std, path) } #[inline] fn remove_directory>(&self, path: P) -> std::result::Result<(), std::io::Error> { - self.std.remove_directory(path) + std::os::wasi::fs::FileExt::remove_directory(&self.std, path) } } #[cfg(windows)] -impl std::os::windows::fs::FileExt for File { +impl crate::fs::FileExt for File { #[inline] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { - self.std.seek_read(buf, offset) + std::os::windows::fs::FileExt::seek_read(&self.std, buf, offset) } #[inline] fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result { - self.std.seek_write(buf, offset) + std::os::windows::fs::FileExt::seek_write(&self.std, buf, offset) } } diff --git a/cap-std/src/fs/mod.rs b/cap-std/src/fs/mod.rs index 33c45682..b14759db 100644 --- a/cap-std/src/fs/mod.rs +++ b/cap-std/src/fs/mod.rs @@ -38,6 +38,6 @@ pub use cap_primitives::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permis // Re-export conditional types from `cap_primitives`. #[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))] pub use cap_primitives::fs::FileTypeExt; -pub use cap_primitives::fs::MetadataExt; #[cfg(unix)] -pub use cap_primitives::fs::PermissionsExt; +pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt}; +pub use cap_primitives::fs::{FileExt, MetadataExt, OpenOptionsExt}; diff --git a/cap-std/src/fs_utf8/file.rs b/cap-std/src/fs_utf8/file.rs index 098f3138..bf98382a 100644 --- a/cap-std/src/fs_utf8/file.rs +++ b/cap-std/src/fs_utf8/file.rs @@ -477,7 +477,7 @@ impl From for process::Stdio { } #[cfg(unix)] -impl std::os::unix::fs::FileExt for File { +impl crate::fs::FileExt for File { #[inline] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { self.cap_std.read_at(buf, offset) @@ -500,7 +500,7 @@ impl std::os::unix::fs::FileExt for File { } #[cfg(target_os = "wasi")] -impl std::os::wasi::fs::FileExt for File { +impl crate::fs::FileExt for File { #[inline] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { self.cap_std.read_at(buf, offset) @@ -589,7 +589,7 @@ impl std::os::wasi::fs::FileExt for File { } #[cfg(windows)] -impl std::os::windows::fs::FileExt for File { +impl crate::fs::FileExt for File { #[inline] fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result { self.cap_std.seek_read(buf, offset) diff --git a/cap-std/src/fs_utf8/mod.rs b/cap-std/src/fs_utf8/mod.rs index 996a7415..7f17ecca 100644 --- a/cap-std/src/fs_utf8/mod.rs +++ b/cap-std/src/fs_utf8/mod.rs @@ -27,9 +27,9 @@ pub use crate::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permissions}; // Re-export conditional types from `cap_primitives`. #[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))] pub use cap_primitives::fs::FileTypeExt; -pub use cap_primitives::fs::MetadataExt; #[cfg(unix)] -pub use cap_primitives::fs::PermissionsExt; +pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt}; +pub use cap_primitives::fs::{FileExt, MetadataExt, OpenOptionsExt}; // Re-export `camino` to make it easy for users to depend on the same // version we do, because we use its types in our public API. diff --git a/tests/fs.rs b/tests/fs.rs index e14b4f41..a8b90c24 100644 --- a/tests/fs.rs +++ b/tests/fs.rs @@ -264,7 +264,7 @@ fn file_test_io_eof() { #[test] #[cfg(unix)] fn file_test_io_read_write_at() { - use std::os::unix::fs::FileExt; + use cap_std::fs::FileExt; let tmpdir = tmpdir(); let filename = "file_rt_io_file_test_read_write_at.txt"; @@ -356,7 +356,7 @@ fn set_get_unix_permissions() { #[test] #[cfg(windows)] fn file_test_io_seek_read_write() { - use std::os::windows::fs::FileExt; + use cap_std::fs::FileExt; let tmpdir = tmpdir(); let filename = "file_rt_io_file_test_seek_read_write.txt"; diff --git a/tests/fs_additional.rs b/tests/fs_additional.rs index 65dd0b6d..6e6705f9 100644 --- a/tests/fs_additional.rs +++ b/tests/fs_additional.rs @@ -440,8 +440,7 @@ fn follow_file_symlink() { #[cfg(unix)] #[test] fn check_dot_access() { - use cap_std::fs::DirBuilder; - use std::os::unix::fs::DirBuilderExt; + use cap_std::fs::{DirBuilder, DirBuilderExt}; let tmpdir = tmpdir(); @@ -583,8 +582,7 @@ fn file_with_trailing_slashdot_ambient() { #[cfg(all(unix, not(any(target_os = "ios", target_os = "macos"))))] #[test] fn dir_searchable_unreadable() { - use cap_std::fs::DirBuilder; - use std::os::unix::fs::DirBuilderExt; + use cap_std::fs::{DirBuilder, DirBuilderExt}; let tmpdir = tmpdir(); @@ -628,8 +626,7 @@ fn dir_searchable_unreadable_ambient() { #[cfg(any(target_os = "ios", target_os = "macos"))] #[test] fn dir_searchable_unreadable() { - use cap_std::fs::DirBuilder; - use std::os::unix::fs::DirBuilderExt; + use cap_std::fs::{DirBuilder, DirBuilderExt}; let tmpdir = tmpdir(); @@ -645,8 +642,7 @@ fn dir_searchable_unreadable() { #[cfg(unix)] #[test] fn dir_unsearchable_unreadable() { - use cap_std::fs::DirBuilder; - use std::os::unix::fs::DirBuilderExt; + use cap_std::fs::{DirBuilder, DirBuilderExt}; let tmpdir = tmpdir(); @@ -829,7 +825,7 @@ fn readdir_write() { #[cfg(any(target_os = "android", target_os = "linux"))] { - use std::os::unix::fs::OpenOptionsExt; + use cap_std::fs::OpenOptionsExt; assert!(tmpdir .open_with( "dir", diff --git a/tests/fs_utf8.rs b/tests/fs_utf8.rs index 3af9eb4f..8ed98eb8 100644 --- a/tests/fs_utf8.rs +++ b/tests/fs_utf8.rs @@ -265,7 +265,7 @@ fn file_test_io_eof() { #[test] #[cfg(unix)] fn file_test_io_read_write_at() { - use std::os::unix::fs::FileExt; + use cap_std::fs_utf8::FileExt; let tmpdir = tmpdir(); let filename = "file_rt_io_file_test_read_write_at.txt"; @@ -357,7 +357,7 @@ fn set_get_unix_permissions() { #[test] #[cfg(windows)] fn file_test_io_seek_read_write() { - use std::os::windows::fs::FileExt; + use cap_std::fs_utf8::FileExt; let tmpdir = tmpdir(); let filename = "file_rt_io_file_test_seek_read_write.txt"; diff --git a/tests/reopen.rs b/tests/reopen.rs index eff6518c..251bf1d2 100644 --- a/tests/reopen.rs +++ b/tests/reopen.rs @@ -67,7 +67,7 @@ fn reopen_perms() { #[cfg(windows)] #[test] fn reopen_explicit_access() { - use std::os::windows::fs::OpenOptionsExt; + use cap_std::fs::OpenOptionsExt; let tmpdir = tmpdir(); diff --git a/tests/sys_common/symlink_junction.rs b/tests/sys_common/symlink_junction.rs index a774880d..3bfe2ec3 100644 --- a/tests/sys_common/symlink_junction.rs +++ b/tests/sys_common/symlink_junction.rs @@ -92,10 +92,9 @@ pub fn cvt( #[cfg(windows)] #[allow(dead_code)] fn symlink_junction_inner(original: &Path, dir: &Dir, junction: &Path) -> io::Result<()> { - use cap_std::fs::OpenOptions; + use cap_std::fs::{OpenOptions, OpenOptionsExt}; use std::mem::MaybeUninit; use std::os::windows::ffi::OsStrExt; - use std::os::windows::fs::OpenOptionsExt; use std::os::windows::io::AsRawHandle; use std::{mem, ptr}; use windows_sys::Win32::Storage::FileSystem::MAXIMUM_REPARSE_DATA_BUFFER_SIZE; @@ -177,10 +176,9 @@ fn symlink_junction_inner_utf8( dir: &cap_std::fs_utf8::Dir, junction: &Utf8Path, ) -> io::Result<()> { - use cap_std::fs::OpenOptions; + use cap_std::fs::{OpenOptions, OpenOptionsExt}; use std::mem::MaybeUninit; use std::os::windows::ffi::OsStrExt; - use std::os::windows::fs::OpenOptionsExt; use std::os::windows::io::AsRawHandle; use std::{mem, ptr}; use windows_sys::Win32::Storage::FileSystem::MAXIMUM_REPARSE_DATA_BUFFER_SIZE;