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