@@ -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 ,
@@ -849,6 +854,217 @@ impl File {
849
854
}
850
855
}
851
856
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
+
852
1068
/// A buffer for holding directory entries.
853
1069
struct DirBuff {
854
1070
buffer : Box < Align8 < [ MaybeUninit < u8 > ; Self :: BUFFER_SIZE ] > > ,
@@ -998,7 +1214,7 @@ impl fmt::Debug for File {
998
1214
// FIXME(#24570): add more info here (e.g., mode)
999
1215
let mut b = f. debug_struct ( "File" ) ;
1000
1216
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 ( ) ) {
1002
1218
b. field ( "path" , & path) ;
1003
1219
}
1004
1220
b. finish ( )
@@ -1487,10 +1703,10 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
1487
1703
}
1488
1704
}
1489
1705
1490
- fn get_path ( f : & File ) -> io:: Result < PathBuf > {
1706
+ fn get_path ( f : impl AsRawHandle ) -> io:: Result < PathBuf > {
1491
1707
fill_utf16_buf (
1492
1708
|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 )
1494
1710
} ,
1495
1711
|buf| PathBuf :: from ( OsString :: from_wide ( buf) ) ,
1496
1712
)
@@ -1503,7 +1719,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
1503
1719
// This flag is so we can open directories too
1504
1720
opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
1505
1721
let f = File :: open_native ( p, & opts) ?;
1506
- get_path ( & f )
1722
+ get_path ( f . handle )
1507
1723
}
1508
1724
1509
1725
pub fn copy ( from : & WCStr , to : & WCStr ) -> io:: Result < u64 > {
0 commit comments