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