• 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 
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     string name("MediaLibraryInotify");
71     pthread_setname_np(pthread_self(), name.c_str());
72     const int32_t READ_LEN = 255;
73     char data[READ_LEN] = {0};
74     while (isWatching_) {
75         int32_t len = read(inotifyFd_, data, READ_LEN);
76         if (len < static_cast<int32_t>(sizeof(struct inotify_event))) {
77             MEDIA_ERR_LOG("read event error, len: %{public}d, fd: %{public}d, error: %{public}d", len, inotifyFd_,
78                 errno);
79             Restart();
80             return;
81         }
82         int32_t index = 0;
83         while (index < len) {
84             struct inotify_event *event = reinterpret_cast<struct inotify_event *>(data + index);
85             index += static_cast<int32_t>(sizeof(struct inotify_event)) +
86 		    static_cast<int32_t>(event->len);
87             unique_lock<mutex> lock(mutex_);
88             if (watchList_.count(event->wd) == 0) {
89                 continue;
90             }
91             auto &item = watchList_.at(event->wd);
92             auto eventMask = event->mask;
93             auto &meetEvent = item.meetEvent_;
94             meetEvent = (eventMask & IN_MODIFY) ? (meetEvent | IN_MODIFY) : meetEvent;
95             meetEvent = (eventMask & IN_CLOSE_WRITE) ? (meetEvent | IN_CLOSE_WRITE) : meetEvent;
96             meetEvent = (eventMask & IN_CLOSE_NOWRITE) ? (meetEvent | IN_CLOSE_NOWRITE) : meetEvent;
97             if (((meetEvent & IN_CLOSE_WRITE) && (meetEvent & IN_MODIFY)) ||
98                 ((meetEvent & IN_CLOSE_NOWRITE) && (meetEvent & IN_MODIFY))) {
99                 MEDIA_INFO_LOG("path:%s, meetEvent:%x file_id:%s", item.path_.c_str(),
100                     meetEvent, item.uri_.c_str());
101                 string id = MediaFileUtils::GetIdFromUri(item.uri_);
102                 string itemPath = ConvertMediaPath(item.path_);
103                 string bundleName = item.bundleName_;
104                 MediaFileUri itemUri(item.uri_);
105                 MediaLibraryApi itemApi = item.api_;
106                 Remove(event->wd);
107                 lock.unlock();
108                 MediaLibraryObjectUtils::ScanFileAsync(itemPath, id, itemApi);
109                 UriPermissionOperations::DeleteBundlePermission(id, bundleName, itemUri.GetTableName());
110             }
111         }
112     }
113     isWatching_ = false;
114 }
115 
Restart()116 void MediaLibraryInotify::Restart()
117 {
118     for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
119         if (inotify_rm_watch(inotifyFd_, iter->first) != 0) {
120             MEDIA_ERR_LOG("rm watch fd: %{public}d, fail: %{public}d", iter->first, errno);
121         }
122     }
123     isWatching_ = false;
124     watchList_.clear();
125     inotifyFd_ = 0;
126     Init();
127 }
128 
DoAging()129 void MediaLibraryInotify::DoAging()
130 {
131     lock_guard<mutex> lock(mutex_);
132     if (watchList_.size() > MAX_AGING_WATCH_LIST) {
133         MEDIA_DEBUG_LOG("watch list clear");
134         watchList_.clear();
135     }
136 }
137 
DoStop()138 void MediaLibraryInotify::DoStop()
139 {
140     lock_guard<mutex> lock(mutex_);
141     for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
142         if (inotify_rm_watch(inotifyFd_, iter->first) != 0) {
143             MEDIA_ERR_LOG("rm watch fd: %{public}d, fail: %{public}d", iter->first, errno);
144         }
145     }
146     isWatching_ = false;
147     watchList_.clear();
148     inotifyFd_ = 0;
149     MEDIA_INFO_LOG("stop success");
150 }
151 
RemoveByFileUri(const string & uri,MediaLibraryApi api)152 int32_t MediaLibraryInotify::RemoveByFileUri(const string &uri, MediaLibraryApi api)
153 {
154     lock_guard<mutex> lock(mutex_);
155     int32_t wd = -1;
156     for (auto iter = watchList_.begin(); iter != watchList_.end(); iter++) {
157         if (iter->second.uri_ == uri && iter->second.api_ == api) {
158             wd = iter->first;
159             MEDIA_DEBUG_LOG("remove uri:%{public}s wd:%{public}d path:%{public}s",
160                 iter->second.uri_.c_str(), wd, iter->second.path_.c_str());
161             break;
162         }
163     }
164     if (wd < 0) {
165         MEDIA_DEBUG_LOG("remove uri:%s fail", uri.c_str());
166         return E_FAIL;
167     }
168     return Remove(wd);
169 }
170 
Remove(int wd)171 int32_t MediaLibraryInotify::Remove(int wd)
172 {
173     watchList_.erase(wd);
174     if (inotify_rm_watch(inotifyFd_, wd) != 0) {
175         MEDIA_ERR_LOG("rm watch fd:%{public}d fail:%{public}d", wd, errno);
176         return E_FAIL;
177     }
178     return E_SUCCESS;
179 }
180 
Init()181 int32_t MediaLibraryInotify::Init()
182 {
183     if (inotifyFd_ <= 0) {
184         inotifyFd_ = inotify_init();
185         if (inotifyFd_ < 0) {
186             MEDIA_ERR_LOG("add AddWatchList fail fd: %{public}d, error: %{public}d", inotifyFd_, errno);
187             return E_FAIL;
188         }
189         MEDIA_INFO_LOG("init inotifyFd_: %{public}d success", inotifyFd_);
190     }
191     return E_SUCCESS;
192 }
193 
AddWatchList(const string & path,const string & uri,MediaLibraryApi api)194 int32_t MediaLibraryInotify::AddWatchList(const string &path, const string &uri, MediaLibraryApi api)
195 {
196     lock_guard<mutex> lock(mutex_);
197     if (watchList_.size() > MAX_WATCH_LIST) {
198         MEDIA_ERR_LOG("watch list full, add uri:%{public}s fail", uri.c_str());
199         return E_FAIL;
200     }
201     int32_t wd = inotify_add_watch(inotifyFd_, path.c_str(), IN_CLOSE | IN_MODIFY);
202     if (wd > 0) {
203         string bundleName = MediaLibraryBundleManager::GetInstance()->GetClientBundleName();
204         struct WatchInfo item(path, uri, bundleName, api);
205         watchList_.emplace(wd, item);
206     }
207     if (!isWatching_.load()) {
208         isWatching_ = true;
209         thread(&MediaLibraryInotify::WatchCallBack, this).detach();
210     }
211     return E_SUCCESS;
212 }
213 } // namespace Media
214 } // namespace OHOS
215