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