Skip to content

Commit c3a9db0

Browse files
committed
add windows implementation
1 parent f74ff7f commit c3a9db0

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,
@@ -849,6 +854,217 @@ impl File {
849854
}
850855
}
851856

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

1490-
fn get_path(f: &File) -> io::Result<PathBuf> {
1706+
fn get_path(f: impl AsRawHandle) -> io::Result<PathBuf> {
14911707
fill_utf16_buf(
14921708
|buf, sz| unsafe {
1493-
c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1709+
c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
14941710
},
14951711
|buf| PathBuf::from(OsString::from_wide(buf)),
14961712
)
@@ -1503,7 +1719,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
15031719
// This flag is so we can open directories too
15041720
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
15051721
let f = File::open_native(p, &opts)?;
1506-
get_path(&f)
1722+
get_path(f.handle)
15071723
}
15081724

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

0 commit comments

Comments
 (0)