• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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