• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
16 #include "cloud_sync_napi.h"
17 
18 #include <sys/types.h>
19 #include <sys/xattr.h>
20 
21 #include "async_work.h"
22 #include "cloud_sync_manager.h"
23 #include "dfs_error.h"
24 #include "dfsu_access_token_helper.h"
25 #include "securec.h"
26 #include "uri.h"
27 #include "utils_log.h"
28 
29 namespace OHOS::FileManagement::CloudSync {
30 using namespace FileManagement::LibN;
31 using namespace std;
32 const int32_t PARAM0 = 0;
33 static const unsigned int READ_SIZE = 1024;
34 const string FILE_SCHEME = "file";
35 thread_local unique_ptr<ChangeListenerNapi> g_listObj = nullptr;
36 mutex CloudSyncNapi::sOnOffMutex_;
37 static mutex obsMutex_;
38 const int32_t AGING_DAYS = 30;
39 CloudSyncState CloudSyncCallbackImpl::preState_ = CloudSyncState::COMPLETED;
40 ErrorType CloudSyncCallbackImpl::preError_ = ErrorType::NO_ERROR;
41 
42 class ObserverImpl : public AAFwk::DataAbilityObserverStub {
43 public:
ObserverImpl(const shared_ptr<CloudNotifyObserver> cloudNotifyObserver)44     explicit ObserverImpl(const shared_ptr<CloudNotifyObserver> cloudNotifyObserver)
45         : cloudNotifyObserver_(cloudNotifyObserver){};
46     void OnChange();
47     void OnChangeExt(const AAFwk::ChangeInfo &info);
48     static sptr<ObserverImpl> GetObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
49     static bool FindObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
50     static bool DeleteObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer);
51 
52 private:
53     struct ObserverParam {
54         sptr<ObserverImpl> obs_;
55         list<Uri> uris_;
56     };
57     shared_ptr<CloudNotifyObserver> cloudNotifyObserver_;
58     static ConcurrentMap<CloudNotifyObserver *, ObserverParam> observers_;
59 };
60 
61 ConcurrentMap<CloudNotifyObserver *, ObserverImpl::ObserverParam> ObserverImpl::observers_;
62 
OnChange()63 void ObserverImpl::OnChange() {}
64 
OnChangeExt(const AAFwk::ChangeInfo & info)65 void ObserverImpl::OnChangeExt(const AAFwk::ChangeInfo &info)
66 {
67     if (cloudNotifyObserver_ == nullptr) {
68         LOGE("cloudNotifyObserver_ is null!");
69         return;
70     }
71     cloudNotifyObserver_->OnchangeExt(info);
72 }
73 
GetObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)74 sptr<ObserverImpl> ObserverImpl::GetObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
75 {
76     lock_guard<mutex> lock(obsMutex_);
77     sptr<ObserverImpl> result = nullptr;
78     observers_.Compute(observer.get(), [&result, &uri, &observer](const auto &key, auto &value) {
79         if (value.obs_ == nullptr) {
80             value.obs_ = new (nothrow) ObserverImpl(observer);
81             value.uris_.push_back(uri);
82         } else {
83             auto it = find(value.uris_.begin(), value.uris_.end(), uri);
84             if (it == value.uris_.end()) {
85                 value.uris_.push_back(uri);
86             }
87         }
88 
89         result = value.obs_;
90         return result != nullptr;
91     });
92 
93     return result;
94 }
95 
FindObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)96 bool ObserverImpl::FindObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
97 {
98     lock_guard<mutex> lock(obsMutex_);
99     auto result = observers_.Find(observer.get());
100     if (result.first) {
101         auto it = std::find(result.second.uris_.begin(), result.second.uris_.end(), uri);
102         if (it == result.second.uris_.end()) {
103             return false;
104         }
105     }
106     return result.first;
107 }
108 
DeleteObserver(const Uri & uri,const shared_ptr<CloudNotifyObserver> & observer)109 bool ObserverImpl::DeleteObserver(const Uri &uri, const shared_ptr<CloudNotifyObserver> &observer)
110 {
111     lock_guard<mutex> lock(obsMutex_);
112     return observers_.ComputeIfPresent(observer.get(), [&uri](auto &key, auto &value) {
113         value.uris_.remove_if([&uri](const auto &value) { return uri == value; });
114         return !value.uris_.empty();
115     });
116 }
117 
CloudSyncCallbackImpl(napi_env env,napi_value fun)118 CloudSyncCallbackImpl::CloudSyncCallbackImpl(napi_env env, napi_value fun) : env_(env)
119 {
120     if (fun != nullptr) {
121         napi_create_reference(env_, fun, 1, &cbOnRef_);
122     }
123 }
124 
OnComplete(UvChangeMsg * msg)125 void CloudSyncCallbackImpl::OnComplete(UvChangeMsg *msg)
126 {
127     auto cloudSyncCallback = msg->cloudSyncCallback_.lock();
128     if (cloudSyncCallback == nullptr || cloudSyncCallback->cbOnRef_ == nullptr) {
129         LOGE("cloudSyncCallback->cbOnRef_ is nullptr");
130         return;
131     }
132     auto env = cloudSyncCallback->env_;
133     auto ref = cloudSyncCallback->cbOnRef_;
134     napi_handle_scope scope = nullptr;
135     napi_open_handle_scope(env, &scope);
136     napi_value jsCallback = nullptr;
137     napi_status status = napi_get_reference_value(env, ref, &jsCallback);
138     if (status != napi_ok) {
139         LOGE("Create reference failed, status: %{public}d", status);
140         napi_close_handle_scope(env, scope);
141         return;
142     }
143     NVal obj = NVal::CreateObject(env);
144     obj.AddProp("state", NVal::CreateInt32(env, (int32_t)msg->state_).val_);
145     obj.AddProp("error", NVal::CreateInt32(env, (int32_t)msg->error_).val_);
146     napi_value retVal = nullptr;
147     napi_value global = nullptr;
148     napi_get_global(env, &global);
149     status = napi_call_function(env, global, jsCallback, ARGS_ONE, &(obj.val_), &retVal);
150     if (status != napi_ok) {
151         LOGE("napi call function failed, status: %{public}d", status);
152     }
153     napi_close_handle_scope(env, scope);
154 }
155 
OnDeathRecipient()156 void CloudSyncCallbackImpl::OnDeathRecipient()
157 {
158     auto isInDownOrUpload = (preState_ == CloudSyncState::UPLOADING) || (preState_ == CloudSyncState::DOWNLOADING);
159     if (isInDownOrUpload && (preError_ == ErrorType::NO_ERROR)) {
160         OnSyncStateChanged(CloudSyncState::STOPPED, ErrorType::NO_ERROR);
161     }
162 }
163 
OnSyncStateChanged(CloudSyncState state,ErrorType error)164 void CloudSyncCallbackImpl::OnSyncStateChanged(CloudSyncState state, ErrorType error)
165 {
166     LOGI("notify - state: %{public}d, error: %{public}d", state, error);
167     uv_loop_s *loop = nullptr;
168     napi_get_uv_event_loop(env_, &loop);
169     if (loop == nullptr) {
170         return;
171     }
172 
173     uv_work_t *work = new (nothrow) uv_work_t;
174     if (work == nullptr) {
175         LOGE("Failed to create uv work");
176         return;
177     }
178 
179     UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(shared_from_this(), state, error);
180     if (msg == nullptr) {
181         delete work;
182         return;
183     }
184 
185     work->data = reinterpret_cast<void *>(msg);
186     int ret = uv_queue_work(
187         loop, work, [](uv_work_t *work) {},
188         [](uv_work_t *work, int status) {
189             auto msg = reinterpret_cast<UvChangeMsg *>(work->data);
190             OnComplete(msg);
191             delete msg;
192             delete work;
193         });
194     preState_ = state;
195     preError_ = error;
196     if (ret != 0) {
197         LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
198         delete msg;
199         delete work;
200     }
201 }
202 
DeleteReference()203 void CloudSyncCallbackImpl::DeleteReference()
204 {
205     if (cbOnRef_ != nullptr) {
206         napi_delete_reference(env_, cbOnRef_);
207         cbOnRef_ = nullptr;
208     }
209 }
210 
OnSyncStateChanged(SyncType type,SyncPromptState state)211 void CloudSyncCallbackImpl::OnSyncStateChanged(SyncType type, SyncPromptState state)
212 {
213     return;
214 }
215 
OnOptimizeProcess(const OptimizeState state,const int32_t progress)216 void CloudOptimizeCallbackImpl::OnOptimizeProcess(const OptimizeState state, const int32_t progress)
217 {
218     napi_env env = env_;
219     auto task = [this, env, state, progress] () {
220         if (!cbOnRef_) {
221             LOGE("cbOnRef_ is nullptr");
222             return;
223         }
224 
225         napi_handle_scope scope = nullptr;
226         napi_status status = napi_open_handle_scope(env, &scope);
227         if (status != napi_ok) {
228             LOGE("Create reference failed, status: %{public}d", status);
229             return;
230         }
231 
232         napi_value jsCallback = cbOnRef_.Deref(env).val_;
233         NVal obj = NVal::CreateObject(env);
234         obj.AddProp("state", NVal::CreateInt32(env, (int32_t)state).val_);
235         obj.AddProp("progress", NVal::CreateInt32(env, (int32_t)progress).val_);
236         napi_value retVal = nullptr;
237         status = napi_call_function(env_, nullptr, jsCallback, ARGS_ONE, &(obj.val_), &retVal);
238         if (status != napi_ok) {
239             LOGE("napi call function failed, status: %{public}d", status);
240         }
241         napi_close_handle_scope(env, scope);
242     };
243     auto ret = napi_send_event(env, task, napi_eprio_immediate);
244     if  (ret != 0) {
245         LOGE("failed to send event, ret: %{public}d", ret);
246     }
247 }
248 
GetBundleName(const napi_env & env,const NFuncArg & funcArg)249 string CloudSyncNapi::GetBundleName(const napi_env &env, const NFuncArg &funcArg)
250 {
251     string bundleName;
252     auto bundleEntity = NClass::GetEntityOf<BundleEntity>(env, funcArg.GetThisVar());
253     if (bundleEntity) {
254         bundleName = bundleEntity->bundleName_;
255     }
256     return bundleName;
257 }
258 
Constructor(napi_env env,napi_callback_info info)259 napi_value CloudSyncNapi::Constructor(napi_env env, napi_callback_info info)
260 {
261     NFuncArg funcArg(env, info);
262     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
263         NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
264         return nullptr;
265     }
266     if (funcArg.GetArgc() == NARG_CNT::ZERO) {
267         LOGD("init without bundleName");
268         return funcArg.GetThisVar();
269     }
270     if (funcArg.GetArgc() == NARG_CNT::ONE) {
271         auto [succ, bundleName, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
272         if (!succ || bundleName.get() == string("")) {
273             LOGE("Failed to get bundle name");
274             NError(E_PARAMS).ThrowErr(env);
275             return nullptr;
276         }
277         auto bundleEntity = make_unique<BundleEntity>(bundleName.get());
278         if (!NClass::SetEntityFor<BundleEntity>(env, funcArg.GetThisVar(), move(bundleEntity))) {
279             LOGE("Failed to set file entity");
280             NError(EIO).ThrowErr(env);
281             return nullptr;
282         }
283         LOGI("init with bundleName");
284         return funcArg.GetThisVar();
285     }
286     return nullptr;
287 }
288 
InitArgsOnCallback(const napi_env & env,NFuncArg & funcArg)289 bool CloudSyncNapi::InitArgsOnCallback(const napi_env &env, NFuncArg &funcArg)
290 {
291     if (!funcArg.InitArgs(NARG_CNT::TWO)) {
292         NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
293         LOGE("OnCallback Number of arguments unmatched");
294         return false;
295     }
296 
297     auto [succ, type, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
298     if (!(succ && (type.get() == std::string("progress")))) {
299         NError(E_PARAMS).ThrowErr(env);
300         return false;
301     }
302 
303     if (!NVal(env, funcArg[(int)NARG_POS::SECOND]).TypeIs(napi_function)) {
304         LOGE("Argument type mismatch");
305         NError(E_PARAMS).ThrowErr(env);
306         return false;
307     }
308 
309     if (callback_ != nullptr) {
310         LOGI("callback already exist");
311         return false;
312     }
313     return true;
314 }
315 
OnCallback(napi_env env,napi_callback_info info)316 napi_value CloudSyncNapi::OnCallback(napi_env env, napi_callback_info info)
317 {
318     NFuncArg funcArg(env, info);
319     if (!InitArgsOnCallback(env, funcArg)) {
320         return nullptr;
321     }
322 
323     string bundleName = GetBundleName(env, funcArg);
324     callback_ = make_shared<CloudSyncCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]).val_);
325     int32_t ret = CloudSyncManager::GetInstance().RegisterCallback(callback_, bundleName);
326     if (ret != E_OK) {
327         LOGE("OnCallback Register error, result: %{public}d", ret);
328         NError(Convert2JsErrNum(ret)).ThrowErr(env);
329         return nullptr;
330     }
331 
332     return NVal::CreateUndefined(env).val_;
333 }
334 
InitArgsOffCallback(const napi_env & env,NFuncArg & funcArg)335 bool CloudSyncNapi::InitArgsOffCallback(const napi_env &env, NFuncArg &funcArg)
336 {
337     if (!funcArg.InitArgs(NARG_CNT::ONE, NARG_CNT::TWO)) {
338         NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
339         LOGE("OffCallback Number of arguments unmatched");
340         return false;
341     }
342 
343     auto [succ, type, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
344     if (!(succ && (type.get() == std::string("progress")))) {
345         NError(E_PARAMS).ThrowErr(env);
346         return false;
347     }
348 
349     if (funcArg.GetArgc() == (uint)NARG_CNT::TWO && !NVal(env, funcArg[(int)NARG_POS::SECOND]).TypeIs(napi_function)) {
350         LOGE("Argument type mismatch");
351         NError(E_PARAMS).ThrowErr(env);
352         return false;
353     }
354     return true;
355 }
356 
OffCallback(napi_env env,napi_callback_info info)357 napi_value CloudSyncNapi::OffCallback(napi_env env, napi_callback_info info)
358 {
359     NFuncArg funcArg(env, info);
360     if (!InitArgsOffCallback(env, funcArg)) {
361         return nullptr;
362     }
363 
364     string bundleName = GetBundleName(env, funcArg);
365     int32_t ret = CloudSyncManager::GetInstance().UnRegisterCallback(bundleName);
366     if (ret != E_OK) {
367         LOGE("OffCallback UnRegister error, result: %{public}d", ret);
368         NError(Convert2JsErrNum(ret)).ThrowErr(env);
369         return nullptr;
370     }
371     if (callback_ != nullptr) {
372         /* napi delete reference */
373         callback_->DeleteReference();
374         callback_ = nullptr;
375     }
376     return NVal::CreateUndefined(env).val_;
377 }
378 
Start(napi_env env,napi_callback_info info)379 napi_value CloudSyncNapi::Start(napi_env env, napi_callback_info info)
380 {
381     NFuncArg funcArg(env, info);
382     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
383         NError(E_PARAMS).ThrowErr(env);
384     }
385 
386     string bundleName = GetBundleName(env, funcArg);
387     auto cbExec = [bundleName]() -> NError {
388         int32_t ret = CloudSyncManager::GetInstance().StartSync(bundleName);
389         if (ret != E_OK) {
390             LOGE("Start Sync error, result: %{public}d", ret);
391             return NError(Convert2JsErrNum(ret));
392         }
393         return NError(ERRNO_NOERR);
394     };
395 
396     auto cbComplete = [](napi_env env, NError err) -> NVal {
397         if (err) {
398             return {env, err.GetNapiErr(env)};
399         }
400         return NVal::CreateUndefined(env);
401     };
402 
403     std::string procedureName = "Start";
404     auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
405     return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
406 }
407 
Stop(napi_env env,napi_callback_info info)408 napi_value CloudSyncNapi::Stop(napi_env env, napi_callback_info info)
409 {
410     NFuncArg funcArg(env, info);
411     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
412         NError(E_PARAMS).ThrowErr(env);
413         return nullptr;
414     }
415 
416     string bundleName = GetBundleName(env, funcArg);
417     auto cbExec = [bundleName]() -> NError {
418         int32_t ret = CloudSyncManager::GetInstance().StopSync(bundleName);
419         if (ret != E_OK) {
420             LOGE("Stop Sync error, result: %{public}d", ret);
421             return NError(Convert2JsErrNum(ret));
422         }
423         return NError(ERRNO_NOERR);
424     };
425 
426     auto cbComplete = [](napi_env env, NError err) -> NVal {
427         if (err) {
428             return {env, err.GetNapiErr(env)};
429         }
430         return NVal::CreateUndefined(env);
431     };
432 
433     std::string procedureName = "Stop";
434     auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
435     return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
436 }
437 
CheckRef(napi_env env,napi_ref ref,ChangeListenerNapi & listObj,const string & uri)438 bool CloudSyncNapi::CheckRef(napi_env env, napi_ref ref, ChangeListenerNapi &listObj, const string &uri)
439 {
440     napi_value offCallback = nullptr;
441     napi_status status = napi_get_reference_value(env, ref, &offCallback);
442     if (status != napi_ok) {
443         LOGE("Create reference fail, status: %{public}d", status);
444         return false;
445     }
446     bool isSame = false;
447     shared_ptr<CloudNotifyObserver> obs;
448     string obsUri;
449     {
450         lock_guard<mutex> lock(sOnOffMutex_);
451         for (auto it = listObj.observers_.begin(); it < listObj.observers_.end(); it++) {
452             napi_value onCallback = nullptr;
453             status = napi_get_reference_value(env, (*it)->ref_, &onCallback);
454             if (status != napi_ok) {
455                 LOGE("Create reference fail, status: %{public}d", status);
456                 return false;
457             }
458             napi_strict_equals(env, offCallback, onCallback, &isSame);
459             if (isSame) {
460                 obsUri = (*it)->uri_;
461                 if (uri.compare(obsUri) != 0) {
462                     return true;
463                 }
464                 return false;
465             }
466         }
467     }
468     return true;
469 }
470 
CheckIsValidUri(Uri uri)471 static bool CheckIsValidUri(Uri uri)
472 {
473     string scheme = uri.GetScheme();
474     if (scheme != FILE_SCHEME) {
475         return false;
476     }
477     string sandboxPath = uri.GetPath();
478     char realPath[PATH_MAX + 1]{'\0'};
479     if (realpath(sandboxPath.c_str(), realPath) == nullptr) {
480         LOGE("realpath failed with %{public}d", errno);
481         return false;
482     }
483     if (strncmp(realPath, sandboxPath.c_str(), sandboxPath.size()) != 0) {
484         LOGE("sandboxPath is not equal to realPath");
485         return false;
486     }
487     if (sandboxPath.find("/data/storage/el2/cloud") != 0) {
488         LOGE("not surported uri");
489         return false;
490     }
491     return true;
492 }
493 
GetRegisterParams(napi_env env,napi_callback_info info,RegisterParams & registerParams)494 static int32_t GetRegisterParams(napi_env env, napi_callback_info info, RegisterParams &registerParams)
495 {
496     NFuncArg funcArg(env, info);
497     if (!funcArg.InitArgs(NARG_CNT::THREE)) {
498         LOGE("Arguments number mismatch");
499         return E_PARAMS;
500     }
501 
502     auto [succUri, uri, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
503     if (!succUri) {
504         LOGE("get arg uri fail");
505         return E_PARAMS;
506     }
507     registerParams.uri = string(uri.get());
508     if (!CheckIsValidUri(Uri(registerParams.uri))) {
509         LOGE("RegisterChange uri parameter format error!");
510         return E_PARAMS;
511     }
512 
513     auto [succRecursion, recursion] = NVal(env, funcArg[(int)NARG_POS::SECOND]).ToBool();
514     if (!succRecursion) {
515         LOGE("get arg recursion fail");
516         return E_PARAMS;
517     }
518     registerParams.recursion = recursion;
519 
520     if (!NVal(env, funcArg[(int)NARG_POS::THIRD]).TypeIs(napi_function)) {
521         LOGE("Argument type mismatch");
522         return E_PARAMS;
523     }
524     napi_status status =
525         napi_create_reference(env, NVal(env, funcArg[(int)NARG_POS::THIRD]).val_, 1, &registerParams.cbOnRef);
526     if (status != napi_ok) {
527         LOGE("Create reference fail, status: %{public}d", status);
528         return E_PARAMS;
529     }
530 
531     return ERR_OK;
532 }
533 
RegisterToObs(napi_env env,const RegisterParams & registerParams)534 int32_t CloudSyncNapi::RegisterToObs(napi_env env, const RegisterParams &registerParams)
535 {
536     auto observer = make_shared<CloudNotifyObserver>(*g_listObj, registerParams.uri, registerParams.cbOnRef);
537     Uri uri(registerParams.uri);
538     auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
539     if (obsMgrClient == nullptr) {
540         LOGE("get DataObsMgrClient failed");
541         return E_SA_LOAD_FAILED;
542     }
543     sptr<ObserverImpl> obs = ObserverImpl::GetObserver(uri, observer);
544     if (obs == nullptr) {
545         LOGE("new ObserverImpl failed");
546         return E_INVAL_ARG;
547     }
548     ErrCode ret = obsMgrClient->RegisterObserverExt(uri, obs, registerParams.recursion);
549     if (ret != E_OK) {
550         LOGE("ObsMgr register fail");
551         ObserverImpl::DeleteObserver(uri, observer);
552         return E_INVAL_ARG;
553     }
554     lock_guard<mutex> lock(CloudSyncNapi::sOnOffMutex_);
555     g_listObj->observers_.push_back(observer);
556     return E_OK;
557 }
558 
RegisterChange(napi_env env,napi_callback_info info)559 napi_value CloudSyncNapi::RegisterChange(napi_env env, napi_callback_info info)
560 {
561     if (g_listObj == nullptr) {
562         g_listObj = make_unique<ChangeListenerNapi>(env);
563     }
564 
565     RegisterParams registerParams;
566     int32_t ret = GetRegisterParams(env, info, registerParams);
567     if (ret != ERR_OK) {
568         LOGE("Get Params fail");
569         NError(ret).ThrowErr(env);
570         return nullptr;
571     }
572 
573     if (CheckRef(env, registerParams.cbOnRef, *g_listObj, registerParams.uri)) {
574         ret = RegisterToObs(env, registerParams);
575         if (ret != E_OK) {
576             LOGE("Get Params fail");
577             NError(ret).ThrowErr(env);
578             return nullptr;
579         }
580     } else {
581         LOGE("Check Ref fail");
582         NError(E_PARAMS).ThrowErr(env);
583         napi_delete_reference(env, registerParams.cbOnRef);
584         return nullptr;
585     }
586     return NVal::CreateUndefined(env).val_;
587 }
588 
UnregisterFromObs(napi_env env,const string & uri)589 napi_value CloudSyncNapi::UnregisterFromObs(napi_env env, const string &uri)
590 {
591     auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
592     if (obsMgrClient == nullptr) {
593         LOGE("get DataObsMgrClient failed");
594         NError(E_SA_LOAD_FAILED).ThrowErr(env);
595         return nullptr;
596     }
597     std::vector<std::shared_ptr<CloudNotifyObserver>> offObservers;
598     {
599         lock_guard<mutex> lock(sOnOffMutex_);
600         for (auto iter = g_listObj->observers_.begin(); iter != g_listObj->observers_.end();) {
601             if (uri == (*iter)->uri_) {
602                 offObservers.push_back(*iter);
603                 vector<shared_ptr<CloudNotifyObserver>>::iterator tmp = iter;
604                 iter = g_listObj->observers_.erase(tmp);
605             } else {
606                 iter++;
607             }
608         }
609     }
610     for (auto observer : offObservers) {
611         if (!ObserverImpl::FindObserver(Uri(uri), observer)) {
612             LOGE("observer not exist");
613             NError(E_PARAMS).ThrowErr(env);
614             return nullptr;
615         }
616         sptr<ObserverImpl> obs = ObserverImpl::GetObserver(Uri(uri), observer);
617         if (obs == nullptr) {
618             LOGE("new observerimpl failed");
619             NError(E_PARAMS).ThrowErr(env);
620             return nullptr;
621         }
622         ErrCode ret = obsMgrClient->UnregisterObserverExt(Uri(uri), obs);
623         if (ret != ERR_OK) {
624             LOGE("call obs unregister fail");
625             NError(E_PARAMS).ThrowErr(env);
626             return nullptr;
627         }
628         ObserverImpl::DeleteObserver(Uri(uri), observer);
629     }
630     return NVal::CreateUndefined(env).val_;
631 }
632 
UnregisterChange(napi_env env,napi_callback_info info)633 napi_value CloudSyncNapi::UnregisterChange(napi_env env, napi_callback_info info)
634 {
635     if (g_listObj == nullptr || g_listObj->observers_.empty()) {
636         LOGI("no obs to unregister");
637         return nullptr;
638     }
639 
640     NFuncArg funcArg(env, info);
641     if (!funcArg.InitArgs(NARG_CNT::ONE)) {
642         LOGE("params number mismatch");
643         NError(E_PARAMS).ThrowErr(env);
644         return nullptr;
645     }
646     auto [succUri, uri, ignore] = NVal(env, funcArg[(int)NARG_POS::FIRST]).ToUTF8String();
647     if (!succUri || !CheckIsValidUri(Uri(uri.get()))) {
648         LOGE("get uri fail");
649         NError(E_PARAMS).ThrowErr(env);
650         return nullptr;
651     }
652 
653     return UnregisterFromObs(env, uri.get());
654 }
655 
SetClassName(const std::string classname)656 void CloudSyncNapi::SetClassName(const std::string classname)
657 {
658     className_ = classname;
659 }
660 
GetClassName()661 std::string CloudSyncNapi::GetClassName()
662 {
663     return className_;
664 }
665 
ToExport(std::vector<napi_property_descriptor> props)666 bool CloudSyncNapi::ToExport(std::vector<napi_property_descriptor> props)
667 {
668     std::string className = GetClassName();
669     auto [succ, classValue] =
670         NClass::DefineClass(exports_.env_, className, Constructor, std::move(props));
671     if (!succ) {
672         NError(E_GETRESULT).ThrowErr(exports_.env_);
673         LOGE("Failed to define CloudSyncNapi class");
674         return false;
675     }
676 
677     succ = NClass::SaveClass(exports_.env_, className, classValue);
678     if (!succ) {
679         NError(E_GETRESULT).ThrowErr(exports_.env_);
680         LOGE("Failed to save CloudSyncNapi class");
681         return false;
682     }
683 
684     return exports_.AddProp(className, classValue);
685 }
686 
Export()687 bool CloudSyncNapi::Export()
688 {
689     std::vector<napi_property_descriptor> props = {
690         NVal::DeclareNapiFunction("on", OnCallback),
691         NVal::DeclareNapiFunction("off", OffCallback),
692         NVal::DeclareNapiFunction("start", Start),
693         NVal::DeclareNapiFunction("stop", Stop),
694         NVal::DeclareNapiFunction("getFileSyncState", GetFileSyncState),
695         NVal::DeclareNapiFunction("optimizeStorage", OptimizeStorage),
696         NVal::DeclareNapiFunction("startOptimizeSpace", StartOptimizeStorage),
697         NVal::DeclareNapiFunction("stopOptimizeSpace", StopOptimizeStorage),
698     };
699     std::string className = GetClassName();
700     auto [succ, classValue] =
701         NClass::DefineClass(exports_.env_, className, CloudSyncNapi::Constructor, std::move(props));
702     if (!succ) {
703         NError(E_GETRESULT).ThrowErr(exports_.env_);
704         LOGE("Failed to define GallerySync class");
705         return false;
706     }
707 
708     succ = NClass::SaveClass(exports_.env_, className, classValue);
709     if (!succ) {
710         NError(E_GETRESULT).ThrowErr(exports_.env_);
711         LOGE("Failed to save GallerySync class");
712         return false;
713     }
714 
715     return exports_.AddProp(className, classValue);
716 }
717 
HandOptimizeStorageParams(napi_env env,napi_callback_info info,NFuncArg & funcArg,OptimizeSpaceOptions & optimizeOptions)718 int32_t CloudSyncNapi::HandOptimizeStorageParams(napi_env env, napi_callback_info info, NFuncArg &funcArg,
719     OptimizeSpaceOptions &optimizeOptions)
720 {
721     bool succ = false;
722     auto argv = NVal(env, funcArg[NARG_POS::FIRST]);
723     if (!argv.HasProp("totalSize") || !argv.HasProp("agingDays")) {
724         return ERR_BAD_VALUE;
725     }
726 
727     int64_t totalSize = 0;
728     int32_t agingDays = 0;
729     tie(succ, totalSize) = argv.GetProp("totalSize").ToInt64();
730     if (!succ) {
731         return ERR_BAD_VALUE;
732     }
733     tie(succ, agingDays) = argv.GetProp("agingDays").ToInt32();
734     if (!succ) {
735         return ERR_BAD_VALUE;
736     }
737 
738     if (!NVal(env, funcArg[(int)NARG_POS::SECOND]).TypeIs(napi_function)) {
739         LOGE("Argument type mismatch");
740         return ERR_BAD_VALUE;
741     }
742 
743     LOGI("totalSize:%{public}lld, agingDays:%{public}d", static_cast<long long>(totalSize), agingDays);
744     optimizeOptions.totalSize = totalSize;
745     optimizeOptions.agingDays = agingDays;
746     return ERR_OK;
747 }
748 
StartOptimizeStorage(napi_env env,napi_callback_info info)749 napi_value CloudSyncNapi::StartOptimizeStorage(napi_env env, napi_callback_info info)
750 {
751     LOGI("StartOptimizeStorage enter");
752     NFuncArg funcArg(env, info);
753     if (!funcArg.InitArgs(NARG_CNT::TWO)) {
754         NError(E_PARAMS).ThrowErr(env, "Number of arguments unmatched");
755         LOGE("Number of arguments unmatched");
756         return nullptr;
757     }
758     OptimizeSpaceOptions optimizeOptions {};
759     int32_t res = HandOptimizeStorageParams(env, info, funcArg, optimizeOptions);
760     if (res != ERR_OK) {
761         LOGE("hand paeams failed");
762         NError(E_PARAMS).ThrowErr(env);
763         return nullptr;
764     }
765 
766     auto callback = make_shared<CloudOptimizeCallbackImpl>(env, NVal(env, funcArg[(int)NARG_POS::SECOND]));
767     auto cbExec = [optimizeOptions, callback]() -> NError {
768         int32_t ret = CloudSyncManager::GetInstance().OptimizeStorage(optimizeOptions, callback);
769         if (ret != E_OK) {
770             LOGE("StartOptimizeStorage error, result: %{public}d", ret);
771             return NError(Convert2JsErrNum(ret));
772         }
773         return NError(ERRNO_NOERR);
774     };
775 
776     auto cbComplete = [](napi_env env, NError err) -> NVal {
777         if (err) {
778             return {env, err.GetNapiErr(env)};
779         }
780         return NVal::CreateUndefined(env);
781     };
782 
783     std::string procedureName = "StartOptimizeStorage";
784     auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::THREE));
785     return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
786 }
787 
StopOptimizeStorage(napi_env env,napi_callback_info info)788 napi_value CloudSyncNapi::StopOptimizeStorage(napi_env env, napi_callback_info info)
789 {
790     LOGI("StopOptimizeStorage enter");
791     int32_t ret = CloudSyncManager::GetInstance().StopOptimizeStorage();
792     if (ret != E_OK) {
793         LOGE("StopOptimizeStorage error, result: %{public}d", ret);
794         NError(Convert2JsErrNum(ret)).ThrowErr(env);
795         return nullptr;
796     }
797 
798     return NVal::CreateUndefined(env).val_;
799 }
800 
OptimizeStorage(napi_env env,napi_callback_info info)801 napi_value CloudSyncNapi::OptimizeStorage(napi_env env, napi_callback_info info)
802 {
803     NFuncArg funcArg(env, info);
804     if (!funcArg.InitArgs(NARG_CNT::ZERO, NARG_CNT::ONE)) {
805         NError(E_PARAMS).ThrowErr(env);
806     }
807 
808     OptimizeSpaceOptions optimizeOptions {};
809     optimizeOptions.totalSize = 0;
810     optimizeOptions.agingDays = AGING_DAYS;
811 
812     auto cbExec = [optimizeOptions]() -> NError {
813         int32_t ret = CloudSyncManager::GetInstance().OptimizeStorage(optimizeOptions);
814         if (ret != E_OK) {
815             LOGE("OptimizeStorage error, result: %{public}d", ret);
816             return NError(Convert2JsErrNum(ret));
817         }
818         return NError(ERRNO_NOERR);
819     };
820 
821     auto cbComplete = [](napi_env env, NError err) -> NVal {
822         if (err) {
823             return {env, err.GetNapiErr(env)};
824         }
825         return NVal::CreateUndefined(env);
826     };
827 
828     std::string procedureName = "OptimizeStorage";
829     auto asyncWork = GetPromiseOrCallBackWork(env, funcArg, static_cast<size_t>(NARG_CNT::TWO));
830     return asyncWork == nullptr ? nullptr : asyncWork->Schedule(procedureName, cbExec, cbComplete).val_;
831 }
832 
GetFileSyncState(napi_env env,napi_callback_info info)833 napi_value CloudSyncNapi::GetFileSyncState(napi_env env, napi_callback_info info)
834 {
835     NFuncArg funcArg(env, info);
836     if (!funcArg.InitArgs(static_cast<int>(NARG_CNT::ONE))) {
837         HILOGE("Number of arguments unmatched");
838         NError(EINVAL).ThrowErr(env);
839         return nullptr;
840     }
841     bool succ = false;
842     std::unique_ptr<char []> path;
843     tie(succ, path, std::ignore) = NVal(env, funcArg[static_cast<int>(NARG_POS::FIRST)]).ToUTF8String();
844     if (!succ) {
845         HILOGE("Invalid path");
846         NError(EINVAL).ThrowErr(env);
847         return nullptr;
848     }
849     Uri uri(path.get());
850     std::string sandBoxPath = uri.GetPath();
851     std::string xattrKey = "user.cloud.filestatus";
852     auto xattrValueSize = getxattr(sandBoxPath.c_str(), xattrKey.c_str(), nullptr, 0);
853     if (xattrValueSize < 0) {
854         NError(EINVAL).ThrowErr(env);
855         return nullptr;
856     }
857     std::unique_ptr<char[]> xattrValue = std::make_unique<char[]>((long)xattrValueSize + 1);
858     if (xattrValue == nullptr) {
859         NError(EINVAL).ThrowErr(env);
860         return nullptr;
861     }
862     xattrValueSize = getxattr(sandBoxPath.c_str(), xattrKey.c_str(), xattrValue.get(), xattrValueSize);
863     if (xattrValueSize <= 0) {
864         NError(EINVAL).ThrowErr(env);
865         return nullptr;
866     }
867     int32_t fileStatus = std::stoi(xattrValue.get());
868     int32_t val;
869     if (fileStatus == FileSync::FILESYNC_TO_BE_UPLOADED || fileStatus == FileSync::FILESYNC_UPLOADING ||
870         fileStatus == FileSync::FILESYNC_UPLOAD_FAILURE || fileStatus == FileSync::FILESYNC_UPLOAD_SUCCESS) {
871         val = statusMap[fileStatus];
872     } else {
873         val = FileSyncState::FILESYNCSTATE_COMPLETED;
874     }
875     return NVal::CreateInt32(env, val).val_;
876 }
877 
OnChange(CloudChangeListener & listener,const napi_ref cbRef)878 void ChangeListenerNapi::OnChange(CloudChangeListener &listener, const napi_ref cbRef)
879 {
880     uv_loop_s *loop = nullptr;
881     napi_get_uv_event_loop(env_, &loop);
882     if (loop == nullptr) {
883         return;
884     }
885 
886     uv_work_t *work = new (nothrow) uv_work_t;
887     if (work == nullptr) {
888         return;
889     }
890 
891     UvChangeMsg *msg = new (std::nothrow) UvChangeMsg(env_, cbRef, listener.changeInfo, listener.strUri);
892     if (msg == nullptr) {
893         delete work;
894         return;
895     }
896     if (!listener.changeInfo.uris_.empty()) {
897         if (static_cast<NotifyType>(listener.changeInfo.changeType_) == NotifyType::NOTIFY_NONE) {
898             LOGE("changeInfo.changeType_ is other");
899             delete msg;
900             delete work;
901             return;
902         }
903         if (msg->changeInfo_.size_ > 0) {
904             msg->data_ = (uint8_t *)malloc(msg->changeInfo_.size_);
905             if (msg->data_ == nullptr) {
906                 LOGE("new msg->data failed");
907                 delete msg;
908                 delete work;
909                 return;
910             }
911             int copyRet = memcpy_s(msg->data_, msg->changeInfo_.size_, msg->changeInfo_.data_, msg->changeInfo_.size_);
912             if (copyRet != 0) {
913                 LOGE("Parcel data copy failed, err = %{public}d", copyRet);
914                 free(msg->data_);
915                 delete msg;
916                 delete work;
917                 return;
918             }
919         }
920     }
921     work->data = reinterpret_cast<void *>(msg);
922 
923     int ret = UvQueueWork(loop, work);
924     if (ret != 0) {
925         LOGE("Failed to execute libuv work queue, ret: %{public}d", ret);
926         free(msg->data_);
927         delete msg;
928         delete work;
929     }
930 }
931 
UvQueueWork(uv_loop_s * loop,uv_work_t * work)932 int32_t ChangeListenerNapi::UvQueueWork(uv_loop_s *loop, uv_work_t *work)
933 {
934     return uv_queue_work(
935         loop, work,
936         [](uv_work_t *w) {
937             if (w == nullptr) {
938                 LOGE("Invalid uv_work");
939             }
940         },
941         [](uv_work_t *w, int s) {
942             // js thread
943             if (w == nullptr) {
944                 return;
945             }
946 
947             UvChangeMsg *msg = reinterpret_cast<UvChangeMsg *>(w->data);
948             do {
949                 if (msg == nullptr) {
950                     LOGE("UvChangeMsg is null");
951                     break;
952                 }
953                 napi_env env = msg->env_;
954                 napi_handle_scope scope = nullptr;
955                 napi_open_handle_scope(env, &scope);
956 
957                 napi_value jsCallback = nullptr;
958                 napi_status status = napi_get_reference_value(env, msg->ref_, &jsCallback);
959                 if (status != napi_ok) {
960                     napi_close_handle_scope(env, scope);
961                     LOGE("Create reference fail, status: %{public}d", status);
962                     break;
963                 }
964                 napi_value retVal = nullptr;
965                 napi_value result[ARGS_ONE];
966                 result[PARAM0] = ChangeListenerNapi::SolveOnChange(env, msg);
967                 if (result[PARAM0] == nullptr) {
968                     napi_close_handle_scope(env, scope);
969                     break;
970                 }
971                 napi_call_function(env, nullptr, jsCallback, ARGS_ONE, result, &retVal);
972                 napi_close_handle_scope(env, scope);
973                 if (status != napi_ok) {
974                     LOGE("CallJs napi_call_function fail, status: %{public}d", status);
975                     break;
976                 }
977             } while (0);
978             free(msg->data_);
979             delete msg;
980             delete w;
981         });
982 }
983 
SetValueArray(const napi_env & env,const char * fieldStr,const std::list<Uri> listValue,napi_value & result)984 static napi_status SetValueArray(const napi_env &env, const char *fieldStr, const std::list<Uri> listValue,
985     napi_value &result)
986 {
987     napi_value value = nullptr;
988     napi_status status = napi_create_array_with_length(env, listValue.size(), &value);
989     if (status != napi_ok) {
990         LOGE("Create array error! field: %{public}s", fieldStr);
991         return status;
992     }
993     int elementIndex = 0;
994     for (auto uri : listValue) {
995         napi_value uriRet = nullptr;
996         napi_create_string_utf8(env, uri.ToString().c_str(), NAPI_AUTO_LENGTH, &uriRet);
997         status = napi_set_element(env, value, elementIndex++, uriRet);
998         if (status != napi_ok) {
999             LOGE("Set lite item failed, error: %d", status);
1000         }
1001     }
1002     status = napi_set_named_property(env, result, fieldStr, value);
1003     if (status != napi_ok) {
1004         LOGE("Set array named property error! field: %{public}s", fieldStr);
1005     }
1006 
1007     return status;
1008 }
1009 
SetValueInt32(const napi_env & env,const char * fieldStr,const int intValue,napi_value & result)1010 static napi_status SetValueInt32(const napi_env &env, const char *fieldStr, const int intValue, napi_value &result)
1011 {
1012     napi_value value;
1013     napi_status status = napi_create_int32(env, intValue, &value);
1014     if (status != napi_ok) {
1015         LOGE("Set value create int32 error! field: %{public}s", fieldStr);
1016         return status;
1017     }
1018     status = napi_set_named_property(env, result, fieldStr, value);
1019     if (status != napi_ok) {
1020         LOGE("Set int32 named property error! field: %{public}s", fieldStr);
1021     }
1022     return status;
1023 }
1024 
SetIsDir(const napi_env & env,const shared_ptr<MessageParcel> parcel,napi_value & result)1025 static napi_status SetIsDir(const napi_env &env, const shared_ptr<MessageParcel> parcel, napi_value &result)
1026 {
1027     uint32_t len = 0;
1028     napi_status status = napi_invalid_arg;
1029     if (!parcel->ReadUint32(len)) {
1030         LOGE("Failed to read sub uri list length");
1031         return status;
1032     }
1033     napi_value isDirArray = nullptr;
1034     napi_create_array_with_length(env, len, &isDirArray);
1035     int elementIndex = 0;
1036     if (len > READ_SIZE) {
1037         return napi_invalid_arg;
1038     }
1039     for (uint32_t i = 0; i < len; i++) {
1040         bool isDir = parcel->ReadBool();
1041         napi_value isDirRet = nullptr;
1042         napi_get_boolean(env, isDir, &isDirRet);
1043         napi_set_element(env, isDirArray, elementIndex++, isDirRet);
1044     }
1045     status = napi_set_named_property(env, result, "isDirectory", isDirArray);
1046     if (status != napi_ok) {
1047         LOGE("Set subUri named property error!");
1048     }
1049     return status;
1050 }
1051 
SolveOnChange(napi_env env,UvChangeMsg * msg)1052 napi_value ChangeListenerNapi::SolveOnChange(napi_env env, UvChangeMsg *msg)
1053 {
1054     static napi_value result;
1055     if (msg->changeInfo_.uris_.empty()) {
1056         napi_get_undefined(env, &result);
1057         return result;
1058     }
1059     napi_create_object(env, &result);
1060     SetValueArray(env, "uris", msg->changeInfo_.uris_, result);
1061     if (msg->data_ != nullptr && msg->changeInfo_.size_ > 0) {
1062         uint8_t *parcelData = (uint8_t *)malloc(msg->changeInfo_.size_);
1063         if (parcelData == nullptr) {
1064             LOGE("new parcelData failed");
1065             return nullptr;
1066         }
1067         int copyRet = memcpy_s(parcelData, msg->changeInfo_.size_, msg->data_, msg->changeInfo_.size_);
1068         if (copyRet != 0) {
1069             LOGE("Parcel data copy failed, err = %{public}d", copyRet);
1070             free(parcelData);
1071             return nullptr;
1072         }
1073         shared_ptr<MessageParcel> parcel = make_shared<MessageParcel>();
1074         if (parcel->ParseFrom(reinterpret_cast<uintptr_t>(parcelData), msg->changeInfo_.size_)) {
1075             napi_status status = SetIsDir(env, parcel, result);
1076             if (status != napi_ok) {
1077                 LOGE("Set subArray named property error! field: subUris");
1078                 return nullptr;
1079             }
1080         } else {
1081             free(parcelData);
1082         }
1083     }
1084     SetValueInt32(env, "type", (int)msg->changeInfo_.changeType_, result);
1085     return result;
1086 }
1087 
OnChange()1088 void CloudNotifyObserver::OnChange() {}
1089 
OnchangeExt(const AAFwk::ChangeInfo & changeInfo)1090 void CloudNotifyObserver::OnchangeExt(const AAFwk::ChangeInfo &changeInfo)
1091 {
1092     CloudChangeListener listener;
1093     listener.changeInfo = changeInfo;
1094     listener.strUri = uri_;
1095     listObj_.OnChange(listener, ref_);
1096 }
1097 
1098 } // namespace OHOS::FileManagement::CloudSync
1099