Merge changes from topic "flag_info_write" into main
* changes: aconfig: create flag info file write c api aconfig: create flag info file write rust api
This commit is contained in:
commit
82546539f1
14 changed files with 793 additions and 172 deletions
|
@ -51,7 +51,9 @@ pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
|
|||
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
|
||||
pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
|
||||
|
||||
use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit, InvalidStoredFlagType};
|
||||
use crate::AconfigStorageError::{
|
||||
BytesParseFail, HashTableSizeLimit, InvalidFlagValueType, InvalidStoredFlagType,
|
||||
};
|
||||
|
||||
/// Storage file version
|
||||
pub const FILE_VERSION: u32 = 1;
|
||||
|
@ -103,7 +105,7 @@ impl TryFrom<u8> for StorageFileType {
|
|||
}
|
||||
|
||||
/// Flag type enum as stored by storage file
|
||||
/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATOBILITY. THE MAX IS U16.
|
||||
/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum StoredFlagType {
|
||||
ReadWriteBoolean = 0,
|
||||
|
@ -124,6 +126,36 @@ impl TryFrom<u16> for StoredFlagType {
|
|||
}
|
||||
}
|
||||
|
||||
/// Flag value type enum, one FlagValueType maps to many StoredFlagType
|
||||
/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum FlagValueType {
|
||||
Boolean = 0,
|
||||
}
|
||||
|
||||
impl TryFrom<StoredFlagType> for FlagValueType {
|
||||
type Error = AconfigStorageError;
|
||||
|
||||
fn try_from(value: StoredFlagType) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
StoredFlagType::ReadWriteBoolean => Ok(Self::Boolean),
|
||||
StoredFlagType::ReadOnlyBoolean => Ok(Self::Boolean),
|
||||
StoredFlagType::FixedReadOnlyBoolean => Ok(Self::Boolean),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for FlagValueType {
|
||||
type Error = AconfigStorageError;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
x if x == Self::Boolean as u16 => Ok(Self::Boolean),
|
||||
_ => Err(InvalidFlagValueType(anyhow!("Invalid flag value type"))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Storage query api error
|
||||
#[non_exhaustive]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
|
@ -163,6 +195,9 @@ pub enum AconfigStorageError {
|
|||
|
||||
#[error("invalid stored flag type")]
|
||||
InvalidStoredFlagType(#[source] anyhow::Error),
|
||||
|
||||
#[error("invalid flag value type")]
|
||||
InvalidFlagValueType(#[source] anyhow::Error),
|
||||
}
|
||||
|
||||
/// Get the right hash table size given number of entries in the table. Use a
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
namespace aconfig_storage {
|
||||
|
||||
/// Storage file type enum
|
||||
/// Storage file type enum, to be consistent with the one defined in
|
||||
/// aconfig_storage_file/src/lib.rs
|
||||
enum StorageFileType {
|
||||
package_map,
|
||||
flag_map,
|
||||
|
@ -14,6 +15,29 @@ enum StorageFileType {
|
|||
flag_info
|
||||
};
|
||||
|
||||
/// Flag type enum, to be consistent with the one defined in
|
||||
/// aconfig_storage_file/src/lib.rs
|
||||
enum StoredFlagType {
|
||||
ReadWriteBoolean = 0,
|
||||
ReadOnlyBoolean = 1,
|
||||
FixedReadOnlyBoolean = 2,
|
||||
};
|
||||
|
||||
/// Flag value type enum, to be consistent with the one defined in
|
||||
/// aconfig_storage_file/src/lib.rs
|
||||
enum FlagValueType {
|
||||
Boolean = 0,
|
||||
};
|
||||
|
||||
/// Flag info enum, to be consistent with the one defined in
|
||||
/// aconfig_storage_file/src/flag_info.rs
|
||||
enum FlagInfoBit {
|
||||
IsSticky = 1<<0,
|
||||
IsReadWrite = 1<<1,
|
||||
HasOverride = 1<<2,
|
||||
};
|
||||
|
||||
|
||||
/// Mapped storage file
|
||||
struct MappedStorageFile {
|
||||
void* file_ptr;
|
||||
|
@ -27,13 +51,6 @@ struct PackageReadContext {
|
|||
uint32_t boolean_start_index;
|
||||
};
|
||||
|
||||
/// Flag type enum, to be consistent with the one defined in aconfig_storage_file/src/lib.rs
|
||||
enum StoredFlagType {
|
||||
ReadWriteBoolean = 0,
|
||||
ReadOnlyBoolean = 1,
|
||||
FixedReadOnlyBoolean = 2,
|
||||
};
|
||||
|
||||
/// Flag read context query result
|
||||
struct FlagReadContext {
|
||||
bool flag_exists;
|
||||
|
@ -91,13 +108,6 @@ android::base::Result<bool> get_boolean_flag_value(
|
|||
MappedStorageFile const& file,
|
||||
uint32_t index);
|
||||
|
||||
/// Flag info enum, to be consistent with the one defined in aconfig_storage_file/src/lib.rs
|
||||
enum FlagInfoBit {
|
||||
IsSticky = 1<<0,
|
||||
IsReadWrite = 1<<1,
|
||||
HasOverride = 1<<2,
|
||||
};
|
||||
|
||||
/// Get boolean flag attribute
|
||||
/// \input file: mapped storage file
|
||||
/// \input index: the boolean flag index in the file
|
||||
|
|
|
@ -29,7 +29,7 @@ use aconfig_storage_file::protos::{
|
|||
};
|
||||
|
||||
/// Find where storage files are stored for a particular container
|
||||
fn find_container_storage_location(
|
||||
pub fn find_container_storage_location(
|
||||
location_pb_file: &str,
|
||||
container: &str,
|
||||
) -> Result<ProtoStorageFileInfo, AconfigStorageError> {
|
||||
|
|
|
@ -14,6 +14,7 @@ rust_defaults {
|
|||
"libcxx",
|
||||
"libthiserror",
|
||||
"libaconfig_storage_file",
|
||||
"libaconfig_storage_read_api",
|
||||
],
|
||||
}
|
||||
|
||||
|
@ -30,6 +31,7 @@ rust_test_host {
|
|||
defaults: ["aconfig_storage_write_api.defaults"],
|
||||
data: [
|
||||
"tests/flag.val",
|
||||
"tests/flag.info",
|
||||
],
|
||||
rustlibs: [
|
||||
"libaconfig_storage_read_api",
|
||||
|
@ -68,12 +70,13 @@ cc_library_static {
|
|||
srcs: ["aconfig_storage_write_api.cpp"],
|
||||
generated_headers: [
|
||||
"cxx-bridge-header",
|
||||
"libcxx_aconfig_storage_write_api_bridge_header"
|
||||
"libcxx_aconfig_storage_write_api_bridge_header",
|
||||
],
|
||||
generated_sources: ["libcxx_aconfig_storage_write_api_bridge_code"],
|
||||
whole_static_libs: ["libaconfig_storage_write_api_cxx_bridge"],
|
||||
export_include_dirs: ["include"],
|
||||
static_libs: [
|
||||
"libaconfig_storage_read_api_cc",
|
||||
"libaconfig_storage_protos_cc",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libbase",
|
||||
|
|
|
@ -38,7 +38,8 @@ static Result<storage_records_pb> read_storage_records_pb(std::string const& pb_
|
|||
/// Get storage file path
|
||||
static Result<std::string> find_storage_file(
|
||||
std::string const& pb_file,
|
||||
std::string const& container) {
|
||||
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
|
||||
|
@ -47,15 +48,26 @@ static Result<std::string> find_storage_file(
|
|||
|
||||
for (auto& entry : records_pb->files()) {
|
||||
if (entry.container() == container) {
|
||||
return entry.flag_val();
|
||||
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();
|
||||
case StorageFileType::flag_info:
|
||||
return entry.flag_info();
|
||||
default:
|
||||
return Error() << "Invalid file type " << file_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Error() << "Unable to find storage files for container " << container;;
|
||||
return Error() << "Unable to find storage files for container " << container;
|
||||
}
|
||||
|
||||
/// Map a storage file
|
||||
static Result<MappedFlagValueFile> map_storage_file(std::string const& file) {
|
||||
static Result<MutableMappedStorageFile> map_storage_file(std::string const& file) {
|
||||
struct stat file_stat;
|
||||
if (stat(file.c_str(), &file_stat) < 0) {
|
||||
return ErrnoError() << "stat failed";
|
||||
|
@ -78,7 +90,7 @@ static Result<MappedFlagValueFile> map_storage_file(std::string const& file) {
|
|||
return ErrnoError() << "mmap failed";
|
||||
}
|
||||
|
||||
auto mapped_file = MappedFlagValueFile();
|
||||
auto mapped_file = MutableMappedStorageFile();
|
||||
mapped_file.file_ptr = map_result;
|
||||
mapped_file.file_size = file_size;
|
||||
|
||||
|
@ -87,34 +99,37 @@ static Result<MappedFlagValueFile> map_storage_file(std::string const& file) {
|
|||
|
||||
namespace private_internal_api {
|
||||
|
||||
/// Get mapped file implementation.
|
||||
Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
|
||||
/// Get mutable mapped file implementation.
|
||||
Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
|
||||
std::string const& pb_file,
|
||||
std::string const& container) {
|
||||
auto file_result = find_storage_file(pb_file, container);
|
||||
std::string const& container,
|
||||
StorageFileType file_type) {
|
||||
if (file_type != StorageFileType::flag_val &&
|
||||
file_type != StorageFileType::flag_info) {
|
||||
return Error() << "Cannot create mutable mapped file for this file type";
|
||||
}
|
||||
|
||||
auto file_result = find_storage_file(pb_file, container, file_type);
|
||||
if (!file_result.ok()) {
|
||||
return Error() << file_result.error();
|
||||
}
|
||||
auto mapped_result = map_storage_file(*file_result);
|
||||
if (!mapped_result.ok()) {
|
||||
return Error() << "failed to map " << *file_result << ": "
|
||||
<< mapped_result.error();
|
||||
}
|
||||
return *mapped_result;
|
||||
|
||||
return map_storage_file(*file_result);
|
||||
}
|
||||
|
||||
} // namespace private internal api
|
||||
|
||||
/// Get mapped writeable flag value file
|
||||
Result<MappedFlagValueFile> get_mapped_flag_value_file(
|
||||
std::string const& container) {
|
||||
return private_internal_api::get_mapped_flag_value_file_impl(
|
||||
kPersistStorageRecordsPb, container);
|
||||
/// Get mutable mapped file
|
||||
Result<MutableMappedStorageFile> get_mutable_mapped_file(
|
||||
std::string const& container,
|
||||
StorageFileType file_type) {
|
||||
return private_internal_api::get_mutable_mapped_file_impl(
|
||||
kPersistStorageRecordsPb, container, file_type);
|
||||
}
|
||||
|
||||
/// Set boolean flag value
|
||||
Result<void> set_boolean_flag_value(
|
||||
const MappedFlagValueFile& file,
|
||||
const MutableMappedStorageFile& file,
|
||||
uint32_t offset,
|
||||
bool value) {
|
||||
auto content = rust::Slice<uint8_t>(
|
||||
|
@ -126,6 +141,38 @@ Result<void> set_boolean_flag_value(
|
|||
return {};
|
||||
}
|
||||
|
||||
/// Set if flag is sticky
|
||||
Result<void> set_flag_is_sticky(
|
||||
const MutableMappedStorageFile& file,
|
||||
FlagValueType value_type,
|
||||
uint32_t offset,
|
||||
bool value) {
|
||||
auto content = rust::Slice<uint8_t>(
|
||||
static_cast<uint8_t*>(file.file_ptr), file.file_size);
|
||||
auto update_cxx = update_flag_is_sticky_cxx(
|
||||
content, static_cast<uint16_t>(value_type), offset, value);
|
||||
if (!update_cxx.update_success) {
|
||||
return Error() << std::string(update_cxx.error_message.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/// Set if flag has override
|
||||
Result<void> set_flag_has_override(
|
||||
const MutableMappedStorageFile& file,
|
||||
FlagValueType value_type,
|
||||
uint32_t offset,
|
||||
bool value) {
|
||||
auto content = rust::Slice<uint8_t>(
|
||||
static_cast<uint8_t*>(file.file_ptr), file.file_size);
|
||||
auto update_cxx = update_flag_has_override_cxx(
|
||||
content, static_cast<uint16_t>(value_type), offset, value);
|
||||
if (!update_cxx.update_success) {
|
||||
return Error() << std::string(update_cxx.error_message.c_str());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Result<void> create_flag_info(
|
||||
std::string const& package_map,
|
||||
std::string const& flag_map,
|
||||
|
|
|
@ -4,13 +4,14 @@
|
|||
#include <string>
|
||||
|
||||
#include <android-base/result.h>
|
||||
#include <aconfig_storage/aconfig_storage_read_api.hpp>
|
||||
|
||||
using namespace android::base;
|
||||
|
||||
namespace aconfig_storage {
|
||||
|
||||
/// Mapped flag value file
|
||||
struct MappedFlagValueFile{
|
||||
struct MutableMappedStorageFile{
|
||||
void* file_ptr;
|
||||
size_t file_size;
|
||||
};
|
||||
|
@ -18,19 +19,35 @@ struct MappedFlagValueFile{
|
|||
/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
|
||||
namespace private_internal_api {
|
||||
|
||||
Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
|
||||
Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
|
||||
std::string const& pb_file,
|
||||
std::string const& container);
|
||||
std::string const& container,
|
||||
StorageFileType file_type);
|
||||
|
||||
} // namespace private_internal_api
|
||||
|
||||
/// Get mapped writeable flag value file
|
||||
Result<MappedFlagValueFile> get_mapped_flag_value_file(
|
||||
std::string const& container);
|
||||
/// Get mapped writeable storage file
|
||||
Result<MutableMappedStorageFile> get_mutable_mapped_file(
|
||||
std::string const& container,
|
||||
StorageFileType file_type);
|
||||
|
||||
/// Set boolean flag value
|
||||
Result<void> set_boolean_flag_value(
|
||||
const MappedFlagValueFile& file,
|
||||
const MutableMappedStorageFile& file,
|
||||
uint32_t offset,
|
||||
bool value);
|
||||
|
||||
/// Set if flag is sticky
|
||||
Result<void> set_flag_is_sticky(
|
||||
const MutableMappedStorageFile& file,
|
||||
FlagValueType value_type,
|
||||
uint32_t offset,
|
||||
bool value);
|
||||
|
||||
/// Set if flag has override
|
||||
Result<void> set_flag_has_override(
|
||||
const MutableMappedStorageFile& file,
|
||||
FlagValueType value_type,
|
||||
uint32_t offset,
|
||||
bool value);
|
||||
|
||||
|
|
129
tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
Normal file
129
tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//! flag info update module defines the flag info file write to mapped bytes
|
||||
|
||||
use aconfig_storage_file::{
|
||||
read_u8_from_bytes, AconfigStorageError, FlagInfoBit, FlagInfoHeader, FlagValueType,
|
||||
FILE_VERSION,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
|
||||
fn get_flag_info_offset(
|
||||
buf: &mut [u8],
|
||||
flag_type: FlagValueType,
|
||||
flag_index: u32,
|
||||
) -> Result<usize, AconfigStorageError> {
|
||||
let interpreted_header = FlagInfoHeader::from_bytes(buf)?;
|
||||
if interpreted_header.version > FILE_VERSION {
|
||||
return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
|
||||
"Cannot write to storage file with a higher version of {} with lib version {}",
|
||||
interpreted_header.version,
|
||||
FILE_VERSION
|
||||
)));
|
||||
}
|
||||
|
||||
// get byte offset to the flag info
|
||||
let head = match flag_type {
|
||||
FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize,
|
||||
};
|
||||
|
||||
if head >= interpreted_header.file_size as usize {
|
||||
return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
|
||||
"Flag value offset goes beyond the end of the file."
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
fn get_flag_attribute_and_offset(
|
||||
buf: &mut [u8],
|
||||
flag_type: FlagValueType,
|
||||
flag_index: u32,
|
||||
) -> Result<(u8, usize), AconfigStorageError> {
|
||||
let head = get_flag_info_offset(buf, flag_type, flag_index)?;
|
||||
let mut pos = head;
|
||||
let attribute = read_u8_from_bytes(buf, &mut pos)?;
|
||||
Ok((attribute, head))
|
||||
}
|
||||
|
||||
/// Set if flag is sticky
|
||||
pub fn update_flag_is_sticky(
|
||||
buf: &mut [u8],
|
||||
flag_type: FlagValueType,
|
||||
flag_index: u32,
|
||||
value: bool,
|
||||
) -> Result<(), AconfigStorageError> {
|
||||
let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
|
||||
let is_sticky = (attribute & (FlagInfoBit::IsSticky as u8)) != 0;
|
||||
if is_sticky != value {
|
||||
buf[head] = (attribute ^ FlagInfoBit::IsSticky as u8).to_le_bytes()[0];
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set if flag has override
|
||||
pub fn update_flag_has_override(
|
||||
buf: &mut [u8],
|
||||
flag_type: FlagValueType,
|
||||
flag_index: u32,
|
||||
value: bool,
|
||||
) -> Result<(), AconfigStorageError> {
|
||||
let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
|
||||
let has_override = (attribute & (FlagInfoBit::HasOverride as u8)) != 0;
|
||||
if has_override != value {
|
||||
buf[head] = (attribute ^ FlagInfoBit::HasOverride as u8).to_le_bytes()[0];
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aconfig_storage_file::test_utils::create_test_flag_info_list;
|
||||
use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
|
||||
|
||||
#[test]
|
||||
// this test point locks down is sticky update
|
||||
fn test_update_flag_is_sticky() {
|
||||
let flag_info_list = create_test_flag_info_list();
|
||||
let mut buf = flag_info_list.into_bytes();
|
||||
for i in 0..flag_info_list.header.num_flags {
|
||||
update_flag_is_sticky(&mut buf, FlagValueType::Boolean, i, true).unwrap();
|
||||
let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
|
||||
assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
|
||||
update_flag_is_sticky(&mut buf, FlagValueType::Boolean, i, false).unwrap();
|
||||
let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
|
||||
assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
// this test point locks down has override update
|
||||
fn test_update_flag_has_override() {
|
||||
let flag_info_list = create_test_flag_info_list();
|
||||
let mut buf = flag_info_list.into_bytes();
|
||||
for i in 0..flag_info_list.header.num_flags {
|
||||
update_flag_has_override(&mut buf, FlagValueType::Boolean, i, true).unwrap();
|
||||
let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
|
||||
assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
|
||||
update_flag_has_override(&mut buf, FlagValueType::Boolean, i, false).unwrap();
|
||||
let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
|
||||
assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -49,20 +49,7 @@ pub fn update_boolean_flag_value(
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use aconfig_storage_file::{FlagValueList, StorageFileType};
|
||||
|
||||
pub fn create_test_flag_value_list() -> FlagValueList {
|
||||
let header = FlagValueHeader {
|
||||
version: FILE_VERSION,
|
||||
container: String::from("system"),
|
||||
file_type: StorageFileType::FlagVal as u8,
|
||||
file_size: 35,
|
||||
num_flags: 8,
|
||||
boolean_value_offset: 27,
|
||||
};
|
||||
let booleans: Vec<bool> = vec![false; 8];
|
||||
FlagValueList { header, booleans }
|
||||
}
|
||||
use aconfig_storage_file::test_utils::create_test_flag_value_list;
|
||||
|
||||
#[test]
|
||||
// this test point locks down flag value update
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
//! `aconfig_storage_write_api` is a crate that defines write apis to update flag value
|
||||
//! in storage file. It provides one api to interface with storage files.
|
||||
|
||||
pub mod flag_info_update;
|
||||
pub mod flag_value_update;
|
||||
pub mod mapped_file;
|
||||
|
||||
|
@ -24,8 +25,8 @@ pub mod mapped_file;
|
|||
mod test_utils;
|
||||
|
||||
use aconfig_storage_file::{
|
||||
AconfigStorageError, FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagTable, PackageTable,
|
||||
StorageFileType, StoredFlagType, FILE_VERSION,
|
||||
AconfigStorageError, FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagTable, FlagValueType,
|
||||
PackageTable, StorageFileType, StoredFlagType, FILE_VERSION,
|
||||
};
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
@ -36,10 +37,11 @@ use std::io::{Read, Write};
|
|||
/// Storage file location pb file
|
||||
pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/persistent_storage_file_records.pb";
|
||||
|
||||
/// Get mmaped flag value file given the container name
|
||||
/// Get read write mapped storage files.
|
||||
///
|
||||
/// \input container: the flag package container
|
||||
/// \return a result of mapped file
|
||||
/// \input file_type: storage file type enum
|
||||
/// \return a result of read write mapped file
|
||||
///
|
||||
///
|
||||
/// # Safety
|
||||
|
@ -48,8 +50,11 @@ pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/persistent_storage_fi
|
|||
/// file not thru this memory mapped file or there are concurrent writes to this
|
||||
/// memory mapped file. Ensure all writes to the underlying file are thru this memory
|
||||
/// mapped file and there are no concurrent writes.
|
||||
pub unsafe fn get_mapped_flag_value_file(container: &str) -> Result<MmapMut, AconfigStorageError> {
|
||||
unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container) }
|
||||
pub unsafe fn get_mapped_storage_file(
|
||||
container: &str,
|
||||
file_type: StorageFileType,
|
||||
) -> Result<MmapMut, AconfigStorageError> {
|
||||
unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
|
||||
}
|
||||
|
||||
/// Set boolean flag value thru mapped file and flush the change to file
|
||||
|
@ -70,6 +75,44 @@ pub fn set_boolean_flag_value(
|
|||
})
|
||||
}
|
||||
|
||||
/// Set if flag is sticky thru mapped file and flush the change to file
|
||||
///
|
||||
/// \input mapped_file: the mapped flag info file
|
||||
/// \input index: flag index
|
||||
/// \input value: updated flag sticky value
|
||||
/// \return a result of ()
|
||||
///
|
||||
pub fn set_flag_is_sticky(
|
||||
file: &mut MmapMut,
|
||||
flag_type: FlagValueType,
|
||||
index: u32,
|
||||
value: bool,
|
||||
) -> Result<(), AconfigStorageError> {
|
||||
crate::flag_info_update::update_flag_is_sticky(file, flag_type, index, value)?;
|
||||
file.flush().map_err(|errmsg| {
|
||||
AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
|
||||
})
|
||||
}
|
||||
|
||||
/// Set if flag has override thru mapped file and flush the change to file
|
||||
///
|
||||
/// \input mapped_file: the mapped flag info file
|
||||
/// \input index: flag index
|
||||
/// \input value: updated flag has override value
|
||||
/// \return a result of ()
|
||||
///
|
||||
pub fn set_flag_has_override(
|
||||
file: &mut MmapMut,
|
||||
flag_type: FlagValueType,
|
||||
index: u32,
|
||||
value: bool,
|
||||
) -> Result<(), AconfigStorageError> {
|
||||
crate::flag_info_update::update_flag_has_override(file, flag_type, index, value)?;
|
||||
file.flush().map_err(|errmsg| {
|
||||
AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
|
||||
})
|
||||
}
|
||||
|
||||
/// Read in storage file as bytes
|
||||
fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
|
||||
let mut file = File::open(file_path).map_err(|errmsg| {
|
||||
|
@ -163,6 +206,18 @@ mod ffi {
|
|||
pub error_message: String,
|
||||
}
|
||||
|
||||
// Flag is sticky update return for cc interlop
|
||||
pub struct FlagIsStickyUpdateCXX {
|
||||
pub update_success: bool,
|
||||
pub error_message: String,
|
||||
}
|
||||
|
||||
// Flag has override update return for cc interlop
|
||||
pub struct FlagHasOverrideUpdateCXX {
|
||||
pub update_success: bool,
|
||||
pub error_message: String,
|
||||
}
|
||||
|
||||
// Flag info file creation return for cc interlop
|
||||
pub struct FlagInfoCreationCXX {
|
||||
pub success: bool,
|
||||
|
@ -177,6 +232,20 @@ mod ffi {
|
|||
value: bool,
|
||||
) -> BooleanFlagValueUpdateCXX;
|
||||
|
||||
pub fn update_flag_is_sticky_cxx(
|
||||
file: &mut [u8],
|
||||
flag_type: u16,
|
||||
offset: u32,
|
||||
value: bool,
|
||||
) -> FlagIsStickyUpdateCXX;
|
||||
|
||||
pub fn update_flag_has_override_cxx(
|
||||
file: &mut [u8],
|
||||
flag_type: u16,
|
||||
offset: u32,
|
||||
value: bool,
|
||||
) -> FlagHasOverrideUpdateCXX;
|
||||
|
||||
pub fn create_flag_info_cxx(
|
||||
package_map: &str,
|
||||
flag_map: &str,
|
||||
|
@ -201,6 +270,58 @@ pub(crate) fn update_boolean_flag_value_cxx(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_flag_is_sticky_cxx(
|
||||
file: &mut [u8],
|
||||
flag_type: u16,
|
||||
offset: u32,
|
||||
value: bool,
|
||||
) -> ffi::FlagIsStickyUpdateCXX {
|
||||
match FlagValueType::try_from(flag_type) {
|
||||
Ok(value_type) => {
|
||||
match crate::flag_info_update::update_flag_is_sticky(file, value_type, offset, value) {
|
||||
Ok(()) => ffi::FlagIsStickyUpdateCXX {
|
||||
update_success: true,
|
||||
error_message: String::from("")
|
||||
},
|
||||
Err(errmsg) => ffi::FlagIsStickyUpdateCXX {
|
||||
update_success: false,
|
||||
error_message: format!("{:?}", errmsg),
|
||||
},
|
||||
}
|
||||
}
|
||||
Err(errmsg) => ffi::FlagIsStickyUpdateCXX {
|
||||
update_success: false,
|
||||
error_message: format!("{:?}", errmsg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_flag_has_override_cxx(
|
||||
file: &mut [u8],
|
||||
flag_type: u16,
|
||||
offset: u32,
|
||||
value: bool,
|
||||
) -> ffi::FlagHasOverrideUpdateCXX {
|
||||
match FlagValueType::try_from(flag_type) {
|
||||
Ok(value_type) => {
|
||||
match crate::flag_info_update::update_flag_has_override(file, value_type, offset, value) {
|
||||
Ok(()) => ffi::FlagHasOverrideUpdateCXX {
|
||||
update_success: true,
|
||||
error_message: String::from("")
|
||||
},
|
||||
Err(errmsg) => ffi::FlagHasOverrideUpdateCXX {
|
||||
update_success: false,
|
||||
error_message: format!("{:?}", errmsg),
|
||||
},
|
||||
}
|
||||
}
|
||||
Err(errmsg) => ffi::FlagHasOverrideUpdateCXX {
|
||||
update_success: false,
|
||||
error_message: format!("{:?}", errmsg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create flag info file cc interlop
|
||||
pub(crate) fn create_flag_info_cxx(
|
||||
package_map: &str,
|
||||
|
@ -224,6 +345,8 @@ mod tests {
|
|||
create_test_flag_info_list, create_test_flag_table, create_test_package_table,
|
||||
write_bytes_to_temp_file,
|
||||
};
|
||||
use aconfig_storage_file::FlagInfoBit;
|
||||
use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
|
||||
use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
@ -248,6 +371,7 @@ files {{
|
|||
package_map: "some_package.map"
|
||||
flag_map: "some_flag.map"
|
||||
flag_val: "{}"
|
||||
flag_info: "some_flag.info"
|
||||
timestamp: 12345
|
||||
}}
|
||||
"#,
|
||||
|
@ -260,7 +384,12 @@ files {{
|
|||
// The safety here is guaranteed as only this single threaded test process will
|
||||
// write to this file
|
||||
unsafe {
|
||||
let mut file = crate::mapped_file::get_mapped_file(&record_pb_path, "system").unwrap();
|
||||
let mut file = crate::mapped_file::get_mapped_file(
|
||||
&record_pb_path,
|
||||
"system",
|
||||
StorageFileType::FlagVal,
|
||||
)
|
||||
.unwrap();
|
||||
for i in 0..8 {
|
||||
set_boolean_flag_value(&mut file, i, true).unwrap();
|
||||
let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
|
||||
|
@ -273,6 +402,97 @@ files {{
|
|||
}
|
||||
}
|
||||
|
||||
fn get_flag_attribute_at_offset(file: &str, offset: u32) -> u8 {
|
||||
let mut f = File::open(&file).unwrap();
|
||||
let mut bytes = Vec::new();
|
||||
f.read_to_end(&mut bytes).unwrap();
|
||||
find_boolean_flag_attribute(&bytes, offset).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_flag_is_sticky() {
|
||||
let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
|
||||
let flag_info_path = flag_info_file.path().display().to_string();
|
||||
let text_proto = format!(
|
||||
r#"
|
||||
files {{
|
||||
version: 0
|
||||
container: "system"
|
||||
package_map: "some_package.map"
|
||||
flag_map: "some_flag.map"
|
||||
flag_val: "some_flag.val"
|
||||
flag_info: "{}"
|
||||
timestamp: 12345
|
||||
}}
|
||||
"#,
|
||||
flag_info_path
|
||||
);
|
||||
let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
|
||||
let record_pb_path = record_pb_file.path().display().to_string();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is guaranteed as only this single threaded test process will
|
||||
// write to this file
|
||||
unsafe {
|
||||
let mut file = crate::mapped_file::get_mapped_file(
|
||||
&record_pb_path,
|
||||
"system",
|
||||
StorageFileType::FlagInfo,
|
||||
)
|
||||
.unwrap();
|
||||
for i in 0..8 {
|
||||
set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, true).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
|
||||
set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, false).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_flag_has_override() {
|
||||
let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
|
||||
let flag_info_path = flag_info_file.path().display().to_string();
|
||||
let text_proto = format!(
|
||||
r#"
|
||||
files {{
|
||||
version: 0
|
||||
container: "system"
|
||||
package_map: "some_package.map"
|
||||
flag_map: "some_flag.map"
|
||||
flag_val: "some_flag.val"
|
||||
flag_info: "{}"
|
||||
timestamp: 12345
|
||||
}}
|
||||
"#,
|
||||
flag_info_path
|
||||
);
|
||||
let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
|
||||
let record_pb_path = record_pb_file.path().display().to_string();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is guaranteed as only this single threaded test process will
|
||||
// write to this file
|
||||
unsafe {
|
||||
let mut file = crate::mapped_file::get_mapped_file(
|
||||
&record_pb_path,
|
||||
"system",
|
||||
StorageFileType::FlagInfo,
|
||||
)
|
||||
.unwrap();
|
||||
for i in 0..8 {
|
||||
set_flag_has_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
|
||||
set_flag_has_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> {
|
||||
let file = NamedTempFile::new().map_err(|_| {
|
||||
AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
|
||||
|
|
|
@ -14,43 +14,41 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::{BufReader, Read};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use memmap2::MmapMut;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::Read;
|
||||
|
||||
use aconfig_storage_file::protos::{storage_record_pb::try_from_binary_proto, ProtoStorageFiles};
|
||||
use aconfig_storage_file::AconfigStorageError::{
|
||||
self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
|
||||
};
|
||||
use aconfig_storage_file::AconfigStorageError::{self, FileReadFail, MapFileFail};
|
||||
use aconfig_storage_file::StorageFileType;
|
||||
use aconfig_storage_read_api::mapped_file::find_container_storage_location;
|
||||
|
||||
/// Find where persistent storage value file is for a particular container
|
||||
fn find_persist_flag_value_file(
|
||||
location_pb_file: &str,
|
||||
container: &str,
|
||||
) -> Result<String, AconfigStorageError> {
|
||||
let file = File::open(location_pb_file).map_err(|errmsg| {
|
||||
FileReadFail(anyhow!("Failed to open file {}: {}", location_pb_file, errmsg))
|
||||
})?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let mut bytes = Vec::new();
|
||||
reader.read_to_end(&mut bytes).map_err(|errmsg| {
|
||||
FileReadFail(anyhow!("Failed to read file {}: {}", location_pb_file, errmsg))
|
||||
})?;
|
||||
let storage_locations: ProtoStorageFiles = try_from_binary_proto(&bytes).map_err(|errmsg| {
|
||||
ProtobufParseFail(anyhow!(
|
||||
"Failed to parse storage location pb file {}: {}",
|
||||
location_pb_file,
|
||||
errmsg
|
||||
))
|
||||
})?;
|
||||
for location_info in storage_locations.files.iter() {
|
||||
if location_info.container() == container {
|
||||
return Ok(location_info.flag_val().to_string());
|
||||
}
|
||||
/// Get the mutable memory mapping of a storage file
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The memory mapped file may have undefined behavior if there are writes to this
|
||||
/// file not thru this memory mapped file or there are concurrent writes to this
|
||||
/// memory mapped file. Ensure all writes to the underlying file are thru this memory
|
||||
/// mapped file and there are no concurrent writes.
|
||||
unsafe fn map_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
|
||||
// make sure file has read write permission
|
||||
let perms = fs::metadata(file_path).unwrap().permissions();
|
||||
if perms.readonly() {
|
||||
return Err(MapFileFail(anyhow!("fail to map non read write storage file {}", file_path)));
|
||||
}
|
||||
|
||||
let file =
|
||||
OpenOptions::new().read(true).write(true).open(file_path).map_err(|errmsg| {
|
||||
FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
|
||||
})?;
|
||||
|
||||
unsafe {
|
||||
let mapped_file = MmapMut::map_mut(&file).map_err(|errmsg| {
|
||||
MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
|
||||
})?;
|
||||
Ok(mapped_file)
|
||||
}
|
||||
Err(StorageFileNotFound(anyhow!("Persistent flag value file does not exist for {}", container)))
|
||||
}
|
||||
|
||||
/// Get a mapped storage file given the container and file type
|
||||
|
@ -64,24 +62,16 @@ fn find_persist_flag_value_file(
|
|||
pub unsafe fn get_mapped_file(
|
||||
location_pb_file: &str,
|
||||
container: &str,
|
||||
file_type: StorageFileType,
|
||||
) -> Result<MmapMut, AconfigStorageError> {
|
||||
let file_path = find_persist_flag_value_file(location_pb_file, container)?;
|
||||
|
||||
// make sure file has read write permission
|
||||
let perms = fs::metadata(&file_path).unwrap().permissions();
|
||||
if perms.readonly() {
|
||||
return Err(MapFileFail(anyhow!("fail to map non read write storage file {}", file_path)));
|
||||
}
|
||||
|
||||
let file =
|
||||
OpenOptions::new().read(true).write(true).open(&file_path).map_err(|errmsg| {
|
||||
FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
|
||||
})?;
|
||||
|
||||
unsafe {
|
||||
MmapMut::map_mut(&file).map_err(|errmsg| {
|
||||
MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
|
||||
})
|
||||
let files_location = find_container_storage_location(location_pb_file, container)?;
|
||||
match file_type {
|
||||
StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
|
||||
StorageFileType::FlagInfo => unsafe { map_file(files_location.flag_info()) },
|
||||
_ => Err(MapFileFail(anyhow!(
|
||||
"Cannot map file type {:?} as writeable memory mapped files.",
|
||||
file_type
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,42 +81,10 @@ mod tests {
|
|||
use crate::test_utils::copy_to_temp_file;
|
||||
use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
|
||||
|
||||
#[test]
|
||||
fn test_find_persist_flag_value_file_location() {
|
||||
let text_proto = r#"
|
||||
files {
|
||||
version: 0
|
||||
container: "system"
|
||||
package_map: "/system/etc/package.map"
|
||||
flag_map: "/system/etc/flag.map"
|
||||
flag_val: "/metadata/aconfig/system.val"
|
||||
timestamp: 12345
|
||||
}
|
||||
files {
|
||||
version: 1
|
||||
container: "product"
|
||||
package_map: "/product/etc/package.map"
|
||||
flag_map: "/product/etc/flag.map"
|
||||
flag_val: "/metadata/aconfig/product.val"
|
||||
timestamp: 54321
|
||||
}
|
||||
"#;
|
||||
let file = write_proto_to_temp_file(&text_proto).unwrap();
|
||||
let file_full_path = file.path().display().to_string();
|
||||
let flag_value_file = find_persist_flag_value_file(&file_full_path, "system").unwrap();
|
||||
assert_eq!(flag_value_file, "/metadata/aconfig/system.val");
|
||||
let flag_value_file = find_persist_flag_value_file(&file_full_path, "product").unwrap();
|
||||
assert_eq!(flag_value_file, "/metadata/aconfig/product.val");
|
||||
let err = find_persist_flag_value_file(&file_full_path, "vendor").unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", err),
|
||||
"StorageFileNotFound(Persistent flag value file does not exist for vendor)"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mapped_file_contents() {
|
||||
let mut rw_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
|
||||
let mut rw_val_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
|
||||
let mut rw_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
|
||||
let text_proto = format!(
|
||||
r#"
|
||||
files {{
|
||||
|
@ -135,21 +93,37 @@ files {{
|
|||
package_map: "some_package.map"
|
||||
flag_map: "some_flag.map"
|
||||
flag_val: "{}"
|
||||
flag_info: "{}"
|
||||
timestamp: 12345
|
||||
}}
|
||||
"#,
|
||||
rw_file.path().display().to_string()
|
||||
rw_val_file.path().display().to_string(),
|
||||
rw_info_file.path().display().to_string()
|
||||
);
|
||||
let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
|
||||
let storage_record_file_path = storage_record_file.path().display().to_string();
|
||||
|
||||
let mut content = Vec::new();
|
||||
rw_file.read_to_end(&mut content).unwrap();
|
||||
rw_val_file.read_to_end(&mut content).unwrap();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is guaranteed here as no writes happens to this temp file
|
||||
unsafe {
|
||||
let mmaped_file = get_mapped_file(&storage_record_file_path, "system").unwrap();
|
||||
let mmaped_file =
|
||||
get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
|
||||
.unwrap();
|
||||
assert_eq!(mmaped_file[..], content[..]);
|
||||
}
|
||||
|
||||
let mut content = Vec::new();
|
||||
rw_info_file.read_to_end(&mut content).unwrap();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is guaranteed here as no writes happens to this temp file
|
||||
unsafe {
|
||||
let mmaped_file =
|
||||
get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagInfo)
|
||||
.unwrap();
|
||||
assert_eq!(mmaped_file[..], content[..]);
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +139,7 @@ files {{
|
|||
package_map: "some_package.map"
|
||||
flag_map: "some_flag.map"
|
||||
flag_val: "{}"
|
||||
flag_info: "some_flag.info"
|
||||
timestamp: 12345
|
||||
}}
|
||||
"#,
|
||||
|
@ -176,7 +151,9 @@ files {{
|
|||
// SAFETY:
|
||||
// The safety here is guaranteed here as no writes happens to this temp file
|
||||
unsafe {
|
||||
let error = get_mapped_file(&storage_record_file_path, "system").unwrap_err();
|
||||
let error =
|
||||
get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", error),
|
||||
format!(
|
||||
|
@ -186,4 +163,49 @@ files {{
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mapped_not_supported_file() {
|
||||
let text_proto = format!(
|
||||
r#"
|
||||
files {{
|
||||
version: 0
|
||||
container: "system"
|
||||
package_map: "some_package.map"
|
||||
flag_map: "some_flag.map"
|
||||
flag_val: "some_flag.val"
|
||||
flag_info: "some_flag.info"
|
||||
timestamp: 12345
|
||||
}}
|
||||
"#,
|
||||
);
|
||||
let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
|
||||
let storage_record_file_path = storage_record_file.path().display().to_string();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is guaranteed here as no writes happens to this temp file
|
||||
unsafe {
|
||||
let error =
|
||||
get_mapped_file(&storage_record_file_path, "system", StorageFileType::PackageMap)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", error),
|
||||
format!(
|
||||
"MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
|
||||
StorageFileType::PackageMap
|
||||
)
|
||||
);
|
||||
|
||||
let error =
|
||||
get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagMap)
|
||||
.unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", error),
|
||||
format!(
|
||||
"MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
|
||||
StorageFileType::FlagMap
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
|
||||
rust_test {
|
||||
name: "aconfig_storage_write_api.test.rust",
|
||||
srcs: [
|
||||
"storage_write_api_test.rs"
|
||||
"storage_write_api_test.rs",
|
||||
],
|
||||
rustlibs: [
|
||||
"libanyhow",
|
||||
|
@ -14,6 +13,7 @@ rust_test {
|
|||
],
|
||||
data: [
|
||||
"flag.val",
|
||||
"flag.info",
|
||||
],
|
||||
test_suites: ["general-tests"],
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ cc_test {
|
|||
],
|
||||
data: [
|
||||
"flag.val",
|
||||
"flag.info",
|
||||
],
|
||||
test_suites: [
|
||||
"device-tests",
|
||||
|
|
BIN
tools/aconfig/aconfig_storage_write_api/tests/flag.info
Normal file
BIN
tools/aconfig/aconfig_storage_write_api/tests/flag.info
Normal file
Binary file not shown.
|
@ -50,7 +50,8 @@ class AconfigStorageTest : public ::testing::Test {
|
|||
return temp_file;
|
||||
}
|
||||
|
||||
Result<std::string> write_storage_location_pb_file(std::string const& flag_val) {
|
||||
Result<std::string> write_storage_location_pb_file(std::string const& flag_val,
|
||||
std::string const& flag_info) {
|
||||
auto temp_file = std::tmpnam(nullptr);
|
||||
auto proto = storage_files();
|
||||
auto* info = proto.add_files();
|
||||
|
@ -59,6 +60,7 @@ class AconfigStorageTest : public ::testing::Test {
|
|||
info->set_package_map("some_package.map");
|
||||
info->set_flag_map("some_flag.map");
|
||||
info->set_flag_val(flag_val);
|
||||
info->set_flag_info(flag_info);
|
||||
info->set_timestamp(12345);
|
||||
|
||||
auto content = std::string();
|
||||
|
@ -72,22 +74,25 @@ class AconfigStorageTest : public ::testing::Test {
|
|||
void SetUp() override {
|
||||
auto const test_dir = android::base::GetExecutableDirectory();
|
||||
flag_val = *copy_to_rw_temp_file(test_dir + "/flag.val");
|
||||
storage_record_pb = *write_storage_location_pb_file(flag_val);
|
||||
flag_info = *copy_to_rw_temp_file(test_dir + "/flag.info");
|
||||
storage_record_pb = *write_storage_location_pb_file(flag_val, flag_info);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
std::remove(flag_val.c_str());
|
||||
std::remove(flag_info.c_str());
|
||||
std::remove(storage_record_pb.c_str());
|
||||
}
|
||||
|
||||
std::string flag_val;
|
||||
std::string flag_info;
|
||||
std::string storage_record_pb;
|
||||
};
|
||||
|
||||
/// 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_result = private_api::get_mapped_flag_value_file_impl(
|
||||
storage_record_pb, "vendor");
|
||||
auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "vendor", api::StorageFileType::flag_val);
|
||||
ASSERT_FALSE(mapped_file_result.ok());
|
||||
ASSERT_EQ(mapped_file_result.error().message(),
|
||||
"Unable to find storage files for container vendor");
|
||||
|
@ -96,17 +101,34 @@ TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {
|
|||
/// Negative test to lock down the error when mapping a non writeable storage file
|
||||
TEST_F(AconfigStorageTest, test_non_writable_storage_file_mapping) {
|
||||
ASSERT_TRUE(chmod(flag_val.c_str(), S_IRUSR | S_IRGRP | S_IROTH) != -1);
|
||||
auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
|
||||
storage_record_pb, "mockup");
|
||||
auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "mockup", api::StorageFileType::flag_val);
|
||||
ASSERT_FALSE(mapped_file_result.ok());
|
||||
auto it = mapped_file_result.error().message().find("cannot map nonwriteable file");
|
||||
ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
|
||||
}
|
||||
|
||||
/// Negative test to lock down the error when mapping a file type that cannot be modified
|
||||
TEST_F(AconfigStorageTest, test_invalid_storage_file_type_mapping) {
|
||||
auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "mockup", api::StorageFileType::package_map);
|
||||
ASSERT_FALSE(mapped_file_result.ok());
|
||||
auto it = mapped_file_result.error().message().find(
|
||||
"Cannot create mutable mapped file for this file type");
|
||||
ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
|
||||
|
||||
mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "mockup", api::StorageFileType::flag_map);
|
||||
ASSERT_FALSE(mapped_file_result.ok());
|
||||
it = mapped_file_result.error().message().find(
|
||||
"Cannot create mutable mapped file for this file type");
|
||||
ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
|
||||
}
|
||||
|
||||
/// Test to lock down storage flag value update api
|
||||
TEST_F(AconfigStorageTest, test_boolean_flag_value_update) {
|
||||
auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
|
||||
storage_record_pb, "mockup");
|
||||
auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "mockup", api::StorageFileType::flag_val);
|
||||
ASSERT_TRUE(mapped_file_result.ok());
|
||||
auto mapped_file = *mapped_file_result;
|
||||
|
||||
|
@ -124,8 +146,8 @@ TEST_F(AconfigStorageTest, test_boolean_flag_value_update) {
|
|||
|
||||
/// Negative test to lock down the error when querying flag value out of range
|
||||
TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) {
|
||||
auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
|
||||
storage_record_pb, "mockup");
|
||||
auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "mockup", api::StorageFileType::flag_val);
|
||||
ASSERT_TRUE(mapped_file_result.ok());
|
||||
auto mapped_file = *mapped_file_result;
|
||||
auto update_result = api::set_boolean_flag_value(mapped_file, 8, true);
|
||||
|
@ -133,3 +155,61 @@ TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) {
|
|||
ASSERT_EQ(update_result.error().message(),
|
||||
std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
|
||||
}
|
||||
|
||||
/// Test to lock down storage flag stickiness update api
|
||||
TEST_F(AconfigStorageTest, test_flag_is_sticky_update) {
|
||||
auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "mockup", api::StorageFileType::flag_info);
|
||||
ASSERT_TRUE(mapped_file_result.ok());
|
||||
auto mapped_file = *mapped_file_result;
|
||||
|
||||
for (int offset = 0; offset < 8; ++offset) {
|
||||
auto update_result = api::set_flag_is_sticky(
|
||||
mapped_file, api::FlagValueType::Boolean, offset, true);
|
||||
ASSERT_TRUE(update_result.ok());
|
||||
auto ro_mapped_file = api::MappedStorageFile();
|
||||
ro_mapped_file.file_ptr = mapped_file.file_ptr;
|
||||
ro_mapped_file.file_size = mapped_file.file_size;
|
||||
auto attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
|
||||
ASSERT_TRUE(attribute.ok());
|
||||
ASSERT_TRUE(*attribute & api::FlagInfoBit::IsSticky);
|
||||
|
||||
update_result = api::set_flag_is_sticky(
|
||||
mapped_file, api::FlagValueType::Boolean, offset, false);
|
||||
ASSERT_TRUE(update_result.ok());
|
||||
ro_mapped_file.file_ptr = mapped_file.file_ptr;
|
||||
ro_mapped_file.file_size = mapped_file.file_size;
|
||||
attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
|
||||
ASSERT_TRUE(attribute.ok());
|
||||
ASSERT_FALSE(*attribute & api::FlagInfoBit::IsSticky);
|
||||
}
|
||||
}
|
||||
|
||||
/// Test to lock down storage flag has override update api
|
||||
TEST_F(AconfigStorageTest, test_flag_has_override_update) {
|
||||
auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
|
||||
storage_record_pb, "mockup", api::StorageFileType::flag_info);
|
||||
ASSERT_TRUE(mapped_file_result.ok());
|
||||
auto mapped_file = *mapped_file_result;
|
||||
|
||||
for (int offset = 0; offset < 8; ++offset) {
|
||||
auto update_result = api::set_flag_has_override(
|
||||
mapped_file, api::FlagValueType::Boolean, offset, true);
|
||||
ASSERT_TRUE(update_result.ok());
|
||||
auto ro_mapped_file = api::MappedStorageFile();
|
||||
ro_mapped_file.file_ptr = mapped_file.file_ptr;
|
||||
ro_mapped_file.file_size = mapped_file.file_size;
|
||||
auto attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
|
||||
ASSERT_TRUE(attribute.ok());
|
||||
ASSERT_TRUE(*attribute & api::FlagInfoBit::HasOverride);
|
||||
|
||||
update_result = api::set_flag_has_override(
|
||||
mapped_file, api::FlagValueType::Boolean, offset, false);
|
||||
ASSERT_TRUE(update_result.ok());
|
||||
ro_mapped_file.file_ptr = mapped_file.file_ptr;
|
||||
ro_mapped_file.file_size = mapped_file.file_size;
|
||||
attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
|
||||
ASSERT_TRUE(attribute.ok());
|
||||
ASSERT_FALSE(*attribute & api::FlagInfoBit::HasOverride);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
#[cfg(not(feature = "cargo"))]
|
||||
mod aconfig_storage_write_api_test {
|
||||
use aconfig_storage_file::protos::ProtoStorageFiles;
|
||||
use aconfig_storage_file::{FlagInfoBit, FlagValueType, StorageFileType};
|
||||
use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
|
||||
use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
|
||||
use aconfig_storage_write_api::{mapped_file::get_mapped_file, set_boolean_flag_value};
|
||||
use aconfig_storage_write_api::{
|
||||
mapped_file::get_mapped_file, set_boolean_flag_value, set_flag_has_override,
|
||||
set_flag_is_sticky,
|
||||
};
|
||||
|
||||
use protobuf::Message;
|
||||
use std::fs::{self, File};
|
||||
|
@ -10,7 +15,7 @@ mod aconfig_storage_write_api_test {
|
|||
use tempfile::NamedTempFile;
|
||||
|
||||
/// Write storage location record pb to a temp file
|
||||
fn write_storage_record_file(flag_val: &str) -> NamedTempFile {
|
||||
fn write_storage_record_file(flag_val: &str, flag_info: &str) -> NamedTempFile {
|
||||
let text_proto = format!(
|
||||
r#"
|
||||
files {{
|
||||
|
@ -19,10 +24,11 @@ files {{
|
|||
package_map: "some_package_map"
|
||||
flag_map: "some_flag_map"
|
||||
flag_val: "{}"
|
||||
flag_info: "{}"
|
||||
timestamp: 12345
|
||||
}}
|
||||
"#,
|
||||
flag_val
|
||||
flag_val, flag_info
|
||||
);
|
||||
let storage_files: ProtoStorageFiles =
|
||||
protobuf::text_format::parse_from_str(&text_proto).unwrap();
|
||||
|
@ -48,18 +54,30 @@ files {{
|
|||
find_boolean_flag_value(&bytes, offset).unwrap()
|
||||
}
|
||||
|
||||
/// Get flag attribute at offset
|
||||
fn get_flag_attribute_at_offset(file: &str, offset: u32) -> u8 {
|
||||
let mut f = File::open(file).unwrap();
|
||||
let mut bytes = Vec::new();
|
||||
f.read_to_end(&mut bytes).unwrap();
|
||||
find_boolean_flag_attribute(&bytes, offset).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test to lock down flag value update api
|
||||
fn test_boolean_flag_value_update() {
|
||||
let flag_value_file = copy_to_temp_rw_file("./flag.val");
|
||||
let flag_info_file = copy_to_temp_rw_file("./flag.info");
|
||||
let flag_value_path = flag_value_file.path().display().to_string();
|
||||
let record_pb_file = write_storage_record_file(&flag_value_path);
|
||||
let flag_info_path = flag_info_file.path().display().to_string();
|
||||
let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
|
||||
let record_pb_path = record_pb_file.path().display().to_string();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is ensured as only this single threaded test process will
|
||||
// write to this file
|
||||
let mut file = unsafe { get_mapped_file(&record_pb_path, "mockup").unwrap() };
|
||||
let mut file = unsafe {
|
||||
get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagVal).unwrap()
|
||||
};
|
||||
for i in 0..8 {
|
||||
set_boolean_flag_value(&mut file, i, true).unwrap();
|
||||
let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
|
||||
|
@ -70,4 +88,56 @@ files {{
|
|||
assert!(!value);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test to lock down flag is sticky update api
|
||||
fn test_set_flag_is_sticky() {
|
||||
let flag_value_file = copy_to_temp_rw_file("./flag.val");
|
||||
let flag_info_file = copy_to_temp_rw_file("./flag.info");
|
||||
let flag_value_path = flag_value_file.path().display().to_string();
|
||||
let flag_info_path = flag_info_file.path().display().to_string();
|
||||
let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
|
||||
let record_pb_path = record_pb_file.path().display().to_string();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is ensured as only this single threaded test process will
|
||||
// write to this file
|
||||
let mut file = unsafe {
|
||||
get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
|
||||
};
|
||||
for i in 0..8 {
|
||||
set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, true).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
|
||||
set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, false).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test to lock down flag is sticky update api
|
||||
fn test_set_flag_has_override() {
|
||||
let flag_value_file = copy_to_temp_rw_file("./flag.val");
|
||||
let flag_info_file = copy_to_temp_rw_file("./flag.info");
|
||||
let flag_value_path = flag_value_file.path().display().to_string();
|
||||
let flag_info_path = flag_info_file.path().display().to_string();
|
||||
let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
|
||||
let record_pb_path = record_pb_file.path().display().to_string();
|
||||
|
||||
// SAFETY:
|
||||
// The safety here is ensured as only this single threaded test process will
|
||||
// write to this file
|
||||
let mut file = unsafe {
|
||||
get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
|
||||
};
|
||||
for i in 0..8 {
|
||||
set_flag_has_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
|
||||
set_flag_has_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
|
||||
let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
|
||||
assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue