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