@@ -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,217 @@ 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| {
952
+ run_path_with_wcstr ( to. as_ref ( ) , & |to| self . rename_native ( from, to_dir, to) )
953
+ } )
954
+ }
955
+
956
+ fn new_native ( path : & WCStr , opts : & OpenOptions ) -> io:: Result < Self > {
957
+ let name = c:: UNICODE_STRING {
958
+ Length : path. count_bytes ( ) as _ ,
959
+ MaximumLength : path. count_bytes ( ) as _ ,
960
+ Buffer : path. as_ptr ( ) as * mut _ ,
961
+ } ;
962
+ let object_attributes = c:: OBJECT_ATTRIBUTES {
963
+ Length : size_of :: < c:: OBJECT_ATTRIBUTES > ( ) as _ ,
964
+ RootDirectory : ptr:: null_mut ( ) ,
965
+ ObjectName : & name,
966
+ Attributes : 0 ,
967
+ SecurityDescriptor : ptr:: null ( ) ,
968
+ SecurityQualityOfService : ptr:: null ( ) ,
969
+ } ;
970
+ let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
971
+ let handle =
972
+ unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, true ) }
973
+ . io_result ( ) ?;
974
+ Ok ( Self { handle } )
975
+ }
976
+
977
+ fn open_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
978
+ let name = c:: UNICODE_STRING {
979
+ Length : path. count_bytes ( ) as _ ,
980
+ MaximumLength : path. count_bytes ( ) as _ ,
981
+ Buffer : path. as_ptr ( ) as * mut _ ,
982
+ } ;
983
+ let object_attributes = c:: OBJECT_ATTRIBUTES {
984
+ Length : size_of :: < c:: OBJECT_ATTRIBUTES > ( ) as _ ,
985
+ RootDirectory : self . handle . as_raw_handle ( ) ,
986
+ ObjectName : & name,
987
+ Attributes : 0 ,
988
+ SecurityDescriptor : ptr:: null ( ) ,
989
+ SecurityQualityOfService : ptr:: null ( ) ,
990
+ } ;
991
+ let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
992
+ unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, false ) }
993
+ . io_result ( )
994
+ }
995
+
996
+ fn create_dir_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
997
+ let name = c:: UNICODE_STRING {
998
+ Length : path. count_bytes ( ) as _ ,
999
+ MaximumLength : path. count_bytes ( ) as _ ,
1000
+ Buffer : path. as_ptr ( ) as * mut _ ,
1001
+ } ;
1002
+ let object_attributes = c:: OBJECT_ATTRIBUTES {
1003
+ Length : size_of :: < c:: OBJECT_ATTRIBUTES > ( ) as _ ,
1004
+ RootDirectory : self . handle . as_raw_handle ( ) ,
1005
+ ObjectName : & name,
1006
+ Attributes : 0 ,
1007
+ SecurityDescriptor : ptr:: null ( ) ,
1008
+ SecurityQualityOfService : ptr:: null ( ) ,
1009
+ } ;
1010
+ let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
1011
+ unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, true ) }
1012
+ . io_result ( )
1013
+ }
1014
+
1015
+ fn remove_native ( & self , path : & WCStr , dir : bool ) -> io:: Result < ( ) > {
1016
+ let mut opts = OpenOptions :: new ( ) ;
1017
+ opts. access_mode ( c:: GENERIC_WRITE ) ;
1018
+ let handle =
1019
+ if dir { self . create_dir_native ( path, & opts) } else { self . open_native ( path, & opts) } ?;
1020
+ let info = c:: FILE_DISPOSITION_INFO_EX { Flags : c:: FILE_DISPOSITION_FLAG_DELETE } ;
1021
+ let result = unsafe {
1022
+ c:: SetFileInformationByHandle (
1023
+ handle. as_raw_handle ( ) ,
1024
+ c:: FileDispositionInfoEx ,
1025
+ ( & info) . as_ptr ( ) ,
1026
+ size_of :: < c:: FILE_DISPOSITION_INFO_EX > ( ) as _ ,
1027
+ )
1028
+ } ;
1029
+ if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
1030
+ }
1031
+
1032
+ fn rename_native ( & self , from : & WCStr , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
1033
+ let mut opts = OpenOptions :: new ( ) ;
1034
+ opts. access_mode ( c:: GENERIC_WRITE ) ;
1035
+ let handle = self . open_native ( from, & opts) ?;
1036
+ let info = c:: FILE_RENAME_INFO {
1037
+ Anonymous : c:: FILE_RENAME_INFO_0 { ReplaceIfExists : true } ,
1038
+ RootDirectory : to_dir. handle . as_raw_handle ( ) ,
1039
+ FileNameLength : to. count_bytes ( ) as _ ,
1040
+ FileName : [ to. as_ptr ( ) as u16 ] ,
1041
+ } ;
1042
+ let result = unsafe {
1043
+ c:: SetFileInformationByHandle (
1044
+ handle. as_raw_handle ( ) ,
1045
+ c:: FileRenameInfo ,
1046
+ ptr:: addr_of!( info) as _ ,
1047
+ size_of :: < c:: FILE_RENAME_INFO > ( ) as _ ,
1048
+ )
1049
+ } ;
1050
+ if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
1051
+ }
1052
+ }
1053
+
1054
+ impl fmt:: Debug for Dir {
1055
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1056
+ let mut b = f. debug_struct ( "Dir" ) ;
1057
+ b. field ( "handle" , & self . handle . as_raw_handle ( ) ) ;
1058
+ if let Ok ( path) = get_path ( self . handle . as_handle ( ) ) {
1059
+ b. field ( "path" , & path) ;
1060
+ }
1061
+ b. finish ( )
1062
+ }
1063
+ }
1064
+
849
1065
/// A buffer for holding directory entries.
850
1066
struct DirBuff {
851
1067
buffer : Box < Align8 < [ MaybeUninit < u8 > ; Self :: BUFFER_SIZE ] > > ,
@@ -995,7 +1211,7 @@ impl fmt::Debug for File {
995
1211
// FIXME(#24570): add more info here (e.g., mode)
996
1212
let mut b = f. debug_struct ( "File" ) ;
997
1213
b. field ( "handle" , & self . handle . as_raw_handle ( ) ) ;
998
- if let Ok ( path) = get_path ( self ) {
1214
+ if let Ok ( path) = get_path ( self . handle . as_handle ( ) ) {
999
1215
b. field ( "path" , & path) ;
1000
1216
}
1001
1217
b. finish ( )
@@ -1484,10 +1700,10 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
1484
1700
}
1485
1701
}
1486
1702
1487
- fn get_path ( f : & File ) -> io:: Result < PathBuf > {
1703
+ fn get_path ( f : impl AsRawHandle ) -> io:: Result < PathBuf > {
1488
1704
fill_utf16_buf (
1489
1705
|buf, sz| unsafe {
1490
- c:: GetFinalPathNameByHandleW ( f. handle . as_raw_handle ( ) , buf, sz, c:: VOLUME_NAME_DOS )
1706
+ c:: GetFinalPathNameByHandleW ( f. as_raw_handle ( ) , buf, sz, c:: VOLUME_NAME_DOS )
1491
1707
} ,
1492
1708
|buf| PathBuf :: from ( OsString :: from_wide ( buf) ) ,
1493
1709
)
@@ -1500,7 +1716,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
1500
1716
// This flag is so we can open directories too
1501
1717
opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
1502
1718
let f = File :: open_native ( p, & opts) ?;
1503
- get_path ( & f )
1719
+ get_path ( f . handle )
1504
1720
}
1505
1721
1506
1722
pub fn copy ( from : & WCStr , to : & WCStr ) -> io:: Result < u64 > {
0 commit comments