1 /*
2 * Copyright (c) 2023-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 #include "session_backup_n_exporter.h"
16
17 #include <functional>
18 #include <memory>
19
20 #include "b_error/b_error.h"
21 #include "b_filesystem/b_file.h"
22 #include "b_resources/b_constants.h"
23 #include "b_sa/b_sa_utils.h"
24 #include "b_session_backup.h"
25 #include "backup_kit_inner.h"
26 #include "directory_ex.h"
27 #include "filemgmt_libhilog.h"
28 #include "general_callbacks.h"
29 #include "service_proxy.h"
30
31 namespace OHOS::FileManagement::Backup {
32 using namespace std;
33 using namespace LibN;
34
35 struct BackupEntity {
36 unique_ptr<BSessionBackup> session;
37 shared_ptr<GeneralCallbacks> callbacks;
38 };
39
OnFileReady(weak_ptr<GeneralCallbacks> pCallbacks,const BFileInfo & fileInfo,UniqueFd fd,int sysErrno)40 static void OnFileReady(weak_ptr<GeneralCallbacks> pCallbacks, const BFileInfo &fileInfo, UniqueFd fd, int sysErrno)
41 {
42 if (pCallbacks.expired()) {
43 HILOGI("callbacks is unbound");
44 return;
45 }
46 auto callbacks = pCallbacks.lock();
47 if (!callbacks) {
48 HILOGI("callback function onFileReady has already been released");
49 return;
50 }
51 if (!bool(callbacks->onFileReady)) {
52 HILOGI("callback function onFileReady is undefined");
53 return;
54 }
55 ErrCode errCode = BError::GetCodeByErrno(sysErrno);
56 std::string errMsg = "system errno: " + to_string(sysErrno);
57 std::tuple<uint32_t, std::string> errInfo = std::make_tuple(errCode, errMsg);
58
59 auto cbCompl = [bundleName {fileInfo.owner}, fileName {fileInfo.fileName},
60 fd {make_shared<UniqueFd>(fd.Release())}, errInfo](napi_env env, NError err) -> NVal {
61 if (err) {
62 return {env, err.GetNapiErr(env)};
63 }
64 HILOGI("callback function backup onFileReady cbCompl errcode: %{public}d", std::get<0>(errInfo));
65 NVal obj;
66 ErrParam errorParam = [ errInfo ]() {
67 return errInfo;
68 };
69 if (std::get<0>(errInfo) != 0) {
70 obj = NVal {env, NError(errorParam).GetNapiErr(env)};
71 napi_status status = napi_set_named_property(env, obj.val_, FILEIO_TAG_ERR_DATA.c_str(),
72 NVal::CreateUTF8String(env, bundleName).val_);
73 if (status != napi_ok) {
74 HILOGE("Failed to set data property, status %{public}d, bundleName %{public}s",
75 status, bundleName.c_str());
76 }
77 } else {
78 obj = NVal::CreateObject(env);
79 obj.AddProp({
80 NVal::DeclareNapiProperty(BConstants::BUNDLE_NAME.c_str(),
81 NVal::CreateUTF8String(env, bundleName).val_),
82 NVal::DeclareNapiProperty(BConstants::URI.c_str(), NVal::CreateUTF8String(env, fileName).val_),
83 NVal::DeclareNapiProperty(BConstants::FD.c_str(), NVal::CreateInt32(env, fd->Release()).val_)});
84 }
85 return {obj};
86 };
87
88 callbacks->onFileReady.ThreadSafeSchedule(cbCompl);
89 }
90
onBundleBegin(weak_ptr<GeneralCallbacks> pCallbacks,ErrCode err,const BundleName name)91 static void onBundleBegin(weak_ptr<GeneralCallbacks> pCallbacks, ErrCode err, const BundleName name)
92 {
93 HILOGI("Callback onBundleBegin, bundleName=%{public}s, errCode=%{public}d", name.c_str(), err);
94 if (pCallbacks.expired()) {
95 HILOGI("callbacks is unbound");
96 return;
97 }
98 auto callbacks = pCallbacks.lock();
99 if (!callbacks) {
100 HILOGI("callback function onBundleBegin has already been released");
101 return;
102 }
103 if (!bool(callbacks->onBundleBegin)) {
104 HILOGI("callback function onBundleBegin is undefined");
105 return;
106 }
107
108 ErrCode errCode = BError::GetBackupCodeByErrno(err);
109 std::string errMsg = BError::GetBackupMsgByErrno(errCode) + ", origin errno: " + to_string(err);
110 std::tuple<uint32_t, std::string> errInfo = std::make_tuple(errCode, errMsg);
111
112 auto cbCompl = [name {name}, errCode {err}, errInfo](napi_env env, NError err) -> NVal {
113 NVal bundleName = NVal::CreateUTF8String(env, name);
114 if (!err && errCode == 0) {
115 return bundleName;
116 }
117 ErrParam errorParam = [ errInfo ]() {
118 return errInfo;
119 };
120 NVal res;
121 if (err) {
122 res = NVal {env, err.GetNapiErr(env)};
123 } else {
124 res = NVal {env, NError(errorParam).GetNapiErr(env)};
125 }
126 napi_status status = napi_set_named_property(env, res.val_, FILEIO_TAG_ERR_DATA.c_str(), bundleName.val_);
127 if (status != napi_ok) {
128 HILOGE("Failed to set data property, status %{public}d, bundleName %{public}s", status, name.c_str());
129 }
130
131 return res;
132 };
133
134 callbacks->onBundleBegin.ThreadSafeSchedule(cbCompl);
135 }
136
onBundleEnd(weak_ptr<GeneralCallbacks> pCallbacks,ErrCode err,const BundleName name)137 static void onBundleEnd(weak_ptr<GeneralCallbacks> pCallbacks, ErrCode err, const BundleName name)
138 {
139 HILOGI("Callback onBundleEnd, bundleName=%{public}s, errCode=%{public}d", name.c_str(), err);
140 if (pCallbacks.expired()) {
141 HILOGI("callbacks is unbound");
142 return;
143 }
144 auto callbacks = pCallbacks.lock();
145 if (!callbacks) {
146 HILOGI("callback function onBundleEnd has already been released");
147 return;
148 }
149 if (!bool(callbacks->onBundleEnd)) {
150 HILOGI("callback function onBundleEnd is undefined");
151 return;
152 }
153
154 ErrCode errCode = BError::GetBackupCodeByErrno(err);
155 std::string errMsg = BError::GetBackupMsgByErrno(errCode) + ", origin errno: " + to_string(err);
156 std::tuple<uint32_t, std::string> errInfo = std::make_tuple(errCode, errMsg);
157
158 auto cbCompl = [name {name}, errCode {err}, errInfo](napi_env env, NError err) -> NVal {
159 NVal bundleName = NVal::CreateUTF8String(env, name);
160 if (!err && errCode == 0) {
161 return bundleName;
162 }
163 ErrParam errorParam = [ errInfo ]() {
164 return errInfo;
165 };
166 NVal res;
167 if (err) {
168 res = NVal {env, err.GetNapiErr(env)};
169 } else {
170 res = NVal {env, NError(errorParam).GetNapiErr(env)};
171 }
172 napi_status status = napi_set_named_property(env, res.val_, FILEIO_TAG_ERR_DATA.c_str(), bundleName.val_);
173 if (status != napi_ok) {
174 HILOGE("Failed to set data property, status %{public}d, bundleName %{public}s", status, name.c_str());
175 }
176
177 return res;
178 };
179
180 callbacks->onBundleEnd.ThreadSafeSchedule(cbCompl);
181 }
182
onAllBundlesEnd(weak_ptr<GeneralCallbacks> pCallbacks,ErrCode err)183 static void onAllBundlesEnd(weak_ptr<GeneralCallbacks> pCallbacks, ErrCode err)
184 {
185 if (pCallbacks.expired()) {
186 HILOGI("callbacks is unbound");
187 return;
188 }
189 auto callbacks = pCallbacks.lock();
190 if (!callbacks) {
191 HILOGI("callback function onAllBundlesEnd has already been released");
192 return;
193 }
194 if (!bool(callbacks->onAllBundlesEnd)) {
195 HILOGI("callback function onAllBundlesEnd is undefined");
196 return;
197 }
198
199 ErrCode errCode = BError::GetBackupCodeByErrno(err);
200 std::string errMsg = BError::GetBackupMsgByErrno(errCode) + ", origin errno: " + to_string(err);
201 std::tuple<uint32_t, std::string> errInfo = std::make_tuple(errCode, errMsg);
202
203 auto cbCompl = [errCode {err}, errInfo](napi_env env, NError err) -> NVal {
204 if (!err && errCode == 0) {
205 return NVal::CreateUndefined(env);
206 }
207 ErrParam errorParam = [ errInfo ]() {
208 return errInfo;
209 };
210 NVal res;
211 if (err) {
212 res = NVal {env, err.GetNapiErr(env)};
213 } else {
214 res = NVal {env, NError(errorParam).GetNapiErr(env)};
215 }
216
217 return res;
218 };
219
220 callbacks->onAllBundlesEnd.ThreadSafeSchedule(cbCompl);
221 }
222
OnResultReport(weak_ptr<GeneralCallbacks> pCallbacks,const std::string bundleName,const std::string result)223 static void OnResultReport(weak_ptr<GeneralCallbacks> pCallbacks, const std::string bundleName,
224 const std::string result)
225 {
226 HILOGI("Callback OnResultReport, bundleName=%{public}s", bundleName.c_str());
227 if (pCallbacks.expired()) {
228 HILOGI("callbacks is unbound");
229 return;
230 }
231 auto callbacks = pCallbacks.lock();
232 if (!callbacks) {
233 HILOGI("callback function onResultReport has already been released");
234 return;
235 }
236 if (!bool(callbacks->onResultReport)) {
237 HILOGI("callback function onResultReport is undefined");
238 return;
239 }
240 auto cbCompl = [bName {bundleName}, res {result}](napi_env env, vector<napi_value> &argv) -> bool {
241 napi_value napi_bName = nullptr;
242 napi_create_string_utf8(env, bName.c_str(), bName.size(), &napi_bName);
243 argv.push_back(napi_bName);
244 napi_value napi_res = nullptr;
245 napi_create_string_utf8(env, res.c_str(), res.size(), &napi_res);
246 argv.push_back(napi_res);
247 return true;
248 };
249 callbacks->onResultReport.CallJsMethod(cbCompl);
250 }
251
OnBackupServiceDied(weak_ptr<GeneralCallbacks> pCallbacks)252 static void OnBackupServiceDied(weak_ptr<GeneralCallbacks> pCallbacks)
253 {
254 HILOGI("Callback OnBackupServiceDied.");
255 if (pCallbacks.expired()) {
256 HILOGI("callbacks is unbound");
257 return;
258 }
259 auto callbacks = pCallbacks.lock();
260 if (!callbacks) {
261 HILOGI("js callback function onBackupServiceDied has already been released");
262 return;
263 }
264 if (!bool(callbacks->onBackupServiceDied)) {
265 HILOGI("callback function onBackupServiceDied is undefined");
266 return;
267 }
268
269 auto cbCompl = [](napi_env env, vector<napi_value> &argv) -> bool {
270 napi_value napi_res = nullptr;
271 napi_get_undefined(env, &napi_res);
272 argv.push_back(napi_res);
273 return true;
274 };
275 callbacks->onBackupServiceDied.CallJsMethod(cbCompl);
276 }
277
OnProcess(weak_ptr<GeneralCallbacks> pCallbacks,const BundleName name,const std::string processInfo)278 static void OnProcess(weak_ptr<GeneralCallbacks> pCallbacks, const BundleName name, const std::string processInfo)
279 {
280 HILOGI("Callback OnProcess, bundleName=%{public}s", name.c_str());
281 if (pCallbacks.expired()) {
282 HILOGI("callbacks is unbound");
283 return;
284 }
285 auto callbacks = pCallbacks.lock();
286 if (!callbacks) {
287 HILOGI("callback function OnProcess has already been released");
288 return;
289 }
290 if (!bool(callbacks->onProcess)) {
291 HILOGI("callback function OnProcess is undefined");
292 return;
293 }
294 auto cbCompl = [bundleName {name}, process {processInfo}](napi_env env, vector<napi_value> &argv) -> bool {
295 napi_value napi_bName = nullptr;
296 napi_create_string_utf8(env, bundleName.c_str(), bundleName.size(), &napi_bName);
297 argv.push_back(napi_bName);
298 napi_value napi_process = nullptr;
299 napi_create_string_utf8(env, process.c_str(), process.size(), &napi_process);
300 argv.push_back(napi_process);
301 return true;
302 };
303 callbacks->onProcess.CallJsMethod(cbCompl);
304 }
305
Constructor(napi_env env,napi_callback_info cbinfo)306 napi_value SessionBackupNExporter::Constructor(napi_env env, napi_callback_info cbinfo)
307 {
308 HILOGD("called SessionBackup::Constructor begin");
309 if (!SAUtils::CheckBackupPermission()) {
310 HILOGE("Has not permission!");
311 NError(E_PERMISSION).ThrowErr(env);
312 return nullptr;
313 }
314 if (!SAUtils::IsSystemApp()) {
315 HILOGE("System App check fail!");
316 NError(E_PERMISSION_SYS).ThrowErr(env);
317 return nullptr;
318 }
319 NFuncArg funcArg(env, cbinfo);
320 if (!funcArg.InitArgs(NARG_CNT::ONE)) {
321 HILOGE("Number of arguments unmatched.");
322 NError(BError(BError::Codes::SDK_INVAL_ARG, "Number of arguments unmatched.").GetCode()).ThrowErr(env);
323 return nullptr;
324 }
325
326 NVal callbacks(env, funcArg[NARG_POS::FIRST]);
327 if (!callbacks.TypeIs(napi_object)) {
328 HILOGE("First argument is not an object.");
329 NError(BError(BError::Codes::SDK_INVAL_ARG, "First argument is not an object.").GetCode()).ThrowErr(env);
330 return nullptr;
331 }
332
333 NVal ptr(env, funcArg.GetThisVar());
334 auto backupEntity = std::make_unique<BackupEntity>();
335 backupEntity->callbacks = make_shared<GeneralCallbacks>(env, ptr, callbacks);
336 backupEntity->session = BSessionBackup::Init(BSessionBackup::Callbacks {
337 .onFileReady = bind(OnFileReady, backupEntity->callbacks, placeholders::_1, placeholders::_2, placeholders::_3),
338 .onBundleStarted = bind(onBundleBegin, backupEntity->callbacks, placeholders::_1, placeholders::_2),
339 .onBundleFinished = bind(onBundleEnd, backupEntity->callbacks, placeholders::_1, placeholders::_2),
340 .onAllBundlesFinished = bind(onAllBundlesEnd, backupEntity->callbacks, placeholders::_1),
341 .onResultReport = bind(OnResultReport, backupEntity->callbacks, placeholders::_1, placeholders::_2),
342 .onBackupServiceDied = bind(OnBackupServiceDied, backupEntity->callbacks),
343 .onProcess = bind(OnProcess, backupEntity->callbacks, placeholders::_1, placeholders::_2)});
344 if (!backupEntity->session) {
345 NError(BError(BError::Codes::SDK_INVAL_ARG, "Failed to init backup").GetCode()).ThrowErr(env);
346 return nullptr;
347 }
348 if (!NClass::SetEntityFor<BackupEntity>(env, funcArg.GetThisVar(), move(backupEntity))) {
349 HILOGE("Failed to set BackupEntity entity.");
350 NError(BError(BError::Codes::SDK_INVAL_ARG, "Failed to set BackupEntity entity").GetCode()).ThrowErr(env);
351 return nullptr;
352 }
353
354 HILOGD("called SessionBackup::Constructor end");
355 return funcArg.GetThisVar();
356 }
357
VerifyParamSuccess(NFuncArg & funcArg,std::vector<std::string> & bundleNames,std::vector<std::string> & bundleInfos,napi_env env)358 static bool VerifyParamSuccess(NFuncArg &funcArg, std::vector<std::string> &bundleNames,
359 std::vector<std::string> &bundleInfos, napi_env env)
360 {
361 if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
362 HILOGE("Number of arguments unmatched.");
363 NError(BError(BError::Codes::SDK_INVAL_ARG, "Number of arguments unmatched.").GetCode()).ThrowErr(env);
364 return false;
365 }
366 NVal jsBundles(env, funcArg[NARG_POS::FIRST]);
367 auto [succ, jsBundleNames, ignore] = jsBundles.ToStringArray();
368 if (!succ) {
369 HILOGE("First argument is not bundles array.");
370 NError(BError(BError::Codes::SDK_INVAL_ARG, "First argument is not bundles array.").GetCode()).ThrowErr(env);
371 return false;
372 }
373 bundleNames = jsBundleNames;
374 NVal jsInfos(env, funcArg[NARG_POS::SECOND]);
375 if (jsInfos.TypeIs(napi_undefined) || jsInfos.TypeIs(napi_null)) {
376 HILOGW("Third param is not exist");
377 return true;
378 }
379 auto [deSuc, jsBundleInfos, deIgnore] = jsInfos.ToStringArray();
380 if (deSuc) {
381 bundleInfos = jsBundleInfos;
382 if (bundleNames.size() != bundleInfos.size()) {
383 HILOGE("bundleNames count is not equals bundleInfos count");
384 return false;
385 }
386 return true;
387 }
388 HILOGI("Second param is callback");
389 return true;
390 }
391
AppendBundles(napi_env env,napi_callback_info cbinfo)392 napi_value SessionBackupNExporter::AppendBundles(napi_env env, napi_callback_info cbinfo)
393 {
394 HILOGD("called SessionBackup::AppendBundles begin");
395 if (!SAUtils::CheckBackupPermission()) {
396 HILOGE("Has not permission!");
397 NError(E_PERMISSION).ThrowErr(env);
398 return nullptr;
399 }
400 if (!SAUtils::IsSystemApp()) {
401 HILOGE("System App check fail!");
402 NError(E_PERMISSION_SYS).ThrowErr(env);
403 return nullptr;
404 }
405 std::vector<std::string> bundleNames;
406 std::vector<std::string> bundleInfos;
407 NFuncArg funcArg(env, cbinfo);
408 if (!VerifyParamSuccess(funcArg, bundleNames, bundleInfos, env)) {
409 HILOGE("VerifyParamSuccess fail");
410 return nullptr;
411 }
412 auto backupEntity = NClass::GetEntityOf<BackupEntity>(env, funcArg.GetThisVar());
413 if (!(backupEntity && backupEntity->session)) {
414 HILOGE("Failed to get backupSession entity.");
415 NError(BError(BError::Codes::SDK_INVAL_ARG, "Failed to get backupSession entity.").GetCode()).ThrowErr(env);
416 return nullptr;
417 }
418
419 auto cbExec = [session {backupEntity->session.get()}, bundles {bundleNames}, infos {bundleInfos}]() -> NError {
420 if (!session) {
421 return NError(BError(BError::Codes::SDK_INVAL_ARG, "backup session is nullptr").GetCode());
422 }
423 if (!infos.empty()) {
424 return NError(session->AppendBundles(bundles, infos));
425 }
426 return NError(session->AppendBundles(bundles));
427 };
428 auto cbCompl = [](napi_env env, NError err) -> NVal {
429 return err ? NVal {env, err.GetNapiErr(env)} : NVal::CreateUndefined(env);
430 };
431
432 HILOGD("Called SessionBackup::AppendBundles end.");
433
434 NVal thisVar(env, funcArg.GetThisVar());
435 if (funcArg.GetArgc() == NARG_CNT::ONE) {
436 return NAsyncWorkPromise(env, thisVar).Schedule(className, cbExec, cbCompl).val_;
437 } else if (!bundleInfos.empty()) {
438 return NAsyncWorkPromise(env, thisVar).Schedule(className, cbExec, cbCompl).val_;
439 } else {
440 NVal cb(env, funcArg[NARG_POS::SECOND]);
441 return NAsyncWorkCallback(env, thisVar, cb).Schedule(className, cbExec, cbCompl).val_;
442 }
443 }
444
Release(napi_env env,napi_callback_info cbinfo)445 napi_value SessionBackupNExporter::Release(napi_env env, napi_callback_info cbinfo)
446 {
447 HILOGD("called SessionBackup::Release begin");
448 if (!SAUtils::CheckBackupPermission()) {
449 HILOGE("Has not permission!");
450 NError(E_PERMISSION).ThrowErr(env);
451 return nullptr;
452 }
453 if (!SAUtils::IsSystemApp()) {
454 HILOGE("System App check fail!");
455 NError(E_PERMISSION_SYS).ThrowErr(env);
456 return nullptr;
457 }
458 NFuncArg funcArg(env, cbinfo);
459 if (!funcArg.InitArgs(NARG_CNT::ZERO)) {
460 HILOGE("Number of arguments unmatched.");
461 NError(BError(BError::Codes::SDK_INVAL_ARG, "Number of arguments unmatched.").GetCode()).ThrowErr(env);
462 return nullptr;
463 }
464
465 auto backupEntity = NClass::GetEntityOf<BackupEntity>(env, funcArg.GetThisVar());
466 if (!(backupEntity && backupEntity->session)) {
467 HILOGE("Failed to get backupSession entity.");
468 NError(BError(BError::Codes::SDK_INVAL_ARG, "Failed to get backupSession entity.").GetCode()).ThrowErr(env);
469 return nullptr;
470 }
471
472 auto cbExec = [session {backupEntity->session.get()}]() -> NError {
473 if (!session) {
474 return NError(BError(BError::Codes::SDK_INVAL_ARG, "backup session is nullptr").GetCode());
475 }
476 return NError(session->Release());
477 };
478 auto cbCompl = [](napi_env env, NError err) -> NVal {
479 return err ? NVal {env, err.GetNapiErr(env)} : NVal::CreateUndefined(env);
480 };
481
482 HILOGD("Called SessionBackup::Release end.");
483
484 NVal thisVar(env, funcArg.GetThisVar());
485 return NAsyncWorkPromise(env, thisVar).Schedule(className, cbExec, cbCompl).val_;
486 }
487
Export()488 bool SessionBackupNExporter::Export()
489 {
490 HILOGD("called SessionBackupNExporter::Export begin");
491 vector<napi_property_descriptor> props = {
492 NVal::DeclareNapiFunction("appendBundles", AppendBundles),
493 NVal::DeclareNapiFunction("release", Release),
494 };
495
496 auto [succ, classValue] = NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
497 if (!succ) {
498 HILOGE("Failed to define class");
499 NError(EIO).ThrowErr(exports_.env_);
500 return false;
501 }
502 succ = NClass::SaveClass(exports_.env_, className, classValue);
503 if (!succ) {
504 HILOGE("Failed to save class");
505 NError(EIO).ThrowErr(exports_.env_);
506 return false;
507 }
508
509 HILOGD("called SessionBackupNExporter::Export end");
510 return exports_.AddProp(className, classValue);
511 }
512
GetClassName()513 string SessionBackupNExporter::GetClassName()
514 {
515 return SessionBackupNExporter::className;
516 }
517
SessionBackupNExporter(napi_env env,napi_value exports)518 SessionBackupNExporter::SessionBackupNExporter(napi_env env, napi_value exports) : NExporter(env, exports) {}
519
~SessionBackupNExporter()520 SessionBackupNExporter::~SessionBackupNExporter() {}
521 } // namespace OHOS::FileManagement::Backup