@@ -16,6 +16,10 @@ mod utils;
1616fn main ( ) {
1717 test_dup_stdout_stderr ( ) ;
1818 test_canonicalize_too_long ( ) ;
19+ test_rename ( ) ;
20+ test_ftruncate :: < libc:: off_t > ( libc:: ftruncate) ;
21+ #[ cfg( target_os = "linux" ) ]
22+ test_ftruncate :: < libc:: off64_t > ( libc:: ftruncate64) ;
1923 test_readlink ( ) ;
2024 test_file_open_unix_allow_two_args ( ) ;
2125 test_file_open_unix_needs_three_args ( ) ;
@@ -133,6 +137,65 @@ fn test_readlink() {
133137 assert_eq ! ( Error :: last_os_error( ) . kind( ) , ErrorKind :: NotFound ) ;
134138}
135139
140+ fn test_rename ( ) {
141+ let path1 = prepare ( "miri_test_libc_fs_source.txt" ) ;
142+ let path2 = prepare ( "miri_test_libc_fs_rename_destination.txt" ) ;
143+
144+ let file = File :: create ( & path1) . unwrap ( ) ;
145+ drop ( file) ;
146+
147+ let c_path1 = CString :: new ( path1. as_os_str ( ) . as_bytes ( ) ) . expect ( "CString::new failed" ) ;
148+ let c_path2 = CString :: new ( path2. as_os_str ( ) . as_bytes ( ) ) . expect ( "CString::new failed" ) ;
149+
150+ // Renaming should succeed
151+ unsafe { libc:: rename ( c_path1. as_ptr ( ) , c_path2. as_ptr ( ) ) } ;
152+ // Check that old file path isn't present
153+ assert_eq ! ( ErrorKind :: NotFound , path1. metadata( ) . unwrap_err( ) . kind( ) ) ;
154+ // Check that the file has moved successfully
155+ assert ! ( path2. metadata( ) . unwrap( ) . is_file( ) ) ;
156+
157+ // Renaming a nonexistent file should fail
158+ let res = unsafe { libc:: rename ( c_path1. as_ptr ( ) , c_path2. as_ptr ( ) ) } ;
159+ assert_eq ! ( res, -1 ) ;
160+ assert_eq ! ( Error :: last_os_error( ) . kind( ) , ErrorKind :: NotFound ) ;
161+
162+ remove_file ( & path2) . unwrap ( ) ;
163+ }
164+
165+ fn test_ftruncate < T : From < i32 > > (
166+ ftruncate : unsafe extern "C" fn ( fd : libc:: c_int , length : T ) -> libc:: c_int ,
167+ ) {
168+ // libc::off_t is i32 in target i686-unknown-linux-gnu
169+ // https://docs.rs/libc/latest/i686-unknown-linux-gnu/libc/type.off_t.html
170+
171+ let bytes = b"hello" ;
172+ let path = prepare ( "miri_test_libc_fs_ftruncate.txt" ) ;
173+ let mut file = File :: create ( & path) . unwrap ( ) ;
174+ file. write ( bytes) . unwrap ( ) ;
175+ file. sync_all ( ) . unwrap ( ) ;
176+ assert_eq ! ( file. metadata( ) . unwrap( ) . len( ) , 5 ) ;
177+
178+ let c_path = CString :: new ( path. as_os_str ( ) . as_bytes ( ) ) . expect ( "CString::new failed" ) ;
179+ let fd = unsafe { libc:: open ( c_path. as_ptr ( ) , libc:: O_RDWR ) } ;
180+
181+ // Truncate to a bigger size
182+ let mut res = unsafe { ftruncate ( fd, T :: from ( 10 ) ) } ;
183+ assert_eq ! ( res, 0 ) ;
184+ assert_eq ! ( file. metadata( ) . unwrap( ) . len( ) , 10 ) ;
185+
186+ // Write after truncate
187+ file. write ( b"dup" ) . unwrap ( ) ;
188+ file. sync_all ( ) . unwrap ( ) ;
189+ assert_eq ! ( file. metadata( ) . unwrap( ) . len( ) , 10 ) ;
190+
191+ // Truncate to smaller size
192+ res = unsafe { ftruncate ( fd, T :: from ( 2 ) ) } ;
193+ assert_eq ! ( res, 0 ) ;
194+ assert_eq ! ( file. metadata( ) . unwrap( ) . len( ) , 2 ) ;
195+
196+ remove_file ( & path) . unwrap ( ) ;
197+ }
198+
136199#[ cfg( target_os = "linux" ) ]
137200fn test_o_tmpfile_flag ( ) {
138201 use std:: fs:: { create_dir, OpenOptions } ;
0 commit comments