(&mut self)
| 237 | |
| 238 | #[must_use] |
| 239 | fn read_more(&mut self) -> Option<io::Result<()>> { |
| 240 | // The first few times we're called, we allocate a relatively small |
| 241 | // buffer, because many directories are small. If we're called more, |
| 242 | // use progressively larger allocations, up to a fixed maximum. |
| 243 | // |
| 244 | // The specific sizes and policy here have not been tuned in detail yet |
| 245 | // and may need to be adjusted. In doing so, we should be careful to |
| 246 | // avoid unbounded buffer growth. This buffer only exists to share the |
| 247 | // cost of a `getdents` call over many entries, so if it gets too big, |
| 248 | // cache and heap usage will outweigh the benefit. And ultimately, |
| 249 | // directories can contain more entries than we can allocate contiguous |
| 250 | // memory for, so we'll always need to cap the size at some point. |
| 251 | if self.buf.len() < 1024 * size_of::<linux_dirent64>() { |
| 252 | self.buf.reserve(32 * size_of::<linux_dirent64>()); |
| 253 | } |
| 254 | self.buf.resize(self.buf.capacity(), 0); |
| 255 | let nread = match io::retry_on_intr(|| { |
| 256 | crate::backend::fs::syscalls::getdents(self.fd.as_fd(), &mut self.buf) |
| 257 | }) { |
| 258 | Ok(nread) => nread, |
| 259 | Err(io::Errno::NOENT) => { |
| 260 | self.any_errors = true; |
| 261 | return None; |
| 262 | } |
| 263 | Err(err) => { |
| 264 | self.any_errors = true; |
| 265 | return Some(Err(err)); |
| 266 | } |
| 267 | }; |
| 268 | self.buf.resize(nread, 0); |
| 269 | self.pos = 0; |
| 270 | if nread == 0 { |
| 271 | None |
| 272 | } else { |
| 273 | Some(Ok(())) |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | /// `fstat(self)` |
| 278 | #[inline] |
no test coverage detected