|
1 | 1 | //@ignore-target-windows: No libc on Windows
|
2 | 2 | //@compile-flags: -Zmiri-disable-isolation
|
| 3 | +#![feature(io_error_more)] |
3 | 4 | #![feature(rustc_private)]
|
4 | 5 |
|
5 | 6 | use std::fs::{remove_file, File};
|
6 | 7 | use std::os::unix::io::AsRawFd;
|
| 8 | +use std::path::PathBuf; |
7 | 9 |
|
8 |
| -fn tmp() -> std::path::PathBuf { |
| 10 | +fn tmp() -> PathBuf { |
9 | 11 | std::env::var("MIRI_TEMP")
|
10 |
| - .map(std::path::PathBuf::from) |
| 12 | + .map(|tmp| { |
| 13 | + // MIRI_TEMP is set outside of our emulated |
| 14 | + // program, so it may have path separators that don't |
| 15 | + // correspond to our target platform. We normalize them here |
| 16 | + // before constructing a `PathBuf` |
| 17 | + return PathBuf::from(tmp.replace("\\", "/")); |
| 18 | + }) |
11 | 19 | .unwrap_or_else(|_| std::env::temp_dir())
|
12 | 20 | }
|
13 | 21 |
|
| 22 | +/// Test allocating variant of `realpath`. |
| 23 | +fn test_posix_realpath_alloc() { |
| 24 | + use std::ffi::OsString; |
| 25 | + use std::ffi::{CStr, CString}; |
| 26 | + use std::fs::{remove_file, File}; |
| 27 | + use std::os::unix::ffi::OsStrExt; |
| 28 | + use std::os::unix::ffi::OsStringExt; |
| 29 | + |
| 30 | + let buf; |
| 31 | + let path = tmp().join("miri_test_libc_posix_realpath_alloc"); |
| 32 | + let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); |
| 33 | + |
| 34 | + // Cleanup before test. |
| 35 | + remove_file(&path).ok(); |
| 36 | + // Create file. |
| 37 | + drop(File::create(&path).unwrap()); |
| 38 | + unsafe { |
| 39 | + let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut()); |
| 40 | + assert!(!r.is_null()); |
| 41 | + buf = CStr::from_ptr(r).to_bytes().to_vec(); |
| 42 | + libc::free(r as *mut _); |
| 43 | + } |
| 44 | + let canonical = PathBuf::from(OsString::from_vec(buf)); |
| 45 | + assert_eq!(path.file_name(), canonical.file_name()); |
| 46 | + |
| 47 | + // Cleanup after test. |
| 48 | + remove_file(&path).unwrap(); |
| 49 | +} |
| 50 | + |
| 51 | +/// Test non-allocating variant of `realpath`. |
| 52 | +fn test_posix_realpath_noalloc() { |
| 53 | + use std::ffi::{CStr, CString}; |
| 54 | + use std::fs::{remove_file, File}; |
| 55 | + use std::os::unix::ffi::OsStrExt; |
| 56 | + |
| 57 | + let path = tmp().join("miri_test_libc_posix_realpath_noalloc"); |
| 58 | + let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); |
| 59 | + |
| 60 | + let mut v = vec![0; libc::PATH_MAX as usize]; |
| 61 | + |
| 62 | + // Cleanup before test. |
| 63 | + remove_file(&path).ok(); |
| 64 | + // Create file. |
| 65 | + drop(File::create(&path).unwrap()); |
| 66 | + unsafe { |
| 67 | + let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr()); |
| 68 | + assert!(!r.is_null()); |
| 69 | + } |
| 70 | + let c = unsafe { CStr::from_ptr(v.as_ptr()) }; |
| 71 | + let canonical = PathBuf::from(c.to_str().expect("CStr to str")); |
| 72 | + |
| 73 | + assert_eq!(path.file_name(), canonical.file_name()); |
| 74 | + |
| 75 | + // Cleanup after test. |
| 76 | + remove_file(&path).unwrap(); |
| 77 | +} |
| 78 | + |
| 79 | +/// Test failure cases for `realpath`. |
| 80 | +fn test_posix_realpath_errors() { |
| 81 | + use std::convert::TryInto; |
| 82 | + use std::ffi::CString; |
| 83 | + use std::fs::{create_dir_all, remove_dir_all}; |
| 84 | + use std::io::ErrorKind; |
| 85 | + use std::os::unix::ffi::OsStrExt; |
| 86 | + use std::os::unix::fs::symlink; |
| 87 | + |
| 88 | + // Test non-existent path returns an error. |
| 89 | + let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed"); |
| 90 | + let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) }; |
| 91 | + assert!(r.is_null()); |
| 92 | + let e = std::io::Error::last_os_error(); |
| 93 | + assert_eq!(e.raw_os_error(), Some(libc::ENOENT)); |
| 94 | + assert_eq!(e.kind(), ErrorKind::NotFound); |
| 95 | + |
| 96 | + // Test that a long path returns an error. |
| 97 | + // |
| 98 | + // Linux first checks if the path exists and macos does not. |
| 99 | + // Using an existing path ensures all platforms return `ENAMETOOLONG` given a long path. |
| 100 | + // |
| 101 | + // Rather than creating a bunch of directories, we create two directories containing symlinks. |
| 102 | + // Sadly we can't avoid creating directories and instead use a path like "./././././" or "./../../" as linux |
| 103 | + // appears to collapse "." and ".." before checking path length. |
| 104 | + let path = tmp().join("posix_realpath_errors"); |
| 105 | + // Cleanup before test. |
| 106 | + remove_dir_all(&path).ok(); |
| 107 | + |
| 108 | + // The directories we will put symlinks in. |
| 109 | + let x = path.join("x/"); |
| 110 | + let y = path.join("y/"); |
| 111 | + |
| 112 | + // The symlinks in each directory pointing to each other. |
| 113 | + let yx_sym = y.join("x"); |
| 114 | + let xy_sym = x.join("y"); |
| 115 | + |
| 116 | + // Create directories. |
| 117 | + create_dir_all(&x).expect("dir x"); |
| 118 | + create_dir_all(&y).expect("dir y"); |
| 119 | + |
| 120 | + // Create symlinks between directories. |
| 121 | + symlink(&x, &yx_sym).expect("symlink x"); |
| 122 | + symlink(&y, &xy_sym).expect("symlink y "); |
| 123 | + |
| 124 | + // This path exists due to the symlinks created above. |
| 125 | + let too_long = path.join("x/y/".repeat(libc::PATH_MAX.try_into().unwrap())); |
| 126 | + |
| 127 | + let c_path = CString::new(too_long.into_os_string().as_bytes()).expect("CString::new failed"); |
| 128 | + let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) }; |
| 129 | + let e = std::io::Error::last_os_error(); |
| 130 | + |
| 131 | + assert!(r.is_null()); |
| 132 | + assert_eq!(e.raw_os_error(), Some(libc::ENAMETOOLONG)); |
| 133 | + assert_eq!(e.kind(), ErrorKind::InvalidFilename); |
| 134 | + |
| 135 | + // Cleanup after test. |
| 136 | + remove_dir_all(&path).ok(); |
| 137 | +} |
| 138 | + |
14 | 139 | #[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
15 | 140 | fn test_posix_fadvise() {
|
16 | 141 | use std::convert::TryInto;
|
@@ -336,6 +461,10 @@ fn main() {
|
336 | 461 |
|
337 | 462 | test_posix_gettimeofday();
|
338 | 463 |
|
| 464 | + test_posix_realpath_alloc(); |
| 465 | + test_posix_realpath_noalloc(); |
| 466 | + test_posix_realpath_errors(); |
| 467 | + |
339 | 468 | #[cfg(any(target_os = "linux"))]
|
340 | 469 | test_sync_file_range();
|
341 | 470 |
|
|
0 commit comments