• 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 #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