• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::io::prelude::*;
2 
3 use crate::env;
4 use crate::fs::{self, File, FileTimes, OpenOptions};
5 use crate::io::{BorrowedBuf, ErrorKind, SeekFrom};
6 use crate::mem::MaybeUninit;
7 use crate::path::Path;
8 use crate::str;
9 use crate::sync::Arc;
10 use crate::sys_common::io::test::{tmpdir, TempDir};
11 use crate::thread;
12 use crate::time::{Duration, Instant, SystemTime};
13 
14 use rand::RngCore;
15 
16 #[cfg(target_os = "macos")]
17 use crate::ffi::{c_char, c_int};
18 #[cfg(unix)]
19 use crate::os::unix::fs::symlink as symlink_dir;
20 #[cfg(unix)]
21 use crate::os::unix::fs::symlink as symlink_file;
22 #[cfg(unix)]
23 use crate::os::unix::fs::symlink as symlink_junction;
24 #[cfg(windows)]
25 use crate::os::windows::fs::{symlink_dir, symlink_file};
26 #[cfg(windows)]
27 use crate::sys::fs::symlink_junction;
28 #[cfg(target_os = "macos")]
29 use crate::sys::weak::weak;
30 
31 macro_rules! check {
32     ($e:expr) => {
33         match $e {
34             Ok(t) => t,
35             Err(e) => panic!("{} failed with: {e}", stringify!($e)),
36         }
37     };
38 }
39 
40 #[cfg(windows)]
41 macro_rules! error {
42     ($e:expr, $s:expr) => {
43         match $e {
44             Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
45             Err(ref err) => {
46                 assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s)
47             }
48         }
49     };
50 }
51 
52 #[cfg(unix)]
53 macro_rules! error {
54     ($e:expr, $s:expr) => {
55         error_contains!($e, $s)
56     };
57 }
58 
59 macro_rules! error_contains {
60     ($e:expr, $s:expr) => {
61         match $e {
62             Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
63             Err(ref err) => {
64                 assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s)
65             }
66         }
67     };
68 }
69 
70 // Several test fail on windows if the user does not have permission to
71 // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
72 // disabling these test on Windows, use this function to test whether we
73 // have permission, and return otherwise. This way, we still don't run these
74 // tests most of the time, but at least we do if the user has the right
75 // permissions.
got_symlink_permission(tmpdir: &TempDir) -> bool76 pub fn got_symlink_permission(tmpdir: &TempDir) -> bool {
77     if cfg!(unix) {
78         return true;
79     }
80     let link = tmpdir.join("some_hopefully_unique_link_name");
81 
82     match symlink_file(r"nonexisting_target", link) {
83         // ERROR_PRIVILEGE_NOT_HELD = 1314
84         Err(ref err) if err.raw_os_error() == Some(1314) => false,
85         Ok(_) | Err(_) => true,
86     }
87 }
88 
89 #[cfg(target_os = "macos")]
able_to_not_follow_symlinks_while_hard_linking() -> bool90 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
91     weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
92     linkat.get().is_some()
93 }
94 
95 #[cfg(not(target_os = "macos"))]
able_to_not_follow_symlinks_while_hard_linking() -> bool96 fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
97     return true;
98 }
99 
100 #[test]
file_test_io_smoke_test()101 fn file_test_io_smoke_test() {
102     let message = "it's alright. have a good time";
103     let tmpdir = tmpdir();
104     let filename = &tmpdir.join("file_rt_io_file_test.txt");
105     {
106         let mut write_stream = check!(File::create(filename));
107         check!(write_stream.write(message.as_bytes()));
108     }
109     {
110         let mut read_stream = check!(File::open(filename));
111         let mut read_buf = [0; 1028];
112         let read_str = match check!(read_stream.read(&mut read_buf)) {
113             0 => panic!("shouldn't happen"),
114             n => str::from_utf8(&read_buf[..n]).unwrap().to_string(),
115         };
116         assert_eq!(read_str, message);
117     }
118     check!(fs::remove_file(filename));
119 }
120 
121 #[test]
invalid_path_raises()122 fn invalid_path_raises() {
123     let tmpdir = tmpdir();
124     let filename = &tmpdir.join("file_that_does_not_exist.txt");
125     let result = File::open(filename);
126 
127     #[cfg(all(unix, not(target_os = "vxworks")))]
128     error!(result, "No such file or directory");
129     #[cfg(target_os = "vxworks")]
130     error!(result, "no such file or directory");
131     #[cfg(windows)]
132     error!(result, 2); // ERROR_FILE_NOT_FOUND
133 }
134 
135 #[test]
file_test_iounlinking_invalid_path_should_raise_condition()136 fn file_test_iounlinking_invalid_path_should_raise_condition() {
137     let tmpdir = tmpdir();
138     let filename = &tmpdir.join("file_another_file_that_does_not_exist.txt");
139 
140     let result = fs::remove_file(filename);
141 
142     #[cfg(all(unix, not(target_os = "vxworks")))]
143     error!(result, "No such file or directory");
144     #[cfg(target_os = "vxworks")]
145     error!(result, "no such file or directory");
146     #[cfg(windows)]
147     error!(result, 2); // ERROR_FILE_NOT_FOUND
148 }
149 
150 #[test]
file_test_io_non_positional_read()151 fn file_test_io_non_positional_read() {
152     let message: &str = "ten-four";
153     let mut read_mem = [0; 8];
154     let tmpdir = tmpdir();
155     let filename = &tmpdir.join("file_rt_io_file_test_positional.txt");
156     {
157         let mut rw_stream = check!(File::create(filename));
158         check!(rw_stream.write(message.as_bytes()));
159     }
160     {
161         let mut read_stream = check!(File::open(filename));
162         {
163             let read_buf = &mut read_mem[0..4];
164             check!(read_stream.read(read_buf));
165         }
166         {
167             let read_buf = &mut read_mem[4..8];
168             check!(read_stream.read(read_buf));
169         }
170     }
171     check!(fs::remove_file(filename));
172     let read_str = str::from_utf8(&read_mem).unwrap();
173     assert_eq!(read_str, message);
174 }
175 
176 #[test]
file_test_io_seek_and_tell_smoke_test()177 fn file_test_io_seek_and_tell_smoke_test() {
178     let message = "ten-four";
179     let mut read_mem = [0; 4];
180     let set_cursor = 4 as u64;
181     let tell_pos_pre_read;
182     let tell_pos_post_read;
183     let tmpdir = tmpdir();
184     let filename = &tmpdir.join("file_rt_io_file_test_seeking.txt");
185     {
186         let mut rw_stream = check!(File::create(filename));
187         check!(rw_stream.write(message.as_bytes()));
188     }
189     {
190         let mut read_stream = check!(File::open(filename));
191         check!(read_stream.seek(SeekFrom::Start(set_cursor)));
192         tell_pos_pre_read = check!(read_stream.seek(SeekFrom::Current(0)));
193         check!(read_stream.read(&mut read_mem));
194         tell_pos_post_read = check!(read_stream.seek(SeekFrom::Current(0)));
195     }
196     check!(fs::remove_file(filename));
197     let read_str = str::from_utf8(&read_mem).unwrap();
198     assert_eq!(read_str, &message[4..8]);
199     assert_eq!(tell_pos_pre_read, set_cursor);
200     assert_eq!(tell_pos_post_read, message.len() as u64);
201 }
202 
203 #[test]
file_test_io_seek_and_write()204 fn file_test_io_seek_and_write() {
205     let initial_msg = "food-is-yummy";
206     let overwrite_msg = "-the-bar!!";
207     let final_msg = "foo-the-bar!!";
208     let seek_idx = 3;
209     let mut read_mem = [0; 13];
210     let tmpdir = tmpdir();
211     let filename = &tmpdir.join("file_rt_io_file_test_seek_and_write.txt");
212     {
213         let mut rw_stream = check!(File::create(filename));
214         check!(rw_stream.write(initial_msg.as_bytes()));
215         check!(rw_stream.seek(SeekFrom::Start(seek_idx)));
216         check!(rw_stream.write(overwrite_msg.as_bytes()));
217     }
218     {
219         let mut read_stream = check!(File::open(filename));
220         check!(read_stream.read(&mut read_mem));
221     }
222     check!(fs::remove_file(filename));
223     let read_str = str::from_utf8(&read_mem).unwrap();
224     assert!(read_str == final_msg);
225 }
226 
227 #[test]
file_test_io_seek_shakedown()228 fn file_test_io_seek_shakedown() {
229     //                   01234567890123
230     let initial_msg = "qwer-asdf-zxcv";
231     let chunk_one: &str = "qwer";
232     let chunk_two: &str = "asdf";
233     let chunk_three: &str = "zxcv";
234     let mut read_mem = [0; 4];
235     let tmpdir = tmpdir();
236     let filename = &tmpdir.join("file_rt_io_file_test_seek_shakedown.txt");
237     {
238         let mut rw_stream = check!(File::create(filename));
239         check!(rw_stream.write(initial_msg.as_bytes()));
240     }
241     {
242         let mut read_stream = check!(File::open(filename));
243 
244         check!(read_stream.seek(SeekFrom::End(-4)));
245         check!(read_stream.read(&mut read_mem));
246         assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_three);
247 
248         check!(read_stream.seek(SeekFrom::Current(-9)));
249         check!(read_stream.read(&mut read_mem));
250         assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_two);
251 
252         check!(read_stream.seek(SeekFrom::Start(0)));
253         check!(read_stream.read(&mut read_mem));
254         assert_eq!(str::from_utf8(&read_mem).unwrap(), chunk_one);
255     }
256     check!(fs::remove_file(filename));
257 }
258 
259 #[test]
file_test_io_eof()260 fn file_test_io_eof() {
261     let tmpdir = tmpdir();
262     let filename = tmpdir.join("file_rt_io_file_test_eof.txt");
263     let mut buf = [0; 256];
264     {
265         let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
266         let mut rw = check!(oo.open(&filename));
267         assert_eq!(check!(rw.read(&mut buf)), 0);
268         assert_eq!(check!(rw.read(&mut buf)), 0);
269     }
270     check!(fs::remove_file(&filename));
271 }
272 
273 #[test]
274 #[cfg(unix)]
file_test_io_read_write_at()275 fn file_test_io_read_write_at() {
276     use crate::os::unix::fs::FileExt;
277 
278     let tmpdir = tmpdir();
279     let filename = tmpdir.join("file_rt_io_file_test_read_write_at.txt");
280     let mut buf = [0; 256];
281     let write1 = "asdf";
282     let write2 = "qwer-";
283     let write3 = "-zxcv";
284     let content = "qwer-asdf-zxcv";
285     {
286         let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
287         let mut rw = check!(oo.open(&filename));
288         assert_eq!(check!(rw.write_at(write1.as_bytes(), 5)), write1.len());
289         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
290         assert_eq!(check!(rw.read_at(&mut buf, 5)), write1.len());
291         assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
292         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
293         assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
294         assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok("\0\0\0\0\0"));
295         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 0);
296         assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
297         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
298         assert_eq!(check!(rw.read(&mut buf)), write1.len());
299         assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
300         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
301         assert_eq!(check!(rw.read_at(&mut buf[..write2.len()], 0)), write2.len());
302         assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
303         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
304         assert_eq!(check!(rw.write_at(write3.as_bytes(), 9)), write3.len());
305         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
306     }
307     {
308         let mut read = check!(File::open(&filename));
309         assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
310         assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
311         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 0);
312         assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
313         assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
314         assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
315         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 9);
316         assert_eq!(check!(read.read(&mut buf)), write3.len());
317         assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
318         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
319         assert_eq!(check!(read.read_at(&mut buf, 0)), content.len());
320         assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
321         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
322         assert_eq!(check!(read.read_at(&mut buf, 14)), 0);
323         assert_eq!(check!(read.read_at(&mut buf, 15)), 0);
324         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
325     }
326     check!(fs::remove_file(&filename));
327 }
328 
329 #[test]
330 #[cfg(unix)]
set_get_unix_permissions()331 fn set_get_unix_permissions() {
332     use crate::os::unix::fs::PermissionsExt;
333 
334     let tmpdir = tmpdir();
335     let filename = &tmpdir.join("set_get_unix_permissions");
336     check!(fs::create_dir(filename));
337     let mask = 0o7777;
338 
339     check!(fs::set_permissions(filename, fs::Permissions::from_mode(0)));
340     let metadata0 = check!(fs::metadata(filename));
341     assert_eq!(mask & metadata0.permissions().mode(), 0);
342 
343     check!(fs::set_permissions(filename, fs::Permissions::from_mode(0o1777)));
344     let metadata1 = check!(fs::metadata(filename));
345     #[cfg(all(unix, not(target_os = "vxworks")))]
346     assert_eq!(mask & metadata1.permissions().mode(), 0o1777);
347     #[cfg(target_os = "vxworks")]
348     assert_eq!(mask & metadata1.permissions().mode(), 0o0777);
349 }
350 
351 #[test]
352 #[cfg(windows)]
file_test_io_seek_read_write()353 fn file_test_io_seek_read_write() {
354     use crate::os::windows::fs::FileExt;
355 
356     let tmpdir = tmpdir();
357     let filename = tmpdir.join("file_rt_io_file_test_seek_read_write.txt");
358     let mut buf = [0; 256];
359     let write1 = "asdf";
360     let write2 = "qwer-";
361     let write3 = "-zxcv";
362     let content = "qwer-asdf-zxcv";
363     {
364         let oo = OpenOptions::new().create_new(true).write(true).read(true).clone();
365         let mut rw = check!(oo.open(&filename));
366         assert_eq!(check!(rw.seek_write(write1.as_bytes(), 5)), write1.len());
367         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
368         assert_eq!(check!(rw.seek_read(&mut buf, 5)), write1.len());
369         assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
370         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
371         assert_eq!(check!(rw.seek(SeekFrom::Start(0))), 0);
372         assert_eq!(check!(rw.write(write2.as_bytes())), write2.len());
373         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
374         assert_eq!(check!(rw.read(&mut buf)), write1.len());
375         assert_eq!(str::from_utf8(&buf[..write1.len()]), Ok(write1));
376         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 9);
377         assert_eq!(check!(rw.seek_read(&mut buf[..write2.len()], 0)), write2.len());
378         assert_eq!(str::from_utf8(&buf[..write2.len()]), Ok(write2));
379         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 5);
380         assert_eq!(check!(rw.seek_write(write3.as_bytes(), 9)), write3.len());
381         assert_eq!(check!(rw.seek(SeekFrom::Current(0))), 14);
382     }
383     {
384         let mut read = check!(File::open(&filename));
385         assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
386         assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
387         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
388         assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
389         assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
390         assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
391         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
392         assert_eq!(check!(read.seek(SeekFrom::End(-5))), 9);
393         assert_eq!(check!(read.read(&mut buf)), write3.len());
394         assert_eq!(str::from_utf8(&buf[..write3.len()]), Ok(write3));
395         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
396         assert_eq!(check!(read.seek_read(&mut buf, 0)), content.len());
397         assert_eq!(str::from_utf8(&buf[..content.len()]), Ok(content));
398         assert_eq!(check!(read.seek(SeekFrom::Current(0))), 14);
399         assert_eq!(check!(read.seek_read(&mut buf, 14)), 0);
400         assert_eq!(check!(read.seek_read(&mut buf, 15)), 0);
401     }
402     check!(fs::remove_file(&filename));
403 }
404 
405 #[test]
file_test_read_buf()406 fn file_test_read_buf() {
407     let tmpdir = tmpdir();
408     let filename = &tmpdir.join("test");
409     check!(fs::write(filename, &[1, 2, 3, 4]));
410 
411     let mut buf: [MaybeUninit<u8>; 128] = MaybeUninit::uninit_array();
412     let mut buf = BorrowedBuf::from(buf.as_mut_slice());
413     let mut file = check!(File::open(filename));
414     check!(file.read_buf(buf.unfilled()));
415     assert_eq!(buf.filled(), &[1, 2, 3, 4]);
416     // File::read_buf should omit buffer initialization.
417     assert_eq!(buf.init_len(), 4);
418 
419     check!(fs::remove_file(filename));
420 }
421 
422 #[test]
file_test_stat_is_correct_on_is_file()423 fn file_test_stat_is_correct_on_is_file() {
424     let tmpdir = tmpdir();
425     let filename = &tmpdir.join("file_stat_correct_on_is_file.txt");
426     {
427         let mut opts = OpenOptions::new();
428         let mut fs = check!(opts.read(true).write(true).create(true).open(filename));
429         let msg = "hw";
430         fs.write(msg.as_bytes()).unwrap();
431 
432         let fstat_res = check!(fs.metadata());
433         assert!(fstat_res.is_file());
434     }
435     let stat_res_fn = check!(fs::metadata(filename));
436     assert!(stat_res_fn.is_file());
437     let stat_res_meth = check!(filename.metadata());
438     assert!(stat_res_meth.is_file());
439     check!(fs::remove_file(filename));
440 }
441 
442 #[test]
file_test_stat_is_correct_on_is_dir()443 fn file_test_stat_is_correct_on_is_dir() {
444     let tmpdir = tmpdir();
445     let filename = &tmpdir.join("file_stat_correct_on_is_dir");
446     check!(fs::create_dir(filename));
447     let stat_res_fn = check!(fs::metadata(filename));
448     assert!(stat_res_fn.is_dir());
449     let stat_res_meth = check!(filename.metadata());
450     assert!(stat_res_meth.is_dir());
451     check!(fs::remove_dir(filename));
452 }
453 
454 #[test]
file_test_fileinfo_false_when_checking_is_file_on_a_directory()455 fn file_test_fileinfo_false_when_checking_is_file_on_a_directory() {
456     let tmpdir = tmpdir();
457     let dir = &tmpdir.join("fileinfo_false_on_dir");
458     check!(fs::create_dir(dir));
459     assert!(!dir.is_file());
460     check!(fs::remove_dir(dir));
461 }
462 
463 #[test]
file_test_fileinfo_check_exists_before_and_after_file_creation()464 fn file_test_fileinfo_check_exists_before_and_after_file_creation() {
465     let tmpdir = tmpdir();
466     let file = &tmpdir.join("fileinfo_check_exists_b_and_a.txt");
467     check!(check!(File::create(file)).write(b"foo"));
468     assert!(file.exists());
469     check!(fs::remove_file(file));
470     assert!(!file.exists());
471 }
472 
473 #[test]
file_test_directoryinfo_check_exists_before_and_after_mkdir()474 fn file_test_directoryinfo_check_exists_before_and_after_mkdir() {
475     let tmpdir = tmpdir();
476     let dir = &tmpdir.join("before_and_after_dir");
477     assert!(!dir.exists());
478     check!(fs::create_dir(dir));
479     assert!(dir.exists());
480     assert!(dir.is_dir());
481     check!(fs::remove_dir(dir));
482     assert!(!dir.exists());
483 }
484 
485 #[test]
file_test_directoryinfo_readdir()486 fn file_test_directoryinfo_readdir() {
487     let tmpdir = tmpdir();
488     let dir = &tmpdir.join("di_readdir");
489     check!(fs::create_dir(dir));
490     let prefix = "foo";
491     for n in 0..3 {
492         let f = dir.join(&format!("{n}.txt"));
493         let mut w = check!(File::create(&f));
494         let msg_str = format!("{}{}", prefix, n.to_string());
495         let msg = msg_str.as_bytes();
496         check!(w.write(msg));
497     }
498     let files = check!(fs::read_dir(dir));
499     let mut mem = [0; 4];
500     for f in files {
501         let f = f.unwrap().path();
502         {
503             let n = f.file_stem().unwrap();
504             check!(check!(File::open(&f)).read(&mut mem));
505             let read_str = str::from_utf8(&mem).unwrap();
506             let expected = format!("{}{}", prefix, n.to_str().unwrap());
507             assert_eq!(expected, read_str);
508         }
509         check!(fs::remove_file(&f));
510     }
511     check!(fs::remove_dir(dir));
512 }
513 
514 #[test]
file_create_new_already_exists_error()515 fn file_create_new_already_exists_error() {
516     let tmpdir = tmpdir();
517     let file = &tmpdir.join("file_create_new_error_exists");
518     check!(fs::File::create(file));
519     let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err();
520     assert_eq!(e.kind(), ErrorKind::AlreadyExists);
521 }
522 
523 #[test]
mkdir_path_already_exists_error()524 fn mkdir_path_already_exists_error() {
525     let tmpdir = tmpdir();
526     let dir = &tmpdir.join("mkdir_error_twice");
527     check!(fs::create_dir(dir));
528     let e = fs::create_dir(dir).unwrap_err();
529     assert_eq!(e.kind(), ErrorKind::AlreadyExists);
530 }
531 
532 #[test]
recursive_mkdir()533 fn recursive_mkdir() {
534     let tmpdir = tmpdir();
535     let dir = tmpdir.join("d1/d2");
536     check!(fs::create_dir_all(&dir));
537     assert!(dir.is_dir())
538 }
539 
540 #[test]
recursive_mkdir_failure()541 fn recursive_mkdir_failure() {
542     let tmpdir = tmpdir();
543     let dir = tmpdir.join("d1");
544     let file = dir.join("f1");
545 
546     check!(fs::create_dir_all(&dir));
547     check!(File::create(&file));
548 
549     let result = fs::create_dir_all(&file);
550 
551     assert!(result.is_err());
552 }
553 
554 #[test]
concurrent_recursive_mkdir()555 fn concurrent_recursive_mkdir() {
556     for _ in 0..100 {
557         let dir = tmpdir();
558         let mut dir = dir.join("a");
559         for _ in 0..40 {
560             dir = dir.join("a");
561         }
562         let mut join = vec![];
563         for _ in 0..8 {
564             let dir = dir.clone();
565             join.push(thread::spawn(move || {
566                 check!(fs::create_dir_all(&dir));
567             }))
568         }
569 
570         // No `Display` on result of `join()`
571         join.drain(..).map(|join| join.join().unwrap()).count();
572     }
573 }
574 
575 #[test]
recursive_mkdir_slash()576 fn recursive_mkdir_slash() {
577     check!(fs::create_dir_all(Path::new("/")));
578 }
579 
580 #[test]
recursive_mkdir_dot()581 fn recursive_mkdir_dot() {
582     check!(fs::create_dir_all(Path::new(".")));
583 }
584 
585 #[test]
recursive_mkdir_empty()586 fn recursive_mkdir_empty() {
587     check!(fs::create_dir_all(Path::new("")));
588 }
589 
590 #[test]
recursive_rmdir()591 fn recursive_rmdir() {
592     let tmpdir = tmpdir();
593     let d1 = tmpdir.join("d1");
594     let dt = d1.join("t");
595     let dtt = dt.join("t");
596     let d2 = tmpdir.join("d2");
597     let canary = d2.join("do_not_delete");
598     check!(fs::create_dir_all(&dtt));
599     check!(fs::create_dir_all(&d2));
600     check!(check!(File::create(&canary)).write(b"foo"));
601     check!(symlink_junction(&d2, &dt.join("d2")));
602     let _ = symlink_file(&canary, &d1.join("canary"));
603     check!(fs::remove_dir_all(&d1));
604 
605     assert!(!d1.is_dir());
606     assert!(canary.exists());
607 }
608 
609 #[test]
recursive_rmdir_of_symlink()610 fn recursive_rmdir_of_symlink() {
611     // test we do not recursively delete a symlink but only dirs.
612     let tmpdir = tmpdir();
613     let link = tmpdir.join("d1");
614     let dir = tmpdir.join("d2");
615     let canary = dir.join("do_not_delete");
616     check!(fs::create_dir_all(&dir));
617     check!(check!(File::create(&canary)).write(b"foo"));
618     check!(symlink_junction(&dir, &link));
619     check!(fs::remove_dir_all(&link));
620 
621     assert!(!link.is_dir());
622     assert!(canary.exists());
623 }
624 
625 #[test]
recursive_rmdir_of_file_fails()626 fn recursive_rmdir_of_file_fails() {
627     // test we do not delete a directly specified file.
628     let tmpdir = tmpdir();
629     let canary = tmpdir.join("do_not_delete");
630     check!(check!(File::create(&canary)).write(b"foo"));
631     let result = fs::remove_dir_all(&canary);
632     #[cfg(unix)]
633     error!(result, "Not a directory");
634     #[cfg(windows)]
635     error!(result, 267); // ERROR_DIRECTORY - The directory name is invalid.
636     assert!(result.is_err());
637     assert!(canary.exists());
638 }
639 
640 #[test]
641 // only Windows makes a distinction between file and directory symlinks.
642 #[cfg(windows)]
recursive_rmdir_of_file_symlink()643 fn recursive_rmdir_of_file_symlink() {
644     let tmpdir = tmpdir();
645     if !got_symlink_permission(&tmpdir) {
646         return;
647     };
648 
649     let f1 = tmpdir.join("f1");
650     let f2 = tmpdir.join("f2");
651     check!(check!(File::create(&f1)).write(b"foo"));
652     check!(symlink_file(&f1, &f2));
653     match fs::remove_dir_all(&f2) {
654         Ok(..) => panic!("wanted a failure"),
655         Err(..) => {}
656     }
657 }
658 
659 #[test]
660 #[ignore] // takes too much time
recursive_rmdir_toctou()661 fn recursive_rmdir_toctou() {
662     // Test for time-of-check to time-of-use issues.
663     //
664     // Scenario:
665     // The attacker wants to get directory contents deleted, to which they do not have access.
666     // They have a way to get a privileged Rust binary call `std::fs::remove_dir_all()` on a
667     // directory they control, e.g. in their home directory.
668     //
669     // The POC sets up the `attack_dest/attack_file` which the attacker wants to have deleted.
670     // The attacker repeatedly creates a directory and replaces it with a symlink from
671     // `victim_del` to `attack_dest` while the victim code calls `std::fs::remove_dir_all()`
672     // on `victim_del`. After a few seconds the attack has succeeded and
673     // `attack_dest/attack_file` is deleted.
674     let tmpdir = tmpdir();
675     let victim_del_path = tmpdir.join("victim_del");
676     let victim_del_path_clone = victim_del_path.clone();
677 
678     // setup dest
679     let attack_dest_dir = tmpdir.join("attack_dest");
680     let attack_dest_dir = attack_dest_dir.as_path();
681     fs::create_dir(attack_dest_dir).unwrap();
682     let attack_dest_file = tmpdir.join("attack_dest/attack_file");
683     File::create(&attack_dest_file).unwrap();
684 
685     let drop_canary_arc = Arc::new(());
686     let drop_canary_weak = Arc::downgrade(&drop_canary_arc);
687 
688     eprintln!("x: {:?}", &victim_del_path);
689 
690     // victim just continuously removes `victim_del`
691     thread::spawn(move || {
692         while drop_canary_weak.upgrade().is_some() {
693             let _ = fs::remove_dir_all(&victim_del_path_clone);
694         }
695     });
696 
697     // attacker (could of course be in a separate process)
698     let start_time = Instant::now();
699     while Instant::now().duration_since(start_time) < Duration::from_secs(1000) {
700         if !attack_dest_file.exists() {
701             panic!(
702                 "Victim deleted symlinked file outside of victim_del. Attack succeeded in {:?}.",
703                 Instant::now().duration_since(start_time)
704             );
705         }
706         let _ = fs::create_dir(&victim_del_path);
707         let _ = fs::remove_dir(&victim_del_path);
708         let _ = symlink_dir(attack_dest_dir, &victim_del_path);
709     }
710 }
711 
712 #[test]
unicode_path_is_dir()713 fn unicode_path_is_dir() {
714     assert!(Path::new(".").is_dir());
715     assert!(!Path::new("test/stdtest/fs.rs").is_dir());
716 
717     let tmpdir = tmpdir();
718 
719     let mut dirpath = tmpdir.path().to_path_buf();
720     dirpath.push("test-가一ー你好");
721     check!(fs::create_dir(&dirpath));
722     assert!(dirpath.is_dir());
723 
724     let mut filepath = dirpath;
725     filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs");
726     check!(File::create(&filepath)); // ignore return; touch only
727     assert!(!filepath.is_dir());
728     assert!(filepath.exists());
729 }
730 
731 #[test]
unicode_path_exists()732 fn unicode_path_exists() {
733     assert!(Path::new(".").exists());
734     assert!(!Path::new("test/nonexistent-bogus-path").exists());
735 
736     let tmpdir = tmpdir();
737     let unicode = tmpdir.path();
738     let unicode = unicode.join("test-각丁ー再见");
739     check!(fs::create_dir(&unicode));
740     assert!(unicode.exists());
741     assert!(!Path::new("test/unicode-bogus-path-각丁ー再见").exists());
742 }
743 
744 #[test]
copy_file_does_not_exist()745 fn copy_file_does_not_exist() {
746     let from = Path::new("test/nonexistent-bogus-path");
747     let to = Path::new("test/other-bogus-path");
748 
749     match fs::copy(&from, &to) {
750         Ok(..) => panic!(),
751         Err(..) => {
752             assert!(!from.exists());
753             assert!(!to.exists());
754         }
755     }
756 }
757 
758 #[test]
copy_src_does_not_exist()759 fn copy_src_does_not_exist() {
760     let tmpdir = tmpdir();
761     let from = Path::new("test/nonexistent-bogus-path");
762     let to = tmpdir.join("out.txt");
763     check!(check!(File::create(&to)).write(b"hello"));
764     assert!(fs::copy(&from, &to).is_err());
765     assert!(!from.exists());
766     let mut v = Vec::new();
767     check!(check!(File::open(&to)).read_to_end(&mut v));
768     assert_eq!(v, b"hello");
769 }
770 
771 #[test]
copy_file_ok()772 fn copy_file_ok() {
773     let tmpdir = tmpdir();
774     let input = tmpdir.join("in.txt");
775     let out = tmpdir.join("out.txt");
776 
777     check!(check!(File::create(&input)).write(b"hello"));
778     check!(fs::copy(&input, &out));
779     let mut v = Vec::new();
780     check!(check!(File::open(&out)).read_to_end(&mut v));
781     assert_eq!(v, b"hello");
782 
783     assert_eq!(check!(input.metadata()).permissions(), check!(out.metadata()).permissions());
784 }
785 
786 #[test]
copy_file_dst_dir()787 fn copy_file_dst_dir() {
788     let tmpdir = tmpdir();
789     let out = tmpdir.join("out");
790 
791     check!(File::create(&out));
792     match fs::copy(&*out, tmpdir.path()) {
793         Ok(..) => panic!(),
794         Err(..) => {}
795     }
796 }
797 
798 #[test]
copy_file_dst_exists()799 fn copy_file_dst_exists() {
800     let tmpdir = tmpdir();
801     let input = tmpdir.join("in");
802     let output = tmpdir.join("out");
803 
804     check!(check!(File::create(&input)).write("foo".as_bytes()));
805     check!(check!(File::create(&output)).write("bar".as_bytes()));
806     check!(fs::copy(&input, &output));
807 
808     let mut v = Vec::new();
809     check!(check!(File::open(&output)).read_to_end(&mut v));
810     assert_eq!(v, b"foo".to_vec());
811 }
812 
813 #[test]
copy_file_src_dir()814 fn copy_file_src_dir() {
815     let tmpdir = tmpdir();
816     let out = tmpdir.join("out");
817 
818     match fs::copy(tmpdir.path(), &out) {
819         Ok(..) => panic!(),
820         Err(..) => {}
821     }
822     assert!(!out.exists());
823 }
824 
825 #[test]
copy_file_preserves_perm_bits()826 fn copy_file_preserves_perm_bits() {
827     let tmpdir = tmpdir();
828     let input = tmpdir.join("in.txt");
829     let out = tmpdir.join("out.txt");
830 
831     let attr = check!(check!(File::create(&input)).metadata());
832     let mut p = attr.permissions();
833     p.set_readonly(true);
834     check!(fs::set_permissions(&input, p));
835     check!(fs::copy(&input, &out));
836     assert!(check!(out.metadata()).permissions().readonly());
837     check!(fs::set_permissions(&input, attr.permissions()));
838     check!(fs::set_permissions(&out, attr.permissions()));
839 }
840 
841 #[test]
842 #[cfg(windows)]
copy_file_preserves_streams()843 fn copy_file_preserves_streams() {
844     let tmp = tmpdir();
845     check!(check!(File::create(tmp.join("in.txt:bunny"))).write("carrot".as_bytes()));
846     assert_eq!(check!(fs::copy(tmp.join("in.txt"), tmp.join("out.txt"))), 0);
847     assert_eq!(check!(tmp.join("out.txt").metadata()).len(), 0);
848     let mut v = Vec::new();
849     check!(check!(File::open(tmp.join("out.txt:bunny"))).read_to_end(&mut v));
850     assert_eq!(v, b"carrot".to_vec());
851 }
852 
853 #[test]
copy_file_returns_metadata_len()854 fn copy_file_returns_metadata_len() {
855     let tmp = tmpdir();
856     let in_path = tmp.join("in.txt");
857     let out_path = tmp.join("out.txt");
858     check!(check!(File::create(&in_path)).write(b"lettuce"));
859     #[cfg(windows)]
860     check!(check!(File::create(tmp.join("in.txt:bunny"))).write(b"carrot"));
861     let copied_len = check!(fs::copy(&in_path, &out_path));
862     assert_eq!(check!(out_path.metadata()).len(), copied_len);
863 }
864 
865 #[test]
copy_file_follows_dst_symlink()866 fn copy_file_follows_dst_symlink() {
867     let tmp = tmpdir();
868     if !got_symlink_permission(&tmp) {
869         return;
870     };
871 
872     let in_path = tmp.join("in.txt");
873     let out_path = tmp.join("out.txt");
874     let out_path_symlink = tmp.join("out_symlink.txt");
875 
876     check!(fs::write(&in_path, "foo"));
877     check!(fs::write(&out_path, "bar"));
878     check!(symlink_file(&out_path, &out_path_symlink));
879 
880     check!(fs::copy(&in_path, &out_path_symlink));
881 
882     assert!(check!(out_path_symlink.symlink_metadata()).file_type().is_symlink());
883     assert_eq!(check!(fs::read(&out_path_symlink)), b"foo".to_vec());
884     assert_eq!(check!(fs::read(&out_path)), b"foo".to_vec());
885 }
886 
887 #[test]
symlinks_work()888 fn symlinks_work() {
889     let tmpdir = tmpdir();
890     if !got_symlink_permission(&tmpdir) {
891         return;
892     };
893 
894     let input = tmpdir.join("in.txt");
895     let out = tmpdir.join("out.txt");
896 
897     check!(check!(File::create(&input)).write("foobar".as_bytes()));
898     check!(symlink_file(&input, &out));
899     assert!(check!(out.symlink_metadata()).file_type().is_symlink());
900     assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
901     let mut v = Vec::new();
902     check!(check!(File::open(&out)).read_to_end(&mut v));
903     assert_eq!(v, b"foobar".to_vec());
904 }
905 
906 #[test]
symlink_noexist()907 fn symlink_noexist() {
908     // Symlinks can point to things that don't exist
909     let tmpdir = tmpdir();
910     if !got_symlink_permission(&tmpdir) {
911         return;
912     };
913 
914     // Use a relative path for testing. Symlinks get normalized by Windows,
915     // so we might not get the same path back for absolute paths
916     check!(symlink_file(&"foo", &tmpdir.join("bar")));
917     assert_eq!(check!(fs::read_link(&tmpdir.join("bar"))).to_str().unwrap(), "foo");
918 }
919 
920 #[test]
read_link()921 fn read_link() {
922     let tmpdir = tmpdir();
923     if cfg!(windows) {
924         // directory symlink
925         assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
926         // junction
927         assert_eq!(check!(fs::read_link(r"C:\Users\Default User")), Path::new(r"C:\Users\Default"));
928         // junction with special permissions
929         // Since not all localized windows versions contain the folder "Documents and Settings" in english,
930         // we will briefly check, if it exists and otherwise skip the test. Except during CI we will always execute the test.
931         if Path::new(r"C:\Documents and Settings\").exists() || env::var_os("CI").is_some() {
932             assert_eq!(
933                 check!(fs::read_link(r"C:\Documents and Settings\")),
934                 Path::new(r"C:\Users")
935             );
936         }
937         // Check that readlink works with non-drive paths on Windows.
938         let link = tmpdir.join("link_unc");
939         check!(symlink_dir(r"\\localhost\c$\", &link));
940         assert_eq!(check!(fs::read_link(&link)), Path::new(r"\\localhost\c$\"));
941     }
942     let link = tmpdir.join("link");
943     if !got_symlink_permission(&tmpdir) {
944         return;
945     };
946     check!(symlink_file(&"foo", &link));
947     assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
948 }
949 
950 #[test]
readlink_not_symlink()951 fn readlink_not_symlink() {
952     let tmpdir = tmpdir();
953     match fs::read_link(tmpdir.path()) {
954         Ok(..) => panic!("wanted a failure"),
955         Err(..) => {}
956     }
957 }
958 
959 #[test]
links_work()960 fn links_work() {
961     let tmpdir = tmpdir();
962     let input = tmpdir.join("in.txt");
963     let out = tmpdir.join("out.txt");
964 
965     check!(check!(File::create(&input)).write("foobar".as_bytes()));
966     check!(fs::hard_link(&input, &out));
967     assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
968     assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len());
969     let mut v = Vec::new();
970     check!(check!(File::open(&out)).read_to_end(&mut v));
971     assert_eq!(v, b"foobar".to_vec());
972 
973     // can't link to yourself
974     match fs::hard_link(&input, &input) {
975         Ok(..) => panic!("wanted a failure"),
976         Err(..) => {}
977     }
978     // can't link to something that doesn't exist
979     match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
980         Ok(..) => panic!("wanted a failure"),
981         Err(..) => {}
982     }
983 }
984 
985 #[test]
chmod_works()986 fn chmod_works() {
987     let tmpdir = tmpdir();
988     let file = tmpdir.join("in.txt");
989 
990     check!(File::create(&file));
991     let attr = check!(fs::metadata(&file));
992     assert!(!attr.permissions().readonly());
993     let mut p = attr.permissions();
994     p.set_readonly(true);
995     check!(fs::set_permissions(&file, p.clone()));
996     let attr = check!(fs::metadata(&file));
997     assert!(attr.permissions().readonly());
998 
999     match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
1000         Ok(..) => panic!("wanted an error"),
1001         Err(..) => {}
1002     }
1003 
1004     p.set_readonly(false);
1005     check!(fs::set_permissions(&file, p));
1006 }
1007 
1008 #[test]
fchmod_works()1009 fn fchmod_works() {
1010     let tmpdir = tmpdir();
1011     let path = tmpdir.join("in.txt");
1012 
1013     let file = check!(File::create(&path));
1014     let attr = check!(fs::metadata(&path));
1015     assert!(!attr.permissions().readonly());
1016     let mut p = attr.permissions();
1017     p.set_readonly(true);
1018     check!(file.set_permissions(p.clone()));
1019     let attr = check!(fs::metadata(&path));
1020     assert!(attr.permissions().readonly());
1021 
1022     p.set_readonly(false);
1023     check!(file.set_permissions(p));
1024 }
1025 
1026 #[test]
sync_doesnt_kill_anything()1027 fn sync_doesnt_kill_anything() {
1028     let tmpdir = tmpdir();
1029     let path = tmpdir.join("in.txt");
1030 
1031     let mut file = check!(File::create(&path));
1032     check!(file.sync_all());
1033     check!(file.sync_data());
1034     check!(file.write(b"foo"));
1035     check!(file.sync_all());
1036     check!(file.sync_data());
1037 }
1038 
1039 #[test]
truncate_works()1040 fn truncate_works() {
1041     let tmpdir = tmpdir();
1042     let path = tmpdir.join("in.txt");
1043 
1044     let mut file = check!(File::create(&path));
1045     check!(file.write(b"foo"));
1046     check!(file.sync_all());
1047 
1048     // Do some simple things with truncation
1049     assert_eq!(check!(file.metadata()).len(), 3);
1050     check!(file.set_len(10));
1051     assert_eq!(check!(file.metadata()).len(), 10);
1052     check!(file.write(b"bar"));
1053     check!(file.sync_all());
1054     assert_eq!(check!(file.metadata()).len(), 10);
1055 
1056     let mut v = Vec::new();
1057     check!(check!(File::open(&path)).read_to_end(&mut v));
1058     assert_eq!(v, b"foobar\0\0\0\0".to_vec());
1059 
1060     // Truncate to a smaller length, don't seek, and then write something.
1061     // Ensure that the intermediate zeroes are all filled in (we have `seek`ed
1062     // past the end of the file).
1063     check!(file.set_len(2));
1064     assert_eq!(check!(file.metadata()).len(), 2);
1065     check!(file.write(b"wut"));
1066     check!(file.sync_all());
1067     assert_eq!(check!(file.metadata()).len(), 9);
1068     let mut v = Vec::new();
1069     check!(check!(File::open(&path)).read_to_end(&mut v));
1070     assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
1071 }
1072 
1073 #[test]
open_flavors()1074 fn open_flavors() {
1075     use crate::fs::OpenOptions as OO;
1076     fn c<T: Clone>(t: &T) -> T {
1077         t.clone()
1078     }
1079 
1080     let tmpdir = tmpdir();
1081 
1082     let mut r = OO::new();
1083     r.read(true);
1084     let mut w = OO::new();
1085     w.write(true);
1086     let mut rw = OO::new();
1087     rw.read(true).write(true);
1088     let mut a = OO::new();
1089     a.append(true);
1090     let mut ra = OO::new();
1091     ra.read(true).append(true);
1092 
1093     #[cfg(windows)]
1094     let invalid_options = 87; // ERROR_INVALID_PARAMETER
1095     #[cfg(all(unix, not(target_os = "vxworks")))]
1096     let invalid_options = "Invalid argument";
1097     #[cfg(target_os = "vxworks")]
1098     let invalid_options = "invalid argument";
1099 
1100     // Test various combinations of creation modes and access modes.
1101     //
1102     // Allowed:
1103     // creation mode           | read  | write | read-write | append | read-append |
1104     // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
1105     // not set (open existing) |   X   |   X   |     X      |   X    |      X      |
1106     // create                  |       |   X   |     X      |   X    |      X      |
1107     // truncate                |       |   X   |     X      |        |             |
1108     // create and truncate     |       |   X   |     X      |        |             |
1109     // create_new              |       |   X   |     X      |   X    |      X      |
1110     //
1111     // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
1112 
1113     // write-only
1114     check!(c(&w).create_new(true).open(&tmpdir.join("a")));
1115     check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a")));
1116     check!(c(&w).truncate(true).open(&tmpdir.join("a")));
1117     check!(c(&w).create(true).open(&tmpdir.join("a")));
1118     check!(c(&w).open(&tmpdir.join("a")));
1119 
1120     // read-only
1121     error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
1122     error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
1123     error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
1124     error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
1125     check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
1126 
1127     // read-write
1128     check!(c(&rw).create_new(true).open(&tmpdir.join("c")));
1129     check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c")));
1130     check!(c(&rw).truncate(true).open(&tmpdir.join("c")));
1131     check!(c(&rw).create(true).open(&tmpdir.join("c")));
1132     check!(c(&rw).open(&tmpdir.join("c")));
1133 
1134     // append
1135     check!(c(&a).create_new(true).open(&tmpdir.join("d")));
1136     error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
1137     error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
1138     check!(c(&a).create(true).open(&tmpdir.join("d")));
1139     check!(c(&a).open(&tmpdir.join("d")));
1140 
1141     // read-append
1142     check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
1143     error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
1144     error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
1145     check!(c(&ra).create(true).open(&tmpdir.join("e")));
1146     check!(c(&ra).open(&tmpdir.join("e")));
1147 
1148     // Test opening a file without setting an access mode
1149     let mut blank = OO::new();
1150     error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
1151 
1152     // Test write works
1153     check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
1154 
1155     // Test write fails for read-only
1156     check!(r.open(&tmpdir.join("h")));
1157     {
1158         let mut f = check!(r.open(&tmpdir.join("h")));
1159         assert!(f.write("wut".as_bytes()).is_err());
1160     }
1161 
1162     // Test write overwrites
1163     {
1164         let mut f = check!(c(&w).open(&tmpdir.join("h")));
1165         check!(f.write("baz".as_bytes()));
1166     }
1167     {
1168         let mut f = check!(c(&r).open(&tmpdir.join("h")));
1169         let mut b = vec![0; 6];
1170         check!(f.read(&mut b));
1171         assert_eq!(b, "bazbar".as_bytes());
1172     }
1173 
1174     // Test truncate works
1175     {
1176         let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1177         check!(f.write("foo".as_bytes()));
1178     }
1179     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1180 
1181     // Test append works
1182     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1183     {
1184         let mut f = check!(c(&a).open(&tmpdir.join("h")));
1185         check!(f.write("bar".as_bytes()));
1186     }
1187     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1188 
1189     // Test .append(true) equals .write(true).append(true)
1190     {
1191         let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1192         check!(f.write("baz".as_bytes()));
1193     }
1194     assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
1195 }
1196 
1197 #[test]
_assert_send_sync()1198 fn _assert_send_sync() {
1199     fn _assert_send_sync<T: Send + Sync>() {}
1200     _assert_send_sync::<OpenOptions>();
1201 }
1202 
1203 #[test]
binary_file()1204 fn binary_file() {
1205     let mut bytes = [0; 1024];
1206     crate::test_helpers::test_rng().fill_bytes(&mut bytes);
1207 
1208     let tmpdir = tmpdir();
1209 
1210     check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
1211     let mut v = Vec::new();
1212     check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
1213     assert!(v == &bytes[..]);
1214 }
1215 
1216 #[test]
write_then_read()1217 fn write_then_read() {
1218     let mut bytes = [0; 1024];
1219     crate::test_helpers::test_rng().fill_bytes(&mut bytes);
1220 
1221     let tmpdir = tmpdir();
1222 
1223     check!(fs::write(&tmpdir.join("test"), &bytes[..]));
1224     let v = check!(fs::read(&tmpdir.join("test")));
1225     assert!(v == &bytes[..]);
1226 
1227     check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
1228     error_contains!(
1229         fs::read_to_string(&tmpdir.join("not-utf8")),
1230         "stream did not contain valid UTF-8"
1231     );
1232 
1233     let s = "����������";
1234     check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
1235     let string = check!(fs::read_to_string(&tmpdir.join("utf8")));
1236     assert_eq!(string, s);
1237 }
1238 
1239 #[test]
file_try_clone()1240 fn file_try_clone() {
1241     let tmpdir = tmpdir();
1242 
1243     let mut f1 =
1244         check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
1245     let mut f2 = check!(f1.try_clone());
1246 
1247     check!(f1.write_all(b"hello world"));
1248     check!(f1.seek(SeekFrom::Start(2)));
1249 
1250     let mut buf = vec![];
1251     check!(f2.read_to_end(&mut buf));
1252     assert_eq!(buf, b"llo world");
1253     drop(f2);
1254 
1255     check!(f1.write_all(b"!"));
1256 }
1257 
1258 #[test]
1259 #[cfg(not(windows))]
unlink_readonly()1260 fn unlink_readonly() {
1261     let tmpdir = tmpdir();
1262     let path = tmpdir.join("file");
1263     check!(File::create(&path));
1264     let mut perm = check!(fs::metadata(&path)).permissions();
1265     perm.set_readonly(true);
1266     check!(fs::set_permissions(&path, perm));
1267     check!(fs::remove_file(&path));
1268 }
1269 
1270 #[test]
mkdir_trailing_slash()1271 fn mkdir_trailing_slash() {
1272     let tmpdir = tmpdir();
1273     let path = tmpdir.join("file");
1274     check!(fs::create_dir_all(&path.join("a/")));
1275 }
1276 
1277 #[test]
canonicalize_works_simple()1278 fn canonicalize_works_simple() {
1279     let tmpdir = tmpdir();
1280     let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1281     let file = tmpdir.join("test");
1282     File::create(&file).unwrap();
1283     assert_eq!(fs::canonicalize(&file).unwrap(), file);
1284 }
1285 
1286 #[test]
realpath_works()1287 fn realpath_works() {
1288     let tmpdir = tmpdir();
1289     if !got_symlink_permission(&tmpdir) {
1290         return;
1291     };
1292 
1293     let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1294     let file = tmpdir.join("test");
1295     let dir = tmpdir.join("test2");
1296     let link = dir.join("link");
1297     let linkdir = tmpdir.join("test3");
1298 
1299     File::create(&file).unwrap();
1300     fs::create_dir(&dir).unwrap();
1301     symlink_file(&file, &link).unwrap();
1302     symlink_dir(&dir, &linkdir).unwrap();
1303 
1304     assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
1305 
1306     assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir);
1307     assert_eq!(fs::canonicalize(&file).unwrap(), file);
1308     assert_eq!(fs::canonicalize(&link).unwrap(), file);
1309     assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir);
1310     assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file);
1311 }
1312 
1313 #[test]
realpath_works_tricky()1314 fn realpath_works_tricky() {
1315     let tmpdir = tmpdir();
1316     if !got_symlink_permission(&tmpdir) {
1317         return;
1318     };
1319 
1320     let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1321     let a = tmpdir.join("a");
1322     let b = a.join("b");
1323     let c = b.join("c");
1324     let d = a.join("d");
1325     let e = d.join("e");
1326     let f = a.join("f");
1327 
1328     fs::create_dir_all(&b).unwrap();
1329     fs::create_dir_all(&d).unwrap();
1330     File::create(&f).unwrap();
1331     if cfg!(not(windows)) {
1332         symlink_file("../d/e", &c).unwrap();
1333         symlink_file("../f", &e).unwrap();
1334     }
1335     if cfg!(windows) {
1336         symlink_file(r"..\d\e", &c).unwrap();
1337         symlink_file(r"..\f", &e).unwrap();
1338     }
1339 
1340     assert_eq!(fs::canonicalize(&c).unwrap(), f);
1341     assert_eq!(fs::canonicalize(&e).unwrap(), f);
1342 }
1343 
1344 #[test]
dir_entry_methods()1345 fn dir_entry_methods() {
1346     let tmpdir = tmpdir();
1347 
1348     fs::create_dir_all(&tmpdir.join("a")).unwrap();
1349     File::create(&tmpdir.join("b")).unwrap();
1350 
1351     for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
1352         let fname = file.file_name();
1353         match fname.to_str() {
1354             Some("a") => {
1355                 assert!(file.file_type().unwrap().is_dir());
1356                 assert!(file.metadata().unwrap().is_dir());
1357             }
1358             Some("b") => {
1359                 assert!(file.file_type().unwrap().is_file());
1360                 assert!(file.metadata().unwrap().is_file());
1361             }
1362             f => panic!("unknown file name: {f:?}"),
1363         }
1364     }
1365 }
1366 
1367 #[test]
dir_entry_debug()1368 fn dir_entry_debug() {
1369     let tmpdir = tmpdir();
1370     File::create(&tmpdir.join("b")).unwrap();
1371     let mut read_dir = tmpdir.path().read_dir().unwrap();
1372     let dir_entry = read_dir.next().unwrap().unwrap();
1373     let actual = format!("{dir_entry:?}");
1374     let expected = format!("DirEntry({:?})", dir_entry.0.path());
1375     assert_eq!(actual, expected);
1376 }
1377 
1378 #[test]
read_dir_not_found()1379 fn read_dir_not_found() {
1380     let res = fs::read_dir("/path/that/does/not/exist");
1381     assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1382 }
1383 
1384 #[test]
file_open_not_found()1385 fn file_open_not_found() {
1386     let res = File::open("/path/that/does/not/exist");
1387     assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1388 }
1389 
1390 #[test]
create_dir_all_with_junctions()1391 fn create_dir_all_with_junctions() {
1392     let tmpdir = tmpdir();
1393     let target = tmpdir.join("target");
1394 
1395     let junction = tmpdir.join("junction");
1396     let b = junction.join("a/b");
1397 
1398     let link = tmpdir.join("link");
1399     let d = link.join("c/d");
1400 
1401     fs::create_dir(&target).unwrap();
1402 
1403     check!(symlink_junction(&target, &junction));
1404     check!(fs::create_dir_all(&b));
1405     // the junction itself is not a directory, but `is_dir()` on a Path
1406     // follows links
1407     assert!(junction.is_dir());
1408     assert!(b.exists());
1409 
1410     if !got_symlink_permission(&tmpdir) {
1411         return;
1412     };
1413     check!(symlink_dir(&target, &link));
1414     check!(fs::create_dir_all(&d));
1415     assert!(link.is_dir());
1416     assert!(d.exists());
1417 }
1418 
1419 #[test]
metadata_access_times()1420 fn metadata_access_times() {
1421     let tmpdir = tmpdir();
1422 
1423     let b = tmpdir.join("b");
1424     File::create(&b).unwrap();
1425 
1426     let a = check!(fs::metadata(&tmpdir.path()));
1427     let b = check!(fs::metadata(&b));
1428 
1429     assert_eq!(check!(a.accessed()), check!(a.accessed()));
1430     assert_eq!(check!(a.modified()), check!(a.modified()));
1431     assert_eq!(check!(b.accessed()), check!(b.modified()));
1432 
1433     if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1434         check!(a.created());
1435         check!(b.created());
1436     }
1437 
1438     if cfg!(target_os = "linux") {
1439         // Not always available
1440         match (a.created(), b.created()) {
1441             (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1442             (Err(e1), Err(e2))
1443                 if e1.kind() == ErrorKind::Uncategorized
1444                     && e2.kind() == ErrorKind::Uncategorized
1445                     || e1.kind() == ErrorKind::Unsupported
1446                         && e2.kind() == ErrorKind::Unsupported => {}
1447             (a, b) => {
1448                 panic!("creation time must be always supported or not supported: {a:?} {b:?}")
1449             }
1450         }
1451     }
1452 }
1453 
1454 /// Test creating hard links to symlinks.
1455 #[test]
symlink_hard_link()1456 fn symlink_hard_link() {
1457     let tmpdir = tmpdir();
1458     if !got_symlink_permission(&tmpdir) {
1459         return;
1460     };
1461     if !able_to_not_follow_symlinks_while_hard_linking() {
1462         return;
1463     }
1464 
1465     // Create "file", a file.
1466     check!(fs::File::create(tmpdir.join("file")));
1467 
1468     // Create "symlink", a symlink to "file".
1469     check!(symlink_file("file", tmpdir.join("symlink")));
1470 
1471     // Create "hard_link", a hard link to "symlink".
1472     check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
1473 
1474     // "hard_link" should appear as a symlink.
1475     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1476 
1477     // We should be able to open "file" via any of the above names.
1478     let _ = check!(fs::File::open(tmpdir.join("file")));
1479     assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1480     let _ = check!(fs::File::open(tmpdir.join("symlink")));
1481     let _ = check!(fs::File::open(tmpdir.join("hard_link")));
1482 
1483     // Rename "file" to "file.renamed".
1484     check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
1485 
1486     // Now, the symlink and the hard link should be dangling.
1487     assert!(fs::File::open(tmpdir.join("file")).is_err());
1488     let _ = check!(fs::File::open(tmpdir.join("file.renamed")));
1489     assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1490     assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1491 
1492     // The symlink and the hard link should both still point to "file".
1493     assert!(fs::read_link(tmpdir.join("file")).is_err());
1494     assert!(fs::read_link(tmpdir.join("file.renamed")).is_err());
1495     assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file"));
1496     assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file"));
1497 
1498     // Remove "file.renamed".
1499     check!(fs::remove_file(tmpdir.join("file.renamed")));
1500 
1501     // Now, we can't open the file by any name.
1502     assert!(fs::File::open(tmpdir.join("file")).is_err());
1503     assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1504     assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1505     assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1506 
1507     // "hard_link" should still appear as a symlink.
1508     assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1509 }
1510 
1511 /// Ensure `fs::create_dir` works on Windows with longer paths.
1512 #[test]
1513 #[cfg(windows)]
create_dir_long_paths()1514 fn create_dir_long_paths() {
1515     use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1516     const PATH_LEN: usize = 247;
1517 
1518     let tmpdir = tmpdir();
1519     let mut path = tmpdir.path().to_path_buf();
1520     path.push("a");
1521     let mut path = path.into_os_string();
1522 
1523     let utf16_len = path.encode_wide().count();
1524     if utf16_len >= PATH_LEN {
1525         // Skip the test in the unlikely event the local user has a long temp directory path.
1526         // This should not affect CI.
1527         return;
1528     }
1529     // Increase the length of the path.
1530     path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1531 
1532     // This should succeed.
1533     fs::create_dir(&path).unwrap();
1534 
1535     // This will fail if the path isn't converted to verbatim.
1536     path.push("a");
1537     fs::create_dir(&path).unwrap();
1538 
1539     // #90940: Ensure an empty path returns the "Not Found" error.
1540     let path = Path::new("");
1541     assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound);
1542 }
1543 
1544 /// Ensure ReadDir works on large directories.
1545 /// Regression test for https://github.com/rust-lang/rust/issues/93384.
1546 #[test]
read_large_dir()1547 fn read_large_dir() {
1548     let tmpdir = tmpdir();
1549 
1550     let count = 32 * 1024;
1551     for i in 0..count {
1552         check!(fs::File::create(tmpdir.join(&i.to_string())));
1553     }
1554 
1555     for entry in fs::read_dir(tmpdir.path()).unwrap() {
1556         entry.unwrap();
1557     }
1558 }
1559 
1560 /// Test the fallback for getting the metadata of files like hiberfil.sys that
1561 /// Windows holds a special lock on, preventing normal means of querying
1562 /// metadata. See #96980.
1563 ///
1564 /// Note this fails in CI because `hiberfil.sys` does not actually exist there.
1565 /// Therefore it's marked as ignored.
1566 #[test]
1567 #[ignore]
1568 #[cfg(windows)]
hiberfil_sys()1569 fn hiberfil_sys() {
1570     let hiberfil = Path::new(r"C:\hiberfil.sys");
1571     assert_eq!(true, hiberfil.try_exists().unwrap());
1572     fs::symlink_metadata(hiberfil).unwrap();
1573     fs::metadata(hiberfil).unwrap();
1574     assert_eq!(true, hiberfil.exists());
1575 }
1576 
1577 /// Test that two different ways of obtaining the FileType give the same result.
1578 /// Cf. https://github.com/rust-lang/rust/issues/104900
1579 #[test]
test_eq_direntry_metadata()1580 fn test_eq_direntry_metadata() {
1581     let tmpdir = tmpdir();
1582     let file_path = tmpdir.join("file");
1583     File::create(file_path).unwrap();
1584     for e in fs::read_dir(tmpdir.path()).unwrap() {
1585         let e = e.unwrap();
1586         let p = e.path();
1587         let ft1 = e.file_type().unwrap();
1588         let ft2 = p.metadata().unwrap().file_type();
1589         assert_eq!(ft1, ft2);
1590     }
1591 }
1592 
1593 /// Regression test for https://github.com/rust-lang/rust/issues/50619.
1594 #[test]
1595 #[cfg(target_os = "linux")]
test_read_dir_infinite_loop()1596 fn test_read_dir_infinite_loop() {
1597     use crate::io::ErrorKind;
1598     use crate::process::Command;
1599 
1600     // Create a zombie child process
1601     let Ok(mut child) = Command::new("echo").spawn() else { return };
1602 
1603     // Make sure the process is (un)dead
1604     match child.kill() {
1605         // InvalidInput means the child already exited
1606         Err(e) if e.kind() != ErrorKind::InvalidInput => return,
1607         _ => {}
1608     }
1609 
1610     // open() on this path will succeed, but readdir() will fail
1611     let id = child.id();
1612     let path = format!("/proc/{id}/net");
1613 
1614     // Skip the test if we can't open the directory in the first place
1615     let Ok(dir) = fs::read_dir(path) else { return };
1616 
1617     // Check for duplicate errors
1618     assert!(dir.filter(|e| e.is_err()).take(2).count() < 2);
1619 }
1620 
1621 #[test]
rename_directory()1622 fn rename_directory() {
1623     let tmpdir = tmpdir();
1624     let old_path = tmpdir.join("foo/bar/baz");
1625     fs::create_dir_all(&old_path).unwrap();
1626     let test_file = &old_path.join("temp.txt");
1627 
1628     File::create(test_file).unwrap();
1629 
1630     let new_path = tmpdir.join("quux/blat");
1631     fs::create_dir_all(&new_path).unwrap();
1632     fs::rename(&old_path, &new_path.join("newdir")).unwrap();
1633     assert!(new_path.join("newdir").is_dir());
1634     assert!(new_path.join("newdir/temp.txt").exists());
1635 }
1636 
1637 #[test]
test_file_times()1638 fn test_file_times() {
1639     #[cfg(target_os = "ios")]
1640     use crate::os::ios::fs::FileTimesExt;
1641     #[cfg(target_os = "macos")]
1642     use crate::os::macos::fs::FileTimesExt;
1643     #[cfg(target_os = "tvos")]
1644     use crate::os::tvos::fs::FileTimesExt;
1645     #[cfg(target_os = "tvos")]
1646     use crate::os::tvos::fs::FileTimesExt;
1647     #[cfg(target_os = "watchos")]
1648     use crate::os::watchos::fs::FileTimesExt;
1649     #[cfg(windows)]
1650     use crate::os::windows::fs::FileTimesExt;
1651 
1652     let tmp = tmpdir();
1653     let file = File::create(tmp.join("foo")).unwrap();
1654     let mut times = FileTimes::new();
1655     let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345);
1656     let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321);
1657     times = times.set_accessed(accessed).set_modified(modified);
1658     #[cfg(any(
1659         windows,
1660         target_os = "macos",
1661         target_os = "ios",
1662         target_os = "watchos",
1663         target_os = "tvos",
1664     ))]
1665     let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123);
1666     #[cfg(any(
1667         windows,
1668         target_os = "macos",
1669         target_os = "ios",
1670         target_os = "watchos",
1671         target_os = "tvos",
1672     ))]
1673     {
1674         times = times.set_created(created);
1675     }
1676     match file.set_times(times) {
1677         // Allow unsupported errors on platforms which don't support setting times.
1678         #[cfg(not(any(
1679             windows,
1680             all(
1681                 unix,
1682                 not(any(
1683                     target_os = "android",
1684                     target_os = "redox",
1685                     target_os = "espidf",
1686                     target_os = "horizon"
1687                 ))
1688             )
1689         )))]
1690         Err(e) if e.kind() == ErrorKind::Unsupported => return,
1691         Err(e) => panic!("error setting file times: {e:?}"),
1692         Ok(_) => {}
1693     }
1694     let metadata = file.metadata().unwrap();
1695     assert_eq!(metadata.accessed().unwrap(), accessed);
1696     assert_eq!(metadata.modified().unwrap(), modified);
1697     #[cfg(any(
1698         windows,
1699         target_os = "macos",
1700         target_os = "ios",
1701         target_os = "watchos",
1702         target_os = "tvos",
1703     ))]
1704     {
1705         assert_eq!(metadata.created().unwrap(), created);
1706     }
1707 }
1708