1 // ignore-windows: File handling is not implemented yet
2 // compile-flags: -Zmiri-disable-isolation
3
4 #![feature(rustc_private)]
5
6 extern crate libc;
7
8 use std::fs::{
9 File, create_dir, OpenOptions, read_dir, remove_dir, remove_dir_all, remove_file, rename,
10 };
11 use std::ffi::CString;
12 use std::io::{Read, Write, Error, ErrorKind, Result, Seek, SeekFrom};
13 use std::path::{PathBuf, Path};
14
15
main()16 fn main() {
17 test_file();
18 test_file_clone();
19 test_file_create_new();
20 test_seek();
21 test_metadata();
22 test_file_set_len();
23 test_file_sync();
24 test_symlink();
25 test_errors();
26 test_rename();
27 test_directory();
28 test_dup_stdout_stderr();
29 }
30
tmp() -> PathBuf31 fn tmp() -> PathBuf {
32 std::env::var("MIRI_TEMP")
33 .map(|tmp| {
34 // MIRI_TEMP is set outside of our emulated
35 // program, so it may have path separators that don't
36 // correspond to our target platform. We normalize them here
37 // before constructing a `PathBuf`
38
39 #[cfg(windows)]
40 return PathBuf::from(tmp.replace("/", "\\"));
41
42 #[cfg(not(windows))]
43 return PathBuf::from(tmp.replace("\\", "/"));
44 }).unwrap_or_else(|_| std::env::temp_dir())
45 }
46
47 /// Prepare: compute filename and make sure the file does not exist.
48 fn prepare(filename: &str) -> PathBuf {
49 let path = tmp().join(filename);
50 // Clean the paths for robustness.
51 remove_file(&path).ok();
52 path
53 }
54
55 /// Prepare directory: compute directory name and make sure it does not exist.
56 fn prepare_dir(dirname: &str) -> PathBuf {
57 let path = tmp().join(&dirname);
58 // Clean the directory for robustness.
59 remove_dir_all(&path).ok();
60 path
61 }
62
63 /// Prepare like above, and also write some initial content to the file.
64 fn prepare_with_content(filename: &str, content: &[u8]) -> PathBuf {
65 let path = prepare(filename);
66 let mut file = File::create(&path).unwrap();
67 file.write(content).unwrap();
68 path
69 }
70
71 fn test_file() {
72 let bytes = b"Hello, World!\n";
73 let path = prepare("miri_test_fs_file.txt");
74
75 // Test creating, writing and closing a file (closing is tested when `file` is dropped).
76 let mut file = File::create(&path).unwrap();
77 // Writing 0 bytes should not change the file contents.
78 file.write(&mut []).unwrap();
79 assert_eq!(file.metadata().unwrap().len(), 0);
80
81 file.write(bytes).unwrap();
82 assert_eq!(file.metadata().unwrap().len(), bytes.len() as u64);
83 // Test opening, reading and closing a file.
84 let mut file = File::open(&path).unwrap();
85 let mut contents = Vec::new();
86 // Reading 0 bytes should not move the file pointer.
87 file.read(&mut []).unwrap();
88 // Reading until EOF should get the whole text.
89 file.read_to_end(&mut contents).unwrap();
90 assert_eq!(bytes, contents.as_slice());
91
92 // Removing file should succeed.
93 remove_file(&path).unwrap();
94 }
95
96 fn test_file_clone() {
97 let bytes = b"Hello, World!\n";
98 let path = prepare_with_content("miri_test_fs_file_clone.txt", bytes);
99
100 // Cloning a file should be successful.
101 let file = File::open(&path).unwrap();
102 let mut cloned = file.try_clone().unwrap();
103 // Reading from a cloned file should get the same text.
104 let mut contents = Vec::new();
105 cloned.read_to_end(&mut contents).unwrap();
106 assert_eq!(bytes, contents.as_slice());
107
108 // Removing file should succeed.
109 remove_file(&path).unwrap();
110 }
111
112 fn test_file_create_new() {
113 let path = prepare("miri_test_fs_file_create_new.txt");
114
115 // Creating a new file that doesn't yet exist should succeed.
116 OpenOptions::new().write(true).create_new(true).open(&path).unwrap();
117 // Creating a new file that already exists should fail.
118 assert_eq!(ErrorKind::AlreadyExists, OpenOptions::new().write(true).create_new(true).open(&path).unwrap_err().kind());
119 // Optionally creating a new file that already exists should succeed.
120 OpenOptions::new().write(true).create(true).open(&path).unwrap();
121
122 // Clean up
123 remove_file(&path).unwrap();
124 }
125
126 fn test_seek() {
127 let bytes = b"Hello, entire World!\n";
128 let path = prepare_with_content("miri_test_fs_seek.txt", bytes);
129
130 let mut file = File::open(&path).unwrap();
131 let mut contents = Vec::new();
132 file.read_to_end(&mut contents).unwrap();
133 assert_eq!(bytes, contents.as_slice());
134 // Test that seeking to the beginning and reading until EOF gets the text again.
135 file.seek(SeekFrom::Start(0)).unwrap();
136 let mut contents = Vec::new();
137 file.read_to_end(&mut contents).unwrap();
138 assert_eq!(bytes, contents.as_slice());
139 // Test seeking relative to the end of the file.
140 file.seek(SeekFrom::End(-1)).unwrap();
141 let mut contents = Vec::new();
142 file.read_to_end(&mut contents).unwrap();
143 assert_eq!(&bytes[bytes.len() - 1..], contents.as_slice());
144 // Test seeking relative to the current position.
145 file.seek(SeekFrom::Start(5)).unwrap();
146 file.seek(SeekFrom::Current(-3)).unwrap();
147 let mut contents = Vec::new();
148 file.read_to_end(&mut contents).unwrap();
149 assert_eq!(&bytes[2..], contents.as_slice());
150
151 // Removing file should succeed.
152 remove_file(&path).unwrap();
153 }
154
155 fn check_metadata(bytes: &[u8], path: &Path) -> Result<()> {
156 // Test that the file metadata is correct.
157 let metadata = path.metadata()?;
158 // `path` should point to a file.
159 assert!(metadata.is_file());
160 // The size of the file must be equal to the number of written bytes.
161 assert_eq!(bytes.len() as u64, metadata.len());
162 Ok(())
163 }
164
165 fn test_metadata() {
166 let bytes = b"Hello, meta-World!\n";
167 let path = prepare_with_content("miri_test_fs_metadata.txt", bytes);
168
169 // Test that metadata of an absolute path is correct.
170 check_metadata(bytes, &path).unwrap();
171 // Test that metadata of a relative path is correct.
172 std::env::set_current_dir(path.parent().unwrap()).unwrap();
173 check_metadata(bytes, Path::new(path.file_name().unwrap())).unwrap();
174
175 // Removing file should succeed.
176 remove_file(&path).unwrap();
177 }
178
179 fn test_file_set_len() {
180 let bytes = b"Hello, World!\n";
181 let path = prepare_with_content("miri_test_fs_set_len.txt", bytes);
182
183 // Test extending the file
184 let mut file = OpenOptions::new().read(true).write(true).open(&path).unwrap();
185 let bytes_extended = b"Hello, World!\n\x00\x00\x00\x00\x00\x00";
186 file.set_len(20).unwrap();
187 let mut contents = Vec::new();
188 file.read_to_end(&mut contents).unwrap();
189 assert_eq!(bytes_extended, contents.as_slice());
190
191 // Test truncating the file
192 file.seek(SeekFrom::Start(0)).unwrap();
193 file.set_len(10).unwrap();
194 let mut contents = Vec::new();
195 file.read_to_end(&mut contents).unwrap();
196 assert_eq!(&bytes[..10], contents.as_slice());
197
198 // Can't use set_len on a file not opened for writing
199 let file = OpenOptions::new().read(true).open(&path).unwrap();
200 assert_eq!(ErrorKind::InvalidInput, file.set_len(14).unwrap_err().kind());
201
202 remove_file(&path).unwrap();
203 }
204
205 fn test_file_sync() {
206 let bytes = b"Hello, World!\n";
207 let path = prepare_with_content("miri_test_fs_sync.txt", bytes);
208
209 // Test that we can call sync_data and sync_all (can't readily test effects of this operation)
210 let file = OpenOptions::new().write(true).open(&path).unwrap();
211 file.sync_data().unwrap();
212 file.sync_all().unwrap();
213
214 // Test that we can call sync_data and sync_all on a file opened for reading.
215 let file = File::open(&path).unwrap();
216 file.sync_data().unwrap();
217 file.sync_all().unwrap();
218
219 remove_file(&path).unwrap();
220 }
221
222 fn test_symlink() {
223 let bytes = b"Hello, World!\n";
224 let path = prepare_with_content("miri_test_fs_link_target.txt", bytes);
225 let symlink_path = prepare("miri_test_fs_symlink.txt");
226
227 // Creating a symbolic link should succeed.
228 #[cfg(unix)]
229 std::os::unix::fs::symlink(&path, &symlink_path).unwrap();
230 #[cfg(windows)]
231 std::os::windows::fs::symlink_file(&path, &symlink_path).unwrap();
232 // Test that the symbolic link has the same contents as the file.
233 let mut symlink_file = File::open(&symlink_path).unwrap();
234 let mut contents = Vec::new();
235 symlink_file.read_to_end(&mut contents).unwrap();
236 assert_eq!(bytes, contents.as_slice());
237
238
239 #[cfg(unix)]
240 {
241 use std::os::unix::ffi::OsStrExt;
242
243 let expected_path = path.as_os_str().as_bytes();
244
245 // Test that the expected string gets written to a buffer of proper
246 // length, and that a trailing null byte is not written.
247 let symlink_c_str = CString::new(symlink_path.as_os_str().as_bytes()).unwrap();
248 let symlink_c_ptr = symlink_c_str.as_ptr();
249
250 // Make the buf one byte larger than it needs to be,
251 // and check that the last byte is not overwritten.
252 let mut large_buf = vec![0xFF; expected_path.len() + 1];
253 let res = unsafe { libc::readlink(symlink_c_ptr, large_buf.as_mut_ptr().cast(), large_buf.len()) };
254 // Check that the resovled path was properly written into the buf.
255 assert_eq!(&large_buf[..(large_buf.len() - 1)], expected_path);
256 assert_eq!(large_buf.last(), Some(&0xFF));
257 assert_eq!(res, large_buf.len() as isize - 1);
258
259 // Test that the resolved path is truncated if the provided buffer
260 // is too small.
261 let mut small_buf = [0u8; 2];
262 let res = unsafe { libc::readlink(symlink_c_ptr, small_buf.as_mut_ptr().cast(), small_buf.len()) };
263 assert_eq!(small_buf, &expected_path[..small_buf.len()]);
264 assert_eq!(res, small_buf.len() as isize);
265
266 // Test that we report a proper error for a missing path.
267 let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
268 let res = unsafe { libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len()) };
269 assert_eq!(res, -1);
270 assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
271 }
272
273
274 // Test that metadata of a symbolic link is correct.
275 check_metadata(bytes, &symlink_path).unwrap();
276 // Test that the metadata of a symbolic link is correct when not following it.
277 assert!(symlink_path.symlink_metadata().unwrap().file_type().is_symlink());
278 // Removing symbolic link should succeed.
279 remove_file(&symlink_path).unwrap();
280
281 // Removing file should succeed.
282 remove_file(&path).unwrap();
283 }
284
285 fn test_errors() {
286 let bytes = b"Hello, World!\n";
287 let path = prepare("miri_test_fs_errors.txt");
288
289 // The following tests also check that the `__errno_location()` shim is working properly.
290 // Opening a non-existing file should fail with a "not found" error.
291 assert_eq!(ErrorKind::NotFound, File::open(&path).unwrap_err().kind());
292 // Removing a non-existing file should fail with a "not found" error.
293 assert_eq!(ErrorKind::NotFound, remove_file(&path).unwrap_err().kind());
294 // Reading the metadata of a non-existing file should fail with a "not found" error.
295 assert_eq!(ErrorKind::NotFound, check_metadata(bytes, &path).unwrap_err().kind());
296 }
297
298 fn test_rename() {
299 // Renaming a file should succeed.
300 let path1 = prepare("miri_test_fs_rename_source.txt");
301 let path2 = prepare("miri_test_fs_rename_destination.txt");
302
303 let file = File::create(&path1).unwrap();
304 drop(file);
305
306 // Renaming should succeed
307 rename(&path1, &path2).unwrap();
308 // Check that the old file path isn't present
309 assert_eq!(ErrorKind::NotFound, path1.metadata().unwrap_err().kind());
310 // Check that the file has moved successfully
311 assert!(path2.metadata().unwrap().is_file());
312
313 // Renaming a nonexistent file should fail
314 assert_eq!(ErrorKind::NotFound, rename(&path1, &path2).unwrap_err().kind());
315
316 remove_file(&path2).unwrap();
317 }
318
319 fn test_directory() {
320 let dir_path = prepare_dir("miri_test_fs_dir");
321 // Creating a directory should succeed.
322 create_dir(&dir_path).unwrap();
323 // Test that the metadata of a directory is correct.
324 assert!(dir_path.metadata().unwrap().is_dir());
325 // Creating a directory when it already exists should fail.
326 assert_eq!(ErrorKind::AlreadyExists, create_dir(&dir_path).unwrap_err().kind());
327
328 // Create some files inside the directory
329 let path_1 = dir_path.join("test_file_1");
330 drop(File::create(&path_1).unwrap());
331 let path_2 = dir_path.join("test_file_2");
332 drop(File::create(&path_2).unwrap());
333 // Test that the files are present inside the directory
334 let dir_iter = read_dir(&dir_path).unwrap();
335 let mut file_names = dir_iter.map(|e| e.unwrap().file_name()).collect::<Vec<_>>();
336 file_names.sort_unstable();
337 assert_eq!(file_names, vec!["test_file_1", "test_file_2"]);
338 // Clean up the files in the directory
339 remove_file(&path_1).unwrap();
340 remove_file(&path_2).unwrap();
341 // Now there should be nothing left in the directory.
342 let dir_iter = read_dir(&dir_path).unwrap();
343 let file_names = dir_iter.map(|e| e.unwrap().file_name()).collect::<Vec<_>>();
344 assert!(file_names.is_empty());
345
346 // Deleting the directory should succeed.
347 remove_dir(&dir_path).unwrap();
348 // Reading the metadata of a non-existent directory should fail with a "not found" error.
349 assert_eq!(ErrorKind::NotFound, check_metadata(&[], &dir_path).unwrap_err().kind());
350 }
351
352 fn test_dup_stdout_stderr() {
353 let bytes = b"hello dup fd\n";
354 unsafe {
355 let new_stdout = libc::fcntl(1, libc::F_DUPFD, 0);
356 let new_stderr = libc::fcntl(2, libc::F_DUPFD, 0);
357 libc::write(new_stdout, bytes.as_ptr() as *const libc::c_void, bytes.len());
358 libc::write(new_stderr, bytes.as_ptr() as *const libc::c_void, bytes.len());
359 }
360 }
361