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