]> git.lizzy.rs Git - rust.git/blob - library/std/src/fs/tests.rs
Auto merge of #93348 - spastorino:fix-perf-overlap-mode2, r=nikomatsakis
[rust.git] / library / std / src / fs / tests.rs
1 use crate::io::prelude::*;
2
3 use crate::fs::{self, File, OpenOptions};
4 use crate::io::{ErrorKind, SeekFrom};
5 use crate::path::Path;
6 use crate::str;
7 use crate::sync::Arc;
8 use crate::sys_common::io::test::{tmpdir, TempDir};
9 use crate::thread;
10 use crate::time::{Duration, Instant};
11
12 use rand::{rngs::StdRng, RngCore, SeedableRng};
13
14 #[cfg(unix)]
15 use crate::os::unix::fs::symlink as symlink_dir;
16 #[cfg(unix)]
17 use crate::os::unix::fs::symlink as symlink_file;
18 #[cfg(unix)]
19 use crate::os::unix::fs::symlink as symlink_junction;
20 #[cfg(windows)]
21 use crate::os::windows::fs::{symlink_dir, symlink_file};
22 #[cfg(windows)]
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};
28
29 macro_rules! check {
30     ($e:expr) => {
31         match $e {
32             Ok(t) => t,
33             Err(e) => panic!("{} failed with: {}", stringify!($e), e),
34         }
35     };
36 }
37
38 #[cfg(windows)]
39 macro_rules! error {
40     ($e:expr, $s:expr) => {
41         match $e {
42             Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
43             Err(ref err) => {
44                 assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s)
45             }
46         }
47     };
48 }
49
50 #[cfg(unix)]
51 macro_rules! error {
52     ($e:expr, $s:expr) => {
53         error_contains!($e, $s)
54     };
55 }
56
57 macro_rules! error_contains {
58     ($e:expr, $s:expr) => {
59         match $e {
60             Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
61             Err(ref err) => {
62                 assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s)
63             }
64         }
65     };
66 }
67
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
73 // permissions.
74 pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
75     if cfg!(unix) {
76         return true;
77     }
78     let link = tmpdir.join("some_hopefully_unique_link_name");
79
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,
84     }
85 }
86
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()
91 }
92
93 #[cfg(not(target_os = "macos"))]
94 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
95     return true;
96 }
97
98 #[test]
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");
103     {
104         let mut write_stream = check!(File::create(filename));
105         check!(write_stream.write(message.as_bytes()));
106     }
107     {
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(),
113         };
114         assert_eq!(read_str, message);
115     }
116     check!(fs::remove_file(filename));
117 }
118
119 #[test]
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);
124
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");
129     #[cfg(windows)]
130     error!(result, 2); // ERROR_FILE_NOT_FOUND
131 }
132
133 #[test]
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");
137
138     let result = fs::remove_file(filename);
139
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");
144     #[cfg(windows)]
145     error!(result, 2); // ERROR_FILE_NOT_FOUND
146 }
147
148 #[test]
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");
154     {
155         let mut rw_stream = check!(File::create(filename));
156         check!(rw_stream.write(message.as_bytes()));
157     }
158     {
159         let mut read_stream = check!(File::open(filename));
160         {
161             let read_buf = &mut read_mem[0..4];
162             check!(read_stream.read(read_buf));
163         }
164         {
165             let read_buf = &mut read_mem[4..8];
166             check!(read_stream.read(read_buf));
167         }
168     }
169     check!(fs::remove_file(filename));
170     let read_str = str::from_utf8(&read_mem).unwrap();
171     assert_eq!(read_str, message);
172 }
173
174 #[test]
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");
183     {
184         let mut rw_stream = check!(File::create(filename));
185         check!(rw_stream.write(message.as_bytes()));
186     }
187     {
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)));
193     }
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);
199 }
200
201 #[test]
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!!";
206     let seek_idx = 3;
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");
210     {
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()));
215     }
216     {
217         let mut read_stream = check!(File::open(filename));
218         check!(read_stream.read(&mut read_mem));
219     }
220     check!(fs::remove_file(filename));
221     let read_str = str::from_utf8(&read_mem).unwrap();
222     assert!(read_str == final_msg);
223 }
224
225 #[test]
226 fn file_test_io_seek_shakedown() {
227     //                   01234567890123
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");
235     {
236         let mut rw_stream = check!(File::create(filename));
237         check!(rw_stream.write(initial_msg.as_bytes()));
238     }
239     {
240         let mut read_stream = check!(File::open(filename));
241
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);
245
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);
249
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);
253     }
254     check!(fs::remove_file(filename));
255 }
256
257 #[test]
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];
262     {
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);
267     }
268     check!(fs::remove_file(&filename));
269 }
270
271 #[test]
272 #[cfg(unix)]
273 fn file_test_io_read_write_at() {
274     use crate::os::unix::fs::FileExt;
275
276     let tmpdir = tmpdir();
277     let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
278     let mut buf = [0; 256];
279     let write1 = "asdf";
280     let write2 = "qwer-";
281     let write3 = "-zxcv";
282     let content = "qwer-asdf-zxcv";
283     {
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);
304     }
305     {
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);
323     }
324     check!(fs::remove_file(&filename));
325 }
326
327 #[test]
328 #[cfg(unix)]
329 fn set_get_unix_permissions() {
330     use crate::os::unix::fs::PermissionsExt;
331
332     let tmpdir = tmpdir();
333     let filename = &tmpdir.join("set_get_unix_permissions");
334     check!(fs::create_dir(filename));
335     let mask = 0o7777;
336
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);
340
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);
347 }
348
349 #[test]
350 #[cfg(windows)]
351 fn file_test_io_seek_read_write() {
352     use crate::os::windows::fs::FileExt;
353
354     let tmpdir = tmpdir();
355     let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt");
356     let mut buf = [0; 256];
357     let write1 = "asdf";
358     let write2 = "qwer-";
359     let write3 = "-zxcv";
360     let content = "qwer-asdf-zxcv";
361     {
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);
380     }
381     {
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);
399     }
400     check!(fs::remove_file(&filename));
401 }
402
403 #[test]
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");
407     {
408         let mut opts = OpenOptions::new();
409         let mut fs = check!(opts.read(true).write(true).create(true).open(filename));
410         let msg = "hw";
411         fs.write(msg.as_bytes()).unwrap();
412
413         let fstat_res = check!(fs.metadata());
414         assert!(fstat_res.is_file());
415     }
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));
421 }
422
423 #[test]
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));
433 }
434
435 #[test]
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));
442 }
443
444 #[test]
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());
452 }
453
454 #[test]
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());
464 }
465
466 #[test]
467 fn file_test_directoryinfo_readdir() {
468     let tmpdir = tmpdir();
469     let dir = &tmpdir.join("di_readdir");
470     check!(fs::create_dir(dir));
471     let prefix = "foo";
472     for n in 0..3 {
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));
478     }
479     let files = check!(fs::read_dir(dir));
480     let mut mem = [0; 4];
481     for f in files {
482         let f = f.unwrap().path();
483         {
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);
489         }
490         check!(fs::remove_file(&f));
491     }
492     check!(fs::remove_dir(dir));
493 }
494
495 #[test]
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);
502 }
503
504 #[test]
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);
511 }
512
513 #[test]
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())
519 }
520
521 #[test]
522 fn recursive_mkdir_failure() {
523     let tmpdir = tmpdir();
524     let dir = tmpdir.join("d1");
525     let file = dir.join("f1");
526
527     check!(fs::create_dir_all(&dir));
528     check!(File::create(&file));
529
530     let result = fs::create_dir_all(&file);
531
532     assert!(result.is_err());
533 }
534
535 #[test]
536 fn concurrent_recursive_mkdir() {
537     for _ in 0..100 {
538         let dir = tmpdir();
539         let mut dir = dir.join("a");
540         for _ in 0..40 {
541             dir = dir.join("a");
542         }
543         let mut join = vec![];
544         for _ in 0..8 {
545             let dir = dir.clone();
546             join.push(thread::spawn(move || {
547                 check!(fs::create_dir_all(&dir));
548             }))
549         }
550
551         // No `Display` on result of `join()`
552         join.drain(..).map(|join| join.join().unwrap()).count();
553     }
554 }
555
556 #[test]
557 fn recursive_mkdir_slash() {
558     check!(fs::create_dir_all(Path::new("/")));
559 }
560
561 #[test]
562 fn recursive_mkdir_dot() {
563     check!(fs::create_dir_all(Path::new(".")));
564 }
565
566 #[test]
567 fn recursive_mkdir_empty() {
568     check!(fs::create_dir_all(Path::new("")));
569 }
570
571 #[test]
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));
585
586     assert!(!d1.is_dir());
587     assert!(canary.exists());
588 }
589
590 #[test]
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));
601
602     assert!(!link.is_dir());
603     assert!(canary.exists());
604 }
605
606 #[test]
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);
613     #[cfg(unix)]
614     error!(result, "Not a directory");
615     #[cfg(windows)]
616     error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid.
617     assert!(result.is_err());
618     assert!(canary.exists());
619 }
620
621 #[test]
622 // only Windows makes a distinction between file and directory symlinks.
623 #[cfg(windows)]
624 fn recursive_rmdir_of_file_symlink() {
625     let tmpdir = tmpdir();
626     if !got_symlink_permission(&tmpdir) {
627         return;
628     };
629
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"),
636         Err(..) => {}
637     }
638 }
639
640 #[test]
641 #[ignore] // takes too much time
642 fn recursive_rmdir_toctou() {
643     // Test for time-of-check to time-of-use issues.
644     //
645     // Scenario:
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.
649     //
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();
658
659     // setup dest
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();
665
666     let drop_canary_arc = Arc::new(());
667     let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
668
669     eprintln!("x: {:?}", &victim_del_path);
670
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);
675         }
676     });
677
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() {
682             panic!(
683                 "Victim deleted symlinked file outside of victim_del. Attack succeeded in {:?}.",
684                 Instant::now().duration_since(start_time)
685             );
686         }
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);
690     }
691 }
692
693 #[test]
694 fn unicode_path_is_dir() {
695     assert!(Path::new(".").is_dir());
696     assert!(!Path::new("test/stdtest/fs.rs").is_dir());
697
698     let tmpdir = tmpdir();
699
700     let mut dirpath = tmpdir.path().to_path_buf();
701     dirpath.push("test-가一ー你好");
702     check!(fs::create_dir(&dirpath));
703     assert!(dirpath.is_dir());
704
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());
710 }
711
712 #[test]
713 fn unicode_path_exists() {
714     assert!(Path::new(".").exists());
715     assert!(!Path::new("test/nonexistent-bogus-path").exists());
716
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());
723 }
724
725 #[test]
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");
729
730     match fs::copy(&from, &to) {
731         Ok(..) => panic!(),
732         Err(..) => {
733             assert!(!from.exists());
734             assert!(!to.exists());
735         }
736     }
737 }
738
739 #[test]
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");
750 }
751
752 #[test]
753 fn copy_file_ok() {
754     let tmpdir = tmpdir();
755     let input = tmpdir.join("in.txt");
756     let out = tmpdir.join("out.txt");
757
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");
763
764     assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions());
765 }
766
767 #[test]
768 fn copy_file_dst_dir() {
769     let tmpdir = tmpdir();
770     let out = tmpdir.join("out");
771
772     check!(File::create(&out));
773     match fs::copy(&*out, tmpdir.path()) {
774         Ok(..) => panic!(),
775         Err(..) => {}
776     }
777 }
778
779 #[test]
780 fn copy_file_dst_exists() {
781     let tmpdir = tmpdir();
782     let input = tmpdir.join("in");
783     let output = tmpdir.join("out");
784
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));
788
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());
792 }
793
794 #[test]
795 fn copy_file_src_dir() {
796     let tmpdir = tmpdir();
797     let out = tmpdir.join("out");
798
799     match fs::copy(tmpdir.path(), &out) {
800         Ok(..) => panic!(),
801         Err(..) => {}
802     }
803     assert!(!out.exists());
804 }
805
806 #[test]
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");
811
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()));
820 }
821
822 #[test]
823 #[cfg(windows)]
824 fn copy_file_preserves_streams() {
825     let tmp = tmpdir();
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());
832 }
833
834 #[test]
835 fn copy_file_returns_metadata_len() {
836     let tmp = tmpdir();
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"));
840     #[cfg(windows)]
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);
844 }
845
846 #[test]
847 fn copy_file_follows_dst_symlink() {
848     let tmp = tmpdir();
849     if !got_symlink_permission(&tmp) {
850         return;
851     };
852
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");
856
857     check!(fs::write(&in_path, "foo"));
858     check!(fs::write(&out_path, "bar"));
859     check!(symlink_file(&out_path, &out_path_symlink));
860
861     check!(fs::copy(&in_path, &out_path_symlink));
862
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());
866 }
867
868 #[test]
869 fn symlinks_work() {
870     let tmpdir = tmpdir();
871     if !got_symlink_permission(&tmpdir) {
872         return;
873     };
874
875     let input = tmpdir.join("in.txt");
876     let out = tmpdir.join("out.txt");
877
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());
885 }
886
887 #[test]
888 fn symlink_noexist() {
889     // Symlinks can point to things that don't exist
890     let tmpdir = tmpdir();
891     if !got_symlink_permission(&tmpdir) {
892         return;
893     };
894
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");
899 }
900
901 #[test]
902 fn read_link() {
903     if cfg!(windows) {
904         // directory symlink
905         assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
906         // junction
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"));
910     }
911     let tmpdir = tmpdir();
912     let link = tmpdir.join("link");
913     if !got_symlink_permission(&tmpdir) {
914         return;
915     };
916     check!(symlink_file(&"foo", &link));
917     assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
918 }
919
920 #[test]
921 fn readlink_not_symlink() {
922     let tmpdir = tmpdir();
923     match fs::read_link(tmpdir.path()) {
924         Ok(..) => panic!("wanted a failure"),
925         Err(..) => {}
926     }
927 }
928
929 #[test]
930 fn links_work() {
931     let tmpdir = tmpdir();
932     let input = tmpdir.join("in.txt");
933     let out = tmpdir.join("out.txt");
934
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());
942
943     // can't link to yourself
944     match fs::hard_link(&input, &input) {
945         Ok(..) => panic!("wanted a failure"),
946         Err(..) => {}
947     }
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"),
951         Err(..) => {}
952     }
953 }
954
955 #[test]
956 fn chmod_works() {
957     let tmpdir = tmpdir();
958     let file = tmpdir.join("in.txt");
959
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());
968
969     match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
970         Ok(..) => panic!("wanted an error"),
971         Err(..) => {}
972     }
973
974     p.set_readonly(false);
975     check!(fs::set_permissions(&file, p));
976 }
977
978 #[test]
979 fn fchmod_works() {
980     let tmpdir = tmpdir();
981     let path = tmpdir.join("in.txt");
982
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());
991
992     p.set_readonly(false);
993     check!(file.set_permissions(p));
994 }
995
996 #[test]
997 fn sync_doesnt_kill_anything() {
998     let tmpdir = tmpdir();
999     let path = tmpdir.join("in.txt");
1000
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());
1007 }
1008
1009 #[test]
1010 fn truncate_works() {
1011     let tmpdir = tmpdir();
1012     let path = tmpdir.join("in.txt");
1013
1014     let mut file = check!(File::create(&path));
1015     check!(file.write(b"foo"));
1016     check!(file.sync_all());
1017
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);
1025
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());
1029
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());
1041 }
1042
1043 #[test]
1044 fn open_flavors() {
1045     use crate::fs::OpenOptions as OO;
1046     fn c<T: Clone>(t: &T) -> T {
1047         t.clone()
1048     }
1049
1050     let tmpdir = tmpdir();
1051
1052     let mut r = OO::new();
1053     r.read(true);
1054     let mut w = OO::new();
1055     w.write(true);
1056     let mut rw = OO::new();
1057     rw.read(true).write(true);
1058     let mut a = OO::new();
1059     a.append(true);
1060     let mut ra = OO::new();
1061     ra.read(true).append(true);
1062
1063     #[cfg(windows)]
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";
1069
1070     // Test various combinations of creation modes and access modes.
1071     //
1072     // Allowed:
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      |
1080     //
1081     // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
1082
1083     // write-only
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")));
1089
1090     // read-only
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
1096
1097     // read-write
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")));
1103
1104     // append
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")));
1110
1111     // read-append
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")));
1117
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);
1121
1122     // Test write works
1123     check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
1124
1125     // Test write fails for read-only
1126     check!(r.open(&tmpdir.join("h")));
1127     {
1128         let mut f = check!(r.open(&tmpdir.join("h")));
1129         assert!(f.write("wut".as_bytes()).is_err());
1130     }
1131
1132     // Test write overwrites
1133     {
1134         let mut f = check!(c(&w).open(&tmpdir.join("h")));
1135         check!(f.write("baz".as_bytes()));
1136     }
1137     {
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());
1142     }
1143
1144     // Test truncate works
1145     {
1146         let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1147         check!(f.write("foo".as_bytes()));
1148     }
1149     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1150
1151     // Test append works
1152     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1153     {
1154         let mut f = check!(c(&a).open(&tmpdir.join("h")));
1155         check!(f.write("bar".as_bytes()));
1156     }
1157     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1158
1159     // Test .append(true) equals .write(true).append(true)
1160     {
1161         let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1162         check!(f.write("baz".as_bytes()));
1163     }
1164     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
1165 }
1166
1167 #[test]
1168 fn _assert_send_sync() {
1169     fn _assert_send_sync<T: Send + Sync>() {}
1170     _assert_send_sync::<OpenOptions>();
1171 }
1172
1173 #[test]
1174 fn binary_file() {
1175     let mut bytes = [0; 1024];
1176     StdRng::from_entropy().fill_bytes(&mut bytes);
1177
1178     let tmpdir = tmpdir();
1179
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[..]);
1184 }
1185
1186 #[test]
1187 fn write_then_read() {
1188     let mut bytes = [0; 1024];
1189     StdRng::from_entropy().fill_bytes(&mut bytes);
1190
1191     let tmpdir = tmpdir();
1192
1193     check!(fs::write(&tmpdir.join("test"), &bytes[..]));
1194     let v = check!(fs::read(&tmpdir.join("test")));
1195     assert!(v == &bytes[..]);
1196
1197     check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
1198     error_contains!(
1199         fs::read_to_string(&tmpdir.join("not-utf8")),
1200         "stream did not contain valid UTF-8"
1201     );
1202
1203     let s = "𐁁𐀓𐀠𐀴𐀍";
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);
1207 }
1208
1209 #[test]
1210 fn file_try_clone() {
1211     let tmpdir = tmpdir();
1212
1213     let mut f1 =
1214         check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
1215     let mut f2 = check!(f1.try_clone());
1216
1217     check!(f1.write_all(b"hello world"));
1218     check!(f1.seek(SeekFrom::Start(2)));
1219
1220     let mut buf = vec![];
1221     check!(f2.read_to_end(&mut buf));
1222     assert_eq!(buf, b"llo world");
1223     drop(f2);
1224
1225     check!(f1.write_all(b"!"));
1226 }
1227
1228 #[test]
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));
1238 }
1239
1240 #[test]
1241 fn mkdir_trailing_slash() {
1242     let tmpdir = tmpdir();
1243     let path = tmpdir.join("file");
1244     check!(fs::create_dir_all(&path.join("a/")));
1245 }
1246
1247 #[test]
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);
1254 }
1255
1256 #[test]
1257 fn realpath_works() {
1258     let tmpdir = tmpdir();
1259     if !got_symlink_permission(&tmpdir) {
1260         return;
1261     };
1262
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");
1268
1269     File::create(&file).unwrap();
1270     fs::create_dir(&dir).unwrap();
1271     symlink_file(&file, &link).unwrap();
1272     symlink_dir(&dir, &linkdir).unwrap();
1273
1274     assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
1275
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);
1281 }
1282
1283 #[test]
1284 fn realpath_works_tricky() {
1285     let tmpdir = tmpdir();
1286     if !got_symlink_permission(&tmpdir) {
1287         return;
1288     };
1289
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");
1297
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();
1304     }
1305     if cfg!(windows) {
1306         symlink_file(r"..\d\e", &c).unwrap();
1307         symlink_file(r"..\f", &e).unwrap();
1308     }
1309
1310     assert_eq!(fs::canonicalize(&c).unwrap(), f);
1311     assert_eq!(fs::canonicalize(&e).unwrap(), f);
1312 }
1313
1314 #[test]
1315 fn dir_entry_methods() {
1316     let tmpdir = tmpdir();
1317
1318     fs::create_dir_all(&tmpdir.join("a")).unwrap();
1319     File::create(&tmpdir.join("b")).unwrap();
1320
1321     for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
1322         let fname = file.file_name();
1323         match fname.to_str() {
1324             Some("a") => {
1325                 assert!(file.file_type().unwrap().is_dir());
1326                 assert!(file.metadata().unwrap().is_dir());
1327             }
1328             Some("b") => {
1329                 assert!(file.file_type().unwrap().is_file());
1330                 assert!(file.metadata().unwrap().is_file());
1331             }
1332             f => panic!("unknown file name: {:?}", f),
1333         }
1334     }
1335 }
1336
1337 #[test]
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);
1346 }
1347
1348 #[test]
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);
1352 }
1353
1354 #[test]
1355 fn create_dir_all_with_junctions() {
1356     let tmpdir = tmpdir();
1357     let target = tmpdir.join("target");
1358
1359     let junction = tmpdir.join("junction");
1360     let b = junction.join("a/b");
1361
1362     let link = tmpdir.join("link");
1363     let d = link.join("c/d");
1364
1365     fs::create_dir(&target).unwrap();
1366
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
1370     // follows links
1371     assert!(junction.is_dir());
1372     assert!(b.exists());
1373
1374     if !got_symlink_permission(&tmpdir) {
1375         return;
1376     };
1377     check!(symlink_dir(&target, &link));
1378     check!(fs::create_dir_all(&d));
1379     assert!(link.is_dir());
1380     assert!(d.exists());
1381 }
1382
1383 #[test]
1384 fn metadata_access_times() {
1385     let tmpdir = tmpdir();
1386
1387     let b = tmpdir.join("b");
1388     File::create(&b).unwrap();
1389
1390     let a = check!(fs::metadata(&tmpdir.path()));
1391     let b = check!(fs::metadata(&b));
1392
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()));
1396
1397     if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1398         check!(a.created());
1399         check!(b.created());
1400     }
1401
1402     if cfg!(target_os = "linux") {
1403         // Not always available
1404         match (a.created(), b.created()) {
1405             (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1406             (Err(e1), Err(e2))
1407                 if e1.kind() == ErrorKind::Uncategorized
1408                     && e2.kind() == ErrorKind::Uncategorized
1409                     || e1.kind() == ErrorKind::Unsupported
1410                         && e2.kind() == ErrorKind::Unsupported => {}
1411             (a, b) => {
1412                 panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,)
1413             }
1414         }
1415     }
1416 }
1417
1418 /// Test creating hard links to symlinks.
1419 #[test]
1420 fn symlink_hard_link() {
1421     let tmpdir = tmpdir();
1422     if !got_symlink_permission(&tmpdir) {
1423         return;
1424     };
1425     if !able_to_not_follow_symlinks_while_hard_linking() {
1426         return;
1427     }
1428
1429     // Create "file", a file.
1430     check!(fs::File::create(tmpdir.join("file")));
1431
1432     // Create "symlink", a symlink to "file".
1433     check!(symlink_file("file", tmpdir.join("symlink")));
1434
1435     // Create "hard_link", a hard link to "symlink".
1436     check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
1437
1438     // "hard_link" should appear as a symlink.
1439     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1440
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")));
1446
1447     // Rename "file" to "file.renamed".
1448     check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
1449
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());
1455
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"));
1461
1462     // Remove "file.renamed".
1463     check!(fs::remove_file(tmpdir.join("file.renamed")));
1464
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());
1470
1471     // "hard_link" should still appear as a symlink.
1472     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1473 }
1474
1475 /// Ensure `fs::create_dir` works on Windows with longer paths.
1476 #[test]
1477 #[cfg(windows)]
1478 fn create_dir_long_paths() {
1479     use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1480     const PATH_LEN: usize = 247;
1481
1482     let tmpdir = tmpdir();
1483     let mut path = tmpdir.path().to_path_buf();
1484     path.push("a");
1485     let mut path = path.into_os_string();
1486
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.
1491         return;
1492     }
1493     // Increase the length of the path.
1494     path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1495
1496     // This should succeed.
1497     fs::create_dir(&path).unwrap();
1498
1499     // This will fail if the path isn't converted to verbatim.
1500     path.push("a");
1501     fs::create_dir(&path).unwrap();
1502
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);
1506 }
1507
1508 /// Ensure ReadDir works on large directories.
1509 /// Regression test for https://github.com/rust-lang/rust/issues/93384.
1510 #[test]
1511 fn read_large_dir() {
1512     let tmpdir = tmpdir();
1513
1514     let count = 32 * 1024;
1515     for i in 0..count {
1516         check!(fs::File::create(tmpdir.join(&i.to_string())));
1517     }
1518
1519     for entry in fs::read_dir(tmpdir.path()).unwrap() {
1520         entry.unwrap();
1521     }
1522 }