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 ®isterParams)
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, ®isterParams.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 ®isterParams)
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