Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 808bf915 authored by Dennis Shen's avatar Dennis Shen
Browse files

aconfig: update aconfig_storage_read_api c++ lib

Now the read api lib no longer owns the memory mapped file. The rust lib
aconfig_storage_read_api api to get memory mapped file just returns an
Mmap rust struct. However cxx does not support Mmap data type, so we
cannot export the api to get memory mapped file from rust to c++. This
means we have to implement c++ apis to get memory mapped files.

This change is basically implementing c++ api to get memory mapped files,
mirroring the rust implementation. In addition, the integration test is
also updated to lock down the new c++ api implementation.

Bug: b/321077378
Test: atest aconfig_storage_read_api.test.cpp
Change-Id: I5e93a220cdd05cc0e5b0eb441284ac3e9dc34da7
parent 71f9303d
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -73,4 +73,9 @@ cc_library_static {
    generated_sources: ["libcxx_aconfig_storage_read_api_bridge_code"],
    whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"],
    export_include_dirs: ["include"],
    static_libs: [
        "libaconfig_storage_protos_cc",
        "libprotobuf-cpp-lite",
        "libbase",
    ],
}
+147 −66
Original line number Diff line number Diff line
#include "aconfig_storage/aconfig_storage_read_api.hpp"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/result.h>
#include <protos/aconfig_storage_metadata.pb.h>

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "rust/cxx.h"
#include "aconfig_storage/lib.rs.h"
#include "aconfig_storage/aconfig_storage_read_api.hpp"

using storage_records_pb = android::aconfig_storage_metadata::storage_files;
using storage_record_pb = android::aconfig_storage_metadata::storage_file_info;
using namespace android::base;

