1 /*
2 * Copyright (c) 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 "clouddisk_notify.h"
17 #include "clouddisk_notify_utils.h"
18 #include "clouddisk_rdbstore.h"
19 #include "dfs_error.h"
20 #include "ffrt_inner.h"
21 #include "file_column.h"
22 #include "securec.h"
23 #include "uri.h"
24 #include "utils_log.h"
25
26 namespace OHOS::FileManagement::CloudDisk {
27 const string BUNDLENAME_FLAG = "<BundleName>";
28 const string CLOUDDISK_URI_PREFIX = "file://<BundleName>/data/storage/el2/cloud";
29 const string BACKFLASH = "/";
30 const string RECYCLE_BIN = ".trash";
31 constexpr uint32_t MAX_NOTIFY_LIST_SIZE = 32;
32 constexpr size_t MNOTIFY_TIME_INTERVAL = 500;
33 static pair<string, shared_ptr<CloudDiskRdbStore>> cacheRdbStore("", nullptr);
34 std::mutex cacheRdbMutex;
35
GetInstance()36 CloudDiskNotify &CloudDiskNotify::GetInstance()
37 {
38 static CloudDiskNotify instance_;
39 return instance_;
40 }
41
GetRdbStore(const string &bundleName, int32_t userId)42 static shared_ptr<CloudDiskRdbStore> GetRdbStore(const string &bundleName, int32_t userId)
43 {
44 std::lock_guard<std::mutex> lock(cacheRdbMutex);
45 string storeKey = bundleName + to_string(userId);
46 if (cacheRdbStore.first == storeKey) {
47 return cacheRdbStore.second;
48 }
49 cacheRdbStore.first = storeKey;
50 cacheRdbStore.second = make_shared<CloudDiskRdbStore>(bundleName, userId);
51 return cacheRdbStore.second;
52 }
53
GetTrashNotifyData(const NotifyParamDisk ¶mDisk, NotifyData ¬ifyData)54 static int32_t GetTrashNotifyData(const NotifyParamDisk ¶mDisk, NotifyData ¬ifyData)
55 {
56 if (paramDisk.inoPtr == nullptr) {
57 return E_INVAL_ARG;
58 }
59 string realPrefix = CLOUDDISK_URI_PREFIX;
60 realPrefix.replace(CLOUDDISK_URI_PREFIX.find(BUNDLENAME_FLAG), BUNDLENAME_FLAG.length(),
61 paramDisk.inoPtr->bundleName);
62 notifyData.uri = realPrefix + BACKFLASH + RECYCLE_BIN + BACKFLASH + paramDisk.inoPtr->fileName;
63 return E_OK;
64 }
65
GetTrashNotifyData(const CacheNode &cacheNode, const ParamServiceOther ¶mOthers, NotifyData ¬ifyData)66 static int32_t GetTrashNotifyData(const CacheNode &cacheNode, const ParamServiceOther ¶mOthers,
67 NotifyData ¬ifyData)
68 {
69 string realPrefix = CLOUDDISK_URI_PREFIX;
70 realPrefix.replace(CLOUDDISK_URI_PREFIX.find(BUNDLENAME_FLAG), BUNDLENAME_FLAG.length(), paramOthers.bundleName);
71 notifyData.uri = realPrefix + BACKFLASH + RECYCLE_BIN + BACKFLASH + cacheNode.fileName;
72 return E_OK;
73 }
74
TrashUriAddRowId(shared_ptr<CloudDiskRdbStore> rdbStore, const string &cloudId, string &uri)75 static int32_t TrashUriAddRowId(shared_ptr<CloudDiskRdbStore> rdbStore, const string &cloudId, string &uri)
76 {
77 int64_t rowId = 0;
78 int32_t ret = rdbStore->GetRowId(cloudId, rowId);
79 if (ret != E_OK) {
80 LOGE("Get rowId fail, ret: %{public}d", ret);
81 return ret;
82 }
83 uri = uri + "_" + std::to_string(rowId);
84 return E_OK;
85 }
86
GetDataInner(const NotifyParamDisk ¶mDisk, NotifyData ¬ifyData)87 static int32_t GetDataInner(const NotifyParamDisk ¶mDisk, NotifyData ¬ifyData)
88 {
89 int32_t ret;
90 if (paramDisk.inoPtr != nullptr) {
91 ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func,
92 paramDisk.inoPtr, notifyData);
93 } else {
94 ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func,
95 paramDisk.ino, notifyData);
96 }
97 return ret;
98 }
99
GetDataInnerWithName(const NotifyParamDisk ¶mDisk, NotifyData ¬ifyData)100 static int32_t GetDataInnerWithName(const NotifyParamDisk ¶mDisk, NotifyData ¬ifyData)
101 {
102 if (paramDisk.name.empty()) {
103 return E_INVAL_ARG;
104 }
105 int32_t ret;
106 if (paramDisk.inoPtr != nullptr) {
107 ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func,
108 paramDisk.inoPtr, paramDisk.name, notifyData);
109 } else {
110 ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func,
111 paramDisk.ino, paramDisk.name, notifyData);
112 }
113 return ret;
114 }
115
HandleSetAttr(const NotifyParamDisk ¶mDisk)116 static void HandleSetAttr(const NotifyParamDisk ¶mDisk)
117 {
118 NotifyData notifyData;
119 if (GetDataInner(paramDisk, notifyData) != E_OK) {
120 return;
121 }
122 notifyData.type = NotifyType::NOTIFY_MODIFIED;
123 notifyData.isLocalOperation = true;
124 CloudDiskNotify::GetInstance().AddNotify(notifyData);
125 }
126
HandleRecycleRestore(const NotifyParamDisk ¶mDisk)127 static void HandleRecycleRestore(const NotifyParamDisk ¶mDisk)
128 {
129 NotifyData trashNotifyData;
130 if (GetTrashNotifyData(paramDisk, trashNotifyData) != E_OK) {
131 LOGE("Get trash notify data fail");
132 return;
133 }
134 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramDisk.inoPtr->bundleName, paramDisk.data->userId);
135 if (rdbStore == nullptr) {
136 LOGE("Get rdb store fail, bundleName: %{public}s", paramDisk.inoPtr->bundleName.c_str());
137 return;
138 }
139 if (TrashUriAddRowId(rdbStore, paramDisk.inoPtr->cloudId, trashNotifyData.uri) != E_OK) {
140 return;
141 }
142
143 NotifyData originNotifyData;
144 if (GetDataInner(paramDisk, originNotifyData) != E_OK) {
145 LOGE("Get origin notify data fail");
146 return;
147 }
148
149 NotifyData notifyData;
150 notifyData.type = NotifyType::NOTIFY_RENAMED;
151 notifyData.isDir = originNotifyData.isDir;
152
153 if (paramDisk.opsType == NotifyOpsType::DAEMON_RECYCLE) {
154 notifyData.uri = trashNotifyData.uri;
155 notifyData.extraUri = originNotifyData.uri;
156 }
157 if (paramDisk.opsType == NotifyOpsType::DAEMON_RESTORE) {
158 notifyData.uri = originNotifyData.uri;
159 notifyData.extraUri = trashNotifyData.uri;
160 }
161 notifyData.isLocalOperation = true;
162 CloudDiskNotify::GetInstance().AddNotify(notifyData);
163 }
164
HandleWrite(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)165 static void HandleWrite(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)
166 {
167 NotifyData notifyData;
168 if (GetDataInner(paramDisk, notifyData) != E_OK) {
169 return;
170 }
171
172 if (paramOthers.dirtyType == static_cast<int32_t>(DirtyType::TYPE_NO_NEED_UPLOAD)) {
173 notifyData.type = NotifyType::NOTIFY_ADDED;
174 }
175 if (paramOthers.fileDirty == CLOUD_DISK_FILE_WRITE) {
176 notifyData.type = NotifyType::NOTIFY_FILE_CHANGED;
177 }
178 notifyData.isLocalOperation = true;
179 CloudDiskNotify::GetInstance().AddNotify(notifyData);
180 }
181
HandleMkdir(const NotifyParamDisk ¶mDisk)182 static void HandleMkdir(const NotifyParamDisk ¶mDisk)
183 {
184 NotifyData notifyData;
185 if (GetDataInnerWithName(paramDisk, notifyData) != E_OK) {
186 return;
187 }
188 notifyData.type = NotifyType::NOTIFY_ADDED;
189 notifyData.isDir = true;
190 notifyData.isLocalOperation = true;
191 CloudDiskNotify::GetInstance().AddNotify(notifyData);
192 }
193
HandleUnlink(const NotifyParamDisk ¶mDisk)194 static void HandleUnlink(const NotifyParamDisk ¶mDisk)
195 {
196 NotifyData notifyData;
197 if (GetDataInnerWithName(paramDisk, notifyData) != E_OK) {
198 return;
199 }
200 notifyData.type = NotifyType::NOTIFY_DELETED;
201 notifyData.isDir = paramDisk.opsType == NotifyOpsType::DAEMON_RMDIR;
202 notifyData.isLocalOperation = true;
203 CloudDiskNotify::GetInstance().AddNotify(notifyData);
204 }
205
HandleRename(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)206 static void HandleRename(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)
207 {
208 NotifyData oldNotifyData;
209 NotifyData newNotifyData;
210 int32_t ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func, paramDisk.ino,
211 paramDisk.name, oldNotifyData);
212 if (ret != E_OK) {
213 LOGE("get notify data fail, name: %{public}s", GetAnonyString(paramDisk.name).c_str());
214 return;
215 }
216 ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func, paramDisk.newIno,
217 paramDisk.newName, newNotifyData);
218 if (ret != E_OK) {
219 LOGE("get new notify data fail, name: %{public}s", GetAnonyString(paramDisk.newName).c_str());
220 return;
221 }
222 NotifyData notifyData;
223 notifyData.uri = newNotifyData.uri;
224 notifyData.extraUri = oldNotifyData.uri;
225 notifyData.isDir = paramOthers.isDir;
226 notifyData.type = NotifyType::NOTIFY_RENAMED;
227 notifyData.isLocalOperation = true;
228 CloudDiskNotify::GetInstance().AddNotify(notifyData);
229 }
230
TryNotify(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)231 void CloudDiskNotify::TryNotify(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)
232 {
233 switch (paramDisk.opsType) {
234 case NotifyOpsType::DAEMON_SETATTR:
235 case NotifyOpsType::DAEMON_SETXATTR:
236 HandleSetAttr(paramDisk);
237 break;
238 case NotifyOpsType::DAEMON_RECYCLE:
239 case NotifyOpsType::DAEMON_RESTORE:
240 HandleRecycleRestore(paramDisk);
241 break;
242 case NotifyOpsType::DAEMON_MKDIR:
243 HandleMkdir(paramDisk);
244 break;
245 case NotifyOpsType::DAEMON_RMDIR:
246 case NotifyOpsType::DAEMON_UNLINK:
247 HandleUnlink(paramDisk);
248 break;
249 case NotifyOpsType::DAEMON_RENAME:
250 HandleRename(paramDisk, paramOthers);
251 break;
252 case NotifyOpsType::DAEMON_WRITE:
253 HandleWrite(paramDisk, paramOthers);
254 break;
255 default:
256 LOGE("bad ops, opsType: %{public}d", paramDisk.opsType);
257 break;
258 }
259 }
260
HandleInsert(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)261 static void HandleInsert(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
262 {
263 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
264 if (rdbStore == nullptr) {
265 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
266 return;
267 }
268 NotifyData notifyData;
269 int32_t ret;
270 if (paramService.node.isRecycled) {
271 ret = GetTrashNotifyData(paramService.node, paramOthers, notifyData);
272 if (TrashUriAddRowId(rdbStore, paramService.cloudId, notifyData.uri) != E_OK) {
273 return;
274 }
275 notifyData.isDir = paramService.node.isDir == TYPE_DIR_STR;
276 } else {
277 ret = rdbStore->GetNotifyData(paramService.node, notifyData);
278 }
279 if (ret == E_OK) {
280 notifyData.type = NotifyType::NOTIFY_ADDED;
281 CloudDiskNotify::GetInstance().AddNotify(notifyData);
282 }
283 }
284
HandleUpdate(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)285 static void HandleUpdate(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
286 {
287 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
288 if (rdbStore == nullptr) {
289 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
290 return;
291 }
292 NotifyData inNotifyData;
293 NotifyData notifyData;
294 CacheNode curNode{paramService.cloudId};
295 if (rdbStore->GetCurNode(paramService.cloudId, curNode) == E_OK &&
296 rdbStore->GetNotifyUri(curNode, notifyData.uri) == E_OK) {
297 notifyData.type = NotifyType::NOTIFY_MODIFIED;
298 notifyData.isDir = curNode.isDir == TYPE_DIR_STR;
299 if (paramService.notifyType == NotifyType::NOTIFY_NONE &&
300 rdbStore->GetNotifyData(paramService.node, inNotifyData) == E_OK) {
301 if (inNotifyData.uri != notifyData.uri) {
302 notifyData.type = NotifyType::NOTIFY_DELETED;
303 inNotifyData.type = NotifyType::NOTIFY_RENAMED;
304 }
305 }
306 }
307 CloudDiskNotify::GetInstance().AddNotify(notifyData);
308 CloudDiskNotify::GetInstance().AddNotify(inNotifyData);
309 }
310
HandleUpdateRecycle(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)311 static void HandleUpdateRecycle(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
312 {
313 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
314 if (rdbStore == nullptr) {
315 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
316 return;
317 }
318 NotifyData trashNotifyData;
319 GetTrashNotifyData(paramService.node, paramOthers, trashNotifyData);
320 if (TrashUriAddRowId(rdbStore, paramService.cloudId, trashNotifyData.uri) != E_OK) {
321 return;
322 }
323
324 NotifyData originNotifyData;
325 if (rdbStore->GetNotifyData(paramService.node, originNotifyData) != E_OK) {
326 LOGE("Get origin notify data fail");
327 return;
328 }
329 trashNotifyData.isDir = paramService.node.isDir == TYPE_DIR_STR;
330 originNotifyData.isDir = trashNotifyData.isDir;
331 if (paramService.node.isRecycled) {
332 trashNotifyData.type = NotifyType::NOTIFY_ADDED;
333 originNotifyData.type = NotifyType::NOTIFY_DELETED;
334 } else {
335 trashNotifyData.type = NotifyType::NOTIFY_DELETED;
336 originNotifyData.type = NotifyType::NOTIFY_ADDED;
337 }
338 CloudDiskNotify::GetInstance().AddNotify(trashNotifyData);
339 CloudDiskNotify::GetInstance().AddNotify(originNotifyData);
340 }
341
HandleDelete(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)342 static void HandleDelete(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
343 {
344 if (paramService.cloudId.empty()) {
345 NotifyData notifyData{"", false, NotifyType::NOTIFY_DELETED};
346 notifyData.uri = CLOUDDISK_URI_PREFIX;
347 notifyData.uri.replace(CLOUDDISK_URI_PREFIX.find(BUNDLENAME_FLAG), BUNDLENAME_FLAG.length(),
348 paramOthers.bundleName);
349 notifyData.isDir = true;
350 CloudDiskNotify::GetInstance().AddNotify(notifyData);
351 } else {
352 for (auto notifyData : (paramOthers.notifyDataList)) {
353 CloudDiskNotify::GetInstance().AddNotify(notifyData);
354 }
355 }
356 }
357
HandleDeleteBatch(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)358 static void HandleDeleteBatch(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
359 {
360 for (auto notifyData : (paramOthers.notifyDataList)) {
361 CloudDiskNotify::GetInstance().AddNotify(notifyData);
362 }
363 }
364
TryNotifyService(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)365 void CloudDiskNotify::TryNotifyService(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
366 {
367 switch (paramService.opsType) {
368 case NotifyOpsType::SERVICE_INSERT:
369 HandleInsert(paramService, paramOthers);
370 break;
371 case NotifyOpsType::SERVICE_UPDATE:
372 HandleUpdate(paramService, paramOthers);
373 break;
374 case NotifyOpsType::SERVICE_UPDATE_RECYCLE:
375 HandleUpdateRecycle(paramService, paramOthers);
376 break;
377 case NotifyOpsType::SERVICE_DELETE:
378 HandleDelete(paramService, paramOthers);
379 break;
380 case NotifyOpsType::SERVICE_DELETE_BATCH:
381 HandleDeleteBatch(paramService, paramOthers);
382 break;
383 default:
384 LOGE("bad ops, opsType: %{public}d", paramService.opsType);
385 break;
386 }
387 }
388
GetDeleteNotifyData(const vector<NativeRdb::ValueObject> &deleteIds, vector<NotifyData> ¬ifyDataList, const ParamServiceOther ¶mOthers)389 int32_t CloudDiskNotify::GetDeleteNotifyData(const vector<NativeRdb::ValueObject> &deleteIds,
390 vector<NotifyData> ¬ifyDataList,
391 const ParamServiceOther ¶mOthers)
392 {
393 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
394 if (rdbStore == nullptr) {
395 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
396 return E_RDB;
397 }
398 for (auto deleteId : deleteIds) {
399 NotifyData notifyData{"", false, NotifyType::NOTIFY_DELETED};
400 string cloudId = static_cast<string>(deleteId);
401 CacheNode curNode{cloudId};
402 if (rdbStore->GetCurNode(cloudId, curNode) == E_OK && rdbStore->GetNotifyUri(curNode, notifyData.uri) == E_OK) {
403 notifyData.isDir = curNode.isDir == TYPE_DIR_STR;
404 notifyDataList.push_back(notifyData);
405 }
406 }
407 return E_OK;
408 }
409
AddNotify(const NotifyData ¬ifyData)410 void CloudDiskNotify::AddNotify(const NotifyData ¬ifyData)
411 {
412 LOGD("push cur notify into list type: %{public}d, uri: %{public}s, isDir: %{public}d", notifyData.type,
413 GetAnonyString(notifyData.uri).c_str(), notifyData.isDir);
414 if (notifyData.type == NotifyType::NOTIFY_NONE) {
415 return;
416 }
417
418 auto notifyFunc = [notifyData] {
419 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
420 if (obsMgrClient == nullptr) {
421 LOGE("obsMgrClient is null");
422 return;
423 }
424 Parcel parcel;
425 parcel.WriteUint32(1);
426 parcel.WriteBool(notifyData.isDir);
427 parcel.WriteBool(notifyData.isLocalOperation);
428 parcel.WriteString(notifyData.extraUri);
429 uintptr_t buf = parcel.GetData();
430 if (parcel.GetDataSize() == 0) {
431 LOGE("parcel.getDataSize fail");
432 return;
433 }
434
435 auto *uBuf = new (std::nothrow) uint8_t[parcel.GetDataSize()];
436 if (uBuf == nullptr) {
437 return;
438 }
439 int32_t ret = memcpy_s(uBuf, parcel.GetDataSize(), reinterpret_cast<uint8_t *>(buf), parcel.GetDataSize());
440 if (ret != 0) {
441 LOGE("Parcel Data copy failed, err: %{public}d", ret);
442 delete[] uBuf;
443 return;
444 }
445 ChangeInfo changeInfo({static_cast<ChangeInfo::ChangeType>(notifyData.type), {Uri(notifyData.uri)}, uBuf,
446 parcel.GetDataSize()});
447 obsMgrClient->NotifyChangeExt(changeInfo);
448 delete[] uBuf;
449 };
450 ffrt::thread(notifyFunc).detach();
451 }
452
NotifyChangeOuter()453 void CloudDiskNotify::NotifyChangeOuter()
454 {
455 LOGD("Start Notify Outer");
456 list<CacheNotifyInfo> tmpNfList_;
457 {
458 lock_guard<mutex> lock(mutex_);
459 if (nfList_.empty()) {
460 return;
461 }
462 nfList_.swap(tmpNfList_);
463 nfList_.clear();
464 }
465
466 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
467 if (obsMgrClient == nullptr) {
468 LOGE("obsMgrClient is null");
469 return;
470 }
471 for (auto cacheNode = tmpNfList_.begin(); cacheNode != tmpNfList_.end(); ++cacheNode) {
472 Parcel parcel;
473 parcel.WriteUint32(static_cast<uint32_t>(cacheNode->isDirList.size()));
474 for (auto isDir = cacheNode->isDirList.begin(); isDir != cacheNode->isDirList.end(); ++isDir) {
475 parcel.WriteBool(*isDir);
476 }
477 uintptr_t buf = parcel.GetData();
478 if (parcel.GetDataSize() == 0) {
479 LOGE("parcel.getDataSize fail");
480 return;
481 }
482
483 auto *uBuf = new (std::nothrow) uint8_t[parcel.GetDataSize()];
484 if (uBuf == nullptr) {
485 return;
486 }
487 int32_t ret = memcpy_s(uBuf, parcel.GetDataSize(), reinterpret_cast<uint8_t *>(buf), parcel.GetDataSize());
488 if (ret != 0) {
489 LOGE("Parcel Data copy failed, err: %{public}d", ret);
490 delete[] uBuf;
491 return;
492 }
493 ChangeInfo changeInfo({static_cast<ChangeInfo::ChangeType>(cacheNode->notifyType), cacheNode->uriList, uBuf,
494 parcel.GetDataSize()});
495 obsMgrClient->NotifyChangeExt(changeInfo);
496 delete[] uBuf;
497 }
498 }
499 } // namespace OHOS::FileManagement::CloudDisk
500