1 /*
2 * Copyright (C) 2023 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 #define MLOG_TAG "FileInotify"
16 #include "medialibrary_inotify.h"
17
18 #include <string>
19 #include <thread>
20
21 #include "unistd.h"
22 #include "media_log.h"
23 #include "media_file_uri.h"
24 #include "medialibrary_bundle_manager.h"
25 #include "medialibrary_object_utils.h"
26 #include "medialibrary_errno.h"
27 #include "medialibrary_data_manager_utils.h"
28 #include "medialibrary_uripermission_operations.h"
29 #include "permission_utils.h"
30
31 using namespace std;
32 namespace OHOS {
33 namespace Media {
34 std::shared_ptr<MediaLibraryInotify> MediaLibraryInotify::instance_ = nullptr;
35 std::mutex MediaLibraryInotify::mutex_;
36 const int32_t MAX_WATCH_LIST = 200;
37 const int32_t MAX_AGING_WATCH_LIST = 100;
38
GetInstance()39 shared_ptr<MediaLibraryInotify> MediaLibraryInotify::GetInstance()
40 {
41 if (instance_ != nullptr) {
42 return instance_;
43 }
44 lock_guard<mutex> lock(mutex_);
45 if (instance_ == nullptr) {
46 instance_ = make_shared<MediaLibraryInotify>();
47 if (instance_ == nullptr) {
48 MEDIA_ERR_LOG("GetInstance nullptr");
49 return instance_;
50 }
51 instance_->Init();
52 }
53 return instance_;
54 }
55
ConvertMediaPath(const std::string & path)56 static string ConvertMediaPath(const std::string &path)
57 {
58 // if input path is /storage/media/local/xxx, convert to /storage/cloud/xxx
59 string mediaPath = path;
60 string localPath = "/storage/media/local/";
61 string cloudPath = "/storage/cloud/";
62 if (mediaPath.find(localPath) != string::npos) {
63 mediaPath.replace(mediaPath.find(localPath), localPath.length(), cloudPath);
64 }
65 return mediaPath;
66 }
67
WatchCallBack()68 void MediaLibraryInotify::WatchCallBack()
69 {
70 const int32_t READ_LEN = 255;
71 char data[READ_LEN] = {0};
72 while (isWatching_) {
73 int32_t len = read(inotifyFd_, data, READ_LEN);
74 int32_t index = 0;
75 while (index < len) {
76 struct inotify_event *event = reinterpret_cast<struct inotify_event *>(data + index);
77 index += sizeof(struct inotify_event) + event->len;
78 unique_lock<mutex> lock(mutex_);
79 if (watchList_.count(event->wd) == 0) {
80 continue;
81 }
82 auto &item = watchList_.at(event->wd);
83 auto eventMask = event->mask;
84 auto &meetEvent = item.meetEvent_;
85 meetEvent = (eventMask & IN_MODIFY) ? (meetEvent | IN_MODIFY) : meetEvent;
86 meetEvent = (eventMask & IN_CLOSE_WRITE) ? (meetEvent | IN_CLOSE_WRITE) : meetEvent;
87 meetEvent = (eventMask & IN_CLOSE_NOWRITE) ? (meetEvent | IN_CLOSE_NOWRITE) : meetEvent;
88 if (((meetEvent & IN_CLOSE_WRITE) && (meetEvent & IN_MODIFY)) ||
89 ((meetEvent & IN_CLOSE_NOWRITE) && (meetEvent & IN_MODIFY))) {
90 MEDIA_DEBUG_LOG("path:%s, meetEvent:%x file_id:%s", item.path_.c_str(),
91 meetEvent, item.uri_.c_str());
92 string id = MediaLibraryDataManagerUtils::GetIdFromUri(item.uri_);
93 string itemPath = ConvertMediaPath(item.path_);
94 string bundleName = item.bundleName_;
95 MediaFileUri itemUri(item.uri_);
96 MediaLibraryApi itemApi = item.api_;
97 Remove(event->wd);
98 lock.unlock();
99 MediaLibraryObjectUtils::ScanFileAsync(itemPath, id, itemApi);
100 UriPermissionOperations::DeleteBundlePermission(id, bundleName, itemUri.GetTableName());
101 }
102 }
103 }
104 isWatching_ = false;
105 }
106
DoAging()107 void MediaLibraryInotify::DoAging()
108 {
109 lock_guard<mutex> lock(mutex_);
110 if (watchList_.size() > MAX_AGING_WATCH_LIST) {
111 MEDIA_DEBUG_LOG("watch list clear");
112 watchList_.clear();
113 }
114 }
115
DoStop()116 void MediaLibraryInotify::DoStop()
117 {
118 lock_guard<mutex> lock(mutex_);
119 for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
120 if (inotify_rm_watch(inotifyFd_, iter->first) != 0) {
121 MEDIA_ERR_LOG("rm watch fd: %{public}d, fail: %{public}d", iter->first, errno);
122 }
123 }
124 isWatching_ = false;
125 watchList_.clear();
126 inotifyFd_ = 0;
127 }
128
RemoveByFileUri(const string & uri,MediaLibraryApi api)129 int32_t MediaLibraryInotify::RemoveByFileUri(const string &uri, MediaLibraryApi api)
130 {
131 lock_guard<mutex> lock(mutex_);
132 int32_t wd = -1;
133 for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
134 if (iter->second.uri_ == uri && iter->second.api_ == api) {
135 wd = iter->first;
136 MEDIA_DEBUG_LOG("remove uri:%s wd:%d path:%s",
137 iter->second.uri_.c_str(), wd, iter->second.path_.c_str());
138 break;
139 }
140 }
141 if (wd < 0) {
142 MEDIA_DEBUG_LOG("remove uri:%s fail", uri.c_str());
143 return E_FAIL;
144 }
145 return Remove(wd);
146 }
147
Remove(int wd)148 int32_t MediaLibraryInotify::Remove(int wd)
149 {
150 watchList_.erase(wd);
151 if (inotify_rm_watch(inotifyFd_, wd) != 0) {
152 MEDIA_ERR_LOG("rm watch fd:%d fail:%d", wd, errno);
153 return E_FAIL;
154 }
155 return E_SUCCESS;
156 }
157
Init()158 int32_t MediaLibraryInotify::Init()
159 {
160 if (inotifyFd_ <= 0) {
161 inotifyFd_ = inotify_init();
162 if (inotifyFd_ < 0) {
163 MEDIA_ERR_LOG("add AddWatchList fail");
164 return E_FAIL;
165 }
166 }
167 return E_SUCCESS;
168 }
169
AddWatchList(const string & path,const string & uri,MediaLibraryApi api)170 int32_t MediaLibraryInotify::AddWatchList(const string &path, const string &uri, MediaLibraryApi api)
171 {
172 lock_guard<mutex> lock(mutex_);
173 if (watchList_.size() > MAX_WATCH_LIST) {
174 MEDIA_ERR_LOG("watch list full, add uri:%s fail", uri.c_str());
175 return E_FAIL;
176 }
177 int32_t wd = inotify_add_watch(inotifyFd_, path.c_str(), IN_CLOSE | IN_MODIFY);
178 if (wd > 0) {
179 string bundleName = MediaLibraryBundleManager::GetInstance()->GetClientBundleName();
180 struct WatchInfo item(path, uri, bundleName, api);
181 watchList_.emplace(wd, item);
182 }
183 if (!isWatching_.load()) {
184 isWatching_ = true;
185 thread(&MediaLibraryInotify::WatchCallBack, this).detach();
186 }
187 return E_SUCCESS;
188 }
189 } // namespace Media
190 } // namespace OHOS