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 && run_) {
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 HILOGE("rm watch failed, err: %{public}d", errno);
172 } else {
173 HILOGE("rm watch fail");
174 }
175 }
176 if (oldWd == -1) {
177 int rmErr = errno;
178 if (access(arg->fileName.c_str(), F_OK) == 0) {
179 HILOGE("Failed to stop notify errCode:%{public}d", rmErr);
180 wdFileNameMap_.erase(arg->fileName);
181 CloseNotifyFdLocked();
182 return rmErr;
183 }
184 }
185 wdFileNameMap_.erase(arg->fileName);
186 return CloseNotifyFdLocked();
187 }
188
ReadNotifyEvent(WatcherCallback callback)189 void FileWatcher::ReadNotifyEvent(WatcherCallback callback)
190 {
191 int len = 0;
192 int index = 0;
193 char buf[BUF_SIZE] = {0};
194 struct inotify_event *event = nullptr;
195 while (((len = read(notifyFd_, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {};
196 while (index < len) {
197 event = reinterpret_cast<inotify_event *>(buf + index);
198 if ((len - index) < sizeof(struct inotify_event)) {
199 HILOGE("out of bounds access, len:%{public}d, index: %{public}d, inotify: %{public}zu",
200 len, index, sizeof(struct inotify_event));
201 break;
202 }
203 if (event->len > (static_cast<unsigned int>(len - index - sizeof(struct inotify_event)))) {
204 HILOGE("out of bounds access, index: %{public}d, inotify: %{public}zu, "
205 "event :%{public}u, len: %{public}d",
206 index, sizeof(struct inotify_event),
207 event->len, len);
208 break;
209 }
210 NotifyEvent(event, callback);
211 index += sizeof(struct inotify_event) + static_cast<int>(event->len);
212 }
213 }
214
ReadNotifyEventLocked(WatcherCallback callback)215 void FileWatcher::ReadNotifyEventLocked(WatcherCallback callback)
216 {
217 {
218 lock_guard<mutex> lock(readMutex_);
219 if (closed_) {
220 HILOGE("read after close");
221 return;
222 }
223 reading_ = true;
224 }
225 ReadNotifyEvent(callback);
226 {
227 lock_guard<mutex> watcherLock(watchMutex_);
228 lock_guard<mutex> lock(readMutex_);
229 reading_ = false;
230 if (closed_) {
231 HILOGE("close after read");
232 CloseNotifyFd();
233 return;
234 }
235 }
236 }
237
GetNotifyEvent(WatcherCallback callback)238 void FileWatcher::GetNotifyEvent(WatcherCallback callback)
239 {
240 if (run_) {
241 return;
242 }
243 run_ = true;
244 nfds_t nfds = 2;
245 struct pollfd fds[2];
246 fds[0].fd = eventFd_;
247 fds[0].events = 0;
248 fds[1].fd = notifyFd_;
249 fds[1].events = POLLIN;
250 int ret = 0;
251 while (run_) {
252 ret = poll(fds, nfds, -1);
253 if (ret > 0) {
254 if (static_cast<unsigned short>(fds[0].revents) & POLLNVAL) {
255 run_ = false;
256 return;
257 }
258 if (static_cast<unsigned short>(fds[1].revents) & POLLIN) {
259 ReadNotifyEventLocked(callback);
260 }
261 } else if (ret < 0 && errno == EINTR) {
262 continue;
263 } else {
264 HILOGE("Failed to poll NotifyFd, errno=%{public}d", errno);
265 return;
266 }
267 }
268 }
269
AddWatcherInfo(const string & fileName,shared_ptr<WatcherInfoArg> arg)270 bool FileWatcher::AddWatcherInfo(const string &fileName, shared_ptr<WatcherInfoArg> arg)
271 {
272 for (auto &iter : watcherInfoSet_) {
273 if (iter->fileName == arg->fileName && iter->events == arg->events) {
274 bool isSame = false;
275 napi_strict_equals(iter->env, iter->nRef.Deref(iter->env).val_, arg->nRef.Deref(arg->env).val_, &isSame);
276 if (isSame) {
277 HILOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str());
278 return false;
279 }
280 }
281 }
282 watcherInfoSet_.insert(arg);
283 return true;
284 }
285
RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)286 uint32_t FileWatcher::RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)
287 {
288 watcherInfoSet_.erase(arg);
289 uint32_t otherEvents = 0;
290 for (const auto &iter : watcherInfoSet_) {
291 if (iter->fileName == arg->fileName && iter->wd > 0) {
292 otherEvents |= iter->events;
293 }
294 }
295 return otherEvents;
296 }
297
CheckIncludeEvent(const uint32_t & mask,const uint32_t & event)298 bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event)
299 {
300 if ((mask & event) > 0) {
301 return true;
302 }
303 return false;
304 }
305
NotifyEvent(const struct inotify_event * event,WatcherCallback callback)306 void FileWatcher::NotifyEvent(const struct inotify_event *event, WatcherCallback callback)
307 {
308 lock_guard<mutex> lock(watchMutex_);
309 string tempFileName;
310 auto found = find_if(wdFileNameMap_.begin(), wdFileNameMap_.end(),
311 [event](const pair<std::string, std::pair<int, uint32_t>> &iter) {
312 return iter.second.first == event->wd;
313 });
314 if (found != wdFileNameMap_.end()) {
315 tempFileName = found->first;
316 }
317
318 for (const auto &iter : watcherInfoSet_) {
319 string fileName = tempFileName;
320 uint32_t watchEvent = 0;
321 if ((iter->fileName == fileName) && (iter->wd > 0)) {
322 watchEvent = iter->events;
323 }
324 if (!CheckIncludeEvent(event->mask, watchEvent)) {
325 continue;
326 }
327 if (event->len > 0) {
328 fileName += "/" + string(event->name);
329 }
330 callback(iter->env, iter->nRef, fileName, event->mask & IN_ALL_EVENTS, event->cookie);
331 }
332 }
333
CheckEventValid(const uint32_t & event)334 bool FileWatcher::CheckEventValid(const uint32_t &event)
335 {
336 if ((event & IN_ALL_EVENTS) == event) {
337 return true;
338 } else {
339 HILOGE("Param event:%{public}x is not valid", event);
340 return false;
341 }
342 }
343 } // namespace OHOS::FileManagement::ModuleFileIO
344