1 /*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 //! This module provides interfaces for database management.
17 //! Databases are isolated based on users and protected by locks.
18
19 use std::fs::File;
20 use std::fs::OpenOptions;
21 use std::{fs, path::Path, sync::Mutex};
22
23 use asset_common::{CallingInfo, OwnerType, OWNER_INFO_SEPARATOR};
24 use asset_definition::{log_throw_error, AssetError, ErrCode, Extension, Result, Value};
25 use asset_file_operator::common::DB_SUFFIX;
26 use asset_log::logi;
27 use asset_utils::hasher;
28 use lazy_static::lazy_static;
29 use serde::{Deserialize, Serialize};
30 use ylong_json::{to_writer, from_reader};
31
32 use crate::database::get_specific_db_version;
33 use crate::{
34 database::{
35 fmt_backup_path, fmt_de_db_path_with_name, get_db, get_db_by_type, get_split_db_lock_by_user_id, Database,
36 CE_ROOT_PATH, DE_ROOT_PATH, OLD_DB_NAME,
37 },
38 types::{column, DB_UPGRADE_VERSION, DB_UPGRADE_VERSION_V4, DbMap, QueryOptions},
39 };
40
41 const MINIM_OWNER_INFO_LEN: usize = 3;
42 const REMOVE_INDEX: usize = 2;
43 const TRUNCATE_LEN: usize = 4;
44 static MAX_BATCH_NUM: u32 = 100;
45
46 /// Code used to identify the origin version.
47 #[derive(Clone, Debug, PartialEq)]
48 pub enum OriginVersion {
49 /// Code for version 5.0.1.
50 V1 = 1,
51 /// Code for version 5.0.2.
52 V2 = 2,
53 /// Code for other versions.
54 V3 = 3,
55 }
56
57 /// Struct used to identify the original version and the list of haps to be upgraded.
58 #[derive(Debug, Serialize, Deserialize, PartialEq, Default)]
59 pub struct UpgradeData {
60 /// The original version.
61 pub version: u32,
62 /// The list of haps to be upgraded.
63 pub upgrade_list: Vec<String>,
64 }
65
66 lazy_static! {
67 static ref GLOBAL_FILE_LOCK: Mutex<()> = Mutex::new(());
68 }
69
70 #[inline(always)]
fmt_old_de_db_path(user_id: i32) -> String71 pub(crate) fn fmt_old_de_db_path(user_id: i32) -> String {
72 format!("{}/{}/asset.db", DE_ROOT_PATH, user_id)
73 }
74
check_old_db_exist(user_id: i32) -> bool75 fn check_old_db_exist(user_id: i32) -> bool {
76 let path_str = fmt_old_de_db_path(user_id);
77 let path = Path::new(&path_str);
78 path.exists()
79 }
80
to_hex(bytes: &Vec<u8>) -> Vec<u8>81 fn to_hex(bytes: &Vec<u8>) -> Vec<u8> {
82 let bytes_len = bytes.len();
83 let scale_capacity = 2;
84 let mut hex_vec = Vec::with_capacity(bytes_len * scale_capacity);
85 for byte in bytes.iter() {
86 hex_vec.extend(format!("{:02x}", byte).as_bytes());
87 }
88 hex_vec
89 }
90
91 /// Use owner_type and owner_info construct db name.
construct_splited_db_name(calling_info: &CallingInfo, is_ce: bool) -> Result<String>92 pub fn construct_splited_db_name(calling_info: &CallingInfo, is_ce: bool) -> Result<String> {
93 let mut res: String = match calling_info.owner_type_enum() {
94 OwnerType::HapGroup => match (calling_info.developer_id(), calling_info.group_id()) {
95 (Some(developer_id), Some(group_id)) => format!(
96 "Group_{}_{}",
97 String::from_utf8_lossy(developer_id).trim_end_matches('\0'),
98 String::from_utf8_lossy(&to_hex(&hasher::sha256(true, group_id)))
99 ),
100 _ => return log_throw_error!(ErrCode::DatabaseError, "[FATAL]Wrong queried owner group info."),
101 },
102 OwnerType::Hap => {
103 let owner_info_string = String::from_utf8_lossy(calling_info.owner_info()).to_string();
104 let split_owner_info: Vec<&str> = owner_info_string.split(OWNER_INFO_SEPARATOR).collect();
105 if split_owner_info.len() < MINIM_OWNER_INFO_LEN || split_owner_info.last().is_none() {
106 return log_throw_error!(ErrCode::DatabaseError, "[FATAL]Wrong queried owner info!");
107 }
108 let app_index = split_owner_info.last().unwrap();
109 let mut split_owner_info_mut = split_owner_info.clone();
110 for _ in 0..REMOVE_INDEX {
111 split_owner_info_mut.pop();
112 }
113 let owner_info = split_owner_info_mut.join("_").clone();
114 format!("Hap_{}_{}", owner_info, app_index)
115 },
116 OwnerType::Native => format!("Native_{}", String::from_utf8_lossy(calling_info.owner_info())),
117 };
118 if is_ce {
119 res = format!("enc_{}", res)
120 }
121 Ok(res)
122 }
123
get_db_before_split(user_id: i32) -> Result<Database>124 fn get_db_before_split(user_id: i32) -> Result<Database> {
125 let db_path = fmt_de_db_path_with_name(user_id, OLD_DB_NAME);
126 get_db_by_type(user_id, OLD_DB_NAME, db_path, None)
127 }
128
get_value_from_db_map(db_map: &DbMap, key: &str) -> Result<Value>129 fn get_value_from_db_map(db_map: &DbMap, key: &str) -> Result<Value> {
130 match db_map.get(key) {
131 Some(value) => Ok(value.clone()),
132 _ => log_throw_error!(ErrCode::DatabaseError, "[FATAL]Get value from {} failed.", key),
133 }
134 }
135
remove_old_db(user_id: i32) -> Result<()>136 fn remove_old_db(user_id: i32) -> Result<()> {
137 let mut remove_db_files = vec![];
138 let path = fmt_de_db_path_with_name(user_id, OLD_DB_NAME);
139 remove_db_files.push(path.clone());
140 remove_db_files.push(fmt_backup_path(path.as_str()));
141 for file_path in &remove_db_files {
142 fs::remove_file(file_path)?;
143 }
144 Ok(())
145 }
146
get_new_db(user_id: i32, info_map: &DbMap) -> Result<Database>147 fn get_new_db(user_id: i32, info_map: &DbMap) -> Result<Database> {
148 // 1.1 construct db name
149 let owner_type = OwnerType::try_from(info_map.get_num_attr(&column::OWNER_TYPE)?.to_owned())?;
150 let owner_info = info_map.get_bytes_attr(&column::OWNER)?;
151 let calling_info = CallingInfo::new(user_id, owner_type, owner_info.to_vec(), None);
152 let new_db_name = construct_splited_db_name(&calling_info, false)?;
153 // 1.2 construct new db
154 let db_path = fmt_de_db_path_with_name(user_id, &new_db_name);
155 get_db_by_type(user_id, &new_db_name, db_path, None)
156 }
157
158 /// Trigger upgrade of database version and renaming secret key alias.
trigger_db_upgrade(user_id: i32, is_ce: bool) -> Result<()>159 pub fn trigger_db_upgrade(user_id: i32, is_ce: bool) -> Result<()> {
160 let path = if is_ce {
161 format!("{}/{}/asset_service", CE_ROOT_PATH, user_id)
162 } else {
163 format!("{}/{}", DE_ROOT_PATH, user_id)
164 };
165 for entry in fs::read_dir(path)? {
166 let entry = entry?;
167 if entry.file_name().to_string_lossy().ends_with(DB_SUFFIX) {
168 if let Some(file_name_stem) = entry.file_name().to_string_lossy().strip_suffix(DB_SUFFIX) {
169 let _ = get_db(user_id, file_name_stem, is_ce)?;
170 }
171 }
172 }
173 Ok(())
174 }
175
construct_old_query_condition(info_map: &DbMap) -> Result<DbMap>176 fn construct_old_query_condition(info_map: &DbMap) -> Result<DbMap> {
177 let mut old_data_query_condition = DbMap::new();
178 let owner_info = info_map.get_bytes_attr(&column::OWNER)?;
179 old_data_query_condition.insert(column::OWNER, Value::Bytes(owner_info.clone()));
180 Ok(old_data_query_condition)
181 }
182
calculate_batch_split_times(old_data_query_condition: &DbMap, old_db: &mut Database) -> Result<u32>183 fn calculate_batch_split_times(old_data_query_condition: &DbMap, old_db: &mut Database) -> Result<u32> {
184 let query_times = (old_db.query_data_count(old_data_query_condition)? + MAX_BATCH_NUM - 1) / MAX_BATCH_NUM;
185 Ok(query_times)
186 }
187
migrate_data( old_db: &mut Database, new_db: &mut Database, split_time: u32, old_data_query_condition: &DbMap, ) -> Result<()>188 fn migrate_data(
189 old_db: &mut Database,
190 new_db: &mut Database,
191 split_time: u32,
192 old_data_query_condition: &DbMap,
193 ) -> Result<()> {
194 // 3.1 query data in old db
195 let query_options =
196 QueryOptions { offset: None, limit: Some(MAX_BATCH_NUM), order_by: None, order: None, amend: None };
197
198 let old_data_vec = old_db.query_datas(&vec![], old_data_query_condition, Some(&query_options), false)?;
199 // 3.2 insert data in new db
200 for data in &old_data_vec {
201 let mut condition = DbMap::new();
202 condition.insert(column::ALIAS, get_value_from_db_map(data, column::ALIAS)?);
203 condition.insert(column::OWNER, get_value_from_db_map(data, column::OWNER)?);
204 condition.insert(column::OWNER_TYPE, get_value_from_db_map(data, column::OWNER_TYPE)?);
205 let mut data_clone = data.clone();
206 data_clone.remove(column::ID);
207 new_db.replace_datas(&condition, false, &data_clone)?;
208 // 3.3 remove data in old db
209 old_db.delete_datas(&condition, None, false)?;
210 }
211 logi!("[INFO]Upgrade [{}] [{}]times", new_db.get_db_name(), split_time);
212 Ok(())
213 }
214
split_db(user_id: i32) -> Result<()>215 fn split_db(user_id: i32) -> Result<()> {
216 // 1. open old db
217 let mut old_db = get_db_before_split(user_id)?;
218
219 // 2. get split db info
220 let empty_condition = DbMap::new();
221 let owner_info_db_list =
222 old_db.query_datas(&vec![column::OWNER_TYPE, column::OWNER], &empty_condition, None, false)?;
223 for info_map in &owner_info_db_list {
224 // 1. get new db
225 let mut new_db = get_new_db(user_id, info_map)?;
226 // 2. batch insert data from old db to new db.
227 let old_data_query_condition = construct_old_query_condition(info_map)?;
228 for split_time in 0..calculate_batch_split_times(&old_data_query_condition, &mut old_db)? {
229 migrate_data(&mut old_db, &mut new_db, split_time, &old_data_query_condition)?;
230 }
231 logi!("[INFO]Upgrade [{}] success!", new_db.get_db_name());
232 }
233 logi!("[INFO]Upgrade all db success!");
234 remove_old_db(user_id)?;
235 Ok(())
236 }
237
238 /// check db need split or not. If needed, split it by owner.
check_and_split_db(user_id: i32) -> Result<()>239 pub fn check_and_split_db(user_id: i32) -> Result<()> {
240 let mut ret: bool = false;
241 if check_old_db_exist(user_id) {
242 let _lock = get_split_db_lock_by_user_id(user_id).mtx.lock().unwrap();
243 if check_old_db_exist(user_id) {
244 logi!("[INFO]Start splitting db.");
245 split_db(user_id)?;
246 ret = create_upgrade_file(user_id, OriginVersion::V1).is_ok();
247 }
248 }
249 let folder_name = fmt_file_dir(user_id);
250 let folder = Path::new(&folder_name);
251 let mut db_version = DB_UPGRADE_VERSION;
252 let mut is_clone_app_exist: bool = false;
253 if !is_upgrade_file_exist(user_id) {
254 if let Ok(entries) = fs::read_dir(folder) {
255 let mut entry_version = String::new();
256 for path in entries {
257 let entry = match path {
258 Ok(e) => e.path().to_string_lossy().into_owned(),
259 Err(_) => continue,
260 };
261 if entry.contains("_1.db") && !is_hap_special(&entry) {
262 is_clone_app_exist = true;
263 }
264 if entry.contains("_0.db") {
265 entry_version = entry.clone();
266 }
267 }
268
269 if !entry_version.is_empty() && entry_version.contains(DB_SUFFIX) {
270 let index = entry_version.rfind('/').unwrap();
271 let index_last = entry_version.rfind(DB_SUFFIX).unwrap();
272 if index_last > index + 1 {
273 let db_name = &entry_version[index + 1..index_last];
274 db_version = get_specific_db_version(user_id, db_name, fmt_de_db_path_with_name(user_id, db_name))?;
275 }
276 }
277 }
278 }
279
280 if !ret && !is_upgrade_file_exist(user_id) && db_version == DB_UPGRADE_VERSION_V4 {
281 if !is_clone_app_exist {
282 ret = create_upgrade_file(user_id, OriginVersion::V1).is_ok();
283 } else {
284 ret = create_upgrade_file(user_id, OriginVersion::V2).is_ok();
285 }
286 }
287 if !ret && !is_upgrade_file_exist(user_id) {
288 let _ = create_upgrade_file(user_id, OriginVersion::V3);
289 }
290
291 Ok(())
292 }
293
294 #[inline(always)]
fmt_file_path(user_id: i32) -> String295 fn fmt_file_path(user_id: i32) -> String {
296 format!("{}/{}/upgrade.cache", DE_ROOT_PATH, user_id)
297 }
298
299 #[inline(always)]
fmt_file_dir(user_id: i32) -> String300 fn fmt_file_dir(user_id: i32) -> String {
301 format!("{}/{}", DE_ROOT_PATH, user_id)
302 }
303
304 /// Check if hap is a special hap.
is_hap_special(info: &str) -> bool305 pub fn is_hap_special(info: &str) -> bool {
306 info.contains("com.alipay.mobile.client")
307 }
308
309 /// Function to create an upgrade file.
create_upgrade_file(user_id: i32, origin_version: OriginVersion) -> Result<()>310 pub fn create_upgrade_file(user_id: i32, origin_version: OriginVersion) -> Result<()> {
311 let _lock = GLOBAL_FILE_LOCK.lock().unwrap();
312 let path_str = fmt_file_path(user_id);
313 let file_path = Path::new(&path_str);
314 let mut file = match OpenOptions::new().write(true).create(true).open(file_path) {
315 Ok(file) => file,
316 Err(_) => {
317 return log_throw_error!(ErrCode::FileOperationError, "Create file failed.");
318 },
319 };
320 let upgrade_list = create_upgrade_list_inner(user_id, &origin_version);
321 let content = UpgradeData { version: origin_version as u32, upgrade_list };
322 to_writer(&content, &mut file)
323 .map_err(|_| AssetError { code: ErrCode::FileOperationError, msg: "Write file failed.".to_owned() })
324 }
325
326 /// Check if upgrade file exists.
is_upgrade_file_exist(user_id: i32) -> bool327 pub fn is_upgrade_file_exist(user_id: i32) -> bool {
328 let _lock = GLOBAL_FILE_LOCK.lock().unwrap();
329 let file_name = fmt_file_path(user_id);
330 let file_path = Path::new(&file_name);
331 file_path.exists()
332 }
333
334 /// To get original version and the list of haps to be upgraded.
get_file_content(user_id: i32) -> Result<UpgradeData>335 pub fn get_file_content(user_id: i32) -> Result<UpgradeData> {
336 let _lock = GLOBAL_FILE_LOCK.lock().unwrap();
337 let path = fmt_file_path(user_id);
338 let file = File::open(path)?;
339 match from_reader(file) {
340 Ok(content) => Ok(content),
341 Err(_) => log_throw_error!(ErrCode::FileOperationError, "Get content from upgrade file failed."),
342 }
343 }
344
345 /// To get original version.
get_upgrade_version(user_id: i32) -> Result<OriginVersion>346 pub fn get_upgrade_version(user_id: i32) -> Result<OriginVersion> {
347 match get_file_content(user_id)?.version {
348 version if version == OriginVersion::V1 as u32 => Ok(OriginVersion::V1),
349 version if version == OriginVersion::V2 as u32 => Ok(OriginVersion::V2),
350 version if version == OriginVersion::V3 as u32 => Ok(OriginVersion::V3),
351 _ => Err(AssetError { code: ErrCode::FileOperationError, msg: "Get upgrade version failed.".to_owned() }),
352 }
353 }
354
355 /// To get the list of haps to be upgraded.
get_upgrade_list(user_id: i32) -> Result<Vec<String>>356 pub fn get_upgrade_list(user_id: i32) -> Result<Vec<String>> {
357 Ok(get_file_content(user_id)?.upgrade_list)
358 }
359
is_file_hap(path: &Path) -> bool360 fn is_file_hap(path: &Path) -> bool {
361 path.file_name()
362 .and_then(|os_str| os_str.to_str())
363 .map_or(false, |name| name.starts_with("Hap_") && name.ends_with("_0.db"))
364 }
365
create_upgrade_list_inner(user_id: i32, version: &OriginVersion) -> Vec<String>366 fn create_upgrade_list_inner(user_id: i32, version: &OriginVersion) -> Vec<String> {
367 if user_id == 0 || version == &OriginVersion::V2 {
368 return Vec::new();
369 }
370 let folder_name = fmt_file_dir(user_id);
371 let folder = Path::new(&folder_name);
372
373 let entries = match fs::read_dir(folder) {
374 Ok(entries) => entries,
375 Err(_) => return Vec::new(),
376 };
377
378 let mut upgrade_list = Vec::new();
379 for entry in entries {
380 let path = match entry {
381 Ok(e) => e.path(),
382 Err(_) => continue,
383 };
384 if !is_file_hap(&path) {
385 continue;
386 }
387 let file_name = path.to_string_lossy().into_owned();
388 let index_first = match file_name.find("Hap_") {
389 Some(index) => index,
390 None => continue,
391 };
392
393 let index_last = match file_name.rfind('_') {
394 Some(index) => index,
395 None => continue,
396 };
397 if index_first >= index_last {
398 continue;
399 }
400 let owner = file_name[(index_first + TRUNCATE_LEN)..index_last].to_owned();
401 upgrade_list.push(owner);
402 }
403 upgrade_list
404 }
405
406 /// Update the list of haps to be upgraded.
update_upgrade_list(user_id: i32, remove_file: &String) -> Result<()>407 pub fn update_upgrade_list(user_id: i32, remove_file: &String) -> Result<()> {
408 let content = get_file_content(user_id)?;
409 let mut upgrade_list = content.upgrade_list;
410 let _lock = GLOBAL_FILE_LOCK.lock().unwrap();
411 upgrade_list.retain(|x| x != remove_file);
412 let path_str = fmt_file_path(user_id);
413 let file_path = Path::new(&path_str);
414 let mut file = match OpenOptions::new().write(true).create(true).truncate(true).open(file_path) {
415 Ok(file) => file,
416 Err(_) => {
417 return log_throw_error!(ErrCode::FileOperationError, "Create file failed.");
418 },
419 };
420 let content = UpgradeData { version: content.version as u32, upgrade_list };
421 to_writer(&content, &mut file)
422 .map_err(|_| AssetError { code: ErrCode::FileOperationError, msg: "Write file failed.".to_owned() })
423 }