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 <unistd.h>
18
19 using namespace OHOS::CJSystemapi::FileFs;
20
21 namespace OHOS::CJSystemapi {
22 using namespace std;
23
24 mutex WatcherImpl::watchMutex_;
25
WatcherImpl()26 WatcherImpl::WatcherImpl() {}
27
GetNotifyId()28 int32_t WatcherImpl::GetNotifyId()
29 {
30 return notifyFd_;
31 }
32
InitNotify()33 bool WatcherImpl::InitNotify()
34 {
35 notifyFd_ = inotify_init();
36 if (notifyFd_ < 0) {
37 LOGE("Failed to init notify errCode:%{public}d", errno);
38 return false;
39 }
40 return true;
41 }
42
CheckEventWatched(const string & fileName,const uint32_t & event)43 tuple<bool, int> WatcherImpl::CheckEventWatched(const string &fileName, const uint32_t &event)
44 {
45 int wd = -1;
46 auto iter = wdFileNameMap_.find(fileName);
47 if (iter == wdFileNameMap_.end()) {
48 return {false, wd};
49 }
50 wd = iter->second.first;
51 if ((iter->second.second & event) == event) {
52 return {true, wd};
53 }
54 return {false, wd};
55 }
56
AddWatcherInfo(const string & fileName,shared_ptr<WatcherInfoArg> arg)57 bool WatcherImpl::AddWatcherInfo(const string &fileName, shared_ptr<WatcherInfoArg> arg)
58 {
59 for (auto const &iter : watcherInfoSet_) {
60 if (iter->fileName == arg->fileName && iter->events == arg->events) {
61 LOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str());
62 return false;
63 }
64 }
65 watcherInfoSet_.insert(arg);
66 return true;
67 }
68
CheckIncludeEvent(const uint32_t & mask,const uint32_t & event)69 bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event)
70 {
71 if ((mask & event) > 0) {
72 return true;
73 }
74 return false;
75 }
76
RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)77 uint32_t WatcherImpl::RemoveWatcherInfo(shared_ptr<WatcherInfoArg> arg)
78 {
79 watcherInfoSet_.erase(arg);
80 uint32_t otherEvents = 0;
81 for (const auto &iter : watcherInfoSet_) {
82 if (iter->fileName == arg->fileName && iter->wd > 0) {
83 otherEvents |= iter->events;
84 }
85 }
86 return otherEvents;
87 }
88
StartNotify()89 int32_t WatcherImpl::StartNotify()
90 {
91 lock_guard<mutex> lock(watchMutex_);
92 if (notifyFd_ < 0) {
93 LOGE("Failed to start notify notifyFd_:%{public}d", notifyFd_);
94 return GetErrorCode(EIO);
95 }
96 auto [isWatched, wd] = CheckEventWatched(data_->fileName, data_->events);
97 if (isWatched && wd > 0) {
98 data_->wd = wd;
99 return SUCCESS_CODE;
100 }
101 uint32_t watchEvents = 0;
102 if (wd != -1) {
103 watchEvents = wdFileNameMap_[data_->fileName].second | data_->events;
104 } else {
105 watchEvents = data_->events;
106 }
107 int newWd = inotify_add_watch(notifyFd_, data_->fileName.c_str(), watchEvents);
108 if (newWd < 0) {
109 LOGE("Failed to start notify errCode:%{public}d", errno);
110 return GetErrorCode(errno);
111 }
112 data_->wd = newWd;
113 wdFileNameMap_[data_->fileName].first = newWd;
114 wdFileNameMap_[data_->fileName].second = watchEvents;
115 return SUCCESS_CODE;
116 }
117
NotifyToWatchNewEvents(const string & fileName,const int & wd,const uint32_t & watchEvents)118 int WatcherImpl::NotifyToWatchNewEvents(const string &fileName, const int &wd, const uint32_t &watchEvents)
119 {
120 int newWd = inotify_add_watch(notifyFd_, fileName.c_str(), watchEvents);
121 if (newWd < 0) {
122 LOGE("Failed to start new notify errCode:%{public}d", errno);
123 return GetErrorCode(errno);
124 }
125
126 if (newWd != wd) {
127 LOGE("New notify wd is error");
128 return GetErrorCode(EIO);
129 }
130 wdFileNameMap_[fileName].second = watchEvents;
131 return SUCCESS_CODE;
132 }
133
GetNotifyEvent()134 void WatcherImpl::GetNotifyEvent()
135 {
136 if (run_) {
137 return;
138 }
139 run_ = true;
140 char buf[BUF_SIZE] = {0};
141 struct inotify_event *event = nullptr;
142 fd_set fds;
143 FD_ZERO(&fds);
144 FD_SET(notifyFd_, &fds);
145 while (run_) {
146 if (notifyFd_ < 0) {
147 LOGE("Failed to run Listener Thread because notifyFd_:%{public}d", notifyFd_);
148 break;
149 }
150 if (select(notifyFd_ + 1, &fds, nullptr, nullptr, nullptr) > 0) {
151 int len = 0;
152 int index = 0;
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);
157 index += static_cast<int32_t>(sizeof(struct inotify_event) + event->len);
158 }
159 }
160 }
161 }
162
NotifyEvent(const struct inotify_event * event)163 void WatcherImpl::NotifyEvent(const struct inotify_event *event)
164 {
165 lock_guard<mutex> lock(watchMutex_);
166 string tempFileName;
167 auto found = find_if(wdFileNameMap_.begin(), wdFileNameMap_.end(),
168 [event](const pair<std::string, std::pair<int, uint32_t>> &iter) {
169 return iter.second.first == event->wd;
170 });
171 if (found != wdFileNameMap_.end()) {
172 tempFileName = found->first;
173 }
174
175 for (const auto &iter : watcherInfoSet_) {
176 string fileName = tempFileName;
177 uint32_t watchEvent = 0;
178 if ((iter->fileName == fileName) && (iter->wd > 0)) {
179 watchEvent = iter->events;
180 }
181 if (!CheckIncludeEvent(event->mask, watchEvent)) {
182 continue;
183 }
184 if (event->len > 0) {
185 fileName += "/" + string(event->name);
186 }
187 CWatchEvent ret = {
188 .fileName = fileName.c_str(),
189 .event = event->mask & IN_ALL_EVENTS,
190 .cookie = event->cookie };
191 data_->watchCallback_(ret);
192 }
193 }
194
CloseNotifyFd()195 int WatcherImpl::CloseNotifyFd()
196 {
197 int closeRet = SUCCESS_CODE;
198 if (watcherInfoSet_.size() == 0) {
199 closeRet = close(notifyFd_);
200 if (closeRet != 0) {
201 LOGE("Failed to stop notify close fd errCode:%{public}d", closeRet);
202 }
203 notifyFd_ = -1;
204 run_ = false;
205 }
206
207 return closeRet;
208 }
209
StopNotify()210 int32_t WatcherImpl::StopNotify()
211 {
212 unique_lock<mutex> lock(watchMutex_);
213 if (notifyFd_ < 0) {
214 LOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_);
215 return EIO;
216 }
217 uint32_t newEvents = RemoveWatcherInfo(data_);
218 if (newEvents > 0) {
219 return NotifyToWatchNewEvents(data_->fileName, data_->wd, newEvents);
220 }
221 if (inotify_rm_watch(notifyFd_, data_->wd) == -1) {
222 int rmErr = errno;
223 if (access(data_->fileName.c_str(), F_OK) == 0) {
224 LOGE("Failed to stop notify errCode:%{public}d", rmErr);
225 wdFileNameMap_.erase(data_->fileName);
226 CloseNotifyFd();
227 return rmErr;
228 }
229 }
230 wdFileNameMap_.erase(data_->fileName);
231 return CloseNotifyFd();
232 }
233
CheckEventValid(const uint32_t & event)234 bool WatcherImpl::CheckEventValid(const uint32_t &event)
235 {
236 if ((event & IN_ALL_EVENTS) == event) {
237 return true;
238 } else {
239 LOGE("Param event:%{public}x is not valid", event);
240 return false;
241 }
242 }
243
244 }