]> git.proxmox.com Git - rustc.git/blame - library/std/src/fs/tests.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / library / std / src / fs / tests.rs
CommitLineData
1b1a35ee
XL
1use crate::io::prelude::*;
2
5e7ed085 3use crate::env;
1b1a35ee
XL
4use crate::fs::{self, File, OpenOptions};
5use crate::io::{ErrorKind, SeekFrom};
6use crate::path::Path;
7use crate::str;
3c0e092e 8use crate::sync::Arc;
1b1a35ee
XL
9use crate::sys_common::io::test::{tmpdir, TempDir};
10use crate::thread;
3c0e092e 11use crate::time::{Duration, Instant};
1b1a35ee 12
9c376795 13use rand::RngCore;
1b1a35ee 14
9ffffee4
FG
15#[cfg(target_os = "macos")]
16use crate::ffi::{c_char, c_int};
1b1a35ee
XL
17#[cfg(unix)]
18use crate::os::unix::fs::symlink as symlink_dir;
19#[cfg(unix)]
20use crate::os::unix::fs::symlink as symlink_file;
21#[cfg(unix)]
22use crate::os::unix::fs::symlink as symlink_junction;
23#[cfg(windows)]
24use crate::os::windows::fs::{symlink_dir, symlink_file};
25#[cfg(windows)]
26use crate::sys::fs::symlink_junction;
136023e0
XL
27#[cfg(target_os = "macos")]
28use crate::sys::weak::weak;
1b1a35ee
XL
29
30macro_rules! check {
31 ($e:expr) => {
32 match $e {
33 Ok(t) => t,
5e7ed085 34 Err(e) => panic!("{} failed with: {e}", stringify!($e)),
1b1a35ee
XL
35 }
36 };
37}
38
39#[cfg(windows)]
40macro_rules! error {
41 ($e:expr, $s:expr) => {
42 match $e {
43 Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
a2a8927a
XL
44 Err(ref err) => {
45 assert!(err.raw_os_error() == Some($s), "`{}` did not have a code of `{}`", err, $s)
46 }
1b1a35ee
XL
47 }
48 };
49}
50
51#[cfg(unix)]
52macro_rules! error {
53 ($e:expr, $s:expr) => {
54 error_contains!($e, $s)
55 };
56}
57
58macro_rules! error_contains {
59 ($e:expr, $s:expr) => {
60 match $e {
61 Ok(_) => panic!("Unexpected success. Should've been: {:?}", $s),
62 Err(ref err) => {
a2a8927a 63 assert!(err.to_string().contains($s), "`{}` did not contain `{}`", err, $s)
1b1a35ee
XL
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.
75pub 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) {
1b1a35ee
XL
82 // ERROR_PRIVILEGE_NOT_HELD = 1314
83 Err(ref err) if err.raw_os_error() == Some(1314) => false,
29967ef6 84 Ok(_) | Err(_) => true,
1b1a35ee
XL
85 }
86}
87
136023e0
XL
88#[cfg(target_os = "macos")]
89fn 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"))]
95fn able_to_not_follow_symlinks_while_hard_linking() -> bool {
96 return true;
97}
98
1b1a35ee
XL
99#[test]
100fn 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]
121fn 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]
135fn 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]
150fn 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]
176fn 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]
203fn 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]
227fn 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]
259fn 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)]
274fn 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)]
330fn 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)]
352fn 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]
405fn 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]
425fn 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]
437fn 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]
446fn 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]
456fn 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]
468fn 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 {
5e7ed085 474 let f = dir.join(&format!("{n}.txt"));
1b1a35ee
XL
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]
497fn 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]
506fn 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]
515fn 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]
523fn 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]
537fn 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]
558fn recursive_mkdir_slash() {
559 check!(fs::create_dir_all(Path::new("/")));
560}
561
562#[test]
563fn recursive_mkdir_dot() {
564 check!(fs::create_dir_all(Path::new(".")));
565}
566
567#[test]
568fn recursive_mkdir_empty() {
569 check!(fs::create_dir_all(Path::new("")));
570}
571
572#[test]
573fn 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]
592fn 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
3c0e092e
XL
607#[test]
608fn 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
1b1a35ee
XL
622#[test]
623// only Windows makes a distinction between file and directory symlinks.
624#[cfg(windows)]
625fn 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
3c0e092e
XL
641#[test]
642#[ignore] // takes too much time
643fn recursive_rmdir_toctou() {
644 // Test for time-of-check to time-of-use issues.
645 //
646 // Scenario:
04454e1e
FG
647 // The attacker wants to get directory contents deleted, to which they do not have access.
648 // They have a way to get a privileged Rust binary call `std::fs::remove_dir_all()` on a
649 // directory they control, e.g. in their home directory.
3c0e092e
XL
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
1b1a35ee
XL
694#[test]
695fn 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]
714fn 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]
727fn 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]
741fn 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]
754fn 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]
769fn 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]
781fn 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]
796fn 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]
808fn 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)]
825fn 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]
836fn 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]
848fn 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]
870fn 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]
889fn 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,
94222f64 897 // so we might not get the same path back for absolute paths
1b1a35ee
XL
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]
903fn read_link() {
904 if cfg!(windows) {
905 // directory symlink
3c0e092e 906 assert_eq!(check!(fs::read_link(r"C:\Users\All Users")), Path::new(r"C:\ProgramData"));
1b1a35ee 907 // junction
3c0e092e 908 assert_eq!(check!(fs::read_link(r"C:\Users\Default User")), Path::new(r"C:\Users\Default"));
1b1a35ee 909 // junction with special permissions
5e7ed085
FG
910 // Since not all localized windows versions contain the folder "Documents and Settings" in english,
911 // we will briefly check, if it exists and otherwise skip the test. Except during CI we will always execute the test.
912 if Path::new(r"C:\Documents and Settings\").exists() || env::var_os("CI").is_some() {
913 assert_eq!(
914 check!(fs::read_link(r"C:\Documents and Settings\")),
915 Path::new(r"C:\Users")
916 );
917 }
1b1a35ee
XL
918 }
919 let tmpdir = tmpdir();
920 let link = tmpdir.join("link");
921 if !got_symlink_permission(&tmpdir) {
922 return;
923 };
924 check!(symlink_file(&"foo", &link));
925 assert_eq!(check!(fs::read_link(&link)).to_str().unwrap(), "foo");
926}
927
928#[test]
929fn readlink_not_symlink() {
930 let tmpdir = tmpdir();
931 match fs::read_link(tmpdir.path()) {
932 Ok(..) => panic!("wanted a failure"),
933 Err(..) => {}
934 }
935}
936
937#[test]
938fn links_work() {
939 let tmpdir = tmpdir();
940 let input = tmpdir.join("in.txt");
941 let out = tmpdir.join("out.txt");
942
943 check!(check!(File::create(&input)).write("foobar".as_bytes()));
944 check!(fs::hard_link(&input, &out));
945 assert_eq!(check!(fs::metadata(&out)).len(), check!(fs::metadata(&input)).len());
946 assert_eq!(check!(fs::metadata(&out)).len(), check!(input.metadata()).len());
947 let mut v = Vec::new();
948 check!(check!(File::open(&out)).read_to_end(&mut v));
949 assert_eq!(v, b"foobar".to_vec());
950
951 // can't link to yourself
952 match fs::hard_link(&input, &input) {
953 Ok(..) => panic!("wanted a failure"),
954 Err(..) => {}
955 }
956 // can't link to something that doesn't exist
957 match fs::hard_link(&tmpdir.join("foo"), &tmpdir.join("bar")) {
958 Ok(..) => panic!("wanted a failure"),
959 Err(..) => {}
960 }
961}
962
963#[test]
964fn chmod_works() {
965 let tmpdir = tmpdir();
966 let file = tmpdir.join("in.txt");
967
968 check!(File::create(&file));
969 let attr = check!(fs::metadata(&file));
970 assert!(!attr.permissions().readonly());
971 let mut p = attr.permissions();
972 p.set_readonly(true);
973 check!(fs::set_permissions(&file, p.clone()));
974 let attr = check!(fs::metadata(&file));
975 assert!(attr.permissions().readonly());
976
977 match fs::set_permissions(&tmpdir.join("foo"), p.clone()) {
978 Ok(..) => panic!("wanted an error"),
979 Err(..) => {}
980 }
981
982 p.set_readonly(false);
983 check!(fs::set_permissions(&file, p));
984}
985
986#[test]
987fn fchmod_works() {
988 let tmpdir = tmpdir();
989 let path = tmpdir.join("in.txt");
990
991 let file = check!(File::create(&path));
992 let attr = check!(fs::metadata(&path));
993 assert!(!attr.permissions().readonly());
994 let mut p = attr.permissions();
995 p.set_readonly(true);
996 check!(file.set_permissions(p.clone()));
997 let attr = check!(fs::metadata(&path));
998 assert!(attr.permissions().readonly());
999
1000 p.set_readonly(false);
1001 check!(file.set_permissions(p));
1002}
1003
1004#[test]
1005fn sync_doesnt_kill_anything() {
1006 let tmpdir = tmpdir();
1007 let path = tmpdir.join("in.txt");
1008
1009 let mut file = check!(File::create(&path));
1010 check!(file.sync_all());
1011 check!(file.sync_data());
1012 check!(file.write(b"foo"));
1013 check!(file.sync_all());
1014 check!(file.sync_data());
1015}
1016
1017#[test]
1018fn truncate_works() {
1019 let tmpdir = tmpdir();
1020 let path = tmpdir.join("in.txt");
1021
1022 let mut file = check!(File::create(&path));
1023 check!(file.write(b"foo"));
1024 check!(file.sync_all());
1025
1026 // Do some simple things with truncation
1027 assert_eq!(check!(file.metadata()).len(), 3);
1028 check!(file.set_len(10));
1029 assert_eq!(check!(file.metadata()).len(), 10);
1030 check!(file.write(b"bar"));
1031 check!(file.sync_all());
1032 assert_eq!(check!(file.metadata()).len(), 10);
1033
1034 let mut v = Vec::new();
1035 check!(check!(File::open(&path)).read_to_end(&mut v));
1036 assert_eq!(v, b"foobar\0\0\0\0".to_vec());
1037
1038 // Truncate to a smaller length, don't seek, and then write something.
1039 // Ensure that the intermediate zeroes are all filled in (we have `seek`ed
1040 // past the end of the file).
1041 check!(file.set_len(2));
1042 assert_eq!(check!(file.metadata()).len(), 2);
1043 check!(file.write(b"wut"));
1044 check!(file.sync_all());
1045 assert_eq!(check!(file.metadata()).len(), 9);
1046 let mut v = Vec::new();
1047 check!(check!(File::open(&path)).read_to_end(&mut v));
1048 assert_eq!(v, b"fo\0\0\0\0wut".to_vec());
1049}
1050
1051#[test]
1052fn open_flavors() {
1053 use crate::fs::OpenOptions as OO;
1054 fn c<T: Clone>(t: &T) -> T {
1055 t.clone()
1056 }
1057
1058 let tmpdir = tmpdir();
1059
1060 let mut r = OO::new();
1061 r.read(true);
1062 let mut w = OO::new();
1063 w.write(true);
1064 let mut rw = OO::new();
1065 rw.read(true).write(true);
1066 let mut a = OO::new();
1067 a.append(true);
1068 let mut ra = OO::new();
1069 ra.read(true).append(true);
1070
1071 #[cfg(windows)]
1072 let invalid_options = 87; // ERROR_INVALID_PARAMETER
1073 #[cfg(all(unix, not(target_os = "vxworks")))]
1074 let invalid_options = "Invalid argument";
1075 #[cfg(target_os = "vxworks")]
1076 let invalid_options = "invalid argument";
1077
1078 // Test various combinations of creation modes and access modes.
1079 //
1080 // Allowed:
1081 // creation mode | read | write | read-write | append | read-append |
1082 // :-----------------------|:-----:|:-----:|:----------:|:------:|:-----------:|
1083 // not set (open existing) | X | X | X | X | X |
1084 // create | | X | X | X | X |
1085 // truncate | | X | X | | |
1086 // create and truncate | | X | X | | |
1087 // create_new | | X | X | X | X |
1088 //
1089 // tested in reverse order, so 'create_new' creates the file, and 'open existing' opens it.
1090
1091 // write-only
1092 check!(c(&w).create_new(true).open(&tmpdir.join("a")));
1093 check!(c(&w).create(true).truncate(true).open(&tmpdir.join("a")));
1094 check!(c(&w).truncate(true).open(&tmpdir.join("a")));
1095 check!(c(&w).create(true).open(&tmpdir.join("a")));
1096 check!(c(&w).open(&tmpdir.join("a")));
1097
1098 // read-only
1099 error!(c(&r).create_new(true).open(&tmpdir.join("b")), invalid_options);
1100 error!(c(&r).create(true).truncate(true).open(&tmpdir.join("b")), invalid_options);
1101 error!(c(&r).truncate(true).open(&tmpdir.join("b")), invalid_options);
1102 error!(c(&r).create(true).open(&tmpdir.join("b")), invalid_options);
1103 check!(c(&r).open(&tmpdir.join("a"))); // try opening the file created with write_only
1104
1105 // read-write
1106 check!(c(&rw).create_new(true).open(&tmpdir.join("c")));
1107 check!(c(&rw).create(true).truncate(true).open(&tmpdir.join("c")));
1108 check!(c(&rw).truncate(true).open(&tmpdir.join("c")));
1109 check!(c(&rw).create(true).open(&tmpdir.join("c")));
1110 check!(c(&rw).open(&tmpdir.join("c")));
1111
1112 // append
1113 check!(c(&a).create_new(true).open(&tmpdir.join("d")));
1114 error!(c(&a).create(true).truncate(true).open(&tmpdir.join("d")), invalid_options);
1115 error!(c(&a).truncate(true).open(&tmpdir.join("d")), invalid_options);
1116 check!(c(&a).create(true).open(&tmpdir.join("d")));
1117 check!(c(&a).open(&tmpdir.join("d")));
1118
1119 // read-append
1120 check!(c(&ra).create_new(true).open(&tmpdir.join("e")));
1121 error!(c(&ra).create(true).truncate(true).open(&tmpdir.join("e")), invalid_options);
1122 error!(c(&ra).truncate(true).open(&tmpdir.join("e")), invalid_options);
1123 check!(c(&ra).create(true).open(&tmpdir.join("e")));
1124 check!(c(&ra).open(&tmpdir.join("e")));
1125
1126 // Test opening a file without setting an access mode
1127 let mut blank = OO::new();
1128 error!(blank.create(true).open(&tmpdir.join("f")), invalid_options);
1129
1130 // Test write works
1131 check!(check!(File::create(&tmpdir.join("h"))).write("foobar".as_bytes()));
1132
1133 // Test write fails for read-only
1134 check!(r.open(&tmpdir.join("h")));
1135 {
1136 let mut f = check!(r.open(&tmpdir.join("h")));
1137 assert!(f.write("wut".as_bytes()).is_err());
1138 }
1139
1140 // Test write overwrites
1141 {
1142 let mut f = check!(c(&w).open(&tmpdir.join("h")));
1143 check!(f.write("baz".as_bytes()));
1144 }
1145 {
1146 let mut f = check!(c(&r).open(&tmpdir.join("h")));
1147 let mut b = vec![0; 6];
1148 check!(f.read(&mut b));
1149 assert_eq!(b, "bazbar".as_bytes());
1150 }
1151
1152 // Test truncate works
1153 {
1154 let mut f = check!(c(&w).truncate(true).open(&tmpdir.join("h")));
1155 check!(f.write("foo".as_bytes()));
1156 }
1157 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1158
1159 // Test append works
1160 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 3);
1161 {
1162 let mut f = check!(c(&a).open(&tmpdir.join("h")));
1163 check!(f.write("bar".as_bytes()));
1164 }
1165 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 6);
1166
1167 // Test .append(true) equals .write(true).append(true)
1168 {
1169 let mut f = check!(c(&w).append(true).open(&tmpdir.join("h")));
1170 check!(f.write("baz".as_bytes()));
1171 }
1172 assert_eq!(check!(fs::metadata(&tmpdir.join("h"))).len(), 9);
1173}
1174
1175#[test]
1176fn _assert_send_sync() {
1177 fn _assert_send_sync<T: Send + Sync>() {}
1178 _assert_send_sync::<OpenOptions>();
1179}
1180
1181#[test]
1182fn binary_file() {
1183 let mut bytes = [0; 1024];
9c376795 1184 crate::test_helpers::test_rng().fill_bytes(&mut bytes);
1b1a35ee
XL
1185
1186 let tmpdir = tmpdir();
1187
1188 check!(check!(File::create(&tmpdir.join("test"))).write(&bytes));
1189 let mut v = Vec::new();
1190 check!(check!(File::open(&tmpdir.join("test"))).read_to_end(&mut v));
1191 assert!(v == &bytes[..]);
1192}
1193
1194#[test]
1195fn write_then_read() {
1196 let mut bytes = [0; 1024];
9c376795 1197 crate::test_helpers::test_rng().fill_bytes(&mut bytes);
1b1a35ee
XL
1198
1199 let tmpdir = tmpdir();
1200
1201 check!(fs::write(&tmpdir.join("test"), &bytes[..]));
1202 let v = check!(fs::read(&tmpdir.join("test")));
1203 assert!(v == &bytes[..]);
1204
1205 check!(fs::write(&tmpdir.join("not-utf8"), &[0xFF]));
1206 error_contains!(
1207 fs::read_to_string(&tmpdir.join("not-utf8")),
1208 "stream did not contain valid UTF-8"
1209 );
1210
1211 let s = "𐁁𐀓𐀠𐀴𐀍";
1212 check!(fs::write(&tmpdir.join("utf8"), s.as_bytes()));
1213 let string = check!(fs::read_to_string(&tmpdir.join("utf8")));
1214 assert_eq!(string, s);
1215}
1216
1217#[test]
1218fn file_try_clone() {
1219 let tmpdir = tmpdir();
1220
1221 let mut f1 =
1222 check!(OpenOptions::new().read(true).write(true).create(true).open(&tmpdir.join("test")));
1223 let mut f2 = check!(f1.try_clone());
1224
1225 check!(f1.write_all(b"hello world"));
1226 check!(f1.seek(SeekFrom::Start(2)));
1227
1228 let mut buf = vec![];
1229 check!(f2.read_to_end(&mut buf));
1230 assert_eq!(buf, b"llo world");
1231 drop(f2);
1232
1233 check!(f1.write_all(b"!"));
1234}
1235
1236#[test]
1237#[cfg(not(windows))]
1238fn unlink_readonly() {
1239 let tmpdir = tmpdir();
1240 let path = tmpdir.join("file");
1241 check!(File::create(&path));
1242 let mut perm = check!(fs::metadata(&path)).permissions();
1243 perm.set_readonly(true);
1244 check!(fs::set_permissions(&path, perm));
1245 check!(fs::remove_file(&path));
1246}
1247
1248#[test]
1249fn mkdir_trailing_slash() {
1250 let tmpdir = tmpdir();
1251 let path = tmpdir.join("file");
1252 check!(fs::create_dir_all(&path.join("a/")));
1253}
1254
1255#[test]
1256fn canonicalize_works_simple() {
1257 let tmpdir = tmpdir();
1258 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1259 let file = tmpdir.join("test");
1260 File::create(&file).unwrap();
1261 assert_eq!(fs::canonicalize(&file).unwrap(), file);
1262}
1263
1264#[test]
1265fn realpath_works() {
1266 let tmpdir = tmpdir();
1267 if !got_symlink_permission(&tmpdir) {
1268 return;
1269 };
1270
1271 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1272 let file = tmpdir.join("test");
1273 let dir = tmpdir.join("test2");
1274 let link = dir.join("link");
1275 let linkdir = tmpdir.join("test3");
1276
1277 File::create(&file).unwrap();
1278 fs::create_dir(&dir).unwrap();
1279 symlink_file(&file, &link).unwrap();
1280 symlink_dir(&dir, &linkdir).unwrap();
1281
1282 assert!(link.symlink_metadata().unwrap().file_type().is_symlink());
1283
1284 assert_eq!(fs::canonicalize(&tmpdir).unwrap(), tmpdir);
1285 assert_eq!(fs::canonicalize(&file).unwrap(), file);
1286 assert_eq!(fs::canonicalize(&link).unwrap(), file);
1287 assert_eq!(fs::canonicalize(&linkdir).unwrap(), dir);
1288 assert_eq!(fs::canonicalize(&linkdir.join("link")).unwrap(), file);
1289}
1290
1291#[test]
1292fn realpath_works_tricky() {
1293 let tmpdir = tmpdir();
1294 if !got_symlink_permission(&tmpdir) {
1295 return;
1296 };
1297
1298 let tmpdir = fs::canonicalize(tmpdir.path()).unwrap();
1299 let a = tmpdir.join("a");
1300 let b = a.join("b");
1301 let c = b.join("c");
1302 let d = a.join("d");
1303 let e = d.join("e");
1304 let f = a.join("f");
1305
1306 fs::create_dir_all(&b).unwrap();
1307 fs::create_dir_all(&d).unwrap();
1308 File::create(&f).unwrap();
1309 if cfg!(not(windows)) {
1310 symlink_file("../d/e", &c).unwrap();
1311 symlink_file("../f", &e).unwrap();
1312 }
1313 if cfg!(windows) {
1314 symlink_file(r"..\d\e", &c).unwrap();
1315 symlink_file(r"..\f", &e).unwrap();
1316 }
1317
1318 assert_eq!(fs::canonicalize(&c).unwrap(), f);
1319 assert_eq!(fs::canonicalize(&e).unwrap(), f);
1320}
1321
1322#[test]
1323fn dir_entry_methods() {
1324 let tmpdir = tmpdir();
1325
1326 fs::create_dir_all(&tmpdir.join("a")).unwrap();
1327 File::create(&tmpdir.join("b")).unwrap();
1328
1329 for file in tmpdir.path().read_dir().unwrap().map(|f| f.unwrap()) {
1330 let fname = file.file_name();
1331 match fname.to_str() {
1332 Some("a") => {
1333 assert!(file.file_type().unwrap().is_dir());
1334 assert!(file.metadata().unwrap().is_dir());
1335 }
1336 Some("b") => {
1337 assert!(file.file_type().unwrap().is_file());
1338 assert!(file.metadata().unwrap().is_file());
1339 }
5e7ed085 1340 f => panic!("unknown file name: {f:?}"),
1b1a35ee
XL
1341 }
1342 }
1343}
1344
1345#[test]
1346fn dir_entry_debug() {
1347 let tmpdir = tmpdir();
1348 File::create(&tmpdir.join("b")).unwrap();
1349 let mut read_dir = tmpdir.path().read_dir().unwrap();
1350 let dir_entry = read_dir.next().unwrap().unwrap();
5e7ed085 1351 let actual = format!("{dir_entry:?}");
1b1a35ee
XL
1352 let expected = format!("DirEntry({:?})", dir_entry.0.path());
1353 assert_eq!(actual, expected);
1354}
1355
1356#[test]
1357fn read_dir_not_found() {
1358 let res = fs::read_dir("/path/that/does/not/exist");
1359 assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1360}
1361
5e7ed085
FG
1362#[test]
1363fn file_open_not_found() {
1364 let res = File::open("/path/that/does/not/exist");
1365 assert_eq!(res.err().unwrap().kind(), ErrorKind::NotFound);
1366}
1367
1b1a35ee
XL
1368#[test]
1369fn create_dir_all_with_junctions() {
1370 let tmpdir = tmpdir();
1371 let target = tmpdir.join("target");
1372
1373 let junction = tmpdir.join("junction");
1374 let b = junction.join("a/b");
1375
1376 let link = tmpdir.join("link");
1377 let d = link.join("c/d");
1378
1379 fs::create_dir(&target).unwrap();
1380
1381 check!(symlink_junction(&target, &junction));
1382 check!(fs::create_dir_all(&b));
1383 // the junction itself is not a directory, but `is_dir()` on a Path
1384 // follows links
1385 assert!(junction.is_dir());
1386 assert!(b.exists());
1387
1388 if !got_symlink_permission(&tmpdir) {
1389 return;
1390 };
1391 check!(symlink_dir(&target, &link));
1392 check!(fs::create_dir_all(&d));
1393 assert!(link.is_dir());
1394 assert!(d.exists());
1395}
1396
1397#[test]
1398fn metadata_access_times() {
1399 let tmpdir = tmpdir();
1400
1401 let b = tmpdir.join("b");
1402 File::create(&b).unwrap();
1403
1404 let a = check!(fs::metadata(&tmpdir.path()));
1405 let b = check!(fs::metadata(&b));
1406
1407 assert_eq!(check!(a.accessed()), check!(a.accessed()));
1408 assert_eq!(check!(a.modified()), check!(a.modified()));
1409 assert_eq!(check!(b.accessed()), check!(b.modified()));
1410
1411 if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
1412 check!(a.created());
1413 check!(b.created());
1414 }
1415
1416 if cfg!(target_os = "linux") {
1417 // Not always available
1418 match (a.created(), b.created()) {
1419 (Ok(t1), Ok(t2)) => assert!(t1 <= t2),
1420 (Err(e1), Err(e2))
136023e0
XL
1421 if e1.kind() == ErrorKind::Uncategorized
1422 && e2.kind() == ErrorKind::Uncategorized
cdc7bbd5
XL
1423 || e1.kind() == ErrorKind::Unsupported
1424 && e2.kind() == ErrorKind::Unsupported => {}
1b1a35ee 1425 (a, b) => {
5e7ed085 1426 panic!("creation time must be always supported or not supported: {a:?} {b:?}")
1b1a35ee
XL
1427 }
1428 }
1429 }
1430}
29967ef6
XL
1431
1432/// Test creating hard links to symlinks.
1433#[test]
1434fn symlink_hard_link() {
1435 let tmpdir = tmpdir();
fc512014
XL
1436 if !got_symlink_permission(&tmpdir) {
1437 return;
1438 };
136023e0
XL
1439 if !able_to_not_follow_symlinks_while_hard_linking() {
1440 return;
1441 }
29967ef6
XL
1442
1443 // Create "file", a file.
1444 check!(fs::File::create(tmpdir.join("file")));
1445
1446 // Create "symlink", a symlink to "file".
1447 check!(symlink_file("file", tmpdir.join("symlink")));
1448
1449 // Create "hard_link", a hard link to "symlink".
1450 check!(fs::hard_link(tmpdir.join("symlink"), tmpdir.join("hard_link")));
1451
1452 // "hard_link" should appear as a symlink.
1453 assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1454
a2a8927a 1455 // We should be able to open "file" via any of the above names.
29967ef6
XL
1456 let _ = check!(fs::File::open(tmpdir.join("file")));
1457 assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1458 let _ = check!(fs::File::open(tmpdir.join("symlink")));
1459 let _ = check!(fs::File::open(tmpdir.join("hard_link")));
1460
1461 // Rename "file" to "file.renamed".
1462 check!(fs::rename(tmpdir.join("file"), tmpdir.join("file.renamed")));
1463
1464 // Now, the symlink and the hard link should be dangling.
1465 assert!(fs::File::open(tmpdir.join("file")).is_err());
1466 let _ = check!(fs::File::open(tmpdir.join("file.renamed")));
1467 assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1468 assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1469
1470 // The symlink and the hard link should both still point to "file".
1471 assert!(fs::read_link(tmpdir.join("file")).is_err());
1472 assert!(fs::read_link(tmpdir.join("file.renamed")).is_err());
1473 assert_eq!(check!(fs::read_link(tmpdir.join("symlink"))), Path::new("file"));
1474 assert_eq!(check!(fs::read_link(tmpdir.join("hard_link"))), Path::new("file"));
1475
1476 // Remove "file.renamed".
1477 check!(fs::remove_file(tmpdir.join("file.renamed")));
1478
1479 // Now, we can't open the file by any name.
1480 assert!(fs::File::open(tmpdir.join("file")).is_err());
1481 assert!(fs::File::open(tmpdir.join("file.renamed")).is_err());
1482 assert!(fs::File::open(tmpdir.join("symlink")).is_err());
1483 assert!(fs::File::open(tmpdir.join("hard_link")).is_err());
1484
1485 // "hard_link" should still appear as a symlink.
1486 assert!(check!(fs::symlink_metadata(tmpdir.join("hard_link"))).file_type().is_symlink());
1487}
3c0e092e
XL
1488
1489/// Ensure `fs::create_dir` works on Windows with longer paths.
1490#[test]
1491#[cfg(windows)]
1492fn create_dir_long_paths() {
1493 use crate::{ffi::OsStr, iter, os::windows::ffi::OsStrExt};
1494 const PATH_LEN: usize = 247;
1495
1496 let tmpdir = tmpdir();
1497 let mut path = tmpdir.path().to_path_buf();
1498 path.push("a");
1499 let mut path = path.into_os_string();
1500
1501 let utf16_len = path.encode_wide().count();
1502 if utf16_len >= PATH_LEN {
1503 // Skip the test in the unlikely event the local user has a long temp directory path.
1504 // This should not affect CI.
1505 return;
1506 }
1507 // Increase the length of the path.
1508 path.extend(iter::repeat(OsStr::new("a")).take(PATH_LEN - utf16_len));
1509
1510 // This should succeed.
1511 fs::create_dir(&path).unwrap();
1512
1513 // This will fail if the path isn't converted to verbatim.
1514 path.push("a");
1515 fs::create_dir(&path).unwrap();
1516
1517 // #90940: Ensure an empty path returns the "Not Found" error.
1518 let path = Path::new("");
1519 assert_eq!(path.canonicalize().unwrap_err().kind(), crate::io::ErrorKind::NotFound);
1520}
5099ac24
FG
1521
1522/// Ensure ReadDir works on large directories.
1523/// Regression test for https://github.com/rust-lang/rust/issues/93384.
1524#[test]
1525fn read_large_dir() {
1526 let tmpdir = tmpdir();
1527
1528 let count = 32 * 1024;
1529 for i in 0..count {
1530 check!(fs::File::create(tmpdir.join(&i.to_string())));
1531 }
1532
1533 for entry in fs::read_dir(tmpdir.path()).unwrap() {
1534 entry.unwrap();
1535 }
1536}
064997fb
FG
1537
1538/// Test the fallback for getting the metadata of files like hiberfil.sys that
1539/// Windows holds a special lock on, preventing normal means of querying
1540/// metadata. See #96980.
1541///
1542/// Note this fails in CI because `hiberfil.sys` does not actually exist there.
1543/// Therefore it's marked as ignored.
1544#[test]
1545#[ignore]
1546#[cfg(windows)]
1547fn hiberfil_sys() {
1548 let hiberfil = Path::new(r"C:\hiberfil.sys");
1549 assert_eq!(true, hiberfil.try_exists().unwrap());
1550 fs::symlink_metadata(hiberfil).unwrap();
1551 fs::metadata(hiberfil).unwrap();
1552 assert_eq!(true, hiberfil.exists());
1553}
9c376795
FG
1554
1555/// Test that two different ways of obtaining the FileType give the same result.
1556/// Cf. https://github.com/rust-lang/rust/issues/104900
1557#[test]
1558fn test_eq_direntry_metadata() {
1559 let tmpdir = tmpdir();
1560 let file_path = tmpdir.join("file");
1561 File::create(file_path).unwrap();
1562 for e in fs::read_dir(tmpdir.path()).unwrap() {
1563 let e = e.unwrap();
1564 let p = e.path();
1565 let ft1 = e.file_type().unwrap();
1566 let ft2 = p.metadata().unwrap().file_type();
1567 assert_eq!(ft1, ft2);
1568 }
1569}
1570
1571/// Regression test for https://github.com/rust-lang/rust/issues/50619.
1572#[test]
1573#[cfg(target_os = "linux")]
1574fn test_read_dir_infinite_loop() {
1575 use crate::io::ErrorKind;
1576 use crate::process::Command;
1577
1578 // Create a zombie child process
1579 let Ok(mut child) = Command::new("echo").spawn() else { return };
1580
1581 // Make sure the process is (un)dead
1582 match child.kill() {
1583 // InvalidInput means the child already exited
1584 Err(e) if e.kind() != ErrorKind::InvalidInput => return,
1585 _ => {}
1586 }
1587
1588 // open() on this path will succeed, but readdir() will fail
1589 let id = child.id();
1590 let path = format!("/proc/{id}/net");
1591
1592 // Skip the test if we can't open the directory in the first place
1593 let Ok(dir) = fs::read_dir(path) else { return };
1594
1595 // Check for duplicate errors
1596 assert!(dir.filter(|e| e.is_err()).take(2).count() < 2);
1597}
9ffffee4
FG
1598
1599#[test]
1600fn rename_directory() {
1601 let tmpdir = tmpdir();
1602 let old_path = tmpdir.join("foo/bar/baz");
1603 fs::create_dir_all(&old_path).unwrap();
1604 let test_file = &old_path.join("temp.txt");
1605
1606 File::create(test_file).unwrap();
1607
1608 let new_path = tmpdir.join("quux/blat");
1609 fs::create_dir_all(&new_path).unwrap();
1610 fs::rename(&old_path, &new_path.join("newdir")).unwrap();
1611 assert!(new_path.join("newdir").is_dir());
1612 assert!(new_path.join("newdir/temp.txt").exists());
1613}