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::{AconfigStorageError, FlagValueType};
28
29 use anyhow::anyhow;
30 use memmap2::MmapMut;
31
32 /// Get read write mapped storage files.
33 ///
34 /// \input file_path: path to the storage file
35 ///
36 /// # Safety
37 ///
38 /// The memory mapped file may have undefined behavior if there are writes to this
39 /// file not thru this memory mapped file or there are concurrent writes to this
40 /// memory mapped file. Ensure all writes to the underlying file are thru this memory
41 /// mapped file and there are no concurrent writes.
map_mutable_storage_file(file_path: &str) -> Result<MmapMut, AconfigStorageError>42 pub unsafe fn map_mutable_storage_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
43 crate::mapped_file::map_file(file_path)
44 }
45
46 /// Set boolean flag value thru mapped file and flush the change to file
47 ///
48 /// \input mapped_file: the mapped flag value file
49 /// \input index: flag index
50 /// \input value: updated flag value
51 /// \return a result of ()
52 ///
set_boolean_flag_value( file: &mut MmapMut, index: u32, value: bool, ) -> Result<(), AconfigStorageError>53 pub fn set_boolean_flag_value(
54 file: &mut MmapMut,
55 index: u32,
56 value: bool,
57 ) -> Result<(), AconfigStorageError> {
58 crate::flag_value_update::update_boolean_flag_value(file, index, value)?;
59 file.flush().map_err(|errmsg| {
60 AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
61 })
62 }
63
64 /// Set if flag is has server override thru mapped file and flush the change to file
65 ///
66 /// \input mapped_file: the mapped flag info file
67 /// \input index: flag index
68 /// \input value: updated flag has server override value
69 /// \return a result of ()
70 ///
set_flag_has_server_override( file: &mut MmapMut, flag_type: FlagValueType, index: u32, value: bool, ) -> Result<(), AconfigStorageError>71 pub fn set_flag_has_server_override(
72 file: &mut MmapMut,
73 flag_type: FlagValueType,
74 index: u32,
75 value: bool,
76 ) -> Result<(), AconfigStorageError> {
77 crate::flag_info_update::update_flag_has_server_override(file, flag_type, index, value)?;
78 file.flush().map_err(|errmsg| {
79 AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
80 })
81 }
82
83 /// Set if flag has local override thru mapped file and flush the change to file
84 ///
85 /// \input mapped_file: the mapped flag info file
86 /// \input index: flag index
87 /// \input value: updated flag has local override value
88 /// \return a result of ()
89 ///
set_flag_has_local_override( file: &mut MmapMut, flag_type: FlagValueType, index: u32, value: bool, ) -> Result<(), AconfigStorageError>90 pub fn set_flag_has_local_override(
91 file: &mut MmapMut,
92 flag_type: FlagValueType,
93 index: u32,
94 value: bool,
95 ) -> Result<(), AconfigStorageError> {
96 crate::flag_info_update::update_flag_has_local_override(file, flag_type, index, value)?;
97 file.flush().map_err(|errmsg| {
98 AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
99 })
100 }
101
102 // *************************************** //
103 // CC INTERLOP
104 // *************************************** //
105
106 // Exported rust data structure and methods, c++ code will be generated
107 #[cxx::bridge]
108 mod ffi {
109 // Flag value update return for cc interlop
110 pub struct BooleanFlagValueUpdateCXX {
111 pub update_success: bool,
112 pub offset: usize,
113 pub error_message: String,
114 }
115
116 // Flag has server override update return for cc interlop
117 pub struct FlagHasServerOverrideUpdateCXX {
118 pub update_success: bool,
119 pub offset: usize,
120 pub error_message: String,
121 }
122
123 // Flag has local override update return for cc interlop
124 pub struct FlagHasLocalOverrideUpdateCXX {
125 pub update_success: bool,
126 pub offset: usize,
127 pub error_message: String,
128 }
129
130 // Rust export to c++
131 extern "Rust" {
update_boolean_flag_value_cxx( file: &mut [u8], offset: u32, value: bool, ) -> BooleanFlagValueUpdateCXX132 pub fn update_boolean_flag_value_cxx(
133 file: &mut [u8],
134 offset: u32,
135 value: bool,
136 ) -> BooleanFlagValueUpdateCXX;
137
update_flag_has_server_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> FlagHasServerOverrideUpdateCXX138 pub fn update_flag_has_server_override_cxx(
139 file: &mut [u8],
140 flag_type: u16,
141 offset: u32,
142 value: bool,
143 ) -> FlagHasServerOverrideUpdateCXX;
144
update_flag_has_local_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> FlagHasLocalOverrideUpdateCXX145 pub fn update_flag_has_local_override_cxx(
146 file: &mut [u8],
147 flag_type: u16,
148 offset: u32,
149 value: bool,
150 ) -> FlagHasLocalOverrideUpdateCXX;
151 }
152 }
153
update_boolean_flag_value_cxx( file: &mut [u8], offset: u32, value: bool, ) -> ffi::BooleanFlagValueUpdateCXX154 pub(crate) fn update_boolean_flag_value_cxx(
155 file: &mut [u8],
156 offset: u32,
157 value: bool,
158 ) -> ffi::BooleanFlagValueUpdateCXX {
159 match crate::flag_value_update::update_boolean_flag_value(file, offset, value) {
160 Ok(head) => ffi::BooleanFlagValueUpdateCXX {
161 update_success: true,
162 offset: head,
163 error_message: String::from(""),
164 },
165 Err(errmsg) => ffi::BooleanFlagValueUpdateCXX {
166 update_success: false,
167 offset: usize::MAX,
168 error_message: format!("{:?}", errmsg),
169 },
170 }
171 }
172
update_flag_has_server_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> ffi::FlagHasServerOverrideUpdateCXX173 pub(crate) fn update_flag_has_server_override_cxx(
174 file: &mut [u8],
175 flag_type: u16,
176 offset: u32,
177 value: bool,
178 ) -> ffi::FlagHasServerOverrideUpdateCXX {
179 match FlagValueType::try_from(flag_type) {
180 Ok(value_type) => {
181 match crate::flag_info_update::update_flag_has_server_override(
182 file, value_type, offset, value,
183 ) {
184 Ok(head) => ffi::FlagHasServerOverrideUpdateCXX {
185 update_success: true,
186 offset: head,
187 error_message: String::from(""),
188 },
189 Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {
190 update_success: false,
191 offset: usize::MAX,
192 error_message: format!("{:?}", errmsg),
193 },
194 }
195 }
196 Err(errmsg) => ffi::FlagHasServerOverrideUpdateCXX {
197 update_success: false,
198 offset: usize::MAX,
199 error_message: format!("{:?}", errmsg),
200 },
201 }
202 }
203
update_flag_has_local_override_cxx( file: &mut [u8], flag_type: u16, offset: u32, value: bool, ) -> ffi::FlagHasLocalOverrideUpdateCXX204 pub(crate) fn update_flag_has_local_override_cxx(
205 file: &mut [u8],
206 flag_type: u16,
207 offset: u32,
208 value: bool,
209 ) -> ffi::FlagHasLocalOverrideUpdateCXX {
210 match FlagValueType::try_from(flag_type) {
211 Ok(value_type) => {
212 match crate::flag_info_update::update_flag_has_local_override(
213 file, value_type, offset, value,
214 ) {
215 Ok(head) => ffi::FlagHasLocalOverrideUpdateCXX {
216 update_success: true,
217 offset: head,
218 error_message: String::from(""),
219 },
220 Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {
221 update_success: false,
222 offset: usize::MAX,
223 error_message: format!("{:?}", errmsg),
224 },
225 }
226 }
227 Err(errmsg) => ffi::FlagHasLocalOverrideUpdateCXX {
228 update_success: false,
229 offset: usize::MAX,
230 error_message: format!("{:?}", errmsg),
231 },
232 }
233 }
234
235 #[cfg(test)]
236 mod tests {
237 use super::*;
238 use crate::test_utils::copy_to_temp_file;
239 use aconfig_storage_file::FlagInfoBit;
240 use aconfig_storage_read_api::flag_info_query::find_flag_attribute;
241 use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
242 use std::fs::File;
243 use std::io::Read;
244
get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool245 fn get_boolean_flag_value_at_offset(file: &str, offset: u32) -> bool {
246 let mut f = File::open(&file).unwrap();
247 let mut bytes = Vec::new();
248 f.read_to_end(&mut bytes).unwrap();
249 find_boolean_flag_value(&bytes, offset).unwrap()
250 }
251
252 #[test]
test_set_boolean_flag_value()253 fn test_set_boolean_flag_value() {
254 let flag_value_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
255 let flag_value_path = flag_value_file.path().display().to_string();
256
257 // SAFETY:
258 // The safety here is guaranteed as only this single threaded test process will
259 // write to this file
260 unsafe {
261 let mut file = map_mutable_storage_file(&flag_value_path).unwrap();
262 for i in 0..8 {
263 set_boolean_flag_value(&mut file, i, true).unwrap();
264 let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
265 assert_eq!(value, true);
266
267 set_boolean_flag_value(&mut file, i, false).unwrap();
268 let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
269 assert_eq!(value, false);
270 }
271 }
272 }
273
get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8274 fn get_flag_attribute_at_offset(file: &str, value_type: FlagValueType, offset: u32) -> u8 {
275 let mut f = File::open(&file).unwrap();
276 let mut bytes = Vec::new();
277 f.read_to_end(&mut bytes).unwrap();
278 find_flag_attribute(&bytes, value_type, offset).unwrap()
279 }
280
281 #[test]
test_set_flag_has_server_override()282 fn test_set_flag_has_server_override() {
283 let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
284 let flag_info_path = flag_info_file.path().display().to_string();
285
286 // SAFETY:
287 // The safety here is guaranteed as only this single threaded test process will
288 // write to this file
289 unsafe {
290 let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
291 for i in 0..8 {
292 set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
293 let attribute =
294 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
295 assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) != 0);
296 set_flag_has_server_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
297 let attribute =
298 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
299 assert!((attribute & (FlagInfoBit::HasServerOverride as u8)) == 0);
300 }
301 }
302 }
303
304 #[test]
test_set_flag_has_local_override()305 fn test_set_flag_has_local_override() {
306 let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
307 let flag_info_path = flag_info_file.path().display().to_string();
308
309 // SAFETY:
310 // The safety here is guaranteed as only this single threaded test process will
311 // write to this file
312 unsafe {
313 let mut file = map_mutable_storage_file(&flag_info_path).unwrap();
314 for i in 0..8 {
315 set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
316 let attribute =
317 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
318 assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) != 0);
319 set_flag_has_local_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
320 let attribute =
321 get_flag_attribute_at_offset(&flag_info_path, FlagValueType::Boolean, i);
322 assert!((attribute & (FlagInfoBit::HasLocalOverride as u8)) == 0);
323 }
324 }
325 }
326 }
327