1 //@ignore-target-windows: No libc on Windows
2 //@compile-flags: -Zmiri-disable-isolation
3 #![feature(io_error_more)]
5 use std::fs::{remove_file, File};
6 use std::os::unix::io::AsRawFd;
7 use std::path::PathBuf;
10 std::env::var("MIRI_TEMP")
12 // MIRI_TEMP is set outside of our emulated
13 // program, so it may have path separators that don't
14 // correspond to our target platform. We normalize them here
15 // before constructing a `PathBuf`
16 return PathBuf::from(tmp.replace("\\", "/"));
18 .unwrap_or_else(|_| std::env::temp_dir())
21 /// Test allocating variant of `realpath`.
22 fn test_posix_realpath_alloc() {
23 use std::ffi::OsString;
24 use std::ffi::{CStr, CString};
25 use std::os::unix::ffi::OsStrExt;
26 use std::os::unix::ffi::OsStringExt;
29 let path = tmp().join("miri_test_libc_posix_realpath_alloc");
30 let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
32 // Cleanup before test.
33 remove_file(&path).ok();
35 drop(File::create(&path).unwrap());
37 let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut());
38 assert!(!r.is_null());
39 buf = CStr::from_ptr(r).to_bytes().to_vec();
40 libc::free(r as *mut _);
42 let canonical = PathBuf::from(OsString::from_vec(buf));
43 assert_eq!(path.file_name(), canonical.file_name());
45 // Cleanup after test.
46 remove_file(&path).unwrap();
49 /// Test non-allocating variant of `realpath`.
50 fn test_posix_realpath_noalloc() {
51 use std::ffi::{CStr, CString};
52 use std::os::unix::ffi::OsStrExt;
54 let path = tmp().join("miri_test_libc_posix_realpath_noalloc");
55 let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
57 let mut v = vec![0; libc::PATH_MAX as usize];
59 // Cleanup before test.
60 remove_file(&path).ok();
62 drop(File::create(&path).unwrap());
64 let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr());
65 assert!(!r.is_null());
67 let c = unsafe { CStr::from_ptr(v.as_ptr()) };
68 let canonical = PathBuf::from(c.to_str().expect("CStr to str"));
70 assert_eq!(path.file_name(), canonical.file_name());
72 // Cleanup after test.
73 remove_file(&path).unwrap();
76 /// Test failure cases for `realpath`.
77 fn test_posix_realpath_errors() {
78 use std::ffi::CString;
79 use std::io::ErrorKind;
81 // Test non-existent path returns an error.
82 let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed");
83 let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) };
85 let e = std::io::Error::last_os_error();
86 assert_eq!(e.raw_os_error(), Some(libc::ENOENT));
87 assert_eq!(e.kind(), ErrorKind::NotFound);
90 #[cfg(target_os = "linux")]
91 fn test_posix_fadvise() {
92 use std::convert::TryInto;
95 let path = tmp().join("miri_test_libc_posix_fadvise.txt");
96 // Cleanup before test
97 remove_file(&path).ok();
99 // Set up an open file
100 let mut file = File::create(&path).unwrap();
101 let bytes = b"Hello, World!\n";
102 file.write(bytes).unwrap();
104 // Test calling posix_fadvise on a file.
105 let result = unsafe {
109 bytes.len().try_into().unwrap(),
110 libc::POSIX_FADV_DONTNEED,
114 remove_file(&path).unwrap();
115 assert_eq!(result, 0);
118 #[cfg(target_os = "linux")]
119 fn test_sync_file_range() {
122 let path = tmp().join("miri_test_libc_sync_file_range.txt");
123 // Cleanup before test.
124 remove_file(&path).ok();
127 let mut file = File::create(&path).unwrap();
128 let bytes = b"Hello, World!\n";
129 file.write(bytes).unwrap();
131 // Test calling sync_file_range on the file.
132 let result_1 = unsafe {
133 libc::sync_file_range(
137 libc::SYNC_FILE_RANGE_WAIT_BEFORE
138 | libc::SYNC_FILE_RANGE_WRITE
139 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
144 // Test calling sync_file_range on a file opened for reading.
145 let file = File::open(&path).unwrap();
146 let result_2 = unsafe {
147 libc::sync_file_range(
151 libc::SYNC_FILE_RANGE_WAIT_BEFORE
152 | libc::SYNC_FILE_RANGE_WRITE
153 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
158 remove_file(&path).unwrap();
159 assert_eq!(result_1, 0);
160 assert_eq!(result_2, 0);
163 /// Tests whether each thread has its own `__errno_location`.
164 fn test_thread_local_errno() {
165 #[cfg(target_os = "linux")]
166 use libc::__errno_location;
167 #[cfg(any(target_os = "macos", target_os = "freebsd"))]
168 use libc::__error as __errno_location;
171 *__errno_location() = 0xBEEF;
172 std::thread::spawn(|| {
173 assert_eq!(*__errno_location(), 0);
174 *__errno_location() = 0xBAD1DEA;
175 assert_eq!(*__errno_location(), 0xBAD1DEA);
179 assert_eq!(*__errno_location(), 0xBEEF);
183 /// Tests whether clock support exists at all
185 let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit();
186 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
187 assert_eq!(is_error, 0);
188 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
189 assert_eq!(is_error, 0);
190 #[cfg(target_os = "linux")]
192 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
193 assert_eq!(is_error, 0);
195 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
196 assert_eq!(is_error, 0);
198 #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
200 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) };
201 assert_eq!(is_error, 0);
205 fn test_posix_gettimeofday() {
206 let mut tp = std::mem::MaybeUninit::<libc::timeval>::uninit();
207 let tz = std::ptr::null_mut::<libc::timezone>();
208 #[cfg(target_os = "macos")] // `tz` has a different type on macOS
209 let tz = tz as *mut libc::c_void;
210 let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz) };
211 assert_eq!(is_error, 0);
212 let tv = unsafe { tp.assume_init() };
213 assert!(tv.tv_sec > 0);
214 assert!(tv.tv_usec >= 0); // Theoretically this could be 0.
216 // Test that non-null tz returns an error.
217 let mut tz = std::mem::MaybeUninit::<libc::timezone>::uninit();
218 let tz_ptr = tz.as_mut_ptr();
219 #[cfg(target_os = "macos")] // `tz` has a different type on macOS
220 let tz_ptr = tz_ptr as *mut libc::c_void;
221 let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr) };
222 assert_eq!(is_error, -1);
226 // Testing whether our isatty shim returns the right value would require controlling whether
227 // these streams are actually TTYs, which is hard.
228 // For now, we just check that these calls are supported at all.
230 libc::isatty(libc::STDIN_FILENO);
231 libc::isatty(libc::STDOUT_FILENO);
232 libc::isatty(libc::STDERR_FILENO);
234 // But when we open a file, it is definitely not a TTY.
235 let path = tmp().join("notatty.txt");
236 // Cleanup before test.
237 remove_file(&path).ok();
238 let file = File::create(&path).unwrap();
240 assert_eq!(libc::isatty(file.as_raw_fd()), 0);
241 assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY);
243 // Cleanup after test.
245 remove_file(&path).unwrap();
249 fn test_posix_mkstemp() {
250 use std::ffi::CString;
252 use std::os::unix::ffi::OsStrExt;
253 use std::os::unix::io::FromRawFd;
256 let valid_template = "fooXXXXXX";
257 // C needs to own this as `mkstemp(3)` says:
258 // "Since it will be modified, `template` must not be a string constant, but
259 // should be declared as a character array."
260 // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`.
261 let ptr = CString::new(valid_template).unwrap().into_raw();
262 let fd = unsafe { libc::mkstemp(ptr) };
263 // Take ownership back in Rust to not leak memory.
264 let slice = unsafe { CString::from_raw(ptr) };
266 let osstr = OsStr::from_bytes(slice.to_bytes());
267 let path: &Path = osstr.as_ref();
268 let name = path.file_name().unwrap().to_string_lossy();
269 assert!(name.ne("fooXXXXXX"));
270 assert!(name.starts_with("foo"));
271 assert_eq!(name.len(), 9);
273 name.chars().skip(3).filter(char::is_ascii_alphanumeric).collect::<Vec<char>>().len(),
276 let file = unsafe { File::from_raw_fd(fd) };
277 assert!(file.set_len(0).is_ok());
279 let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"];
280 for t in invalid_templates {
281 let ptr = CString::new(t).unwrap().into_raw();
282 let fd = unsafe { libc::mkstemp(ptr) };
283 let _ = unsafe { CString::from_raw(ptr) };
284 // "On error, -1 is returned, and errno is set to
285 // indicate the error"
287 let e = std::io::Error::last_os_error();
288 assert_eq!(e.raw_os_error(), Some(libc::EINVAL));
289 assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput);
294 test_posix_gettimeofday();
295 test_posix_mkstemp();
297 test_posix_realpath_alloc();
298 test_posix_realpath_noalloc();
299 test_posix_realpath_errors();
301 test_thread_local_errno();
306 #[cfg(target_os = "linux")]
308 test_posix_fadvise();
309 test_sync_file_range();