• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         size_t 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             free(ret.fileName);
233             continue;
234         }
235         callback(ret);
236         free(ret.fileName);
237     }
238 }
239 
CloseNotifyFd()240 int FileWatcherManager::CloseNotifyFd()
241 {
242     int closeRet = SUCCESS_CODE;
243     if (watcherInfoSet_.size() == 0) {
244         closeRet = close(notifyFd_);
245         if (closeRet != 0) {
246             LOGE("Failed to stop notify close fd errCode:%{public}d", closeRet);
247         }
248         notifyFd_ = -1;
249         closeRet = close(eventFd_);
250         if (closeRet != 0) {
251             LOGE("Failed to close eventfd errCode:%{public}d", closeRet);
252         }
253         eventFd_ = -1;
254         run_ = false;
255     }
256 
257     return closeRet;
258 }
259 
StopNotify(std::shared_ptr<WatcherInfoArg> arg)260 int32_t FileWatcherManager::StopNotify(std::shared_ptr<WatcherInfoArg> arg)
261 {
262     unique_lock<mutex> lock(watchMutex_);
263     if (notifyFd_ < 0) {
264         LOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_);
265         return EIO;
266     }
267     uint32_t newEvents = RemoveWatcherInfo(arg);
268     if (newEvents > 0) {
269         if (access(arg->fileName.c_str(), F_OK) == 0) {
270             return NotifyToWatchNewEvents(arg->fileName, arg->wd, newEvents);
271         }
272         LOGE("The Watched file does not exist, and the remaining monitored events will be invalid.");
273         return SUCCESS_CODE;
274     }
275     if (inotify_rm_watch(notifyFd_, arg->wd) == -1) {
276         int rmErr = errno;
277         if (access(arg->fileName.c_str(), F_OK) == 0) {
278             LOGE("Failed to stop notify errCode:%{public}d", rmErr);
279             wdFileNameMap_.erase(arg->fileName);
280             CloseNotifyFd();
281             return rmErr;
282         }
283     }
284     wdFileNameMap_.erase(arg->fileName);
285     return CloseNotifyFd();
286 }
287 
CheckEventValid(const uint32_t & event)288 bool FileWatcherManager::CheckEventValid(const uint32_t &event)
289 {
290     if ((event & IN_ALL_EVENTS) == event) {
291         return true;
292     } else {
293         LOGE("Param event:%{public}x is not valid", event);
294         return false;
295     }
296 }
297 
298 }