@@ -293,6 +293,30 @@ impl OpenOptions {
293293 } )
294294 }
295295
296+ fn get_disposition ( & self ) -> io:: Result < u32 > {
297+ match ( self . write , self . append ) {
298+ ( true , false ) => { }
299+ ( false , false ) => {
300+ if self . truncate || self . create || self . create_new {
301+ return Err ( Error :: from_raw_os_error ( c:: ERROR_INVALID_PARAMETER as i32 ) ) ;
302+ }
303+ }
304+ ( _, true ) => {
305+ if self . truncate && !self . create_new {
306+ return Err ( Error :: from_raw_os_error ( c:: ERROR_INVALID_PARAMETER as i32 ) ) ;
307+ }
308+ }
309+ }
310+
311+ Ok ( match ( self . create , self . truncate , self . create_new ) {
312+ ( false , false , false ) => c:: FILE_OPEN ,
313+ ( true , false , false ) => c:: FILE_OPEN_IF ,
314+ ( false , true , false ) => c:: FILE_OVERWRITE ,
315+ ( true , true , false ) => c:: FILE_OVERWRITE_IF ,
316+ ( _, _, true ) => c:: FILE_CREATE ,
317+ } )
318+ }
319+
296320 fn get_flags_and_attributes ( & self ) -> u32 {
297321 self . custom_flags
298322 | self . attributes
@@ -853,20 +877,16 @@ impl File {
853877
854878unsafe fn nt_create_file (
855879 access : u32 ,
880+ disposition : u32 ,
856881 object_attributes : & c:: OBJECT_ATTRIBUTES ,
857882 share : u32 ,
858883 dir : bool ,
859884) -> Result < Handle , WinError > {
860885 let mut handle = ptr:: null_mut ( ) ;
861886 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- } ;
887+ let access = access | c:: SYNCHRONIZE ;
888+ let options = if dir { c:: FILE_DIRECTORY_FILE } else { c:: FILE_NON_DIRECTORY_FILE }
889+ | c:: FILE_SYNCHRONOUS_IO_NONALERT ;
870890 let status = unsafe {
871891 c:: NtCreateFile (
872892 & mut handle,
@@ -877,7 +897,7 @@ unsafe fn nt_create_file(
877897 c:: FILE_ATTRIBUTE_NORMAL ,
878898 share,
879899 disposition,
880- if dir { c :: FILE_DIRECTORY_FILE } else { c :: FILE_NON_DIRECTORY_FILE } ,
900+ options ,
881901 ptr:: null ( ) ,
882902 0 ,
883903 )
@@ -908,38 +928,48 @@ fn run_path_with_wcstr<T, P: AsRef<Path>>(
908928 f ( path)
909929}
910930
931+ fn run_path_with_utf16 < T , P : AsRef < Path > > (
932+ path : P ,
933+ f : & dyn Fn ( & [ u16 ] ) -> io:: Result < T > ,
934+ ) -> io:: Result < T > {
935+ let utf16: Vec < u16 > = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect ( ) ;
936+ f ( & utf16)
937+ }
938+
911939impl Dir {
912940 pub fn new < P : AsRef < Path > > ( path : P ) -> io:: Result < Self > {
913941 let opts = OpenOptions :: new ( ) ;
914- run_path_with_wcstr ( path , & |path| Self :: new_native ( path, & opts) )
942+ Self :: new_native ( path. as_ref ( ) , & opts)
915943 }
916944
917945 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) )
946+ Self :: new_native ( path. as_ref ( ) , & opts)
919947 }
920948
921949 pub fn open < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < File > {
922950 let mut opts = OpenOptions :: new ( ) ;
951+ let path = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect :: < Vec < _ > > ( ) ;
923952 opts. read ( true ) ;
924- Ok ( File { handle : run_path_with_wcstr ( path , & |path| self . open_native ( path, & opts) ) ? } )
953+ Ok ( File { handle : self . open_native ( & path, & opts) ? } )
925954 }
926955
927956 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) ) ? } )
957+ let path = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect :: < Vec < _ > > ( ) ;
958+ Ok ( File { handle : self . open_native ( & path, & opts) ? } )
929959 }
930960
931961 pub fn create_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
932962 let mut opts = OpenOptions :: new ( ) ;
933963 opts. write ( true ) ;
934- run_path_with_wcstr ( path, & |path| self . create_dir_native ( path, & opts) . map ( |_| ( ) ) )
964+ run_path_with_utf16 ( path, & |path| self . create_dir_native ( path, & opts) . map ( |_| ( ) ) )
935965 }
936966
937967 pub fn remove_file < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
938- run_path_with_wcstr ( path, & |path| self . remove_native ( path, false ) )
968+ run_path_with_utf16 ( path, & |path| self . remove_native ( path, false ) )
939969 }
940970
941971 pub fn remove_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
942- run_path_with_wcstr ( path, & |path| self . remove_native ( path, true ) )
972+ run_path_with_utf16 ( path, & |path| self . remove_native ( path, true ) )
943973 }
944974
945975 pub fn rename < P : AsRef < Path > , Q : AsRef < Path > > (
@@ -948,36 +978,18 @@ impl Dir {
948978 to_dir : & Self ,
949979 to : Q ,
950980 ) -> 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- } )
981+ run_path_with_wcstr ( to. as_ref ( ) , & |to| self . rename_native ( from. as_ref ( ) , to_dir, to) )
954982 }
955983
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 ( ) ?;
984+ fn new_native ( path : & Path , opts : & OpenOptions ) -> io:: Result < Self > {
985+ let handle = File :: open ( path, opts) ?. into_inner ( ) ;
974986 Ok ( Self { handle } )
975987 }
976988
977- fn open_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
989+ fn open_native ( & self , path : & [ u16 ] , opts : & OpenOptions ) -> io:: Result < Handle > {
978990 let name = c:: UNICODE_STRING {
979- Length : path. count_bytes ( ) as _ ,
980- MaximumLength : path. count_bytes ( ) as _ ,
991+ Length : path. len ( ) as _ ,
992+ MaximumLength : path. len ( ) as _ ,
981993 Buffer : path. as_ptr ( ) as * mut _ ,
982994 } ;
983995 let object_attributes = c:: OBJECT_ATTRIBUTES {
@@ -989,14 +1001,22 @@ impl Dir {
9891001 SecurityQualityOfService : ptr:: null ( ) ,
9901002 } ;
9911003 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 ( )
1004+ unsafe {
1005+ nt_create_file (
1006+ opts. get_access_mode ( ) ?,
1007+ opts. get_disposition ( ) ?,
1008+ & object_attributes,
1009+ share,
1010+ false ,
1011+ )
1012+ }
1013+ . io_result ( )
9941014 }
9951015
996- fn create_dir_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
1016+ fn create_dir_native ( & self , path : & [ u16 ] , opts : & OpenOptions ) -> io:: Result < Handle > {
9971017 let name = c:: UNICODE_STRING {
998- Length : path. count_bytes ( ) as _ ,
999- MaximumLength : path. count_bytes ( ) as _ ,
1018+ Length : path. len ( ) as _ ,
1019+ MaximumLength : path. len ( ) as _ ,
10001020 Buffer : path. as_ptr ( ) as * mut _ ,
10011021 } ;
10021022 let object_attributes = c:: OBJECT_ATTRIBUTES {
@@ -1008,11 +1028,19 @@ impl Dir {
10081028 SecurityQualityOfService : ptr:: null ( ) ,
10091029 } ;
10101030 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 ( )
1031+ unsafe {
1032+ nt_create_file (
1033+ opts. get_access_mode ( ) ?,
1034+ opts. get_disposition ( ) ?,
1035+ & object_attributes,
1036+ share,
1037+ true ,
1038+ )
1039+ }
1040+ . io_result ( )
10131041 }
10141042
1015- fn remove_native ( & self , path : & WCStr , dir : bool ) -> io:: Result < ( ) > {
1043+ fn remove_native ( & self , path : & [ u16 ] , dir : bool ) -> io:: Result < ( ) > {
10161044 let mut opts = OpenOptions :: new ( ) ;
10171045 opts. access_mode ( c:: GENERIC_WRITE ) ;
10181046 let handle =
@@ -1029,24 +1057,54 @@ impl Dir {
10291057 if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
10301058 }
10311059
1032- fn rename_native ( & self , from : & WCStr , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
1060+ fn rename_native ( & self , from : & Path , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
10331061 let mut opts = OpenOptions :: new ( ) ;
10341062 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 ] ,
1063+ let handle = run_path_with_utf16 ( from, & |u| self . open_native ( u, & opts) ) ?;
1064+ // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
1065+ // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
1066+ let Ok ( new_len_without_nul_in_bytes) : Result < u32 , _ > =
1067+ ( ( to. count_bytes ( ) - 1 ) * 2 ) . try_into ( )
1068+ else {
1069+ return Err ( io:: Error :: new ( io:: ErrorKind :: InvalidFilename , "Filename too long" ) ) ;
10411070 } ;
1071+ let offset: u32 = offset_of ! ( c:: FILE_RENAME_INFO , FileName ) . try_into ( ) . unwrap ( ) ;
1072+ let struct_size = offset + new_len_without_nul_in_bytes + 2 ;
1073+ let layout =
1074+ Layout :: from_size_align ( struct_size as usize , align_of :: < c:: FILE_RENAME_INFO > ( ) )
1075+ . unwrap ( ) ;
1076+
1077+ // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
1078+ let file_rename_info;
1079+ unsafe {
1080+ file_rename_info = alloc ( layout) . cast :: < c:: FILE_RENAME_INFO > ( ) ;
1081+ if file_rename_info. is_null ( ) {
1082+ return Err ( io:: ErrorKind :: OutOfMemory . into ( ) ) ;
1083+ }
1084+
1085+ ( & raw mut ( * file_rename_info) . Anonymous ) . write ( c:: FILE_RENAME_INFO_0 {
1086+ Flags : c:: FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c:: FILE_RENAME_FLAG_POSIX_SEMANTICS ,
1087+ } ) ;
1088+
1089+ ( & raw mut ( * file_rename_info) . RootDirectory ) . write ( to_dir. handle . as_raw_handle ( ) ) ;
1090+ // Don't include the NULL in the size
1091+ ( & raw mut ( * file_rename_info) . FileNameLength ) . write ( new_len_without_nul_in_bytes) ;
1092+
1093+ to. as_ptr ( ) . copy_to_nonoverlapping (
1094+ ( & raw mut ( * file_rename_info) . FileName ) . cast :: < u16 > ( ) ,
1095+ run_path_with_wcstr ( from, & |s| Ok ( s. count_bytes ( ) ) ) . unwrap ( ) ,
1096+ ) ;
1097+ }
1098+
10421099 let result = unsafe {
10431100 c:: SetFileInformationByHandle (
10441101 handle. as_raw_handle ( ) ,
1045- c:: FileRenameInfo ,
1046- ptr :: addr_of! ( info ) as _ ,
1047- size_of :: < c :: FILE_RENAME_INFO > ( ) as _ ,
1102+ c:: FileRenameInfoEx ,
1103+ file_rename_info . cast :: < c_void > ( ) ,
1104+ struct_size ,
10481105 )
10491106 } ;
1107+ unsafe { dealloc ( file_rename_info. cast :: < u8 > ( ) , layout) } ;
10501108 if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
10511109 }
10521110}
0 commit comments