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