[util][dirent] On Fuchsia use zxio

Bug: 40999

Change-Id: I55450ed77b3cb3cfb0818fe372b05421c94bcf01
diff --git a/src/util/BUILD.gn b/src/util/BUILD.gn
index 26a2f63..9031325 100644
--- a/src/util/BUILD.gn
+++ b/src/util/BUILD.gn
@@ -113,7 +113,7 @@
       "$magma_build_root/src/magma_util/platform:futex",
       "$magma_build_root/src/magma_util:common",
       "//zircon/public/lib/zx",
-      "//zircon/system/fidl/fuchsia-io:llcpp",
+      "//zircon/public/lib/zxio",
     ]
   } else {
     sources += [ "anon_file.c" ]
diff --git a/src/util/os_dirent_fuchsia.cpp b/src/util/os_dirent_fuchsia.cpp
index 11efa39..8fdcf7d 100644
--- a/src/util/os_dirent_fuchsia.cpp
+++ b/src/util/os_dirent_fuchsia.cpp
@@ -7,13 +7,11 @@
 #include "list.h"
 #include "os/fuchsia.h"
 
-#include <fuchsia/io/llcpp/fidl.h>
-#include <lib/zx/channel.h>
+#include <lib/zxio/inception.h>
+#include <lib/zxio/zxio.h>
 #include <magma_util/dlog.h>
 
-namespace fio = llcpp::fuchsia::io;
-
-// Offsets must match struct os_dir, static_asserts below.
+// Offsets must match struct os_dirent, static_asserts below.
 struct os_dirent_impl {
    ino_t d_ino;
    char d_name[NAME_MAX];
@@ -24,62 +22,77 @@
 static_assert(offsetof(struct os_dirent_impl, d_name) == offsetof(struct os_dirent, d_name),
               "d_ino offset mismatch");
 
-// From io.fidl:
-struct __attribute__((__packed__)) dirent {
-   uint64_t ino;
-   uint8_t size;
-   uint8_t type;
-   char name[0];
-};
-
-static zx_status_t readdir(const zx::channel& control_channel, void* buffer, size_t capacity,
-                           size_t* out_actual)
-{
-   // Explicitly allocating message buffers to avoid heap allocation.
-   fidl::Buffer<fio::Directory::ReadDirentsRequest> request_buffer;
-   fidl::Buffer<fio::Directory::ReadDirentsResponse> response_buffer;
-   auto result =
-       fio::Directory::Call::ReadDirents(zx::unowned_channel(control_channel),
-                                         request_buffer.view(), capacity, response_buffer.view());
-   if (result.status() != ZX_OK)
-      return result.status();
-
-   fio::Directory::ReadDirentsResponse* response = result.Unwrap();
-   if (response->s != ZX_OK)
-      return response->s;
-
-   const auto& dirents = response->dirents;
-   if (dirents.count() > capacity)
-      return ZX_ERR_IO;
-
-   memcpy(buffer, dirents.data(), dirents.count());
-   *out_actual = dirents.count();
-   return response->s;
-}
-
-struct os_dir {
+class os_dir {
+public:
    os_dir() { buffer = new char[buffer_size()]; }
-   ~os_dir() { delete [] buffer; }
 
-   static size_t buffer_size() { return 4096; }
+   ~os_dir()
+   {
+      if (dir_init) {
+         zxio_close(&io_storage.io);
+      }
+      delete[] buffer;
+   }
 
-   zx::channel control_channel;
+   static size_t buffer_size() { return ZXIO_DIRENT_ITERATOR_DEFAULT_BUFFER_SIZE; }
+
+   // Always consumes |dir_channel|
+   bool Init(zx_handle_t dir_channel)
+   {
+      zx_status_t status = zxio_dir_init(&io_storage, dir_channel);
+      if (status != ZX_OK) {
+         DLOG("zxio_dir_init failed: %d", status);
+         return false;
+      }
+      dir_init = true;
+
+      status = zxio_dirent_iterator_init(&iterator, &io_storage.io, buffer, buffer_size());
+      if (status != ZX_OK) {
+         DLOG("zxio_dirent_iterator_init failed: %d", status);
+         return false;
+      }
+
+      return true;
+   }
+
+   struct os_dirent_impl* Next()
+   {
+      zxio_dirent_t* dirent;
+      zx_status_t status = zxio_dirent_iterator_next(&iterator, &dirent);
+      if (status != ZX_OK) {
+         DLOG("zxio_dirent_iterator_next failed: %d", status);
+         return nullptr;
+      }
+
+      entry.d_ino = dirent->inode;
+      strncpy(entry.d_name, dirent->name, dirent->size);
+      entry.d_name[dirent->size] = 0;
+
+      return &entry;
+   }
+
+private:
+   bool dir_init = false;
+   zxio_storage_t io_storage;
+   zxio_dirent_iterator_t iterator;
    struct os_dirent_impl entry;
    char* buffer = nullptr;
-   size_t count = 0;
-   size_t index = 0;
 };
 
 os_dir_t* os_opendir(const char* path)
 {
-   zx::channel control_channel;
-   if (!fuchsia_open(path, control_channel.reset_and_get_address())) {
+   zx_handle_t dir_channel;
+   if (!fuchsia_open(path, &dir_channel)) {
       DLOG("fuchsia_open(%s) failed\n", path);
       return nullptr;
    }
 
    auto dir = new os_dir();
-   dir->control_channel = std::move(control_channel);
+
+   if (!dir->Init(dir_channel)) {
+      delete dir;
+      return nullptr;
+   }
 
    return dir;
 }
@@ -91,38 +104,4 @@
    return 0;
 }
 
-struct os_dirent* os_readdir(os_dir_t* dir)
-{
-   if (dir->index >= dir->count) {
-      size_t count;
-      zx_status_t status =
-          readdir(dir->control_channel, dir->buffer, os_dir::buffer_size(), &count);
-      if (status != ZX_OK) {
-         DLOG("os_readdir: readdir failed: %d\n", status);
-         return nullptr;
-      }
-
-      dir->count = count;
-      dir->index = 0;
-   }
-
-   if (dir->index + sizeof(dirent) > dir->count) {
-      DLOG("os_readdir: no more entries");
-      return nullptr;
-   }
-
-   dirent* entry = reinterpret_cast<dirent*>(&dir->buffer[dir->index]);
-   size_t entry_size = sizeof(dirent) + entry->size;
-
-   if (dir->index + entry_size > dir->count) {
-      DLOG("os_readdir: last entry incomplete (this shouldn't happen)");
-      return nullptr;
-   }
-   dir->index += entry_size;
-
-   dir->entry.d_ino = entry->ino;
-   strncpy(dir->entry.d_name, entry->name, entry->size);
-   dir->entry.d_name[entry->size] = 0;
-
-   return reinterpret_cast<os_dirent*>(&dir->entry);
-}
+struct os_dirent* os_readdir(os_dir_t* dir) { return reinterpret_cast<os_dirent*>(dir->Next()); }