text: // Copyright 2019 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.
  
  #ifndef SRC_STORAGE_LIB_PAVER_ABR_CLIENT_H_
  #define SRC_STORAGE_LIB_PAVER_ABR_CLIENT_H_
  
  #include <fuchsia/paver/llcpp/fidl.h>
  #include <lib/abr/abr.h>
  #include <lib/zx/channel.h>
  #include <lib/zx/status.h>
  
  #include <memory>
  #include <vector>
  
  #include <fbl/unique_fd.h>
  
  #include "src/storage/lib/paver/partition-client.h"
  #include "src/storage/lib/paver/paver-context.h"
  
  namespace abr {
  
  zx::status<llcpp::fuchsia::paver::Configuration> QueryBootConfig(const zx::channel& svc_root);
  
  // Interface for interacting with ABR data.
  class Client {
   public:
    // Factory create method.
    static zx::status<std::unique_ptr<abr::Client>> Create(fbl::unique_fd devfs_root,
                                                           const zx::channel& svc_root,
                                                           std::shared_ptr<paver::Context> context);
    virtual ~Client() = default;
  
    AbrSlotIndex GetBootSlot(bool update_metadata, bool* is_slot_marked_successful) const {
      return AbrGetBootSlot(&abr_ops_, update_metadata, is_slot_marked_successful);
    }
  
    zx::status<> MarkSlotActive(AbrSlotIndex index) {
      return AbrResultToZxStatus(AbrMarkSlotActive(&abr_ops_, index));
    }
  
    zx::status<> MarkSlotUnbootable(AbrSlotIndex index) {
      return AbrResultToZxStatus(AbrMarkSlotUnbootable(&abr_ops_, index));
    }
  
    zx::status<> MarkSlotSuccessful(AbrSlotIndex index) {
      return AbrResultToZxStatus(AbrMarkSlotSuccessful(&abr_ops_, index));
    }
  
    zx::status<AbrSlotInfo> GetSlotInfo(AbrSlotIndex index) const {
      AbrSlotInfo info;
      auto status = AbrResultToZxStatus(AbrGetSlotInfo(&abr_ops_, index, &info));
      if (status.is_error()) {
        return status.take_error();
      }
      return zx::ok(info);
    }
  
    static zx::status<> AbrResultToZxStatus(AbrResult status);
  
    virtual zx::status<> Flush() const = 0;
  
    void InitializeAbrOps();
  
    Client() { abr_ops_ = {this, Client::ReadAbrMetaData, Client::WriteAbrMetaData}; }
  
    // No copy, move, assign.
    // This is to ensure that |abr_ops_| is always valid. |abr_ops_.context| shall always be
    // a |this| pointer to the Client instance that hosts |abr_ops_|. This may be
    // violated if we allow Client to be copied/moved/assign.
    Client(const Client&) = delete;
    Client& operator=(const Client&) = delete;
  
    Client(Client&&) = delete;
    Client& operator=(Client&&) = delete;
  
   private:
    AbrOps abr_ops_;
  
    // ReadAbrMetaData and WriteAbrMetaData will be assigned to fields in AbrOps
    static bool ReadAbrMetaData(void* context, size_t size, uint8_t* buffer);
  
    static bool WriteAbrMetaData(void* context, const uint8_t* buffer, size_t size);
  
    virtual zx::status<> Read(uint8_t* buffer, size_t size) = 0;
  
    virtual zx::status<> Write(const uint8_t* buffer, size_t size) = 0;
  };
  
  class ClientFactory {
   public:
    // Factory create method.
    static zx::status<std::unique_ptr<abr::Client>> Create(fbl::unique_fd devfs_root,
                                                           const zx::channel& svc_root,
                                                           std::shared_ptr<paver::Context> context);
  
    static void Register(std::unique_ptr<ClientFactory> factory);
  
    virtual ~ClientFactory() = default;
  
   private:
    virtual zx::status<std::unique_ptr<abr::Client>> New(fbl::unique_fd devfs_root,
                                                         const zx::channel& svc_root,
                                                         std::shared_ptr<paver::Context> context) = 0;
  
    static std::vector<std::unique_ptr<ClientFactory>>* registered_factory_list();
  };
  
  // Implementation of abr::Client which works with a contiguous partition storing AbrData.
  class AbrPartitionClient : public Client {
   public:
    // |partition| should contain AbrData with no offset.
    static zx::status<std::unique_ptr<abr::Client>> Create(
        std::unique_ptr<paver::PartitionClient> partition);
  
   private:
    AbrPartitionClient(std::unique_ptr<paver::PartitionClient> partition, zx::vmo vmo,
                       size_t block_size)
        : partition_(std::move(partition)), vmo_(std::move(vmo)), block_size_(block_size) {}
  
    zx::status<> Read(uint8_t* buffer, size_t size) override;
  
    zx::status<> Write(const uint8_t* buffer, size_t size) override;
  
    zx::status<> Flush() const override { return partition_->Flush(); }
  
    std::unique_ptr<paver::PartitionClient> partition_;
    zx::vmo vmo_;
    size_t block_size_;
  };
  
  }  // namespace abr
  
  #endif  // SRC_STORAGE_LIB_PAVER_ABR_CLIENT_H_
  