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 closeRet = close(notifyFd_);
122 if (closeRet != 0) {
123 HILOGE("Failed to stop notify close fd errCode:%{public}d", errno);
124 }
125 notifyFd_ = -1;
126 closeRet = close(eventFd_);
127 if (closeRet != 0) {
128 HILOGE("Failed to close eventfd errCode:%{public}d", errno);
129 }
130 eventFd_ = -1;
131 run_ = false;
132 }
133
134 return closeRet;
135 }
136
StopNotify(shared_ptr<WatcherInfoArg> arg)137 int FileWatcher::StopNotify(shared_ptr<WatcherInfoArg> arg)
138 {
139 unique_lock<mutex> lock(watchMutex_);
140 if (notifyFd_ < 0) {
141 HILOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_);
142 return EIO;
143 }
144 uint32_t newEvents = RemoveWatcherInfo(arg);
145 if (newEvents > 0) {
146 if (access(arg->fileName.c_str(), F_OK) == 0) {
147 return NotifyToWatchNewEvents(arg->fileName, arg->wd, newEvents);
148 }
149 HILOGE("The Watched file does not exist, and the remaining monitored events will be invalid.");
150 return ERRNO_NOERR;
151 }
152 if (inotify_rm_watch(notifyFd_, arg->wd) == -1) {
153 int rmErr = errno;
154 if (access(arg->fileName.c_str(), F_OK) == 0) {
155 HILOGE("Failed to stop notify errCode:%{public}d", rmErr);
156 wdFileNameMap_.erase(arg->fileName);
157 CloseNotifyFd();
158 return rmErr;
159 }
160 }
161 wdFileNameMap_.erase(arg->fileName);
162 return CloseNotifyFd();
163 }
164
ReadNotifyEvent(WatcherCallback callback)165 void FileWatcher::ReadNotifyEvent(WatcherCallback callback)
166 {
167 int len = 0;
168 int index = 0;
169 char buf[BUF_SIZE] = {0};
170 struct inotify_event *event = nullptr;
171 while (((len = read(notifyFd_, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {};
172 while (index < len) {
173 event = reinterpret_cast<inotify_event *>(buf + index);
174 NotifyEvent(event, callback);
175 index += sizeof(struct inotify_event) + static_cast<int>(event->len);
176 }
177 }
178
GetNotifyEvent(WatcherCallback callback)179 void FileWatcher::GetNotifyEvent(WatcherCallback callback)
180 {
181 if (run_) {
182 return;
183 }
184 run_ = true;
185 nfds_t nfds = 2;
186 struct pollfd fds[2];
187 fds[0].fd = eventFd_;
188 fds[0].events = 0;
189 fds[1].fd = notifyFd_;
190 fds[1].events = POLLIN;
191 int ret = 0;
192 while (run_) {
193 ret = poll(fds, nfds, -1);
194 if (ret > 0) {
195 if (static_cast<unsigned short>(fds[0].revents) & POLLNVAL) {
196 run_ = false;
197 return;
198 }
199 if (static_cast<unsigned short>(fds[1].revents) & POLLIN) {
200 ReadNotifyEvent(callback);
201 }
202 } else if (ret < 0 && errno == EINTR) {
203 continue;
204 } else {
205 HILOGE("Failed to poll NotifyFd, errno=%{public}d", errno);
206 return;
207 }
208 }
209 }
210
AddWatcherInfo(const string & fileName,shared_ptr<WatcherInfoArg> arg)211 bool FileWatcher::AddWatcherInfo(const string &fileName, shared_ptr<WatcherInfoArg> arg)
212 {
213 for (auto &iter : watcherInfoSet_) {
214 if (iter->fileName == arg->fileName && iter->events == arg->events) {
215 bool isSame = false;
216 napi_strict_equals(iter->env, iter->nRef.Deref(iter->env).val_, arg->nRef.Deref(arg->env).val_, &isSame);
217 if (isSame) {
218 HILOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str());
219 return false;
220 }
221 }
222 }
223 watcherInfoSet_.insert(arg);
224 return true;
225 }
226
RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)227 uint32_t FileWatcher::RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)
228 {
229 watcherInfoSet_.erase(arg);
230 uint32_t otherEvents = 0;
231 for (const auto &iter : watcherInfoSet_) {
232 if (iter->fileName == arg->fileName && iter->wd > 0) {
233 otherEvents |= iter->events;
234 }
235 }
236 return otherEvents;
237 }
238
CheckIncludeEvent(const uint32_t & mask,const uint32_t & event)239 bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event)
240 {
241 if ((mask & event) > 0) {
242 return true;
243 }
244 return false;
245 }
246
NotifyEvent(const struct inotify_event * event,WatcherCallback callback)247 void FileWatcher::NotifyEvent(const struct inotify_event *event, WatcherCallback callback)
248 {
249 lock_guard<mutex> lock(watchMutex_);
250 string tempFileName;
251 auto found = find_if(wdFileNameMap_.begin(), wdFileNameMap_.end(),
252 [event](const pair<std::string, std::pair<int, uint32_t>> &iter) {
253 return iter.second.first == event->wd;
254 });
255 if (found != wdFileNameMap_.end()) {
256 tempFileName = found->first;
257 }
258
259 for (const auto &iter : watcherInfoSet_) {
260 string fileName = tempFileName;
261 uint32_t watchEvent = 0;
262 if ((iter->fileName == fileName) && (iter->wd > 0)) {
263 watchEvent = iter->events;
264 }
265 if (!CheckIncludeEvent(event->mask, watchEvent)) {
266 continue;
267 }
268 if (event->len > 0) {
269 fileName += "/" + string(event->name);
270 }
271 callback(iter->env, iter->nRef, fileName, event->mask & IN_ALL_EVENTS, event->cookie);
272 }
273 }
274
CheckEventValid(const uint32_t & event)275 bool FileWatcher::CheckEventValid(const uint32_t &event)
276 {
277 if ((event & IN_ALL_EVENTS) == event) {
278 return true;
279 } else {
280 HILOGE("Param event:%{public}x is not valid", event);
281 return false;
282 }
283 }
284 } // namespace OHOS::FileManagement::ModuleFileIO
285