namespace aconfig_storage {

/// Storage location pb file
static constexpr char kAvailableStorageRecordsPb[] =
    "/metadata/aconfig/available_storage_file_records.pb";

/// Read aconfig storage records pb file
static Result<storage_records_pb> read_storage_records_pb(std::string const& pb_file) {
  auto records = storage_records_pb();
  auto content = std::string();
  if (!ReadFileToString(pb_file, &content)) {
    return ErrnoError() << "ReadFileToString failed";
  }

  if (!records.ParseFromString(content)) {
    return ErrnoError() << "Unable to parse persistent storage records protobuf";
  }
  return records;
}

/// Get storage file path
static Result<std::string> find_storage_file(
    std::string const& pb_file,
    std::string const& container,
    StorageFileType file_type) {
  auto records_pb = read_storage_records_pb(pb_file);
  if (!records_pb.ok()) {
    return Error() << "Unable to read storage records from " << pb_file
                   << " : " << records_pb.error();
  }

  for (auto& entry : records_pb->files()) {
    if (entry.container() == container) {
      switch(file_type) {
        case StorageFileType::package_map:
          return entry.package_map();
        case StorageFileType::flag_map:
          return entry.flag_map();
        case StorageFileType::flag_val:
          return entry.flag_val();
        default:
          return Error() << "Invalid file type " << file_type;
      }
    }
  }

  return Error() << "Unable to find storage files for container " << container;;
}

/// Map a storage file
static Result<MappedStorageFile> map_storage_file(std::string const& file) {
  int fd = open(file.c_str(), O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
  if (fd == -1) {
    return Error() << "failed to open " << file;
  };

  struct stat fd_stat;
  if (fstat(fd, &fd_stat) < 0) {
    return Error() << "fstat failed";
  }

  if ((fd_stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) {
    return Error() << "cannot map writeable file";
  }

  size_t file_size = fd_stat.st_size;

  void* const map_result = mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
  if (map_result == MAP_FAILED) {
    return Error() << "mmap failed";
  }

  auto mapped_file = MappedStorageFile();
  mapped_file.file_ptr = map_result;
  mapped_file.file_size = file_size;

  return mapped_file;
}

namespace private_internal_api {

/// Get mapped file implementation.
MappedStorageFileQuery get_mapped_file_impl(
    std::string const& pb_file,
    std::string const& container,
    StorageFileType file_type) {
  auto query =  MappedStorageFileQuery();

  auto file_result = find_storage_file(pb_file, container, file_type);
  if (!file_result.ok()) {
    query.query_success = false;
    query.error_message = file_result.error().message();
    query.mapped_file.file_ptr = nullptr;
    query.mapped_file.file_size = 0;
    return query;
  }

  auto mapped_file_result = map_storage_file(*file_result);
  if (!mapped_file_result.ok()) {
    query.query_success = false;
    query.error_message = mapped_file_result.error().message();
    query.mapped_file.file_ptr = nullptr;
    query.mapped_file.file_size = 0;
  } else {
    query.query_success = true;
    query.error_message = "";
    query.mapped_file = *mapped_file_result;
  }

  return query;
}

} // namespace private internal api

/// Get mapped storage file
MappedStorageFileQuery get_mapped_file(
    std::string const& container,
    StorageFileType file_type) {
  return private_internal_api::get_mapped_file_impl(
      kAvailableStorageRecordsPb, container, file_type);
}

/// Get storage file version number
VersionNumberQuery get_storage_file_version(
    std::string const& file_path) {
@@ -18,11 +151,11 @@ VersionNumberQuery get_storage_file_version(

/// Get package offset
PackageOffsetQuery get_package_offset(
    std::string const& container,
    MappedStorageFile const& file,
    std::string const& package) {
  auto offset_cxx = get_package_offset_cxx(
      rust::Str(container.c_str()),
      rust::Str(package.c_str()));
  auto content = rust::Slice<const uint8_t>(
      static_cast<uint8_t*>(file.file_ptr), file.file_size);
  auto offset_cxx = get_package_offset_cxx(content, rust::Str(package.c_str()));
  auto offset = PackageOffsetQuery();
  offset.query_success = offset_cxx.query_success;
  offset.error_message = std::string(offset_cxx.error_message.c_str());
@@ -34,13 +167,12 @@ PackageOffsetQuery get_package_offset(

/// Get flag offset
FlagOffsetQuery get_flag_offset(
    std::string const& container,
    MappedStorageFile const& file,
    uint32_t package_id,
    std::string const& flag_name){
  auto offset_cxx = get_flag_offset_cxx(
      rust::Str(container.c_str()),
      package_id,
      rust::Str(flag_name.c_str()));
  auto content = rust::Slice<const uint8_t>(
      static_cast<uint8_t*>(file.file_ptr), file.file_size);
  auto offset_cxx = get_flag_offset_cxx(content, package_id, rust::Str(flag_name.c_str()));
  auto offset = FlagOffsetQuery();
  offset.query_success = offset_cxx.query_success;
  offset.error_message = std::string(offset_cxx.error_message.c_str());
@@ -51,11 +183,11 @@ FlagOffsetQuery get_flag_offset(

/// Get boolean flag value
BooleanFlagValueQuery get_boolean_flag_value(
    std::string const& container,
    MappedStorageFile const& file,
    uint32_t offset) {
  auto value_cxx = get_boolean_flag_value_cxx(
      rust::Str(container.c_str()),
      offset);
  auto content = rust::Slice<const uint8_t>(
      static_cast<uint8_t*>(file.file_ptr), file.file_size);
  auto value_cxx = get_boolean_flag_value_cxx(content, offset);
  auto value = BooleanFlagValueQuery();
  value.query_success = value_cxx.query_success;
  value.error_message = std::string(value_cxx.error_message.c_str());
@@ -63,55 +195,4 @@ BooleanFlagValueQuery get_boolean_flag_value(
  return value;
}

namespace test_only_api {
PackageOffsetQuery get_package_offset_impl(
    std::string const& pb_file,
    std::string const& container,
    std::string const& package) {
  auto offset_cxx =  get_package_offset_cxx_impl(
      rust::Str(pb_file.c_str()),
      rust::Str(container.c_str()),
      rust::Str(package.c_str()));
  auto offset = PackageOffsetQuery();
  offset.query_success = offset_cxx.query_success;
  offset.error_message = std::string(offset_cxx.error_message.c_str());
  offset.package_exists = offset_cxx.package_exists;
  offset.package_id = offset_cxx.package_id;
  offset.boolean_offset = offset_cxx.boolean_offset;
  return offset;
}

FlagOffsetQuery get_flag_offset_impl(
    std::string const& pb_file,
    std::string const& container,
    uint32_t package_id,
    std::string const& flag_name) {
  auto offset_cxx =  get_flag_offset_cxx_impl(
      rust::Str(pb_file.c_str()),
      rust::Str(container.c_str()),
      package_id,
      rust::Str(flag_name.c_str()));
  auto offset = FlagOffsetQuery();
  offset.query_success = offset_cxx.query_success;
  offset.error_message = std::string(offset_cxx.error_message.c_str());
  offset.flag_exists = offset_cxx.flag_exists;
  offset.flag_offset = offset_cxx.flag_offset;
  return offset;
}

BooleanFlagValueQuery get_boolean_flag_value_impl(
    std::string const& pb_file,
    std::string const& container,
    uint32_t offset) {
  auto value_cxx =  get_boolean_flag_value_cxx_impl(
      rust::Str(pb_file.c_str()),
      rust::Str(container.c_str()),
      offset);
  auto value = BooleanFlagValueQuery();
  value.query_success = value_cxx.query_success;
  value.error_message = std::string(value_cxx.error_message.c_str());
  value.flag_value = value_cxx.flag_value;
  return value;
}
} // namespace test_only_api
} // namespace aconfig_storage
+44 −24
Original line number Diff line number Diff line
@@ -5,6 +5,36 @@

namespace aconfig_storage {

/// Storage file type enum
enum StorageFileType {
  package_map,
  flag_map,
  flag_val
};

/// Mapped storage file
struct MappedStorageFile {
  void* file_ptr;
  size_t file_size;
};

/// Mapped storage file query
struct MappedStorageFileQuery {
  bool query_success;
  std::string error_message;
  MappedStorageFile mapped_file;
};

/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
namespace private_internal_api {

MappedStorageFileQuery get_mapped_file_impl(
    std::string const& pb_file,
    std::string const& container,
    StorageFileType file_type);

} // namespace private_internal_api

/// Storage version number query result
struct VersionNumberQuery {
  bool query_success;
@@ -36,6 +66,14 @@ struct BooleanFlagValueQuery {
  bool flag_value;
};

/// Get mapped storage file
/// \input container: stoarge container name
/// \input file_type: storage file type enum
/// \returns a MappedStorageFileQuery
MappedStorageFileQuery get_mapped_file(
    std::string const& container,
    StorageFileType file_type);

/// Get storage file version number
/// \input file_path: the path to the storage file
/// \returns a VersionNumberQuery
@@ -43,47 +81,29 @@ VersionNumberQuery get_storage_file_version(
    std::string const& file_path);

/// Get package offset
/// \input container: the flag container name
/// \input file: mapped storage file
/// \input package: the flag package name
/// \returns a PackageOffsetQuery
PackageOffsetQuery get_package_offset(
    std::string const& container,
    MappedStorageFile const& file,
    std::string const& package);

/// Get flag offset
/// \input container: the flag container name
/// \input file: mapped storage file
/// \input package_id: the flag package id obtained from package offset query
/// \input flag_name: flag name
/// \returns a FlagOffsetQuery
FlagOffsetQuery get_flag_offset(
    std::string const& container,
    MappedStorageFile const& file,
    uint32_t package_id,
    std::string const& flag_name);

/// Get boolean flag value
/// \input container: the flag container name
/// \input file: mapped storage file
/// \input offset: the boolean flag value byte offset in the file
/// \returns a BooleanFlagValueQuery
BooleanFlagValueQuery get_boolean_flag_value(
    std::string const& container,
    MappedStorageFile const& file,
    uint32_t offset);

/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE, TEST ONLY
namespace test_only_api {
PackageOffsetQuery get_package_offset_impl(
    std::string const& pb_file,
    std::string const& container,
    std::string const& package);

FlagOffsetQuery get_flag_offset_impl(
    std::string const& pb_file,
    std::string const& container,
    uint32_t package_id,
    std::string const& flag_name);

BooleanFlagValueQuery get_boolean_flag_value_impl(
    std::string const& pb_file,
    std::string const& container,
    uint32_t offset);
} // namespace test_only_api
} // namespace aconfig_storage
+92 −43
Original line number Diff line number Diff line
@@ -26,14 +26,10 @@
#include <android-base/result.h>

using android::aconfig_storage_metadata::storage_files;
using ::android::base::ReadFileToString;
using ::android::base::WriteStringToFile;
using ::android::base::Result;
using ::android::base::Error;
using ::aconfig_storage::test_only_api::get_package_offset_impl;
using ::aconfig_storage::test_only_api::get_flag_offset_impl;
using ::aconfig_storage::test_only_api::get_boolean_flag_value_impl;
using ::aconfig_storage::get_storage_file_version;
using namespace android::base;

namespace api = aconfig_storage;
namespace private_api = aconfig_storage::private_internal_api;

class AconfigStorageTest : public ::testing::Test {
 protected:
@@ -46,7 +42,7 @@ class AconfigStorageTest : public ::testing::Test {
    if (!WriteStringToFile(content, temp_file)) {
      return Error() << "failed to copy file: " << source_file;
    }
    if (chmod(temp_file.c_str(), S_IRUSR | S_IRGRP) == -1) {
    if (chmod(temp_file.c_str(), S_IRUSR | S_IRGRP | S_IROTH) == -1) {
      return Error() << "failed to make file read only";
    }
    return temp_file;
@@ -95,41 +91,80 @@ class AconfigStorageTest : public ::testing::Test {
  std::string storage_record_pb;
};


/// Test to lock down storage file version query api
TEST_F(AconfigStorageTest, test_storage_version_query) {
  auto query = get_storage_file_version(package_map);
  auto query = api::get_storage_file_version(package_map);
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_EQ(query.version_number, 1);
  query = get_storage_file_version(flag_map);
  query = api::get_storage_file_version(flag_map);
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_EQ(query.version_number, 1);
  query = get_storage_file_version(flag_val);
  query = api::get_storage_file_version(flag_val);
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_EQ(query.version_number, 1);
}

/// Negative test to lock down the error when mapping none exist storage files
TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "vendor", api::StorageFileType::package_map);
  ASSERT_FALSE(mapped_file_query.query_success);
  ASSERT_EQ(mapped_file_query.error_message,
            "Unable to find storage files for container vendor");
}

/// Negative test to lock down the error when mapping a writeable storage file
TEST_F(AconfigStorageTest, test_writable_storage_file_mapping) {
  ASSERT_TRUE(chmod(package_map.c_str(),
                    S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH) != -1);
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::package_map);
  ASSERT_FALSE(mapped_file_query.query_success);
  ASSERT_EQ(mapped_file_query.error_message, "cannot map writeable file");

  ASSERT_TRUE(chmod(flag_map.c_str(),
                    S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH) != -1);
  mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::flag_map);
  ASSERT_FALSE(mapped_file_query.query_success);
  ASSERT_EQ(mapped_file_query.error_message, "cannot map writeable file");

  ASSERT_TRUE(chmod(flag_val.c_str(),
                    S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH) != -1);
  mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::flag_val);
  ASSERT_FALSE(mapped_file_query.query_success);
  ASSERT_EQ(mapped_file_query.error_message, "cannot map writeable file");
}

/// Test to lock down storage package offset query api
TEST_F(AconfigStorageTest, test_package_offset_query) {
  auto query = get_package_offset_impl(
      storage_record_pb, "system", "com.android.aconfig.storage.test_1");
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::package_map);
  ASSERT_TRUE(mapped_file_query.query_success);
  auto mapped_file = mapped_file_query.mapped_file;

  auto query = api::get_package_offset(
      mapped_file, "com.android.aconfig.storage.test_1");
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_TRUE(query.package_exists);
  ASSERT_EQ(query.package_id, 0);
  ASSERT_EQ(query.boolean_offset, 0);

  query = get_package_offset_impl(
      storage_record_pb, "system", "com.android.aconfig.storage.test_2");
  query = api::get_package_offset(
      mapped_file, "com.android.aconfig.storage.test_2");
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_TRUE(query.package_exists);
  ASSERT_EQ(query.package_id, 1);
  ASSERT_EQ(query.boolean_offset, 3);

  query = get_package_offset_impl(
      storage_record_pb, "system", "com.android.aconfig.storage.test_4");
  query = api::get_package_offset(
      mapped_file, "com.android.aconfig.storage.test_4");
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_TRUE(query.package_exists);
@@ -137,21 +172,27 @@ TEST_F(AconfigStorageTest, test_package_offset_query) {
  ASSERT_EQ(query.boolean_offset, 6);
}

TEST_F(AconfigStorageTest, test_invalid_package_offset_query) {
  auto query = get_package_offset_impl(
      storage_record_pb, "system", "com.android.aconfig.storage.test_3");
/// Test to lock down when querying none exist package
TEST_F(AconfigStorageTest, test_none_existent_package_offset_query) {
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::package_map);
  ASSERT_TRUE(mapped_file_query.query_success);
  auto mapped_file = mapped_file_query.mapped_file;

  auto query = api::get_package_offset(
      mapped_file, "com.android.aconfig.storage.test_3");
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_FALSE(query.package_exists);

  query = get_package_offset_impl(
      storage_record_pb, "vendor", "com.android.aconfig.storage.test_1");
  ASSERT_EQ(query.error_message,
            std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
  ASSERT_FALSE(query.query_success);
}

/// Test to lock down storage flag offset query api
TEST_F(AconfigStorageTest, test_flag_offset_query) {
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::flag_map);
  ASSERT_TRUE(mapped_file_query.query_success);
  auto mapped_file = mapped_file_query.mapped_file;

  auto baseline = std::vector<std::tuple<int, std::string, int>>{
    {0, "enabled_ro", 1},
    {0, "enabled_rw", 2},
@@ -163,7 +204,7 @@ TEST_F(AconfigStorageTest, test_flag_offset_query) {
    {0, "disabled_rw", 0},
  };
  for (auto const&[package_id, flag_name, expected_offset] : baseline) {
    auto query = get_flag_offset_impl(storage_record_pb, "system", package_id, flag_name);
    auto query = api::get_flag_offset(mapped_file, package_id, flag_name);
    ASSERT_EQ(query.error_message, std::string());
    ASSERT_TRUE(query.query_success);
    ASSERT_TRUE(query.flag_exists);
@@ -171,39 +212,47 @@ TEST_F(AconfigStorageTest, test_flag_offset_query) {
  }
}

TEST_F(AconfigStorageTest, test_invalid_flag_offset_query) {
  auto query = get_flag_offset_impl(storage_record_pb, "system", 0, "none_exist");
/// Test to lock down when querying none exist flag
TEST_F(AconfigStorageTest, test_none_existent_flag_offset_query) {
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::flag_map);
  ASSERT_TRUE(mapped_file_query.query_success);
  auto mapped_file = mapped_file_query.mapped_file;

  auto query = api::get_flag_offset(mapped_file, 0, "none_exist");
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_FALSE(query.flag_exists);

  query = get_flag_offset_impl(storage_record_pb, "system", 3, "enabled_ro");
  query = api::get_flag_offset(mapped_file, 3, "enabled_ro");
  ASSERT_EQ(query.error_message, std::string());
  ASSERT_TRUE(query.query_success);
  ASSERT_FALSE(query.flag_exists);

  query = get_flag_offset_impl(storage_record_pb, "vendor", 0, "enabled_ro");
  ASSERT_EQ(query.error_message,
            std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
  ASSERT_FALSE(query.query_success);
}

/// Test to lock down storage flag value query api
TEST_F(AconfigStorageTest, test_boolean_flag_value_query) {
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::flag_val);
  ASSERT_TRUE(mapped_file_query.query_success);
  auto mapped_file = mapped_file_query.mapped_file;

  for (int offset = 0; offset < 8; ++offset) {
    auto query = get_boolean_flag_value_impl(storage_record_pb, "system", offset);
    auto query = api::get_boolean_flag_value(mapped_file, offset);
    ASSERT_EQ(query.error_message, std::string());
    ASSERT_TRUE(query.query_success);
    ASSERT_FALSE(query.flag_value);
  }
}

/// Negative test to lock down the error when querying flag value out of range
TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_query) {
  auto query = get_boolean_flag_value_impl(storage_record_pb, "vendor", 0);
  ASSERT_EQ(query.error_message,
            std::string("StorageFileNotFound(Storage file does not exist for vendor)"));
  ASSERT_FALSE(query.query_success);
  auto mapped_file_query = private_api::get_mapped_file_impl(
      storage_record_pb, "system", api::StorageFileType::flag_val);
  ASSERT_TRUE(mapped_file_query.query_success);
  auto mapped_file = mapped_file_query.mapped_file;

  query = get_boolean_flag_value_impl(storage_record_pb, "system", 8);
  auto query = api::get_boolean_flag_value(mapped_file, 8);
  ASSERT_EQ(query.error_message,
            std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
  ASSERT_FALSE(query.query_success);