• 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::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 }