/* * 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. */ // When building with the Android tool-chain // // - an external crate `aconfig_storage_metadata_protos` will be generated // - the feature "cargo" will be disabled // // When building with cargo // // - a local sub-module will be generated in OUT_DIR and included in this file // - the feature "cargo" will be enabled // // This module hides these differences from the rest of the codebase. // ---- When building with the Android tool-chain ---- #[cfg(not(feature = "cargo"))] mod auto_generated { pub use aconfig_storage_protos::aconfig_storage_metadata as ProtoStorage; pub use ProtoStorage::Storage_file_info as ProtoStorageFileInfo; pub use ProtoStorage::Storage_files as ProtoStorageFiles; } // ---- When building with cargo ---- #[cfg(feature = "cargo")] mod auto_generated { // include! statements should be avoided (because they import file contents verbatim), but // because this is only used during local development, and only if using cargo instead of the // Android tool-chain, we allow it include!(concat!(env!("OUT_DIR"), "/aconfig_storage_protos/mod.rs")); pub use aconfig_storage_metadata::Storage_file_info as ProtoStorageFileInfo; pub use aconfig_storage_metadata::Storage_files as ProtoStorageFiles; } // ---- Common for both the Android tool-chain and cargo ---- pub use auto_generated::*; use anyhow::Result; use protobuf::Message; use std::io::Write; use tempfile::NamedTempFile; pub mod storage_record_pb { use super::*; use anyhow::ensure; pub fn try_from_binary_proto(bytes: &[u8]) -> Result { let message: ProtoStorageFiles = protobuf::Message::parse_from_bytes(bytes)?; verify_fields(&message)?; Ok(message) } pub fn verify_fields(storage_files: &ProtoStorageFiles) -> Result<()> { for storage_file_info in storage_files.files.iter() { ensure!( !storage_file_info.package_map().is_empty(), "invalid storage file record: missing package map file for container {}", storage_file_info.container() ); ensure!( !storage_file_info.flag_map().is_empty(), "invalid storage file record: missing flag map file for container {}", storage_file_info.container() ); ensure!( !storage_file_info.flag_val().is_empty(), "invalid storage file record: missing flag val file for container {}", storage_file_info.container() ); } Ok(()) } pub fn get_binary_proto_from_text_proto(text_proto: &str) -> Result> { let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?; let mut binary_proto = Vec::new(); storage_files.write_to_vec(&mut binary_proto)?; Ok(binary_proto) } pub fn write_proto_to_temp_file(text_proto: &str) -> Result { let bytes = get_binary_proto_from_text_proto(text_proto).unwrap(); let mut file = NamedTempFile::new()?; let _ = file.write_all(&bytes); Ok(file) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_storage_record_pb() { 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 binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let storage_files = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap(); assert_eq!(storage_files.files.len(), 2); let system_file = &storage_files.files[0]; assert_eq!(system_file.version(), 0); assert_eq!(system_file.container(), "system"); assert_eq!(system_file.package_map(), "/system/etc/package.map"); assert_eq!(system_file.flag_map(), "/system/etc/flag.map"); assert_eq!(system_file.flag_val(), "/metadata/aconfig/system.val"); assert_eq!(system_file.timestamp(), 12345); let product_file = &storage_files.files[1]; assert_eq!(product_file.version(), 1); assert_eq!(product_file.container(), "product"); assert_eq!(product_file.package_map(), "/product/etc/package.map"); assert_eq!(product_file.flag_map(), "/product/etc/flag.map"); assert_eq!(product_file.flag_val(), "/metadata/aconfig/product.val"); assert_eq!(product_file.timestamp(), 54321); } #[test] fn test_parse_invalid_storage_record_pb() { let text_proto = r#" files { version: 0 container: "system" package_map: "" flag_map: "/system/etc/flag.map" flag_val: "/metadata/aconfig/system.val" timestamp: 12345 } "#; let binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); assert_eq!( format!("{:?}", err), "invalid storage file record: missing package map file for container system" ); let text_proto = r#" files { version: 0 container: "system" package_map: "/system/etc/package.map" flag_map: "" flag_val: "/metadata/aconfig/system.val" timestamp: 12345 } "#; let binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); assert_eq!( format!("{:?}", err), "invalid storage file record: missing flag map file for container system" ); let text_proto = r#" files { version: 0 container: "system" package_map: "/system/etc/package.map" flag_map: "/system/etc/flag.map" flag_val: "" timestamp: 12345 } "#; let binary_proto_bytes = storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); assert_eq!( format!("{:?}", err), "invalid storage file record: missing flag val file for container system" ); } }