1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // When building with the Android tool-chain 18 // 19 // - an external crate `aconfig_storage_metadata_protos` will be generated 20 // - the feature "cargo" will be disabled 21 // 22 // When building with cargo 23 // 24 // - a local sub-module will be generated in OUT_DIR and included in this file 25 // - the feature "cargo" will be enabled 26 // 27 // This module hides these differences from the rest of the codebase. 28 29 // ---- When building with the Android tool-chain ---- 30 #[cfg(not(feature = "cargo"))] 31 mod auto_generated { 32 pub use aconfig_storage_protos::aconfig_storage_metadata as ProtoStorage; 33 pub use ProtoStorage::Storage_file_info as ProtoStorageFileInfo; 34 pub use ProtoStorage::Storage_files as ProtoStorageFiles; 35 } 36 37 // ---- When building with cargo ---- 38 #[cfg(feature = "cargo")] 39 mod auto_generated { 40 // include! statements should be avoided (because they import file contents verbatim), but 41 // because this is only used during local development, and only if using cargo instead of the 42 // Android tool-chain, we allow it 43 include!(concat!(env!("OUT_DIR"), "/aconfig_storage_protos/mod.rs")); 44 pub use aconfig_storage_metadata::Storage_file_info as ProtoStorageFileInfo; 45 pub use aconfig_storage_metadata::Storage_files as ProtoStorageFiles; 46 } 47 48 // ---- Common for both the Android tool-chain and cargo ---- 49 pub use auto_generated::*; 50 51 use anyhow::Result; 52 use protobuf::Message; 53 use std::io::Write; 54 use tempfile::NamedTempFile; 55 56 pub mod storage_record_pb { 57 use super::*; 58 use anyhow::ensure; 59 try_from_binary_proto(bytes: &[u8]) -> Result<ProtoStorageFiles>60 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoStorageFiles> { 61 let message: ProtoStorageFiles = protobuf::Message::parse_from_bytes(bytes)?; 62 verify_fields(&message)?; 63 Ok(message) 64 } 65 verify_fields(storage_files: &ProtoStorageFiles) -> Result<()>66 pub fn verify_fields(storage_files: &ProtoStorageFiles) -> Result<()> { 67 for storage_file_info in storage_files.files.iter() { 68 ensure!( 69 !storage_file_info.package_map().is_empty(), 70 "invalid storage file record: missing package map file for container {}", 71 storage_file_info.container() 72 ); 73 ensure!( 74 !storage_file_info.flag_map().is_empty(), 75 "invalid storage file record: missing flag map file for container {}", 76 storage_file_info.container() 77 ); 78 ensure!( 79 !storage_file_info.flag_val().is_empty(), 80 "invalid storage file record: missing flag val file for container {}", 81 storage_file_info.container() 82 ); 83 } 84 Ok(()) 85 } 86 get_binary_proto_from_text_proto(text_proto: &str) -> Result<Vec<u8>>87 pub fn get_binary_proto_from_text_proto(text_proto: &str) -> Result<Vec<u8>> { 88 let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?; 89 let mut binary_proto = Vec::new(); 90 storage_files.write_to_vec(&mut binary_proto)?; 91 Ok(binary_proto) 92 } 93 write_proto_to_temp_file(text_proto: &str) -> Result<NamedTempFile>94 pub fn write_proto_to_temp_file(text_proto: &str) -> Result<NamedTempFile> { 95 let bytes = get_binary_proto_from_text_proto(text_proto).unwrap(); 96 let mut file = NamedTempFile::new()?; 97 let _ = file.write_all(&bytes); 98 Ok(file) 99 } 100 } 101 102 #[cfg(test)] 103 mod tests { 104 use super::*; 105 106 #[test] test_parse_storage_record_pb()107 fn test_parse_storage_record_pb() { 108 let text_proto = r#" 109 files { 110 version: 0 111 container: "system" 112 package_map: "/system/etc/package.map" 113 flag_map: "/system/etc/flag.map" 114 flag_val: "/metadata/aconfig/system.val" 115 timestamp: 12345 116 } 117 files { 118 version: 1 119 container: "product" 120 package_map: "/product/etc/package.map" 121 flag_map: "/product/etc/flag.map" 122 flag_val: "/metadata/aconfig/product.val" 123 timestamp: 54321 124 } 125 "#; 126 let binary_proto_bytes = 127 storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); 128 let storage_files = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap(); 129 assert_eq!(storage_files.files.len(), 2); 130 let system_file = &storage_files.files[0]; 131 assert_eq!(system_file.version(), 0); 132 assert_eq!(system_file.container(), "system"); 133 assert_eq!(system_file.package_map(), "/system/etc/package.map"); 134 assert_eq!(system_file.flag_map(), "/system/etc/flag.map"); 135 assert_eq!(system_file.flag_val(), "/metadata/aconfig/system.val"); 136 assert_eq!(system_file.timestamp(), 12345); 137 let product_file = &storage_files.files[1]; 138 assert_eq!(product_file.version(), 1); 139 assert_eq!(product_file.container(), "product"); 140 assert_eq!(product_file.package_map(), "/product/etc/package.map"); 141 assert_eq!(product_file.flag_map(), "/product/etc/flag.map"); 142 assert_eq!(product_file.flag_val(), "/metadata/aconfig/product.val"); 143 assert_eq!(product_file.timestamp(), 54321); 144 } 145 146 #[test] test_parse_invalid_storage_record_pb()147 fn test_parse_invalid_storage_record_pb() { 148 let text_proto = r#" 149 files { 150 version: 0 151 container: "system" 152 package_map: "" 153 flag_map: "/system/etc/flag.map" 154 flag_val: "/metadata/aconfig/system.val" 155 timestamp: 12345 156 } 157 "#; 158 let binary_proto_bytes = 159 storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); 160 let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); 161 assert_eq!( 162 format!("{:?}", err), 163 "invalid storage file record: missing package map file for container system" 164 ); 165 166 let text_proto = r#" 167 files { 168 version: 0 169 container: "system" 170 package_map: "/system/etc/package.map" 171 flag_map: "" 172 flag_val: "/metadata/aconfig/system.val" 173 timestamp: 12345 174 } 175 "#; 176 let binary_proto_bytes = 177 storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); 178 let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); 179 assert_eq!( 180 format!("{:?}", err), 181 "invalid storage file record: missing flag map file for container system" 182 ); 183 184 let text_proto = r#" 185 files { 186 version: 0 187 container: "system" 188 package_map: "/system/etc/package.map" 189 flag_map: "/system/etc/flag.map" 190 flag_val: "" 191 timestamp: 12345 192 } 193 "#; 194 let binary_proto_bytes = 195 storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap(); 196 let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err(); 197 assert_eq!( 198 format!("{:?}", err), 199 "invalid storage file record: missing flag val file for container system" 200 ); 201 } 202 } 203