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_restore_async.h"
43
44 namespace OHOS::FileManagement::Backup {
45 using namespace std;
46
47 class SessionAsync {
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<BSessionRestoreAsync> 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<SessionAsync> ctx,const BFileInfo & fileInfo,UniqueFd fd,int32_t errCode)99 static void OnFileReady(shared_ptr<SessionAsync> ctx, const BFileInfo &fileInfo, UniqueFd fd, int32_t errCode)
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 string tmpPath = string(BConstants::BACKUP_TOOL_RECEIVE_DIR) + fileInfo.owner + "/" + formatFileName;
111 if (access(tmpPath.data(), F_OK) != 0) {
112 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
113 }
114 BExcepUltils::VerifyPath(tmpPath, false);
115 UniqueFd fdLocal(open(tmpPath.data(), O_RDONLY));
116 if (fdLocal < 0) {
117 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
118 }
119 BFile::SendFile(fd, fdLocal);
120 std::string bundleName = fileInfo.owner;
121 {
122 unique_lock<mutex> fileLock(ctx->fileCountLock_);
123 ++ctx->fileCount_[bundleName];
124 // 文件准备完成
125 printf("FileReady count/num = %d/%d\n", ctx->fileCount_[bundleName], ctx->fileNums_[bundleName]);
126 if (ctx->fileCount_[bundleName] == ctx->fileNums_[bundleName]) {
127 printf("PublishFile start.\n");
128 BFileInfo fileInfoTemp = fileInfo;
129 fileInfoTemp.fileName = "";
130 int ret = ctx->session_->PublishFile(fileInfoTemp);
131 if (ret != 0) {
132 throw BError(BError::Codes::TOOL_INVAL_ARG, "PublishFile error");
133 }
134 }
135 }
136 }
137
OnBundleStarted(shared_ptr<SessionAsync> ctx,ErrCode err,const BundleName name)138 static void OnBundleStarted(shared_ptr<SessionAsync> ctx, ErrCode err, const BundleName name)
139 {
140 printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str());
141 if (err != 0) {
142 ctx->UpdateBundleFinishedCount();
143 ctx->TryNotify();
144 }
145 }
146
OnBundleFinished(shared_ptr<SessionAsync> ctx,ErrCode err,const BundleName name)147 static void OnBundleFinished(shared_ptr<SessionAsync> ctx, ErrCode err, const BundleName name)
148 {
149 printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
150 if (err != 0) {
151 ctx->UpdateBundleFinishedCount();
152 ctx->TryNotify();
153 }
154 }
155
OnAllBundlesFinished(shared_ptr<SessionAsync> ctx,ErrCode err)156 static void OnAllBundlesFinished(shared_ptr<SessionAsync> ctx, ErrCode err)
157 {
158 printf("all bundles finished end\n");
159 ctx->TryNotify(true);
160 }
161
OnResultReport(shared_ptr<SessionAsync> ctx,const std::string & bundleName,const std::string & resultInfo)162 static void OnResultReport(shared_ptr<SessionAsync> ctx, const std::string &bundleName, const std::string &resultInfo)
163 {
164 printf("OnResultReport bundleName = %s, resultInfo = %s\n", bundleName.c_str(), resultInfo.c_str());
165 }
166
OnBackupServiceDied(shared_ptr<SessionAsync> ctx)167 static void OnBackupServiceDied(shared_ptr<SessionAsync> ctx)
168 {
169 printf("backupServiceDied\n");
170 ctx->TryNotify(true);
171 }
172
OnProcess(shared_ptr<SessionAsync> ctx,const std::string bundleName,const std::string processInfo)173 static void OnProcess(shared_ptr<SessionAsync> ctx, const std::string bundleName, const std::string processInfo)
174 {
175 printf("OnProcess bundleName = %s, processInfo = %s\n", bundleName.c_str(), processInfo.c_str());
176 }
177
ReadyExtManage(const string & path,std::vector<ExtManageInfo> & pkgInfo)178 static map<string, tuple<string, struct stat, bool, bool>> ReadyExtManage(const string &path,
179 std::vector<ExtManageInfo>& pkgInfo)
180 {
181 map<string, tuple<string, struct stat, bool, bool>> info;
182 for (auto &item : pkgInfo) {
183 string fileName = item.hashName;
184 string filePath = item.fileName;
185 if (fileName.empty() || filePath.empty()) {
186 continue;
187 }
188
189 // Rename big file with real name in backup/receive directory
190 // To support file with different path but same name, create directories in /data/backup/receive
191 string realNameInReceive = path + BConstants::FILE_SEPARATOR_CHAR + filePath;
192 string realPathInReceive = realNameInReceive.substr(0, realNameInReceive.rfind("/"));
193 string currentNameInReceive = path + BConstants::FILE_SEPARATOR_CHAR + fileName;
194 if (access(realPathInReceive.c_str(), F_OK) != 0) {
195 if (!ForceCreateDirectory(realPathInReceive.data())) {
196 printf("err create directory %d %s\n", errno, strerror(errno));
197 }
198 }
199 if (rename(currentNameInReceive.data(), realNameInReceive.data()) != 0) {
200 printf("err rename %d %s\n", errno, strerror(errno));
201 }
202
203 // update fileName with filePath according to clone optimize
204 item.hashName = filePath;
205 if (item.hashName.front() == BConstants::FILE_SEPARATOR_CHAR) {
206 item.hashName = item.hashName.substr(1);
207 }
208
209 // update filePath
210 if (!item.isBigFile && !item.isUserTar) {
211 item.fileName = "/";
212 } else {
213 item.fileName = "";
214 }
215 info.emplace(item.hashName, make_tuple(item.fileName, item.sta, item.isBigFile, item.isUserTar));
216 }
217 return info;
218 }
219
AdapteCloneOptimize(const string & path)220 static void AdapteCloneOptimize(const string &path)
221 {
222 string manageJsonStr = path;
223 manageJsonStr = manageJsonStr + BConstants::FILE_SEPARATOR_CHAR + string(BConstants::EXT_BACKUP_MANAGE);
224 UniqueFd fd(open(manageJsonStr.data(), O_RDWR));
225 if (fd < 0) {
226 HILOGE("Failed to open json file = %{private}s, err = %{public}d", manageJsonStr.c_str(), errno);
227 return;
228 }
229 BJsonCachedEntity<BJsonEntityExtManage> cachedEntityOld(std::move(fd));
230 auto cacheOld = cachedEntityOld.Structuralize();
231 auto pkgInfo = cacheOld.GetExtManageInfo();
232 close(cachedEntityOld.GetFd().Release());
233
234 auto info = ReadyExtManage(path, pkgInfo);
235 UniqueFd fdJson(open(manageJsonStr.data(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR));
236 if (fdJson < 0) {
237 HILOGE("Failed to open json file = %{private}s, err = %{public}d", manageJsonStr.c_str(), errno);
238 return;
239 }
240 BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(std::move(fdJson));
241 auto cache = cachedEntity.Structuralize();
242 cache.SetExtManageForClone(info);
243 cachedEntity.Persist();
244 close(cachedEntity.GetFd().Release());
245 }
246
RestoreApp(shared_ptr<SessionAsync> restore,vector<BundleName> & bundleNames)247 static void RestoreApp(shared_ptr<SessionAsync> restore, vector<BundleName> &bundleNames)
248 {
249 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "RestoreApp");
250 if (!restore || !restore->session_) {
251 throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
252 }
253 for (auto &bundleName : bundleNames) {
254 if (bundleName.find('/') != string::npos) {
255 throw BError(BError::Codes::TOOL_INVAL_ARG, "bundleName is not valid");
256 }
257 string path = string(BConstants::BACKUP_TOOL_RECEIVE_DIR) + bundleName;
258 if (access(path.data(), F_OK) != 0) {
259 HILOGE("bundleName tar does not exist, file %{public}s errno : %{public}d",
260 path.c_str(), errno);
261 continue;
262 }
263
264 // update manage.json and fileName
265 AdapteCloneOptimize(path);
266
267 // manage.json first
268 string manageJsonPath = string(BConstants::PATH_BUNDLE_BACKUP_HOME).
269 append(BConstants::SA_BUNDLE_BACKUP_RESTORE).append(BConstants::EXT_BACKUP_MANAGE);
270 if (manageJsonPath.front() == BConstants::FILE_SEPARATOR_CHAR) {
271 manageJsonPath = manageJsonPath.substr(1);
272 }
273 restore->session_->GetFileHandle(bundleName, manageJsonPath);
274
275 string manageJsonStr = path + BConstants::FILE_SEPARATOR_CHAR + string(BConstants::EXT_BACKUP_MANAGE);
276 UniqueFd fd(open(manageJsonStr.data(), O_RDONLY));
277 if (fd < 0) {
278 HILOGE("Failed to open json file = %{private}s, err = %{public}d", manageJsonStr.c_str(), errno);
279 return;
280 }
281 BJsonCachedEntity<BJsonEntityExtManage> cachedEntity(std::move(fd));
282 auto cache = cachedEntity.Structuralize();
283 auto pkgInfo = cache.GetExtManageInfo();
284 for (auto &item : pkgInfo) {
285 printf("New FileName %s\n", item.hashName.data());
286 restore->session_->GetFileHandle(bundleName, item.hashName);
287 }
288 }
289 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
290 }
291
ChangeBundleInfo(const string & pathCapFile,const vector<string> & bundleNames,const string & type)292 static int32_t ChangeBundleInfo(const string &pathCapFile, const vector<string> &bundleNames, const string &type)
293 {
294 BExcepUltils::VerifyPath(pathCapFile, false);
295 UniqueFd fd(open(pathCapFile.data(), O_RDWR, S_IRWXU));
296 if (fd < 0) {
297 fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
298 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
299 return -errno;
300 }
301 BJsonCachedEntity<BJsonEntityCaps> cachedEntity(move(fd));
302 auto cache = cachedEntity.Structuralize();
303 vector<BJsonEntityCaps::BundleInfo> bundleInfos;
304 auto cacheBundleInfos = cache.GetBundleInfos();
305 for (auto name : bundleNames) {
306 string versionName = string(BConstants::DEFAULT_VERSION_NAME);
307 int64_t versionCode = static_cast<int64_t>(BConstants::DEFAULT_VERSION_CODE);
308 if (type == "false") {
309 versionName = string(BConstants::DEFAULT_VERSION_NAME_CLONE);
310 versionCode = static_cast<int64_t>(BConstants::DEFAULT_VERSION_CODE);
311 }
312 for (auto &&bundleInfo : cacheBundleInfos) {
313 if (bundleInfo.name != name) {
314 continue;
315 }
316 bundleInfos.emplace_back(BJsonEntityCaps::BundleInfo {.name = name,
317 .versionCode = versionCode,
318 .versionName = versionName,
319 .spaceOccupied = bundleInfo.spaceOccupied,
320 .increSpaceOccupied = bundleInfo.increSpaceOccupied,
321 .allToBackup = bundleInfo.allToBackup,
322 .fullBackupOnly = bundleInfo.fullBackupOnly,
323 .extensionName = bundleInfo.extensionName});
324 }
325 }
326 cache.SetBundleInfos(bundleInfos);
327 cachedEntity.Persist();
328
329 return 0;
330 }
331
AppendBundles(shared_ptr<SessionAsync> restore,const string & pathCapFile,vector<string> bundleNames,const string & type,const string & userId)332 static int32_t AppendBundles(shared_ptr<SessionAsync> restore,
333 const string &pathCapFile,
334 vector<string> bundleNames,
335 const string &type,
336 const string &userId)
337 {
338 BExcepUltils::VerifyPath(pathCapFile, false);
339 UniqueFd fd(open(pathCapFile.data(), O_RDWR, S_IRWXU));
340 if (fd < 0) {
341 fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
342 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
343 return -errno;
344 }
345 RestoreTypeEnum restoreType = RestoreTypeEnum::RESTORE_DATA_WAIT_SEND;
346 if (type == "true") {
347 restoreType = RestoreTypeEnum::RESTORE_DATA_READDY;
348 }
349 try {
350 int ret = restore->session_->AppendBundles(move(fd), bundleNames, restoreType, atoi(userId.data()));
351 if (ret != 0) {
352 printf("restore append bundles error: %d\n", ret);
353 return -ret;
354 }
355 if (type == "false") {
356 RestoreApp(restore, bundleNames);
357 }
358 } catch (const BError &e) {
359 printf("restore append bundles error: %d\n", e.GetCode());
360 return -1;
361 } catch (...) {
362 printf("Unexpected exception");
363 return -1;
364 }
365 restore->Wait();
366 return 0;
367 }
368
InitArg(const string & pathCapFile,const vector<string> & bundleNames,const string & type,const string & userId)369 static int32_t InitArg(const string &pathCapFile,
370 const vector<string> &bundleNames,
371 const string &type,
372 const string &userId)
373 {
374 StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Init");
375 BExcepUltils::VerifyPath(pathCapFile, false);
376
377 if (ChangeBundleInfo(pathCapFile, bundleNames, type)) {
378 fprintf(stderr, "ChangeBundleInfo error");
379 return -errno;
380 }
381
382 auto ctx = make_shared<SessionAsync>();
383 size_t len = bundleNames.size();
384 for (size_t i = 0; i < len; ++i) {
385 ctx->fileNums_[bundleNames[i]] = ToolsOp::GetFIleNums(bundleNames[i]);
386 }
387 ctx->session_ = BSessionRestoreAsync::Init(BSessionRestoreAsync::Callbacks {
388 .onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2, placeholders::_3),
389 .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
390 .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
391 .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
392 .onResultReport = bind(OnResultReport, ctx, placeholders::_1, placeholders::_2),
393 .onBackupServiceDied = bind(OnBackupServiceDied, ctx),
394 .onProcess = bind(OnProcess, ctx, placeholders::_1, placeholders::_2)});
395 if (ctx->session_ == nullptr) {
396 printf("Failed to init restore\n");
397 FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
398 return -EPERM;
399 }
400 ctx->SetBundleFinishedCount(bundleNames.size());
401 return AppendBundles(ctx, pathCapFile, bundleNames, type, userId);
402 }
403
Exec(map<string,vector<string>> & mapArgToVal)404 static int Exec(map<string, vector<string>> &mapArgToVal)
405 {
406 if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() ||
407 mapArgToVal.find("restoreType") == mapArgToVal.end() || mapArgToVal.find("userId") == mapArgToVal.end()) {
408 return -EPERM;
409 }
410 return InitArg(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"], *(mapArgToVal["restoreType"].begin()),
411 *(mapArgToVal["userId"].begin()));
412 }
413
RestoreAsyncRegister()414 bool RestoreAsyncRegister()
415 {
416 return ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
417 .opName = {"restoreAsync"},
418 .argList = {{
419 .paramName = "pathCapFile",
420 .repeatable = false,
421 },
422 {
423 .paramName = "bundles",
424 .repeatable = true,
425 },
426 {
427 .paramName = "restoreType",
428 .repeatable = true,
429 },
430 {
431 .paramName = "userId",
432 .repeatable = true,
433 }},
434 .funcGenHelpMsg = GenHelpMsg,
435 .funcExec = Exec,
436 }});
437 }
438 } // namespace OHOS::FileManagement::Backup