Due to the unfortunate design of wasi::fd_readdir, It's nearly impossible to provide a correct implementation. The below implementation is just a workaround for most usages and may not be correct in some edge cases. The readdir entry API is going to be updated to use a stream type, so we don't have to deal with it right now.
| 497 | // readdir entry API is going to be updated to use a stream type, so we don't |
| 498 | // have to deal with it right now. |
| 499 | WasiExpect<void> INode::fdReaddir(Span<uint8_t> Buffer, |
| 500 | __wasi_dircookie_t Cookie, |
| 501 | __wasi_size_t &Size) noexcept { |
| 502 | if (unlikely(!Dir.ok())) { |
| 503 | if (FdHolder NewFd(::dup(Fd)); unlikely(!NewFd.ok())) { |
| 504 | return WasiUnexpect(fromErrNo(errno)); |
| 505 | } else if (DIR *D = ::fdopendir(NewFd.Fd); unlikely(!D)) { |
| 506 | return WasiUnexpect(fromErrNo(errno)); |
| 507 | } else { |
| 508 | NewFd.release(); |
| 509 | Dir.emplace(D); |
| 510 | } |
| 511 | } |
| 512 | |
| 513 | if (Cookie == 0) { |
| 514 | ::rewinddir(Dir.Dir); |
| 515 | } else if (unlikely(Cookie != Dir.Cookie)) { |
| 516 | ::seekdir(Dir.Dir, Cookie); |
| 517 | } |
| 518 | |
| 519 | Size = 0; |
| 520 | do { |
| 521 | if (!Dir.Buffer.empty()) { |
| 522 | const auto NewDataSize = |
| 523 | std::min<uint32_t>(Buffer.size(), Dir.Buffer.size()); |
| 524 | std::copy(Dir.Buffer.begin(), Dir.Buffer.begin() + NewDataSize, |
| 525 | Buffer.begin()); |
| 526 | Buffer = Buffer.subspan(NewDataSize); |
| 527 | Size += NewDataSize; |
| 528 | Dir.Buffer.clear(); |
| 529 | if (unlikely(Buffer.empty())) { |
| 530 | break; |
| 531 | } |
| 532 | } |
| 533 | errno = 0; |
| 534 | dirent *SysDirent = ::readdir(Dir.Dir); |
| 535 | if (SysDirent == nullptr) { |
| 536 | if (errno != 0) { |
| 537 | return WasiUnexpect(fromErrNo(errno)); |
| 538 | } |
| 539 | // End of entries |
| 540 | break; |
| 541 | } |
| 542 | Dir.Cookie = SysDirent->d_off; |
| 543 | std::string_view Name = SysDirent->d_name; |
| 544 | |
| 545 | Dir.Buffer.resize(sizeof(__wasi_dirent_t) + Name.size()); |
| 546 | |
| 547 | __wasi_dirent_t *const Dirent = |
| 548 | reinterpret_cast<__wasi_dirent_t *>(Dir.Buffer.data()); |
| 549 | Dirent->d_next = Dir.Cookie; |
| 550 | Dirent->d_ino = SysDirent->d_ino; |
| 551 | Dirent->d_type = fromFileType(SysDirent->d_type); |
| 552 | Dirent->d_namlen = Name.size(); |
| 553 | std::copy(Name.cbegin(), Name.cend(), |
| 554 | Dir.Buffer.begin() + sizeof(__wasi_dirent_t)); |
| 555 | } while (!Buffer.empty()); |
| 556 | Size = EndianValue(Size).le(); |
nothing calls this directly
no test coverage detected