Skip to content

Commit f73a48d

Browse files
osiewiczgitbot
authored and
gitbot
committed
wasi/fs: Improve stopping condition for <ReadDir as Iterator>::next
When upgrading [Zed](zed-industries/zed#19349) to Rust 1.82 I've encountered a test failure in our test suite. Specifically, one of our extension tests started hanging. I've tracked it down to a call to std::fs::remove_dir_all not returning when an extension is compiled with Rust 1.82 Our extension system uses WASM components, thus I've looked at the diff between 1.81 and 1.82 with respect to WASI and found a87feee4ca9a673bf6aad673fcb09a2ec9f805dc As it turned out, calling remove_dir_all from extension returned io::ErrorKind::NotFound in 1.81; the underlying issue is that the ReadDir iterator never actually terminates iteration, however since it loops around, with 1.81 we'd come across an entry second time and fail to remove it, since it would've been removed previously. With 1.82 and a87feee4ca9a673bf6aad673fcb09a2ec9f805dc it is no longer the case, thus we're seeing the hang. This commit makes ReadDir::next adhere to readdir contract, namely it will no longer call readdir once the returned # of bytes is smaller than the size of a passed-in buffer. Previously we'd only terminate the loop if readdir returned 0.
1 parent ab4bfc9 commit f73a48d

File tree

1 file changed

+17
-14
lines changed
  • std/src/sys/pal/wasi

1 file changed

+17
-14
lines changed

std/src/sys/pal/wasi/fs.rs

+17-14
Original file line numberDiff line numberDiff line change
@@ -172,20 +172,22 @@ impl Iterator for ReadDir {
172172
let offset = if self.offset == self.cap {
173173
let cookie = self.cookie.take()?;
174174
match self.inner.dir.fd.readdir(&mut self.buf, cookie) {
175-
Ok(bytes) => self.cap = bytes,
175+
Ok(bytes) => {
176+
// No more entries if we read less than buffer size
177+
if bytes < self.buf.len() {
178+
self.cookie = None;
179+
if bytes == 0 {
180+
return None;
181+
}
182+
} else {
183+
self.cookie = Some(cookie);
184+
}
185+
self.cap = self.buf.len();
186+
self.offset = 0;
187+
0
188+
}
176189
Err(e) => return Some(Err(e)),
177190
}
178-
self.offset = 0;
179-
self.cookie = Some(cookie);
180-
181-
// If we didn't actually read anything, this is in theory the
182-
// end of the directory.
183-
if self.cap == 0 {
184-
self.cookie = None;
185-
return None;
186-
}
187-
188-
0
189191
} else {
190192
self.offset
191193
};
@@ -197,7 +199,6 @@ impl Iterator for ReadDir {
197199
// where we last left off.
198200
let dirent_size = mem::size_of::<wasi::Dirent>();
199201
if data.len() < dirent_size {
200-
assert!(self.cookie.is_some());
201202
assert!(self.buf.len() >= dirent_size);
202203
self.offset = self.cap;
203204
continue;
@@ -218,7 +219,9 @@ impl Iterator for ReadDir {
218219
self.offset = self.cap;
219220
continue;
220221
}
221-
self.cookie = Some(dirent.d_next);
222+
self.cookie.as_mut().map(|cookie| {
223+
*cookie = dirent.d_next;
224+
});
222225
self.offset = offset + dirent_size + dirent.d_namlen as usize;
223226

224227
let name = &data[..(dirent.d_namlen as usize)];

0 commit comments

Comments
 (0)