44#![ feature( io_error_more) ]
55#![ feature( io_error_uncategorized) ]
66
7- use std:: ffi:: CString ;
7+ use std:: ffi:: { CStr , CString , OsString } ;
88use std:: fs:: { canonicalize, remove_dir_all, remove_file, File } ;
99use std:: io:: { Error , ErrorKind , Write } ;
1010use std:: os:: unix:: ffi:: OsStrExt ;
11+ use std:: os:: unix:: io:: AsRawFd ;
1112use std:: path:: PathBuf ;
1213
1314#[ path = "../../utils/mod.rs" ]
@@ -27,6 +28,14 @@ fn main() {
2728 #[ cfg( target_os = "linux" ) ]
2829 test_o_tmpfile_flag ( ) ;
2930 test_posix_mkstemp ( ) ;
31+ test_posix_realpath_alloc ( ) ;
32+ test_posix_realpath_noalloc ( ) ;
33+ test_posix_realpath_errors ( ) ;
34+ #[ cfg( target_os = "linux" ) ]
35+ test_posix_fadvise ( ) ;
36+ #[ cfg( target_os = "linux" ) ]
37+ test_sync_file_range ( ) ;
38+ test_isatty ( ) ;
3039}
3140
3241/// Prepare: compute filename and make sure the file does not exist.
@@ -256,3 +265,166 @@ fn test_posix_mkstemp() {
256265 assert_eq ! ( e. kind( ) , std:: io:: ErrorKind :: InvalidInput ) ;
257266 }
258267}
268+
269+ /// Test allocating variant of `realpath`.
270+ fn test_posix_realpath_alloc ( ) {
271+ use std:: os:: unix:: ffi:: OsStrExt ;
272+ use std:: os:: unix:: ffi:: OsStringExt ;
273+
274+ let buf;
275+ let path = utils:: tmp ( ) . join ( "miri_test_libc_posix_realpath_alloc" ) ;
276+ let c_path = CString :: new ( path. as_os_str ( ) . as_bytes ( ) ) . expect ( "CString::new failed" ) ;
277+
278+ // Cleanup before test.
279+ remove_file ( & path) . ok ( ) ;
280+ // Create file.
281+ drop ( File :: create ( & path) . unwrap ( ) ) ;
282+ unsafe {
283+ let r = libc:: realpath ( c_path. as_ptr ( ) , std:: ptr:: null_mut ( ) ) ;
284+ assert ! ( !r. is_null( ) ) ;
285+ buf = CStr :: from_ptr ( r) . to_bytes ( ) . to_vec ( ) ;
286+ libc:: free ( r as * mut _ ) ;
287+ }
288+ let canonical = PathBuf :: from ( OsString :: from_vec ( buf) ) ;
289+ assert_eq ! ( path. file_name( ) , canonical. file_name( ) ) ;
290+
291+ // Cleanup after test.
292+ remove_file ( & path) . unwrap ( ) ;
293+ }
294+
295+ /// Test non-allocating variant of `realpath`.
296+ fn test_posix_realpath_noalloc ( ) {
297+ use std:: ffi:: { CStr , CString } ;
298+ use std:: os:: unix:: ffi:: OsStrExt ;
299+
300+ let path = utils:: tmp ( ) . join ( "miri_test_libc_posix_realpath_noalloc" ) ;
301+ let c_path = CString :: new ( path. as_os_str ( ) . as_bytes ( ) ) . expect ( "CString::new failed" ) ;
302+
303+ let mut v = vec ! [ 0 ; libc:: PATH_MAX as usize ] ;
304+
305+ // Cleanup before test.
306+ remove_file ( & path) . ok ( ) ;
307+ // Create file.
308+ drop ( File :: create ( & path) . unwrap ( ) ) ;
309+ unsafe {
310+ let r = libc:: realpath ( c_path. as_ptr ( ) , v. as_mut_ptr ( ) ) ;
311+ assert ! ( !r. is_null( ) ) ;
312+ }
313+ let c = unsafe { CStr :: from_ptr ( v. as_ptr ( ) ) } ;
314+ let canonical = PathBuf :: from ( c. to_str ( ) . expect ( "CStr to str" ) ) ;
315+
316+ assert_eq ! ( path. file_name( ) , canonical. file_name( ) ) ;
317+
318+ // Cleanup after test.
319+ remove_file ( & path) . unwrap ( ) ;
320+ }
321+
322+ /// Test failure cases for `realpath`.
323+ fn test_posix_realpath_errors ( ) {
324+ use std:: ffi:: CString ;
325+ use std:: io:: ErrorKind ;
326+
327+ // Test nonexistent path returns an error.
328+ let c_path = CString :: new ( "./nothing_to_see_here" ) . expect ( "CString::new failed" ) ;
329+ let r = unsafe { libc:: realpath ( c_path. as_ptr ( ) , std:: ptr:: null_mut ( ) ) } ;
330+ assert ! ( r. is_null( ) ) ;
331+ let e = std:: io:: Error :: last_os_error ( ) ;
332+ assert_eq ! ( e. raw_os_error( ) , Some ( libc:: ENOENT ) ) ;
333+ assert_eq ! ( e. kind( ) , ErrorKind :: NotFound ) ;
334+ }
335+
336+ #[ cfg( target_os = "linux" ) ]
337+ fn test_posix_fadvise ( ) {
338+ use std:: io:: Write ;
339+
340+ let path = utils:: tmp ( ) . join ( "miri_test_libc_posix_fadvise.txt" ) ;
341+ // Cleanup before test
342+ remove_file ( & path) . ok ( ) ;
343+
344+ // Set up an open file
345+ let mut file = File :: create ( & path) . unwrap ( ) ;
346+ let bytes = b"Hello, World!\n " ;
347+ file. write ( bytes) . unwrap ( ) ;
348+
349+ // Test calling posix_fadvise on a file.
350+ let result = unsafe {
351+ libc:: posix_fadvise (
352+ file. as_raw_fd ( ) ,
353+ 0 ,
354+ bytes. len ( ) . try_into ( ) . unwrap ( ) ,
355+ libc:: POSIX_FADV_DONTNEED ,
356+ )
357+ } ;
358+ drop ( file) ;
359+ remove_file ( & path) . unwrap ( ) ;
360+ assert_eq ! ( result, 0 ) ;
361+ }
362+
363+ #[ cfg( target_os = "linux" ) ]
364+ fn test_sync_file_range ( ) {
365+ use std:: io:: Write ;
366+
367+ let path = utils:: tmp ( ) . join ( "miri_test_libc_sync_file_range.txt" ) ;
368+ // Cleanup before test.
369+ remove_file ( & path) . ok ( ) ;
370+
371+ // Write to a file.
372+ let mut file = File :: create ( & path) . unwrap ( ) ;
373+ let bytes = b"Hello, World!\n " ;
374+ file. write ( bytes) . unwrap ( ) ;
375+
376+ // Test calling sync_file_range on the file.
377+ let result_1 = unsafe {
378+ libc:: sync_file_range (
379+ file. as_raw_fd ( ) ,
380+ 0 ,
381+ 0 ,
382+ libc:: SYNC_FILE_RANGE_WAIT_BEFORE
383+ | libc:: SYNC_FILE_RANGE_WRITE
384+ | libc:: SYNC_FILE_RANGE_WAIT_AFTER ,
385+ )
386+ } ;
387+ drop ( file) ;
388+
389+ // Test calling sync_file_range on a file opened for reading.
390+ let file = File :: open ( & path) . unwrap ( ) ;
391+ let result_2 = unsafe {
392+ libc:: sync_file_range (
393+ file. as_raw_fd ( ) ,
394+ 0 ,
395+ 0 ,
396+ libc:: SYNC_FILE_RANGE_WAIT_BEFORE
397+ | libc:: SYNC_FILE_RANGE_WRITE
398+ | libc:: SYNC_FILE_RANGE_WAIT_AFTER ,
399+ )
400+ } ;
401+ drop ( file) ;
402+
403+ remove_file ( & path) . unwrap ( ) ;
404+ assert_eq ! ( result_1, 0 ) ;
405+ assert_eq ! ( result_2, 0 ) ;
406+ }
407+
408+ fn test_isatty ( ) {
409+ // Testing whether our isatty shim returns the right value would require controlling whether
410+ // these streams are actually TTYs, which is hard.
411+ // For now, we just check that these calls are supported at all.
412+ unsafe {
413+ libc:: isatty ( libc:: STDIN_FILENO ) ;
414+ libc:: isatty ( libc:: STDOUT_FILENO ) ;
415+ libc:: isatty ( libc:: STDERR_FILENO ) ;
416+
417+ // But when we open a file, it is definitely not a TTY.
418+ let path = utils:: tmp ( ) . join ( "notatty.txt" ) ;
419+ // Cleanup before test.
420+ remove_file ( & path) . ok ( ) ;
421+ let file = File :: create ( & path) . unwrap ( ) ;
422+
423+ assert_eq ! ( libc:: isatty( file. as_raw_fd( ) ) , 0 ) ;
424+ assert_eq ! ( std:: io:: Error :: last_os_error( ) . raw_os_error( ) . unwrap( ) , libc:: ENOTTY ) ;
425+
426+ // Cleanup after test.
427+ drop ( file) ;
428+ remove_file ( & path) . unwrap ( ) ;
429+ }
430+ }
0 commit comments