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 #include "backup_manager.h"
17 
18 #include <unistd.h>
19 
20 #include <fstream>
21 #include <iostream>
22 
23 #include "backuprule/backup_rule_manager.h"
24 #include "crypto_manager.h"
25 #include "device_manager_adapter.h"
26 #include "directory/directory_manager.h"
27 #include "log_print.h"
28 #include "metadata/meta_data_manager.h"
29 #include "types.h"
30 namespace OHOS::DistributedData {
31 namespace {
32 constexpr const int COPY_SIZE = 1024;
33 constexpr const int MICROSEC_TO_SEC = 1000;
34 constexpr const char *AUTO_BACKUP_NAME = "autoBackup.bak";
35 constexpr const char *BACKUP_BK_POSTFIX = ".bk";
36 constexpr const char *BACKUP_TMP_POSTFIX = ".tmp";
37 }
38 
BackupManager()39 BackupManager::BackupManager()
40 {
41 }
42 
~BackupManager()43 BackupManager::~BackupManager()
44 {
45 }
46 
GetInstance()47 BackupManager &BackupManager::GetInstance()
48 {
49     static BackupManager instance;
50     return instance;
51 }
52 
Init()53 void BackupManager::Init()
54 {
55     std::vector<StoreMetaData> metas;
56     MetaDataManager::GetInstance().LoadMeta(
57         StoreMetaData::GetPrefix({ DeviceManagerAdapter::GetInstance().GetLocalDevice().uuid }), metas, true);
58     for (auto &meta : metas) {
59         if (!meta.isBackup || meta.isDirty) {
60             continue;
61         }
62         auto backupPath = DirectoryManager::GetInstance().GetStoreBackupPath(meta) + "/" + AUTO_BACKUP_NAME;
63         switch (GetClearType(meta)) {
64             case ROLLBACK:
65                 RollBackData(backupPath);
66                 break;
67             case CLEAN_DATA:
68                 CleanData(backupPath);
69                 break;
70             case DO_NOTHING:
71             default:
72                 break;
73         }
74     }
75 }
76 
SetBackupParam(const BackupParam & backupParam)77 void BackupManager::SetBackupParam(const BackupParam &backupParam)
78 {
79     schedularDelay_ = backupParam.schedularDelay;
80     schedularInternal_ = backupParam.schedularInternal;
81     backupInternal_ = backupParam.backupInternal;
82     backupNumber_ = backupParam.backupNumber;
83 }
84 
RegisterExporter(int32_t type,Exporter exporter)85 void BackupManager::RegisterExporter(int32_t type, Exporter exporter)
86 {
87     if (exporters_[type] == nullptr) {
88         exporters_[type] = exporter;
89     } else {
90         ZLOGI("Auto backup exporter has registed, type:%{public}d.", type);
91     }
92 }
93 
BackSchedule(std::shared_ptr<ExecutorPool> executors)94 void BackupManager::BackSchedule(std::shared_ptr<ExecutorPool> executors)
95 {
96     if (!executors_) {
97         executors_ = std::move(executors);
98     }
99     std::chrono::duration<int> delay(schedularDelay_);
100     std::chrono::duration<int> internal(schedularInternal_);
101     ZLOGI("BackupManager Schedule start.");
102     executors_->Schedule(
103         [this]() {
104             if (!CanBackup()) {
105                 return;
106             }
107 
108             ZLOGI("start automatic backup.");
109             std::vector<StoreMetaData> metas;
110             MetaDataManager::GetInstance().LoadMeta(
111                 StoreMetaData::GetPrefix({ DeviceManagerAdapter::GetInstance().GetLocalDevice().uuid }), metas, true);
112 
113             int64_t end = std::min(startNum_ + backupNumber_, static_cast<int64_t>(metas.size()));
114             for (int64_t i = startNum_; i < end; startNum_++, i++) {
115                 auto &meta = metas[i];
116                 if (!meta.isBackup || meta.isDirty) {
117                     continue;
118                 }
119                 DoBackup(meta);
120             }
121             if (startNum_ >= static_cast<int64_t>(metas.size())) {
122                 startNum_ = 0;
123             }
124             sync();
125             backupSuccessTime_ = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
126         },
127         delay, internal);
128 }
129 
DoBackup(const StoreMetaData & meta)130 void BackupManager::DoBackup(const StoreMetaData &meta)
131 {
132     bool result = false;
133     auto key = meta.GetSecretKey();
134     auto backupKey = meta.GetBackupSecretKey();
135     std::vector<uint8_t> decryptKey;
136     SecretKeyMetaData secretKey;
137     if (MetaDataManager::GetInstance().LoadMeta(key, secretKey, true)) {
138         CryptoManager::GetInstance().Decrypt(secretKey.sKey, decryptKey);
139     }
140     auto backupPath = DirectoryManager::GetInstance().GetStoreBackupPath(meta);
141     std::string backupFullPath = backupPath + "/" + AUTO_BACKUP_NAME;
142 
143     KeepData(backupFullPath);
144     if (exporters_[meta.storeType] != nullptr) {
145         exporters_[meta.storeType](meta, backupFullPath + BACKUP_TMP_POSTFIX, result);
146     }
147     if (result) {
148         SaveData(backupFullPath, backupKey, secretKey);
149     } else {
150         CleanData(backupFullPath);
151     }
152     decryptKey.assign(decryptKey.size(), 0);
153 }
154 
CanBackup()155 bool BackupManager::CanBackup()
156 {
157     if (!BackupRuleManager::GetInstance().CanBackup()) {
158         return false;
159     }
160     int64_t currentTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
161     if (currentTime - backupSuccessTime_ < backupInternal_ * MICROSEC_TO_SEC && backupSuccessTime_ > 0) {
162         ZLOGE("no more than backup internal time since the last backup success.");
163         return false;
164     }
165     return true;
166 }
167 
KeepData(const std::string & path)168 void BackupManager::KeepData(const std::string &path)
169 {
170     auto backupPath = path + BACKUP_BK_POSTFIX;
171     CopyFile(path, backupPath, true);
172 }
173 
SaveData(const std::string & path,const std::string & key,const SecretKeyMetaData & secretKey)174 void BackupManager::SaveData(const std::string &path, const std::string &key, const SecretKeyMetaData &secretKey)
175 {
176     auto tmpPath = path + BACKUP_TMP_POSTFIX;
177     auto backupPath = path + BACKUP_BK_POSTFIX;
178     CopyFile(tmpPath, path);
179     RemoveFile(tmpPath.c_str());
180     if (secretKey.sKey.size() != 0) {
181         MetaDataManager::GetInstance().SaveMeta(key, secretKey, true);
182     }
183     RemoveFile(backupPath.c_str());
184 }
185 
RollBackData(const std::string & path)186 void BackupManager::RollBackData(const std::string &path)
187 {
188     auto tmpPath = path + BACKUP_TMP_POSTFIX;
189     auto backupPath = path + BACKUP_BK_POSTFIX;
190     CopyFile(backupPath, path);
191     RemoveFile(tmpPath.c_str());
192     RemoveFile(backupPath.c_str());
193 }
194 
CleanData(const std::string & path)195 void BackupManager::CleanData(const std::string &path)
196 {
197     auto backupPath = path + BACKUP_BK_POSTFIX;
198     auto tmpPath = path + BACKUP_TMP_POSTFIX;
199     RemoveFile(tmpPath.c_str());
200     RemoveFile(backupPath.c_str());
201 }
202 
203 /**
204  *  learning by watching blow table, we can konw :
205  *  as encrypt db, when backup's password same as db's password, need clean data,
206  *  others if .bk file or .tmp file exist, rollback data
207  *  as unencrypt db, tmp file exist, rollback, tmp file not exist, but .bk file exist, clean data
208  *
209  *  backup step (encrypt)   file status             key in meat         option          file num
210  *  1, backup old data      autoBachup.bak          autoBachup.key      rollback        .bk = 1
211  *                          autoBachup.bak.bk                                           .tmp = 0
212  *
213  *  2, do backup            autoBachup.bak          autoBachup.key      rollback        .bk = 1
214  *                          autoBachup.bak.bk                                           .tmp = 1
215  *                          autoBachup.bak.tmp
216  *
217  *  3, copy data            autoBachup.bak(new)     autoBachup.key      rollback        .bk = 1
218  *                          autoBachup.bak.bk                                           .tmp = 1
219  *                          autoBachup.bak.tmp
220  *
221  *  4, delet tmp data       autoBachup.bak(new)     autoBachup.key      rollback        .bk = 1
222  *                          autoBachup.bak.bk                                           .tmp = 0
223  *
224  *  5, save key             autoBachup.bak(new)     autoBachup.key(new) clean data      .bk = 1
225  *                          autoBachup.bak.bk                                           .tmp = 0
226  *
227  *  6, delet backup data    autoBachup.bak          autoBachup.key      do nothing      .bk = 0
228  *                          -                       -                                   .tmp = 0
229  *
230  *  backup step (unencrypt) file status                     option                      file num
231  *  1, backup old data      autoBachup.bak                  clean data                  .bk = 1
232  *                          autoBachup.bak.bk                                           .tmp = 0
233  *
234  *  2, do backup            autoBachup.bak                  rollback data               .bk = 1
235  *                          autoBachup.bak.bk,                                          .tmp = 1
236  *                          autoBachup.bak.tmp
237  *
238  *  3, copy data            autoBachup.bak(new)             rollback data               .bk = 1
239  *                          autoBachup.bak.bk,                                          .tmp = 1
240  *                          autoBachup.bak.tmp
241  *
242  *  4, delet tmp data       autoBachup.bak                  clean data                  .bk = 1
243  *                          autoBachup.bak.bk                                           .tmp = 0
244  *
245  *
246  *  5, delet backup data    autoBachup.bak                  do nothing                  .bk = 0
247  *                                                                                      .tmp =0
248  * */
GetClearType(const StoreMetaData & meta)249 BackupManager::ClearType BackupManager::GetClearType(const StoreMetaData &meta)
250 {
251     auto backupFile = DirectoryManager::GetInstance().GetStoreBackupPath(meta) + "/" + AUTO_BACKUP_NAME;
252     auto dbKey = meta.GetSecretKey();
253     auto backupKey = meta.GetBackupSecretKey();
254     auto bkFile = backupFile + BACKUP_BK_POSTFIX;
255 
256     SecretKeyMetaData dbPassword;
257     if (MetaDataManager::GetInstance().LoadMeta(dbKey, dbPassword, true)) {
258         SecretKeyMetaData backupPassword;
259         MetaDataManager::GetInstance().LoadMeta(backupKey, backupPassword, true);
260         if (dbPassword.sKey != backupPassword.sKey && IsFileExist(bkFile)) {
261             return ROLLBACK;
262         }
263         if (dbPassword.sKey == backupPassword.sKey && IsFileExist(bkFile)) {
264             return CLEAN_DATA;
265         }
266     } else {
267         auto tmpFile = backupFile + BACKUP_TMP_POSTFIX;
268         if (IsFileExist(tmpFile)) {
269             return ROLLBACK;
270         }
271         if (!IsFileExist(tmpFile) && IsFileExist(bkFile)) {
272             return CLEAN_DATA;
273         }
274     }
275     return DO_NOTHING;
276 }
277 
CopyFile(const std::string & oldPath,const std::string & newPath,bool isCreate)278 void BackupManager::CopyFile(const std::string &oldPath, const std::string &newPath, bool isCreate)
279 {
280     std::fstream fin, fout;
281     if (!IsFileExist(oldPath)) {
282         return;
283     }
284     fin.open(oldPath, std::ios_base::in);
285     if (isCreate) {
286         fout.open(newPath, std::ios_base::out | std::ios_base::ate);
287     } else {
288         fout.open(newPath, std::ios_base::out | std::ios_base::trunc);
289     }
290     char buf[COPY_SIZE] = { 0 };
291     while (fin.good()) {
292         fin.read(buf, COPY_SIZE);
293         fout.write(buf, fin.gcount());
294     }
295     fin.close();
296     fout.close();
297 }
298 
GetPassWord(const StoreMetaData & meta,std::vector<uint8_t> & password)299 bool BackupManager::GetPassWord(const StoreMetaData &meta, std::vector<uint8_t> &password)
300 {
301     std::string key = meta.GetBackupSecretKey();
302     SecretKeyMetaData secretKey;
303     MetaDataManager::GetInstance().LoadMeta(key, secretKey, true);
304     return CryptoManager::GetInstance().Decrypt(secretKey.sKey, password);
305 }
306 
IsFileExist(const std::string & path)307 bool BackupManager::IsFileExist(const std::string &path)
308 {
309     if (path.empty()) {
310         return false;
311     }
312     if (access(path.c_str(), F_OK) != 0) {
313         return false;
314     }
315     return true;
316 }
317 
RemoveFile(const std::string & path)318 bool BackupManager::RemoveFile(const std::string &path)
319 {
320     if (access(path.c_str(), F_OK) != 0) {
321         return true;
322     }
323     if (remove(path.c_str()) != 0) {
324         ZLOGE("remove error:%{public}d, path:%{public}s", errno, path.c_str());
325         return false;
326     }
327     return true;
328 }
329 } // namespace OHOS::DistributedData