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