• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 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 "errors.h"
38 #include "hitrace_meter.h"
39 #include "service_proxy.h"
40 #include "tools_op.h"
41 
42 namespace OHOS::FileManagement::Backup {
43 using namespace std;
44 
45 class SessionAsync {
46 public:
TryNotify(bool flag=false)47     void TryNotify(bool flag = false)
48     {
49         if (flag == true) {
50             ready_ = true;
51             cv_.notify_all();
52         }
53     }
54 
Wait()55     void Wait()
56     {
57         unique_lock<mutex> lk(lock_);
58         cv_.wait(lk, [&] { return ready_; });
59     }
60 
61     shared_ptr<BSessionRestoreAsync> session_ = {};
62 
63 private:
64     mutable condition_variable cv_;
65     mutex lock_;
66     bool ready_ = false;
67 };
68 
GenHelpMsg()69 static string GenHelpMsg()
70 {
71     return "\tThis operation helps to restore application data.\n"
72            "\t\t--pathCapFile\t\t This parameter should be the path of the capability file.\n"
73            "\t\t--bundle\t\t This parameter is bundleName.\n"
74            "\t\t--userId\t\t This parameter is userId.\n"
75            "\t\t--restoreType\t\t The parameter is a bool variable. true Simulates the upgrade service scenario; false "
76            "simulates the application recovery scenario.\n";
77 }
78 
OnFileReady(shared_ptr<SessionAsync> ctx,const BFileInfo & fileInfo,UniqueFd fd)79 static void OnFileReady(shared_ptr<SessionAsync> ctx, const BFileInfo &fileInfo, UniqueFd fd)
80 {
81     printf("FileReady owner = %s, fileName = %s, sn = %u, fd = %d\n", fileInfo.owner.c_str(), fileInfo.fileName.c_str(),
82            fileInfo.sn, fd.Get());
83     if (!regex_match(fileInfo.fileName, regex("^[0-9a-zA-Z_.]+$")) &&
84         fileInfo.fileName != BConstants::RESTORE_INSTALL_PATH) {
85         throw BError(BError::Codes::TOOL_INVAL_ARG, "Filename is not alphanumeric");
86     }
87     string tmpPath;
88     if (fileInfo.fileName == BConstants::RESTORE_INSTALL_PATH) {
89         printf("OnFileReady bundle hap\n");
90         tmpPath = string(BConstants::BACKUP_TOOL_INSTALL_DIR) + fileInfo.owner + ".hap";
91     } else {
92         tmpPath = string(BConstants::BACKUP_TOOL_RECEIVE_DIR) + fileInfo.owner + "/" + fileInfo.fileName;
93     }
94     if (access(tmpPath.data(), F_OK) != 0) {
95         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
96     }
97     BExcepUltils::VerifyPath(tmpPath, false);
98     UniqueFd fdLocal(open(tmpPath.data(), O_RDONLY));
99     if (fdLocal < 0) {
100         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
101     }
102     BFile::SendFile(fd, fdLocal);
103     int ret = ctx->session_->PublishFile(fileInfo);
104     if (ret != 0) {
105         throw BError(BError::Codes::TOOL_INVAL_ARG, "PublishFile error");
106     }
107 }
108 
OnBundleStarted(shared_ptr<SessionAsync> ctx,ErrCode err,const BundleName name)109 static void OnBundleStarted(shared_ptr<SessionAsync> ctx, ErrCode err, const BundleName name)
110 {
111     printf("BundleStarted errCode = %d, BundleName = %s\n", err, name.c_str());
112     if (err != 0) {
113         ctx->TryNotify(true);
114     }
115 }
116 
OnBundleFinished(shared_ptr<SessionAsync> ctx,ErrCode err,const BundleName name)117 static void OnBundleFinished(shared_ptr<SessionAsync> ctx, ErrCode err, const BundleName name)
118 {
119     printf("BundleFinished errCode = %d, BundleName = %s\n", err, name.c_str());
120     if (err != 0) {
121         ctx->TryNotify(true);
122     }
123 }
124 
OnAllBundlesFinished(shared_ptr<SessionAsync> ctx,ErrCode err)125 static void OnAllBundlesFinished(shared_ptr<SessionAsync> ctx, ErrCode err)
126 {
127     printf("AllBundlesFinished errCode = %d\n", err);
128     if (err != 0) {
129         ctx->TryNotify(true);
130     }
131 }
132 
OnBackupServiceDied(shared_ptr<SessionAsync> ctx)133 static void OnBackupServiceDied(shared_ptr<SessionAsync> ctx)
134 {
135     printf("backupServiceDied\n");
136     ctx->TryNotify(true);
137 }
138 
RestoreApp(shared_ptr<SessionAsync> restore,vector<BundleName> & bundleNames)139 static void RestoreApp(shared_ptr<SessionAsync> restore, vector<BundleName> &bundleNames)
140 {
141     StartTrace(HITRACE_TAG_FILEMANAGEMENT, "RestoreApp");
142     if (!restore || !restore->session_) {
143         throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
144     }
145     for (auto &bundleName : bundleNames) {
146         if (!regex_match(bundleName, regex("^[0-9a-zA-Z_.]+$"))) {
147             throw BError(BError::Codes::TOOL_INVAL_ARG, "bundleName is not alphanumeric");
148         }
149         string path = string(BConstants::BACKUP_TOOL_RECEIVE_DIR) + bundleName;
150         if (access(path.data(), F_OK) != 0) {
151             throw BError(BError::Codes::TOOL_INVAL_ARG, generic_category().message(errno));
152         }
153         const auto [err, filePaths] = BDir::GetDirFiles(path);
154         if (err != 0) {
155             throw BError(BError::Codes::TOOL_INVAL_ARG, "error path");
156         }
157         // install bundle.hap
158         string installPath = string(BConstants::BACKUP_TOOL_INSTALL_DIR) + bundleName + ".hap";
159         if (access(installPath.data(), F_OK) == 0) {
160             printf("install bundle hap %s\n", installPath.c_str());
161             restore->session_->GetFileHandle(bundleName, string(BConstants::RESTORE_INSTALL_PATH));
162         }
163         for (auto &filePath : filePaths) {
164             string fileName = filePath.substr(filePath.rfind("/") + 1);
165             restore->session_->GetFileHandle(bundleName, fileName);
166         }
167     }
168     FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
169 }
170 
ChangeBundleInfo(const string & pathCapFile,const vector<string> & bundleNames,const string & type)171 static int32_t ChangeBundleInfo(const string &pathCapFile, const vector<string> &bundleNames, const string &type)
172 {
173     BExcepUltils::VerifyPath(pathCapFile, false);
174     UniqueFd fd(open(pathCapFile.data(), O_RDWR, S_IRWXU));
175     if (fd < 0) {
176         fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
177         FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
178         return -errno;
179     }
180     BJsonCachedEntity<BJsonEntityCaps> cachedEntity(move(fd));
181     auto cache = cachedEntity.Structuralize();
182     vector<BJsonEntityCaps::BundleInfo> bundleInfos;
183     for (auto name : bundleNames) {
184         string versionName = string(BConstants::DEFAULT_VERSION_NAME);
185         uint32_t versionCode = static_cast<uint32_t>(BConstants::DEFAULT_VERSION_CODE);
186         bundleInfos.emplace_back(BJsonEntityCaps::BundleInfo {
187             .name = name, .needToInstall = false, .versionCode = versionCode, .versionName = versionName});
188     }
189     cache.SetBundleInfos(bundleInfos);
190     cachedEntity.Persist();
191 
192     return 0;
193 }
194 
AppendBundles(shared_ptr<SessionAsync> restore,const string & pathCapFile,vector<string> bundleNames,const string & type,const string & userId)195 static int32_t AppendBundles(shared_ptr<SessionAsync> restore,
196                              const string &pathCapFile,
197                              vector<string> bundleNames,
198                              const string &type,
199                              const string &userId)
200 {
201     BExcepUltils::VerifyPath(pathCapFile, false);
202     UniqueFd fd(open(pathCapFile.data(), O_RDWR, S_IRWXU));
203     if (fd < 0) {
204         fprintf(stderr, "Failed to open file error: %d %s\n", errno, strerror(errno));
205         FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
206         return -errno;
207     }
208     RestoreTypeEnum restoreType = RestoreTypeEnum::RESTORE_DATA_WAIT_SEND;
209     if (type == "true") {
210         restoreType = RestoreTypeEnum::RESTORE_DATA_READDY;
211     }
212     try {
213         int ret = restore->session_->AppendBundles(move(fd), bundleNames, restoreType, atoi(userId.data()));
214         if (ret != 0) {
215             printf("restore append bundles error: %d\n", ret);
216             return -ret;
217         }
218         if (type == "false") {
219             RestoreApp(restore, bundleNames);
220         }
221     } catch (const BError &e) {
222         printf("restore append bundles error: %d\n", e.GetCode());
223         return -1;
224     } catch (...) {
225         printf("Unexpected exception");
226         return -1;
227     }
228     restore->Wait();
229     return 0;
230 }
231 
InitArg(const string & pathCapFile,const vector<string> & bundleNames,const string & type,const string & userId)232 static int32_t InitArg(const string &pathCapFile,
233                        const vector<string> &bundleNames,
234                        const string &type,
235                        const string &userId)
236 {
237     StartTrace(HITRACE_TAG_FILEMANAGEMENT, "Init");
238     BExcepUltils::VerifyPath(pathCapFile, false);
239 
240     if (ChangeBundleInfo(pathCapFile, bundleNames, type)) {
241         fprintf(stderr, "ChangeBundleInfo error");
242         return -errno;
243     }
244 
245     auto ctx = make_shared<SessionAsync>();
246     ctx->session_ = BSessionRestoreAsync::Init(BSessionRestoreAsync::Callbacks {
247         .onFileReady = bind(OnFileReady, ctx, placeholders::_1, placeholders::_2),
248         .onBundleStarted = bind(OnBundleStarted, ctx, placeholders::_1, placeholders::_2),
249         .onBundleFinished = bind(OnBundleFinished, ctx, placeholders::_1, placeholders::_2),
250         .onAllBundlesFinished = bind(OnAllBundlesFinished, ctx, placeholders::_1),
251         .onBackupServiceDied = bind(OnBackupServiceDied, ctx)});
252     if (ctx->session_ == nullptr) {
253         printf("Failed to init restore\n");
254         FinishTrace(HITRACE_TAG_FILEMANAGEMENT);
255         return -EPERM;
256     }
257 
258     return AppendBundles(ctx, pathCapFile, bundleNames, type, userId);
259 }
260 
Exec(map<string,vector<string>> & mapArgToVal)261 static int Exec(map<string, vector<string>> &mapArgToVal)
262 {
263     if (mapArgToVal.find("pathCapFile") == mapArgToVal.end() || mapArgToVal.find("bundles") == mapArgToVal.end() ||
264         mapArgToVal.find("restoreType") == mapArgToVal.end() || mapArgToVal.find("userId") == mapArgToVal.end()) {
265         return -EPERM;
266     }
267     return InitArg(*(mapArgToVal["pathCapFile"].begin()), mapArgToVal["bundles"], *(mapArgToVal["restoreType"].begin()),
268                    *(mapArgToVal["userId"].begin()));
269 }
270 
271 /**
272  * @brief The hack behind is that "variable with static storage duration has initialization or a destructor with side
273  * effects; it shall not be eliminated even if it appears to be unused" -- point 2.[basic.stc.static].c++ draft
274  *
275  */
276 static bool g_autoRegHack = ToolsOp::Register(ToolsOp {ToolsOp::Descriptor {
277     .opName = {"restoreAsync"},
278     .argList = {{
279                     .paramName = "pathCapFile",
280                     .repeatable = false,
281                 },
282                 {
283                     .paramName = "bundles",
284                     .repeatable = true,
285                 },
286                 {
287                     .paramName = "restoreType",
288                     .repeatable = true,
289                 },
290                 {
291                     .paramName = "userId",
292                     .repeatable = true,
293                 }},
294     .funcGenHelpMsg = GenHelpMsg,
295     .funcExec = Exec,
296 }});
297 } // namespace OHOS::FileManagement::Backup