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 CloudDiskNotify::GetInstance().AddNotify(notifyData);
124 }
125
HandleRecycleRestore(const NotifyParamDisk & paramDisk)126 static void HandleRecycleRestore(const NotifyParamDisk ¶mDisk)
127 {
128 NotifyData trashNotifyData;
129 if (GetTrashNotifyData(paramDisk, trashNotifyData) != E_OK) {
130 LOGE("Get trash notify data fail");
131 return;
132 }
133 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramDisk.inoPtr->bundleName, paramDisk.data->userId);
134 if (rdbStore == nullptr) {
135 LOGE("Get rdb store fail, bundleName: %{public}s", paramDisk.inoPtr->bundleName.c_str());
136 return;
137 }
138 if (TrashUriAddRowId(rdbStore, paramDisk.inoPtr->cloudId, trashNotifyData.uri) != E_OK) {
139 return;
140 }
141
142 NotifyData originNotifyData;
143 if (GetDataInner(paramDisk, originNotifyData) != E_OK) {
144 LOGE("Get origin notify data fail");
145 return;
146 }
147 trashNotifyData.isDir = originNotifyData.isDir;
148 if (paramDisk.opsType == NotifyOpsType::DAEMON_RECYCLE) {
149 trashNotifyData.type = NotifyType::NOTIFY_ADDED;
150 originNotifyData.type = NotifyType::NOTIFY_DELETED;
151 }
152 if (paramDisk.opsType == NotifyOpsType::DAEMON_RESTORE) {
153 trashNotifyData.type = NotifyType::NOTIFY_DELETED;
154 originNotifyData.type = NotifyType::NOTIFY_ADDED;
155 }
156 CloudDiskNotify::GetInstance().AddNotify(trashNotifyData);
157 CloudDiskNotify::GetInstance().AddNotify(originNotifyData);
158 }
159
HandleWrite(const NotifyParamDisk & paramDisk,const ParamDiskOthers & paramOthers)160 static void HandleWrite(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)
161 {
162 NotifyData notifyData;
163 if (GetDataInner(paramDisk, notifyData) != E_OK) {
164 return;
165 }
166 notifyData.type = NotifyType::NOTIFY_MODIFIED;
167 if (paramOthers.dirtyType == static_cast<int32_t>(DirtyType::TYPE_NO_NEED_UPLOAD)) {
168 notifyData.type = NotifyType::NOTIFY_ADDED;
169 }
170 CloudDiskNotify::GetInstance().AddNotify(notifyData);
171 }
172
HandleMkdir(const NotifyParamDisk & paramDisk)173 static void HandleMkdir(const NotifyParamDisk ¶mDisk)
174 {
175 NotifyData notifyData;
176 if (GetDataInnerWithName(paramDisk, notifyData) != E_OK) {
177 return;
178 }
179 notifyData.type = NotifyType::NOTIFY_ADDED;
180 notifyData.isDir = true;
181 CloudDiskNotify::GetInstance().AddNotify(notifyData);
182 }
183
HandleUnlink(const NotifyParamDisk & paramDisk)184 static void HandleUnlink(const NotifyParamDisk ¶mDisk)
185 {
186 NotifyData notifyData;
187 if (GetDataInnerWithName(paramDisk, notifyData) != E_OK) {
188 return;
189 }
190 notifyData.type = NotifyType::NOTIFY_DELETED;
191 notifyData.isDir = paramDisk.opsType == NotifyOpsType::DAEMON_RMDIR;
192 CloudDiskNotify::GetInstance().AddNotify(notifyData);
193 }
194
HandleRename(const NotifyParamDisk & paramDisk,const ParamDiskOthers & paramOthers)195 static void HandleRename(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)
196 {
197 NotifyData notifyData;
198 NotifyData newNotifyData;
199 int32_t ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func, paramDisk.ino,
200 paramDisk.name, notifyData);
201 if (ret != E_OK) {
202 LOGE("get notify data fail, name: %{public}s", GetAnonyString(paramDisk.name).c_str());
203 return;
204 }
205 ret = CloudDiskNotifyUtils::GetNotifyData(paramDisk.data, paramDisk.func, paramDisk.newIno,
206 paramDisk.newName, newNotifyData);
207 if (ret != E_OK) {
208 LOGE("get new notify data fail, name: %{public}s", GetAnonyString(paramDisk.newName).c_str());
209 return;
210 }
211 notifyData.isDir = paramOthers.isDir;
212 newNotifyData.isDir = paramOthers.isDir;
213 notifyData.type = notifyData.isDir ? NotifyType::NOTIFY_DELETED : NotifyType::NOTIFY_NONE;
214 newNotifyData.type = NotifyType::NOTIFY_RENAMED;
215 CloudDiskNotify::GetInstance().AddNotify(notifyData);
216 CloudDiskNotify::GetInstance().AddNotify(newNotifyData);
217 }
218
TryNotify(const NotifyParamDisk & paramDisk,const ParamDiskOthers & paramOthers)219 void CloudDiskNotify::TryNotify(const NotifyParamDisk ¶mDisk, const ParamDiskOthers ¶mOthers)
220 {
221 switch (paramDisk.opsType) {
222 case NotifyOpsType::DAEMON_SETATTR:
223 case NotifyOpsType::DAEMON_SETXATTR:
224 HandleSetAttr(paramDisk);
225 break;
226 case NotifyOpsType::DAEMON_RECYCLE:
227 case NotifyOpsType::DAEMON_RESTORE:
228 HandleRecycleRestore(paramDisk);
229 break;
230 case NotifyOpsType::DAEMON_MKDIR:
231 HandleMkdir(paramDisk);
232 break;
233 case NotifyOpsType::DAEMON_RMDIR:
234 case NotifyOpsType::DAEMON_UNLINK:
235 HandleUnlink(paramDisk);
236 break;
237 case NotifyOpsType::DAEMON_RENAME:
238 HandleRename(paramDisk, paramOthers);
239 break;
240 case NotifyOpsType::DAEMON_WRITE:
241 HandleWrite(paramDisk, paramOthers);
242 break;
243 default:
244 LOGE("bad ops, opsType: %{public}d", paramDisk.opsType);
245 break;
246 }
247 }
248
HandleInsert(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)249 static void HandleInsert(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
250 {
251 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
252 if (rdbStore == nullptr) {
253 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
254 return;
255 }
256 NotifyData notifyData;
257 int32_t ret;
258 if (paramService.node.isRecycled) {
259 ret = GetTrashNotifyData(paramService.node, paramOthers, notifyData);
260 if (TrashUriAddRowId(rdbStore, paramService.cloudId, notifyData.uri) != E_OK) {
261 return;
262 }
263 notifyData.isDir = paramService.node.isDir == TYPE_DIR_STR;
264 } else {
265 ret = rdbStore->GetNotifyData(paramService.node, notifyData);
266 }
267 if (ret == E_OK) {
268 notifyData.type = NotifyType::NOTIFY_ADDED;
269 CloudDiskNotify::GetInstance().AddNotify(notifyData);
270 }
271 }
272
HandleUpdate(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)273 static void HandleUpdate(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
274 {
275 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
276 if (rdbStore == nullptr) {
277 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
278 return;
279 }
280 NotifyData inNotifyData;
281 NotifyData notifyData;
282 CacheNode curNode{paramService.cloudId};
283 if (rdbStore->GetCurNode(paramService.cloudId, curNode) == E_OK &&
284 rdbStore->GetNotifyUri(curNode, notifyData.uri) == E_OK) {
285 notifyData.type = NotifyType::NOTIFY_MODIFIED;
286 notifyData.isDir = curNode.isDir == TYPE_DIR_STR;
287 if (paramService.notifyType == NotifyType::NOTIFY_NONE &&
288 rdbStore->GetNotifyData(paramService.node, inNotifyData) == E_OK) {
289 if (inNotifyData.uri != notifyData.uri) {
290 notifyData.type = NotifyType::NOTIFY_DELETED;
291 inNotifyData.type = NotifyType::NOTIFY_RENAMED;
292 }
293 }
294 }
295 CloudDiskNotify::GetInstance().AddNotify(notifyData);
296 CloudDiskNotify::GetInstance().AddNotify(inNotifyData);
297 }
298
HandleUpdateRecycle(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)299 static void HandleUpdateRecycle(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
300 {
301 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
302 if (rdbStore == nullptr) {
303 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
304 return;
305 }
306 NotifyData trashNotifyData;
307 if (GetTrashNotifyData(paramService.node, paramOthers, trashNotifyData) != E_OK) {
308 LOGE("Get trash notify data fail");
309 return;
310 }
311 if (TrashUriAddRowId(rdbStore, paramService.cloudId, trashNotifyData.uri) != E_OK) {
312 return;
313 }
314
315 NotifyData originNotifyData;
316 if (rdbStore->GetNotifyData(paramService.node, originNotifyData) != E_OK) {
317 LOGE("Get origin notify data fail");
318 return;
319 }
320 trashNotifyData.isDir = paramService.node.isDir == TYPE_DIR_STR;
321 originNotifyData.isDir = trashNotifyData.isDir;
322 if (paramService.node.isRecycled) {
323 trashNotifyData.type = NotifyType::NOTIFY_ADDED;
324 originNotifyData.type = NotifyType::NOTIFY_DELETED;
325 } else {
326 trashNotifyData.type = NotifyType::NOTIFY_DELETED;
327 originNotifyData.type = NotifyType::NOTIFY_ADDED;
328 }
329 CloudDiskNotify::GetInstance().AddNotify(trashNotifyData);
330 CloudDiskNotify::GetInstance().AddNotify(originNotifyData);
331 }
332
HandleDelete(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)333 static void HandleDelete(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
334 {
335 if (paramService.cloudId.empty()) {
336 NotifyData notifyData{"", false, NotifyType::NOTIFY_DELETED};
337 notifyData.uri = CLOUDDISK_URI_PREFIX;
338 notifyData.uri.replace(CLOUDDISK_URI_PREFIX.find(BUNDLENAME_FLAG), BUNDLENAME_FLAG.length(),
339 paramOthers.bundleName);
340 notifyData.isDir = true;
341 CloudDiskNotify::GetInstance().AddNotify(notifyData);
342 } else {
343 for (auto notifyData : (paramOthers.notifyDataList)) {
344 CloudDiskNotify::GetInstance().AddNotify(notifyData);
345 }
346 }
347 }
348
HandleDeleteBatch(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)349 static void HandleDeleteBatch(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
350 {
351 for (auto notifyData : (paramOthers.notifyDataList)) {
352 CloudDiskNotify::GetInstance().AddNotify(notifyData);
353 }
354 }
355
TryNotifyService(const NotifyParamService & paramService,const ParamServiceOther & paramOthers)356 void CloudDiskNotify::TryNotifyService(const NotifyParamService ¶mService, const ParamServiceOther ¶mOthers)
357 {
358 switch (paramService.opsType) {
359 case NotifyOpsType::SERVICE_INSERT:
360 HandleInsert(paramService, paramOthers);
361 break;
362 case NotifyOpsType::SERVICE_UPDATE:
363 HandleUpdate(paramService, paramOthers);
364 break;
365 case NotifyOpsType::SERVICE_UPDATE_RECYCLE:
366 HandleUpdateRecycle(paramService, paramOthers);
367 break;
368 case NotifyOpsType::SERVICE_DELETE:
369 HandleDelete(paramService, paramOthers);
370 break;
371 case NotifyOpsType::SERVICE_DELETE_BATCH:
372 HandleDeleteBatch(paramService, paramOthers);
373 break;
374 default:
375 LOGE("bad ops, opsType: %{public}d", paramService.opsType);
376 break;
377 }
378 }
379
GetDeleteNotifyData(const vector<NativeRdb::ValueObject> & deleteIds,vector<NotifyData> & notifyDataList,const ParamServiceOther & paramOthers)380 int32_t CloudDiskNotify::GetDeleteNotifyData(const vector<NativeRdb::ValueObject> &deleteIds,
381 vector<NotifyData> ¬ifyDataList,
382 const ParamServiceOther ¶mOthers)
383 {
384 shared_ptr<CloudDiskRdbStore> rdbStore = GetRdbStore(paramOthers.bundleName, paramOthers.userId);
385 if (rdbStore == nullptr) {
386 LOGE("Get rdb store fail, bundleName: %{public}s", paramOthers.bundleName.c_str());
387 return E_RDB;
388 }
389 for (auto deleteId : deleteIds) {
390 NotifyData notifyData{"", false, NotifyType::NOTIFY_DELETED};
391 string cloudId = static_cast<string>(deleteId);
392 CacheNode curNode{cloudId};
393 if (rdbStore->GetCurNode(cloudId, curNode) == E_OK && rdbStore->GetNotifyUri(curNode, notifyData.uri) == E_OK) {
394 notifyData.isDir = curNode.isDir == TYPE_DIR_STR;
395 notifyDataList.push_back(notifyData);
396 }
397 }
398 return E_OK;
399 }
400
AddNotify(NotifyData notifyData)401 void CloudDiskNotify::AddNotify(NotifyData notifyData)
402 {
403 LOGD("push cur notify into list type: %{public}d, uri: %{public}s, isDir: %{public}d", notifyData.type,
404 GetAnonyString(notifyData.uri).c_str(), notifyData.isDir);
405 if (notifyData.type == NotifyType::NOTIFY_NONE) {
406 return;
407 }
408
409 auto notifyFunc = [notifyData] {
410 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
411 if (obsMgrClient == nullptr) {
412 LOGE("obsMgrClient is null");
413 return;
414 }
415 Parcel parcel;
416 parcel.WriteUint32(1);
417 parcel.WriteBool(notifyData.isDir);
418 uintptr_t buf = parcel.GetData();
419 if (parcel.GetDataSize() == 0) {
420 LOGE("parcel.getDataSize fail");
421 return;
422 }
423
424 auto *uBuf = new (std::nothrow) uint8_t[parcel.GetDataSize()];
425 if (uBuf == nullptr) {
426 return;
427 }
428 int32_t ret = memcpy_s(uBuf, parcel.GetDataSize(), reinterpret_cast<uint8_t *>(buf), parcel.GetDataSize());
429 if (ret != 0) {
430 LOGE("Parcel Data copy failed, err: %{public}d", ret);
431 delete[] uBuf;
432 return;
433 }
434 ChangeInfo changeInfo({static_cast<ChangeInfo::ChangeType>(notifyData.type), {Uri(notifyData.uri)}, uBuf,
435 parcel.GetDataSize()});
436 obsMgrClient->NotifyChangeExt(changeInfo);
437 delete[] uBuf;
438 };
439 ffrt::thread(notifyFunc).detach();
440 }
441
NotifyChangeOuter()442 void CloudDiskNotify::NotifyChangeOuter()
443 {
444 LOGD("Start Notify Outer");
445 list<CacheNotifyInfo> tmpNfList_;
446 {
447 lock_guard<mutex> lock(mutex_);
448 if (nfList_.empty()) {
449 return;
450 }
451 nfList_.swap(tmpNfList_);
452 nfList_.clear();
453 }
454
455 auto obsMgrClient = AAFwk::DataObsMgrClient::GetInstance();
456 if (obsMgrClient == nullptr) {
457 LOGE("obsMgrClient is null");
458 return;
459 }
460 for (auto cacheNode = tmpNfList_.begin(); cacheNode != tmpNfList_.end(); ++cacheNode) {
461 Parcel parcel;
462 parcel.WriteUint32(static_cast<uint32_t>(cacheNode->isDirList.size()));
463 for (auto isDir = cacheNode->isDirList.begin(); isDir != cacheNode->isDirList.end(); ++isDir) {
464 parcel.WriteBool(*isDir);
465 }
466 uintptr_t buf = parcel.GetData();
467 if (parcel.GetDataSize() == 0) {
468 LOGE("parcel.getDataSize fail");
469 return;
470 }
471
472 auto *uBuf = new (std::nothrow) uint8_t[parcel.GetDataSize()];
473 if (uBuf == nullptr) {
474 return;
475 }
476 int32_t ret = memcpy_s(uBuf, parcel.GetDataSize(), reinterpret_cast<uint8_t *>(buf), parcel.GetDataSize());
477 if (ret != 0) {
478 LOGE("Parcel Data copy failed, err: %{public}d", ret);
479 delete[] uBuf;
480 return;
481 }
482 ChangeInfo changeInfo({static_cast<ChangeInfo::ChangeType>(cacheNode->notifyType), cacheNode->uriList, uBuf,
483 parcel.GetDataSize()});
484 obsMgrClient->NotifyChangeExt(changeInfo);
485 delete[] uBuf;
486 }
487 }
488 } // namespace OHOS::FileManagement::CloudDisk
489