Skip to content

Commit 5fec5e5

Browse files
committed
add windows implementation
1 parent 079e1c8 commit 5fec5e5

File tree

1 file changed

+220
-4
lines changed

1 file changed

+220
-4
lines changed

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

Lines changed: 220 additions & 4 deletions
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,
@@ -854,6 +859,217 @@ impl File {
854859
}
855860
}
856861

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

1495-
fn get_path(f: &File) -> io::Result<PathBuf> {
1711+
fn get_path(f: impl AsRawHandle) -> io::Result<PathBuf> {
14961712
fill_utf16_buf(
14971713
|buf, sz| unsafe {
1498-
c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1714+
c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
14991715
},
15001716
|buf| PathBuf::from(OsString::from_wide(buf)),
15011717
)
@@ -1508,7 +1724,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
15081724
// This flag is so we can open directories too
15091725
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
15101726
let f = File::open_native(p, &opts)?;
1511-
get_path(&f)
1727+
get_path(f.handle)
15121728
}
15131729

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

0 commit comments

Comments
 (0)