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