@@ -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
@@ -861,20 +885,16 @@ impl File {
861885
862886unsafe fn nt_create_file (
863887 access : u32 ,
888+ disposition : u32 ,
864889 object_attributes : & c:: OBJECT_ATTRIBUTES ,
865890 share : u32 ,
866891 dir : bool ,
867892) -> Result < Handle , WinError > {
868893 let mut handle = ptr:: null_mut ( ) ;
869894 let mut io_status = c:: IO_STATUS_BLOCK :: PENDING ;
870- let disposition = match ( access & c:: GENERIC_READ > 0 , access & c:: GENERIC_WRITE > 0 ) {
871- ( true , true ) => c:: FILE_OPEN_IF ,
872- ( true , false ) => c:: FILE_OPEN ,
873- ( false , true ) => c:: FILE_CREATE ,
874- ( false , false ) => {
875- return Err ( WinError :: new ( c:: ERROR_INVALID_PARAMETER ) ) ;
876- }
877- } ;
895+ let access = access | c:: SYNCHRONIZE ;
896+ let options = if dir { c:: FILE_DIRECTORY_FILE } else { c:: FILE_NON_DIRECTORY_FILE }
897+ | c:: FILE_SYNCHRONOUS_IO_NONALERT ;
878898 let status = unsafe {
879899 c:: NtCreateFile (
880900 & mut handle,
@@ -885,7 +905,7 @@ unsafe fn nt_create_file(
885905 c:: FILE_ATTRIBUTE_NORMAL ,
886906 share,
887907 disposition,
888- if dir { c :: FILE_DIRECTORY_FILE } else { c :: FILE_NON_DIRECTORY_FILE } ,
908+ options ,
889909 ptr:: null ( ) ,
890910 0 ,
891911 )
@@ -916,38 +936,48 @@ fn run_path_with_wcstr<T, P: AsRef<Path>>(
916936 f ( path)
917937}
918938
939+ fn run_path_with_utf16 < T , P : AsRef < Path > > (
940+ path : P ,
941+ f : & dyn Fn ( & [ u16 ] ) -> io:: Result < T > ,
942+ ) -> io:: Result < T > {
943+ let utf16: Vec < u16 > = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect ( ) ;
944+ f ( & utf16)
945+ }
946+
919947impl Dir {
920948 pub fn new < P : AsRef < Path > > ( path : P ) -> io:: Result < Self > {
921949 let opts = OpenOptions :: new ( ) ;
922- run_path_with_wcstr ( path , & |path| Self :: new_native ( path, & opts) )
950+ Self :: new_native ( path. as_ref ( ) , & opts)
923951 }
924952
925953 pub fn new_with < P : AsRef < Path > > ( path : P , opts : & OpenOptions ) -> io:: Result < Self > {
926- run_path_with_wcstr ( path , & |path| Self :: new_native ( path, & opts) )
954+ Self :: new_native ( path. as_ref ( ) , & opts)
927955 }
928956
929957 pub fn open < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < File > {
930958 let mut opts = OpenOptions :: new ( ) ;
959+ let path = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect :: < Vec < _ > > ( ) ;
931960 opts. read ( true ) ;
932- Ok ( File { handle : run_path_with_wcstr ( path , & |path| self . open_native ( path, & opts) ) ? } )
961+ Ok ( File { handle : self . open_native ( & path, & opts) ? } )
933962 }
934963
935964 pub fn open_with < P : AsRef < Path > > ( & self , path : P , opts : & OpenOptions ) -> io:: Result < File > {
936- Ok ( File { handle : run_path_with_wcstr ( path, & |path| self . open_native ( path, & opts) ) ? } )
965+ let path = path. as_ref ( ) . as_os_str ( ) . encode_wide ( ) . collect :: < Vec < _ > > ( ) ;
966+ Ok ( File { handle : self . open_native ( & path, & opts) ? } )
937967 }
938968
939969 pub fn create_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
940970 let mut opts = OpenOptions :: new ( ) ;
941971 opts. write ( true ) ;
942- run_path_with_wcstr ( path, & |path| self . create_dir_native ( path, & opts) . map ( |_| ( ) ) )
972+ run_path_with_utf16 ( path, & |path| self . create_dir_native ( path, & opts) . map ( |_| ( ) ) )
943973 }
944974
945975 pub fn remove_file < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
946- run_path_with_wcstr ( path, & |path| self . remove_native ( path, false ) )
976+ run_path_with_utf16 ( path, & |path| self . remove_native ( path, false ) )
947977 }
948978
949979 pub fn remove_dir < P : AsRef < Path > > ( & self , path : P ) -> io:: Result < ( ) > {
950- run_path_with_wcstr ( path, & |path| self . remove_native ( path, true ) )
980+ run_path_with_utf16 ( path, & |path| self . remove_native ( path, true ) )
951981 }
952982
953983 pub fn rename < P : AsRef < Path > , Q : AsRef < Path > > (
@@ -956,36 +986,18 @@ impl Dir {
956986 to_dir : & Self ,
957987 to : Q ,
958988 ) -> io:: Result < ( ) > {
959- run_path_with_wcstr ( from. as_ref ( ) , & |from| {
960- run_path_with_wcstr ( to. as_ref ( ) , & |to| self . rename_native ( from, to_dir, to) )
961- } )
989+ run_path_with_wcstr ( to. as_ref ( ) , & |to| self . rename_native ( from. as_ref ( ) , to_dir, to) )
962990 }
963991
964- fn new_native ( path : & WCStr , opts : & OpenOptions ) -> io:: Result < Self > {
965- let name = c:: UNICODE_STRING {
966- Length : path. count_bytes ( ) as _ ,
967- MaximumLength : path. count_bytes ( ) as _ ,
968- Buffer : path. as_ptr ( ) as * mut _ ,
969- } ;
970- let object_attributes = c:: OBJECT_ATTRIBUTES {
971- Length : size_of :: < c:: OBJECT_ATTRIBUTES > ( ) as _ ,
972- RootDirectory : ptr:: null_mut ( ) ,
973- ObjectName : & name,
974- Attributes : 0 ,
975- SecurityDescriptor : ptr:: null ( ) ,
976- SecurityQualityOfService : ptr:: null ( ) ,
977- } ;
978- let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
979- let handle =
980- unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, true ) }
981- . io_result ( ) ?;
992+ fn new_native ( path : & Path , opts : & OpenOptions ) -> io:: Result < Self > {
993+ let handle = File :: open ( path, opts) ?. into_inner ( ) ;
982994 Ok ( Self { handle } )
983995 }
984996
985- fn open_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
997+ fn open_native ( & self , path : & [ u16 ] , opts : & OpenOptions ) -> io:: Result < Handle > {
986998 let name = c:: UNICODE_STRING {
987- Length : path. count_bytes ( ) as _ ,
988- MaximumLength : path. count_bytes ( ) as _ ,
999+ Length : path. len ( ) as _ ,
1000+ MaximumLength : path. len ( ) as _ ,
9891001 Buffer : path. as_ptr ( ) as * mut _ ,
9901002 } ;
9911003 let object_attributes = c:: OBJECT_ATTRIBUTES {
@@ -997,14 +1009,22 @@ impl Dir {
9971009 SecurityQualityOfService : ptr:: null ( ) ,
9981010 } ;
9991011 let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
1000- unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, false ) }
1001- . io_result ( )
1012+ unsafe {
1013+ nt_create_file (
1014+ opts. get_access_mode ( ) ?,
1015+ opts. get_disposition ( ) ?,
1016+ & object_attributes,
1017+ share,
1018+ false ,
1019+ )
1020+ }
1021+ . io_result ( )
10021022 }
10031023
1004- fn create_dir_native ( & self , path : & WCStr , opts : & OpenOptions ) -> io:: Result < Handle > {
1024+ fn create_dir_native ( & self , path : & [ u16 ] , opts : & OpenOptions ) -> io:: Result < Handle > {
10051025 let name = c:: UNICODE_STRING {
1006- Length : path. count_bytes ( ) as _ ,
1007- MaximumLength : path. count_bytes ( ) as _ ,
1026+ Length : path. len ( ) as _ ,
1027+ MaximumLength : path. len ( ) as _ ,
10081028 Buffer : path. as_ptr ( ) as * mut _ ,
10091029 } ;
10101030 let object_attributes = c:: OBJECT_ATTRIBUTES {
@@ -1016,11 +1036,19 @@ impl Dir {
10161036 SecurityQualityOfService : ptr:: null ( ) ,
10171037 } ;
10181038 let share = c:: FILE_SHARE_READ | c:: FILE_SHARE_WRITE | c:: FILE_SHARE_DELETE ;
1019- unsafe { nt_create_file ( opts. get_access_mode ( ) ?, & object_attributes, share, true ) }
1020- . io_result ( )
1039+ unsafe {
1040+ nt_create_file (
1041+ opts. get_access_mode ( ) ?,
1042+ opts. get_disposition ( ) ?,
1043+ & object_attributes,
1044+ share,
1045+ true ,
1046+ )
1047+ }
1048+ . io_result ( )
10211049 }
10221050
1023- fn remove_native ( & self , path : & WCStr , dir : bool ) -> io:: Result < ( ) > {
1051+ fn remove_native ( & self , path : & [ u16 ] , dir : bool ) -> io:: Result < ( ) > {
10241052 let mut opts = OpenOptions :: new ( ) ;
10251053 opts. access_mode ( c:: GENERIC_WRITE ) ;
10261054 let handle =
@@ -1037,24 +1065,54 @@ impl Dir {
10371065 if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
10381066 }
10391067
1040- fn rename_native ( & self , from : & WCStr , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
1068+ fn rename_native ( & self , from : & Path , to_dir : & Self , to : & WCStr ) -> io:: Result < ( ) > {
10411069 let mut opts = OpenOptions :: new ( ) ;
10421070 opts. access_mode ( c:: GENERIC_WRITE ) ;
1043- let handle = self . open_native ( from, & opts) ?;
1044- let info = c:: FILE_RENAME_INFO {
1045- Anonymous : c:: FILE_RENAME_INFO_0 { ReplaceIfExists : true } ,
1046- RootDirectory : to_dir. handle . as_raw_handle ( ) ,
1047- FileNameLength : to. count_bytes ( ) as _ ,
1048- FileName : [ to. as_ptr ( ) as u16 ] ,
1071+ let handle = run_path_with_utf16 ( from, & |u| self . open_native ( u, & opts) ) ?;
1072+ // Calculate the layout of the `FILE_RENAME_INFO` we pass to `SetFileInformation`
1073+ // This is a dynamically sized struct so we need to get the position of the last field to calculate the actual size.
1074+ let Ok ( new_len_without_nul_in_bytes) : Result < u32 , _ > =
1075+ ( ( to. count_bytes ( ) - 1 ) * 2 ) . try_into ( )
1076+ else {
1077+ return Err ( io:: Error :: new ( io:: ErrorKind :: InvalidFilename , "Filename too long" ) ) ;
10491078 } ;
1079+ let offset: u32 = offset_of ! ( c:: FILE_RENAME_INFO , FileName ) . try_into ( ) . unwrap ( ) ;
1080+ let struct_size = offset + new_len_without_nul_in_bytes + 2 ;
1081+ let layout =
1082+ Layout :: from_size_align ( struct_size as usize , align_of :: < c:: FILE_RENAME_INFO > ( ) )
1083+ . unwrap ( ) ;
1084+
1085+ // SAFETY: We allocate enough memory for a full FILE_RENAME_INFO struct and a filename.
1086+ let file_rename_info;
1087+ unsafe {
1088+ file_rename_info = alloc ( layout) . cast :: < c:: FILE_RENAME_INFO > ( ) ;
1089+ if file_rename_info. is_null ( ) {
1090+ return Err ( io:: ErrorKind :: OutOfMemory . into ( ) ) ;
1091+ }
1092+
1093+ ( & raw mut ( * file_rename_info) . Anonymous ) . write ( c:: FILE_RENAME_INFO_0 {
1094+ Flags : c:: FILE_RENAME_FLAG_REPLACE_IF_EXISTS | c:: FILE_RENAME_FLAG_POSIX_SEMANTICS ,
1095+ } ) ;
1096+
1097+ ( & raw mut ( * file_rename_info) . RootDirectory ) . write ( to_dir. handle . as_raw_handle ( ) ) ;
1098+ // Don't include the NULL in the size
1099+ ( & raw mut ( * file_rename_info) . FileNameLength ) . write ( new_len_without_nul_in_bytes) ;
1100+
1101+ to. as_ptr ( ) . copy_to_nonoverlapping (
1102+ ( & raw mut ( * file_rename_info) . FileName ) . cast :: < u16 > ( ) ,
1103+ run_path_with_wcstr ( from, & |s| Ok ( s. count_bytes ( ) ) ) . unwrap ( ) ,
1104+ ) ;
1105+ }
1106+
10501107 let result = unsafe {
10511108 c:: SetFileInformationByHandle (
10521109 handle. as_raw_handle ( ) ,
1053- c:: FileRenameInfo ,
1054- ptr :: addr_of! ( info ) as _ ,
1055- size_of :: < c :: FILE_RENAME_INFO > ( ) as _ ,
1110+ c:: FileRenameInfoEx ,
1111+ file_rename_info . cast :: < c_void > ( ) ,
1112+ struct_size ,
10561113 )
10571114 } ;
1115+ unsafe { dealloc ( file_rename_info. cast :: < u8 > ( ) , layout) } ;
10581116 if result == 0 { Err ( api:: get_last_error ( ) ) . io_result ( ) } else { Ok ( ( ) ) }
10591117 }
10601118}
0 commit comments