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