• 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) {
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