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 #include "watcher_entity.h"
16
17 #include <algorithm>
18 #include <cerrno>
19 #include <cstdint>
20 #include <poll.h>
21 #include <sys/eventfd.h>
22 #include <unistd.h>
23
24 #include "filemgmt_libhilog.h"
25 #include "uv.h"
26
27 namespace OHOS::FileManagement::ModuleFileIO {
28 using namespace OHOS::FileManagement::LibN;
29 using namespace std;
30
31 mutex FileWatcher::watchMutex_;
32
FileWatcher()33 FileWatcher::FileWatcher() {}
34
~FileWatcher()35 FileWatcher::~FileWatcher() {}
36
GetNotifyId()37 int32_t FileWatcher::GetNotifyId()
38 {
39 return notifyFd_;
40 }
41
InitNotify()42 bool FileWatcher::InitNotify()
43 {
44 notifyFd_ = inotify_init();
45 if (notifyFd_ < 0) {
46 HILOGE("Failed to init notify errCode:%{public}d", errno);
47 return false;
48 }
49 eventFd_ = eventfd(0, EFD_CLOEXEC);
50 if (eventFd_ < 0) {
51 HILOGE("Failed to init eventfd errCode:%{public}d", errno);
52 return false;
53 }
54 return true;
55 }
56
CheckEventWatched(const string & fileName,const uint32_t & event)57 tuple<bool, int> FileWatcher::CheckEventWatched(const string &fileName, const uint32_t &event)
58 {
59 int wd = -1;
60 auto iter = wdFileNameMap_.find(fileName);
61 if (iter == wdFileNameMap_.end()) {
62 return {false, wd};
63 }
64 wd = iter->second.first;
65 if ((iter->second.second & event) == event) {
66 return {true, wd};
67 }
68 return {false, wd};
69 }
70
StartNotify(shared_ptr<WatcherInfoArg> arg)71 int FileWatcher::StartNotify(shared_ptr<WatcherInfoArg> arg)
72 {
73 lock_guard<mutex> lock(watchMutex_);
74 if (notifyFd_ < 0) {
75 HILOGE("Failed to start notify notifyFd_:%{public}d", notifyFd_);
76 return EIO;
77 }
78
79 auto [isWatched, wd] = CheckEventWatched(arg->fileName, arg->events);
80 if (isWatched && wd > 0) {
81 arg->wd = wd;
82 return ERRNO_NOERR;
83 }
84 uint32_t watchEvents = 0;
85 if (wd != -1) {
86 watchEvents = wdFileNameMap_[arg->fileName].second | arg->events;
87 } else {
88 watchEvents = arg->events;
89 }
90 int newWd = inotify_add_watch(notifyFd_, arg->fileName.c_str(), watchEvents);
91 if (newWd < 0) {
92 HILOGE("Failed to start notify errCode:%{public}d", errno);
93 return errno;
94 }
95 arg->wd = newWd;
96 wdFileNameMap_[arg->fileName].first = newWd;
97 wdFileNameMap_[arg->fileName].second = watchEvents;
98 return ERRNO_NOERR;
99 }
100
NotifyToWatchNewEvents(const string & fileName,const int & wd,const uint32_t & watchEvents)101 int FileWatcher::NotifyToWatchNewEvents(const string &fileName, const int &wd, const uint32_t &watchEvents)
102 {
103 int newWd = inotify_add_watch(notifyFd_, fileName.c_str(), watchEvents);
104 if (newWd < 0) {
105 HILOGE("Failed to start new notify errCode:%{public}d", errno);
106 return errno;
107 }
108
109 if (newWd != wd) {
110 HILOGE("New notify wd is error");
111 return EIO;
112 }
113 wdFileNameMap_[fileName].second = watchEvents;
114 return ERRNO_NOERR;
115 }
116
CloseNotifyFd()117 int FileWatcher::CloseNotifyFd()
118 {
119 int closeRet = ERRNO_NOERR;
120 if (watcherInfoSet_.size() == 0) {
121 run_ = false;
122 closeRet = close(notifyFd_);
123 if (closeRet != 0) {
124 HILOGE("Failed to stop notify close fd errCode:%{public}d", errno);
125 }
126 notifyFd_ = -1;
127 closeRet = close(eventFd_);
128 if (closeRet != 0) {
129 HILOGE("Failed to close eventfd errCode:%{public}d", errno);
130 }
131 eventFd_ = -1;
132 }
133
134 closed_ = false;
135 return closeRet;
136 }
137
CloseNotifyFdLocked()138 int FileWatcher::CloseNotifyFdLocked()
139 {
140 {
141 lock_guard<mutex> lock(readMutex_);
142 closed_ = true;
143 if (reading_) {
144 HILOGE("close while reading");
145 return ERRNO_NOERR;
146 }
147 }
148 return CloseNotifyFd();
149 }
150
StopNotify(shared_ptr<WatcherInfoArg> arg)151 int FileWatcher::StopNotify(shared_ptr<WatcherInfoArg> arg)
152 {
153 unique_lock<mutex> lock(watchMutex_);
154 if (notifyFd_ < 0) {
155 HILOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_);
156 return EIO;
157 }
158 uint32_t newEvents = RemoveWatcherInfo(arg);
159 if (newEvents > 0) {
160 if (access(arg->fileName.c_str(), F_OK) == 0) {
161 return NotifyToWatchNewEvents(arg->fileName, arg->wd, newEvents);
162 }
163 HILOGE("The Watched file does not exist, and the remaining monitored events will be invalid.");
164 return ERRNO_NOERR;
165 }
166 int oldWd = -1;
167 {
168 lock_guard<mutex> lock(readMutex_);
169 if (!(closed_ && reading_)) {
170 oldWd = inotify_rm_watch(notifyFd_, arg->wd);
171 } else {
172 HILOGE("rm watch fail");
173 }
174 }
175 if (oldWd == -1) {
176 int rmErr = errno;
177 if (access(arg->fileName.c_str(), F_OK) == 0) {
178 HILOGE("Failed to stop notify errCode:%{public}d", rmErr);
179 wdFileNameMap_.erase(arg->fileName);
180 CloseNotifyFdLocked();
181 return rmErr;
182 }
183 }
184 wdFileNameMap_.erase(arg->fileName);
185 return CloseNotifyFdLocked();
186 }
187
ReadNotifyEvent(WatcherCallback callback)188 void FileWatcher::ReadNotifyEvent(WatcherCallback callback)
189 {
190 int len = 0;
191 int index = 0;
192 char buf[BUF_SIZE] = {0};
193 struct inotify_event *event = nullptr;
194 while (((len = read(notifyFd_, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {};
195 while (index < len) {
196 event = reinterpret_cast<inotify_event *>(buf + index);
197 NotifyEvent(event, callback);
198 index += sizeof(struct inotify_event) + static_cast<int>(event->len);
199 }
200 }
201
ReadNotifyEventLocked(WatcherCallback callback)202 void FileWatcher::ReadNotifyEventLocked(WatcherCallback callback)
203 {
204 {
205 lock_guard<mutex> lock(readMutex_);
206 if (closed_) {
207 HILOGE("read after close");
208 return;
209 }
210 reading_ = true;
211 }
212 ReadNotifyEvent(callback);
213 {
214 lock_guard<mutex> lock(readMutex_);
215 reading_ = false;
216 if (closed_) {
217 HILOGE("close after read");
218 CloseNotifyFd();
219 return;
220 }
221 }
222 }
223
GetNotifyEvent(WatcherCallback callback)224 void FileWatcher::GetNotifyEvent(WatcherCallback callback)
225 {
226 if (run_) {
227 return;
228 }
229 run_ = true;
230 nfds_t nfds = 2;
231 struct pollfd fds[2];
232 fds[0].fd = eventFd_;
233 fds[0].events = 0;
234 fds[1].fd = notifyFd_;
235 fds[1].events = POLLIN;
236 int ret = 0;
237 while (run_) {
238 ret = poll(fds, nfds, -1);
239 if (ret > 0) {
240 if (static_cast<unsigned short>(fds[0].revents) & POLLNVAL) {
241 run_ = false;
242 return;
243 }
244 if (static_cast<unsigned short>(fds[1].revents) & POLLIN) {
245 ReadNotifyEventLocked(callback);
246 }
247 } else if (ret < 0 && errno == EINTR) {
248 continue;
249 } else {
250 HILOGE("Failed to poll NotifyFd, errno=%{public}d", errno);
251 return;
252 }
253 }
254 }
255
AddWatcherInfo(const string & fileName,shared_ptr<WatcherInfoArg> arg)256 bool FileWatcher::AddWatcherInfo(const string &fileName, shared_ptr<WatcherInfoArg> arg)
257 {
258 for (auto &iter : watcherInfoSet_) {
259 if (iter->fileName == arg->fileName && iter->events == arg->events) {
260 bool isSame = false;
261 napi_strict_equals(iter->env, iter->nRef.Deref(iter->env).val_, arg->nRef.Deref(arg->env).val_, &isSame);
262 if (isSame) {
263 HILOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str());
264 return false;
265 }
266 }
267 }
268 watcherInfoSet_.insert(arg);
269 return true;
270 }
271
RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)272 uint32_t FileWatcher::RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)
273 {
274 watcherInfoSet_.erase(arg);
275 uint32_t otherEvents = 0;
276 for (const auto &iter : watcherInfoSet_) {
277 if (iter->fileName == arg->fileName && iter->wd > 0) {
278 otherEvents |= iter->events;
279 }
280 }
281 return otherEvents;
282 }
283
CheckIncludeEvent(const uint32_t & mask,const uint32_t & event)284 bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event)
285 {
286 if ((mask & event) > 0) {
287 return true;
288 }
289 return false;
290 }
291
NotifyEvent(const struct inotify_event * event,WatcherCallback callback)292 void FileWatcher::NotifyEvent(const struct inotify_event *event, WatcherCallback callback)
293 {
294 lock_guard<mutex> lock(watchMutex_);
295 string tempFileName;
296 auto found = find_if(wdFileNameMap_.begin(), wdFileNameMap_.end(),
297 [event](const pair<std::string, std::pair<int, uint32_t>> &iter) {
298 return iter.second.first == event->wd;
299 });
300 if (found != wdFileNameMap_.end()) {
301 tempFileName = found->first;
302 }
303
304 for (const auto &iter : watcherInfoSet_) {
305 string fileName = tempFileName;
306 uint32_t watchEvent = 0;
307 if ((iter->fileName == fileName) && (iter->wd > 0)) {
308 watchEvent = iter->events;
309 }
310 if (!CheckIncludeEvent(event->mask, watchEvent)) {
311 continue;
312 }
313 if (event->len > 0) {
314 fileName += "/" + string(event->name);
315 }
316 callback(iter->env, iter->nRef, fileName, event->mask & IN_ALL_EVENTS, event->cookie);
317 }
318 }
319
CheckEventValid(const uint32_t & event)320 bool FileWatcher::CheckEventValid(const uint32_t &event)
321 {
322 if ((event & IN_ALL_EVENTS) == event) {
323 return true;
324 } else {
325 HILOGE("Param event:%{public}x is not valid", event);
326 return false;
327 }
328 }
329 } // namespace OHOS::FileManagement::ModuleFileIO
330