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 & paramDisk,NotifyData & notifyData)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 & paramOthers,NotifyData & notifyData)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 & paramDisk,NotifyData & notifyData)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 & paramDisk,NotifyData & notifyData)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 & paramDisk)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 & paramDisk)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 & paramDisk,const ParamDiskOthers & paramOthers)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 & paramDisk)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 & paramDisk)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 & paramDisk,const ParamDiskOthers & paramOthers)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 & paramDisk,const ParamDiskOthers & paramOthers)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 & paramService,const ParamServiceOther & paramOthers)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 & paramService,const ParamServiceOther & paramOthers)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 & paramService,const ParamServiceOther & paramOthers)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 if (GetTrashNotifyData(paramService.node, paramOthers, trashNotifyData) != E_OK) {
320 LOGE("Get trash notify data fail");
321 return;
322 }
323 if (TrashUriAddRowId(rdbStore, paramService.cloudId, trashNotifyData.uri) != E_OK) {
324 return;
325 }
326
327 NotifyData originNotifyData;
328 if (rdbStore->GetNotifyData(paramService.node, originNotifyData) != E_OK) {
329 LOGE("Get origin notify data fail");
330 return;
331 }
332 trashNotifyData.isDir = paramService.node.isDir == TYPE_DIR_STR;
333 originNotifyData.isDir = trashNotifyData.isDir;
334 if (paramService.node.isRecycled) {
335 trashNotifyData.type = NotifyType::NOTIFY_ADDED;
336 originNotifyData.type = NotifyType::NOTIFY_DELETED;
337 } else {
338 trashNotifyData.type = NotifyType::NOTIFY_DELETED;
339 originNotifyData.type = NotifyType::NOTIFY_ADDED;
340 }
341 CloudDiskNotify::GetInstance().AddNotify(trashNotifyData);
342 CloudDiskNotify::GetInstance().AddNotify(originNotifyData);
343 }
344
HandleDelete(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)345 static void HandleDelete(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
346 {
347 if (paramService.cloudId.empty()) {
348 NotifyData notifyData{"", false, NotifyType::NOTIFY_DELETED};
349 notifyData.uri = CLOUDDISK_URI_PREFIX;
350 notifyData.uri.replace(CLOUDDISK_URI_PREFIX.find(BUNDLENAME_FLAG), BUNDLENAME_FLAG.length(),
351 paramOthers.bundleName);
352 notifyData.isDir = true;
353 CloudDiskNotify::GetInstance().AddNotify(notifyData);
354 } else {
355 for (auto notifyData : (paramOthers.notifyDataList)) {
356 CloudDiskNotify::GetInstance().AddNotify(notifyData);
357 }
358 }
359 }
360
HandleDeleteBatch(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)361 static void HandleDeleteBatch(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
362 {
363 for (auto notifyData : (paramOthers.notifyDataList)) {
364 CloudDiskNotify::GetInstance().AddNotify(notifyData);
365 }
366 }
367
TryNotifyService(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)368 void CloudDiskNotify::TryNotifyService(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
369 {
370 switch (paramService.opsType) {
371 case NotifyOpsType::SERVICE_INSERT:
372 HandleInsert(paramService, paramOthers);
373 break;
374 case NotifyOpsType::SERVICE_UPDATE:
375 HandleUpdate(paramService, paramOthers);
376 break;
377 case NotifyOpsType::SERVICE_UPDATE_RECYCLE:
378 HandleUpdateRecycle(paramService, paramOthers);
379 break;
380 case NotifyOpsType::SERVICE_DELETE:
381 HandleDelete(paramService, paramOthers);
382 break;
383 case NotifyOpsType::SERVICE_DELETE_BATCH:
384 HandleDeleteBatch(paramService, paramOthers);
385 break;
386 default:
387 LOGE("bad ops, opsType: %{public}d", paramService.opsType);
388 break;
389 }
390 }
391
GetDeleteNotifyData(const vector<NativeRdb::ValueObject> & deleteIds,vector<NotifyData> & notifyDataList,const ParamServiceOther & paramOthers)392 int32_t CloudDiskNotify::GetDeleteNotifyData(const vector<NativeRdb::ValueObject> &deleteIds,
393 vector<NotifyData> ¬ifyDataList,
394 const ParamServiceOther ¶mOthers)
395 {
396 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
397 if (rdbStore == nullptr) {
398 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
399 return E_RDB;
400 }
401 for (auto deleteId : deleteIds) {
402 NotifyData notifyData{"", false, NotifyType::NOTIFY_DELETED};
403 string cloudId = static_cast<string>(deleteId);
404 CacheNode curNode{cloudId};
405 if (rdbStore->GetCurNode(cloudId, curNode) == E_OK && rdbStore->GetNotifyUri(curNode, notifyData.uri) == E_OK) {
406 notifyData.isDir = curNode.isDir == TYPE_DIR_STR;
407 notifyDataList.push_back(notifyData);
408 }
409 }
410 return E_OK;
411 }
412
AddNotify(NotifyData notifyData)413 void CloudDiskNotify::AddNotify(NotifyData notifyData)
414 {
415 LOGD("push cur notify into list type: %{public}d, uri: %{public}s, isDir: %{public}d", notifyData.type,
416 GetAnonyString(notifyData.uri).c_str(), notifyData.isDir);
417 if (notifyData.type == NotifyType::NOTIFY_NONE) {
418 return;
419 }
420
421 auto notifyFunc = [notifyData] {
422 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
423 if (obsMgrClient == nullptr) {
424 LOGE("obsMgrClient is null");
425 return;
426 }
427 Parcel parcel;
428 parcel.WriteUint32(1);
429 parcel.WriteBool(notifyData.isDir);
430 parcel.WriteBool(notifyData.isLocalOperation);
431 parcel.WriteString(notifyData.extraUri);
432 uintptr_t buf = parcel.GetData();
433 if (parcel.GetDataSize() == 0) {
434 LOGE("parcel.getDataSize fail");
435 return;
436 }
437
438 auto *uBuf = new (std::nothrow) uint8_t[parcel.GetDataSize()];
439 if (uBuf == nullptr) {
440 return;
441 }
442 int32_t ret = memcpy_s(uBuf, parcel.GetDataSize(), reinterpret_cast<uint8_t *>(buf), parcel.GetDataSize());
443 if (ret != 0) {
444 LOGE("Parcel Data copy failed, err: %{public}d", ret);
445 delete[] uBuf;
446 return;
447 }
448 ChangeInfo changeInfo({static_cast<ChangeInfo::ChangeType>(notifyData.type), {Uri(notifyData.uri)}, uBuf,
449 parcel.GetDataSize()});
450 obsMgrClient->NotifyChangeExt(changeInfo);
451 delete[] uBuf;
452 };
453 ffrt::thread(notifyFunc).detach();
454 }
455
NotifyChangeOuter()456 void CloudDiskNotify::NotifyChangeOuter()
457 {
458 LOGD("Start Notify Outer");
459 list<CacheNotifyInfo> tmpNfList_;
460 {
461 lock_guard<mutex> lock(mutex_);
462 if (nfList_.empty()) {
463 return;
464 }
465 nfList_.swap(tmpNfList_);
466 nfList_.clear();
467 }
468
469 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
470 if (obsMgrClient == nullptr) {
471 LOGE("obsMgrClient is null");
472 return;
473 }
474 for (auto cacheNode = tmpNfList_.begin(); cacheNode != tmpNfList_.end(); ++cacheNode) {
475 Parcel parcel;
476 parcel.WriteUint32(static_cast<uint32_t>(cacheNode->isDirList.size()));
477 for (auto isDir = cacheNode->isDirList.begin(); isDir != cacheNode->isDirList.end(); ++isDir) {
478 parcel.WriteBool(*isDir);
479 }
480 uintptr_t buf = parcel.GetData();
481 if (parcel.GetDataSize() == 0) {
482 LOGE("parcel.getDataSize fail");
483 return;
484 }
485
486 auto *uBuf = new (std::nothrow) uint8_t[parcel.GetDataSize()];
487 if (uBuf == nullptr) {
488 return;
489 }
490 int32_t ret = memcpy_s(uBuf, parcel.GetDataSize(), reinterpret_cast<uint8_t *>(buf), parcel.GetDataSize());
491 if (ret != 0) {
492 LOGE("Parcel Data copy failed, err: %{public}d", ret);
493 delete[] uBuf;
494 return;
495 }
496 ChangeInfo changeInfo({static_cast<ChangeInfo::ChangeType>(cacheNode->notifyType), cacheNode->uriList, uBuf,
497 parcel.GetDataSize()});
498 obsMgrClient->NotifyChangeExt(changeInfo);
499 delete[] uBuf;
500 }
501 }
502 } // namespace OHOS::FileManagement::CloudDisk
503