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 }