@@ -10,6 +10,7 @@ use crate::os::windows::io::{AsHandle, BorrowedHandle};
1010use crate :: os:: windows:: prelude:: * ;
1111use crate :: path:: { Path , PathBuf } ;
1212use crate :: sync:: Arc ;
13+ use crate :: sys:: api:: SetFileInformation ;
1314use crate :: sys:: handle:: Handle ;
1415use crate :: sys:: pal:: api:: { self , WinError , set_file_information_by_handle} ;
1516use 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 ) ]
3035pub 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.
8581074struct 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
15141730pub fn copy ( from : & WCStr , to : & WCStr ) -> io:: Result < u64 > {
0 commit comments