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