1 /*
2  * Copyright (c) 2024 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 
16 #include "tools_op_incremental_restore.h"
17 
18 #include <cerrno>
19 #include <condition_variable>
20 #include <cstdint>
21 #include <cstring>
22 #include <fcntl.h>
23 #include <memory>
24 #include <mutex>
25 #include <regex>
26 #include <set>
27 #include <string>
28 #include <sys/sendfile.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <tuple>
32 #include <unistd.h>
33 #include <vector>
34 
35 #include "b_error/b_error.h"
36 #include "b_error/b_excep_utils.h"
37 #include "b_filesystem/b_dir.h"
38 #include "b_filesystem/b_file.h"
39 #include "b_json/b_json_entity_caps.h"
40 #include "b_json/b_json_entity_ext_manage.h"
41 #include "b_resources/b_constants.h"
42 #include "backup_kit_inner.h"
43 #include "base/hiviewdfx/hitrace/interfaces/native/innerkits/include/hitrace_meter/hitrace_meter.h"
44 #include "errors.h"
45 #include "service_proxy.h"
46 #include "tools_op.h"
47 
48 namespace OHOS::FileManagement::Backup {
49 using namespace std;
50 
51 class SessionRestore {
52 public:
UpdateBundleSendFiles(const BundleName & bundleName,const string & fileName)53     void UpdateBundleSendFiles(const BundleName &bundleName, const string &fileName)
54     {
55         lock_guard<mutex> lk(lock_);
56         bundleStatusMap_[bundleName].sendFile.insert(fileName);
57     }
58 
ClearBundleOfMap(const BundleName & bundleName)59     void ClearBundleOfMap(const BundleName &bundleName)
60     {
61         lock_guard<mutex> lk(lock_);
62         bundleStatusMap_.erase(bundleName);
63     }
64 
TryNotify(bool flag=false)65     void TryNotify(bool flag = false)
66     {
67         if (flag == true) {
68             ready_ = true;
69             cv_.notify_all();
70         } else if (bundleStatusMap_.size() == 0 && cnt_ == 0 && isAllBundelsFinished.load()) {
71             ready_ = true;
72             cv_.notify_all();
73         }
74     }
75 
UpdateBundleFinishedCount()76     void UpdateBundleFinishedCount()
77     {
78         lock_guard<mutex> lk(lock_);
79         cnt_--;
80     }
81 
SetBundleFinishedCount(uint32_t cnt)82     void SetBundleFinishedCount(uint32_t cnt)
83     {
84         cnt_ = cnt;
85     }
86 
Wait()87     void Wait()
88     {
89         unique_lock<mutex> lk(lock_);
90         cv_.wait(lk, [&] { return ready_; });
91     }
92 
93     unique_ptr<BIncrementalRestoreSession> session_ = {};
94 
95 private:
96     struct BundleStatus {
97         set<string> sendFile;
98     };
99 
100     map<string, BundleStatus> bundleStatusMap_;
101     mutable condition_variable cv_;
102     mutex lock_;
103     bool ready_ = false;
104     uint32_t cnt_ {0};
105 
106 public:
107     std::atomic<bool> isAllBundelsFinished {false};
108     mutex fileCountLock_;
109     vector<BIncrementalData> lastIncrementalData {};
110     map<string, int> fileCount_;
111     map<string, int> fileNums_;
112 };
113 
GenHelpMsg()114 static string GenHelpMsg()
115 {
116     return "\t\tThis operation helps to restore application data.\n"
117            "\t\t--pathCapFile\t\t This parameter should be the path of the capability file.\n"
118            "\t\t--bundle\t\t This parameter is bundleName.";
119 }
120 
OnFileReady(shared_ptr<SessionRestore> ctx,const BFileInfo & fileInfo,UniqueFd fd,UniqueFd manifestFd,int32_t errCode)121 static void OnFileReady(shared_ptr<SessionRestore> ctx, const BFileInfo &fileInfo, UniqueFd fd, UniqueFd manifestFd,
122     int32_t errCode)
123 {
124     printf("FileReady owner = %s, fileName = %s, sn = %u, fd = %d\n", fileInfo.owner.c_str(), fileInfo.fileName.c_str(),
125            fileInfo.sn, fd.Get());
126     if (fileInfo.fileName.find('/') != string::npos) {
127         throw BError(BError::Codes::TOOL_INVAL_ARG, "Filename is not valid");
128     }
129     auto iter = find_if(ctx->lastIncrementalData.begin(), ctx->lastIncrementalData.end(),
130                         [bundleName {fileInfo.owner}](const auto &data) { return bundleName == data.bundleName; });
131     if (iter == ctx->lastIncrementalData.end()) {
132         throw BError(BError::Codes::TOOL_INVAL_ARG);
133     }
134     // 待恢复文件 路径拼接方式: /data/backup/incrementalreceived/bundleName/时间戳/incremental/文件名
135     string tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + fileInfo.owner + "/" +
136                      to_string(iter->lastIncrementalTime) + string(BConstants::BACKUP_TOOL_INCREMENTAL) + "/" +
137                      fileInfo.fileName;
138     if (access(tmpPath.data(), F_OK) != 0) {
139         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
140     }
141     UniqueFd fdLocal(open(tmpPath.data(), O_RDONLY));
142     if (fdLocal < 0) {
143         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
144     }
145     BFile::SendFile(fd, fdLocal);
146     // manifest文件 路径拼接方式: /data/backup/incrementalreceived/bundleName/时间戳/manifest/文件名.rp
147     tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + fileInfo.owner + "/" +
148               to_string(iter->lastIncrementalTime) + string(BConstants::BACKUP_TOOL_MANIFEST) + "/" +
149               fileInfo.fileName + ".rp";
150     if (access(tmpPath.data(), F_OK) == 0) {
151         UniqueFd fdManifest(open(tmpPath.data(), O_RDONLY));
152         if (fdManifest < 0) {
153             throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
154         }
155         BFile::SendFile(manifestFd, fdManifest);
156     }
157     std::string bundleName = fileInfo.owner;
158     {
159         unique_lock<mutex> fileLock(ctx->fileCountLock_);
160         ++ctx->fileCount_[bundleName];
161         // 文件准备完成
162         printf("FileReady count/num = %d/%d\n", ctx->fileCount_[bundleName], ctx->fileNums_[bundleName]);
163         if (ctx->fileCount_[bundleName] == ctx->fileNums_[bundleName]) {
164             printf("PublishFile start.\n");
165             BFileInfo fileInfoTemp = fileInfo;
166             fileInfoTemp.fileName = "";
167             int ret = ctx->session_->PublishFile(fileInfoTemp);
168             if (ret != 0) {
169                 throw BError(BError::Codes::TOOL_INVAL_ARG, "PublishFile error");
170             }
171         }
172     }
173     ctx->TryNotify();
174 }
175 
OnBundleStarted(shared_ptr<SessionRestore> ctx,ErrCode err,const BundleName name)176 static void OnBundleStarted(shared_ptr<SessionRestore> ctx, ErrCode err, const BundleName name)
177 {
178     printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str());
179     if (err != 0) {
180         ctx->UpdateBundleFinishedCount();
181         ctx->isAllBundelsFinished.store(true);
182         ctx->ClearBundleOfMap(name);
183         ctx->TryNotify();
184     }
185 }
186 
OnBundleFinished(shared_ptr<SessionRestore> ctx,ErrCode err,const BundleName name)187 static void OnBundleFinished(shared_ptr<SessionRestore> ctx, ErrCode err, const BundleName name)
188 {
189     printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
190     ctx->UpdateBundleFinishedCount();
191     if (err != 0) {
192         ctx->isAllBundelsFinished.store(true);
193     }
194     ctx->ClearBundleOfMap(name);
195     ctx->TryNotify();
196 }
197 
OnAllBundlesFinished(shared_ptr<SessionRestore> ctx,ErrCode err)198 static void OnAllBundlesFinished(shared_ptr<SessionRestore> ctx, ErrCode err)
199 {
200     ctx->isAllBundelsFinished.store(true);
201     if (err == 0) {
202         printf("all bundles restore finished end\n");
203     } else {
204         printf("Failed to Unplanned Abort error: %d\n", err);
205         ctx->TryNotify(true);
206         return;
207     }
208     ctx->TryNotify();
209 }
210 
OnBackupServiceDied(shared_ptr<SessionRestore> ctx)211 static void OnBackupServiceDied(shared_ptr<SessionRestore> ctx)
212 {
213     printf("backupServiceDied\n");
214     ctx->TryNotify(true);
215 }
216 
OnResultReport(shared_ptr<SessionRestore> ctx,const std::string & bundleName,const std::string & resultInfo)217 static void OnResultReport(shared_ptr<SessionRestore> ctx, const std::string &bundleName, const std::string &resultInfo)
218 {
219     printf("OnResultReport bundleName = %s, resultInfo = %s\n", bundleName.c_str(), resultInfo.c_str());
220 }
221 
OnProcess(shared_ptr<SessionRestore> ctx,const std::string bundleName,const std::string processInfo)222 static void OnProcess(shared_ptr<SessionRestore> ctx, const std::string bundleName, const std::string processInfo)
223 {
224     printf("OnProcess bundleName = %s, processInfo = %s\n", bundleName.c_str(), processInfo.c_str());
225 }
226 
RestoreApp(shared_ptr<SessionRestore> restore)227 static void RestoreApp(shared_ptr<SessionRestore> restore)
228 {
229     StartTrace(HITRACE_TAG_FILEMANAGEMENT, "RestoreApp");
230     if (!restore || !restore->session_) {
231         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
232     }
233     for (auto &data : restore->lastIncrementalData) {
234         string path = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + data.bundleName + "/" +
235                       to_string(data.lastIncrementalTime) + string(BConstants::BACKUP_TOOL_INCREMENTAL);
236         if (access(path.data(), F_OK) != 0) {
237             HILOGE("bundleName tar does not exist, file %{public}s  errno : %{public}d",
238                 path.c_str(), errno);
239             continue;
240         }
241         const auto [err, filePaths] = BDir::GetDirFiles(path);
242         if (err != 0) {
243             throw BError(BError::Codes::TOOL_INVAL_ARG, "error path");
244         }
245         for (auto &filePath : filePaths) {
246             string fileName = filePath.substr(filePath.rfind("/") + 1);
247             restore->session_->GetFileHandle(data.bundleName, fileName);
248             restore->UpdateBundleSendFiles(data.bundleName, fileName);
249         }
250     }
251     FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
252 }
253 
GetRealPath(string & path)254 static bool GetRealPath(string &path)
255 {
256     string absPath = BExcepUltils::Canonicalize(path);
257     if (access(absPath.data(), F_OK) != 0) {
258         return false;
259     }
260     return true;
261 }
262 
InitRestoreSession(shared_ptr<SessionRestore> ctx,const vector<BundleName> & bundleNames,vector<string> & times)263 static int32_t InitRestoreSession(shared_ptr<SessionRestore> ctx,
264                                   const vector<BundleName> &bundleNames,
265                                   vector<string> &times)
266 {
267     if (bundleNames.size() != times.size()) {
268         fprintf(stderr, "Inconsistent amounts of bundles and incrementalTime!\n");
269         return -EPERM;
270     }
271     if (!ctx) {
272         return -EPERM;
273     }
274     ctx->session_ = BIncrementalRestoreSession::Init(BIncrementalRestoreSession::Callbacks {
275         .onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2, placeholders::_3, placeholders::_4),
276         .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
277         .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
278         .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
279         .onResultReport = bind(OnResultReport, ctx, placeholders::_1, placeholders::_2),
280         .onBackupServiceDied = bind(OnBackupServiceDied, ctx),
281         .onProcess = bind(OnProcess, ctx, placeholders::_1, placeholders::_2)});
282     if (ctx->session_ == nullptr) {
283         printf("Failed to init restore\n");
284         FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
285         return -EPERM;
286     }
287 
288     int num = 0;
289     for (const auto &bundleName : bundleNames) {
290         BIncrementalData data;
291         data.bundleName = bundleName;
292         data.lastIncrementalTime = atoi(times[num].c_str());
293         ctx->lastIncrementalData.push_back(data);
294         num++;
295     }
296     return 0;
297 }
298 
Init(const string & pathCapFile,vector<string> bundleNames,bool depMode,vector<string> times)299 static int32_t Init(const string &pathCapFile, vector<string> bundleNames, bool depMode, vector<string> times)
300 {
301     StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Init");
302     string realPath = pathCapFile;
303     if (!GetRealPath(realPath)) {
304         fprintf(stderr, "path to realpath error");
305         return -errno;
306     }
307 
308     UniqueFd fd(open(realPath.data(), O_RDWR, S_IRWXU));
309     auto ctx = make_shared<SessionRestore>();
310     size_t len = bundleNames.size();
311     for (size_t i = 0; i < len; ++i) {
312         ctx->fileNums_[bundleNames[i]] = ToolsOp::GetFIleNums(bundleNames[i], false);
313     }
314     int32_t ret = InitRestoreSession(ctx, bundleNames, times);
315     if (ret != 0) {
316         printf("Failed to init restore session error:%d\n", ret);
317         return ret;
318     }
319 
320     if (depMode) {
321         for (auto &bundleName : bundleNames) {
322             UniqueFd fileFd(open(realPath.data(), O_RDWR, S_IRWXU));
323             if (fileFd < 0) {
324                 fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
325                 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
326                 return -errno;
327             }
328             int result = ctx->session_->AppendBundles(move(fileFd), {bundleName});
329             if (result != 0) {
330                 printf("restore append bundles error: %d\n", result);
331                 return -result;
332             }
333         }
334     } else {
335         ret = ctx->session_->AppendBundles(move(fd), bundleNames);
336         if (ret != 0) {
337             printf("restore append bundles error: %d\n", ret);
338             return -ret;
339         }
340     }
341     ctx->SetBundleFinishedCount(bundleNames.size());
342     RestoreApp(ctx);
343     ctx->Wait();
344     ctx->session_->Release();
345     return 0;
346 }
347 
g_exec(map<string,vector<string>> & mapArgToVal)348 static int g_exec(map<string, vector<string>> &mapArgToVal)
349 {
350     bool depMode = false;
351     if (mapArgToVal.find("depMode") != mapArgToVal.end()) {
352         string strFlag = *(mapArgToVal["depMode"].begin());
353         depMode = (strFlag == "true");
354     }
355 
356     if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() ||
357         mapArgToVal.find("incrementalTime") == mapArgToVal.end()) {
358         return -EPERM;
359     }
360     return Init(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"], depMode, mapArgToVal["incrementalTime"]);
361 }
362 
IncrementalRestoreRegister()363 bool IncrementalRestoreRegister()
364 {
365     return ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
366         .opName = {"incrementalrestore"},
367         .argList = {{
368                         .paramName = "pathCapFile",
369                         .repeatable = false,
370                     },
371                     {
372                         .paramName = "bundles",
373                         .repeatable = true,
374                     },
375                     {
376                         .paramName = "depMode",
377                         .repeatable = false,
378                     },
379                     {
380                         .paramName = "incrementalTime",
381                         .repeatable = true,
382                     }},
383         .funcGenHelpMsg = GenHelpMsg,
384         .funcExec = g_exec,
385     }});
386 }
387 } // namespace OHOS::FileManagement::Backup