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