@@ -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 ,
@@ -848,6 +853,217 @@ impl File {
848
853
}
849
854
}
850
855
856
+ unsafe fn nt_create_file (
857
+ access : u32 ,
858
+ object_attributes : & c:: OBJECT_ATTRIBUTES ,
859
+ share : u32 ,
860
+ dir : bool ,
861
+ ) -> Result < Handle , WinError > {
862
+ let mut handle = ptr:: null_mut ( ) ;
863
+ let mut io_status = c:: IO_STATUS_BLOCK :: PENDING ;
864
+ let disposition = match ( access & c:: GENERIC_READ > 0 , access & c:: GENERIC_WRITE > 0 ) {
865
+ ( true , true ) => c:: FILE_OPEN_IF ,
866
+ ( true , false ) => c:: FILE_OPEN ,
867
+ ( false , true ) => c:: FILE_CREATE ,
868
+ ( false , false ) => {
869
+ return Err ( WinError :: new ( c:: ERROR_INVALID_PARAMETER ) ) ;
870
+ }
871
+ } ;
872
+ let status = unsafe {
873
+ c:: NtCreateFile (
874
+ & mut handle,
875
+ access,
876
+ object_attributes,
877
+ & mut io_status,
878
+ ptr:: null ( ) ,
879
+ c:: FILE_ATTRIBUTE_NORMAL ,
880
+ share,
881
+ disposition,
882
+ if dir { c:: FILE_DIRECTORY_FILE } else { c:: FILE_NON_DIRECTORY_FILE } ,
883
+ ptr:: null ( ) ,
884
+ 0 ,
885
+ )
886
+ } ;
887
+ if c:: nt_success ( status) {
888
+ // SAFETY: nt_success guarantees that handle is no longer null
889
+ unsafe { Ok ( Handle :: from_raw_handle ( handle) ) }
890
+ } else {
891
+ let win_error = if status == c:: STATUS_DELETE_PENDING {
892
+ // We make a special exception for `STATUS_DELETE_PENDING` because
893
+ // otherwise this will be mapped to `ERROR_ACCESS_DENIED` which is
894
+ // very unhelpful because that can also mean a permission error.
895
+ WinError :: DELETE_PENDING
896
+ } else {
897
+ WinError :: new ( unsafe { c:: RtlNtStatusToDosError ( status) } )
898
+ } ;
899
+ Err ( win_error)
900
+ }
901
+ }
902
+
903
+ fn run_path_with_wcstr < T , P : AsRef < Path > > (
904
+ path : P ,
905
+ f : & dyn Fn ( & WCStr ) -> io:: Result < T > ,
906
+ ) -> io:: Result < T > {
907
+ let path = maybe_verbatim ( path. as_ref ( ) ) ?;
908
+ // SAFETY: maybe_verbatim returns null-terminated strings
909
+ let path = unsafe { WCStr :: from_wchars_with_null_unchecked ( & path) } ;
910
+ f ( path)
911
+ }
912
+
913
+ impl Dir {
914
+ pub fn new < P : AsRef < Path > > ( path : P ) -> io:: Result < Self > {
915
+ let opts = OpenOptions :: new ( ) ;
916
+ run_path_with_wcstr ( path, & |path| Self :: new_native ( path, & opts) )
917
+ }
918
+
919
+ pub fn new_with < P : AsRef < Path > > ( path : P , opts : & OpenOptions ) -> io:: Result < Self > {
920
+ run_path_with_wcstr ( path, & |path| Self :: new_native ( path, & opts) )
921
+ }
922
+
923
+ pub fn open < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < File > {
924
+ let mut opts = OpenOptions :: new ( ) ;
925
+ opts. read ( true ) ;
926
+ Ok ( File { handle : run_path_with_wcstr ( path, & |path| self . open_native ( path, & opts) ) ? } )
927
+ }
928
+
929
+ pub fn open_with < P : AsRef < Path > > ( & self , path : P , opts : & OpenOptions ) -> io:: Result < File > {
930
+ Ok ( File { handle : run_path_with_wcstr ( path, & |path| self . open_native ( path, & opts) ) ? } )
931
+ }
932
+
933
+ pub fn create_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
934
+ run_path_with_wcstr ( path, & |path| {
935
+ self . create_dir_native ( path, & OpenOptions :: new ( ) ) . map ( |_| ( ) )
936
+ } )
937
+ }
938
+
939
+ pub fn remove_file < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
940
+ run_path_with_wcstr ( path, & |path| self . remove_native ( path, false ) )
941
+ }
942
+
943
+ pub fn remove_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
944
+ run_path_with_wcstr ( path, & |path| self . remove_native ( path, true ) )
945
+ }
946
+
947
+ pub fn rename < P : AsRef < Path > , Q : AsRef < Path > > (
948
+ & self ,
949
+ from : P ,
950
+ to_dir : & Self ,
951
+ to : Q ,
952
+ ) -> io:: Result < ( ) > {
953
+ run_path_with_wcstr ( from. as_ref ( ) , & |from| {
954
+ run_path_with_wcstr ( to. as_ref ( ) , & |to| self . rename_native ( from, to_dir, to) )
955
+ } )
956
+ }
957
+
958
+ fn new_native ( path : & WCStr , opts : & OpenOptions ) -> io:: Result < Self > {
959
+ let name = c:: UNICODE_STRING {
960
+ Length : path. count_bytes ( ) as _ ,
961
+ MaximumLength : path. count_bytes ( ) as _ ,
962
+ Buffer : path. as_ptr ( ) as * mut _ ,
963
+ } ;
964
+ let object_attributes = c:: OBJECT_ATTRIBUTES {
965
+ Length : size_of :: < c:: OBJECT_ATTRIBUTES > ( ) as _ ,
966
+ RootDirectory : ptr:: null_mut ( ) ,
967
+ ObjectName : & name,
968
+ Attributes : 0 ,
969
+ SecurityDescriptor : ptr:: null ( ) ,
970
+ SecurityQualityOfService : ptr:: null ( ) ,
971
+ } ;
972
+ let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
973
+ let handle =
974
+ unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, true ) }
975
+ . io_result ( ) ?;
976
+ Ok ( Self { handle } )
977
+ }
978
+
979
+ fn open_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
980
+ let name = c:: UNICODE_STRING {
981
+ Length : path. count_bytes ( ) as _ ,
982
+ MaximumLength : path. count_bytes ( ) as _ ,
983
+ Buffer : path. as_ptr ( ) as * mut _ ,
984
+ } ;
985
+ let object_attributes = c:: OBJECT_ATTRIBUTES {
986
+ Length : size_of :: < c:: OBJECT_ATTRIBUTES > ( ) as _ ,
987
+ RootDirectory : self . handle . as_raw_handle ( ) ,
988
+ ObjectName : & name,
989
+ Attributes : 0 ,
990
+ SecurityDescriptor : ptr:: null ( ) ,
991
+ SecurityQualityOfService : ptr:: null ( ) ,
992
+ } ;
993
+ let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
994
+ unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, false ) }
995
+ . io_result ( )
996
+ }
997
+
998
+ fn create_dir_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
999
+ let name = c:: UNICODE_STRING {
1000
+ Length : path. count_bytes ( ) as _ ,
1001
+ MaximumLength : path. count_bytes ( ) as _ ,
1002
+ Buffer : path. as_ptr ( ) as * mut _ ,
1003
+ } ;
1004
+ let object_attributes = c:: OBJECT_ATTRIBUTES {
1005
+ Length : size_of :: < c:: OBJECT_ATTRIBUTES > ( ) as _ ,
1006
+ RootDirectory : self . handle . as_raw_handle ( ) ,
1007
+ ObjectName : & name,
1008
+ Attributes : 0 ,
1009
+ SecurityDescriptor : ptr:: null ( ) ,
1010
+ SecurityQualityOfService : ptr:: null ( ) ,
1011
+ } ;
1012
+ let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
1013
+ unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, true ) }
1014
+ . io_result ( )
1015
+ }
1016
+
1017
+ fn remove_native ( & self , path : & WCStr , dir : bool ) -> io:: Result < ( ) > {
1018
+ let mut opts = OpenOptions :: new ( ) ;
1019
+ opts. access_mode ( c:: GENERIC_WRITE ) ;
1020
+ let handle =
1021
+ if dir { self . create_dir_native ( path, & opts) } else { self . open_native ( path, & opts) } ?;
1022
+ let info = c:: FILE_DISPOSITION_INFO_EX { Flags : c:: FILE_DISPOSITION_FLAG_DELETE } ;
1023
+ let result = unsafe {
1024
+ c:: SetFileInformationByHandle (
1025
+ handle. as_raw_handle ( ) ,
1026
+ c:: FileDispositionInfoEx ,
1027
+ ( & info) . as_ptr ( ) ,
1028
+ size_of :: < c:: FILE_DISPOSITION_INFO_EX > ( ) as _ ,
1029
+ )
1030
+ } ;
1031
+ if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
1032
+ }
1033
+
1034
+ fn rename_native ( & self , from : & WCStr , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
1035
+ let mut opts = OpenOptions :: new ( ) ;
1036
+ opts. access_mode ( c:: GENERIC_WRITE ) ;
1037
+ let handle = self . open_native ( from, & opts) ?;
1038
+ let info = c:: FILE_RENAME_INFO {
1039
+ Anonymous : c:: FILE_RENAME_INFO_0 { ReplaceIfExists : true } ,
1040
+ RootDirectory : to_dir. handle . as_raw_handle ( ) ,
1041
+ FileNameLength : to. count_bytes ( ) as _ ,
1042
+ FileName : [ to. as_ptr ( ) as u16 ] ,
1043
+ } ;
1044
+ let result = unsafe {
1045
+ c:: SetFileInformationByHandle (
1046
+ handle. as_raw_handle ( ) ,
1047
+ c:: FileRenameInfo ,
1048
+ ptr:: addr_of!( info) as _ ,
1049
+ size_of :: < c:: FILE_RENAME_INFO > ( ) as _ ,
1050
+ )
1051
+ } ;
1052
+ if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
1053
+ }
1054
+ }
1055
+
1056
+ impl fmt:: Debug for Dir {
1057
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
1058
+ let mut b = f. debug_struct ( "Dir" ) ;
1059
+ b. field ( "handle" , & self . handle . as_raw_handle ( ) ) ;
1060
+ if let Ok ( path) = get_path ( self . handle . as_handle ( ) ) {
1061
+ b. field ( "path" , & path) ;
1062
+ }
1063
+ b. finish ( )
1064
+ }
1065
+ }
1066
+
851
1067
/// A buffer for holding directory entries.
852
1068
struct DirBuff {
853
1069
buffer : Box < Align8 < [ MaybeUninit < u8 > ; Self :: BUFFER_SIZE ] > > ,
@@ -997,7 +1213,7 @@ impl fmt::Debug for File {
997
1213
// FIXME(#24570): add more info here (e.g., mode)
998
1214
let mut b = f. debug_struct ( "File" ) ;
999
1215
b. field ( "handle" , & self . handle . as_raw_handle ( ) ) ;
1000
- if let Ok ( path) = get_path ( self ) {
1216
+ if let Ok ( path) = get_path ( self . handle . as_handle ( ) ) {
1001
1217
b. field ( "path" , & path) ;
1002
1218
}
1003
1219
b. finish ( )
@@ -1486,10 +1702,10 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> {
1486
1702
}
1487
1703
}
1488
1704
1489
- fn get_path ( f : & File ) -> io:: Result < PathBuf > {
1705
+ fn get_path ( f : impl AsRawHandle ) -> io:: Result < PathBuf > {
1490
1706
fill_utf16_buf (
1491
1707
|buf, sz| unsafe {
1492
- c:: GetFinalPathNameByHandleW ( f. handle . as_raw_handle ( ) , buf, sz, c:: VOLUME_NAME_DOS )
1708
+ c:: GetFinalPathNameByHandleW ( f. as_raw_handle ( ) , buf, sz, c:: VOLUME_NAME_DOS )
1493
1709
} ,
1494
1710
|buf| PathBuf :: from ( OsString :: from_wide ( buf) ) ,
1495
1711
)
@@ -1502,7 +1718,7 @@ pub fn canonicalize(p: &WCStr) -> io::Result<PathBuf> {
1502
1718
// This flag is so we can open directories too
1503
1719
opts. custom_flags ( c:: FILE_FLAG_BACKUP_SEMANTICS ) ;
1504
1720
let f = File :: open_native ( p, & opts) ?;
1505
- get_path ( & f )
1721
+ get_path ( f . handle )
1506
1722
}
1507
1723
1508
1724
pub fn copy ( from : & WCStr , to : & WCStr ) -> io:: Result < u64 > {
0 commit comments