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