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