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) => assert!(
44                 err.raw_os_error() == Some($s),
45                 format!("`{}` 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), format!("`{}` 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.
got_symlink_permission(tmpdir: &TempDir) -> bool75 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")]
able_to_not_follow_symlinks_while_hard_linking() -> bool89 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"))]
able_to_not_follow_symlinks_while_hard_linking() -> bool95 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
96     return true;
97 }
98 
99 #[test]
file_test_io_smoke_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]
invalid_path_raises()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]
file_test_iounlinking_invalid_path_should_raise_condition()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]
file_test_io_non_positional_read()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]
file_test_io_seek_and_tell_smoke_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]
file_test_io_seek_and_write()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]
file_test_io_seek_shakedown()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]
file_test_io_eof()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)]
file_test_io_read_write_at()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)]
set_get_unix_permissions()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)]
file_test_io_seek_read_write()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]
file_test_stat_is_correct_on_is_file()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]
file_test_stat_is_correct_on_is_dir()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]
file_test_fileinfo_false_when_checking_is_file_on_a_directory()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]
file_test_fileinfo_check_exists_before_and_after_file_creation()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]
file_test_directoryinfo_check_exists_before_and_after_mkdir()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]
file_test_directoryinfo_readdir()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!("{}.txt", n));
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]
file_create_new_already_exists_error()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]
mkdir_path_already_exists_error()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]
recursive_mkdir()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]
recursive_mkdir_failure()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]
concurrent_recursive_mkdir()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]
recursive_mkdir_slash()558 fn recursive_mkdir_slash() {
559     check!(fs::create_dir_all(Path::new("/")));
560 }
561 
562 #[test]
recursive_mkdir_dot()563 fn recursive_mkdir_dot() {
564     check!(fs::create_dir_all(Path::new(".")));
565 }
566 
567 #[test]
recursive_mkdir_empty()568 fn recursive_mkdir_empty() {
569     check!(fs::create_dir_all(Path::new("")));
570 }
571 
572 #[test]
recursive_rmdir()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]
recursive_rmdir_of_symlink()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]
recursive_rmdir_of_file_fails()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)]
recursive_rmdir_of_file_symlink()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
recursive_rmdir_toctou()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 he does not have access.
648     // He has a way to get a privileged Rust binary call `std::fs::remove_dir_all()` on a
649     // directory he controls, e.g. in his 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]
unicode_path_is_dir()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]
unicode_path_exists()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]
copy_file_does_not_exist()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]
copy_src_does_not_exist()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]
copy_file_ok()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]
copy_file_dst_dir()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]
copy_file_dst_exists()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]
copy_file_src_dir()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]
copy_file_preserves_perm_bits()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)]
copy_file_preserves_streams()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]
copy_file_returns_metadata_len()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]
copy_file_follows_dst_symlink()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]
symlinks_work()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]
symlink_noexist()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]
read_link()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         assert_eq!(check!(fs::read_link(r"C:\Documents and Settings\")), Path::new(r"C:\Users"));
911     }
912     let tmpdir = tmpdir();
913     let link = tmpdir.join("link");
914     if !got_symlink_permission(&tmpdir) {
915         return;
916     };
917     check!(symlink_file(&"foo", &link));
918     assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
919 }
920 
921 #[test]
readlink_not_symlink()922 fn readlink_not_symlink() {
923     let tmpdir = tmpdir();
924     match fs::read_link(tmpdir.path()) {
925         Ok(..) => panic!("wanted a failure"),
926         Err(..) => {}
927     }
928 }
929 
930 #[test]
links_work()931 fn links_work() {
932     let tmpdir = tmpdir();
933     let input = tmpdir.join("in.txt");
934     let out = tmpdir.join("out.txt");
935 
936     check!(check!(File::create(&input)).write("foobar".as_bytes()));
937     check!(fs::hard_link(&input, &out));
938     assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
939     assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len());
940     let mut v = Vec::new();
941     check!(check!(File::open(&out)).read_to_end(&mut v));
942     assert_eq!(v, b"foobar".to_vec());
943 
944     // can't link to yourself
945     match fs::hard_link(&input, &input) {
946         Ok(..) => panic!("wanted a failure"),
947         Err(..) => {}
948     }
949     // can't link to something that doesn't exist
950     match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
951         Ok(..) => panic!("wanted a failure"),
952         Err(..) => {}
953     }
954 }
955 
956 #[test]
chmod_works()957 fn chmod_works() {
958     let tmpdir = tmpdir();
959     let file = tmpdir.join("in.txt");
960 
961     check!(File::create(&file));
962     let attr = check!(fs::metadata(&file));
963     assert!(!attr.permissions().readonly());
964     let mut p = attr.permissions();
965     p.set_readonly(true);
966     check!(fs::set_permissions(&file, p.clone()));
967     let attr = check!(fs::metadata(&file));
968     assert!(attr.permissions().readonly());
969 
970     match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
971         Ok(..) => panic!("wanted an error"),
972         Err(..) => {}
973     }
974 
975     p.set_readonly(false);
976     check!(fs::set_permissions(&file, p));
977 }
978 
979 #[test]
fchmod_works()980 fn fchmod_works() {
981     let tmpdir = tmpdir();
982     let path = tmpdir.join("in.txt");
983 
984     let file = check!(File::create(&path));
985     let attr = check!(fs::metadata(&path));
986     assert!(!attr.permissions().readonly());
987     let mut p = attr.permissions();
988     p.set_readonly(true);
989     check!(file.set_permissions(p.clone()));
990     let attr = check!(fs::metadata(&path));
991     assert!(attr.permissions().readonly());
992 
993     p.set_readonly(false);
994     check!(file.set_permissions(p));
995 }
996 
997 #[test]
sync_doesnt_kill_anything()998 fn sync_doesnt_kill_anything() {
999     let tmpdir = tmpdir();
1000     let path = tmpdir.join("in.txt");
1001 
1002     let mut file = check!(File::create(&path));
1003     check!(file.sync_all());
1004     check!(file.sync_data());
1005     check!(file.write(b"foo"));
1006     check!(file.sync_all());
1007     check!(file.sync_data());
1008 }
1009 
1010 #[test]
truncate_works()1011 fn truncate_works() {
1012     let tmpdir = tmpdir();
1013     let path = tmpdir.join("in.txt");
1014 
1015     let mut file = check!(File::create(&path));
1016     check!(file.write(b"foo"));
1017     check!(file.sync_all());
1018 
1019     // Do some simple things with truncation
1020     assert_eq!(check!(file.metadata()).len(), 3);
1021     check!(file.set_len(10));
1022     assert_eq!(check!(file.metadata()).len(), 10);
1023     check!(file.write(b"bar"));
1024     check!(file.sync_all());
1025     assert_eq!(check!(file.metadata()).len(), 10);
1026 
1027     let mut v = Vec::new();
1028     check!(check!(File::open(&path)).read_to_end(&mut v));
1029     assert_eq!(v, b"foobar\0\0\0\0".to_vec());
1030 
1031     // Truncate to a smaller length, don't seek, and then write something.
1032     // Ensure that the intermediate zeroes are all filled in (we have `seek`ed
1033     // past the end of the file).
1034     check!(file.set_len(2));
1035     assert_eq!(check!(file.metadata()).len(), 2);
1036     check!(file.write(b"wut"));
1037     check!(file.sync_all());
1038     assert_eq!(check!(file.metadata()).len(), 9);
1039     let mut v = Vec::new();
1040     check!(check!(File::open(&path)).read_to_end(&mut v));
1041     assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
1042 }
1043 
1044 #[test]
open_flavors()1045 fn open_flavors() {
1046     use crate::fs::OpenOptions as OO;
1047     fn c<T: Clone>(t: &T) -> T {
1048         t.clone()
1049     }
1050 
1051     let tmpdir = tmpdir();
1052 
1053     let mut r = OO::new();
1054     r.read(true);
1055     let mut w = OO::new();
1056     w.write(true);
1057     let mut rw = OO::new();
1058     rw.read(true).write(true);
1059     let mut a = OO::new();
1060     a.append(true);
1061     let mut ra = OO::new();
1062     ra.read(true).append(true);
1063 
1064     #[cfg(windows)]
1065     let invalid_options = 87; // ERROR_INVALID_PARAMETER
1066     #[cfg(all(unix, not(target_os = "vxworks")))]
1067     let invalid_options = "Invalid argument";
1068     #[cfg(target_os = "vxworks")]
1069     let invalid_options = "invalid argument";
1070 
1071     // Test various combinations of creation modes and access modes.
1072     //
1073     // Allowed:
1074     // creation mode           | read  | write | read-write | append | read-append |
1075     // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
1076     // not set (open existing) |   X   |   X   |     X      |   X    |      X      |
1077     // create                  |       |   X   |     X      |   X    |      X      |
1078     // truncate                |       |   X   |     X      |        |             |
1079     // create and truncate     |       |   X   |     X      |        |             |
1080     // create_new              |       |   X   |     X      |   X    |      X      |
1081     //
1082     // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
1083 
1084     // write-only
1085     check!(c(&w).create_new(true).open(&tmpdir.join("a")));
1086     check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a")));
1087     check!(c(&w).truncate(true).open(&tmpdir.join("a")));
1088     check!(c(&w).create(true).open(&tmpdir.join("a")));
1089     check!(c(&w).open(&tmpdir.join("a")));
1090 
1091     // read-only
1092     error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
1093     error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
1094     error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
1095     error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
1096     check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
1097 
1098     // read-write
1099     check!(c(&rw).create_new(true).open(&tmpdir.join("c")));
1100     check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c")));
1101     check!(c(&rw).truncate(true).open(&tmpdir.join("c")));
1102     check!(c(&rw).create(true).open(&tmpdir.join("c")));
1103     check!(c(&rw).open(&tmpdir.join("c")));
1104 
1105     // append
1106     check!(c(&a).create_new(true).open(&tmpdir.join("d")));
1107     error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
1108     error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
1109     check!(c(&a).create(true).open(&tmpdir.join("d")));
1110     check!(c(&a).open(&tmpdir.join("d")));
1111 
1112     // read-append
1113     check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
1114     error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
1115     error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
1116     check!(c(&ra).create(true).open(&tmpdir.join("e")));
1117     check!(c(&ra).open(&tmpdir.join("e")));
1118 
1119     // Test opening a file without setting an access mode
1120     let mut blank = OO::new();
1121     error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
1122 
1123     // Test write works
1124     check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
1125 
1126     // Test write fails for read-only
1127     check!(r.open(&tmpdir.join("h")));
1128     {
1129         let mut f = check!(r.open(&tmpdir.join("h")));
1130         assert!(f.write("wut".as_bytes()).is_err());
1131     }
1132 
1133     // Test write overwrites
1134     {
1135         let mut f = check!(c(&w).open(&tmpdir.join("h")));
1136         check!(f.write("baz".as_bytes()));
1137     }
1138     {
1139         let mut f = check!(c(&r).open(&tmpdir.join("h")));
1140         let mut b = vec![0; 6];
1141         check!(f.read(&mut b));
1142         assert_eq!(b, "bazbar".as_bytes());
1143     }
1144 
1145     // Test truncate works
1146     {
1147         let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1148         check!(f.write("foo".as_bytes()));
1149     }
1150     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1151 
1152     // Test append works
1153     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1154     {
1155         let mut f = check!(c(&a).open(&tmpdir.join("h")));
1156         check!(f.write("bar".as_bytes()));
1157     }
1158     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1159 
1160     // Test .append(true) equals .write(true).append(true)
1161     {
1162         let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1163         check!(f.write("baz".as_bytes()));
1164     }
1165     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
1166 }
1167 
1168 #[test]
_assert_send_sync()1169 fn _assert_send_sync() {
1170     fn _assert_send_sync<T: Send + Sync>() {}
1171     _assert_send_sync::<OpenOptions>();
1172 }
1173 
1174 #[test]
binary_file()1175 fn binary_file() {
1176     let mut bytes = [0; 1024];
1177     StdRng::from_entropy().fill_bytes(&mut bytes);
1178 
1179     let tmpdir = tmpdir();
1180 
1181     check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
1182     let mut v = Vec::new();
1183     check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
1184     assert!(v == &bytes[..]);
1185 }
1186 
1187 #[test]
write_then_read()1188 fn write_then_read() {
1189     let mut bytes = [0; 1024];
1190     StdRng::from_entropy().fill_bytes(&mut bytes);
1191 
1192     let tmpdir = tmpdir();
1193 
1194     check!(fs::write(&tmpdir.join("test"), &bytes[..]));
1195     let v = check!(fs::read(&tmpdir.join("test")));
1196     assert!(v == &bytes[..]);
1197 
1198     check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
1199     error_contains!(
1200         fs::read_to_string(&tmpdir.join("not-utf8")),
1201         "stream did not contain valid UTF-8"
1202     );
1203 
1204     let s = "����������";
1205     check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
1206     let string = check!(fs::read_to_string(&tmpdir.join("utf8")));
1207     assert_eq!(string, s);
1208 }
1209 
1210 #[test]
file_try_clone()1211 fn file_try_clone() {
1212     let tmpdir = tmpdir();
1213 
1214     let mut f1 =
1215         check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
1216     let mut f2 = check!(f1.try_clone());
1217 
1218     check!(f1.write_all(b"hello world"));
1219     check!(f1.seek(SeekFrom::Start(2)));
1220 
1221     let mut buf = vec![];
1222     check!(f2.read_to_end(&mut buf));
1223     assert_eq!(buf, b"llo world");
1224     drop(f2);
1225 
1226     check!(f1.write_all(b"!"));
1227 }
1228 
1229 #[test]
1230 #[cfg(not(windows))]
unlink_readonly()1231 fn unlink_readonly() {
1232     let tmpdir = tmpdir();
1233     let path = tmpdir.join("file");
1234     check!(File::create(&path));
1235     let mut perm = check!(fs::metadata(&path)).permissions();
1236     perm.set_readonly(true);
1237     check!(fs::set_permissions(&path, perm));
1238     check!(fs::remove_file(&path));
1239 }
1240 
1241 #[test]
mkdir_trailing_slash()1242 fn mkdir_trailing_slash() {
1243     let tmpdir = tmpdir();
1244     let path = tmpdir.join("file");
1245     check!(fs::create_dir_all(&path.join("a/")));
1246 }
1247 
1248 #[test]
canonicalize_works_simple()1249 fn canonicalize_works_simple() {
1250     let tmpdir = tmpdir();
1251     let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1252     let file = tmpdir.join("test");
1253     File::create(&file).unwrap();
1254     assert_eq!(fs::canonicalize(&file).unwrap(), file);
1255 }
1256 
1257 #[test]
realpath_works()1258 fn realpath_works() {
1259     let tmpdir = tmpdir();
1260     if !got_symlink_permission(&tmpdir) {
1261         return;
1262     };
1263 
1264     let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1265     let file = tmpdir.join("test");
1266     let dir = tmpdir.join("test2");
1267     let link = dir.join("link");
1268     let linkdir = tmpdir.join("test3");
1269 
1270     File::create(&file).unwrap();
1271     fs::create_dir(&dir).unwrap();
1272     symlink_file(&file, &link).unwrap();
1273     symlink_dir(&dir, &linkdir).unwrap();
1274 
1275     assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
1276 
1277     assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir);
1278     assert_eq!(fs::canonicalize(&file).unwrap(), file);
1279     assert_eq!(fs::canonicalize(&link).unwrap(), file);
1280     assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir);
1281     assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file);
1282 }
1283 
1284 #[test]
realpath_works_tricky()1285 fn realpath_works_tricky() {
1286     let tmpdir = tmpdir();
1287     if !got_symlink_permission(&tmpdir) {
1288         return;
1289     };
1290 
1291     let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1292     let a = tmpdir.join("a");
1293     let b = a.join("b");
1294     let c = b.join("c");
1295     let d = a.join("d");
1296     let e = d.join("e");
1297     let f = a.join("f");
1298 
1299     fs::create_dir_all(&b).unwrap();
1300     fs::create_dir_all(&d).unwrap();
1301     File::create(&f).unwrap();
1302     if cfg!(not(windows)) {
1303         symlink_file("../d/e", &c).unwrap();
1304         symlink_file("../f", &e).unwrap();
1305     }
1306     if cfg!(windows) {
1307         symlink_file(r"..\d\e", &c).unwrap();
1308         symlink_file(r"..\f", &e).unwrap();
1309     }
1310 
1311     assert_eq!(fs::canonicalize(&c).unwrap(), f);
1312     assert_eq!(fs::canonicalize(&e).unwrap(), f);
1313 }
1314 
1315 #[test]
dir_entry_methods()1316 fn dir_entry_methods() {
1317     let tmpdir = tmpdir();
1318 
1319     fs::create_dir_all(&tmpdir.join("a")).unwrap();
1320     File::create(&tmpdir.join("b")).unwrap();
1321 
1322     for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
1323         let fname = file.file_name();
1324         match fname.to_str() {
1325             Some("a") => {
1326                 assert!(file.file_type().unwrap().is_dir());
1327                 assert!(file.metadata().unwrap().is_dir());
1328             }
1329             Some("b") => {
1330                 assert!(file.file_type().unwrap().is_file());
1331                 assert!(file.metadata().unwrap().is_file());
1332             }
1333             f => panic!("unknown file name: {:?}", f),
1334         }
1335     }
1336 }
1337 
1338 #[test]
dir_entry_debug()1339 fn dir_entry_debug() {
1340     let tmpdir = tmpdir();
1341     File::create(&tmpdir.join("b")).unwrap();
1342     let mut read_dir = tmpdir.path().read_dir().unwrap();
1343     let dir_entry = read_dir.next().unwrap().unwrap();
1344     let actual = format!("{:?}", dir_entry);
1345     let expected = format!("DirEntry({:?})", dir_entry.0.path());
1346     assert_eq!(actual, expected);
1347 }
1348 
1349 #[test]
read_dir_not_found()1350 fn read_dir_not_found() {
1351     let res = fs::read_dir("/path/that/does/not/exist");
1352     assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1353 }
1354 
1355 #[test]
create_dir_all_with_junctions()1356 fn create_dir_all_with_junctions() {
1357     let tmpdir = tmpdir();
1358     let target = tmpdir.join("target");
1359 
1360     let junction = tmpdir.join("junction");
1361     let b = junction.join("a/b");
1362 
1363     let link = tmpdir.join("link");
1364     let d = link.join("c/d");
1365 
1366     fs::create_dir(&target).unwrap();
1367 
1368     check!(symlink_junction(&target, &junction));
1369     check!(fs::create_dir_all(&b));
1370     // the junction itself is not a directory, but `is_dir()` on a Path
1371     // follows links
1372     assert!(junction.is_dir());
1373     assert!(b.exists());
1374 
1375     if !got_symlink_permission(&tmpdir) {
1376         return;
1377     };
1378     check!(symlink_dir(&target, &link));
1379     check!(fs::create_dir_all(&d));
1380     assert!(link.is_dir());
1381     assert!(d.exists());
1382 }
1383 
1384 #[test]
metadata_access_times()1385 fn metadata_access_times() {
1386     let tmpdir = tmpdir();
1387 
1388     let b = tmpdir.join("b");
1389     File::create(&b).unwrap();
1390 
1391     let a = check!(fs::metadata(&tmpdir.path()));
1392     let b = check!(fs::metadata(&b));
1393 
1394     assert_eq!(check!(a.accessed()), check!(a.accessed()));
1395     assert_eq!(check!(a.modified()), check!(a.modified()));
1396     assert_eq!(check!(b.accessed()), check!(b.modified()));
1397 
1398     if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1399         check!(a.created());
1400         check!(b.created());
1401     }
1402 
1403     if cfg!(target_os = "linux") {
1404         // Not always available
1405         match (a.created(), b.created()) {
1406             (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1407             (Err(e1), Err(e2))
1408                 if e1.kind() == ErrorKind::Uncategorized
1409                     && e2.kind() == ErrorKind::Uncategorized
1410                     || e1.kind() == ErrorKind::Unsupported
1411                         && e2.kind() == ErrorKind::Unsupported => {}
1412             (a, b) => {
1413                 panic!("creation time must be always supported or not supported: {:?} {:?}", a, b,)
1414             }
1415         }
1416     }
1417 }
1418 
1419 /// Test creating hard links to symlinks.
1420 #[test]
symlink_hard_link()1421 fn symlink_hard_link() {
1422     let tmpdir = tmpdir();
1423     if !got_symlink_permission(&tmpdir) {
1424         return;
1425     };
1426     if !able_to_not_follow_symlinks_while_hard_linking() {
1427         return;
1428     }
1429 
1430     // Create "file", a file.
1431     check!(fs::File::create(tmpdir.join("file")));
1432 
1433     // Create "symlink", a symlink to "file".
1434     check!(symlink_file("file", tmpdir.join("symlink")));
1435 
1436     // Create "hard_link", a hard link to "symlink".
1437     check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
1438 
1439     // "hard_link" should appear as a symlink.
1440     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1441 
1442     // We sould be able to open "file" via any of the above names.
1443     let _ = check!(fs::File::open(tmpdir.join("file")));
1444     assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1445     let _ = check!(fs::File::open(tmpdir.join("symlink")));
1446     let _ = check!(fs::File::open(tmpdir.join("hard_link")));
1447 
1448     // Rename "file" to "file.renamed".
1449     check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
1450 
1451     // Now, the symlink and the hard link should be dangling.
1452     assert!(fs::File::open(tmpdir.join("file")).is_err());
1453     let _ = check!(fs::File::open(tmpdir.join("file.renamed")));
1454     assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1455     assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1456 
1457     // The symlink and the hard link should both still point to "file".
1458     assert!(fs::read_link(tmpdir.join("file")).is_err());
1459     assert!(fs::read_link(tmpdir.join("file.renamed")).is_err());
1460     assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file"));
1461     assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file"));
1462 
1463     // Remove "file.renamed".
1464     check!(fs::remove_file(tmpdir.join("file.renamed")));
1465 
1466     // Now, we can't open the file by any name.
1467     assert!(fs::File::open(tmpdir.join("file")).is_err());
1468     assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1469     assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1470     assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1471 
1472     // "hard_link" should still appear as a symlink.
1473     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1474 }
1475 
1476 /// Ensure `fs::create_dir` works on Windows with longer paths.
1477 #[test]
1478 #[cfg(windows)]
create_dir_long_paths()1479 fn create_dir_long_paths() {
1480     use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1481     const PATH_LEN: usize = 247;
1482 
1483     let tmpdir = tmpdir();
1484     let mut path = tmpdir.path().to_path_buf();
1485     path.push("a");
1486     let mut path = path.into_os_string();
1487 
1488     let utf16_len = path.encode_wide().count();
1489     if utf16_len >= PATH_LEN {
1490         // Skip the test in the unlikely event the local user has a long temp directory path.
1491         // This should not affect CI.
1492         return;
1493     }
1494     // Increase the length of the path.
1495     path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1496 
1497     // This should succeed.
1498     fs::create_dir(&path).unwrap();
1499 
1500     // This will fail if the path isn't converted to verbatim.
1501     path.push("a");
1502     fs::create_dir(&path).unwrap();
1503 
1504     // #90940: Ensure an empty path returns the "Not Found" error.
1505     let path = Path::new("");
1506     assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound);
1507 }
1508