1 /*
2  * Copyright (c) 2024 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 
16 #include "watcher_impl.h"
17 #include <poll.h>
18 #include <sys/eventfd.h>
19 #include <thread>
20 #include <unistd.h>
21 #include "securec.h"
22 
23 using namespace OHOS::CJSystemapi::FileFs;
24 
25 namespace OHOS::CJSystemapi {
26 using namespace std;
27 
28 mutex FileWatcherManager::watchMutex_;
29 
FileWatcherManager()30 FileWatcherManager::FileWatcherManager() {}
31 
~FileWatcherManager()32 FileWatcherManager::~FileWatcherManager() {}
33 
GetNotifyId()34 int32_t FileWatcherManager::GetNotifyId()
35 {
36     return notifyFd_;
37 }
38 
InitNotify()39 bool FileWatcherManager::InitNotify()
40 {
41     notifyFd_ = inotify_init();
42     if (notifyFd_ < 0) {
43         LOGE("Failed to init notify errCode:%{public}d", errno);
44         return false;
45     }
46     eventFd_ = eventfd(0, EFD_CLOEXEC);
47     if (eventFd_ < 0) {
48         LOGE("Failed to init eventfd errCode:%{public}d", errno);
49         return false;
50     }
51     return true;
52 }
53 
CheckEventWatched(const string &fileName, const uint32_t &event)54 tuple<bool, int> FileWatcherManager::CheckEventWatched(const string &fileName, const uint32_t &event)
55 {
56     int wd = -1;
57     auto iter = wdFileNameMap_.find(fileName);
58     if (iter == wdFileNameMap_.end()) {
59         return {false, wd};
60     }
61     wd = iter->second.first;
62     if ((iter->second.second & event) == event) {
63         return {true, wd};
64     }
65     return {false, wd};
66 }
67 
AddWatcherInfo(const string &fileName, shared_ptr<WatcherInfoArg> arg)68 bool FileWatcherManager::AddWatcherInfo(const string &fileName, shared_ptr<WatcherInfoArg> arg)
69 {
70     for (auto const &iter : watcherInfoSet_) {
71         if (iter->fileName == arg->fileName && iter->events == arg->events) {
72             if (iter->callbackId == arg->callbackId) {
73                 LOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str());
74                 return false;
75             }
76         }
77     }
78     watcherInfoSet_.insert(arg);
79     return true;
80 }
81 
CheckIncludeEvent(const uint32_t &mask, const uint32_t &event)82 bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event)
83 {
84     if ((mask & event) > 0) {
85         return true;
86     }
87     return false;
88 }
89 
RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)90 uint32_t FileWatcherManager::RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)
91 {
92     watcherInfoSet_.erase(arg);
93     uint32_t otherEvents = 0;
94     for (const auto &iter : watcherInfoSet_) {
95         if (iter->fileName == arg->fileName && iter->wd > 0) {
96             otherEvents |= iter->events;
97         }
98     }
99     return otherEvents;
100 }
101 
StartNotify(std::shared_ptr<WatcherInfoArg> arg)102 int32_t FileWatcherManager::StartNotify(std::shared_ptr<WatcherInfoArg> arg)
103 {
104     lock_guard<mutex> lock(watchMutex_);
105     if (notifyFd_ < 0) {
106         LOGE("Failed to start notify notifyFd_:%{public}d", notifyFd_);
107         return GetErrorCode(EIO);
108     }
109     auto [isWatched, wd] = CheckEventWatched(arg->fileName, arg->events);
110     if (isWatched && wd > 0) {
111         arg->wd = wd;
112         return SUCCESS_CODE;
113     }
114     uint32_t watchEvents = 0;
115     if (wd != -1) {
116         watchEvents = wdFileNameMap_[arg->fileName].second | arg->events;
117     } else {
118         watchEvents = arg->events;
119     }
120     int newWd = inotify_add_watch(notifyFd_, arg->fileName.c_str(), watchEvents);
121     if (newWd < 0) {
122         LOGE("Failed to start notify errCode:%{public}d", errno);
123         return GetErrorCode(errno);
124     }
125     arg->wd = newWd;
126     wdFileNameMap_[arg->fileName].first = newWd;
127     wdFileNameMap_[arg->fileName].second = watchEvents;
128     return SUCCESS_CODE;
129 }
130 
NotifyToWatchNewEvents(const string &fileName, const int &wd, const uint32_t &watchEvents)131 int FileWatcherManager::NotifyToWatchNewEvents(const string &fileName, const int &wd, const uint32_t &watchEvents)
132 {
133     int newWd = inotify_add_watch(notifyFd_, fileName.c_str(), watchEvents);
134     if (newWd < 0) {
135         LOGE("Failed to start new notify errCode:%{public}d", errno);
136         return GetErrorCode(errno);
137     }
138 
139     if (newWd != wd) {
140         LOGE("New notify wd is error");
141         return GetErrorCode(EIO);
142     }
143     wdFileNameMap_[fileName].second = watchEvents;
144     return SUCCESS_CODE;
145 }
146 
ReadNotifyEvent(std::function<void(CWatchEvent)> callback)147 void FileWatcherManager::ReadNotifyEvent(std::function<void(CWatchEvent)> callback)
148 {
149     int len = 0;
150     int index = 0;
151     char buf[BUF_SIZE] = {0};
152     struct inotify_event *event = nullptr;
153     while (((len = read(notifyFd_, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {};
154     while (index < len) {
155         event = reinterpret_cast<inotify_event *>(buf + index);
156         NotifyEvent(event, callback);
157         index += sizeof(struct inotify_event) + static_cast<int>(event->len);
158     }
159 }
160 
GetNotifyEvent(std::shared_ptr<WatcherInfoArg> arg)161 void FileWatcherManager::GetNotifyEvent(std::shared_ptr<WatcherInfoArg> arg)
162 {
163     if (run_) {
164         return;
165     }
166     auto fileListener = [this, arg]() {
167         run_ = true;
168         nfds_t nfds = 2;
169         struct pollfd fds[2];
170         fds[0].fd = eventFd_;
171         fds[0].events = 0;
172         fds[1].fd = notifyFd_;
173         fds[1].events = POLLIN;
174         while (run_) {
175             int ret = poll(fds, nfds, -1);
176             if (ret > 0) {
177                 if (static_cast<unsigned short>(fds[0].revents) & POLLNVAL) {
178                     run_ = false;
179                     return;
180                 }
181                 if (static_cast<unsigned short>(fds[1].revents) & POLLIN) {
182                     ReadNotifyEvent(arg->watchCallback_);
183                 }
184             } else if (ret < 0 && errno == EINTR) {
185                 continue;
186             } else {
187                 LOGE("Failed to poll NotifyFd, errno=%{public}d", errno);
188                 return;
189             }
190         }
191     };
192     std::thread(fileListener).detach();
193 }
194 
NotifyEvent(const struct inotify_event *event, std::function<void(CWatchEvent)> callback)195 void FileWatcherManager::NotifyEvent(const struct inotify_event *event, std::function<void(CWatchEvent)> callback)
196 {
197     lock_guard<mutex> lock(watchMutex_);
198     string tempFileName;
199     auto found = find_if(wdFileNameMap_.begin(), wdFileNameMap_.end(),
200         [event](const pair<std::string, std::pair<int, uint32_t>> &iter) {
201             return iter.second.first == event->wd;
202     });
203     if (found != wdFileNameMap_.end()) {
204         tempFileName = found->first;
205     }
206 
207     for (const auto &iter : watcherInfoSet_) {
208         string fileName = tempFileName;
209         uint32_t watchEvent = 0;
210         if ((iter->fileName == fileName) && (iter->wd > 0)) {
211             watchEvent = iter->events;
212         }
213         if (!CheckIncludeEvent(event->mask, watchEvent)) {
214             continue;
215         }
216         if (event->len > 0) {
217             fileName += "/" + string(event->name);
218         }
219         CWatchEvent ret = {
220             .fileName = nullptr,
221             .event = event->mask & IN_ALL_EVENTS,
222             .cookie = event->cookie };
223         int len = fileName.size() + 1;
224         ret.fileName = (char*)malloc(len);
225         if (ret.fileName == nullptr) {
226             LOGE("Failed to create WatchEvent");
227             continue;
228         }
229         int errCode = memcpy_s(ret.fileName, len, fileName.c_str(), len);
230         if (errCode != 0) {
231             LOGE("Failed to get file name");
232             continue;
233         }
234         callback(ret);
235         free(ret.fileName);
236     }
237 }
238 
CloseNotifyFd()239 int FileWatcherManager::CloseNotifyFd()
240 {
241     int closeRet = SUCCESS_CODE;
242     if (watcherInfoSet_.size() == 0) {
243         closeRet = close(notifyFd_);
244         if (closeRet != 0) {
245             LOGE("Failed to stop notify close fd errCode:%{public}d", closeRet);
246         }
247         notifyFd_ = -1;
248         closeRet = close(eventFd_);
249         if (closeRet != 0) {
250             LOGE("Failed to close eventfd errCode:%{public}d", closeRet);
251         }
252         eventFd_ = -1;
253         run_ = false;
254     }
255 
256     return closeRet;
257 }
258 
StopNotify(std::shared_ptr<WatcherInfoArg> arg)259 int32_t FileWatcherManager::StopNotify(std::shared_ptr<WatcherInfoArg> arg)
260 {
261     unique_lock<mutex> lock(watchMutex_);
262     if (notifyFd_ < 0) {
263         LOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_);
264         return EIO;
265     }
266     uint32_t newEvents = RemoveWatcherInfo(arg);
267     if (newEvents > 0) {
268         if (access(arg->fileName.c_str(), F_OK) == 0) {
269             return NotifyToWatchNewEvents(arg->fileName, arg->wd, newEvents);
270         }
271         LOGE("The Watched file does not exist, and the remaining monitored events will be invalid.");
272         return SUCCESS_CODE;
273     }
274     if (inotify_rm_watch(notifyFd_, arg->wd) == -1) {
275         int rmErr = errno;
276         if (access(arg->fileName.c_str(), F_OK) == 0) {
277             LOGE("Failed to stop notify errCode:%{public}d", rmErr);
278             wdFileNameMap_.erase(arg->fileName);
279             CloseNotifyFd();
280             return rmErr;
281         }
282     }
283     wdFileNameMap_.erase(arg->fileName);
284     return CloseNotifyFd();
285 }
286 
CheckEventValid(const uint32_t &event)287 bool FileWatcherManager::CheckEventValid(const uint32_t &event)
288 {
289     if ((event & IN_ALL_EVENTS) == event) {
290         return true;
291     } else {
292         LOGE("Param event:%{public}x is not valid", event);
293         return false;
294     }
295 }
296 
297 }