• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 #define LOG_TAG "BackupManager"
16 
17 #include "backup_manager.h"
18 
19 #include "kvdb_service_client.h"
20 #include "log_print.h"
21 #include "task_executor.h"
22 namespace OHOS::DistributedKv {
23 namespace {
24 constexpr const char *BACKUP_POSTFIX = ".bak";
25 constexpr const int BACKUP_POSTFIX_SIZE = 4;
26 constexpr const char *BACKUP_TMP_POSTFIX = ".bk";
27 constexpr const int BACKUP_TMP_POSTFIX_SIZE = 3;
28 constexpr const char *BACKUP_KEY_POSTFIX = ".key";
29 constexpr const char *BACKUP_KEY_PREFIX = "Prefix_backup_";
30 constexpr const char *AUTO_BACKUP_NAME = "autoBackup";
31 constexpr const char *BACKUP_TOP_PATH = "/kvdb/backup";
32 constexpr const char *KEY_PATH = "/key";
33 } // namespace
34 
GetInstance()35 BackupManager &BackupManager::GetInstance()
36 {
37     static BackupManager instance;
38     return instance;
39 }
40 
BackupManager()41 BackupManager::BackupManager()
42 {
43 }
44 
~BackupManager()45 BackupManager::~BackupManager()
46 {
47 }
48 
Init(const std::string & baseDir)49 void BackupManager::Init(const std::string &baseDir)
50 {
51     TaskExecutor::Task task = [this, baseDir]() {
52         auto topPath = baseDir + BACKUP_TOP_PATH;
53         auto keyPath = baseDir + KEY_PATH;
54         auto storeIds = StoreUtil::GetSubPath(topPath);
55         auto keyFiles = StoreUtil::GetFiles(keyPath);
56         for (auto &storeId : storeIds) {
57             if (storeId == "." || storeId == "..") {
58                 continue;
59             }
60             auto backupPath = topPath + "/" + storeId;
61             auto backupFiles = StoreUtil::GetFiles(backupPath);
62             if (HaveResidueFile(backupFiles) || HaveResidueKey(keyFiles, storeId)) {
63                 auto ResidueInfo = BuildResidueInfo(backupFiles, keyFiles, storeId);
64                 ClearResidueFile(ResidueInfo, baseDir, storeId);
65             }
66         }
67     };
68     TaskExecutor::GetInstance().Execute(std::move(task));
69 }
70 
Prepare(const std::string & path,const std::string & storeId)71 void BackupManager::Prepare(const std::string &path, const std::string &storeId)
72 {
73     std::string topPath = path + BACKUP_TOP_PATH;
74     std::string storePath = topPath + "/" + storeId;
75     std::string autoBackupName = storePath + "/" + AUTO_BACKUP_NAME + BACKUP_POSTFIX;
76     (void)StoreUtil::InitPath(topPath);
77     (void)StoreUtil::InitPath(storePath);
78     (void)StoreUtil::CreateFile(autoBackupName);
79 }
80 
KeepData(const std::string & name,bool isCreated)81 void BackupManager::KeepData(const std::string &name, bool isCreated)
82 {
83     auto tmpName = name + BACKUP_TMP_POSTFIX;
84     if (isCreated) {
85         StoreUtil::CreateFile(tmpName);
86     } else {
87         StoreUtil::Rename(name, tmpName);
88     }
89 }
90 
RollBackData(const std::string & name,bool isCreated)91 void BackupManager::RollBackData(const std::string &name, bool isCreated)
92 {
93     auto tmpName = name + BACKUP_TMP_POSTFIX;
94     if (isCreated) {
95         StoreUtil::Remove(name);
96         StoreUtil::Remove(tmpName);
97     } else {
98         StoreUtil::Remove(name);
99         StoreUtil::Rename(tmpName, name);
100     }
101 }
102 
CleanTmpData(const std::string & name)103 void BackupManager::CleanTmpData(const std::string &name)
104 {
105     auto tmpName = name + BACKUP_TMP_POSTFIX;
106     StoreUtil::Remove(tmpName);
107 }
108 
Backup(const BackupInfo & info,std::shared_ptr<DBStore> dbStore)109 Status BackupManager::Backup(const BackupInfo &info, std::shared_ptr<DBStore> dbStore)
110 {
111     if (dbStore == nullptr) {
112         return ALREADY_CLOSED;
113     }
114     if (info.isCheckIntegrity) {
115         auto integrityStatus = dbStore->CheckIntegrity();
116         if (integrityStatus != DistributedDB::DBStatus::OK) {
117             return StoreUtil::ConvertStatus(integrityStatus);
118         }
119     }
120     if (info.name.size() == 0 || info.baseDir.size() == 0 || info.storeId.size() == 0 ||
121         info.name == AUTO_BACKUP_NAME) {
122         return INVALID_ARGUMENT;
123     }
124     std::string topPath = info.baseDir + BACKUP_TOP_PATH;
125     std::string storePath = topPath + "/" + info.storeId;
126     std::string backupFullName = storePath + "/" + info.name + BACKUP_POSTFIX;
127     std::string keyName = BACKUP_KEY_PREFIX + info.storeId + "_" + info.name;
128     std::string keyFullName = info.baseDir + KEY_PATH + "/" + keyName + BACKUP_KEY_POSTFIX;
129 
130     bool isCreate = !StoreUtil::IsFileExist(backupFullName);
131     if ((StoreUtil::GetFiles(storePath).size() >= MAX_BACKUP_NUM) && isCreate) {
132         return ERROR;
133     }
134     (void)StoreUtil::InitPath(topPath);
135     (void)StoreUtil::InitPath(storePath);
136     KeepData(backupFullName, isCreate);
137     auto dbPassword = SecurityManager::GetInstance().GetDBPassword(info.storeId, info.baseDir);
138     if (dbPassword.IsValid()) {
139         KeepData(keyFullName, isCreate);
140     }
141 
142     auto dbStatus = dbStore->Export(backupFullName, dbPassword.password);
143     auto status = StoreUtil::ConvertStatus(dbStatus);
144     if (status == SUCCESS) {
145         if (dbPassword.IsValid()) {
146             SecurityManager::GetInstance().SaveDBPassword(keyName, info.baseDir, dbPassword.password);
147             CleanTmpData(keyFullName);
148         }
149         CleanTmpData(backupFullName);
150     } else {
151         RollBackData(backupFullName, isCreate);
152         if (dbPassword.IsValid()) {
153             RollBackData(keyFullName, isCreate);
154         }
155     }
156     StoreUtil::Flush();
157     return status;
158 }
159 
GetBackupFileInfo(const std::string & name,const std::string & baseDir,const std::string & storeId)160 StoreUtil::FileInfo BackupManager::GetBackupFileInfo(
161     const std::string &name, const std::string &baseDir, const std::string &storeId)
162 {
163     StoreUtil::FileInfo backupFile;
164     std::string path = baseDir + BACKUP_TOP_PATH + "/" + storeId;
165     std::string backupName = name + BACKUP_POSTFIX;
166 
167     auto files = StoreUtil::GetFiles(path);
168     time_t modifyTime = 0;
169     for (auto &file : files) {
170         if (file.name == backupName) {
171             backupFile = std::move(file);
172             break;
173         }
174         if (name.empty() && (file.modifyTime > modifyTime) && (file.size != 0)) {
175             modifyTime = file.modifyTime;
176             backupFile = std::move(file);
177         }
178     }
179     return backupFile;
180 }
181 
Restore(const BackupInfo & info,std::shared_ptr<DBStore> dbStore)182 Status BackupManager::Restore(const BackupInfo &info, std::shared_ptr<DBStore> dbStore)
183 {
184     if (dbStore == nullptr) {
185         return ALREADY_CLOSED;
186     }
187     if (info.storeId.size() == 0 || info.baseDir.size() == 0) {
188         return INVALID_ARGUMENT;
189     }
190     auto backupFile = GetBackupFileInfo(info.name, info.baseDir, info.storeId);
191     if (backupFile.name.size() == 0) {
192         return INVALID_ARGUMENT;
193     }
194     auto fullName = info.baseDir + BACKUP_TOP_PATH + "/" + info.storeId + "/" + backupFile.name;
195     auto password = GetRestorePassword(backupFile.name, info).password;
196     auto dbStatus = dbStore->Import(fullName, password, info.isCheckIntegrity);
197     if (dbStatus == DistributedDB::DBStatus::INVALID_FILE && info.encrypt) {
198         ZLOGI("Use the key from server to restore");
199         auto retryStatus = ImportWithSecretKeyFromService(info, dbStore, fullName, info.isCheckIntegrity);
200         return retryStatus == SUCCESS ? SUCCESS : CRYPT_ERROR;
201     }
202     return StoreUtil::ConvertStatus(dbStatus);
203 }
204 
GetRestorePassword(const std::string & name,const BackupInfo & info)205 BackupManager::DBPassword BackupManager::GetRestorePassword(const std::string &name, const BackupInfo &info)
206 {
207     auto backupName = name.substr(0, name.length() - BACKUP_POSTFIX_SIZE);
208     auto keyName = BACKUP_KEY_PREFIX + info.storeId + "_" + backupName;
209     DBPassword dbPassword;
210     if (backupName == AUTO_BACKUP_NAME) {
211         auto service = KVDBServiceClient::GetInstance();
212         if (service == nullptr) {
213             return dbPassword;
214         }
215         std::vector<std::vector<uint8_t>> pwds;
216         service->GetBackupPassword({ info.appId }, { info.storeId }, info.subUser, pwds,
217             KVDBService::PasswordType::BACKUP_SECRET_KEY);
218         if (pwds.size() != 0) {
219             // When obtaining the key for automatic backup, there is only one element in the list
220             dbPassword.SetValue(pwds[0].data(), pwds[0].size());
221         }
222         for (auto &pwd : pwds) {
223             pwd.assign(pwd.size(), 0);
224         }
225     } else {
226         dbPassword = SecurityManager::GetInstance().GetDBPassword(keyName, info.baseDir);
227     }
228     return dbPassword;
229 }
230 
GetSecretKeyFromService(const AppId & appId,const StoreId & storeId,std::vector<std::vector<uint8_t>> & keys,int32_t subUser)231 Status BackupManager::GetSecretKeyFromService(const AppId &appId, const StoreId &storeId,
232     std::vector<std::vector<uint8_t>> &keys, int32_t subUser)
233 {
234     auto service = KVDBServiceClient::GetInstance();
235     if (service == nullptr) {
236         ZLOGE("Get service failed! appId:%{public}s, storeId:%{public}s",
237             appId.appId.c_str(), StoreUtil::Anonymous(storeId.storeId).c_str());
238         return Status::SERVER_UNAVAILABLE;
239     }
240     auto status = service->GetBackupPassword(appId, storeId, subUser, keys, KVDBService::PasswordType::SECRET_KEY);
241     if (status != Status::SUCCESS) {
242         ZLOGE("Get password from service failed! status:%{public}d, appId:%{public}s storeId:%{public}s",
243             status, appId.appId.c_str(), StoreUtil::Anonymous(storeId.storeId).c_str());
244         return status;
245     }
246     if (keys.empty()) {
247         ZLOGE("Service secret key is empty! status:%{public}d, appId:%{public}s storeId:%{public}s",
248             status, appId.appId.c_str(), StoreUtil::Anonymous(storeId.storeId).c_str());
249         return Status::ERROR;
250     }
251     return Status::SUCCESS;
252 }
253 
ImportWithSecretKeyFromService(const BackupInfo & info,std::shared_ptr<DBStore> dbStore,std::string & fullName,bool isCheckIntegrity)254 Status BackupManager::ImportWithSecretKeyFromService(const BackupInfo &info, std::shared_ptr<DBStore> dbStore,
255     std::string &fullName, bool isCheckIntegrity)
256 {
257     Status status = NOT_FOUND;
258     std::vector<std::vector<uint8_t>> keys;
259     if (GetSecretKeyFromService({ info.appId }, { info.storeId }, keys, info.subUser) != Status::SUCCESS) {
260         for (auto &key : keys) {
261             key.assign(key.size(), 0);
262         }
263         return status;
264     }
265     for (auto &key : keys) {
266         SecurityManager::DBPassword dbPassword;
267         dbPassword.SetValue(key.data(), key.size());
268         auto dbStatus = dbStore->Import(fullName, dbPassword.password, isCheckIntegrity);
269         status = StoreUtil::ConvertStatus(dbStatus);
270         if (status == SUCCESS) {
271             ZLOGI("Import with secretKey from service success!");
272             break;
273         }
274     }
275     for (auto &key : keys) {
276         key.assign(key.size(), 0);
277     }
278     return status;
279 }
280 
DeleteBackup(std::map<std::string,Status> & deleteList,const std::string & baseDir,const std::string & storeId)281 Status BackupManager::DeleteBackup(
282     std::map<std::string, Status> &deleteList, const std::string &baseDir, const std::string &storeId)
283 {
284     if (deleteList.empty() || baseDir.size() == 0 || storeId.size() == 0) {
285         return INVALID_ARGUMENT;
286     }
287 
288     std::string path = baseDir + BACKUP_TOP_PATH + "/" + storeId;
289     auto fileInfos = StoreUtil::GetFiles(path);
290     for (auto &info : fileInfos) {
291         auto it = deleteList.find(info.name.substr(0, info.name.length() - BACKUP_POSTFIX_SIZE));
292         if (it == deleteList.end()) {
293             continue;
294         }
295         auto backupName = info.name.substr(0, info.name.length() - BACKUP_POSTFIX_SIZE);
296         if (backupName == AUTO_BACKUP_NAME) {
297             it->second = INVALID_ARGUMENT;
298             continue;
299         }
300         std::string keyName = BACKUP_KEY_PREFIX + storeId + "_" + backupName;
301         SecurityManager::GetInstance().DelDBPassword(keyName, baseDir);
302         it->second = (StoreUtil::Remove(path + "/" + info.name)) ? SUCCESS : ERROR;
303     }
304     return SUCCESS;
305 }
306 
HaveResidueFile(const std::vector<StoreUtil::FileInfo> & files)307 bool BackupManager::HaveResidueFile(const std::vector<StoreUtil::FileInfo> &files)
308 {
309     for (auto &file : files) {
310         if (IsEndWith(file.name, BACKUP_TMP_POSTFIX)) {
311             return true;
312         }
313     }
314     return false;
315 }
316 
HaveResidueKey(const std::vector<StoreUtil::FileInfo> & files,std::string storeId)317 bool BackupManager::HaveResidueKey(const std::vector<StoreUtil::FileInfo> &files, std::string storeId)
318 {
319     for (auto &file : files) {
320         auto prefix = BACKUP_KEY_PREFIX + storeId;
321         if (IsBeginWith(file.name, prefix) && IsEndWith(file.name, BACKUP_TMP_POSTFIX)) {
322             return true;
323         }
324     }
325     return false;
326 }
327 
GetBackupName(const std::string & fileName)328 std::string BackupManager::GetBackupName(const std::string &fileName)
329 {
330     int postFixLen = IsEndWith(fileName, BACKUP_TMP_POSTFIX) ? BACKUP_POSTFIX_SIZE + BACKUP_TMP_POSTFIX_SIZE
331                                                              : BACKUP_POSTFIX_SIZE;
332     return fileName.substr(0, fileName.length() - postFixLen);
333 }
334 
SetResidueInfo(BackupManager::ResidueInfo & residueInfo,const std::vector<StoreUtil::FileInfo> & files,const std::string & name,const std::string & postFix)335 void BackupManager::SetResidueInfo(BackupManager::ResidueInfo &residueInfo,
336     const std::vector<StoreUtil::FileInfo> &files, const std::string &name, const std::string &postFix)
337 {
338     for (const auto &file : files) {
339         auto fullName = name + postFix;
340         auto fullTmpName = fullName + BACKUP_TMP_POSTFIX;
341         if ((file.name == fullTmpName) && (postFix == BACKUP_POSTFIX)) {
342             residueInfo.hasTmpBackup = true;
343             residueInfo.tmpBackupSize = file.size;
344         }
345         if ((file.name == fullName) && (postFix == BACKUP_POSTFIX)) {
346             residueInfo.hasRawBackup = true;
347         }
348         if ((file.name == fullTmpName) && (postFix == BACKUP_KEY_POSTFIX)) {
349             residueInfo.hasTmpKey = true;
350             residueInfo.tmpKeySize = file.size;
351         }
352         if ((file.name == fullName) && (postFix == BACKUP_KEY_POSTFIX)) {
353             residueInfo.hasRawKey = true;
354         }
355     }
356 }
357 
BuildResidueInfo(const std::vector<StoreUtil::FileInfo> & files,const std::vector<StoreUtil::FileInfo> & keys,const std::string & storeId)358 std::map<std::string, BackupManager::ResidueInfo> BackupManager::BuildResidueInfo(
359     const std::vector<StoreUtil::FileInfo> &files, const std::vector<StoreUtil::FileInfo> &keys,
360     const std::string &storeId)
361 {
362     std::map<std::string, ResidueInfo> residueInfoList;
363     for (auto &file : files) {
364         auto backupName = GetBackupName(file.name);
365         if (backupName == AUTO_BACKUP_NAME) {
366             continue;
367         }
368         auto it = residueInfoList.find(backupName);
369         if (it == residueInfoList.end()) {
370             ResidueInfo residueInfo = { 0, 0, false, false, false, false };
371             SetResidueInfo(residueInfo, files, backupName, BACKUP_POSTFIX);
372             SetResidueInfo(residueInfo, keys, BACKUP_KEY_PREFIX + storeId + "_" + backupName, BACKUP_KEY_POSTFIX);
373             residueInfoList.emplace(backupName, residueInfo);
374         }
375     }
376     return residueInfoList;
377 }
378 
379 /**
380  *  in function NeedRollBack, use the number of tmp and raw file to charge who to do when start,
381  *  learning by watching blow table,
382  *  we can konw when the num of tmp file greater than or equal raw, interrupt happend druing backup
383  *
384  *  backup step (encrypt)   file status                         option          file num
385  *  1, backup old data      -               storeId.key         rollback data   raw = 1
386  *                          storeId.bak.bk  -                                   tmp = 1
387  *
388  *  2, backup old key       -               -                   rollback        raw = 0
389  *                          storeId.bak.bk, storeId.key.bk                      tmp = 2
390  *
391  *  3, do backup            storeId.bak     -                   rollback        raw = 1
392  *                          storeId.bak.bk, storeId.key.bk                      tmp = 2
393  *
394  *  4, store key            storeId.bak     storeId.key         rollback        raw = 2
395  *                          storeId.bak.bk, storeId.key.bk                      tmp = 2
396  *
397  *  5, delet tmp key        storeId.bak     storeId.key         clean data      raw = 2
398  *                          storeId.bak.bk  -                                   tmp = 1
399  *
400  *  6, delet tmp data       storeId.bak     storeId.key         do nothing      raw = 2
401  *                          -               -                                   tmp = 0
402  *
403  *  if step3 has failed, do as 7 ~ 8
404  *
405  *  7, rollback  data       storeId.bak     -                   rollback key    raw = 1
406  *                          -               storeId.key.bk                      tmp = 1
407  *
408  *  8, rollback  data       storeId.bak     storeId.key         do nothing      raw = 2
409  *                          -               -                                   tmp = 0
410  *
411  *  backup step (unencrypt) file status                         option          file num
412  *  1, backup old data      -                                   rollback data   raw = 0
413  *                          storeId.bak.bk  -                                   tmp = 1
414  *
415  *  2, do backup            storeId.bak     -                   rollback data   raw = 1
416  *                          storeId.bak.bk, -                                   tmp = 1
417  *
418  *  6, delet tmp data       storeId.bak     -                   do nothing      raw = 1
419  *                          -               -                                   tmp = 0
420  *
421  * */
GetClearType(const BackupManager::ResidueInfo & residueInfo)422 BackupManager::ClearType BackupManager::GetClearType(const BackupManager::ResidueInfo &residueInfo)
423 {
424     int rawFile = 0;
425     int tmpFile = 0;
426     if (residueInfo.hasRawBackup) {
427         rawFile++;
428     }
429     if (residueInfo.hasRawKey) {
430         rawFile++;
431     }
432     if (residueInfo.hasTmpBackup) {
433         tmpFile++;
434     }
435     if (residueInfo.hasTmpKey) {
436         tmpFile++;
437     }
438     if (tmpFile == 0) {
439         return DO_NOTHING;
440     }
441     if ((tmpFile >= rawFile) && (tmpFile == 1) && residueInfo.hasTmpBackup) {
442         return ROLLBACK_DATA;
443     }
444     if ((tmpFile >= rawFile) && (tmpFile == 1) && residueInfo.hasTmpKey) {
445         return ROLLBACK_KEY;
446     }
447     return (tmpFile >= rawFile) ? ROLLBACK : CLEAN_TMP;
448 }
449 
ClearResidueFile(std::map<std::string,ResidueInfo> residueInfo,const std::string & baseDir,const std::string & storeId)450 void BackupManager::ClearResidueFile(
451     std::map<std::string, ResidueInfo> residueInfo, const std::string &baseDir, const std::string &storeId)
452 {
453     for (auto &info : residueInfo) {
454         auto backupFullName = baseDir + BACKUP_TOP_PATH + "/" + storeId + "/" + info.first + BACKUP_POSTFIX;
455         auto keyFullName =
456             baseDir + KEY_PATH + "/" + BACKUP_KEY_PREFIX + storeId + "_" + info.first + BACKUP_KEY_POSTFIX;
457         switch (GetClearType(info.second)) {
458             case ROLLBACK_DATA:
459                 RollBackData(backupFullName, (info.second.tmpBackupSize == 0));
460                 break;
461             case ROLLBACK_KEY:
462                 RollBackData(keyFullName, (info.second.tmpKeySize == 0));
463                 break;
464             case ROLLBACK:
465                 RollBackData(backupFullName, (info.second.tmpBackupSize == 0));
466                 RollBackData(keyFullName, (info.second.tmpKeySize == 0));
467                 break;
468             case CLEAN_TMP:
469                 CleanTmpData(backupFullName);
470                 CleanTmpData(keyFullName);
471                 break;
472             case DO_NOTHING:
473             default:
474                 break;
475         }
476     }
477 }
478 
IsEndWith(const std::string & fullString,const std::string & end)479 bool BackupManager::IsEndWith(const std::string &fullString, const std::string &end)
480 {
481     if (fullString.length() >= end.length()) {
482         return (fullString.compare(fullString.length() - end.length(), end.length(), end) == 0);
483     } else {
484         return false;
485     }
486 }
487 
IsBeginWith(const std::string & fullString,const std::string & begin)488 bool BackupManager::IsBeginWith(const std::string &fullString, const std::string &begin)
489 {
490     if (fullString.length() >= begin.length()) {
491         return (fullString.compare(0, begin.length(), begin) == 0);
492     } else {
493         return false;
494     }
495 }
496 } // namespace OHOS::DistributedKv
497