@@ -10,6 +10,7 @@ use crate::os::windows::io::{AsHandle, BorrowedHandle};
10
10
use crate :: os:: windows:: prelude:: * ;
11
11
use crate :: path:: { Path , PathBuf } ;
12
12
use crate :: sync:: Arc ;
13
+ use crate :: sys:: api:: SetFileInformation ;
13
14
use crate :: sys:: handle:: Handle ;
14
15
use crate :: sys:: pal:: api:: { self , WinError , set_file_information_by_handle} ;
15
16
use crate :: sys:: pal:: { IoResult , fill_utf16_buf, to_u16s, truncate_utf16_at_nul} ;
@@ -26,6 +27,10 @@ pub struct File {
26
27
handle : Handle ,
27
28
}
28
29
30
+ pub struct Dir {
31
+ handle : Handle ,
32
+ }
33
+
29
34
#[ derive( Clone ) ]
30
35
pub struct FileAttr {
31
36
attributes : u32 ,
@@ -846,6 +851,215 @@ impl File {
846
851
}
847
852
}
848
853
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
+
849
1063
/// A buffer for holding directory entries.
850
1064
struct DirBuff {
851
1065
buffer : Box < Align8 < [ MaybeUninit < u8 > ; Self :: BUFFER_SIZE ] > > ,
@@ -995,7 +1209,7 @@ impl fmt::Debug for File {
995
1209
// FIXME(#24570): add more info here (e.g., mode)
996
1210
let mut b = f. debug_struct ( "File" ) ;
997
1211
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 ( ) ) {
999
1213
b. field ( "path" , & path) ;
1000
1214
}
1001
1215
b. finish ( )
@@ -1484,10 +1698,10 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
1484
1698
}
1485
1699
}
1486
1700
1487
- fn get_path ( f : & File ) -> io:: Result < PathBuf > {
1701
+ fn get_path ( f : impl AsRawHandle ) -> io:: Result < PathBuf > {
1488
1702
fill_utf16_buf (
1489
1703
|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 )
1491
1705
} ,
1492
1706
|buf| PathBuf :: from ( OsString :: from_wide ( buf) ) ,
1493
1707
)
@@ -1500,7 +1714,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
1500
1714
// This flag is so we can open directories too
1501
1715
opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
1502
1716
let f = File :: open_native ( p, & opts) ?;
1503
- get_path ( & f )
1717
+ get_path ( f . handle )
1504
1718
}
1505
1719
1506
1720
pub fn copy ( from : & WCStr , to : & WCStr ) -> io:: Result < u64 > {
0 commit comments