#include "src/devices/block/drivers/block-verity/device-manager.h"

#include <fidl/fuchsia.hardware.block.verified/cpp/wire.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/driver.h>
#include <lib/ddk/platform-defs.h>
#include <threads.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <zircon/status.h>

#include <memory>

#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/macros.h>

#include "src/devices/block/drivers/block-verity/block-verity-bind.h"
#include "src/devices/block/drivers/block-verity/config.h"
#include "src/devices/block/drivers/block-verity/device.h"
#include "src/devices/block/drivers/block-verity/superblock-verifier.h"
#include "src/devices/block/drivers/block-verity/verified-device.h"

namespace block_verity {
namespace {

void SealCompletedCallback(void* cookie, zx_status_t status, const uint8_t* seal_buf,
                           size_t seal_len) {
  auto device_manager = static_cast<DeviceManager*>(cookie);
  device_manager->OnSealCompleted(status, seal_buf, seal_len);
}

void SuperblockVerificationCallback(void* cookie, zx_status_t status,
                                    const Superblock* superblock) {
  auto device_manager = static_cast<DeviceManager*>(cookie);
  device_manager->OnSuperblockVerificationCompleted(status, superblock);
}

}  

zx_status_t DeviceManager::Create(void* ctx, zx_device_t* parent) {
  zx_status_t rc;
  fbl::AllocChecker ac;

  auto manager = fbl::make_unique_checked<DeviceManager>(&ac, parent);
  if (!ac.check()) {
    zxlogf(ERROR, "failed to allocate %zu bytes", sizeof(DeviceManager));
    return ZX_ERR_NO_MEMORY;
  }

  if ((rc = manager->Bind()) != ZX_OK) {
    zxlogf(ERROR, "failed to bind: %s", zx_status_get_string(rc));
    return rc;
  }

  __UNUSED auto* owned_by_devmgr_now = manager.release();

  return ZX_OK;
}

zx_status_t DeviceManager::Bind() {
  zx_status_t rc;
  fbl::AutoLock lock(&mtx_);

  if ((rc = DdkAdd(ddk::DeviceAddArgs("verity").set_flags(DEVICE_ADD_NON_BINDABLE))) != ZX_OK) {
    zxlogf(ERROR, "failed to add verity device: %s", zx_status_get_string(rc));
    state_ = kRemoved;
    return rc;
  }

  state_ = kClosed;
  return ZX_OK;
}

void DeviceManager::DdkUnbind(ddk::UnbindTxn txn) {
  fbl::AutoLock lock(&mtx_);

  state_ = kRemoved;

  txn.Reply();
}

void DeviceManager::DdkRelease() { delete this; }

void DeviceManager::DdkChildPreRelease(void* child_ctx) {
  fbl::AutoLock lock(&mtx_);

  switch (state_) {
    case kAuthoring:
    case kVerifiedRead:

      mutable_child_ = std::nullopt;
      verified_child_ = std::nullopt;
      state_ = kError;
      break;
    case kClosing:
      ZX_ASSERT(mutable_child_.has_value() || verified_child_.has_value());
      if (mutable_child_.has_value()) {
        ZX_ASSERT(child_ctx == *mutable_child_);
      }
      if (verified_child_.has_value()) {
        ZX_ASSERT(child_ctx == *verified_child_);
      }
      ZX_ASSERT(close_completer_.has_value());
      mutable_child_ = std::nullopt;
      verified_child_ = std::nullopt;
      close_completer_->ReplySuccess();
      close_completer_ = std::nullopt;
      state_ = kClosed;
      break;
    case kClosingForSeal: {
      state_ = kSealing;
      mutable_child_ = std::nullopt;

      DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
      if (!info.IsValid()) {
        zxlogf(ERROR, "failed to get valid device info");
        seal_completer_->ReplyError(ZX_ERR_BAD_STATE);
        seal_completer_ = std::nullopt;
        state_ = kError;
        return;
      }
      sealer_ = std::make_unique<DriverSealer>(std::move(info));

      zx_status_t result = sealer_->StartSealing(this, SealCompletedCallback);
      if (result != ZX_OK) {
        zxlogf(ERROR, "sealer failed to start: %d", result);
        state_ = kError;
      }
      break;
    }
    case kBinding:
    case kClosed:
    case kSealing:
    case kVerifiedReadCheck:
    case kError:
    case kUnbinding:
    case kRemoved:
      zxlogf(ERROR, "Got unexpected child prerelease notification while in state %d", state_);
      break;
  }
}

void DeviceManager::OpenForWrite(OpenForWriteRequestView request,
                                 OpenForWriteCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  if (state_ != kClosed) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  block_info_t blk;
  size_t op_size;
  ddk::BlockProtocolClient block_protocol_client(parent());
  block_protocol_client.Query(&blk, &op_size);

  zx_status_t rc = CheckConfig(request->config, blk);
  if (rc != ZX_OK) {
    zxlogf(WARNING, "Refusing OpenForWrite: invalid config");
    async_completer.ReplyError(rc);
    return;
  }

  fbl::AllocChecker ac;
  DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
  if (!info.IsValid()) {
    zxlogf(ERROR, "failed to get valid device info");
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  auto device = fbl::make_unique_checked<block_verity::Device>(&ac, zxdev(), std::move(info));
  if (!ac.check()) {
    zxlogf(ERROR, "failed to allocate %zu bytes", sizeof(Device));
    async_completer.ReplyError(ZX_ERR_NO_MEMORY);
    return;
  }

  if ((rc = device->DdkAdd("mutable")) != ZX_OK) {
    zxlogf(ERROR, "failed to add mutable device: %s", zx_status_get_string(rc));
    async_completer.ReplyError(rc);
    return;
  }
  zxlogf(INFO, "added block-verity mutable child");

  mutable_child_ = device.release();

  state_ = kAuthoring;
  async_completer.ReplySuccess();
}

void DeviceManager::CloseAndGenerateSeal(CloseAndGenerateSealRequestView request,
                                         CloseAndGenerateSealCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  fuchsia_hardware_block_verified::wire::Seal seal;
  if (state_ != kAuthoring) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  state_ = kClosingForSeal;
  (*mutable_child_)->DdkAsyncRemove();

  seal_completer_ = std::move(async_completer);
}

void DeviceManager::OnSealCompleted(zx_status_t status, const uint8_t* seal_buf, size_t seal_len) {
  fbl::AutoLock lock(&mtx_);
  ZX_ASSERT(state_ == kSealing);
  ZX_ASSERT(seal_completer_.has_value());
  ZX_ASSERT(seal_len == 32);

  if (status == ZX_OK) {

    fuchsia_hardware_block_verified::wire::Sha256Seal sha256;
    memcpy(sha256.superblock_hash.begin(), seal_buf, seal_len);

    seal_completer_->ReplySuccess(fuchsia_hardware_block_verified::wire::Seal::WithSha256(
        fidl::ObjectView<fuchsia_hardware_block_verified::wire::Sha256Seal>::FromExternal(
            &sha256)));
  } else {
    zxlogf(WARNING, "Sealer returned failure: %s", zx_status_get_string(status));
    seal_completer_->ReplyError(status);
  }

  state_ = kClosed;
  sealer_.reset();
  seal_completer_ = std::nullopt;
}

void DeviceManager::OnSuperblockVerificationCompleted(zx_status_t status,
                                                      const Superblock* superblock) {
  fbl::AutoLock lock(&mtx_);
  ZX_ASSERT(state_ == kVerifiedReadCheck);
  ZX_ASSERT(open_for_verified_read_completer_.has_value());

  if (status != ZX_OK) {
    zxlogf(WARNING, "Superblock verifier returned failure: %s", zx_status_get_string(status));
    CompleteOpenForVerifiedRead(status);
    return;
  }

  fbl::AllocChecker ac;
  DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
  if (!info.IsValid()) {
    zxlogf(ERROR, "failed to get valid device info");
    CompleteOpenForVerifiedRead(ZX_ERR_BAD_STATE);
    return;
  }

  std::array<uint8_t, kHashOutputSize> integrity_root_hash;
  memcpy(integrity_root_hash.data(), superblock->integrity_root_hash, kHashOutputSize);

  auto device = fbl::make_unique_checked<block_verity::VerifiedDevice>(
      &ac, zxdev(), std::move(info), integrity_root_hash);
  if (!ac.check()) {
    zxlogf(ERROR, "failed to allocate %zu bytes", sizeof(Device));
    CompleteOpenForVerifiedRead(ZX_ERR_NO_MEMORY);
    return;
  }

  zx_status_t rc;
  if ((rc = device->Init()) != ZX_OK) {
    zxlogf(ERROR, "failed to prepare verified device: %s", zx_status_get_string(rc));
    CompleteOpenForVerifiedRead(rc);
    return;
  }

  if ((rc = device->DdkAdd("verified")) != ZX_OK) {
    zxlogf(ERROR, "failed to add verified device: %s", zx_status_get_string(rc));
    CompleteOpenForVerifiedRead(rc);
    return;
  }
  zxlogf(INFO, "added block-verity verified child");

  verified_child_ = device.release();
  CompleteOpenForVerifiedRead(ZX_OK);
}

void DeviceManager::CompleteOpenForVerifiedRead(zx_status_t status) {
  if (status != ZX_OK) {
    open_for_verified_read_completer_->ReplyError(status);
    state_ = kClosed;
  } else {
    open_for_verified_read_completer_->ReplySuccess();
    state_ = kVerifiedRead;
  }

  open_for_verified_read_completer_ = std::nullopt;
  superblock_verifier_.reset();
}

void DeviceManager::OpenForVerifiedRead(OpenForVerifiedReadRequestView request,
                                        OpenForVerifiedReadCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  if (state_ != kClosed) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  block_info_t blk;
  size_t op_size;
  ddk::BlockProtocolClient block_protocol_client(parent());
  block_protocol_client.Query(&blk, &op_size);

  zx_status_t rc = CheckConfig(request->config, blk);
  if (rc != ZX_OK) {
    zxlogf(WARNING, "Refusing OpenForVerifiedRead: invalid config");
    async_completer.ReplyError(rc);
    return;
  }

  open_for_verified_read_completer_ = std::move(async_completer);

  DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
  std::array<uint8_t, kHashOutputSize> expected_hash;
  memcpy(expected_hash.data(), request->seal.sha256().superblock_hash.data(), kHashOutputSize);
  superblock_verifier_ = std::make_unique<SuperblockVerifier>(std::move(info), expected_hash);
  state_ = kVerifiedReadCheck;
  superblock_verifier_->StartVerifying(this, SuperblockVerificationCallback);
}

void DeviceManager::Close(CloseRequestView request, CloseCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  if (state_ != kAuthoring && state_ != kVerifiedRead) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  state_ = kClosing;
  if (mutable_child_.has_value()) {
    (*mutable_child_)->DdkAsyncRemove();
  }
  if (verified_child_.has_value()) {
    (*verified_child_)->DdkAsyncRemove();
  }

  close_completer_ = std::move(async_completer);
}

static constexpr zx_driver_ops_t driver_ops = []() {
  zx_driver_ops_t ops = {};
  ops.version = DRIVER_OPS_VERSION;
  ops.bind = DeviceManager::Create;
  return ops;
}();

}  

ZIRCON_DRIVER(block_verity, block_verity::driver_ops, "zircon", "0.1");
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/devices/block/drivers/block-verity/device-manager.h"

#include <fidl/fuchsia.hardware.block.verified/cpp/wire.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/driver.h>
#include <lib/ddk/platform-defs.h>
#include <threads.h>
#include <zircon/assert.h>
#include <zircon/errors.h>
#include <zircon/status.h>

#include <memory>

#include <ddktl/device.h>
#include <ddktl/fidl.h>
#include <fbl/alloc_checker.h>
#include <fbl/auto_lock.h>
#include <fbl/macros.h>

#include "src/devices/block/drivers/block-verity/block-verity-bind.h"
#include "src/devices/block/drivers/block-verity/config.h"
#include "src/devices/block/drivers/block-verity/device.h"
#include "src/devices/block/drivers/block-verity/superblock-verifier.h"
#include "src/devices/block/drivers/block-verity/verified-device.h"

namespace block_verity {
namespace {

void SealCompletedCallback(void* cookie, zx_status_t status, const uint8_t* seal_buf,
                           size_t seal_len) {
  auto device_manager = static_cast<DeviceManager*>(cookie);
  device_manager->OnSealCompleted(status, seal_buf, seal_len);
}

void SuperblockVerificationCallback(void* cookie, zx_status_t status,
                                    const Superblock* superblock) {
  auto device_manager = static_cast<DeviceManager*>(cookie);
  device_manager->OnSuperblockVerificationCompleted(status, superblock);
}

}  // namespace

zx_status_t DeviceManager::Create(void* ctx, zx_device_t* parent) {
  zx_status_t rc;
  fbl::AllocChecker ac;

  auto manager = fbl::make_unique_checked<DeviceManager>(&ac, parent);
  if (!ac.check()) {
    zxlogf(ERROR, "failed to allocate %zu bytes", sizeof(DeviceManager));
    return ZX_ERR_NO_MEMORY;
  }

  if ((rc = manager->Bind()) != ZX_OK) {
    zxlogf(ERROR, "failed to bind: %s", zx_status_get_string(rc));
    return rc;
  }

  // devmgr is now in charge of the memory for `manager`.
  __UNUSED auto* owned_by_devmgr_now = manager.release();

  return ZX_OK;
}

zx_status_t DeviceManager::Bind() {
  zx_status_t rc;
  fbl::AutoLock lock(&mtx_);

  if ((rc = DdkAdd(ddk::DeviceAddArgs("verity").set_flags(DEVICE_ADD_NON_BINDABLE))) != ZX_OK) {
    zxlogf(ERROR, "failed to add verity device: %s", zx_status_get_string(rc));
    state_ = kRemoved;
    return rc;
  }

  state_ = kClosed;
  return ZX_OK;
}

void DeviceManager::DdkUnbind(ddk::UnbindTxn txn) {
  fbl::AutoLock lock(&mtx_);
  // Mark the device as getting-removed, so we refuse all other FIDL calls.
  state_ = kRemoved;

  // Signal that unbind is completed; child devices can be removed
  txn.Reply();
}

void DeviceManager::DdkRelease() { delete this; }

void DeviceManager::DdkChildPreRelease(void* child_ctx) {
  fbl::AutoLock lock(&mtx_);

  switch (state_) {
    case kAuthoring:
    case kVerifiedRead:
      // The underlying device disappeared unexpectedly.  Drop our reference to
      // it, and mark our state as kError so we don't wind up doing anything
      // dangerous.
      mutable_child_ = std::nullopt;
      verified_child_ = std::nullopt;
      state_ = kError;
      break;
    case kClosing:
      ZX_ASSERT(mutable_child_.has_value() || verified_child_.has_value());
      if (mutable_child_.has_value()) {
        ZX_ASSERT(child_ctx == *mutable_child_);
      }
      if (verified_child_.has_value()) {
        ZX_ASSERT(child_ctx == *verified_child_);
      }
      ZX_ASSERT(close_completer_.has_value());
      mutable_child_ = std::nullopt;
      verified_child_ = std::nullopt;
      close_completer_->ReplySuccess();
      close_completer_ = std::nullopt;
      state_ = kClosed;
      break;
    case kClosingForSeal: {
      state_ = kSealing;
      mutable_child_ = std::nullopt;
      // Now that the mutable device is unbound and about to release, we can
      // start generating integrity data.
      DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
      if (!info.IsValid()) {
        zxlogf(ERROR, "failed to get valid device info");
        seal_completer_->ReplyError(ZX_ERR_BAD_STATE);
        seal_completer_ = std::nullopt;
        state_ = kError;
        return;
      }
      sealer_ = std::make_unique<DriverSealer>(std::move(info));
      // The sealer will recompute and write out all verified block data, update
      // the superblock, issue a flush, and then return the hash of the
      // superblock.
      zx_status_t result = sealer_->StartSealing(this, SealCompletedCallback);
      if (result != ZX_OK) {
        zxlogf(ERROR, "sealer failed to start: %d", result);
        state_ = kError;
      }
      break;
    }
    case kBinding:
    case kClosed:
    case kSealing:
    case kVerifiedReadCheck:
    case kError:
    case kUnbinding:
    case kRemoved:
      zxlogf(ERROR, "Got unexpected child prerelease notification while in state %d", state_);
      break;
  }
}

void DeviceManager::OpenForWrite(OpenForWriteRequestView request,
                                 OpenForWriteCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  if (state_ != kClosed) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  block_info_t blk;
  size_t op_size;
  ddk::BlockProtocolClient block_protocol_client(parent());
  block_protocol_client.Query(&blk, &op_size);

  // Check args
  zx_status_t rc = CheckConfig(request->config, blk);
  if (rc != ZX_OK) {
    zxlogf(WARNING, "Refusing OpenForWrite: invalid config");
    async_completer.ReplyError(rc);
    return;
  }

  // If we make it to here, all arguments have been validated.
  // Go ahead and create the mutable child device.

  fbl::AllocChecker ac;
  DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
  if (!info.IsValid()) {
    zxlogf(ERROR, "failed to get valid device info");
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  auto device = fbl::make_unique_checked<block_verity::Device>(&ac, zxdev(), std::move(info));
  if (!ac.check()) {
    zxlogf(ERROR, "failed to allocate %zu bytes", sizeof(Device));
    async_completer.ReplyError(ZX_ERR_NO_MEMORY);
    return;
  }

  if ((rc = device->DdkAdd("mutable")) != ZX_OK) {
    zxlogf(ERROR, "failed to add mutable device: %s", zx_status_get_string(rc));
    async_completer.ReplyError(rc);
    return;
  }
  zxlogf(INFO, "added block-verity mutable child");

  // devmgr now owns the memory for `device`, but it'll send us a
  // ChildPreRelease hook notification before it destroys it.
  mutable_child_ = device.release();

  state_ = kAuthoring;
  async_completer.ReplySuccess();
}

void DeviceManager::CloseAndGenerateSeal(CloseAndGenerateSealRequestView request,
                                         CloseAndGenerateSealCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  fuchsia_hardware_block_verified::wire::Seal seal;
  if (state_ != kAuthoring) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  // Unbind the appropriate child device.  We'll wait for the prerelease hook to be
  // called to ensure that new reads and writes have quiesced before we start sealing.
  state_ = kClosingForSeal;
  (*mutable_child_)->DdkAsyncRemove();

  // Stash the completer somewhere so we can signal it when we've finished
  // generating the seal.
  seal_completer_ = std::move(async_completer);
}

void DeviceManager::OnSealCompleted(zx_status_t status, const uint8_t* seal_buf, size_t seal_len) {
  fbl::AutoLock lock(&mtx_);
  ZX_ASSERT(state_ == kSealing);
  ZX_ASSERT(seal_completer_.has_value());
  ZX_ASSERT(seal_len == 32);

  if (status == ZX_OK) {
    // Assemble the result struct and reply with success
    fuchsia_hardware_block_verified::wire::Sha256Seal sha256;
    memcpy(sha256.superblock_hash.begin(), seal_buf, seal_len);

    seal_completer_->ReplySuccess(fuchsia_hardware_block_verified::wire::Seal::WithSha256(
        fidl::ObjectView<fuchsia_hardware_block_verified::wire::Sha256Seal>::FromExternal(
            &sha256)));
  } else {
    zxlogf(WARNING, "Sealer returned failure: %s", zx_status_get_string(status));
    seal_completer_->ReplyError(status);
  }

  // Clean up.
  state_ = kClosed;
  sealer_.reset();
  seal_completer_ = std::nullopt;
}

void DeviceManager::OnSuperblockVerificationCompleted(zx_status_t status,
                                                      const Superblock* superblock) {
  fbl::AutoLock lock(&mtx_);
  ZX_ASSERT(state_ == kVerifiedReadCheck);
  ZX_ASSERT(open_for_verified_read_completer_.has_value());

  if (status != ZX_OK) {
    zxlogf(WARNING, "Superblock verifier returned failure: %s", zx_status_get_string(status));
    CompleteOpenForVerifiedRead(status);
    return;
  }

  // Great, looks good.  Let's set up that VerifiedDevice with the superblock
  // root hash.
  fbl::AllocChecker ac;
  DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
  if (!info.IsValid()) {
    zxlogf(ERROR, "failed to get valid device info");
    CompleteOpenForVerifiedRead(ZX_ERR_BAD_STATE);
    return;
  }

  // Extract integrity root hash from superblock.
  std::array<uint8_t, kHashOutputSize> integrity_root_hash;
  memcpy(integrity_root_hash.data(), superblock->integrity_root_hash, kHashOutputSize);

  // Allocate device.
  auto device = fbl::make_unique_checked<block_verity::VerifiedDevice>(
      &ac, zxdev(), std::move(info), integrity_root_hash);
  if (!ac.check()) {
    zxlogf(ERROR, "failed to allocate %zu bytes", sizeof(Device));
    CompleteOpenForVerifiedRead(ZX_ERR_NO_MEMORY);
    return;
  }

  zx_status_t rc;
  if ((rc = device->Init()) != ZX_OK) {
    zxlogf(ERROR, "failed to prepare verified device: %s", zx_status_get_string(rc));
    CompleteOpenForVerifiedRead(rc);
    return;
  }

  if ((rc = device->DdkAdd("verified")) != ZX_OK) {
    zxlogf(ERROR, "failed to add verified device: %s", zx_status_get_string(rc));
    CompleteOpenForVerifiedRead(rc);
    return;
  }
  zxlogf(INFO, "added block-verity verified child");

  // devmgr now owns the memory for `device`, but it'll send us a
  // ChildPreRelease hook notification before it destroys it.
  verified_child_ = device.release();
  CompleteOpenForVerifiedRead(ZX_OK);
}

void DeviceManager::CompleteOpenForVerifiedRead(zx_status_t status) {
  if (status != ZX_OK) {
    open_for_verified_read_completer_->ReplyError(status);
    state_ = kClosed;
  } else {
    open_for_verified_read_completer_->ReplySuccess();
    state_ = kVerifiedRead;
  }

  open_for_verified_read_completer_ = std::nullopt;
  superblock_verifier_.reset();
}

void DeviceManager::OpenForVerifiedRead(OpenForVerifiedReadRequestView request,
                                        OpenForVerifiedReadCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  if (state_ != kClosed) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  block_info_t blk;
  size_t op_size;
  ddk::BlockProtocolClient block_protocol_client(parent());
  block_protocol_client.Query(&blk, &op_size);

  // Check args.
  zx_status_t rc = CheckConfig(request->config, blk);
  if (rc != ZX_OK) {
    zxlogf(WARNING, "Refusing OpenForVerifiedRead: invalid config");
    async_completer.ReplyError(rc);
    return;
  }

  // Stash the completer somewhere so we can signal it when we've finished
  // verifying the superblock.
  open_for_verified_read_completer_ = std::move(async_completer);

  // Load superblock.  Check seal.  Check config matches seal.
  DeviceInfo info = DeviceInfo::CreateFromDevice(parent());
  std::array<uint8_t, kHashOutputSize> expected_hash;
  memcpy(expected_hash.data(), request->seal.sha256().superblock_hash.data(), kHashOutputSize);
  superblock_verifier_ = std::make_unique<SuperblockVerifier>(std::move(info), expected_hash);
  state_ = kVerifiedReadCheck;
  superblock_verifier_->StartVerifying(this, SuperblockVerificationCallback);
}

void DeviceManager::Close(CloseRequestView request, CloseCompleter::Sync& completer) {
  fbl::AutoLock lock(&mtx_);
  auto async_completer = completer.ToAsync();
  if (state_ != kAuthoring && state_ != kVerifiedRead) {
    async_completer.ReplyError(ZX_ERR_BAD_STATE);
    return;
  }

  // Request the appropriate child be removed.
  state_ = kClosing;
  if (mutable_child_.has_value()) {
    (*mutable_child_)->DdkAsyncRemove();
  }
  if (verified_child_.has_value()) {
    (*verified_child_)->DdkAsyncRemove();
  }

  // Stash the completer somewhere so we can signal it when we get the
  // DdkChildPreRelease hook call.
  close_completer_ = std::move(async_completer);
}

static constexpr zx_driver_ops_t driver_ops = []() {
  zx_driver_ops_t ops = {};
  ops.version = DRIVER_OPS_VERSION;
  ops.bind = DeviceManager::Create;
  return ops;
}();

}  // namespace block_verity

ZIRCON_DRIVER(block_verity, block_verity::driver_ops, "zircon", "0.1");
