@@ -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 ,
@@ -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.
8531069struct 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
15091725pub fn copy ( from : & WCStr , to : & WCStr ) -> io:: Result < u64 > {
0 commit comments