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