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 <cerrno>
17 #include <condition_variable>
18 #include <memory>
19 #include <mutex>
20 #include <regex>
21 #include <string>
22 #include <vector>
23
24 #include <fcntl.h>
25 #include <sys/sendfile.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28
29 #include "b_error/b_error.h"
30 #include "b_error/b_excep_utils.h"
31 #include "b_filesystem/b_dir.h"
32 #include "b_filesystem/b_file.h"
33 #include "b_json/b_json_entity_caps.h"
34 #include "b_json/b_json_entity_ext_manage.h"
35 #include "b_resources/b_constants.h"
36 #include "backup_kit_inner.h"
37 #include "directory_ex.h"
38 #include "errors.h"
39 #include "hitrace_meter.h"
40 #include "service_proxy.h"
41 #include "tools_op.h"
42 #include "tools_op_incremental_restore_async.h"
43
44 namespace OHOS::FileManagement::Backup {
45 using namespace std;
46
47 class InrementalSessionAsync {
48 public:
TryNotify(bool flag=false)49 void TryNotify(bool flag = false)
50 {
51 if (flag == true) {
52 ready_ = true;
53 cv_.notify_all();
54 } else if (cnt_ == 0) {
55 ready_ = true;
56 cv_.notify_all();
57 }
58 }
59
Wait()60 void Wait()
61 {
62 unique_lock<mutex> lk(lock_);
63 cv_.wait(lk, [&] { return ready_; });
64 }
65
UpdateBundleFinishedCount()66 void UpdateBundleFinishedCount()
67 {
68 lock_guard<mutex> lk(lock_);
69 cnt_--;
70 }
71
SetBundleFinishedCount(uint32_t cnt)72 void SetBundleFinishedCount(uint32_t cnt)
73 {
74 cnt_ = cnt;
75 }
76
77 shared_ptr<BIncrementalSessionRestoreAsync> session_ = {};
78 map<string, int> fileCount_;
79 map<string, int> fileNums_;
80 mutex fileCountLock_;
81
82 private:
83 mutable condition_variable cv_;
84 mutex lock_;
85 bool ready_ = false;
86 uint32_t cnt_ {0};
87 };
88
GenHelpMsg()89 static string GenHelpMsg()
90 {
91 return "\tThis operation helps to restore application data.\n"
92 "\t\t--pathCapFile\t\t This parameter should be the path of the capability file.\n"
93 "\t\t--bundle\t\t This parameter is bundleName.\n"
94 "\t\t--userId\t\t This parameter is userId.\n"
95 "\t\t--restoreType\t\t The parameter is a bool variable. true Simulates the upgrade service scenario; false "
96 "simulates the application recovery scenario.\n";
97 }
98
OnFileReady(shared_ptr<InrementalSessionAsync> ctx,const BFileInfo & fileInfo,UniqueFd fd,UniqueFd rpFd)99 static void OnFileReady(shared_ptr<InrementalSessionAsync> ctx, const BFileInfo &fileInfo, UniqueFd fd, UniqueFd rpFd)
100 {
101 printf("FileReady owner = %s, fileName = %s, sn = %u, fd = %d\n", fileInfo.owner.c_str(), fileInfo.fileName.c_str(),
102 fileInfo.sn, fd.Get());
103 string formatFileName = fileInfo.fileName;
104 // Only manage.json was in data/backup/receive;
105 // Other flles and tars was in data/backup/receive + bundle + path is manage.json
106 if (formatFileName.rfind(string(BConstants::EXT_BACKUP_MANAGE)) != string::npos) {
107 formatFileName = string(BConstants::EXT_BACKUP_MANAGE);
108 }
109 printf(" OnFileReady formatFileName = %s\n", formatFileName.c_str());
110
111 // data path: /data/backup/incrementalreceived/bundleName/{timestamp}/incremental/{filename}
112 // For Special clone, timestamp is always 0
113 string tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + fileInfo.owner + "/0" +
114 string(BConstants::BACKUP_TOOL_INCREMENTAL) + "/" + formatFileName;
115 printf(" OnFileReady tmpPath = %s \n", tmpPath.c_str());
116 if (access(tmpPath.data(), F_OK) != 0) {
117 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
118 }
119
120 BExcepUltils::VerifyPath(tmpPath, false);
121 UniqueFd fdLocal(open(tmpPath.data(), O_RDONLY));
122 if (fdLocal < 0) {
123 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
124 }
125 BFile::SendFile(fd, fdLocal);
126 std::string bundleName = fileInfo.owner;
127 {
128 unique_lock<mutex> fileLock(ctx->fileCountLock_);
129 ++ctx->fileCount_[bundleName];
130 // 文件准备完成
131 printf("FileReady count/num = %d/%d\n", ctx->fileCount_[bundleName], ctx->fileNums_[bundleName]);
132 if (ctx->fileCount_[bundleName] == ctx->fileNums_[bundleName]) {
133 printf("PublishFile start.\n");
134 BFileInfo fileInfoTemp = fileInfo;
135 fileInfoTemp.fileName = "";
136 int ret = ctx->session_->PublishFile(fileInfoTemp);
137 if (ret != 0) {
138 throw BError(BError::Codes::TOOL_INVAL_ARG, "PublishFile error");
139 }
140 }
141 }
142 }
143
OnBundleStarted(shared_ptr<InrementalSessionAsync> ctx,ErrCode err,const BundleName name)144 static void OnBundleStarted(shared_ptr<InrementalSessionAsync> ctx, ErrCode err, const BundleName name)
145 {
146 printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str());
147 if (err != 0) {
148 ctx->UpdateBundleFinishedCount();
149 ctx->TryNotify();
150 }
151 }
152
OnBundleFinished(shared_ptr<InrementalSessionAsync> ctx,ErrCode err,const BundleName name)153 static void OnBundleFinished(shared_ptr<InrementalSessionAsync> ctx, ErrCode err, const BundleName name)
154 {
155 printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
156 if (err != 0) {
157 ctx->UpdateBundleFinishedCount();
158 ctx->TryNotify();
159 }
160 }
161
OnAllBundlesFinished(shared_ptr<InrementalSessionAsync> ctx,ErrCode err)162 static void OnAllBundlesFinished(shared_ptr<InrementalSessionAsync> ctx, ErrCode err)
163 {
164 printf("all bundles finished end\n");
165 ctx->TryNotify(true);
166 }
167
OnBackupServiceDied(shared_ptr<InrementalSessionAsync> ctx)168 static void OnBackupServiceDied(shared_ptr<InrementalSessionAsync> ctx)
169 {
170 printf("backupServiceDied\n");
171 ctx->TryNotify(true);
172 }
173
ReadyExtManage(const string & path,std::vector<ExtManageInfo> & pkgInfo)174 static map<string, tuple<string, struct stat, bool, bool>> ReadyExtManage(const string &path,
175 std::vector<ExtManageInfo>& pkgInfo)
176 {
177 map<string, tuple<string, struct stat, bool, bool>> info;
178 for (auto &item : pkgInfo) {
179 string fileName = item.hashName;
180 string filePath = item.fileName;
181 if (fileName.empty() || filePath.empty()) {
182 continue;
183 }
184
185 // Rename big file with real name in backup/receive directory
186 // To support file with different path but same name, create directories in /data/backup/receive
187 string realNameInReceive = path + BConstants::FILE_SEPARATOR_CHAR + filePath;
188 string realPathInReceive = realNameInReceive.substr(0, realNameInReceive.rfind("/"));
189 string currentNameInReceive = path + BConstants::FILE_SEPARATOR_CHAR + fileName;
190 if (access(realPathInReceive.c_str(), F_OK) != 0) {
191 if (!ForceCreateDirectory(realPathInReceive.data())) {
192 printf("err create directory %d %s\n", errno, strerror(errno));
193 }
194 }
195 if (rename(currentNameInReceive.data(), realNameInReceive.data()) != 0) {
196 printf("err rename %d %s\n", errno, strerror(errno));
197 }
198
199 // update fileName with filePath according to clone optimize
200 item.hashName = filePath;
201 if (item.hashName.front() == BConstants::FILE_SEPARATOR_CHAR) {
202 item.hashName = item.hashName.substr(1);
203 }
204
205 // update filePath
206 if (!item.isBigFile && !item.isUserTar) {
207 item.fileName = "/";
208 } else {
209 item.fileName = "";
210 }
211 info.emplace(item.hashName, make_tuple(item.fileName, item.sta, item.isBigFile, item.isUserTar));
212 }
213 return info;
214 }
215
AdapteCloneOptimize(const string & path)216 static void AdapteCloneOptimize(const string &path)
217 {
218 string manageJsonStr = path;
219 manageJsonStr = manageJsonStr + BConstants::FILE_SEPARATOR_CHAR + string(BConstants::EXT_BACKUP_MANAGE);
220 UniqueFd fd(open(manageJsonStr.data(), O_RDWR));
221 if (fd < 0) {
222 HILOGE("Failed to open manage json file = %{private}s, err = %{public}d", manageJsonStr.c_str(), errno);
223 return;
224 }
225 BJsonCachedEntity<BJsonEntityExtManage> cachedEntityOld(std::move(fd));
226 auto cacheOld = cachedEntityOld.Structuralize();
227 auto pkgInfo = cacheOld.GetExtManageInfo();
228 close(cachedEntityOld.GetFd().Release());
229
230 auto info = ReadyExtManage(path, pkgInfo);
231 UniqueFd fdJson(open(manageJsonStr.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
232 if (fdJson < 0) {
233 HILOGE("Failed to open manage json file = %{private}s, err = %{public}d", manageJsonStr.c_str(), errno);
234 return;
235 }
236 BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(std::move(fdJson));
237 auto cache = cachedEntity.Structuralize();
238 cache.SetExtManageForClone(info);
239 cachedEntity.Persist();
240 close(cachedEntity.GetFd().Release());
241 }
242
OnResultReport(shared_ptr<InrementalSessionAsync> ctx,const std::string & bundleName,const std::string & resultInfo)243 static void OnResultReport(shared_ptr<InrementalSessionAsync> ctx, const std::string &bundleName,
244 const std::string &resultInfo)
245 {
246 printf("OnResultReport bundleName = %s, resultInfo = %s\n", bundleName.c_str(), resultInfo.c_str());
247 }
248
OnProcess(shared_ptr<Session> ctx,const std::string bundleName,const std::string processInfo)249 static void OnProcess(shared_ptr<Session> ctx, const std::string bundleName, const std::string processInfo)
250 {
251 printf("OnProcess bundleName = %s, processInfo = %s\n", bundleName.c_str(), processInfo.c_str());
252 }
253
RestoreApp(shared_ptr<InrementalSessionAsync> restore,vector<BundleName> & bundleNames)254 static void RestoreApp(shared_ptr<InrementalSessionAsync> restore, vector<BundleName> &bundleNames)
255 {
256 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "RestoreApp");
257 if (!restore || !restore->session_) {
258 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
259 }
260 for (auto &bundleName : bundleNames) {
261 if (bundleName.find('/') != string::npos) {
262 throw BError(BError::Codes::TOOL_INVAL_ARG, "bundleName is not valid");
263 }
264 string tmpPath = string(BConstants::BACKUP_TOOL_INCREMENTAL_RECEIVE_DIR) + bundleName;
265 if (access(tmpPath.data(), F_OK) != 0) {
266 HILOGE("bundleName tar does not exist, file %{public}s errno : %{public}d",
267 tmpPath.c_str(), errno);
268 continue;
269 }
270 // For Special clone, timestamp is always 0
271 tmpPath = tmpPath + "/0";
272 if (access(tmpPath.data(), F_OK) != 0 && mkdir(tmpPath.data(), S_IRWXU) != 0) {
273 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
274 }
275 // data path: /data/backup/incrementalreceived/bundleName/{timestamp}/incremental/{filename}
276 string tmpDataPath = tmpPath + string(BConstants::BACKUP_TOOL_INCREMENTAL);
277 if (access(tmpDataPath.data(), F_OK) != 0 && mkdir(tmpDataPath.data(), S_IRWXU) != 0) {
278 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
279 }
280
281 // update manage.json and fileName
282 AdapteCloneOptimize(tmpDataPath);
283
284 // manage.json first
285 string manageJsonPath = string(BConstants::PATH_BUNDLE_BACKUP_HOME).
286 append(BConstants::SA_BUNDLE_BACKUP_RESTORE).append(BConstants::EXT_BACKUP_MANAGE);
287 if (manageJsonPath.front() == BConstants::FILE_SEPARATOR_CHAR) {
288 manageJsonPath = manageJsonPath.substr(1);
289 }
290 restore->session_->GetFileHandle(bundleName, manageJsonPath);
291
292 string manageJsonStr = tmpDataPath + BConstants::FILE_SEPARATOR_CHAR + string(BConstants::EXT_BACKUP_MANAGE);
293 UniqueFd fd(open(manageJsonStr.data(), O_RDONLY));
294 if (fd < 0) {
295 HILOGE("Failed to open manage json file = %{private}s, err = %{public}d", manageJsonStr.c_str(), errno);
296 return;
297 }
298 BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(std::move(fd));
299 auto cache = cachedEntity.Structuralize();
300 auto pkgInfo = cache.GetExtManageInfo();
301 for (auto &item : pkgInfo) {
302 printf("New FileName %s\n", item.hashName.data());
303 restore->session_->GetFileHandle(bundleName, item.hashName);
304 }
305 }
306 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
307 }
308
ChangeBundleInfo(const string & pathCapFile,const vector<string> & bundleNames,const string & type)309 static int32_t ChangeBundleInfo(const string &pathCapFile, const vector<string> &bundleNames, const string &type)
310 {
311 BExcepUltils::VerifyPath(pathCapFile, false);
312 UniqueFd fd(open(pathCapFile.data(), O_RDWR, S_IRWXU));
313 if (fd < 0) {
314 fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
315 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
316 return -errno;
317 }
318 BJsonCachedEntity<BJsonEntityCaps> cachedEntity(move(fd));
319 auto cache = cachedEntity.Structuralize();
320 vector<BJsonEntityCaps::BundleInfo> bundleInfos;
321 auto cacheBundleInfos = cache.GetBundleInfos();
322 for (auto name : bundleNames) {
323 string versionName = string(BConstants::DEFAULT_VERSION_NAME);
324 int64_t versionCode = static_cast<int64_t>(BConstants::DEFAULT_VERSION_CODE);
325 if (type == "false") {
326 versionName = string(BConstants::DEFAULT_VERSION_NAME_CLONE);
327 versionCode = static_cast<int64_t>(BConstants::DEFAULT_VERSION_CODE);
328 }
329 for (auto &&bundleInfo : cacheBundleInfos) {
330 if (bundleInfo.name != name) {
331 continue;
332 }
333 bundleInfos.emplace_back(BJsonEntityCaps::BundleInfo {.name = name,
334 .versionCode = versionCode,
335 .versionName = versionName,
336 .spaceOccupied = bundleInfo.spaceOccupied,
337 .increSpaceOccupied = bundleInfo.increSpaceOccupied,
338 .allToBackup = bundleInfo.allToBackup,
339 .fullBackupOnly = bundleInfo.fullBackupOnly,
340 .extensionName = bundleInfo.extensionName});
341 }
342 }
343 cache.SetBundleInfos(bundleInfos);
344 cachedEntity.Persist();
345
346 return 0;
347 }
348
AppendBundles(shared_ptr<InrementalSessionAsync> restore,const string & pathCapFile,vector<string> bundleNames,const string & type,const string & userId)349 static int32_t AppendBundles(shared_ptr<InrementalSessionAsync> restore,
350 const string &pathCapFile,
351 vector<string> bundleNames,
352 const string &type,
353 const string &userId)
354 {
355 BExcepUltils::VerifyPath(pathCapFile, false);
356 UniqueFd fd(open(pathCapFile.data(), O_RDWR, S_IRWXU));
357 if (fd < 0) {
358 fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
359 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
360 return -errno;
361 }
362 RestoreTypeEnum restoreType = RestoreTypeEnum::RESTORE_DATA_WAIT_SEND;
363 if (type == "true") {
364 restoreType = RestoreTypeEnum::RESTORE_DATA_READDY;
365 }
366 try {
367 int ret = restore->session_->AppendBundles(move(fd), bundleNames, restoreType, atoi(userId.data()));
368 if (ret != 0) {
369 printf("restore append bundles error: %d\n", ret);
370 return -ret;
371 }
372 if (type == "false") {
373 RestoreApp(restore, bundleNames);
374 }
375 } catch (const BError &e) {
376 printf("restore append bundles error: %d\n", e.GetCode());
377 return -1;
378 } catch (...) {
379 printf("Unexpected exception");
380 return -1;
381 }
382 restore->Wait();
383 return 0;
384 }
385
InitArg(const string & pathCapFile,const vector<string> & bundleNames,const string & type,const string & userId)386 static int32_t InitArg(const string &pathCapFile,
387 const vector<string> &bundleNames,
388 const string &type,
389 const string &userId)
390 {
391 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Init");
392 BExcepUltils::VerifyPath(pathCapFile, false);
393
394 if (ChangeBundleInfo(pathCapFile, bundleNames, type)) {
395 fprintf(stderr, "ChangeBundleInfo error");
396 return -errno;
397 }
398
399 auto ctx = make_shared<InrementalSessionAsync>();
400 size_t len = bundleNames.size();
401 for (size_t i = 0; i < len; ++i) {
402 ctx->fileNums_[bundleNames[i]] = ToolsOp::GetFIleNums(bundleNames[i], false);
403 }
404 ctx->session_ = BIncrementalSessionRestoreAsync::Init(BIncrementalSessionRestoreAsync::Callbacks {
405 .onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2, placeholders::_3),
406 .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
407 .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
408 .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
409 .onResultReport = bind(OnResultReport, ctx, placeholders::_1, placeholders::_2),
410 .onBackupServiceDied = bind(OnBackupServiceDied, ctx),
411 .onProcess = bind(OnProcess, ctx, placeholders::_1, placeholders::_2)});
412 if (ctx->session_ == nullptr) {
413 printf("Failed to init restore\n");
414 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
415 return -EPERM;
416 }
417 ctx->SetBundleFinishedCount(bundleNames.size());
418 return AppendBundles(ctx, pathCapFile, bundleNames, type, userId);
419 }
420
g_exec(map<string,vector<string>> & mapArgToVal)421 static int g_exec(map<string, vector<string>> &mapArgToVal)
422 {
423 if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() ||
424 mapArgToVal.find("restoreType") == mapArgToVal.end() || mapArgToVal.find("userId") == mapArgToVal.end()) {
425 return -EPERM;
426 }
427 return InitArg(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"], *(mapArgToVal["restoreType"].begin()),
428 *(mapArgToVal["userId"].begin()));
429 }
430
IncrementalRestoreAsyncRegister()431 bool IncrementalRestoreAsyncRegister()
432 {
433 return ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
434 .opName = {"incrementalRestoreAsync"},
435 .argList = {{
436 .paramName = "pathCapFile",
437 .repeatable = false,
438 },
439 {
440 .paramName = "bundles",
441 .repeatable = true,
442 },
443 {
444 .paramName = "restoreType",
445 .repeatable = true,
446 },
447 {
448 .paramName = "userId",
449 .repeatable = true,
450 }},
451 .funcGenHelpMsg = GenHelpMsg,
452 .funcExec = g_exec,
453 }});
454 }
455 } // namespace OHOS::FileManagement::Backup