]>
Commit | Line | Data |
---|---|---|
1b1a35ee XL |
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; | |
3c0e092e | 7 | use crate::sync::Arc; |
1b1a35ee XL |
8 | use crate::sys_common::io::test::{tmpdir, TempDir}; |
9 | use crate::thread; | |
3c0e092e | 10 | use crate::time::{Duration, Instant}; |
1b1a35ee XL |
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; | |
136023e0 XL |
24 | #[cfg(target_os = "macos")] |
25 | use crate::sys::weak::weak; | |
26 | #[cfg(target_os = "macos")] | |
27 | use libc::{c_char, c_int}; | |
1b1a35ee XL |
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), | |
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)] | |
51 | macro_rules! error { | |
52 | ($e:expr, $s:expr) => { | |
53 | error_contains!($e, $s) | |
54 | }; | |
55 | } | |
56 | ||
57 | macro_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. | |
74 | pub 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")] |
88 | fn 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"))] | |
94 | fn able_to_not_follow_symlinks_while_hard_linking() -> bool { | |
95 | return true; | |
96 | } | |
97 | ||
1b1a35ee XL |
98 | #[test] |
99 | fn 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] | |
120 | fn 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] | |
134 | fn 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] | |
149 | fn 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] | |
175 | fn 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] | |
202 | fn 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] | |
226 | fn 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] | |
258 | fn 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)] | |
273 | fn 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)] | |
329 | fn 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)] | |
351 | fn 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] | |
404 | fn 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] | |
424 | fn 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] | |
436 | fn 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] | |
445 | fn 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] | |
455 | fn 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] | |
467 | fn 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] | |
496 | fn 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] | |
505 | fn 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] | |
514 | fn 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] | |
522 | fn 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] | |
536 | fn 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] | |
557 | fn recursive_mkdir_slash() { | |
558 | check!(fs::create_dir_all(Path::new("/"))); | |
559 | } | |
560 | ||
561 | #[test] | |
562 | fn recursive_mkdir_dot() { | |
563 | check!(fs::create_dir_all(Path::new("."))); | |
564 | } | |
565 | ||
566 | #[test] | |
567 | fn recursive_mkdir_empty() { | |
568 | check!(fs::create_dir_all(Path::new(""))); | |
569 | } | |
570 | ||
571 | #[test] | |
572 | fn 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] | |
591 | fn 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] |
607 | fn 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)] | |
624 | fn 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 | |
642 | fn 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] |
694 | fn 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] | |
713 | fn 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] | |
726 | fn 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] | |
740 | fn 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] | |
753 | fn 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] | |
768 | fn 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] | |
780 | fn 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] | |
795 | fn 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] | |
807 | fn 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)] | |
824 | fn 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] | |
835 | fn 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] | |
847 | fn 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] | |
869 | fn 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] | |
888 | fn 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] | |
902 | fn 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] | |
921 | fn 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] | |
930 | fn 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] | |
956 | fn 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] | |
979 | fn 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] | |
997 | fn 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] | |
1010 | fn 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] | |
1044 | fn 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] | |
1168 | fn _assert_send_sync() { | |
1169 | fn _assert_send_sync<T: Send + Sync>() {} | |
1170 | _assert_send_sync::<OpenOptions>(); | |
1171 | } | |
1172 | ||
1173 | #[test] | |
1174 | fn 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] | |
1187 | fn 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] | |
1210 | fn 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))] | |
1230 | fn 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] | |
1241 | fn 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] | |
1248 | fn 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] | |
1257 | fn 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] | |
1284 | fn 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] | |
1315 | fn 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] | |
1338 | fn 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] | |
1349 | fn 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] | |
1355 | fn 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] | |
1384 | fn 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] | |
1420 | fn 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)] | |
1478 | fn 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] | |
1511 | fn 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 | } |