Merge "aconfig_storage_file: add flag_info definition" into main
This commit is contained in:
commit
abf5ea2f46
11 changed files with 285 additions and 14 deletions
|
@ -17,7 +17,7 @@
|
|||
use crate::commands::assign_flag_ids;
|
||||
use crate::storage::FlagPackage;
|
||||
use aconfig_storage_file::{
|
||||
get_table_size, FlagTable, FlagTableHeader, FlagTableNode, FILE_VERSION, StorageFileType
|
||||
get_table_size, FlagTable, FlagTableHeader, FlagTableNode, StorageFileType, FILE_VERSION,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
use crate::commands::assign_flag_ids;
|
||||
use crate::storage::FlagPackage;
|
||||
use aconfig_protos::ProtoFlagState;
|
||||
use aconfig_storage_file::{FlagValueHeader, FlagValueList, FILE_VERSION, StorageFileType};
|
||||
use aconfig_storage_file::{FlagValueHeader, FlagValueList, StorageFileType, FILE_VERSION};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
fn new_header(container: &str, num_flags: u32) -> FlagValueHeader {
|
||||
|
|
|
@ -18,7 +18,7 @@ pub mod flag_table;
|
|||
pub mod flag_value;
|
||||
pub mod package_table;
|
||||
|
||||
use anyhow::Result;
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::storage::{
|
||||
|
@ -107,6 +107,7 @@ where
|
|||
let flag_value = create_flag_value(container, &packages)?;
|
||||
Ok(flag_value.as_bytes())
|
||||
}
|
||||
_ => Err(anyhow!("aconfig does not support the creation of this storage file type")),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
use anyhow::Result;
|
||||
|
||||
use aconfig_storage_file::{
|
||||
get_table_size, PackageTable, PackageTableHeader, PackageTableNode, FILE_VERSION, StorageFileType
|
||||
get_table_size, PackageTable, PackageTableHeader, PackageTableNode, StorageFileType,
|
||||
FILE_VERSION,
|
||||
};
|
||||
|
||||
use crate::storage::FlagPackage;
|
||||
|
|
238
tools/aconfig/aconfig_storage_file/src/flag_info.rs
Normal file
238
tools/aconfig/aconfig_storage_file/src/flag_info.rs
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* 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 module defines the flag info file format and methods for serialization
|
||||
//! and deserialization
|
||||
|
||||
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
|
||||
use crate::{AconfigStorageError, StorageFileType};
|
||||
use anyhow::anyhow;
|
||||
use std::fmt;
|
||||
|
||||
/// Flag info header struct
|
||||
#[derive(PartialEq)]
|
||||
pub struct FlagInfoHeader {
|
||||
pub version: u32,
|
||||
pub container: String,
|
||||
pub file_type: u8,
|
||||
pub file_size: u32,
|
||||
pub num_flags: u32,
|
||||
pub boolean_flag_offset: u32,
|
||||
}
|
||||
|
||||
/// Implement debug print trait for header
|
||||
impl fmt::Debug for FlagInfoHeader {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"Version: {}, Container: {}, File Type: {:?}, File Size: {}",
|
||||
self.version,
|
||||
self.container,
|
||||
StorageFileType::try_from(self.file_type),
|
||||
self.file_size
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"Num of Flags: {}, Boolean Flag Offset:{}",
|
||||
self.num_flags, self.boolean_flag_offset
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FlagInfoHeader {
|
||||
/// Serialize to bytes
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
let mut result = Vec::new();
|
||||
result.extend_from_slice(&self.version.to_le_bytes());
|
||||
let container_bytes = self.container.as_bytes();
|
||||
result.extend_from_slice(&(container_bytes.len() as u32).to_le_bytes());
|
||||
result.extend_from_slice(container_bytes);
|
||||
result.extend_from_slice(&self.file_type.to_le_bytes());
|
||||
result.extend_from_slice(&self.file_size.to_le_bytes());
|
||||
result.extend_from_slice(&self.num_flags.to_le_bytes());
|
||||
result.extend_from_slice(&self.boolean_flag_offset.to_le_bytes());
|
||||
result
|
||||
}
|
||||
|
||||
/// Deserialize from bytes
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
|
||||
let mut head = 0;
|
||||
let list = Self {
|
||||
version: read_u32_from_bytes(bytes, &mut head)?,
|
||||
container: read_str_from_bytes(bytes, &mut head)?,
|
||||
file_type: read_u8_from_bytes(bytes, &mut head)?,
|
||||
file_size: read_u32_from_bytes(bytes, &mut head)?,
|
||||
num_flags: read_u32_from_bytes(bytes, &mut head)?,
|
||||
boolean_flag_offset: read_u32_from_bytes(bytes, &mut head)?,
|
||||
};
|
||||
if list.file_type != StorageFileType::FlagInfo as u8 {
|
||||
return Err(AconfigStorageError::BytesParseFail(anyhow!(
|
||||
"binary file is not a flag info file"
|
||||
)));
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
}
|
||||
|
||||
/// bit field for flag info
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum FlagInfoBit {
|
||||
IsSticky = 0,
|
||||
IsReadWrite = 1,
|
||||
HasOverride = 2,
|
||||
}
|
||||
|
||||
/// Flag info node struct
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub struct FlagInfoNode {
|
||||
pub attributes: u8,
|
||||
}
|
||||
|
||||
/// Implement debug print trait for node
|
||||
impl fmt::Debug for FlagInfoNode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"sticky: {}, readwrite: {}, override: {}",
|
||||
self.attributes & (FlagInfoBit::IsSticky as u8),
|
||||
self.attributes & (FlagInfoBit::IsReadWrite as u8),
|
||||
self.attributes & (FlagInfoBit::HasOverride as u8),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FlagInfoNode {
|
||||
/// Serialize to bytes
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
let mut result = Vec::new();
|
||||
result.extend_from_slice(&self.attributes.to_le_bytes());
|
||||
result
|
||||
}
|
||||
|
||||
/// Deserialize from bytes
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
|
||||
let mut head = 0;
|
||||
let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? };
|
||||
Ok(node)
|
||||
}
|
||||
}
|
||||
|
||||
/// Flag info list struct
|
||||
#[derive(PartialEq)]
|
||||
pub struct FlagInfoList {
|
||||
pub header: FlagInfoHeader,
|
||||
pub nodes: Vec<FlagInfoNode>,
|
||||
}
|
||||
|
||||
/// Implement debug print trait for flag info list
|
||||
impl fmt::Debug for FlagInfoList {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
writeln!(f, "Header:")?;
|
||||
write!(f, "{:?}", self.header)?;
|
||||
writeln!(f, "Nodes:")?;
|
||||
for node in self.nodes.iter() {
|
||||
write!(f, "{:?}", node)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FlagInfoList {
|
||||
/// Serialize to bytes
|
||||
pub fn as_bytes(&self) -> Vec<u8> {
|
||||
[
|
||||
self.header.as_bytes(),
|
||||
self.nodes.iter().map(|v| v.as_bytes()).collect::<Vec<_>>().concat(),
|
||||
]
|
||||
.concat()
|
||||
}
|
||||
|
||||
/// Deserialize from bytes
|
||||
pub fn from_bytes(bytes: &[u8]) -> Result<Self, AconfigStorageError> {
|
||||
let header = FlagInfoHeader::from_bytes(bytes)?;
|
||||
let num_flags = header.num_flags;
|
||||
let mut head = header.as_bytes().len();
|
||||
let nodes = (0..num_flags)
|
||||
.map(|_| {
|
||||
let node = FlagInfoNode::from_bytes(&bytes[head..])?;
|
||||
head += node.as_bytes().len();
|
||||
Ok(node)
|
||||
})
|
||||
.collect::<Result<Vec<_>, AconfigStorageError>>()
|
||||
.map_err(|errmsg| {
|
||||
AconfigStorageError::BytesParseFail(anyhow!(
|
||||
"fail to parse flag info list: {}",
|
||||
errmsg
|
||||
))
|
||||
})?;
|
||||
let list = Self { header, nodes };
|
||||
Ok(list)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::create_test_flag_info_list;
|
||||
|
||||
#[test]
|
||||
// this test point locks down the value list serialization
|
||||
fn test_serialization() {
|
||||
let flag_info_list = create_test_flag_info_list();
|
||||
|
||||
let header: &FlagInfoHeader = &flag_info_list.header;
|
||||
let reinterpreted_header = FlagInfoHeader::from_bytes(&header.as_bytes());
|
||||
assert!(reinterpreted_header.is_ok());
|
||||
assert_eq!(header, &reinterpreted_header.unwrap());
|
||||
|
||||
let nodes: &Vec<FlagInfoNode> = &flag_info_list.nodes;
|
||||
for node in nodes.iter() {
|
||||
let reinterpreted_node = FlagInfoNode::from_bytes(&node.as_bytes()).unwrap();
|
||||
assert_eq!(node, &reinterpreted_node);
|
||||
}
|
||||
|
||||
let flag_info_bytes = flag_info_list.as_bytes();
|
||||
let reinterpreted_info_list = FlagInfoList::from_bytes(&flag_info_bytes);
|
||||
assert!(reinterpreted_info_list.is_ok());
|
||||
assert_eq!(&flag_info_list, &reinterpreted_info_list.unwrap());
|
||||
assert_eq!(flag_info_bytes.len() as u32, header.file_size);
|
||||
}
|
||||
|
||||
#[test]
|
||||
// this test point locks down that version number should be at the top of serialized
|
||||
// bytes
|
||||
fn test_version_number() {
|
||||
let flag_info_list = create_test_flag_info_list();
|
||||
let bytes = &flag_info_list.as_bytes();
|
||||
let mut head = 0;
|
||||
let version = read_u32_from_bytes(bytes, &mut head).unwrap();
|
||||
assert_eq!(version, 1234)
|
||||
}
|
||||
|
||||
#[test]
|
||||
// this test point locks down file type check
|
||||
fn test_file_type_check() {
|
||||
let mut flag_info_list = create_test_flag_info_list();
|
||||
flag_info_list.header.file_type = 123u8;
|
||||
let error = FlagInfoList::from_bytes(&flag_info_list.as_bytes()).unwrap_err();
|
||||
assert_eq!(
|
||||
format!("{:?}", error),
|
||||
format!("BytesParseFail(binary file is not a flag info file)")
|
||||
);
|
||||
}
|
||||
}
|
|
@ -32,6 +32,7 @@
|
|||
//! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
|
||||
//! please refer to the g3doc go/android-flags
|
||||
|
||||
pub mod flag_info;
|
||||
pub mod flag_table;
|
||||
pub mod flag_value;
|
||||
pub mod package_table;
|
||||
|
@ -46,6 +47,7 @@ use std::fs::File;
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::io::Read;
|
||||
|
||||
pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
|
||||
pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
|
||||
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
|
||||
pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
|
||||
|
@ -68,6 +70,7 @@ pub enum StorageFileType {
|
|||
PackageMap = 0,
|
||||
FlagMap = 1,
|
||||
FlagVal = 2,
|
||||
FlagInfo = 3,
|
||||
}
|
||||
|
||||
impl TryFrom<&str> for StorageFileType {
|
||||
|
@ -78,6 +81,7 @@ impl TryFrom<&str> for StorageFileType {
|
|||
"package_map" => Ok(Self::PackageMap),
|
||||
"flag_map" => Ok(Self::FlagMap),
|
||||
"flag_val" => Ok(Self::FlagVal),
|
||||
"flag_info" => Ok(Self::FlagInfo),
|
||||
_ => Err(anyhow!(
|
||||
"Invalid storage file type, valid types are package_map|flag_map|flag_val"
|
||||
)),
|
||||
|
@ -93,6 +97,7 @@ impl TryFrom<u8> for StorageFileType {
|
|||
x if x == Self::PackageMap as u8 => Ok(Self::PackageMap),
|
||||
x if x == Self::FlagMap as u8 => Ok(Self::FlagMap),
|
||||
x if x == Self::FlagVal as u8 => Ok(Self::FlagVal),
|
||||
x if x == Self::FlagInfo as u8 => Ok(Self::FlagInfo),
|
||||
_ => Err(anyhow!("Invalid storage file type")),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
//! `aconfig-storage` is a debugging tool to parse storage files
|
||||
|
||||
use aconfig_storage_file::{
|
||||
list_flags, read_file_to_bytes, AconfigStorageError, FlagTable, FlagValueList, PackageTable,
|
||||
StorageFileType,
|
||||
list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList,
|
||||
PackageTable, StorageFileType,
|
||||
};
|
||||
|
||||
use clap::{builder::ArgAction, Arg, Command};
|
||||
|
@ -67,6 +67,10 @@ fn print_storage_file(
|
|||
let flag_value = FlagValueList::from_bytes(&bytes)?;
|
||||
println!("{:?}", flag_value);
|
||||
}
|
||||
StorageFileType::FlagInfo => {
|
||||
let flag_info = FlagInfoList::from_bytes(&bytes)?;
|
||||
println!("{:?}", flag_info);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
|
||||
use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
|
||||
use crate::flag_value::{FlagValueHeader, FlagValueList};
|
||||
use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
|
||||
|
@ -124,6 +125,19 @@ pub(crate) fn create_test_flag_value_list() -> FlagValueList {
|
|||
FlagValueList { header, booleans }
|
||||
}
|
||||
|
||||
pub(crate) fn create_test_flag_info_list() -> FlagInfoList {
|
||||
let header = FlagInfoHeader {
|
||||
version: 1234,
|
||||
container: String::from("system"),
|
||||
file_type: StorageFileType::FlagInfo as u8,
|
||||
file_size: 35,
|
||||
num_flags: 8,
|
||||
boolean_flag_offset: 27,
|
||||
};
|
||||
let nodes: Vec<FlagInfoNode> = vec![FlagInfoNode { attributes: 0 }; 8];
|
||||
FlagInfoList { header, nodes }
|
||||
}
|
||||
|
||||
pub(crate) fn write_bytes_to_temp_file(bytes: &[u8]) -> Result<NamedTempFile, AconfigStorageError> {
|
||||
let mut file = NamedTempFile::new().map_err(|_| {
|
||||
AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
|
||||
|
|
|
@ -67,18 +67,18 @@ static Result<std::string> find_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;
|
||||
return ErrnoError() << "failed to open " << file;
|
||||
};
|
||||
|
||||
struct stat fd_stat;
|
||||
if (fstat(fd, &fd_stat) < 0) {
|
||||
return Error() << "fstat failed";
|
||||
return ErrnoError() << "fstat failed";
|
||||
}
|
||||
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";
|
||||
return ErrnoError() << "mmap failed";
|
||||
}
|
||||
|
||||
auto mapped_file = MappedStorageFile();
|
||||
|
|
|
@ -91,6 +91,9 @@ pub unsafe fn get_mapped_file(
|
|||
StorageFileType::PackageMap => unsafe { map_file(files_location.package_map()) },
|
||||
StorageFileType::FlagMap => unsafe { map_file(files_location.flag_map()) },
|
||||
StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
|
||||
StorageFileType::FlagInfo => {
|
||||
Err(MapFileFail(anyhow!("TODO: add support for flag info file")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,24 +58,24 @@ static Result<std::string> find_storage_file(
|
|||
static Result<MappedFlagValueFile> map_storage_file(std::string const& file) {
|
||||
struct stat file_stat;
|
||||
if (stat(file.c_str(), &file_stat) < 0) {
|
||||
return Error() << "fstat failed";
|
||||
return ErrnoError() << "stat failed";
|
||||
}
|
||||
|
||||
if ((file_stat.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) {
|
||||
return Error() << "cannot map nonwriteable file";
|
||||
return ErrnoError() << "cannot map nonwriteable file";
|
||||
}
|
||||
|
||||
size_t file_size = file_stat.st_size;
|
||||
|
||||
const int fd = open(file.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
return Error() << "failed to open " << file;
|
||||
return ErrnoError() << "failed to open " << file;
|
||||
};
|
||||
|
||||
void* const map_result =
|
||||
mmap(nullptr, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (map_result == MAP_FAILED) {
|
||||
return Error() << "mmap failed";
|
||||
return ErrnoError() << "mmap failed";
|
||||
}
|
||||
|
||||
auto mapped_file = MappedFlagValueFile();
|
||||
|
@ -95,7 +95,12 @@ Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
|
|||
if (!file_result.ok()) {
|
||||
return Error() << file_result.error();
|
||||
}
|
||||
return map_storage_file(*file_result);
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace private internal api
|
||||
|
|
Loading…
Reference in a new issue