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