@@ -294,6 +294,30 @@ impl OpenOptions {
294294 } )
295295 }
296296
297+ fn get_disposition ( & self ) -> io:: Result < u32 > {
298+ match ( self . write , self . append ) {
299+ ( true , false ) => { }
300+ ( false , false ) => {
301+ if self . truncate || self . create || self . create_new {
302+ return Err ( Error :: from_raw_os_error ( c:: ERROR_INVALID_PARAMETER as i32 ) ) ;
303+ }
304+ }
305+ ( _, true ) => {
306+ if self . truncate && !self . create_new {
307+ return Err ( Error :: from_raw_os_error ( c:: ERROR_INVALID_PARAMETER as i32 ) ) ;
308+ }
309+ }
310+ }
311+
312+ Ok ( match ( self . create , self . truncate , self . create_new ) {
313+ ( false , false , false ) => c:: FILE_OPEN ,
314+ ( true , false , false ) => c:: FILE_OPEN_IF ,
315+ ( false , true , false ) => c:: FILE_OVERWRITE ,
316+ ( true , true , false ) => c:: FILE_OVERWRITE_IF ,
317+ ( _, _, true ) => c:: FILE_CREATE ,
318+ } )
319+ }
320+
297321 fn get_flags_and_attributes ( & self ) -> u32 {
298322 self . custom_flags
299323 | self . attributes
@@ -856,20 +880,16 @@ impl File {
856880
857881unsafe fn nt_create_file (
858882 access : u32 ,
883+ disposition : u32 ,
859884 object_attributes : & c:: OBJECT_ATTRIBUTES ,
860885 share : u32 ,
861886 dir : bool ,
862887) -> Result < Handle , WinError > {
863888 let mut handle = ptr:: null_mut ( ) ;
864889 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- } ;
890+ let access = access | c:: SYNCHRONIZE ;
891+ let options = if dir { c:: FILE_DIRECTORY_FILE } else { c:: FILE_NON_DIRECTORY_FILE }
892+ | c:: FILE_SYNCHRONOUS_IO_NONALERT ;
873893 let status = unsafe {
874894 c:: NtCreateFile (
875895 & mut handle,
@@ -880,7 +900,7 @@ unsafe fn nt_create_file(
880900 c:: FILE_ATTRIBUTE_NORMAL ,
881901 share,
882902 disposition,
883- if dir { c :: FILE_DIRECTORY_FILE } else { c :: FILE_NON_DIRECTORY_FILE } ,
903+ options ,
884904 ptr:: null ( ) ,
885905 0 ,
886906 )
@@ -911,38 +931,48 @@ fn run_path_with_wcstr<T, P: AsRef<Path>>(
911931 f ( path)
912932}
913933
934+ fn run_path_with_utf16 < T , P : AsRef < Path > > (
935+ path : P ,
936+ f : & dyn Fn ( & [ u16 ] ) -> io:: Result < T > ,
937+ ) -> io:: Result < T > {
938+ let utf16: Vec < u16 > = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect ( ) ;
939+ f ( & utf16)
940+ }
941+
914942impl Dir {
915943 pub fn new < P : AsRef < Path > > ( path : P ) -> io:: Result < Self > {
916944 let opts = OpenOptions :: new ( ) ;
917- run_path_with_wcstr ( path , & |path| Self :: new_native ( path, & opts) )
945+ Self :: new_native ( path. as_ref ( ) , & opts)
918946 }
919947
920948 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) )
949+ Self :: new_native ( path. as_ref ( ) , & opts)
922950 }
923951
924952 pub fn open < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < File > {
925953 let mut opts = OpenOptions :: new ( ) ;
954+ let path = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect :: < Vec < _ > > ( ) ;
926955 opts. read ( true ) ;
927- Ok ( File { handle : run_path_with_wcstr ( path , & |path| self . open_native ( path, & opts) ) ? } )
956+ Ok ( File { handle : self . open_native ( & path, & opts) ? } )
928957 }
929958
930959 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) ) ? } )
960+ let path = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect :: < Vec < _ > > ( ) ;
961+ Ok ( File { handle : self . open_native ( & path, & opts) ? } )
932962 }
933963
934964 pub fn create_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
935965 let mut opts = OpenOptions :: new ( ) ;
936966 opts. write ( true ) ;
937- run_path_with_wcstr ( path, & |path| self . create_dir_native ( path, & opts) . map ( |_| ( ) ) )
967+ run_path_with_utf16 ( path, & |path| self . create_dir_native ( path, & opts) . map ( |_| ( ) ) )
938968 }
939969
940970 pub fn remove_file < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
941- run_path_with_wcstr ( path, & |path| self . remove_native ( path, false ) )
971+ run_path_with_utf16 ( path, & |path| self . remove_native ( path, false ) )
942972 }
943973
944974 pub fn remove_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
945- run_path_with_wcstr ( path, & |path| self . remove_native ( path, true ) )
975+ run_path_with_utf16 ( path, & |path| self . remove_native ( path, true ) )
946976 }
947977
948978 pub fn rename < P : AsRef < Path > , Q : AsRef < Path > > (
@@ -951,36 +981,18 @@ impl Dir {
951981 to_dir : & Self ,
952982 to : Q ,
953983 ) -> 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- } )
984+ run_path_with_wcstr ( to. as_ref ( ) , & |to| self . rename_native ( from. as_ref ( ) , to_dir, to) )
957985 }
958986
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 ( ) ?;
987+ fn new_native ( path : & Path , opts : & OpenOptions ) -> io:: Result < Self > {
988+ let handle = File :: open ( path, opts) ?. into_inner ( ) ;
977989 Ok ( Self { handle } )
978990 }
979991
980- fn open_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
992+ fn open_native ( & self , path : & [ u16 ] , opts : & OpenOptions ) -> io:: Result < Handle > {
981993 let name = c:: UNICODE_STRING {
982- Length : path. count_bytes ( ) as _ ,
983- MaximumLength : path. count_bytes ( ) as _ ,
994+ Length : path. len ( ) as _ ,
995+ MaximumLength : path. len ( ) as _ ,
984996 Buffer : path. as_ptr ( ) as * mut _ ,
985997 } ;
986998 let object_attributes = c:: OBJECT_ATTRIBUTES {
@@ -992,14 +1004,22 @@ impl Dir {
9921004 SecurityQualityOfService : ptr:: null ( ) ,
9931005 } ;
9941006 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 ( )
1007+ unsafe {
1008+ nt_create_file (
1009+ opts. get_access_mode ( ) ?,
1010+ opts. get_disposition ( ) ?,
1011+ & object_attributes,
1012+ share,
1013+ false ,
1014+ )
1015+ }
1016+ . io_result ( )
9971017 }
9981018
999- fn create_dir_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
1019+ fn create_dir_native ( & self , path : & [ u16 ] , opts : & OpenOptions ) -> io:: Result < Handle > {
10001020 let name = c:: UNICODE_STRING {
1001- Length : path. count_bytes ( ) as _ ,
1002- MaximumLength : path. count_bytes ( ) as _ ,
1021+ Length : path. len ( ) as _ ,
1022+ MaximumLength : path. len ( ) as _ ,
10031023 Buffer : path. as_ptr ( ) as * mut _ ,
10041024 } ;
10051025 let object_attributes = c:: OBJECT_ATTRIBUTES {
@@ -1011,11 +1031,19 @@ impl Dir {
10111031 SecurityQualityOfService : ptr:: null ( ) ,
10121032 } ;
10131033 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 ( )
1034+ unsafe {
1035+ nt_create_file (
1036+ opts. get_access_mode ( ) ?,
1037+ opts. get_disposition ( ) ?,
1038+ & object_attributes,
1039+ share,
1040+ true ,
1041+ )
1042+ }
1043+ . io_result ( )
10161044 }
10171045
1018- fn remove_native ( & self , path : & WCStr , dir : bool ) -> io:: Result < ( ) > {
1046+ fn remove_native ( & self , path : & [ u16 ] , dir : bool ) -> io:: Result < ( ) > {
10191047 let mut opts = OpenOptions :: new ( ) ;
10201048 opts. access_mode ( c:: GENERIC_WRITE ) ;
10211049 let handle =
@@ -1032,24 +1060,54 @@ impl Dir {
10321060 if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
10331061 }
10341062
1035- fn rename_native ( & self , from : & WCStr , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
1063+ fn rename_native ( & self , from : & Path , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
10361064 let mut opts = OpenOptions :: new ( ) ;
10371065 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 ] ,
1066+ let handle = run_path_with_utf16 ( from, & |u| self . open_native ( u, & opts) ) ?;
1067+ // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
1068+ // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
1069+ let Ok ( new_len_without_nul_in_bytes) : Result < u32 , _ > =
1070+ ( ( to. count_bytes ( ) - 1 ) * 2 ) . try_into ( )
1071+ else {
1072+ return Err ( io:: Error :: new ( io:: ErrorKind :: InvalidFilename , "Filename too long" ) ) ;
10441073 } ;
1074+ let offset: u32 = offset_of ! ( c:: FILE_RENAME_INFO , FileName ) . try_into ( ) . unwrap ( ) ;
1075+ let struct_size = offset + new_len_without_nul_in_bytes + 2 ;
1076+ let layout =
1077+ Layout :: from_size_align ( struct_size as usize , align_of :: < c:: FILE_RENAME_INFO > ( ) )
1078+ . unwrap ( ) ;
1079+
1080+ // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
1081+ let file_rename_info;
1082+ unsafe {
1083+ file_rename_info = alloc ( layout) . cast :: < c:: FILE_RENAME_INFO > ( ) ;
1084+ if file_rename_info. is_null ( ) {
1085+ return Err ( io:: ErrorKind :: OutOfMemory . into ( ) ) ;
1086+ }
1087+
1088+ ( & raw mut ( * file_rename_info) . Anonymous ) . write ( c:: FILE_RENAME_INFO_0 {
1089+ Flags : c:: FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c:: FILE_RENAME_FLAG_POSIX_SEMANTICS ,
1090+ } ) ;
1091+
1092+ ( & raw mut ( * file_rename_info) . RootDirectory ) . write ( to_dir. handle . as_raw_handle ( ) ) ;
1093+ // Don't include the NULL in the size
1094+ ( & raw mut ( * file_rename_info) . FileNameLength ) . write ( new_len_without_nul_in_bytes) ;
1095+
1096+ to. as_ptr ( ) . copy_to_nonoverlapping (
1097+ ( & raw mut ( * file_rename_info) . FileName ) . cast :: < u16 > ( ) ,
1098+ run_path_with_wcstr ( from, & |s| Ok ( s. count_bytes ( ) ) ) . unwrap ( ) ,
1099+ ) ;
1100+ }
1101+
10451102 let result = unsafe {
10461103 c:: SetFileInformationByHandle (
10471104 handle. as_raw_handle ( ) ,
1048- c:: FileRenameInfo ,
1049- ptr :: addr_of! ( info ) as _ ,
1050- size_of :: < c :: FILE_RENAME_INFO > ( ) as _ ,
1105+ c:: FileRenameInfoEx ,
1106+ file_rename_info . cast :: < c_void > ( ) ,
1107+ struct_size ,
10511108 )
10521109 } ;
1110+ unsafe { dealloc ( file_rename_info. cast :: < u8 > ( ) , layout) } ;
10531111 if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
10541112 }
10551113}
0 commit comments