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 use std::ffi::{CStr, CString};
12 let path = std::env::var("MIRI_TEMP")
13 .unwrap_or_else(|_| std::env::temp_dir().into_os_string().into_string().unwrap());
14 // These are host paths. We need to convert them to the target.
15 let path = CString::new(path).unwrap();
16 let mut out = Vec::with_capacity(1024);
20 fn miri_host_to_target_path(path: *const i8, out: *mut i8, out_size: usize) -> usize;
22 let ret = miri_host_to_target_path(path.as_ptr(), out.as_mut_ptr(), out.capacity());
24 let out = CStr::from_ptr(out.as_ptr()).to_str().unwrap();
29 /// Test allocating variant of `realpath`.
30 fn test_posix_realpath_alloc() {
31 use std::ffi::OsString;
32 use std::ffi::{CStr, CString};
33 use std::os::unix::ffi::OsStrExt;
34 use std::os::unix::ffi::OsStringExt;
37 let path = tmp().join("miri_test_libc_posix_realpath_alloc");
38 let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
40 // Cleanup before test.
41 remove_file(&path).ok();
43 drop(File::create(&path).unwrap());
45 let r = libc::realpath(c_path.as_ptr(), std::ptr::null_mut());
46 assert!(!r.is_null());
47 buf = CStr::from_ptr(r).to_bytes().to_vec();
48 libc::free(r as *mut _);
50 let canonical = PathBuf::from(OsString::from_vec(buf));
51 assert_eq!(path.file_name(), canonical.file_name());
53 // Cleanup after test.
54 remove_file(&path).unwrap();
57 /// Test non-allocating variant of `realpath`.
58 fn test_posix_realpath_noalloc() {
59 use std::ffi::{CStr, CString};
60 use std::os::unix::ffi::OsStrExt;
62 let path = tmp().join("miri_test_libc_posix_realpath_noalloc");
63 let c_path = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed");
65 let mut v = vec![0; libc::PATH_MAX as usize];
67 // Cleanup before test.
68 remove_file(&path).ok();
70 drop(File::create(&path).unwrap());
72 let r = libc::realpath(c_path.as_ptr(), v.as_mut_ptr());
73 assert!(!r.is_null());
75 let c = unsafe { CStr::from_ptr(v.as_ptr()) };
76 let canonical = PathBuf::from(c.to_str().expect("CStr to str"));
78 assert_eq!(path.file_name(), canonical.file_name());
80 // Cleanup after test.
81 remove_file(&path).unwrap();
84 /// Test failure cases for `realpath`.
85 fn test_posix_realpath_errors() {
86 use std::ffi::CString;
87 use std::io::ErrorKind;
89 // Test non-existent path returns an error.
90 let c_path = CString::new("./nothing_to_see_here").expect("CString::new failed");
91 let r = unsafe { libc::realpath(c_path.as_ptr(), std::ptr::null_mut()) };
93 let e = std::io::Error::last_os_error();
94 assert_eq!(e.raw_os_error(), Some(libc::ENOENT));
95 assert_eq!(e.kind(), ErrorKind::NotFound);
98 #[cfg(target_os = "linux")]
99 fn test_posix_fadvise() {
100 use std::convert::TryInto;
103 let path = tmp().join("miri_test_libc_posix_fadvise.txt");
104 // Cleanup before test
105 remove_file(&path).ok();
107 // Set up an open file
108 let mut file = File::create(&path).unwrap();
109 let bytes = b"Hello, World!\n";
110 file.write(bytes).unwrap();
112 // Test calling posix_fadvise on a file.
113 let result = unsafe {
117 bytes.len().try_into().unwrap(),
118 libc::POSIX_FADV_DONTNEED,
122 remove_file(&path).unwrap();
123 assert_eq!(result, 0);
126 #[cfg(target_os = "linux")]
127 fn test_sync_file_range() {
130 let path = tmp().join("miri_test_libc_sync_file_range.txt");
131 // Cleanup before test.
132 remove_file(&path).ok();
135 let mut file = File::create(&path).unwrap();
136 let bytes = b"Hello, World!\n";
137 file.write(bytes).unwrap();
139 // Test calling sync_file_range on the file.
140 let result_1 = unsafe {
141 libc::sync_file_range(
145 libc::SYNC_FILE_RANGE_WAIT_BEFORE
146 | libc::SYNC_FILE_RANGE_WRITE
147 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
152 // Test calling sync_file_range on a file opened for reading.
153 let file = File::open(&path).unwrap();
154 let result_2 = unsafe {
155 libc::sync_file_range(
159 libc::SYNC_FILE_RANGE_WAIT_BEFORE
160 | libc::SYNC_FILE_RANGE_WRITE
161 | libc::SYNC_FILE_RANGE_WAIT_AFTER,
166 remove_file(&path).unwrap();
167 assert_eq!(result_1, 0);
168 assert_eq!(result_2, 0);
171 /// Tests whether each thread has its own `__errno_location`.
172 fn test_thread_local_errno() {
173 #[cfg(target_os = "linux")]
174 use libc::__errno_location;
175 #[cfg(any(target_os = "macos", target_os = "freebsd"))]
176 use libc::__error as __errno_location;
179 *__errno_location() = 0xBEEF;
180 std::thread::spawn(|| {
181 assert_eq!(*__errno_location(), 0);
182 *__errno_location() = 0xBAD1DEA;
183 assert_eq!(*__errno_location(), 0xBAD1DEA);
187 assert_eq!(*__errno_location(), 0xBEEF);
191 /// Tests whether clock support exists at all
193 let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit();
194 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
195 assert_eq!(is_error, 0);
196 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
197 assert_eq!(is_error, 0);
198 #[cfg(target_os = "linux")]
200 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
201 assert_eq!(is_error, 0);
203 unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
204 assert_eq!(is_error, 0);
206 #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
208 let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) };
209 assert_eq!(is_error, 0);
213 fn test_posix_gettimeofday() {
214 let mut tp = std::mem::MaybeUninit::<libc::timeval>::uninit();
215 let tz = std::ptr::null_mut::<libc::timezone>();
216 #[cfg(target_os = "macos")] // `tz` has a different type on macOS
217 let tz = tz as *mut libc::c_void;
218 let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz) };
219 assert_eq!(is_error, 0);
220 let tv = unsafe { tp.assume_init() };
221 assert!(tv.tv_sec > 0);
222 assert!(tv.tv_usec >= 0); // Theoretically this could be 0.
224 // Test that non-null tz returns an error.
225 let mut tz = std::mem::MaybeUninit::<libc::timezone>::uninit();
226 let tz_ptr = tz.as_mut_ptr();
227 #[cfg(target_os = "macos")] // `tz` has a different type on macOS
228 let tz_ptr = tz_ptr as *mut libc::c_void;
229 let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr) };
230 assert_eq!(is_error, -1);
234 // Testing whether our isatty shim returns the right value would require controlling whether
235 // these streams are actually TTYs, which is hard.
236 // For now, we just check that these calls are supported at all.
238 libc::isatty(libc::STDIN_FILENO);
239 libc::isatty(libc::STDOUT_FILENO);
240 libc::isatty(libc::STDERR_FILENO);
242 // But when we open a file, it is definitely not a TTY.
243 let path = tmp().join("notatty.txt");
244 // Cleanup before test.
245 remove_file(&path).ok();
246 let file = File::create(&path).unwrap();
248 assert_eq!(libc::isatty(file.as_raw_fd()), 0);
249 assert_eq!(std::io::Error::last_os_error().raw_os_error().unwrap(), libc::ENOTTY);
251 // Cleanup after test.
253 remove_file(&path).unwrap();
257 fn test_posix_mkstemp() {
258 use std::ffi::CString;
260 use std::os::unix::ffi::OsStrExt;
261 use std::os::unix::io::FromRawFd;
264 let valid_template = "fooXXXXXX";
265 // C needs to own this as `mkstemp(3)` says:
266 // "Since it will be modified, `template` must not be a string constant, but
267 // should be declared as a character array."
268 // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`.
269 let ptr = CString::new(valid_template).unwrap().into_raw();
270 let fd = unsafe { libc::mkstemp(ptr) };
271 // Take ownership back in Rust to not leak memory.
272 let slice = unsafe { CString::from_raw(ptr) };
274 let osstr = OsStr::from_bytes(slice.to_bytes());
275 let path: &Path = osstr.as_ref();
276 let name = path.file_name().unwrap().to_string_lossy();
277 assert!(name.ne("fooXXXXXX"));
278 assert!(name.starts_with("foo"));
279 assert_eq!(name.len(), 9);
281 name.chars().skip(3).filter(char::is_ascii_alphanumeric).collect::<Vec<char>>().len(),
284 let file = unsafe { File::from_raw_fd(fd) };
285 assert!(file.set_len(0).is_ok());
287 let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"];
288 for t in invalid_templates {
289 let ptr = CString::new(t).unwrap().into_raw();
290 let fd = unsafe { libc::mkstemp(ptr) };
291 let _ = unsafe { CString::from_raw(ptr) };
292 // "On error, -1 is returned, and errno is set to
293 // indicate the error"
295 let e = std::io::Error::last_os_error();
296 assert_eq!(e.raw_os_error(), Some(libc::EINVAL));
297 assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput);
302 test_posix_gettimeofday();
303 test_posix_mkstemp();
305 test_posix_realpath_alloc();
306 test_posix_realpath_noalloc();
307 test_posix_realpath_errors();
309 test_thread_local_errno();
314 #[cfg(target_os = "linux")]
316 test_posix_fadvise();
317 test_sync_file_range();