1 use crate::io::prelude::*;
4 use crate::fs::{self, File, OpenOptions};
5 use crate::io::{ErrorKind, SeekFrom};
9 use crate::sys_common::io::test::{tmpdir, TempDir};
11 use crate::time::{Duration, Instant};
13 use rand::{rngs::StdRng, RngCore, SeedableRng};
16 use crate::os::unix::fs::symlink as symlink_dir;
18 use crate::os::unix::fs::symlink as symlink_file;
20 use crate::os::unix::fs::symlink as symlink_junction;
22 use crate::os::windows::fs::{symlink_dir, symlink_file};
24 use crate::sys::fs::symlink_junction;
25 #[cfg(target_os = "macos")]
26 use crate::sys::weak::weak;
27 #[cfg(target_os = "macos")]
28 use libc::{c_char, c_int};
34 Err(e) => panic!("{} failed with: {e}", stringify!($e)),
41 ($e:expr, $s:expr) => {
43 Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
45 assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s)
53 ($e:expr, $s:expr) => {
54 error_contains!($e, $s)
58 macro_rules! error_contains {
59 ($e:expr, $s:expr) => {
61 Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
63 assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s)
69 // Several test fail on windows if the user does not have permission to
70 // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
71 // disabling these test on Windows, use this function to test whether we
72 // have permission, and return otherwise. This way, we still don't run these
73 // tests most of the time, but at least we do if the user has the right
75 pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
79 let link = tmpdir.join("some_hopefully_unique_link_name");
81 match symlink_file(r"nonexisting_target", link) {
82 // ERROR_PRIVILEGE_NOT_HELD = 1314
83 Err(ref err) if err.raw_os_error() == Some(1314) => false,
84 Ok(_) | Err(_) => true,
88 #[cfg(target_os = "macos")]
89 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
90 weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
91 linkat.get().is_some()
94 #[cfg(not(target_os = "macos"))]
95 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
100 fn file_test_io_smoke_test() {
101 let message = "it's alright. have a good time";
102 let tmpdir = tmpdir();
103 let filename = &tmpdir.join("file_rt_io_file_test.txt");
105 let mut write_stream = check!(File::create(filename));
106 check!(write_stream.write(message.as_bytes()));
109 let mut read_stream = check!(File::open(filename));
110 let mut read_buf = [0; 1028];
111 let read_str = match check!(read_stream.read(&mut read_buf)) {
112 0 => panic!("shouldn't happen"),
113 n => str::from_utf8(&read_buf[..n]).unwrap().to_string(),
115 assert_eq!(read_str, message);
117 check!(fs::remove_file(filename));
121 fn invalid_path_raises() {
122 let tmpdir = tmpdir();
123 let filename = &tmpdir.join("file_that_does_not_exist.txt");
124 let result = File::open(filename);
126 #[cfg(all(unix, not(target_os = "vxworks")))]
127 error!(result, "No such file or directory");
128 #[cfg(target_os = "vxworks")]
129 error!(result, "no such file or directory");
131 error!(result, 2); // ERROR_FILE_NOT_FOUND
135 fn file_test_iounlinking_invalid_path_should_raise_condition() {
136 let tmpdir = tmpdir();
137 let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
139 let result = fs::remove_file(filename);
141 #[cfg(all(unix, not(target_os = "vxworks")))]
142 error!(result, "No such file or directory");
143 #[cfg(target_os = "vxworks")]
144 error!(result, "no such file or directory");
146 error!(result, 2); // ERROR_FILE_NOT_FOUND
150 fn file_test_io_non_positional_read() {
151 let message: &str = "ten-four";
152 let mut read_mem = [0; 8];
153 let tmpdir = tmpdir();
154 let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
156 let mut rw_stream = check!(File::create(filename));
157 check!(rw_stream.write(message.as_bytes()));
160 let mut read_stream = check!(File::open(filename));
162 let read_buf = &mut read_mem[0..4];
163 check!(read_stream.read(read_buf));
166 let read_buf = &mut read_mem[4..8];
167 check!(read_stream.read(read_buf));
170 check!(fs::remove_file(filename));
171 let read_str = str::from_utf8(&read_mem).unwrap();
172 assert_eq!(read_str, message);
176 fn file_test_io_seek_and_tell_smoke_test() {
177 let message = "ten-four";
178 let mut read_mem = [0; 4];
179 let set_cursor = 4 as u64;
180 let tell_pos_pre_read;
181 let tell_pos_post_read;
182 let tmpdir = tmpdir();
183 let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
185 let mut rw_stream = check!(File::create(filename));
186 check!(rw_stream.write(message.as_bytes()));
189 let mut read_stream = check!(File::open(filename));
190 check!(read_stream.seek(SeekFrom::Start(set_cursor)));
191 tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0)));
192 check!(read_stream.read(&mut read_mem));
193 tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0)));
195 check!(fs::remove_file(filename));
196 let read_str = str::from_utf8(&read_mem).unwrap();
197 assert_eq!(read_str, &message[4..8]);
198 assert_eq!(tell_pos_pre_read, set_cursor);
199 assert_eq!(tell_pos_post_read, message.len() as u64);
203 fn file_test_io_seek_and_write() {
204 let initial_msg = "food-is-yummy";
205 let overwrite_msg = "-the-bar!!";
206 let final_msg = "foo-the-bar!!";
208 let mut read_mem = [0; 13];
209 let tmpdir = tmpdir();
210 let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
212 let mut rw_stream = check!(File::create(filename));
213 check!(rw_stream.write(initial_msg.as_bytes()));
214 check!(rw_stream.seek(SeekFrom::Start(seek_idx)));
215 check!(rw_stream.write(overwrite_msg.as_bytes()));
218 let mut read_stream = check!(File::open(filename));
219 check!(read_stream.read(&mut read_mem));
221 check!(fs::remove_file(filename));
222 let read_str = str::from_utf8(&read_mem).unwrap();
223 assert!(read_str == final_msg);
227 fn file_test_io_seek_shakedown() {
229 let initial_msg = "qwer-asdf-zxcv";
230 let chunk_one: &str = "qwer";
231 let chunk_two: &str = "asdf";
232 let chunk_three: &str = "zxcv";
233 let mut read_mem = [0; 4];
234 let tmpdir = tmpdir();
235 let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
237 let mut rw_stream = check!(File::create(filename));
238 check!(rw_stream.write(initial_msg.as_bytes()));
241 let mut read_stream = check!(File::open(filename));
243 check!(read_stream.seek(SeekFrom::End(-4)));
244 check!(read_stream.read(&mut read_mem));
245 assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three);
247 check!(read_stream.seek(SeekFrom::Current(-9)));
248 check!(read_stream.read(&mut read_mem));
249 assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two);
251 check!(read_stream.seek(SeekFrom::Start(0)));
252 check!(read_stream.read(&mut read_mem));
253 assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one);
255 check!(fs::remove_file(filename));
259 fn file_test_io_eof() {
260 let tmpdir = tmpdir();
261 let filename = tmpdir.join("file_rt_io_file_test_eof.txt");
262 let mut buf = [0; 256];
264 let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
265 let mut rw = check!(oo.open(&filename));
266 assert_eq!(check!(rw.read(&mut buf)), 0);
267 assert_eq!(check!(rw.read(&mut buf)), 0);
269 check!(fs::remove_file(&filename));
274 fn file_test_io_read_write_at() {
275 use crate::os::unix::fs::FileExt;
277 let tmpdir = tmpdir();
278 let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
279 let mut buf = [0; 256];
281 let write2 = "qwer-";
282 let write3 = "-zxcv";
283 let content = "qwer-asdf-zxcv";
285 let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
286 let mut rw = check!(oo.open(&filename));
287 assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len());
288 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
289 assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len());
290 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
291 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
292 assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
293 assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0"));
294 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
295 assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
296 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
297 assert_eq!(check!(rw.read(&mut buf)), write1.len());
298 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
299 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
300 assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
301 assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
302 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
303 assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len());
304 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
307 let mut read = check!(File::open(&filename));
308 assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
309 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
310 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0);
311 assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
312 assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
313 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
314 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9);
315 assert_eq!(check!(read.read(&mut buf)), write3.len());
316 assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
317 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
318 assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
319 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
320 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
321 assert_eq!(check!(read.read_at(&mut buf, 14)), 0);
322 assert_eq!(check!(read.read_at(&mut buf, 15)), 0);
323 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
325 check!(fs::remove_file(&filename));
330 fn set_get_unix_permissions() {
331 use crate::os::unix::fs::PermissionsExt;
333 let tmpdir = tmpdir();
334 let filename = &tmpdir.join("set_get_unix_permissions");
335 check!(fs::create_dir(filename));
338 check!(fs::set_permissions(filename, fs::Permissions::from_mode(0)));
339 let metadata0 = check!(fs::metadata(filename));
340 assert_eq!(mask & metadata0.permissions().mode(), 0);
342 check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777)));
343 let metadata1 = check!(fs::metadata(filename));
344 #[cfg(all(unix, not(target_os = "vxworks")))]
345 assert_eq!(mask & metadata1.permissions().mode(), 0o1777);
346 #[cfg(target_os = "vxworks")]
347 assert_eq!(mask & metadata1.permissions().mode(), 0o0777);
352 fn file_test_io_seek_read_write() {
353 use crate::os::windows::fs::FileExt;
355 let tmpdir = tmpdir();
356 let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt");
357 let mut buf = [0; 256];
359 let write2 = "qwer-";
360 let write3 = "-zxcv";
361 let content = "qwer-asdf-zxcv";
363 let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
364 let mut rw = check!(oo.open(&filename));
365 assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len());
366 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
367 assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len());
368 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
369 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
370 assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0);
371 assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
372 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
373 assert_eq!(check!(rw.read(&mut buf)), write1.len());
374 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
375 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
376 assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len());
377 assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
378 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
379 assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len());
380 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14);
383 let mut read = check!(File::open(&filename));
384 assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
385 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
386 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
387 assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
388 assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
389 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
390 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
391 assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
392 assert_eq!(check!(read.read(&mut buf)), write3.len());
393 assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
394 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
395 assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
396 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
397 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
398 assert_eq!(check!(read.seek_read(&mut buf, 14)), 0);
399 assert_eq!(check!(read.seek_read(&mut buf, 15)), 0);
401 check!(fs::remove_file(&filename));
405 fn file_test_stat_is_correct_on_is_file() {
406 let tmpdir = tmpdir();
407 let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
409 let mut opts = OpenOptions::new();
410 let mut fs = check!(opts.read(true).write(true).create(true).open(filename));
412 fs.write(msg.as_bytes()).unwrap();
414 let fstat_res = check!(fs.metadata());
415 assert!(fstat_res.is_file());
417 let stat_res_fn = check!(fs::metadata(filename));
418 assert!(stat_res_fn.is_file());
419 let stat_res_meth = check!(filename.metadata());
420 assert!(stat_res_meth.is_file());
421 check!(fs::remove_file(filename));
425 fn file_test_stat_is_correct_on_is_dir() {
426 let tmpdir = tmpdir();
427 let filename = &tmpdir.join("file_stat_correct_on_is_dir");
428 check!(fs::create_dir(filename));
429 let stat_res_fn = check!(fs::metadata(filename));
430 assert!(stat_res_fn.is_dir());
431 let stat_res_meth = check!(filename.metadata());
432 assert!(stat_res_meth.is_dir());
433 check!(fs::remove_dir(filename));
437 fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
438 let tmpdir = tmpdir();
439 let dir = &tmpdir.join("fileinfo_false_on_dir");
440 check!(fs::create_dir(dir));
441 assert!(!dir.is_file());
442 check!(fs::remove_dir(dir));
446 fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
447 let tmpdir = tmpdir();
448 let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
449 check!(check!(File::create(file)).write(b"foo"));
450 assert!(file.exists());
451 check!(fs::remove_file(file));
452 assert!(!file.exists());
456 fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
457 let tmpdir = tmpdir();
458 let dir = &tmpdir.join("before_and_after_dir");
459 assert!(!dir.exists());
460 check!(fs::create_dir(dir));
461 assert!(dir.exists());
462 assert!(dir.is_dir());
463 check!(fs::remove_dir(dir));
464 assert!(!dir.exists());
468 fn file_test_directoryinfo_readdir() {
469 let tmpdir = tmpdir();
470 let dir = &tmpdir.join("di_readdir");
471 check!(fs::create_dir(dir));
474 let f = dir.join(&format!("{n}.txt"));
475 let mut w = check!(File::create(&f));
476 let msg_str = format!("{}{}", prefix, n.to_string());
477 let msg = msg_str.as_bytes();
478 check!(w.write(msg));
480 let files = check!(fs::read_dir(dir));
481 let mut mem = [0; 4];
483 let f = f.unwrap().path();
485 let n = f.file_stem().unwrap();
486 check!(check!(File::open(&f)).read(&mut mem));
487 let read_str = str::from_utf8(&mem).unwrap();
488 let expected = format!("{}{}", prefix, n.to_str().unwrap());
489 assert_eq!(expected, read_str);
491 check!(fs::remove_file(&f));
493 check!(fs::remove_dir(dir));
497 fn file_create_new_already_exists_error() {
498 let tmpdir = tmpdir();
499 let file = &tmpdir.join("file_create_new_error_exists");
500 check!(fs::File::create(file));
501 let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err();
502 assert_eq!(e.kind(), ErrorKind::AlreadyExists);
506 fn mkdir_path_already_exists_error() {
507 let tmpdir = tmpdir();
508 let dir = &tmpdir.join("mkdir_error_twice");
509 check!(fs::create_dir(dir));
510 let e = fs::create_dir(dir).unwrap_err();
511 assert_eq!(e.kind(), ErrorKind::AlreadyExists);
515 fn recursive_mkdir() {
516 let tmpdir = tmpdir();
517 let dir = tmpdir.join("d1/d2");
518 check!(fs::create_dir_all(&dir));
519 assert!(dir.is_dir())
523 fn recursive_mkdir_failure() {
524 let tmpdir = tmpdir();
525 let dir = tmpdir.join("d1");
526 let file = dir.join("f1");
528 check!(fs::create_dir_all(&dir));
529 check!(File::create(&file));
531 let result = fs::create_dir_all(&file);
533 assert!(result.is_err());
537 fn concurrent_recursive_mkdir() {
540 let mut dir = dir.join("a");
544 let mut join = vec![];
546 let dir = dir.clone();
547 join.push(thread::spawn(move || {
548 check!(fs::create_dir_all(&dir));
552 // No `Display` on result of `join()`
553 join.drain(..).map(|join| join.join().unwrap()).count();
558 fn recursive_mkdir_slash() {
559 check!(fs::create_dir_all(Path::new("/")));
563 fn recursive_mkdir_dot() {
564 check!(fs::create_dir_all(Path::new(".")));
568 fn recursive_mkdir_empty() {
569 check!(fs::create_dir_all(Path::new("")));
573 fn recursive_rmdir() {
574 let tmpdir = tmpdir();
575 let d1 = tmpdir.join("d1");
576 let dt = d1.join("t");
577 let dtt = dt.join("t");
578 let d2 = tmpdir.join("d2");
579 let canary = d2.join("do_not_delete");
580 check!(fs::create_dir_all(&dtt));
581 check!(fs::create_dir_all(&d2));
582 check!(check!(File::create(&canary)).write(b"foo"));
583 check!(symlink_junction(&d2, &dt.join("d2")));
584 let _ = symlink_file(&canary, &d1.join("canary"));
585 check!(fs::remove_dir_all(&d1));
587 assert!(!d1.is_dir());
588 assert!(canary.exists());
592 fn recursive_rmdir_of_symlink() {
593 // test we do not recursively delete a symlink but only dirs.
594 let tmpdir = tmpdir();
595 let link = tmpdir.join("d1");
596 let dir = tmpdir.join("d2");
597 let canary = dir.join("do_not_delete");
598 check!(fs::create_dir_all(&dir));
599 check!(check!(File::create(&canary)).write(b"foo"));
600 check!(symlink_junction(&dir, &link));
601 check!(fs::remove_dir_all(&link));
603 assert!(!link.is_dir());
604 assert!(canary.exists());
608 fn recursive_rmdir_of_file_fails() {
609 // test we do not delete a directly specified file.
610 let tmpdir = tmpdir();
611 let canary = tmpdir.join("do_not_delete");
612 check!(check!(File::create(&canary)).write(b"foo"));
613 let result = fs::remove_dir_all(&canary);
615 error!(result, "Not a directory");
617 error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid.
618 assert!(result.is_err());
619 assert!(canary.exists());
623 // only Windows makes a distinction between file and directory symlinks.
625 fn recursive_rmdir_of_file_symlink() {
626 let tmpdir = tmpdir();
627 if !got_symlink_permission(&tmpdir) {
631 let f1 = tmpdir.join("f1");
632 let f2 = tmpdir.join("f2");
633 check!(check!(File::create(&f1)).write(b"foo"));
634 check!(symlink_file(&f1, &f2));
635 match fs::remove_dir_all(&f2) {
636 Ok(..) => panic!("wanted a failure"),
642 #[ignore] // takes too much time
643 fn recursive_rmdir_toctou() {
644 // Test for time-of-check to time-of-use issues.
647 // The attacker wants to get directory contents deleted, to which they do not have access.
648 // They have a way to get a privileged Rust binary call `std::fs::remove_dir_all()` on a
649 // directory they control, e.g. in their home directory.
651 // The POC sets up the `attack_dest/attack_file` which the attacker wants to have deleted.
652 // The attacker repeatedly creates a directory and replaces it with a symlink from
653 // `victim_del` to `attack_dest` while the victim code calls `std::fs::remove_dir_all()`
654 // on `victim_del`. After a few seconds the attack has succeeded and
655 // `attack_dest/attack_file` is deleted.
656 let tmpdir = tmpdir();
657 let victim_del_path = tmpdir.join("victim_del");
658 let victim_del_path_clone = victim_del_path.clone();
661 let attack_dest_dir = tmpdir.join("attack_dest");
662 let attack_dest_dir = attack_dest_dir.as_path();
663 fs::create_dir(attack_dest_dir).unwrap();
664 let attack_dest_file = tmpdir.join("attack_dest/attack_file");
665 File::create(&attack_dest_file).unwrap();
667 let drop_canary_arc = Arc::new(());
668 let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
670 eprintln!("x: {:?}", &victim_del_path);
672 // victim just continuously removes `victim_del`
673 thread::spawn(move || {
674 while drop_canary_weak.upgrade().is_some() {
675 let _ = fs::remove_dir_all(&victim_del_path_clone);
679 // attacker (could of course be in a separate process)
680 let start_time = Instant::now();
681 while Instant::now().duration_since(start_time) < Duration::from_secs(1000) {
682 if !attack_dest_file.exists() {
684 "Victim deleted symlinked file outside of victim_del. Attack succeeded in {:?}.",
685 Instant::now().duration_since(start_time)
688 let _ = fs::create_dir(&victim_del_path);
689 let _ = fs::remove_dir(&victim_del_path);
690 let _ = symlink_dir(attack_dest_dir, &victim_del_path);
695 fn unicode_path_is_dir() {
696 assert!(Path::new(".").is_dir());
697 assert!(!Path::new("test/stdtest/fs.rs").is_dir());
699 let tmpdir = tmpdir();
701 let mut dirpath = tmpdir.path().to_path_buf();
702 dirpath.push("test-가一ー你好");
703 check!(fs::create_dir(&dirpath));
704 assert!(dirpath.is_dir());
706 let mut filepath = dirpath;
707 filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs");
708 check!(File::create(&filepath)); // ignore return; touch only
709 assert!(!filepath.is_dir());
710 assert!(filepath.exists());
714 fn unicode_path_exists() {
715 assert!(Path::new(".").exists());
716 assert!(!Path::new("test/nonexistent-bogus-path").exists());
718 let tmpdir = tmpdir();
719 let unicode = tmpdir.path();
720 let unicode = unicode.join("test-각丁ー再见");
721 check!(fs::create_dir(&unicode));
722 assert!(unicode.exists());
723 assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
727 fn copy_file_does_not_exist() {
728 let from = Path::new("test/nonexistent-bogus-path");
729 let to = Path::new("test/other-bogus-path");
731 match fs::copy(&from, &to) {
734 assert!(!from.exists());
735 assert!(!to.exists());
741 fn copy_src_does_not_exist() {
742 let tmpdir = tmpdir();
743 let from = Path::new("test/nonexistent-bogus-path");
744 let to = tmpdir.join("out.txt");
745 check!(check!(File::create(&to)).write(b"hello"));
746 assert!(fs::copy(&from, &to).is_err());
747 assert!(!from.exists());
748 let mut v = Vec::new();
749 check!(check!(File::open(&to)).read_to_end(&mut v));
750 assert_eq!(v, b"hello");
755 let tmpdir = tmpdir();
756 let input = tmpdir.join("in.txt");
757 let out = tmpdir.join("out.txt");
759 check!(check!(File::create(&input)).write(b"hello"));
760 check!(fs::copy(&input, &out));
761 let mut v = Vec::new();
762 check!(check!(File::open(&out)).read_to_end(&mut v));
763 assert_eq!(v, b"hello");
765 assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions());
769 fn copy_file_dst_dir() {
770 let tmpdir = tmpdir();
771 let out = tmpdir.join("out");
773 check!(File::create(&out));
774 match fs::copy(&*out, tmpdir.path()) {
781 fn copy_file_dst_exists() {
782 let tmpdir = tmpdir();
783 let input = tmpdir.join("in");
784 let output = tmpdir.join("out");
786 check!(check!(File::create(&input)).write("foo".as_bytes()));
787 check!(check!(File::create(&output)).write("bar".as_bytes()));
788 check!(fs::copy(&input, &output));
790 let mut v = Vec::new();
791 check!(check!(File::open(&output)).read_to_end(&mut v));
792 assert_eq!(v, b"foo".to_vec());
796 fn copy_file_src_dir() {
797 let tmpdir = tmpdir();
798 let out = tmpdir.join("out");
800 match fs::copy(tmpdir.path(), &out) {
804 assert!(!out.exists());
808 fn copy_file_preserves_perm_bits() {
809 let tmpdir = tmpdir();
810 let input = tmpdir.join("in.txt");
811 let out = tmpdir.join("out.txt");
813 let attr = check!(check!(File::create(&input)).metadata());
814 let mut p = attr.permissions();
815 p.set_readonly(true);
816 check!(fs::set_permissions(&input, p));
817 check!(fs::copy(&input, &out));
818 assert!(check!(out.metadata()).permissions().readonly());
819 check!(fs::set_permissions(&input, attr.permissions()));
820 check!(fs::set_permissions(&out, attr.permissions()));
825 fn copy_file_preserves_streams() {
827 check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes()));
828 assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0);
829 assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0);
830 let mut v = Vec::new();
831 check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v));
832 assert_eq!(v, b"carrot".to_vec());
836 fn copy_file_returns_metadata_len() {
838 let in_path = tmp.join("in.txt");
839 let out_path = tmp.join("out.txt");
840 check!(check!(File::create(&in_path)).write(b"lettuce"));
842 check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot"));
843 let copied_len = check!(fs::copy(&in_path, &out_path));
844 assert_eq!(check!(out_path.metadata()).len(), copied_len);
848 fn copy_file_follows_dst_symlink() {
850 if !got_symlink_permission(&tmp) {
854 let in_path = tmp.join("in.txt");
855 let out_path = tmp.join("out.txt");
856 let out_path_symlink = tmp.join("out_symlink.txt");
858 check!(fs::write(&in_path, "foo"));
859 check!(fs::write(&out_path, "bar"));
860 check!(symlink_file(&out_path, &out_path_symlink));
862 check!(fs::copy(&in_path, &out_path_symlink));
864 assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink());
865 assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec());
866 assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec());
871 let tmpdir = tmpdir();
872 if !got_symlink_permission(&tmpdir) {
876 let input = tmpdir.join("in.txt");
877 let out = tmpdir.join("out.txt");
879 check!(check!(File::create(&input)).write("foobar".as_bytes()));
880 check!(symlink_file(&input, &out));
881 assert!(check!(out.symlink_metadata()).file_type().is_symlink());
882 assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
883 let mut v = Vec::new();
884 check!(check!(File::open(&out)).read_to_end(&mut v));
885 assert_eq!(v, b"foobar".to_vec());
889 fn symlink_noexist() {
890 // Symlinks can point to things that don't exist
891 let tmpdir = tmpdir();
892 if !got_symlink_permission(&tmpdir) {
896 // Use a relative path for testing. Symlinks get normalized by Windows,
897 // so we might not get the same path back for absolute paths
898 check!(symlink_file(&"foo", &tmpdir.join("bar")));
899 assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo");
906 assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
908 assert_eq!(check!(fs::read_link(r"C:\Users\Default User")), Path::new(r"C:\Users\Default"));
909 // junction with special permissions
910 // Since not all localized windows versions contain the folder "Documents and Settings" in english,
911 // we will briefly check, if it exists and otherwise skip the test. Except during CI we will always execute the test.
912 if Path::new(r"C:\Documents and Settings\").exists() || env::var_os("CI").is_some() {
914 check!(fs::read_link(r"C:\Documents and Settings\")),
915 Path::new(r"C:\Users")
919 let tmpdir = tmpdir();
920 let link = tmpdir.join("link");
921 if !got_symlink_permission(&tmpdir) {
924 check!(symlink_file(&"foo", &link));
925 assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
929 fn readlink_not_symlink() {
930 let tmpdir = tmpdir();
931 match fs::read_link(tmpdir.path()) {
932 Ok(..) => panic!("wanted a failure"),
939 let tmpdir = tmpdir();
940 let input = tmpdir.join("in.txt");
941 let out = tmpdir.join("out.txt");
943 check!(check!(File::create(&input)).write("foobar".as_bytes()));
944 check!(fs::hard_link(&input, &out));
945 assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
946 assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len());
947 let mut v = Vec::new();
948 check!(check!(File::open(&out)).read_to_end(&mut v));
949 assert_eq!(v, b"foobar".to_vec());
951 // can't link to yourself
952 match fs::hard_link(&input, &input) {
953 Ok(..) => panic!("wanted a failure"),
956 // can't link to something that doesn't exist
957 match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
958 Ok(..) => panic!("wanted a failure"),
965 let tmpdir = tmpdir();
966 let file = tmpdir.join("in.txt");
968 check!(File::create(&file));
969 let attr = check!(fs::metadata(&file));
970 assert!(!attr.permissions().readonly());
971 let mut p = attr.permissions();
972 p.set_readonly(true);
973 check!(fs::set_permissions(&file, p.clone()));
974 let attr = check!(fs::metadata(&file));
975 assert!(attr.permissions().readonly());
977 match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
978 Ok(..) => panic!("wanted an error"),
982 p.set_readonly(false);
983 check!(fs::set_permissions(&file, p));
988 let tmpdir = tmpdir();
989 let path = tmpdir.join("in.txt");
991 let file = check!(File::create(&path));
992 let attr = check!(fs::metadata(&path));
993 assert!(!attr.permissions().readonly());
994 let mut p = attr.permissions();
995 p.set_readonly(true);
996 check!(file.set_permissions(p.clone()));
997 let attr = check!(fs::metadata(&path));
998 assert!(attr.permissions().readonly());
1000 p.set_readonly(false);
1001 check!(file.set_permissions(p));
1005 fn sync_doesnt_kill_anything() {
1006 let tmpdir = tmpdir();
1007 let path = tmpdir.join("in.txt");
1009 let mut file = check!(File::create(&path));
1010 check!(file.sync_all());
1011 check!(file.sync_data());
1012 check!(file.write(b"foo"));
1013 check!(file.sync_all());
1014 check!(file.sync_data());
1018 fn truncate_works() {
1019 let tmpdir = tmpdir();
1020 let path = tmpdir.join("in.txt");
1022 let mut file = check!(File::create(&path));
1023 check!(file.write(b"foo"));
1024 check!(file.sync_all());
1026 // Do some simple things with truncation
1027 assert_eq!(check!(file.metadata()).len(), 3);
1028 check!(file.set_len(10));
1029 assert_eq!(check!(file.metadata()).len(), 10);
1030 check!(file.write(b"bar"));
1031 check!(file.sync_all());
1032 assert_eq!(check!(file.metadata()).len(), 10);
1034 let mut v = Vec::new();
1035 check!(check!(File::open(&path)).read_to_end(&mut v));
1036 assert_eq!(v, b"foobar\0\0\0\0".to_vec());
1038 // Truncate to a smaller length, don't seek, and then write something.
1039 // Ensure that the intermediate zeroes are all filled in (we have `seek`ed
1040 // past the end of the file).
1041 check!(file.set_len(2));
1042 assert_eq!(check!(file.metadata()).len(), 2);
1043 check!(file.write(b"wut"));
1044 check!(file.sync_all());
1045 assert_eq!(check!(file.metadata()).len(), 9);
1046 let mut v = Vec::new();
1047 check!(check!(File::open(&path)).read_to_end(&mut v));
1048 assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
1053 use crate::fs::OpenOptions as OO;
1054 fn c<T: Clone>(t: &T) -> T {
1058 let tmpdir = tmpdir();
1060 let mut r = OO::new();
1062 let mut w = OO::new();
1064 let mut rw = OO::new();
1065 rw.read(true).write(true);
1066 let mut a = OO::new();
1068 let mut ra = OO::new();
1069 ra.read(true).append(true);
1072 let invalid_options = 87; // ERROR_INVALID_PARAMETER
1073 #[cfg(all(unix, not(target_os = "vxworks")))]
1074 let invalid_options = "Invalid argument";
1075 #[cfg(target_os = "vxworks")]
1076 let invalid_options = "invalid argument";
1078 // Test various combinations of creation modes and access modes.
1081 // creation mode | read | write | read-write | append | read-append |
1082 // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
1083 // not set (open existing) | X | X | X | X | X |
1084 // create | | X | X | X | X |
1085 // truncate | | X | X | | |
1086 // create and truncate | | X | X | | |
1087 // create_new | | X | X | X | X |
1089 // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
1092 check!(c(&w).create_new(true).open(&tmpdir.join("a")));
1093 check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a")));
1094 check!(c(&w).truncate(true).open(&tmpdir.join("a")));
1095 check!(c(&w).create(true).open(&tmpdir.join("a")));
1096 check!(c(&w).open(&tmpdir.join("a")));
1099 error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
1100 error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
1101 error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
1102 error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
1103 check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
1106 check!(c(&rw).create_new(true).open(&tmpdir.join("c")));
1107 check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c")));
1108 check!(c(&rw).truncate(true).open(&tmpdir.join("c")));
1109 check!(c(&rw).create(true).open(&tmpdir.join("c")));
1110 check!(c(&rw).open(&tmpdir.join("c")));
1113 check!(c(&a).create_new(true).open(&tmpdir.join("d")));
1114 error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
1115 error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
1116 check!(c(&a).create(true).open(&tmpdir.join("d")));
1117 check!(c(&a).open(&tmpdir.join("d")));
1120 check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
1121 error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
1122 error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
1123 check!(c(&ra).create(true).open(&tmpdir.join("e")));
1124 check!(c(&ra).open(&tmpdir.join("e")));
1126 // Test opening a file without setting an access mode
1127 let mut blank = OO::new();
1128 error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
1131 check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
1133 // Test write fails for read-only
1134 check!(r.open(&tmpdir.join("h")));
1136 let mut f = check!(r.open(&tmpdir.join("h")));
1137 assert!(f.write("wut".as_bytes()).is_err());
1140 // Test write overwrites
1142 let mut f = check!(c(&w).open(&tmpdir.join("h")));
1143 check!(f.write("baz".as_bytes()));
1146 let mut f = check!(c(&r).open(&tmpdir.join("h")));
1147 let mut b = vec![0; 6];
1148 check!(f.read(&mut b));
1149 assert_eq!(b, "bazbar".as_bytes());
1152 // Test truncate works
1154 let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1155 check!(f.write("foo".as_bytes()));
1157 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1159 // Test append works
1160 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1162 let mut f = check!(c(&a).open(&tmpdir.join("h")));
1163 check!(f.write("bar".as_bytes()));
1165 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1167 // Test .append(true) equals .write(true).append(true)
1169 let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1170 check!(f.write("baz".as_bytes()));
1172 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
1176 fn _assert_send_sync() {
1177 fn _assert_send_sync<T: Send + Sync>() {}
1178 _assert_send_sync::<OpenOptions>();
1183 let mut bytes = [0; 1024];
1184 StdRng::from_entropy().fill_bytes(&mut bytes);
1186 let tmpdir = tmpdir();
1188 check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
1189 let mut v = Vec::new();
1190 check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
1191 assert!(v == &bytes[..]);
1195 fn write_then_read() {
1196 let mut bytes = [0; 1024];
1197 StdRng::from_entropy().fill_bytes(&mut bytes);
1199 let tmpdir = tmpdir();
1201 check!(fs::write(&tmpdir.join("test"), &bytes[..]));
1202 let v = check!(fs::read(&tmpdir.join("test")));
1203 assert!(v == &bytes[..]);
1205 check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
1207 fs::read_to_string(&tmpdir.join("not-utf8")),
1208 "stream did not contain valid UTF-8"
1212 check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
1213 let string = check!(fs::read_to_string(&tmpdir.join("utf8")));
1214 assert_eq!(string, s);
1218 fn file_try_clone() {
1219 let tmpdir = tmpdir();
1222 check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
1223 let mut f2 = check!(f1.try_clone());
1225 check!(f1.write_all(b"hello world"));
1226 check!(f1.seek(SeekFrom::Start(2)));
1228 let mut buf = vec![];
1229 check!(f2.read_to_end(&mut buf));
1230 assert_eq!(buf, b"llo world");
1233 check!(f1.write_all(b"!"));
1237 #[cfg(not(windows))]
1238 fn unlink_readonly() {
1239 let tmpdir = tmpdir();
1240 let path = tmpdir.join("file");
1241 check!(File::create(&path));
1242 let mut perm = check!(fs::metadata(&path)).permissions();
1243 perm.set_readonly(true);
1244 check!(fs::set_permissions(&path, perm));
1245 check!(fs::remove_file(&path));
1249 fn mkdir_trailing_slash() {
1250 let tmpdir = tmpdir();
1251 let path = tmpdir.join("file");
1252 check!(fs::create_dir_all(&path.join("a/")));
1256 fn canonicalize_works_simple() {
1257 let tmpdir = tmpdir();
1258 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1259 let file = tmpdir.join("test");
1260 File::create(&file).unwrap();
1261 assert_eq!(fs::canonicalize(&file).unwrap(), file);
1265 fn realpath_works() {
1266 let tmpdir = tmpdir();
1267 if !got_symlink_permission(&tmpdir) {
1271 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1272 let file = tmpdir.join("test");
1273 let dir = tmpdir.join("test2");
1274 let link = dir.join("link");
1275 let linkdir = tmpdir.join("test3");
1277 File::create(&file).unwrap();
1278 fs::create_dir(&dir).unwrap();
1279 symlink_file(&file, &link).unwrap();
1280 symlink_dir(&dir, &linkdir).unwrap();
1282 assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
1284 assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir);
1285 assert_eq!(fs::canonicalize(&file).unwrap(), file);
1286 assert_eq!(fs::canonicalize(&link).unwrap(), file);
1287 assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir);
1288 assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file);
1292 fn realpath_works_tricky() {
1293 let tmpdir = tmpdir();
1294 if !got_symlink_permission(&tmpdir) {
1298 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1299 let a = tmpdir.join("a");
1300 let b = a.join("b");
1301 let c = b.join("c");
1302 let d = a.join("d");
1303 let e = d.join("e");
1304 let f = a.join("f");
1306 fs::create_dir_all(&b).unwrap();
1307 fs::create_dir_all(&d).unwrap();
1308 File::create(&f).unwrap();
1309 if cfg!(not(windows)) {
1310 symlink_file("../d/e", &c).unwrap();
1311 symlink_file("../f", &e).unwrap();
1314 symlink_file(r"..\d\e", &c).unwrap();
1315 symlink_file(r"..\f", &e).unwrap();
1318 assert_eq!(fs::canonicalize(&c).unwrap(), f);
1319 assert_eq!(fs::canonicalize(&e).unwrap(), f);
1323 fn dir_entry_methods() {
1324 let tmpdir = tmpdir();
1326 fs::create_dir_all(&tmpdir.join("a")).unwrap();
1327 File::create(&tmpdir.join("b")).unwrap();
1329 for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
1330 let fname = file.file_name();
1331 match fname.to_str() {
1333 assert!(file.file_type().unwrap().is_dir());
1334 assert!(file.metadata().unwrap().is_dir());
1337 assert!(file.file_type().unwrap().is_file());
1338 assert!(file.metadata().unwrap().is_file());
1340 f => panic!("unknown file name: {f:?}"),
1346 fn dir_entry_debug() {
1347 let tmpdir = tmpdir();
1348 File::create(&tmpdir.join("b")).unwrap();
1349 let mut read_dir = tmpdir.path().read_dir().unwrap();
1350 let dir_entry = read_dir.next().unwrap().unwrap();
1351 let actual = format!("{dir_entry:?}");
1352 let expected = format!("DirEntry({:?})", dir_entry.0.path());
1353 assert_eq!(actual, expected);
1357 fn read_dir_not_found() {
1358 let res = fs::read_dir("/path/that/does/not/exist");
1359 assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1363 fn file_open_not_found() {
1364 let res = File::open("/path/that/does/not/exist");
1365 assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1369 fn create_dir_all_with_junctions() {
1370 let tmpdir = tmpdir();
1371 let target = tmpdir.join("target");
1373 let junction = tmpdir.join("junction");
1374 let b = junction.join("a/b");
1376 let link = tmpdir.join("link");
1377 let d = link.join("c/d");
1379 fs::create_dir(&target).unwrap();
1381 check!(symlink_junction(&target, &junction));
1382 check!(fs::create_dir_all(&b));
1383 // the junction itself is not a directory, but `is_dir()` on a Path
1385 assert!(junction.is_dir());
1386 assert!(b.exists());
1388 if !got_symlink_permission(&tmpdir) {
1391 check!(symlink_dir(&target, &link));
1392 check!(fs::create_dir_all(&d));
1393 assert!(link.is_dir());
1394 assert!(d.exists());
1398 fn metadata_access_times() {
1399 let tmpdir = tmpdir();
1401 let b = tmpdir.join("b");
1402 File::create(&b).unwrap();
1404 let a = check!(fs::metadata(&tmpdir.path()));
1405 let b = check!(fs::metadata(&b));
1407 assert_eq!(check!(a.accessed()), check!(a.accessed()));
1408 assert_eq!(check!(a.modified()), check!(a.modified()));
1409 assert_eq!(check!(b.accessed()), check!(b.modified()));
1411 if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1412 check!(a.created());
1413 check!(b.created());
1416 if cfg!(target_os = "linux") {
1417 // Not always available
1418 match (a.created(), b.created()) {
1419 (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1421 if e1.kind() == ErrorKind::Uncategorized
1422 && e2.kind() == ErrorKind::Uncategorized
1423 || e1.kind() == ErrorKind::Unsupported
1424 && e2.kind() == ErrorKind::Unsupported => {}
1426 panic!("creation time must be always supported or not supported: {a:?} {b:?}")
1432 /// Test creating hard links to symlinks.
1434 fn symlink_hard_link() {
1435 let tmpdir = tmpdir();
1436 if !got_symlink_permission(&tmpdir) {
1439 if !able_to_not_follow_symlinks_while_hard_linking() {
1443 // Create "file", a file.
1444 check!(fs::File::create(tmpdir.join("file")));
1446 // Create "symlink", a symlink to "file".
1447 check!(symlink_file("file", tmpdir.join("symlink")));
1449 // Create "hard_link", a hard link to "symlink".
1450 check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
1452 // "hard_link" should appear as a symlink.
1453 assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1455 // We should be able to open "file" via any of the above names.
1456 let _ = check!(fs::File::open(tmpdir.join("file")));
1457 assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1458 let _ = check!(fs::File::open(tmpdir.join("symlink")));
1459 let _ = check!(fs::File::open(tmpdir.join("hard_link")));
1461 // Rename "file" to "file.renamed".
1462 check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
1464 // Now, the symlink and the hard link should be dangling.
1465 assert!(fs::File::open(tmpdir.join("file")).is_err());
1466 let _ = check!(fs::File::open(tmpdir.join("file.renamed")));
1467 assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1468 assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1470 // The symlink and the hard link should both still point to "file".
1471 assert!(fs::read_link(tmpdir.join("file")).is_err());
1472 assert!(fs::read_link(tmpdir.join("file.renamed")).is_err());
1473 assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file"));
1474 assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file"));
1476 // Remove "file.renamed".
1477 check!(fs::remove_file(tmpdir.join("file.renamed")));
1479 // Now, we can't open the file by any name.
1480 assert!(fs::File::open(tmpdir.join("file")).is_err());
1481 assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1482 assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1483 assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1485 // "hard_link" should still appear as a symlink.
1486 assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1489 /// Ensure `fs::create_dir` works on Windows with longer paths.
1492 fn create_dir_long_paths() {
1493 use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1494 const PATH_LEN: usize = 247;
1496 let tmpdir = tmpdir();
1497 let mut path = tmpdir.path().to_path_buf();
1499 let mut path = path.into_os_string();
1501 let utf16_len = path.encode_wide().count();
1502 if utf16_len >= PATH_LEN {
1503 // Skip the test in the unlikely event the local user has a long temp directory path.
1504 // This should not affect CI.
1507 // Increase the length of the path.
1508 path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1510 // This should succeed.
1511 fs::create_dir(&path).unwrap();
1513 // This will fail if the path isn't converted to verbatim.
1515 fs::create_dir(&path).unwrap();
1517 // #90940: Ensure an empty path returns the "Not Found" error.
1518 let path = Path::new("");
1519 assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound);
1522 /// Ensure ReadDir works on large directories.
1523 /// Regression test for https://github.com/rust-lang/rust/issues/93384.
1525 fn read_large_dir() {
1526 let tmpdir = tmpdir();
1528 let count = 32 * 1024;
1530 check!(fs::File::create(tmpdir.join(&i.to_string())));
1533 for entry in fs::read_dir(tmpdir.path()).unwrap() {
1538 /// Test the fallback for getting the metadata of files like hiberfil.sys that
1539 /// Windows holds a special lock on, preventing normal means of querying
1540 /// metadata. See #96980.
1542 /// Note this fails in CI because `hiberfil.sys` does not actually exist there.
1543 /// Therefore it's marked as ignored.
1548 let hiberfil = Path::new(r"C:\hiberfil.sys");
1549 assert_eq!(true, hiberfil.try_exists().unwrap());
1550 fs::symlink_metadata(hiberfil).unwrap();
1551 fs::metadata(hiberfil).unwrap();
1552 assert_eq!(true, hiberfil.exists());