]> git.lizzy.rs Git - rust.git/blob - library/std/src/fs/tests.rs
Merge from rustc
[rust.git] / library / std / src / fs / tests.rs
1 use crate::io::prelude::*;
2
3 use crate::env;
4 use crate::fs::{self, File, OpenOptions};
5 use crate::io::{ErrorKind, SeekFrom};
6 use crate::path::Path;
7 use crate::str;
8 use crate::sync::Arc;
9 use crate::sys_common::io::test::{tmpdir, TempDir};
10 use crate::thread;
11 use crate::time::{Duration, Instant};
12
13 use rand::{rngs::StdRng, RngCore, SeedableRng};
14
15 #[cfg(unix)]
16 use crate::os::unix::fs::symlink as symlink_dir;
17 #[cfg(unix)]
18 use crate::os::unix::fs::symlink as symlink_file;
19 #[cfg(unix)]
20 use crate::os::unix::fs::symlink as symlink_junction;
21 #[cfg(windows)]
22 use crate::os::windows::fs::{symlink_dir, symlink_file};
23 #[cfg(windows)]
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};
29
30 macro_rules! check {
31     ($e:expr) => {
32         match $e {
33             Ok(t) => t,
34             Err(e) => panic!("{} failed with: {e}", stringify!($e)),
35         }
36     };
37 }
38
39 #[cfg(windows)]
40 macro_rules! error {
41     ($e:expr, $s:expr) => {
42         match $e {
43             Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
44             Err(ref err) => {
45                 assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s)
46             }
47         }
48     };
49 }
50
51 #[cfg(unix)]
52 macro_rules! error {
53     ($e:expr, $s:expr) => {
54         error_contains!($e, $s)
55     };
56 }
57
58 macro_rules! error_contains {
59     ($e:expr, $s:expr) => {
60         match $e {
61             Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
62             Err(ref err) => {
63                 assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s)
64             }
65         }
66     };
67 }
68
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
74 // permissions.
75 pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
76     if cfg!(unix) {
77         return true;
78     }
79     let link = tmpdir.join("some_hopefully_unique_link_name");
80
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,
85     }
86 }
87
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()
92 }
93
94 #[cfg(not(target_os = "macos"))]
95 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
96     return true;
97 }
98
99 #[test]
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");
104     {
105         let mut write_stream = check!(File::create(filename));
106         check!(write_stream.write(message.as_bytes()));
107     }
108     {
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(),
114         };
115         assert_eq!(read_str, message);
116     }
117     check!(fs::remove_file(filename));
118 }
119
120 #[test]
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);
125
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");
130     #[cfg(windows)]
131     error!(result, 2); // ERROR_FILE_NOT_FOUND
132 }
133
134 #[test]
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");
138
139     let result = fs::remove_file(filename);
140
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");
145     #[cfg(windows)]
146     error!(result, 2); // ERROR_FILE_NOT_FOUND
147 }
148
149 #[test]
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");
155     {
156         let mut rw_stream = check!(File::create(filename));
157         check!(rw_stream.write(message.as_bytes()));
158     }
159     {
160         let mut read_stream = check!(File::open(filename));
161         {
162             let read_buf = &mut read_mem[0..4];
163             check!(read_stream.read(read_buf));
164         }
165         {
166             let read_buf = &mut read_mem[4..8];
167             check!(read_stream.read(read_buf));
168         }
169     }
170     check!(fs::remove_file(filename));
171     let read_str = str::from_utf8(&read_mem).unwrap();
172     assert_eq!(read_str, message);
173 }
174
175 #[test]
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");
184     {
185         let mut rw_stream = check!(File::create(filename));
186         check!(rw_stream.write(message.as_bytes()));
187     }
188     {
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)));
194     }
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);
200 }
201
202 #[test]
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!!";
207     let seek_idx = 3;
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");
211     {
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()));
216     }
217     {
218         let mut read_stream = check!(File::open(filename));
219         check!(read_stream.read(&mut read_mem));
220     }
221     check!(fs::remove_file(filename));
222     let read_str = str::from_utf8(&read_mem).unwrap();
223     assert!(read_str == final_msg);
224 }
225
226 #[test]
227 fn file_test_io_seek_shakedown() {
228     //                   01234567890123
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");
236     {
237         let mut rw_stream = check!(File::create(filename));
238         check!(rw_stream.write(initial_msg.as_bytes()));
239     }
240     {
241         let mut read_stream = check!(File::open(filename));
242
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);
246
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);
250
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);
254     }
255     check!(fs::remove_file(filename));
256 }
257
258 #[test]
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];
263     {
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);
268     }
269     check!(fs::remove_file(&filename));
270 }
271
272 #[test]
273 #[cfg(unix)]
274 fn file_test_io_read_write_at() {
275     use crate::os::unix::fs::FileExt;
276
277     let tmpdir = tmpdir();
278     let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
279     let mut buf = [0; 256];
280     let write1 = "asdf";
281     let write2 = "qwer-";
282     let write3 = "-zxcv";
283     let content = "qwer-asdf-zxcv";
284     {
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);
305     }
306     {
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);
324     }
325     check!(fs::remove_file(&filename));
326 }
327
328 #[test]
329 #[cfg(unix)]
330 fn set_get_unix_permissions() {
331     use crate::os::unix::fs::PermissionsExt;
332
333     let tmpdir = tmpdir();
334     let filename = &tmpdir.join("set_get_unix_permissions");
335     check!(fs::create_dir(filename));
336     let mask = 0o7777;
337
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);
341
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);
348 }
349
350 #[test]
351 #[cfg(windows)]
352 fn file_test_io_seek_read_write() {
353     use crate::os::windows::fs::FileExt;
354
355     let tmpdir = tmpdir();
356     let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt");
357     let mut buf = [0; 256];
358     let write1 = "asdf";
359     let write2 = "qwer-";
360     let write3 = "-zxcv";
361     let content = "qwer-asdf-zxcv";
362     {
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);
381     }
382     {
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);
400     }
401     check!(fs::remove_file(&filename));
402 }
403
404 #[test]
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");
408     {
409         let mut opts = OpenOptions::new();
410         let mut fs = check!(opts.read(true).write(true).create(true).open(filename));
411         let msg = "hw";
412         fs.write(msg.as_bytes()).unwrap();
413
414         let fstat_res = check!(fs.metadata());
415         assert!(fstat_res.is_file());
416     }
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));
422 }
423
424 #[test]
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));
434 }
435
436 #[test]
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));
443 }
444
445 #[test]
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());
453 }
454
455 #[test]
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());
465 }
466
467 #[test]
468 fn file_test_directoryinfo_readdir() {
469     let tmpdir = tmpdir();
470     let dir = &tmpdir.join("di_readdir");
471     check!(fs::create_dir(dir));
472     let prefix = "foo";
473     for n in 0..3 {
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));
479     }
480     let files = check!(fs::read_dir(dir));
481     let mut mem = [0; 4];
482     for f in files {
483         let f = f.unwrap().path();
484         {
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);
490         }
491         check!(fs::remove_file(&f));
492     }
493     check!(fs::remove_dir(dir));
494 }
495
496 #[test]
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);
503 }
504
505 #[test]
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);
512 }
513
514 #[test]
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())
520 }
521
522 #[test]
523 fn recursive_mkdir_failure() {
524     let tmpdir = tmpdir();
525     let dir = tmpdir.join("d1");
526     let file = dir.join("f1");
527
528     check!(fs::create_dir_all(&dir));
529     check!(File::create(&file));
530
531     let result = fs::create_dir_all(&file);
532
533     assert!(result.is_err());
534 }
535
536 #[test]
537 fn concurrent_recursive_mkdir() {
538     for _ in 0..100 {
539         let dir = tmpdir();
540         let mut dir = dir.join("a");
541         for _ in 0..40 {
542             dir = dir.join("a");
543         }
544         let mut join = vec![];
545         for _ in 0..8 {
546             let dir = dir.clone();
547             join.push(thread::spawn(move || {
548                 check!(fs::create_dir_all(&dir));
549             }))
550         }
551
552         // No `Display` on result of `join()`
553         join.drain(..).map(|join| join.join().unwrap()).count();
554     }
555 }
556
557 #[test]
558 fn recursive_mkdir_slash() {
559     check!(fs::create_dir_all(Path::new("/")));
560 }
561
562 #[test]
563 fn recursive_mkdir_dot() {
564     check!(fs::create_dir_all(Path::new(".")));
565 }
566
567 #[test]
568 fn recursive_mkdir_empty() {
569     check!(fs::create_dir_all(Path::new("")));
570 }
571
572 #[test]
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));
586
587     assert!(!d1.is_dir());
588     assert!(canary.exists());
589 }
590
591 #[test]
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));
602
603     assert!(!link.is_dir());
604     assert!(canary.exists());
605 }
606
607 #[test]
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);
614     #[cfg(unix)]
615     error!(result, "Not a directory");
616     #[cfg(windows)]
617     error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid.
618     assert!(result.is_err());
619     assert!(canary.exists());
620 }
621
622 #[test]
623 // only Windows makes a distinction between file and directory symlinks.
624 #[cfg(windows)]
625 fn recursive_rmdir_of_file_symlink() {
626     let tmpdir = tmpdir();
627     if !got_symlink_permission(&tmpdir) {
628         return;
629     };
630
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"),
637         Err(..) => {}
638     }
639 }
640
641 #[test]
642 #[ignore] // takes too much time
643 fn recursive_rmdir_toctou() {
644     // Test for time-of-check to time-of-use issues.
645     //
646     // Scenario:
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.
650     //
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();
659
660     // setup dest
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();
666
667     let drop_canary_arc = Arc::new(());
668     let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
669
670     eprintln!("x: {:?}", &victim_del_path);
671
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);
676         }
677     });
678
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() {
683             panic!(
684                 "Victim deleted symlinked file outside of victim_del. Attack succeeded in {:?}.",
685                 Instant::now().duration_since(start_time)
686             );
687         }
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);
691     }
692 }
693
694 #[test]
695 fn unicode_path_is_dir() {
696     assert!(Path::new(".").is_dir());
697     assert!(!Path::new("test/stdtest/fs.rs").is_dir());
698
699     let tmpdir = tmpdir();
700
701     let mut dirpath = tmpdir.path().to_path_buf();
702     dirpath.push("test-가一ー你好");
703     check!(fs::create_dir(&dirpath));
704     assert!(dirpath.is_dir());
705
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());
711 }
712
713 #[test]
714 fn unicode_path_exists() {
715     assert!(Path::new(".").exists());
716     assert!(!Path::new("test/nonexistent-bogus-path").exists());
717
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());
724 }
725
726 #[test]
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");
730
731     match fs::copy(&from, &to) {
732         Ok(..) => panic!(),
733         Err(..) => {
734             assert!(!from.exists());
735             assert!(!to.exists());
736         }
737     }
738 }
739
740 #[test]
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");
751 }
752
753 #[test]
754 fn copy_file_ok() {
755     let tmpdir = tmpdir();
756     let input = tmpdir.join("in.txt");
757     let out = tmpdir.join("out.txt");
758
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");
764
765     assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions());
766 }
767
768 #[test]
769 fn copy_file_dst_dir() {
770     let tmpdir = tmpdir();
771     let out = tmpdir.join("out");
772
773     check!(File::create(&out));
774     match fs::copy(&*out, tmpdir.path()) {
775         Ok(..) => panic!(),
776         Err(..) => {}
777     }
778 }
779
780 #[test]
781 fn copy_file_dst_exists() {
782     let tmpdir = tmpdir();
783     let input = tmpdir.join("in");
784     let output = tmpdir.join("out");
785
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));
789
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());
793 }
794
795 #[test]
796 fn copy_file_src_dir() {
797     let tmpdir = tmpdir();
798     let out = tmpdir.join("out");
799
800     match fs::copy(tmpdir.path(), &out) {
801         Ok(..) => panic!(),
802         Err(..) => {}
803     }
804     assert!(!out.exists());
805 }
806
807 #[test]
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");
812
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()));
821 }
822
823 #[test]
824 #[cfg(windows)]
825 fn copy_file_preserves_streams() {
826     let tmp = tmpdir();
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());
833 }
834
835 #[test]
836 fn copy_file_returns_metadata_len() {
837     let tmp = tmpdir();
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"));
841     #[cfg(windows)]
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);
845 }
846
847 #[test]
848 fn copy_file_follows_dst_symlink() {
849     let tmp = tmpdir();
850     if !got_symlink_permission(&tmp) {
851         return;
852     };
853
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");
857
858     check!(fs::write(&in_path, "foo"));
859     check!(fs::write(&out_path, "bar"));
860     check!(symlink_file(&out_path, &out_path_symlink));
861
862     check!(fs::copy(&in_path, &out_path_symlink));
863
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());
867 }
868
869 #[test]
870 fn symlinks_work() {
871     let tmpdir = tmpdir();
872     if !got_symlink_permission(&tmpdir) {
873         return;
874     };
875
876     let input = tmpdir.join("in.txt");
877     let out = tmpdir.join("out.txt");
878
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());
886 }
887
888 #[test]
889 fn symlink_noexist() {
890     // Symlinks can point to things that don't exist
891     let tmpdir = tmpdir();
892     if !got_symlink_permission(&tmpdir) {
893         return;
894     };
895
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");
900 }
901
902 #[test]
903 fn read_link() {
904     if cfg!(windows) {
905         // directory symlink
906         assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
907         // junction
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() {
913             assert_eq!(
914                 check!(fs::read_link(r"C:\Documents and Settings\")),
915                 Path::new(r"C:\Users")
916             );
917         }
918     }
919     let tmpdir = tmpdir();
920     let link = tmpdir.join("link");
921     if !got_symlink_permission(&tmpdir) {
922         return;
923     };
924     check!(symlink_file(&"foo", &link));
925     assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
926 }
927
928 #[test]
929 fn readlink_not_symlink() {
930     let tmpdir = tmpdir();
931     match fs::read_link(tmpdir.path()) {
932         Ok(..) => panic!("wanted a failure"),
933         Err(..) => {}
934     }
935 }
936
937 #[test]
938 fn links_work() {
939     let tmpdir = tmpdir();
940     let input = tmpdir.join("in.txt");
941     let out = tmpdir.join("out.txt");
942
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());
950
951     // can't link to yourself
952     match fs::hard_link(&input, &input) {
953         Ok(..) => panic!("wanted a failure"),
954         Err(..) => {}
955     }
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"),
959         Err(..) => {}
960     }
961 }
962
963 #[test]
964 fn chmod_works() {
965     let tmpdir = tmpdir();
966     let file = tmpdir.join("in.txt");
967
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());
976
977     match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
978         Ok(..) => panic!("wanted an error"),
979         Err(..) => {}
980     }
981
982     p.set_readonly(false);
983     check!(fs::set_permissions(&file, p));
984 }
985
986 #[test]
987 fn fchmod_works() {
988     let tmpdir = tmpdir();
989     let path = tmpdir.join("in.txt");
990
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());
999
1000     p.set_readonly(false);
1001     check!(file.set_permissions(p));
1002 }
1003
1004 #[test]
1005 fn sync_doesnt_kill_anything() {
1006     let tmpdir = tmpdir();
1007     let path = tmpdir.join("in.txt");
1008
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());
1015 }
1016
1017 #[test]
1018 fn truncate_works() {
1019     let tmpdir = tmpdir();
1020     let path = tmpdir.join("in.txt");
1021
1022     let mut file = check!(File::create(&path));
1023     check!(file.write(b"foo"));
1024     check!(file.sync_all());
1025
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);
1033
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());
1037
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());
1049 }
1050
1051 #[test]
1052 fn open_flavors() {
1053     use crate::fs::OpenOptions as OO;
1054     fn c<T: Clone>(t: &T) -> T {
1055         t.clone()
1056     }
1057
1058     let tmpdir = tmpdir();
1059
1060     let mut r = OO::new();
1061     r.read(true);
1062     let mut w = OO::new();
1063     w.write(true);
1064     let mut rw = OO::new();
1065     rw.read(true).write(true);
1066     let mut a = OO::new();
1067     a.append(true);
1068     let mut ra = OO::new();
1069     ra.read(true).append(true);
1070
1071     #[cfg(windows)]
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";
1077
1078     // Test various combinations of creation modes and access modes.
1079     //
1080     // Allowed:
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      |
1088     //
1089     // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
1090
1091     // write-only
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")));
1097
1098     // read-only
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
1104
1105     // read-write
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")));
1111
1112     // append
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")));
1118
1119     // read-append
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")));
1125
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);
1129
1130     // Test write works
1131     check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
1132
1133     // Test write fails for read-only
1134     check!(r.open(&tmpdir.join("h")));
1135     {
1136         let mut f = check!(r.open(&tmpdir.join("h")));
1137         assert!(f.write("wut".as_bytes()).is_err());
1138     }
1139
1140     // Test write overwrites
1141     {
1142         let mut f = check!(c(&w).open(&tmpdir.join("h")));
1143         check!(f.write("baz".as_bytes()));
1144     }
1145     {
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());
1150     }
1151
1152     // Test truncate works
1153     {
1154         let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1155         check!(f.write("foo".as_bytes()));
1156     }
1157     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1158
1159     // Test append works
1160     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1161     {
1162         let mut f = check!(c(&a).open(&tmpdir.join("h")));
1163         check!(f.write("bar".as_bytes()));
1164     }
1165     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1166
1167     // Test .append(true) equals .write(true).append(true)
1168     {
1169         let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1170         check!(f.write("baz".as_bytes()));
1171     }
1172     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
1173 }
1174
1175 #[test]
1176 fn _assert_send_sync() {
1177     fn _assert_send_sync<T: Send + Sync>() {}
1178     _assert_send_sync::<OpenOptions>();
1179 }
1180
1181 #[test]
1182 fn binary_file() {
1183     let mut bytes = [0; 1024];
1184     StdRng::from_entropy().fill_bytes(&mut bytes);
1185
1186     let tmpdir = tmpdir();
1187
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[..]);
1192 }
1193
1194 #[test]
1195 fn write_then_read() {
1196     let mut bytes = [0; 1024];
1197     StdRng::from_entropy().fill_bytes(&mut bytes);
1198
1199     let tmpdir = tmpdir();
1200
1201     check!(fs::write(&tmpdir.join("test"), &bytes[..]));
1202     let v = check!(fs::read(&tmpdir.join("test")));
1203     assert!(v == &bytes[..]);
1204
1205     check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
1206     error_contains!(
1207         fs::read_to_string(&tmpdir.join("not-utf8")),
1208         "stream did not contain valid UTF-8"
1209     );
1210
1211     let s = "𐁁𐀓𐀠𐀴𐀍";
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);
1215 }
1216
1217 #[test]
1218 fn file_try_clone() {
1219     let tmpdir = tmpdir();
1220
1221     let mut f1 =
1222         check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
1223     let mut f2 = check!(f1.try_clone());
1224
1225     check!(f1.write_all(b"hello world"));
1226     check!(f1.seek(SeekFrom::Start(2)));
1227
1228     let mut buf = vec![];
1229     check!(f2.read_to_end(&mut buf));
1230     assert_eq!(buf, b"llo world");
1231     drop(f2);
1232
1233     check!(f1.write_all(b"!"));
1234 }
1235
1236 #[test]
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));
1246 }
1247
1248 #[test]
1249 fn mkdir_trailing_slash() {
1250     let tmpdir = tmpdir();
1251     let path = tmpdir.join("file");
1252     check!(fs::create_dir_all(&path.join("a/")));
1253 }
1254
1255 #[test]
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);
1262 }
1263
1264 #[test]
1265 fn realpath_works() {
1266     let tmpdir = tmpdir();
1267     if !got_symlink_permission(&tmpdir) {
1268         return;
1269     };
1270
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");
1276
1277     File::create(&file).unwrap();
1278     fs::create_dir(&dir).unwrap();
1279     symlink_file(&file, &link).unwrap();
1280     symlink_dir(&dir, &linkdir).unwrap();
1281
1282     assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
1283
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);
1289 }
1290
1291 #[test]
1292 fn realpath_works_tricky() {
1293     let tmpdir = tmpdir();
1294     if !got_symlink_permission(&tmpdir) {
1295         return;
1296     };
1297
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");
1305
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();
1312     }
1313     if cfg!(windows) {
1314         symlink_file(r"..\d\e", &c).unwrap();
1315         symlink_file(r"..\f", &e).unwrap();
1316     }
1317
1318     assert_eq!(fs::canonicalize(&c).unwrap(), f);
1319     assert_eq!(fs::canonicalize(&e).unwrap(), f);
1320 }
1321
1322 #[test]
1323 fn dir_entry_methods() {
1324     let tmpdir = tmpdir();
1325
1326     fs::create_dir_all(&tmpdir.join("a")).unwrap();
1327     File::create(&tmpdir.join("b")).unwrap();
1328
1329     for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
1330         let fname = file.file_name();
1331         match fname.to_str() {
1332             Some("a") => {
1333                 assert!(file.file_type().unwrap().is_dir());
1334                 assert!(file.metadata().unwrap().is_dir());
1335             }
1336             Some("b") => {
1337                 assert!(file.file_type().unwrap().is_file());
1338                 assert!(file.metadata().unwrap().is_file());
1339             }
1340             f => panic!("unknown file name: {f:?}"),
1341         }
1342     }
1343 }
1344
1345 #[test]
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);
1354 }
1355
1356 #[test]
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);
1360 }
1361
1362 #[test]
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);
1366 }
1367
1368 #[test]
1369 fn create_dir_all_with_junctions() {
1370     let tmpdir = tmpdir();
1371     let target = tmpdir.join("target");
1372
1373     let junction = tmpdir.join("junction");
1374     let b = junction.join("a/b");
1375
1376     let link = tmpdir.join("link");
1377     let d = link.join("c/d");
1378
1379     fs::create_dir(&target).unwrap();
1380
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
1384     // follows links
1385     assert!(junction.is_dir());
1386     assert!(b.exists());
1387
1388     if !got_symlink_permission(&tmpdir) {
1389         return;
1390     };
1391     check!(symlink_dir(&target, &link));
1392     check!(fs::create_dir_all(&d));
1393     assert!(link.is_dir());
1394     assert!(d.exists());
1395 }
1396
1397 #[test]
1398 fn metadata_access_times() {
1399     let tmpdir = tmpdir();
1400
1401     let b = tmpdir.join("b");
1402     File::create(&b).unwrap();
1403
1404     let a = check!(fs::metadata(&tmpdir.path()));
1405     let b = check!(fs::metadata(&b));
1406
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()));
1410
1411     if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1412         check!(a.created());
1413         check!(b.created());
1414     }
1415
1416     if cfg!(target_os = "linux") {
1417         // Not always available
1418         match (a.created(), b.created()) {
1419             (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1420             (Err(e1), Err(e2))
1421                 if e1.kind() == ErrorKind::Uncategorized
1422                     && e2.kind() == ErrorKind::Uncategorized
1423                     || e1.kind() == ErrorKind::Unsupported
1424                         && e2.kind() == ErrorKind::Unsupported => {}
1425             (a, b) => {
1426                 panic!("creation time must be always supported or not supported: {a:?} {b:?}")
1427             }
1428         }
1429     }
1430 }
1431
1432 /// Test creating hard links to symlinks.
1433 #[test]
1434 fn symlink_hard_link() {
1435     let tmpdir = tmpdir();
1436     if !got_symlink_permission(&tmpdir) {
1437         return;
1438     };
1439     if !able_to_not_follow_symlinks_while_hard_linking() {
1440         return;
1441     }
1442
1443     // Create "file", a file.
1444     check!(fs::File::create(tmpdir.join("file")));
1445
1446     // Create "symlink", a symlink to "file".
1447     check!(symlink_file("file", tmpdir.join("symlink")));
1448
1449     // Create "hard_link", a hard link to "symlink".
1450     check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
1451
1452     // "hard_link" should appear as a symlink.
1453     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1454
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")));
1460
1461     // Rename "file" to "file.renamed".
1462     check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
1463
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());
1469
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"));
1475
1476     // Remove "file.renamed".
1477     check!(fs::remove_file(tmpdir.join("file.renamed")));
1478
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());
1484
1485     // "hard_link" should still appear as a symlink.
1486     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1487 }
1488
1489 /// Ensure `fs::create_dir` works on Windows with longer paths.
1490 #[test]
1491 #[cfg(windows)]
1492 fn create_dir_long_paths() {
1493     use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1494     const PATH_LEN: usize = 247;
1495
1496     let tmpdir = tmpdir();
1497     let mut path = tmpdir.path().to_path_buf();
1498     path.push("a");
1499     let mut path = path.into_os_string();
1500
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.
1505         return;
1506     }
1507     // Increase the length of the path.
1508     path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1509
1510     // This should succeed.
1511     fs::create_dir(&path).unwrap();
1512
1513     // This will fail if the path isn't converted to verbatim.
1514     path.push("a");
1515     fs::create_dir(&path).unwrap();
1516
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);
1520 }
1521
1522 /// Ensure ReadDir works on large directories.
1523 /// Regression test for https://github.com/rust-lang/rust/issues/93384.
1524 #[test]
1525 fn read_large_dir() {
1526     let tmpdir = tmpdir();
1527
1528     let count = 32 * 1024;
1529     for i in 0..count {
1530         check!(fs::File::create(tmpdir.join(&i.to_string())));
1531     }
1532
1533     for entry in fs::read_dir(tmpdir.path()).unwrap() {
1534         entry.unwrap();
1535     }
1536 }
1537
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.
1541 ///
1542 /// Note this fails in CI because `hiberfil.sys` does not actually exist there.
1543 /// Therefore it's marked as ignored.
1544 #[test]
1545 #[ignore]
1546 #[cfg(windows)]
1547 fn hiberfil_sys() {
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());
1553 }
1554
1555 /// Test that two different ways of obtaining the FileType give the same result.
1556 /// Cf. https://github.com/rust-lang/rust/issues/104900
1557 #[test]
1558 fn test_eq_direntry_metadata() {
1559     let tmpdir = tmpdir();
1560     let file_path = tmpdir.join("file");
1561     File::create(file_path).unwrap();
1562     for e in fs::read_dir(tmpdir.path()).unwrap() {
1563         let e = e.unwrap();
1564         let p = e.path();
1565         let ft1 = e.file_type().unwrap();
1566         let ft2 = p.metadata().unwrap().file_type();
1567         assert_eq!(ft1, ft2);
1568     }
1569 }
1570
1571 /// Regression test for https://github.com/rust-lang/rust/issues/50619.
1572 #[test]
1573 #[cfg(target_os = "linux")]
1574 fn test_read_dir_infinite_loop() {
1575     use crate::io::ErrorKind;
1576     use crate::process::Command;
1577
1578     // Create a zombie child process
1579     let Ok(mut child) = Command::new("echo").spawn() else { return };
1580
1581     // Make sure the process is (un)dead
1582     match child.kill() {
1583         // InvalidInput means the child already exited
1584         Err(e) if e.kind() != ErrorKind::InvalidInput => return,
1585         _ => {}
1586     }
1587
1588     // open() on this path will succeed, but readdir() will fail
1589     let id = child.id();
1590     let path = format!("/proc/{id}/net");
1591
1592     // Skip the test if we can't open the directory in the first place
1593     let Ok(dir) = fs::read_dir(path) else { return };
1594
1595     // Check for duplicate errors
1596     assert!(dir.filter(|e| e.is_err()).take(2).count() < 2);
1597 }