Skip to content

Commit fb4e8d3

Browse files
committed
add windows implementation
1 parent bf718d8 commit fb4e8d3

File tree

1 file changed

+218
-4
lines changed

1 file changed

+218
-4
lines changed

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

+218-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,215 @@ 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| run_path_with_wcstr(to.as_ref(), &|to| panic!()))
952+
}
953+
954+
fn new_native(path: &WCStr, opts: &OpenOptions) -> io::Result<Self> {
955+
let name = c::UNICODE_STRING {
956+
Length: path.count_bytes() as _,
957+
MaximumLength: path.count_bytes() as _,
958+
Buffer: path.as_ptr() as *mut _,
959+
};
960+
let object_attributes = c::OBJECT_ATTRIBUTES {
961+
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
962+
RootDirectory: ptr::null_mut(),
963+
ObjectName: &name,
964+
Attributes: 0,
965+
SecurityDescriptor: ptr::null(),
966+
SecurityQualityOfService: ptr::null(),
967+
};
968+
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
969+
let handle =
970+
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
971+
.io_result()?;
972+
Ok(Self { handle })
973+
}
974+
975+
fn open_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
976+
let name = c::UNICODE_STRING {
977+
Length: path.count_bytes() as _,
978+
MaximumLength: path.count_bytes() as _,
979+
Buffer: path.as_ptr() as *mut _,
980+
};
981+
let object_attributes = c::OBJECT_ATTRIBUTES {
982+
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
983+
RootDirectory: self.handle.as_raw_handle(),
984+
ObjectName: &name,
985+
Attributes: 0,
986+
SecurityDescriptor: ptr::null(),
987+
SecurityQualityOfService: ptr::null(),
988+
};
989+
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
990+
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, false) }
991+
.io_result()
992+
}
993+
994+
fn create_dir_native(&self, path: &WCStr, opts: &OpenOptions) -> io::Result<Handle> {
995+
let name = c::UNICODE_STRING {
996+
Length: path.count_bytes() as _,
997+
MaximumLength: path.count_bytes() as _,
998+
Buffer: path.as_ptr() as *mut _,
999+
};
1000+
let object_attributes = c::OBJECT_ATTRIBUTES {
1001+
Length: size_of::<c::OBJECT_ATTRIBUTES>() as _,
1002+
RootDirectory: self.handle.as_raw_handle(),
1003+
ObjectName: &name,
1004+
Attributes: 0,
1005+
SecurityDescriptor: ptr::null(),
1006+
SecurityQualityOfService: ptr::null(),
1007+
};
1008+
let share = c::FILE_SHARE_READ | c::FILE_SHARE_WRITE | c::FILE_SHARE_DELETE;
1009+
unsafe { nt_create_file(opts.get_access_mode()?, &object_attributes, share, true) }
1010+
.io_result()
1011+
}
1012+
1013+
fn remove_native(&self, path: &WCStr, dir: bool) -> io::Result<()> {
1014+
let mut opts = OpenOptions::new();
1015+
opts.access_mode(c::GENERIC_WRITE);
1016+
let handle =
1017+
if dir { self.create_dir_native(path, &opts) } else { self.open_native(path, &opts) }?;
1018+
let info = c::FILE_DISPOSITION_INFO_EX { Flags: c::FILE_DISPOSITION_FLAG_DELETE };
1019+
let result = unsafe {
1020+
c::SetFileInformationByHandle(
1021+
handle.as_raw_handle(),
1022+
c::FileDispositionInfoEx,
1023+
(&info).as_ptr(),
1024+
size_of::<c::FILE_DISPOSITION_INFO_EX>() as _,
1025+
)
1026+
};
1027+
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
1028+
}
1029+
1030+
fn rename_native(&self, from: &WCStr, to_dir: &Self, to: &WCStr) -> io::Result<()> {
1031+
let mut opts = OpenOptions::new();
1032+
opts.access_mode(c::GENERIC_WRITE);
1033+
let handle = self.open_native(from, &opts)?;
1034+
let info = c::FILE_RENAME_INFO {
1035+
Anonymous: c::FILE_RENAME_INFO_0 { ReplaceIfExists: true },
1036+
RootDirectory: to_dir.handle.as_raw_handle(),
1037+
FileNameLength: to.count_bytes() as _,
1038+
FileName: [to.as_ptr() as u16],
1039+
};
1040+
let result = unsafe {
1041+
c::SetFileInformationByHandle(
1042+
handle.as_raw_handle(),
1043+
c::FileRenameInfo,
1044+
ptr::addr_of!(info) as _,
1045+
size_of::<c::FILE_RENAME_INFO>() as _,
1046+
)
1047+
};
1048+
if result == 0 { Err(api::get_last_error()).io_result() } else { Ok(()) }
1049+
}
1050+
}
1051+
1052+
impl fmt::Debug for Dir {
1053+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1054+
let mut b = f.debug_struct("Dir");
1055+
b.field("handle", &self.handle.as_raw_handle());
1056+
if let Ok(path) = get_path(self.handle.as_handle()) {
1057+
b.field("path", &path);
1058+
}
1059+
b.finish()
1060+
}
1061+
}
1062+
8491063
/// A buffer for holding directory entries.
8501064
struct DirBuff {
8511065
buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
@@ -995,7 +1209,7 @@ impl fmt::Debug for File {
9951209
// FIXME(#24570): add more info here (e.g., mode)
9961210
let mut b = f.debug_struct("File");
9971211
b.field("handle", &self.handle.as_raw_handle());
998-
if let Ok(path) = get_path(self) {
1212+
if let Ok(path) = get_path(self.handle.as_handle()) {
9991213
b.field("path", &path);
10001214
}
10011215
b.finish()
@@ -1484,10 +1698,10 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
14841698
}
14851699
}
14861700

1487-
fn get_path(f: &File) -> io::Result<PathBuf> {
1701+
fn get_path(f: impl AsRawHandle) -> io::Result<PathBuf> {
14881702
fill_utf16_buf(
14891703
|buf, sz| unsafe {
1490-
c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1704+
c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
14911705
},
14921706
|buf| PathBuf::from(OsString::from_wide(buf)),
14931707
)
@@ -1500,7 +1714,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
15001714
// This flag is so we can open directories too
15011715
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
15021716
let f = File::open_native(p, &opts)?;
1503-
get_path(&f)
1717+
get_path(f.handle)
15041718
}
15051719

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

0 commit comments

Comments
 (0)