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 "media_file_utils.h"
25 #include "medialibrary_bundle_manager.h"
26 #include "medialibrary_object_utils.h"
27 #include "medialibrary_errno.h"
28 #include "medialibrary_uripermission_operations.h"
29 #include "permission_utils.h"
30 #include "dfx_utils.h"
31
32 using namespace std;
33 namespace OHOS {
34 namespace Media {
35 std::shared_ptr<MediaLibraryInotify> MediaLibraryInotify::instance_ = nullptr;
36 std::mutex MediaLibraryInotify::mutex_;
37 const int32_t MAX_WATCH_LIST = 1000;
38 const int32_t SINGLE_BUNDLE_LIST = 200;
39 const int32_t MAX_AGING_WATCH_LIST = 100;
40
GetInstance()41 shared_ptr<MediaLibraryInotify> MediaLibraryInotify::GetInstance()
42 {
43 if (instance_ != nullptr) {
44 return instance_;
45 }
46 lock_guard<mutex> lock(mutex_);
47 if (instance_ == nullptr) {
48 instance_ = make_shared<MediaLibraryInotify>();
49 if (instance_ == nullptr) {
50 MEDIA_ERR_LOG("GetInstance nullptr");
51 return instance_;
52 }
53 instance_->Init();
54 }
55 return instance_;
56 }
57
ConvertMediaPath(const std::string & path)58 static string ConvertMediaPath(const std::string &path)
59 {
60 // if input path is /storage/media/local/xxx, convert to /storage/cloud/xxx
61 string mediaPath = path;
62 string localPath = "/storage/media/local/";
63 string cloudPath = "/storage/cloud/";
64 if (mediaPath.find(localPath) != string::npos) {
65 mediaPath.replace(mediaPath.find(localPath), localPath.length(), cloudPath);
66 }
67 return mediaPath;
68 }
69
WatchCallBack()70 void MediaLibraryInotify::WatchCallBack()
71 {
72 string name("MediaLibraryInotify");
73 pthread_setname_np(pthread_self(), name.c_str());
74 MEDIA_INFO_LOG("MediaLibraryInotify start watchthread");
75 const int32_t READ_LEN = 255;
76 char data[READ_LEN] = {0};
77 while (isWatching_) {
78 int32_t len = read(inotifyFd_, data, READ_LEN);
79 if (len < static_cast<int32_t>(sizeof(struct inotify_event))) {
80 MEDIA_ERR_LOG("read event error, len: %{public}d, fd: %{public}d, error: %{public}d", len, inotifyFd_,
81 errno);
82 Restart();
83 return;
84 }
85 int32_t index = 0;
86 while (index < len) {
87 struct inotify_event *event = reinterpret_cast<struct inotify_event *>(data + index);
88 index += static_cast<int32_t>(sizeof(struct inotify_event)) + event->len;
89 unique_lock<mutex> lock(mutex_);
90 if (watchList_.count(event->wd) == 0) {
91 continue;
92 }
93 auto &item = watchList_.at(event->wd);
94 auto eventMask = event->mask;
95 auto &meetEvent = item.meetEvent_;
96 meetEvent = (eventMask & IN_MODIFY) ? (meetEvent | IN_MODIFY) : meetEvent;
97 meetEvent = (eventMask & IN_CLOSE_WRITE) ? (meetEvent | IN_CLOSE_WRITE) : meetEvent;
98 meetEvent = (eventMask & IN_CLOSE_NOWRITE) ? (meetEvent | IN_CLOSE_NOWRITE) : meetEvent;
99 if (((meetEvent & IN_CLOSE_WRITE) && (meetEvent & IN_MODIFY)) ||
100 ((meetEvent & IN_CLOSE_NOWRITE) && (meetEvent & IN_MODIFY))) {
101 MEDIA_INFO_LOG("path:%s, meetEvent:%x file_id:%s", item.path_.c_str(),
102 meetEvent, item.uri_.c_str());
103 string id = MediaFileUtils::GetIdFromUri(item.uri_);
104 string itemPath = ConvertMediaPath(item.path_);
105 string bundleName = item.bundleName_;
106 MediaFileUri itemUri(item.uri_);
107 MediaLibraryApi itemApi = item.api_;
108 Remove(event->wd);
109 lock.unlock();
110 MediaLibraryObjectUtils::ScanFileAsync(itemPath, id, itemApi);
111 UriPermissionOperations::DeleteBundlePermission(id, bundleName, itemUri.GetTableName());
112 }
113 }
114 }
115 MEDIA_INFO_LOG("MediaLibraryInotify end watchthread");
116 isWatching_ = false;
117 }
118
Restart()119 void MediaLibraryInotify::Restart()
120 {
121 for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
122 if (inotify_rm_watch(inotifyFd_, iter->first) != 0) {
123 MEDIA_ERR_LOG("rm watch fd: %{public}d, fail: %{public}d", iter->first, errno);
124 }
125 }
126 isWatching_ = false;
127 watchList_.clear();
128 inotifyFd_ = 0;
129 Init();
130 }
131
DoAging()132 void MediaLibraryInotify::DoAging()
133 {
134 lock_guard<mutex> lock(mutex_);
135 if (watchList_.size() > MAX_AGING_WATCH_LIST) {
136 MEDIA_DEBUG_LOG("watch list clear");
137 watchList_.clear();
138 }
139 }
140
DoStop()141 void MediaLibraryInotify::DoStop()
142 {
143 lock_guard<mutex> lock(mutex_);
144 for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
145 if (inotify_rm_watch(inotifyFd_, iter->first) != 0) {
146 MEDIA_ERR_LOG("rm watch fd: %{public}d, fail: %{public}d", iter->first, errno);
147 }
148 }
149 isWatching_ = false;
150 watchList_.clear();
151 inotifyFd_ = 0;
152 MEDIA_INFO_LOG("stop success");
153 }
154
RemoveByFileUri(const string & uri,MediaLibraryApi api)155 int32_t MediaLibraryInotify::RemoveByFileUri(const string &uri, MediaLibraryApi api)
156 {
157 lock_guard<mutex> lock(mutex_);
158 int32_t wd = -1;
159 for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
160 if (iter->second.uri_ == uri && iter->second.api_ == api) {
161 wd = iter->first;
162 MEDIA_DEBUG_LOG("remove uri:%{public}s wd:%{public}d path:%{public}s",
163 iter->second.uri_.c_str(), wd, iter->second.path_.c_str());
164 break;
165 }
166 }
167 if (wd < 0) {
168 MEDIA_DEBUG_LOG("remove uri:%s fail", uri.c_str());
169 return E_FAIL;
170 }
171 return Remove(wd);
172 }
173
Remove(int wd)174 int32_t MediaLibraryInotify::Remove(int wd)
175 {
176 watchList_.erase(wd);
177 if (inotify_rm_watch(inotifyFd_, wd) != 0) {
178 MEDIA_ERR_LOG("rm watch fd:%{public}d fail:%{public}d", wd, errno);
179 return E_FAIL;
180 }
181 return E_SUCCESS;
182 }
183
Init()184 int32_t MediaLibraryInotify::Init()
185 {
186 if (inotifyFd_ <= 0) {
187 inotifyFd_ = inotify_init();
188 if (inotifyFd_ < 0) {
189 MEDIA_ERR_LOG("add AddWatchList fail fd: %{public}d, error: %{public}d", inotifyFd_, errno);
190 return E_FAIL;
191 }
192 MEDIA_INFO_LOG("init inotifyFd_: %{public}d success", inotifyFd_);
193 }
194 return E_SUCCESS;
195 }
196
GetBundleCount(const std::string & bundleName)197 int32_t MediaLibraryInotify::GetBundleCount(const std::string &bundleName)
198 {
199 int count = 0;
200 for (auto &pair : watchList_) {
201 if (bundleName.compare(pair.second.bundleName_) == 0) {
202 count++;
203 }
204 }
205 MEDIA_DEBUG_LOG("MediaLibraryInotify GetBundleCount count:%{public}d", count);
206 return count;
207 }
208
BuildDfxInfo()209 const string MediaLibraryInotify::BuildDfxInfo()
210 {
211 unordered_map<string, WatchBundleInfo> bundleInfoMap;
212 for (auto &pair : watchList_) {
213 string bundleName = pair.second.bundleName_;
214 if (bundleInfoMap.find(bundleName) != bundleInfoMap.end()) {
215 auto it = bundleInfoMap.find(bundleName);
216 int32_t count = ++it->second.count;
217 int64_t firstTime = pair.second.currentTime_ < it->second.firstEntryTime ?
218 pair.second.currentTime_ : it->second.firstEntryTime;
219 string firstUri = pair.second.currentTime_ < it->second.firstEntryTime ?
220 pair.second.uri_ : it->second.firstUri;
221 WatchBundleInfo watchBundleInfo(count, firstTime, firstUri, bundleName);
222 it->second = watchBundleInfo;
223 } else {
224 WatchBundleInfo watchBundleInfo(1, pair.second.currentTime_, pair.second.uri_, bundleName);
225 bundleInfoMap.emplace(bundleName, watchBundleInfo);
226 }
227 }
228 string dfxInfo;
229 for (const auto &pair : bundleInfoMap) {
230 dfxInfo.append(pair.second.Dump());
231 dfxInfo.append(";");
232 }
233 return dfxInfo;
234 }
235
AddWatchList(const string & path,const string & uri,MediaLibraryApi api)236 int32_t MediaLibraryInotify::AddWatchList(const string &path, const string &uri, MediaLibraryApi api)
237 {
238 lock_guard<mutex> lock(mutex_);
239 string bundleName = MediaLibraryBundleManager::GetInstance()->GetClientBundleName();
240 if (watchList_.size() >= MAX_WATCH_LIST || GetBundleCount(bundleName) >= SINGLE_BUNDLE_LIST) {
241 MEDIA_ERR_LOG("watch list full, add uri:%{public}s fail, bundleName:%{public}s, info:%{public}s",
242 uri.c_str(), bundleName.c_str(), BuildDfxInfo().c_str());
243 return E_FAIL;
244 }
245 int32_t wd = inotify_add_watch(inotifyFd_, path.c_str(), IN_CLOSE | IN_MODIFY);
246 if (wd > 0) {
247 int64_t currentTime = MediaFileUtils::UTCTimeSeconds();
248 struct WatchInfo item(path, uri, bundleName, api, currentTime);
249 MEDIA_INFO_LOG("inotify emplace path:%{public}s, bundleName:%{public}s, watchSize:%{public}d,",
250 DfxUtils::GetSafePath(path).c_str(), bundleName.c_str(), static_cast<int32_t>(watchList_.size()));
251 watchList_.emplace(wd, item);
252 }
253 if (!isWatching_.load()) {
254 isWatching_ = true;
255 thread(&MediaLibraryInotify::WatchCallBack, this).detach();
256 }
257 return E_SUCCESS;
258 }
259 } // namespace Media
260 } // namespace OHOS