1 #[test]
test_dir()2 fn test_dir() {
3 let t = rustix::fs::openat(
4 rustix::fs::cwd(),
5 rustix::cstr!("."),
6 rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC,
7 rustix::fs::Mode::empty(),
8 )
9 .unwrap();
10
11 let mut dir = rustix::fs::Dir::read_from(&t).unwrap();
12
13 let _file = rustix::fs::openat(
14 &t,
15 rustix::cstr!("Cargo.toml"),
16 rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC,
17 rustix::fs::Mode::empty(),
18 )
19 .unwrap();
20
21 // Read the directory entries. We use `while let Some(entry)` so that we
22 // don't consume the `Dir` so that we can run more tests on it.
23 let mut saw_dot = false;
24 let mut saw_dotdot = false;
25 let mut saw_cargo_toml = false;
26 while let Some(entry) = dir.read() {
27 let entry = entry.unwrap();
28 if entry.file_name() == rustix::cstr!(".") {
29 saw_dot = true;
30 } else if entry.file_name() == rustix::cstr!("..") {
31 saw_dotdot = true;
32 } else if entry.file_name() == rustix::cstr!("Cargo.toml") {
33 saw_cargo_toml = true;
34 }
35 }
36 assert!(saw_dot);
37 assert!(saw_dotdot);
38 assert!(saw_cargo_toml);
39
40 // Rewind the directory so we can iterate over the entries again.
41 dir.rewind();
42
43 // For what comes next, we don't need `mut` anymore.
44 let dir = dir;
45
46 // Read the directory entries, again. This time we use `for entry in dir`.
47 let mut saw_dot = false;
48 let mut saw_dotdot = false;
49 let mut saw_cargo_toml = false;
50 for entry in dir {
51 let entry = entry.unwrap();
52 if entry.file_name() == rustix::cstr!(".") {
53 saw_dot = true;
54 } else if entry.file_name() == rustix::cstr!("..") {
55 saw_dotdot = true;
56 } else if entry.file_name() == rustix::cstr!("Cargo.toml") {
57 saw_cargo_toml = true;
58 }
59 }
60 assert!(saw_dot);
61 assert!(saw_dotdot);
62 assert!(saw_cargo_toml);
63 }
64
65 // Test that `Dir` silently stops iterating if the directory has been removed.
66 //
67 // Except on FreeBSD and macOS, where apparently `readdir` just keeps reading.
68 #[cfg_attr(
69 any(
70 target_os = "macos",
71 target_os = "ios",
72 target_os = "watchos",
73 target_os = "tvos",
74 target_os = "freebsd",
75 target_os = "dragonflybsd"
76 ),
77 ignore
78 )]
79 #[test]
dir_iterator_handles_dir_removal()80 fn dir_iterator_handles_dir_removal() {
81 // create a dir, keep the FD, then delete the dir
82 let tmp = tempfile::tempdir().unwrap();
83 let fd = rustix::fs::openat(
84 rustix::fs::cwd(),
85 tmp.path(),
86 rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC,
87 rustix::fs::Mode::empty(),
88 )
89 .unwrap();
90
91 // Drop the `TempDir`, which deletes the directory.
92 drop(tmp);
93
94 let mut dir = rustix::fs::Dir::read_from(&fd).unwrap();
95 assert!(matches!(dir.next(), None));
96 }
97
98 // Like `dir_iterator_handles_dir_removal`, but close the directory after
99 // `Dir::read_from`.
100 #[cfg_attr(
101 any(
102 target_os = "macos",
103 target_os = "ios",
104 target_os = "watchos",
105 target_os = "tvos",
106 target_os = "freebsd",
107 target_os = "dragonflybsd"
108 ),
109 ignore
110 )]
111 #[test]
dir_iterator_handles_dir_removal_after_open()112 fn dir_iterator_handles_dir_removal_after_open() {
113 // create a dir, keep the FD, then delete the dir
114 let tmp = tempfile::tempdir().unwrap();
115 let fd = rustix::fs::openat(
116 rustix::fs::cwd(),
117 tmp.path(),
118 rustix::fs::OFlags::RDONLY | rustix::fs::OFlags::CLOEXEC,
119 rustix::fs::Mode::empty(),
120 )
121 .unwrap();
122
123 let mut dir = rustix::fs::Dir::read_from(&fd).unwrap();
124
125 // Drop the `TempDir`, which deletes the directory.
126 drop(tmp);
127
128 assert!(matches!(dir.next(), None));
129 }
130