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