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