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 //! `aconfig_storage_write_api` is a crate that defines write apis to update flag value
18 //! in storage file. It provides one api to interface with storage files.
19
20 pub mod flag_info_update;
21 pub mod flag_value_update;
22 pub mod mapped_file;
23
24 #[cfg(test)]
25 mod test_utils;
26
27 use aconfig_storage_file::{
28 AconfigStorageError, FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagTable, FlagValueType,
29 PackageTable, StorageFileType, StoredFlagType, FILE_VERSION,
30 };
31
32 use anyhow::anyhow;
33 use memmap2::MmapMut;
34 use std::fs::File;
35 use std::io::{Read, Write};
36
37 /// Get read write mapped storage files.
38 ///
39 /// \input file_path: path to the storage file
40 ///
41 /// # Safety
42 ///
43 /// The memory mapped file may have undefined behavior if there are writes to this
44 /// file not thru this memory mapped file or there are concurrent writes to this
45 /// memory mapped file. Ensure all writes to the underlying file are thru this memory
46 /// mapped file and there are no concurrent writes.
map_mutable_storage_file(file_path: &str) -> Result<MmapMut, AconfigStorageError>47 pub unsafe fn map_mutable_storage_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
48 crate::mapped_file::map_file(file_path)
49 }
50
51 /// Set boolean flag value thru mapped file and flush the change to file
52 ///
53 /// \input mapped_file: the mapped flag value file
54 /// \input index: flag index
55 /// \input value: updated flag value
56 /// \return a result of ()
57 ///
set_boolean_flag_value( file: &mut MmapMut, index: u32, value: bool, ) -> Result<(), AconfigStorageError>58 pub fn set_boolean_flag_value(
59 file: &mut MmapMut,
60 index: u32,
61 value: bool,
62 ) -> Result<(), AconfigStorageError> {
63 crate::flag_value_update::update_boolean_flag_value(file, index, value)?;
64 file.flush().map_err(|errmsg| {
65 AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
66 })
67 }
68
69 /// Set if flag is has server override thru mapped file and flush the change to file
70 ///
71 /// \input mapped_file: the mapped flag info file
72 /// \input index: flag index
73 /// \input value: updated flag has server override value
74 /// \return a result of ()
75 ///
set_flag_has_server_override( file: &mut MmapMut, flag_type: FlagValueType, index: u32, value: bool, ) -> Result<(), AconfigStorageError>76 pub fn set_flag_has_server_override(
77 file: &mut MmapMut,
78 flag_type: FlagValueType,
79 index: u32,
80 value: bool,
81 ) -> Result<(), AconfigStorageError> {
82 crate::flag_info_update::update_flag_has_server_override(file, flag_type, index, value)?;
83 file.flush().map_err(|errmsg| {
84 AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
85 })
86 }
87
88 /// Set if flag has local override thru mapped file and flush the change to file
89 ///
90 /// \input mapped_file: the mapped flag info file
91 /// \input index: flag index
92 /// \input value: updated flag has local override value
93 /// \return a result of ()
94 ///
set_flag_has_local_override( file: &mut MmapMut, flag_type: FlagValueType, index: u32, value: bool, ) -> Result<(), AconfigStorageError>95 pub fn set_flag_has_local_override(
96 file: &mut MmapMut,
97 flag_type: FlagValueType,
98 index: u32,
99 value: bool,
100 ) -> Result<(), AconfigStorageError> {
101 crate::flag_info_update::update_flag_has_local_override(file, flag_type, index, value)?;
102 file.flush().map_err(|errmsg| {
103 AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
104 })
105 }
106
107 /// Read in storage file as bytes
read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError>108 fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
109 let mut file = File::open(file_path).map_err(|errmsg| {
110 AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
111 })?;
112 let mut buffer = Vec::new();
113 file.read_to_end(&mut buffer).map_err(|errmsg| {
114 AconfigStorageError::FileReadFail(anyhow!(
115 "Failed to read bytes from file {}: {}",
116 file_path,
117 errmsg
118 ))
119 })?;
120 Ok(buffer)
121 }
122
123 /// Create flag info file given package map file and flag map file
124 /// \input package_map: package map file
125 /// \input flag_map: flag map file
126 /// \output flag_info_out: created flag info file
create_flag_info( package_map: &str, flag_map: &str, flag_info_out: &str, ) -> Result<(), AconfigStorageError>127 pub fn create_flag_info(
128 package_map: &str,
129 flag_map: &str,
130 flag_info_out: &str,
131 ) -> Result<(), AconfigStorageError> {
132 let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
133 let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
134
135 if package_table.header.container != flag_table.header.container {
136 return Err(AconfigStorageError::FileCreationFail(anyhow!(
137 "container for package map {} and flag map {} does not match",
138 package_table.header.container,
139 flag_table.header.container,
140 )));
141 }
142
143 let mut package_start_index = vec![0; package_table.header.num_packages as usize];
144 for node in package_table.nodes.iter() {
145 package_start_index[node.package_id as usize] = node.boolean_start_index;
146 }
147
148 let mut is_flag_rw = vec![false; flag_table.header.num_flags as usize];
149 for node in flag_table.nodes.iter() {
150 let flag_index = package_start_index[node.package_id as usize] + node.flag_index as u32;
151 is_flag_rw[flag_index as usize] = node.flag_type == StoredFlagType::ReadWriteBoolean;
152 }
153
154 let mut list = FlagInfoList {
155 header: FlagInfoHeader {
156 version: FILE_VERSION,
157 container: flag_table.header.container,
158 file_type: StorageFileType::FlagInfo as u8,
159 file_size: 0,
160 num_flags: flag_table.header.num_flags,
161 boolean_flag_offset: 0,
162 },
163 nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(),
164 };
165
166 list.header.boolean_flag_offset = list.header.into_bytes().len() as u32;
167 list.header.file_size = list.into_bytes().len() as u32;
168
169 let mut file = File::create(flag_info_out).map_err(|errmsg| {
170 AconfigStorageError::FileCreationFail(anyhow!(
171 "fail to create file {}: {}",
172 flag_info_out,
173 errmsg
174 ))
175 })?;
176 file.write_all(&list.into_bytes()).map_err(|errmsg| {
177 AconfigStorageError::FileCreationFail(anyhow!(
178 "fail to write to file {}: {}",
179 flag_info_out,
180 errmsg
181 ))
182 })?;
183
184 Ok(())
185 }
186
187 // *************************************** //
188 // CC INTERLOP
189 // *************************************** //
190
191 // Exported rust data structure and methods, c++ code will be generated
192 #[cxx::bridge]
193 mod ffi {
194 // Flag value update return for cc interlop
195 pub struct BooleanFlagValueUpdateCXX {
196 pub update_success: bool,
197 pub error_message: String,
198 }
199
200 // Flag has server override update return for cc interlop
201 pub struct FlagHasServerOverrideUpdateCXX {
202 pub update_success: bool,
203 pub error_message: String,
204 }
205
206 // Flag has local override update return for cc interlop
207 pub struct FlagHasLocalOverrideUpdateCXX {
208 pub update_success: bool,
209 pub error_message: String,
210 }
211
212 // Flag info file creation return for cc interlop
213 pub struct FlagInfoCreationCXX {
214 pub success: bool,
215 pub error_message: String,
216 }
217
218 // Rust export to c++
219 extern "Rust" {
update_boolean_flag_value_cxx( file: &mut [u8], offset: u32, value: bool, ) -> BooleanFlagValueUpdateCXX220 pub fn update_boolean_flag_value_cxx(
221 file: &mut [u8],
222 offset: u32,
223 value: bool,
224 ) -> BooleanFlagValueUpdateCXX;
225
update_flag_has_server_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> FlagHasServerOverrideUpdateCXX226 pub fn update_flag_has_server_override_cxx(
227 file: &mut [u8],
228 flag_type: u16,
229 offset: u32,
230 value: bool,
231 ) -> FlagHasServerOverrideUpdateCXX;
232
update_flag_has_local_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> FlagHasLocalOverrideUpdateCXX233 pub fn update_flag_has_local_override_cxx(
234 file: &mut [u8],
235 flag_type: u16,
236 offset: u32,
237 value: bool,
238 ) -> FlagHasLocalOverrideUpdateCXX;
239
create_flag_info_cxx( package_map: &str, flag_map: &str, flag_info_out: &str, ) -> FlagInfoCreationCXX240 pub fn create_flag_info_cxx(
241 package_map: &str,
242 flag_map: &str,
243 flag_info_out: &str,
244 ) -> FlagInfoCreationCXX;
245 }
246 }
247
update_boolean_flag_value_cxx( file: &mut [u8], offset: u32, value: bool, ) -> ffi::BooleanFlagValueUpdateCXX248 pub(crate) fn update_boolean_flag_value_cxx(
249 file: &mut [u8],
250 offset: u32,
251 value: bool,
252 ) -> ffi::BooleanFlagValueUpdateCXX {
253 match crate::flag_value_update::update_boolean_flag_value(file, offset, value) {
254 Ok(()) => {
255 ffi::BooleanFlagValueUpdateCXX { update_success: true, error_message: String::from("") }
256 }
257 Err(errmsg) => ffi::BooleanFlagValueUpdateCXX {
258 update_success: false,
259 error_message: format!("{:?}", errmsg),
260 },
261 }
262 }
263
update_flag_has_server_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> ffi::FlagHasServerOverrideUpdateCXX264 pub(crate) fn update_flag_has_server_override_cxx(
265 file: &mut [u8],
266 flag_type: u16,
267 offset: u32,
268 value: bool,
269 ) -> ffi::FlagHasServerOverrideUpdateCXX {
270 match FlagValueType::try_from(flag_type) {
271 Ok(value_type) => {
272 match crate::flag_info_update::update_flag_has_server_override(
273 file, value_type, offset, value,
274 ) {
275 Ok(()) => ffi::FlagHasServerOverrideUpdateCXX {
276 update_success: true,
277 error_message: String::from(""),
278 },
279 Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {
280 update_success: false,
281 error_message: format!("{:?}", errmsg),
282 },
283 }
284 }
285 Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {
286 update_success: false,
287 error_message: format!("{:?}", errmsg),
288 },
289 }
290 }
291
update_flag_has_local_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> ffi::FlagHasLocalOverrideUpdateCXX292 pub(crate) fn update_flag_has_local_override_cxx(
293 file: &mut [u8],
294 flag_type: u16,
295 offset: u32,
296 value: bool,
297 ) -> ffi::FlagHasLocalOverrideUpdateCXX {
298 match FlagValueType::try_from(flag_type) {
299 Ok(value_type) => {
300 match crate::flag_info_update::update_flag_has_local_override(
301 file, value_type, offset, value,
302 ) {
303 Ok(()) => ffi::FlagHasLocalOverrideUpdateCXX {
304 update_success: true,
305 error_message: String::from(""),
306 },
307 Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {
308 update_success: false,
309 error_message: format!("{:?}", errmsg),
310 },
311 }
312 }
313 Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {
314 update_success: false,
315 error_message: format!("{:?}", errmsg),
316 },
317 }
318 }
319
320 /// Create flag info file cc interlop
create_flag_info_cxx( package_map: &str, flag_map: &str, flag_info_out: &str, ) -> ffi::FlagInfoCreationCXX321 pub(crate) fn create_flag_info_cxx(
322 package_map: &str,
323 flag_map: &str,
324 flag_info_out: &str,
325 ) -> ffi::FlagInfoCreationCXX {
326 match create_flag_info(package_map, flag_map, flag_info_out) {
327 Ok(()) => ffi::FlagInfoCreationCXX { success: true, error_message: String::from("") },
328 Err(errmsg) => {
329 ffi::FlagInfoCreationCXX { success: false, error_message: format!("{:?}", errmsg) }
330 }
331 }
332 }
333
334 #[cfg(test)]
335 mod tests {
336 use super::*;
337 use crate::test_utils::copy_to_temp_file;
338 use aconfig_storage_file::test_utils::{
339 create_test_flag_info_list, create_test_flag_table, create_test_package_table,
340 write_bytes_to_temp_file,
341 };
342 use aconfig_storage_file::FlagInfoBit;
343 use aconfig_storage_read_api::flag_info_query::find_flag_attribute;
344 use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
345 use std::fs::File;
346 use std::io::Read;
347 use tempfile::NamedTempFile;
348
get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool349 fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool {
350 let mut f = File::open(&file).unwrap();
351 let mut bytes = Vec::new();
352 f.read_to_end(&mut bytes).unwrap();
353 find_boolean_flag_value(&bytes, offset).unwrap()
354 }
355
356 #[test]
test_set_boolean_flag_value()357 fn test_set_boolean_flag_value() {
358 let flag_value_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
359 let flag_value_path = flag_value_file.path().display().to_string();
360
361 // SAFETY:
362 // The safety here is guaranteed as only this single threaded test process will
363 // write to this file
364 unsafe {
365 let mut file = map_mutable_storage_file(&flag_value_path).unwrap();
366 for i in 0..8 {
367 set_boolean_flag_value(&mut file, i, true).unwrap();
368 let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
369 assert_eq!(value, true);
370
371 set_boolean_flag_value(&mut file, i, false).unwrap();
372 let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
373 assert_eq!(value, false);
374 }
375 }
376 }
377
get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8378 fn get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8 {
379 let mut f = File::open(&file).unwrap();
380 let mut bytes = Vec::new();
381 f.read_to_end(&mut bytes).unwrap();
382 find_flag_attribute(&bytes, value_type, offset).unwrap()
383 }
384
385 #[test]
test_set_flag_has_server_override()386 fn test_set_flag_has_server_override() {
387 let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
388 let flag_info_path = flag_info_file.path().display().to_string();
389
390 // SAFETY:
391 // The safety here is guaranteed as only this single threaded test process will
392 // write to this file
393 unsafe {
394 let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
395 for i in 0..8 {
396 set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
397 let attribute =
398 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
399 assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0);
400 set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
401 let attribute =
402 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
403 assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0);
404 }
405 }
406 }
407
408 #[test]
test_set_flag_has_local_override()409 fn test_set_flag_has_local_override() {
410 let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
411 let flag_info_path = flag_info_file.path().display().to_string();
412
413 // SAFETY:
414 // The safety here is guaranteed as only this single threaded test process will
415 // write to this file
416 unsafe {
417 let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
418 for i in 0..8 {
419 set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
420 let attribute =
421 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
422 assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0);
423 set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
424 let attribute =
425 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
426 assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0);
427 }
428 }
429 }
430
create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError>431 fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> {
432 let file = NamedTempFile::new().map_err(|_| {
433 AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
434 })?;
435 Ok(file)
436 }
437
438 #[test]
439 // this test point locks down the flag info creation
test_create_flag_info()440 fn test_create_flag_info() {
441 let package_table =
442 write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
443 let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
444 let flag_info = create_empty_temp_file().unwrap();
445
446 let package_table_path = package_table.path().display().to_string();
447 let flag_table_path = flag_table.path().display().to_string();
448 let flag_info_path = flag_info.path().display().to_string();
449
450 assert!(create_flag_info(&package_table_path, &flag_table_path, &flag_info_path).is_ok());
451
452 let flag_info =
453 FlagInfoList::from_bytes(&read_file_to_bytes(&flag_info_path).unwrap()).unwrap();
454 let expected_flag_info = create_test_flag_info_list();
455 assert_eq!(flag_info, expected_flag_info);
456 }
457 }
458