Skip to content

Commit 228d994

Browse files
committed
add windows implementation
1 parent 0a82f16 commit 228d994

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,
@@ -848,6 +853,217 @@ impl File {
848853
}
849854
}
850855

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

1489-
fn get_path(f: &File) -> io::Result<PathBuf> {
1705+
fn get_path(f: impl AsRawHandle) -> io::Result<PathBuf> {
14901706
fill_utf16_buf(
14911707
|buf, sz| unsafe {
1492-
c::GetFinalPathNameByHandleW(f.handle.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
1708+
c::GetFinalPathNameByHandleW(f.as_raw_handle(), buf, sz, c::VOLUME_NAME_DOS)
14931709
},
14941710
|buf| PathBuf::from(OsString::from_wide(buf)),
14951711
)
@@ -1502,7 +1718,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
15021718
// This flag is so we can open directories too
15031719
opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS);
15041720
let f = File::open_native(p, &opts)?;
1505-
get_path(&f)
1721+
get_path(f.handle)
15061722
}
15071723

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

0 commit comments

Comments
 (0)