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, path::Path};
20
21 use asset_common::OwnerType;
22 use asset_definition::{log_throw_error, ErrCode, Extension, Result, Value};
23 use asset_file_operator::common::DB_SUFFIX;
24 use asset_log::logi;
25
26 use crate::{
27 database::{
28 fmt_backup_path, fmt_de_db_path_with_name, get_db, get_normal_db, get_split_db_lock_by_user_id, Database,
29 CE_ROOT_PATH, DE_ROOT_PATH, OLD_DB_NAME,
30 },
31 types::{column, DbMap, QueryOptions, DB_UPGRADE_VERSION_V3},
32 };
33
34 const MINIM_OWNER_INFO_LEN: usize = 3;
35 const REMOVE_INDEX: usize = 2;
36 static MAX_BATCH_NUM: u32 = 100;
37
38 #[inline(always)]
fmt_old_de_db_path(user_id: i32) -> String39 pub(crate) fn fmt_old_de_db_path(user_id: i32) -> String {
40 format!("{}/{}/asset.db", DE_ROOT_PATH, user_id)
41 }
42
check_old_db_exist(user_id: i32) -> bool43 fn check_old_db_exist(user_id: i32) -> bool {
44 let path_str = fmt_old_de_db_path(user_id);
45 let path = Path::new(&path_str);
46 path.exists()
47 }
48
49 /// Use owner_type and owner_info construct db name.
construct_splited_db_name(owner_type: OwnerType, owner_info: &[u8], is_ce: bool) -> Result<String>50 pub fn construct_splited_db_name(owner_type: OwnerType, owner_info: &[u8], is_ce: bool) -> Result<String> {
51 let mut res: String = match owner_type {
52 OwnerType::Hap => {
53 let owner_info_string = String::from_utf8_lossy(owner_info).to_string();
54 let split_owner_info: Vec<&str> = owner_info_string.split('_').collect();
55 if split_owner_info.len() < MINIM_OWNER_INFO_LEN || split_owner_info.last().is_none() {
56 return log_throw_error!(ErrCode::DatabaseError, "[FATAL]The queried owner info is not correct.");
57 }
58 let app_index = split_owner_info.last().unwrap();
59 let mut split_owner_info_mut = split_owner_info.clone();
60 for _ in 0..REMOVE_INDEX {
61 split_owner_info_mut.pop();
62 }
63 let owner_info = split_owner_info_mut.join("_").clone();
64 format!("Hap_{}_{}", owner_info, app_index)
65 },
66 OwnerType::Native => {
67 format!("Native_{}", String::from_utf8_lossy(owner_info))
68 },
69 };
70 if is_ce {
71 res = format!("enc_{}", res)
72 }
73 Ok(res)
74 }
75
get_db_before_split(user_id: i32) -> Result<Database>76 fn get_db_before_split(user_id: i32) -> Result<Database> {
77 get_db(user_id, OLD_DB_NAME, DB_UPGRADE_VERSION_V3, None)
78 }
79
get_value_from_db_map(db_map: &DbMap, key: &str) -> Result<Value>80 fn get_value_from_db_map(db_map: &DbMap, key: &str) -> Result<Value> {
81 match db_map.get(key) {
82 Some(value) => Ok(value.clone()),
83 _ => log_throw_error!(ErrCode::DatabaseError, "[FATAL]Get value from {} failed.", key),
84 }
85 }
86
remove_old_db(user_id: i32) -> Result<()>87 fn remove_old_db(user_id: i32) -> Result<()> {
88 let mut remove_db_files = vec![];
89 let path = fmt_de_db_path_with_name(user_id, OLD_DB_NAME);
90 remove_db_files.push(path.clone());
91 remove_db_files.push(fmt_backup_path(path.as_str()));
92 for file_path in &remove_db_files {
93 fs::remove_file(file_path)?;
94 }
95 Ok(())
96 }
97
get_new_db(user_id: i32, info_map: &DbMap) -> Result<Database>98 fn get_new_db(user_id: i32, info_map: &DbMap) -> Result<Database> {
99 // 1.1 construct db name
100 let owner_type = OwnerType::try_from(info_map.get_num_attr(&column::OWNER_TYPE)?.to_owned())?;
101 let owner_info = info_map.get_bytes_attr(&column::OWNER)?;
102 let new_db_name = construct_splited_db_name(owner_type, owner_info, false)?;
103 // 1.2 construct new db
104 get_db(user_id, &new_db_name, DB_UPGRADE_VERSION_V3, None)
105 }
106
107 /// Trigger upgrade of database version and renaming secret key alias.
trigger_db_upgrade(user_id: i32, is_ce: bool) -> Result<()>108 pub fn trigger_db_upgrade(user_id: i32, is_ce: bool) -> Result<()> {
109 let path = if is_ce {
110 format!("{}/{}/asset_service", CE_ROOT_PATH, user_id)
111 } else {
112 format!("{}/{}", DE_ROOT_PATH, user_id)
113 };
114 for entry in fs::read_dir(path)? {
115 let entry = entry?;
116 if entry.file_name().to_string_lossy().ends_with(DB_SUFFIX) {
117 if let Some(file_name_stem) = entry.file_name().to_string_lossy().strip_suffix(DB_SUFFIX) {
118 let _ = get_normal_db(user_id, file_name_stem, is_ce)?;
119 }
120 }
121 }
122 Ok(())
123 }
124
construct_old_query_condition(info_map: &DbMap) -> Result<DbMap>125 fn construct_old_query_condition(info_map: &DbMap) -> Result<DbMap> {
126 let mut old_data_query_condition = DbMap::new();
127 let owner_info = info_map.get_bytes_attr(&column::OWNER)?;
128 old_data_query_condition.insert(column::OWNER, Value::Bytes(owner_info.clone()));
129 Ok(old_data_query_condition)
130 }
131
calculate_batch_split_times(old_data_query_condition: &DbMap, old_db: &mut Database) -> Result<u32>132 fn calculate_batch_split_times(old_data_query_condition: &DbMap, old_db: &mut Database) -> Result<u32> {
133 let query_times = (old_db.query_data_count(old_data_query_condition)? + MAX_BATCH_NUM - 1) / MAX_BATCH_NUM;
134 Ok(query_times)
135 }
136
migrate_data( old_db: &mut Database, new_db: &mut Database, split_time: u32, old_data_query_condition: &DbMap, ) -> Result<()>137 fn migrate_data(
138 old_db: &mut Database,
139 new_db: &mut Database,
140 split_time: u32,
141 old_data_query_condition: &DbMap,
142 ) -> Result<()> {
143 // 3.1 query data in old db
144 let query_options = QueryOptions { offset: None, limit: Some(MAX_BATCH_NUM), order_by: None, order: None };
145
146 let old_data_vec = old_db.query_datas(&vec![], old_data_query_condition, Some(&query_options), false)?;
147 // 3.2 insert data in new db
148 for data in &old_data_vec {
149 let mut condition = DbMap::new();
150 condition.insert(column::ALIAS, get_value_from_db_map(data, column::ALIAS)?);
151 condition.insert(column::OWNER, get_value_from_db_map(data, column::OWNER)?);
152 condition.insert(column::OWNER_TYPE, get_value_from_db_map(data, column::OWNER_TYPE)?);
153 let mut data_clone = data.clone();
154 data_clone.remove(column::ID);
155 new_db.replace_datas(&condition, false, &data_clone)?;
156 // 3.3 remove data in old db
157 old_db.delete_datas(&condition, None, false)?;
158 }
159 logi!("[INFO]Upgrade [{}] [{}]times", new_db.get_db_name(), split_time);
160 Ok(())
161 }
162
split_db(user_id: i32) -> Result<()>163 fn split_db(user_id: i32) -> Result<()> {
164 // 1. open old db
165 let mut old_db = get_db_before_split(user_id)?;
166
167 // 2. get split db info
168 let empty_condition = DbMap::new();
169 let owner_info_db_list =
170 old_db.query_datas(&vec![column::OWNER_TYPE, column::OWNER], &empty_condition, None, false)?;
171 for info_map in &owner_info_db_list {
172 // 1. get new db
173 let mut new_db = get_new_db(user_id, info_map)?;
174 // 2. batch insert data from old db to new db.
175 let old_data_query_condition = construct_old_query_condition(info_map)?;
176 for split_time in 0..calculate_batch_split_times(&old_data_query_condition, &mut old_db)? {
177 migrate_data(&mut old_db, &mut new_db, split_time, &old_data_query_condition)?;
178 }
179 logi!("[INFO]Upgrade [{}] success!", new_db.get_db_name());
180 }
181 logi!("[INFO]Upgrade all db success!");
182 remove_old_db(user_id)?;
183 Ok(())
184 }
185
186 /// check db need split or not. If needed, split it by owner.
check_and_split_db(user_id: i32) -> Result<()>187 pub fn check_and_split_db(user_id: i32) -> Result<()> {
188 if check_old_db_exist(user_id) {
189 let _lock = get_split_db_lock_by_user_id(user_id).mtx.lock().unwrap();
190 if check_old_db_exist(user_id) {
191 logi!("[INFO]Start splitting db.");
192 split_db(user_id)?;
193 }
194 }
195 trigger_db_upgrade(user_id, false)?;
196 Ok(())
197 }
198