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