1 use crate::io::prelude::*;
3 use crate::fs::{self, File, OpenOptions};
4 use crate::io::{ErrorKind, SeekFrom};
8 use crate::sys_common::io::test::{tmpdir, TempDir};
10 use crate::time::{Duration, Instant};
12 use rand::{rngs::StdRng, RngCore, SeedableRng};
15 use crate::os::unix::fs::symlink as symlink_dir;
17 use crate::os::unix::fs::symlink as symlink_file;
19 use crate::os::unix::fs::symlink as symlink_junction;
21 use crate::os::windows::fs::{symlink_dir, symlink_file};
23 use crate::sys::fs::symlink_junction;
24 #[cfg(target_os = "macos")]
25 use crate::sys::weak::weak;
26 #[cfg(target_os = "macos")]
27 use libc::{c_char, c_int};
33 Err(e) => panic!("{} failed with: {}", stringify!($e), e),
40 ($e:expr, $s:expr) => {
42 Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
44 assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s)
52 ($e:expr, $s:expr) => {
53 error_contains!($e, $s)
57 macro_rules! error_contains {
58 ($e:expr, $s:expr) => {
60 Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
62 assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s)
68 // Several test fail on windows if the user does not have permission to
69 // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
70 // disabling these test on Windows, use this function to test whether we
71 // have permission, and return otherwise. This way, we still don't run these
72 // tests most of the time, but at least we do if the user has the right
74 pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
78 let link = tmpdir.join("some_hopefully_unique_link_name");
80 match symlink_file(r"nonexisting_target", link) {
81 // ERROR_PRIVILEGE_NOT_HELD = 1314
82 Err(ref err) if err.raw_os_error() == Some(1314) => false,
83 Ok(_) | Err(_) => true,
87 #[cfg(target_os = "macos")]
88 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
89 weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
90 linkat.get().is_some()
93 #[cfg(not(target_os = "macos"))]
94 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
99 fn file_test_io_smoke_test() {
100 let message = "it's alright. have a good time";
101 let tmpdir = tmpdir();
102 let filename = &tmpdir.join("file_rt_io_file_test.txt");
104 let mut write_stream = check!(File::create(filename));
105 check!(write_stream.write(message.as_bytes()));
108 let mut read_stream = check!(File::open(filename));
109 let mut read_buf = [0; 1028];
110 let read_str = match check!(read_stream.read(&mut read_buf)) {
111 0 => panic!("shouldn't happen"),
112 n => str::from_utf8(&read_buf[..n]).unwrap().to_string(),
114 assert_eq!(read_str, message);
116 check!(fs::remove_file(filename));
120 fn invalid_path_raises() {
121 let tmpdir = tmpdir();
122 let filename = &tmpdir.join("file_that_does_not_exist.txt");
123 let result = File::open(filename);
125 #[cfg(all(unix, not(target_os = "vxworks")))]
126 error!(result, "No such file or directory");
127 #[cfg(target_os = "vxworks")]
128 error!(result, "no such file or directory");
130 error!(result, 2); // ERROR_FILE_NOT_FOUND
134 fn file_test_iounlinking_invalid_path_should_raise_condition() {
135 let tmpdir = tmpdir();
136 let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
138 let result = fs::remove_file(filename);
140 #[cfg(all(unix, not(target_os = "vxworks")))]
141 error!(result, "No such file or directory");
142 #[cfg(target_os = "vxworks")]
143 error!(result, "no such file or directory");
145 error!(result, 2); // ERROR_FILE_NOT_FOUND
149 fn file_test_io_non_positional_read() {
150 let message: &str = "ten-four";
151 let mut read_mem = [0; 8];
152 let tmpdir = tmpdir();
153 let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
155 let mut rw_stream = check!(File::create(filename));
156 check!(rw_stream.write(message.as_bytes()));
159 let mut read_stream = check!(File::open(filename));
161 let read_buf = &mut read_mem[0..4];
162 check!(read_stream.read(read_buf));
165 let read_buf = &mut read_mem[4..8];
166 check!(read_stream.read(read_buf));
169 check!(fs::remove_file(filename));
170 let read_str = str::from_utf8(&read_mem).unwrap();
171 assert_eq!(read_str, message);
175 fn file_test_io_seek_and_tell_smoke_test() {
176 let message = "ten-four";
177 let mut read_mem = [0; 4];
178 let set_cursor = 4 as u64;
179 let tell_pos_pre_read;
180 let tell_pos_post_read;
181 let tmpdir = tmpdir();
182 let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
184 let mut rw_stream = check!(File::create(filename));
185 check!(rw_stream.write(message.as_bytes()));
188 let mut read_stream = check!(File::open(filename));
189 check!(read_stream.seek(SeekFrom::Start(set_cursor)));
190 tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0)));
191 check!(read_stream.read(&mut read_mem));
192 tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0)));
194 check!(fs::remove_file(filename));
195 let read_str = str::from_utf8(&read_mem).unwrap();
196 assert_eq!(read_str, &message[4..8]);
197 assert_eq!(tell_pos_pre_read, set_cursor);
198 assert_eq!(tell_pos_post_read, message.len() as u64);
202 fn file_test_io_seek_and_write() {
203 let initial_msg = "food-is-yummy";
204 let overwrite_msg = "-the-bar!!";
205 let final_msg = "foo-the-bar!!";
207 let mut read_mem = [0; 13];
208 let tmpdir = tmpdir();
209 let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
211 let mut rw_stream = check!(File::create(filename));
212 check!(rw_stream.write(initial_msg.as_bytes()));
213 check!(rw_stream.seek(SeekFrom::Start(seek_idx)));
214 check!(rw_stream.write(overwrite_msg.as_bytes()));
217 let mut read_stream = check!(File::open(filename));
218 check!(read_stream.read(&mut read_mem));
220 check!(fs::remove_file(filename));
221 let read_str = str::from_utf8(&read_mem).unwrap();
222 assert!(read_str == final_msg);
226 fn file_test_io_seek_shakedown() {
228 let initial_msg = "qwer-asdf-zxcv";
229 let chunk_one: &str = "qwer";
230 let chunk_two: &str = "asdf";
231 let chunk_three: &str = "zxcv";
232 let mut read_mem = [0; 4];
233 let tmpdir = tmpdir();
234 let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
236 let mut rw_stream = check!(File::create(filename));
237 check!(rw_stream.write(initial_msg.as_bytes()));
240 let mut read_stream = check!(File::open(filename));
242 check!(read_stream.seek(SeekFrom::End(-4)));
243 check!(read_stream.read(&mut read_mem));
244 assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three);
246 check!(read_stream.seek(SeekFrom::Current(-9)));
247 check!(read_stream.read(&mut read_mem));
248 assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two);
250 check!(read_stream.seek(SeekFrom::Start(0)));
251 check!(read_stream.read(&mut read_mem));
252 assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one);
254 check!(fs::remove_file(filename));
258 fn file_test_io_eof() {
259 let tmpdir = tmpdir();
260 let filename = tmpdir.join("file_rt_io_file_test_eof.txt");
261 let mut buf = [0; 256];
263 let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
264 let mut rw = check!(oo.open(&filename));
265 assert_eq!(check!(rw.read(&mut buf)), 0);
266 assert_eq!(check!(rw.read(&mut buf)), 0);
268 check!(fs::remove_file(&filename));
273 fn file_test_io_read_write_at() {
274 use crate::os::unix::fs::FileExt;
276 let tmpdir = tmpdir();
277 let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
278 let mut buf = [0; 256];
280 let write2 = "qwer-";
281 let write3 = "-zxcv";
282 let content = "qwer-asdf-zxcv";
284 let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
285 let mut rw = check!(oo.open(&filename));
286 assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len());
287 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
288 assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len());
289 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
290 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
291 assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
292 assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0"));
293 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
294 assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
295 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
296 assert_eq!(check!(rw.read(&mut buf)), write1.len());
297 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
298 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
299 assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
300 assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
301 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
302 assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len());
303 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
306 let mut read = check!(File::open(&filename));
307 assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
308 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
309 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0);
310 assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
311 assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
312 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
313 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9);
314 assert_eq!(check!(read.read(&mut buf)), write3.len());
315 assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
316 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
317 assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
318 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
319 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
320 assert_eq!(check!(read.read_at(&mut buf, 14)), 0);
321 assert_eq!(check!(read.read_at(&mut buf, 15)), 0);
322 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
324 check!(fs::remove_file(&filename));
329 fn set_get_unix_permissions() {
330 use crate::os::unix::fs::PermissionsExt;
332 let tmpdir = tmpdir();
333 let filename = &tmpdir.join("set_get_unix_permissions");
334 check!(fs::create_dir(filename));
337 check!(fs::set_permissions(filename, fs::Permissions::from_mode(0)));
338 let metadata0 = check!(fs::metadata(filename));
339 assert_eq!(mask & metadata0.permissions().mode(), 0);
341 check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777)));
342 let metadata1 = check!(fs::metadata(filename));
343 #[cfg(all(unix, not(target_os = "vxworks")))]
344 assert_eq!(mask & metadata1.permissions().mode(), 0o1777);
345 #[cfg(target_os = "vxworks")]
346 assert_eq!(mask & metadata1.permissions().mode(), 0o0777);
351 fn file_test_io_seek_read_write() {
352 use crate::os::windows::fs::FileExt;
354 let tmpdir = tmpdir();
355 let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt");
356 let mut buf = [0; 256];
358 let write2 = "qwer-";
359 let write3 = "-zxcv";
360 let content = "qwer-asdf-zxcv";
362 let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
363 let mut rw = check!(oo.open(&filename));
364 assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len());
365 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
366 assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len());
367 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
368 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
369 assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0);
370 assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
371 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
372 assert_eq!(check!(rw.read(&mut buf)), write1.len());
373 assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
374 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
375 assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len());
376 assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
377 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
378 assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len());
379 assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14);
382 let mut read = check!(File::open(&filename));
383 assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
384 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
385 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
386 assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
387 assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
388 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
389 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
390 assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
391 assert_eq!(check!(read.read(&mut buf)), write3.len());
392 assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
393 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
394 assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
395 assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
396 assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
397 assert_eq!(check!(read.seek_read(&mut buf, 14)), 0);
398 assert_eq!(check!(read.seek_read(&mut buf, 15)), 0);
400 check!(fs::remove_file(&filename));
404 fn file_test_stat_is_correct_on_is_file() {
405 let tmpdir = tmpdir();
406 let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
408 let mut opts = OpenOptions::new();
409 let mut fs = check!(opts.read(true).write(true).create(true).open(filename));
411 fs.write(msg.as_bytes()).unwrap();
413 let fstat_res = check!(fs.metadata());
414 assert!(fstat_res.is_file());
416 let stat_res_fn = check!(fs::metadata(filename));
417 assert!(stat_res_fn.is_file());
418 let stat_res_meth = check!(filename.metadata());
419 assert!(stat_res_meth.is_file());
420 check!(fs::remove_file(filename));
424 fn file_test_stat_is_correct_on_is_dir() {
425 let tmpdir = tmpdir();
426 let filename = &tmpdir.join("file_stat_correct_on_is_dir");
427 check!(fs::create_dir(filename));
428 let stat_res_fn = check!(fs::metadata(filename));
429 assert!(stat_res_fn.is_dir());
430 let stat_res_meth = check!(filename.metadata());
431 assert!(stat_res_meth.is_dir());
432 check!(fs::remove_dir(filename));
436 fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
437 let tmpdir = tmpdir();
438 let dir = &tmpdir.join("fileinfo_false_on_dir");
439 check!(fs::create_dir(dir));
440 assert!(!dir.is_file());
441 check!(fs::remove_dir(dir));
445 fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
446 let tmpdir = tmpdir();
447 let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
448 check!(check!(File::create(file)).write(b"foo"));
449 assert!(file.exists());
450 check!(fs::remove_file(file));
451 assert!(!file.exists());
455 fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
456 let tmpdir = tmpdir();
457 let dir = &tmpdir.join("before_and_after_dir");
458 assert!(!dir.exists());
459 check!(fs::create_dir(dir));
460 assert!(dir.exists());
461 assert!(dir.is_dir());
462 check!(fs::remove_dir(dir));
463 assert!(!dir.exists());
467 fn file_test_directoryinfo_readdir() {
468 let tmpdir = tmpdir();
469 let dir = &tmpdir.join("di_readdir");
470 check!(fs::create_dir(dir));
473 let f = dir.join(&format!("{}.txt", n));
474 let mut w = check!(File::create(&f));
475 let msg_str = format!("{}{}", prefix, n.to_string());
476 let msg = msg_str.as_bytes();
477 check!(w.write(msg));
479 let files = check!(fs::read_dir(dir));
480 let mut mem = [0; 4];
482 let f = f.unwrap().path();
484 let n = f.file_stem().unwrap();
485 check!(check!(File::open(&f)).read(&mut mem));
486 let read_str = str::from_utf8(&mem).unwrap();
487 let expected = format!("{}{}", prefix, n.to_str().unwrap());
488 assert_eq!(expected, read_str);
490 check!(fs::remove_file(&f));
492 check!(fs::remove_dir(dir));
496 fn file_create_new_already_exists_error() {
497 let tmpdir = tmpdir();
498 let file = &tmpdir.join("file_create_new_error_exists");
499 check!(fs::File::create(file));
500 let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err();
501 assert_eq!(e.kind(), ErrorKind::AlreadyExists);
505 fn mkdir_path_already_exists_error() {
506 let tmpdir = tmpdir();
507 let dir = &tmpdir.join("mkdir_error_twice");
508 check!(fs::create_dir(dir));
509 let e = fs::create_dir(dir).unwrap_err();
510 assert_eq!(e.kind(), ErrorKind::AlreadyExists);
514 fn recursive_mkdir() {
515 let tmpdir = tmpdir();
516 let dir = tmpdir.join("d1/d2");
517 check!(fs::create_dir_all(&dir));
518 assert!(dir.is_dir())
522 fn recursive_mkdir_failure() {
523 let tmpdir = tmpdir();
524 let dir = tmpdir.join("d1");
525 let file = dir.join("f1");
527 check!(fs::create_dir_all(&dir));
528 check!(File::create(&file));
530 let result = fs::create_dir_all(&file);
532 assert!(result.is_err());
536 fn concurrent_recursive_mkdir() {
539 let mut dir = dir.join("a");
543 let mut join = vec![];
545 let dir = dir.clone();
546 join.push(thread::spawn(move || {
547 check!(fs::create_dir_all(&dir));
551 // No `Display` on result of `join()`
552 join.drain(..).map(|join| join.join().unwrap()).count();
557 fn recursive_mkdir_slash() {
558 check!(fs::create_dir_all(Path::new("/")));
562 fn recursive_mkdir_dot() {
563 check!(fs::create_dir_all(Path::new(".")));
567 fn recursive_mkdir_empty() {
568 check!(fs::create_dir_all(Path::new("")));
572 fn recursive_rmdir() {
573 let tmpdir = tmpdir();
574 let d1 = tmpdir.join("d1");
575 let dt = d1.join("t");
576 let dtt = dt.join("t");
577 let d2 = tmpdir.join("d2");
578 let canary = d2.join("do_not_delete");
579 check!(fs::create_dir_all(&dtt));
580 check!(fs::create_dir_all(&d2));
581 check!(check!(File::create(&canary)).write(b"foo"));
582 check!(symlink_junction(&d2, &dt.join("d2")));
583 let _ = symlink_file(&canary, &d1.join("canary"));
584 check!(fs::remove_dir_all(&d1));
586 assert!(!d1.is_dir());
587 assert!(canary.exists());
591 fn recursive_rmdir_of_symlink() {
592 // test we do not recursively delete a symlink but only dirs.
593 let tmpdir = tmpdir();
594 let link = tmpdir.join("d1");
595 let dir = tmpdir.join("d2");
596 let canary = dir.join("do_not_delete");
597 check!(fs::create_dir_all(&dir));
598 check!(check!(File::create(&canary)).write(b"foo"));
599 check!(symlink_junction(&dir, &link));
600 check!(fs::remove_dir_all(&link));
602 assert!(!link.is_dir());
603 assert!(canary.exists());
607 fn recursive_rmdir_of_file_fails() {
608 // test we do not delete a directly specified file.
609 let tmpdir = tmpdir();
610 let canary = tmpdir.join("do_not_delete");
611 check!(check!(File::create(&canary)).write(b"foo"));
612 let result = fs::remove_dir_all(&canary);
614 error!(result, "Not a directory");
616 error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid.
617 assert!(result.is_err());
618 assert!(canary.exists());
622 // only Windows makes a distinction between file and directory symlinks.
624 fn recursive_rmdir_of_file_symlink() {
625 let tmpdir = tmpdir();
626 if !got_symlink_permission(&tmpdir) {
630 let f1 = tmpdir.join("f1");
631 let f2 = tmpdir.join("f2");
632 check!(check!(File::create(&f1)).write(b"foo"));
633 check!(symlink_file(&f1, &f2));
634 match fs::remove_dir_all(&f2) {
635 Ok(..) => panic!("wanted a failure"),
641 #[ignore] // takes too much time
642 fn recursive_rmdir_toctou() {
643 // Test for time-of-check to time-of-use issues.
646 // The attacker wants to get directory contents deleted, to which he does not have access.
647 // He has a way to get a privileged Rust binary call `std::fs::remove_dir_all()` on a
648 // directory he controls, e.g. in his home directory.
650 // The POC sets up the `attack_dest/attack_file` which the attacker wants to have deleted.
651 // The attacker repeatedly creates a directory and replaces it with a symlink from
652 // `victim_del` to `attack_dest` while the victim code calls `std::fs::remove_dir_all()`
653 // on `victim_del`. After a few seconds the attack has succeeded and
654 // `attack_dest/attack_file` is deleted.
655 let tmpdir = tmpdir();
656 let victim_del_path = tmpdir.join("victim_del");
657 let victim_del_path_clone = victim_del_path.clone();
660 let attack_dest_dir = tmpdir.join("attack_dest");
661 let attack_dest_dir = attack_dest_dir.as_path();
662 fs::create_dir(attack_dest_dir).unwrap();
663 let attack_dest_file = tmpdir.join("attack_dest/attack_file");
664 File::create(&attack_dest_file).unwrap();
666 let drop_canary_arc = Arc::new(());
667 let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
669 eprintln!("x: {:?}", &victim_del_path);
671 // victim just continuously removes `victim_del`
672 thread::spawn(move || {
673 while drop_canary_weak.upgrade().is_some() {
674 let _ = fs::remove_dir_all(&victim_del_path_clone);
678 // attacker (could of course be in a separate process)
679 let start_time = Instant::now();
680 while Instant::now().duration_since(start_time) < Duration::from_secs(1000) {
681 if !attack_dest_file.exists() {
683 "Victim deleted symlinked file outside of victim_del. Attack succeeded in {:?}.",
684 Instant::now().duration_since(start_time)
687 let _ = fs::create_dir(&victim_del_path);
688 let _ = fs::remove_dir(&victim_del_path);
689 let _ = symlink_dir(attack_dest_dir, &victim_del_path);
694 fn unicode_path_is_dir() {
695 assert!(Path::new(".").is_dir());
696 assert!(!Path::new("test/stdtest/fs.rs").is_dir());
698 let tmpdir = tmpdir();
700 let mut dirpath = tmpdir.path().to_path_buf();
701 dirpath.push("test-가一ー你好");
702 check!(fs::create_dir(&dirpath));
703 assert!(dirpath.is_dir());
705 let mut filepath = dirpath;
706 filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs");
707 check!(File::create(&filepath)); // ignore return; touch only
708 assert!(!filepath.is_dir());
709 assert!(filepath.exists());
713 fn unicode_path_exists() {
714 assert!(Path::new(".").exists());
715 assert!(!Path::new("test/nonexistent-bogus-path").exists());
717 let tmpdir = tmpdir();
718 let unicode = tmpdir.path();
719 let unicode = unicode.join("test-각丁ー再见");
720 check!(fs::create_dir(&unicode));
721 assert!(unicode.exists());
722 assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
726 fn copy_file_does_not_exist() {
727 let from = Path::new("test/nonexistent-bogus-path");
728 let to = Path::new("test/other-bogus-path");
730 match fs::copy(&from, &to) {
733 assert!(!from.exists());
734 assert!(!to.exists());
740 fn copy_src_does_not_exist() {
741 let tmpdir = tmpdir();
742 let from = Path::new("test/nonexistent-bogus-path");
743 let to = tmpdir.join("out.txt");
744 check!(check!(File::create(&to)).write(b"hello"));
745 assert!(fs::copy(&from, &to).is_err());
746 assert!(!from.exists());
747 let mut v = Vec::new();
748 check!(check!(File::open(&to)).read_to_end(&mut v));
749 assert_eq!(v, b"hello");
754 let tmpdir = tmpdir();
755 let input = tmpdir.join("in.txt");
756 let out = tmpdir.join("out.txt");
758 check!(check!(File::create(&input)).write(b"hello"));
759 check!(fs::copy(&input, &out));
760 let mut v = Vec::new();
761 check!(check!(File::open(&out)).read_to_end(&mut v));
762 assert_eq!(v, b"hello");
764 assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions());
768 fn copy_file_dst_dir() {
769 let tmpdir = tmpdir();
770 let out = tmpdir.join("out");
772 check!(File::create(&out));
773 match fs::copy(&*out, tmpdir.path()) {
780 fn copy_file_dst_exists() {
781 let tmpdir = tmpdir();
782 let input = tmpdir.join("in");
783 let output = tmpdir.join("out");
785 check!(check!(File::create(&input)).write("foo".as_bytes()));
786 check!(check!(File::create(&output)).write("bar".as_bytes()));
787 check!(fs::copy(&input, &output));
789 let mut v = Vec::new();
790 check!(check!(File::open(&output)).read_to_end(&mut v));
791 assert_eq!(v, b"foo".to_vec());
795 fn copy_file_src_dir() {
796 let tmpdir = tmpdir();
797 let out = tmpdir.join("out");
799 match fs::copy(tmpdir.path(), &out) {
803 assert!(!out.exists());
807 fn copy_file_preserves_perm_bits() {
808 let tmpdir = tmpdir();
809 let input = tmpdir.join("in.txt");
810 let out = tmpdir.join("out.txt");
812 let attr = check!(check!(File::create(&input)).metadata());
813 let mut p = attr.permissions();
814 p.set_readonly(true);
815 check!(fs::set_permissions(&input, p));
816 check!(fs::copy(&input, &out));
817 assert!(check!(out.metadata()).permissions().readonly());
818 check!(fs::set_permissions(&input, attr.permissions()));
819 check!(fs::set_permissions(&out, attr.permissions()));
824 fn copy_file_preserves_streams() {
826 check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes()));
827 assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0);
828 assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0);
829 let mut v = Vec::new();
830 check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v));
831 assert_eq!(v, b"carrot".to_vec());
835 fn copy_file_returns_metadata_len() {
837 let in_path = tmp.join("in.txt");
838 let out_path = tmp.join("out.txt");
839 check!(check!(File::create(&in_path)).write(b"lettuce"));
841 check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot"));
842 let copied_len = check!(fs::copy(&in_path, &out_path));
843 assert_eq!(check!(out_path.metadata()).len(), copied_len);
847 fn copy_file_follows_dst_symlink() {
849 if !got_symlink_permission(&tmp) {
853 let in_path = tmp.join("in.txt");
854 let out_path = tmp.join("out.txt");
855 let out_path_symlink = tmp.join("out_symlink.txt");
857 check!(fs::write(&in_path, "foo"));
858 check!(fs::write(&out_path, "bar"));
859 check!(symlink_file(&out_path, &out_path_symlink));
861 check!(fs::copy(&in_path, &out_path_symlink));
863 assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink());
864 assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec());
865 assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec());
870 let tmpdir = tmpdir();
871 if !got_symlink_permission(&tmpdir) {
875 let input = tmpdir.join("in.txt");
876 let out = tmpdir.join("out.txt");
878 check!(check!(File::create(&input)).write("foobar".as_bytes()));
879 check!(symlink_file(&input, &out));
880 assert!(check!(out.symlink_metadata()).file_type().is_symlink());
881 assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
882 let mut v = Vec::new();
883 check!(check!(File::open(&out)).read_to_end(&mut v));
884 assert_eq!(v, b"foobar".to_vec());
888 fn symlink_noexist() {
889 // Symlinks can point to things that don't exist
890 let tmpdir = tmpdir();
891 if !got_symlink_permission(&tmpdir) {
895 // Use a relative path for testing. Symlinks get normalized by Windows,
896 // so we might not get the same path back for absolute paths
897 check!(symlink_file(&"foo", &tmpdir.join("bar")));
898 assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo");
905 assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
907 assert_eq!(check!(fs::read_link(r"C:\Users\Default User")), Path::new(r"C:\Users\Default"));
908 // junction with special permissions
909 assert_eq!(check!(fs::read_link(r"C:\Documents and Settings\")), Path::new(r"C:\Users"));
911 let tmpdir = tmpdir();
912 let link = tmpdir.join("link");
913 if !got_symlink_permission(&tmpdir) {
916 check!(symlink_file(&"foo", &link));
917 assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
921 fn readlink_not_symlink() {
922 let tmpdir = tmpdir();
923 match fs::read_link(tmpdir.path()) {
924 Ok(..) => panic!("wanted a failure"),
931 let tmpdir = tmpdir();
932 let input = tmpdir.join("in.txt");
933 let out = tmpdir.join("out.txt");
935 check!(check!(File::create(&input)).write("foobar".as_bytes()));
936 check!(fs::hard_link(&input, &out));
937 assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
938 assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len());
939 let mut v = Vec::new();
940 check!(check!(File::open(&out)).read_to_end(&mut v));
941 assert_eq!(v, b"foobar".to_vec());
943 // can't link to yourself
944 match fs::hard_link(&input, &input) {
945 Ok(..) => panic!("wanted a failure"),
948 // can't link to something that doesn't exist
949 match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
950 Ok(..) => panic!("wanted a failure"),
957 let tmpdir = tmpdir();
958 let file = tmpdir.join("in.txt");
960 check!(File::create(&file));
961 let attr = check!(fs::metadata(&file));
962 assert!(!attr.permissions().readonly());
963 let mut p = attr.permissions();
964 p.set_readonly(true);
965 check!(fs::set_permissions(&file, p.clone()));
966 let attr = check!(fs::metadata(&file));
967 assert!(attr.permissions().readonly());
969 match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
970 Ok(..) => panic!("wanted an error"),
974 p.set_readonly(false);
975 check!(fs::set_permissions(&file, p));
980 let tmpdir = tmpdir();
981 let path = tmpdir.join("in.txt");
983 let file = check!(File::create(&path));
984 let attr = check!(fs::metadata(&path));
985 assert!(!attr.permissions().readonly());
986 let mut p = attr.permissions();
987 p.set_readonly(true);
988 check!(file.set_permissions(p.clone()));
989 let attr = check!(fs::metadata(&path));
990 assert!(attr.permissions().readonly());
992 p.set_readonly(false);
993 check!(file.set_permissions(p));
997 fn sync_doesnt_kill_anything() {
998 let tmpdir = tmpdir();
999 let path = tmpdir.join("in.txt");
1001 let mut file = check!(File::create(&path));
1002 check!(file.sync_all());
1003 check!(file.sync_data());
1004 check!(file.write(b"foo"));
1005 check!(file.sync_all());
1006 check!(file.sync_data());
1010 fn truncate_works() {
1011 let tmpdir = tmpdir();
1012 let path = tmpdir.join("in.txt");
1014 let mut file = check!(File::create(&path));
1015 check!(file.write(b"foo"));
1016 check!(file.sync_all());
1018 // Do some simple things with truncation
1019 assert_eq!(check!(file.metadata()).len(), 3);
1020 check!(file.set_len(10));
1021 assert_eq!(check!(file.metadata()).len(), 10);
1022 check!(file.write(b"bar"));
1023 check!(file.sync_all());
1024 assert_eq!(check!(file.metadata()).len(), 10);
1026 let mut v = Vec::new();
1027 check!(check!(File::open(&path)).read_to_end(&mut v));
1028 assert_eq!(v, b"foobar\0\0\0\0".to_vec());
1030 // Truncate to a smaller length, don't seek, and then write something.
1031 // Ensure that the intermediate zeroes are all filled in (we have `seek`ed
1032 // past the end of the file).
1033 check!(file.set_len(2));
1034 assert_eq!(check!(file.metadata()).len(), 2);
1035 check!(file.write(b"wut"));
1036 check!(file.sync_all());
1037 assert_eq!(check!(file.metadata()).len(), 9);
1038 let mut v = Vec::new();
1039 check!(check!(File::open(&path)).read_to_end(&mut v));
1040 assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
1045 use crate::fs::OpenOptions as OO;
1046 fn c<T: Clone>(t: &T) -> T {
1050 let tmpdir = tmpdir();
1052 let mut r = OO::new();
1054 let mut w = OO::new();
1056 let mut rw = OO::new();
1057 rw.read(true).write(true);
1058 let mut a = OO::new();
1060 let mut ra = OO::new();
1061 ra.read(true).append(true);
1064 let invalid_options = 87; // ERROR_INVALID_PARAMETER
1065 #[cfg(all(unix, not(target_os = "vxworks")))]
1066 let invalid_options = "Invalid argument";
1067 #[cfg(target_os = "vxworks")]
1068 let invalid_options = "invalid argument";
1070 // Test various combinations of creation modes and access modes.
1073 // creation mode | read | write | read-write | append | read-append |
1074 // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
1075 // not set (open existing) | X | X | X | X | X |
1076 // create | | X | X | X | X |
1077 // truncate | | X | X | | |
1078 // create and truncate | | X | X | | |
1079 // create_new | | X | X | X | X |
1081 // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
1084 check!(c(&w).create_new(true).open(&tmpdir.join("a")));
1085 check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a")));
1086 check!(c(&w).truncate(true).open(&tmpdir.join("a")));
1087 check!(c(&w).create(true).open(&tmpdir.join("a")));
1088 check!(c(&w).open(&tmpdir.join("a")));
1091 error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
1092 error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
1093 error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
1094 error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
1095 check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
1098 check!(c(&rw).create_new(true).open(&tmpdir.join("c")));
1099 check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c")));
1100 check!(c(&rw).truncate(true).open(&tmpdir.join("c")));
1101 check!(c(&rw).create(true).open(&tmpdir.join("c")));
1102 check!(c(&rw).open(&tmpdir.join("c")));
1105 check!(c(&a).create_new(true).open(&tmpdir.join("d")));
1106 error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
1107 error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
1108 check!(c(&a).create(true).open(&tmpdir.join("d")));
1109 check!(c(&a).open(&tmpdir.join("d")));
1112 check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
1113 error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
1114 error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
1115 check!(c(&ra).create(true).open(&tmpdir.join("e")));
1116 check!(c(&ra).open(&tmpdir.join("e")));
1118 // Test opening a file without setting an access mode
1119 let mut blank = OO::new();
1120 error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
1123 check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
1125 // Test write fails for read-only
1126 check!(r.open(&tmpdir.join("h")));
1128 let mut f = check!(r.open(&tmpdir.join("h")));
1129 assert!(f.write("wut".as_bytes()).is_err());
1132 // Test write overwrites
1134 let mut f = check!(c(&w).open(&tmpdir.join("h")));
1135 check!(f.write("baz".as_bytes()));
1138 let mut f = check!(c(&r).open(&tmpdir.join("h")));
1139 let mut b = vec![0; 6];
1140 check!(f.read(&mut b));
1141 assert_eq!(b, "bazbar".as_bytes());
1144 // Test truncate works
1146 let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1147 check!(f.write("foo".as_bytes()));
1149 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1151 // Test append works
1152 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1154 let mut f = check!(c(&a).open(&tmpdir.join("h")));
1155 check!(f.write("bar".as_bytes()));
1157 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1159 // Test .append(true) equals .write(true).append(true)
1161 let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1162 check!(f.write("baz".as_bytes()));
1164 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
1168 fn _assert_send_sync() {
1169 fn _assert_send_sync<T: Send + Sync>() {}
1170 _assert_send_sync::<OpenOptions>();
1175 let mut bytes = [0; 1024];
1176 StdRng::from_entropy().fill_bytes(&mut bytes);
1178 let tmpdir = tmpdir();
1180 check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
1181 let mut v = Vec::new();
1182 check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
1183 assert!(v == &bytes[..]);
1187 fn write_then_read() {
1188 let mut bytes = [0; 1024];
1189 StdRng::from_entropy().fill_bytes(&mut bytes);
1191 let tmpdir = tmpdir();
1193 check!(fs::write(&tmpdir.join("test"), &bytes[..]));
1194 let v = check!(fs::read(&tmpdir.join("test")));
1195 assert!(v == &bytes[..]);
1197 check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
1199 fs::read_to_string(&tmpdir.join("not-utf8")),
1200 "stream did not contain valid UTF-8"
1204 check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
1205 let string = check!(fs::read_to_string(&tmpdir.join("utf8")));
1206 assert_eq!(string, s);
1210 fn file_try_clone() {
1211 let tmpdir = tmpdir();
1214 check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
1215 let mut f2 = check!(f1.try_clone());
1217 check!(f1.write_all(b"hello world"));
1218 check!(f1.seek(SeekFrom::Start(2)));
1220 let mut buf = vec![];
1221 check!(f2.read_to_end(&mut buf));
1222 assert_eq!(buf, b"llo world");
1225 check!(f1.write_all(b"!"));
1229 #[cfg(not(windows))]
1230 fn unlink_readonly() {
1231 let tmpdir = tmpdir();
1232 let path = tmpdir.join("file");
1233 check!(File::create(&path));
1234 let mut perm = check!(fs::metadata(&path)).permissions();
1235 perm.set_readonly(true);
1236 check!(fs::set_permissions(&path, perm));
1237 check!(fs::remove_file(&path));
1241 fn mkdir_trailing_slash() {
1242 let tmpdir = tmpdir();
1243 let path = tmpdir.join("file");
1244 check!(fs::create_dir_all(&path.join("a/")));
1248 fn canonicalize_works_simple() {
1249 let tmpdir = tmpdir();
1250 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1251 let file = tmpdir.join("test");
1252 File::create(&file).unwrap();
1253 assert_eq!(fs::canonicalize(&file).unwrap(), file);
1257 fn realpath_works() {
1258 let tmpdir = tmpdir();
1259 if !got_symlink_permission(&tmpdir) {
1263 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1264 let file = tmpdir.join("test");
1265 let dir = tmpdir.join("test2");
1266 let link = dir.join("link");
1267 let linkdir = tmpdir.join("test3");
1269 File::create(&file).unwrap();
1270 fs::create_dir(&dir).unwrap();
1271 symlink_file(&file, &link).unwrap();
1272 symlink_dir(&dir, &linkdir).unwrap();
1274 assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
1276 assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir);
1277 assert_eq!(fs::canonicalize(&file).unwrap(), file);
1278 assert_eq!(fs::canonicalize(&link).unwrap(), file);
1279 assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir);
1280 assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file);
1284 fn realpath_works_tricky() {
1285 let tmpdir = tmpdir();
1286 if !got_symlink_permission(&tmpdir) {
1290 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1291 let a = tmpdir.join("a");
1292 let b = a.join("b");
1293 let c = b.join("c");
1294 let d = a.join("d");
1295 let e = d.join("e");
1296 let f = a.join("f");
1298 fs::create_dir_all(&b).unwrap();
1299 fs::create_dir_all(&d).unwrap();
1300 File::create(&f).unwrap();
1301 if cfg!(not(windows)) {
1302 symlink_file("../d/e", &c).unwrap();
1303 symlink_file("../f", &e).unwrap();
1306 symlink_file(r"..\d\e", &c).unwrap();
1307 symlink_file(r"..\f", &e).unwrap();
1310 assert_eq!(fs::canonicalize(&c).unwrap(), f);
1311 assert_eq!(fs::canonicalize(&e).unwrap(), f);
1315 fn dir_entry_methods() {
1316 let tmpdir = tmpdir();
1318 fs::create_dir_all(&tmpdir.join("a")).unwrap();
1319 File::create(&tmpdir.join("b")).unwrap();
1321 for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
1322 let fname = file.file_name();
1323 match fname.to_str() {
1325 assert!(file.file_type().unwrap().is_dir());
1326 assert!(file.metadata().unwrap().is_dir());
1329 assert!(file.file_type().unwrap().is_file());
1330 assert!(file.metadata().unwrap().is_file());
1332 f => panic!("unknown file name: {:?}", f),
1338 fn dir_entry_debug() {
1339 let tmpdir = tmpdir();
1340 File::create(&tmpdir.join("b")).unwrap();
1341 let mut read_dir = tmpdir.path().read_dir().unwrap();
1342 let dir_entry = read_dir.next().unwrap().unwrap();
1343 let actual = format!("{:?}", dir_entry);
1344 let expected = format!("DirEntry({:?})", dir_entry.0.path());
1345 assert_eq!(actual, expected);
1349 fn read_dir_not_found() {
1350 let res = fs::read_dir("/path/that/does/not/exist");
1351 assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1355 fn create_dir_all_with_junctions() {
1356 let tmpdir = tmpdir();
1357 let target = tmpdir.join("target");
1359 let junction = tmpdir.join("junction");
1360 let b = junction.join("a/b");
1362 let link = tmpdir.join("link");
1363 let d = link.join("c/d");
1365 fs::create_dir(&target).unwrap();
1367 check!(symlink_junction(&target, &junction));
1368 check!(fs::create_dir_all(&b));
1369 // the junction itself is not a directory, but `is_dir()` on a Path
1371 assert!(junction.is_dir());
1372 assert!(b.exists());
1374 if !got_symlink_permission(&tmpdir) {
1377 check!(symlink_dir(&target, &link));
1378 check!(fs::create_dir_all(&d));
1379 assert!(link.is_dir());
1380 assert!(d.exists());
1384 fn metadata_access_times() {
1385 let tmpdir = tmpdir();
1387 let b = tmpdir.join("b");
1388 File::create(&b).unwrap();
1390 let a = check!(fs::metadata(&tmpdir.path()));
1391 let b = check!(fs::metadata(&b));
1393 assert_eq!(check!(a.accessed()), check!(a.accessed()));
1394 assert_eq!(check!(a.modified()), check!(a.modified()));
1395 assert_eq!(check!(b.accessed()), check!(b.modified()));
1397 if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1398 check!(a.created());
1399 check!(b.created());
1402 if cfg!(target_os = "linux") {
1403 // Not always available
1404 match (a.created(), b.created()) {
1405 (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1407 if e1.kind() == ErrorKind::Uncategorized
1408 && e2.kind() == ErrorKind::Uncategorized
1409 || e1.kind() == ErrorKind::Unsupported
1410 && e2.kind() == ErrorKind::Unsupported => {}
1412 panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,)
1418 /// Test creating hard links to symlinks.
1420 fn symlink_hard_link() {
1421 let tmpdir = tmpdir();
1422 if !got_symlink_permission(&tmpdir) {
1425 if !able_to_not_follow_symlinks_while_hard_linking() {
1429 // Create "file", a file.
1430 check!(fs::File::create(tmpdir.join("file")));
1432 // Create "symlink", a symlink to "file".
1433 check!(symlink_file("file", tmpdir.join("symlink")));
1435 // Create "hard_link", a hard link to "symlink".
1436 check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
1438 // "hard_link" should appear as a symlink.
1439 assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1441 // We should be able to open "file" via any of the above names.
1442 let _ = check!(fs::File::open(tmpdir.join("file")));
1443 assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1444 let _ = check!(fs::File::open(tmpdir.join("symlink")));
1445 let _ = check!(fs::File::open(tmpdir.join("hard_link")));
1447 // Rename "file" to "file.renamed".
1448 check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
1450 // Now, the symlink and the hard link should be dangling.
1451 assert!(fs::File::open(tmpdir.join("file")).is_err());
1452 let _ = check!(fs::File::open(tmpdir.join("file.renamed")));
1453 assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1454 assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1456 // The symlink and the hard link should both still point to "file".
1457 assert!(fs::read_link(tmpdir.join("file")).is_err());
1458 assert!(fs::read_link(tmpdir.join("file.renamed")).is_err());
1459 assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file"));
1460 assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file"));
1462 // Remove "file.renamed".
1463 check!(fs::remove_file(tmpdir.join("file.renamed")));
1465 // Now, we can't open the file by any name.
1466 assert!(fs::File::open(tmpdir.join("file")).is_err());
1467 assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1468 assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1469 assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1471 // "hard_link" should still appear as a symlink.
1472 assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1475 /// Ensure `fs::create_dir` works on Windows with longer paths.
1478 fn create_dir_long_paths() {
1479 use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1480 const PATH_LEN: usize = 247;
1482 let tmpdir = tmpdir();
1483 let mut path = tmpdir.path().to_path_buf();
1485 let mut path = path.into_os_string();
1487 let utf16_len = path.encode_wide().count();
1488 if utf16_len >= PATH_LEN {
1489 // Skip the test in the unlikely event the local user has a long temp directory path.
1490 // This should not affect CI.
1493 // Increase the length of the path.
1494 path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1496 // This should succeed.
1497 fs::create_dir(&path).unwrap();
1499 // This will fail if the path isn't converted to verbatim.
1501 fs::create_dir(&path).unwrap();
1503 // #90940: Ensure an empty path returns the "Not Found" error.
1504 let path = Path::new("");
1505 assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound);
1508 /// Ensure ReadDir works on large directories.
1509 /// Regression test for https://github.com/rust-lang/rust/issues/93384.
1511 fn read_large_dir() {
1512 let tmpdir = tmpdir();
1514 let count = 32 * 1024;
1516 check!(fs::File::create(tmpdir.join(&i.to_string())));
1519 for entry in fs::read_dir(tmpdir.path()).unwrap() {