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