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> ×)
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