Skip to content

Commit 2e182a0

Browse files
committed
add windows implementation
1 parent bf718d8 commit 2e182a0

File tree

1 file changed

+220
-4
lines changed

1 file changed

+220
-4
lines changed

library/std/src/sys/fs/windows.rs

+220-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::os::windows::io::{AsHandle, BorrowedHandle};
1010
use crate::os::windows::prelude::*;
1111
use crate::path::{Path, PathBuf};
1212
use crate::sync::Arc;
13+
use crate::sys::api::SetFileInformation;
1314
use crate::sys::handle::Handle;
1415
use crate::sys::pal::api::{self, WinError, set_file_information_by_handle};
1516
use crate::sys::pal::{IoResult, fill_utf16_buf, to_u16s, truncate_utf16_at_nul};
@@ -26,6 +27,10 @@ pub struct File {
2627
handle: Handle,
2728
}
2829

30+
pub struct Dir {
31+
handle: Handle,
32+
}
33+
2934
#[derive(Clone)]
3035
pub struct FileAttr {
3136
attributes: u32,
@@ -846,6 +851,217 @@ impl File {
846851
}
847852
}
848853

854+
unsafe fn nt_create_file(
855+
access: u32,
856+
object_attributes: &c::OBJECT_ATTRIBUTES,
857+
share: u32,
858+
dir: bool,
859+
) -> Result<Handle, WinError> {
860+
let mut handle = ptr::null_mut();
861+
let mut io_status = c::IO_STATUS_BLOCK::PENDING;
862+
let disposition = match (access & c::GENERIC_READ > 0, access & c::GENERIC_WRITE > 0) {
863+
(true, true) => c::FILE_OPEN_IF,
864+
(true, false) => c::FILE_OPEN,
865+
(false, true) => c::FILE_CREATE,
866+
(false, false) => {
867+
return Err(WinError::new(c::ERROR_INVALID_PARAMETER));
868+
}
869+
};
870+
let status = unsafe {
871+
c::NtCreateFile(
872+
&mut handle,
873+
access,
874+
object_attributes,
875+
&mut io_status,
876+
ptr::null(),
877+
c::FILE_ATTRIBUTE_NORMAL,
878+
share,
879+
disposition,
880+
if dir { c::FILE_DIRECTORY_FILE } else { c::FILE_NON_DIRECTORY_FILE },
881+
ptr::null(),
882+
0,
883+
)
884+
};
885+
if c::nt_success(status) {
886+
// SAFETY: nt_success guarantees that handle is no longer null
887+
unsafe { Ok(Handle::from_raw_handle(handle)) }
888+
} else {
889+
let win_error = if status == c::STATUS_DELETE_PENDING {
890+
// We make a special exception for `STATUS_DELETE_PENDING` because
891+
// otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
892+
// very unhelpful because that can also mean a permission error.
893+
WinError::DELETE_PENDING
894+
} else {
895+
WinError::new(unsafe { c::RtlNtStatusToDosError(status) })
896+
};
897+
Err(win_error)
898+
}
899+
}
900+
901+
fn run_path_with_wcstr<T, P: AsRef<Path>>(
902+
path: P,
903+
f: &dyn Fn(&WCStr) -> io::Result<T>,
904+
) -> io::Result<T> {
905+
let path = maybe_verbatim(path.as_ref())?;
906+
// SAFETY: maybe_verbatim returns null-terminated strings
907+
let path = unsafe { WCStr::from_wchars_with_null_unchecked(&path) };
908+
f(path)
909+
}
910+
911+
impl Dir {
912+
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
913+
let opts = OpenOptions::new();
914+
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
915+
}
916+
917+
pub fn new_with<P: AsRef<Path>>(path: P, opts: &OpenOptions) -> io::Result<Self> {
918+
run_path_with_wcstr(path, &|path| Self::new_native(path, &opts))
919+
}
920+
921+
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
922+
let mut opts = OpenOptions::new();
923+
opts.read(true);
924+
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
925+
}
926+
927+
pub fn open_with<P: AsRef<Path>>(&self, path: P, opts: &OpenOptions) -> io::Result<File> {
928+
Ok(File { handle: run_path_with_wcstr(path, &|path| self.open_native(path, &opts))? })
929+
}
930+
931+
pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
932+
run_path_with_wcstr(path, &|path| {
933+
self.create_dir_native(path, &OpenOptions::new()).map(|_| ())
934+
})
935+
}
936+
937+
pub fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
938+
run_path_with_wcstr(path, &|path| self.remove_native(path, false))
939+
}
940+
941+
pub fn remove_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
942+
run_path_with_wcstr(path, &|path| self.remove_native(path, true))
943+
}
944+
945+
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(
946+
&self,
947+
from: P,
948+
to_dir: &Self,
949+
to: Q,
950+
) -> io::Result<()> {
951+
run_path_with_wcstr(from.as_ref(), &|from| {
952+
run_path_with_wcstr(to.as_ref(), &|to| self.rename_native(from, to_dir, to))
953+
})
954+
}
955+
956+
fn new_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
957+
let name = c::UNICODE_STRING {
958+
Length: path.count_bytes() as _,
959+
MaximumLength: path.count_bytes() as _,
960+
Buffer: path.as_ptr() as *mut _,
961+
};
962+
let object_attributes = c::OBJECT_ATTRIBUTES {
963+
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
964+
RootDirectory: ptr::null_mut(),
965+
ObjectName: &name,
966+
Attributes: 0,
967+
SecurityDescriptor: ptr::null(),
968+
SecurityQualityOfService: ptr::null(),
969+
};
970+
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
971+
let handle =
972+
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
973+
.io_result()?;
974+
Ok(Self { handle })
975+
}
976+
977+
fn open_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
978+
let name = c::UNICODE_STRING {
979+
Length: path.count_bytes() as _,
980+
MaximumLength: path.count_bytes() as _,
981+
Buffer: path.as_ptr() as *mut _,
982+
};
983+
let object_attributes = c::OBJECT_ATTRIBUTES {
984+
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
985+
RootDirectory: self.handle.as_raw_handle(),
986+
ObjectName: &name,
987+
Attributes: 0,
988+
SecurityDescriptor: ptr::null(),
989+
SecurityQualityOfService: ptr::null(),
990+
};
991+
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
992+
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, false) }
993+
.io_result()
994+
}
995+
996+
fn create_dir_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
997+
let name = c::UNICODE_STRING {
998+
Length: path.count_bytes() as _,
999+
MaximumLength: path.count_bytes() as _,
1000+
Buffer: path.as_ptr() as *mut _,
1001+
};
1002+
let object_attributes = c::OBJECT_ATTRIBUTES {
1003+
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
1004+
RootDirectory: self.handle.as_raw_handle(),
1005+
ObjectName: &name,
1006+
Attributes: 0,
1007+
SecurityDescriptor: ptr::null(),
1008+
SecurityQualityOfService: ptr::null(),
1009+
};
1010+
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
1011+
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
1012+
.io_result()
1013+
}
1014+
1015+
fn remove_native(&self, path: &WCStr, dir: bool) -> io::Result<()> {
1016+
let mut opts = OpenOptions::new();
1017+
opts.access_mode(c::GENERIC_WRITE);
1018+
let handle =
1019+
if dir { self.create_dir_native(path, &opts) } else { self.open_native(path, &opts) }?;
1020+
let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE };
1021+
let result = unsafe {
1022+
c::SetFileInformationByHandle(
1023+
handle.as_raw_handle(),
1024+
c::FileDispositionInfoEx,
1025+
(&info).as_ptr(),
1026+
size_of::<c::FILE_DISPOSITION_INFO_EX>() as _,
1027+
)
1028+
};
1029+
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
1030+
}
1031+
1032+
fn rename_native(&self, from: &WCStr, to_dir: &Self, to: &WCStr) -> io::Result<()> {
1033+
let mut opts = OpenOptions::new();
1034+
opts.access_mode(c::GENERIC_WRITE);
1035+
let handle = self.open_native(from, &opts)?;
1036+
let info = c::FILE_RENAME_INFO {
1037+
Anonymous: c::FILE_RENAME_INFO_0 { ReplaceIfExists: true },
1038+
RootDirectory: to_dir.handle.as_raw_handle(),
1039+
FileNameLength: to.count_bytes() as _,
1040+
FileName: [to.as_ptr() as u16],
1041+
};
1042+
let result = unsafe {
1043+
c::SetFileInformationByHandle(
1044+
handle.as_raw_handle(),
1045+
c::FileRenameInfo,
1046+
ptr::addr_of!(info) as _,
1047+
size_of::<c::FILE_RENAME_INFO>() as _,
1048+
)
1049+
};
1050+
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
1051+
}
1052+
}
1053+
1054+
impl fmt::Debug for Dir {
1055+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1056+
let mut b = f.debug_struct("Dir");
1057+
b.field("handle", &self.handle.as_raw_handle());
1058+
if let Ok(path) = get_path(self.handle.as_handle()) {
1059+
b.field("path", &path);
1060+
}
1061+
b.finish()
1062+
}
1063+
}
1064+
8491065
/// A buffer for holding directory entries.
8501066
struct DirBuff {
8511067
buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
@@ -995,7 +1211,7 @@ impl fmt::Debug for File {
9951211
// FIXME(#24570): add more info here (e.g., mode)
9961212
let mut b = f.debug_struct("File");
9971213
b.field("handle", &self.handle.as_raw_handle());
998-
if let Ok(path) = get_path(self) {
1214+
if let Ok(path) = get_path(self.handle.as_handle()) {
9991215
b.field("path", &path);
10001216
}
10011217
b.finish()
@@ -1484,10 +1700,10 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
14841700
}
14851701
}
14861702

1487-
fn get_path(f: &File) -> io::Result<PathBuf> {
1703+
fn get_path(f: impl AsRawHandle) -> io::Result<PathBuf> {
14881704
fill_utf16_buf(
14891705
|buf, sz| unsafe {
1490-
c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1706+
c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
14911707
},
14921708
|buf| PathBuf::from(OsString::from_wide(buf)),
14931709
)
@@ -1500,7 +1716,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
15001716
// This flag is so we can open directories too
15011717
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
15021718
let f = File::open_native(p, &opts)?;
1503-
get_path(&f)
1719+
get_path(f.handle)
15041720
}
15051721

15061722
pub fn copy(from: &WCStr, to: &WCStr) -> io::Result<u64> {

0 commit comments

Comments
 (0)