• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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