• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "fs_file_watcher.h"
16 
17 #include <algorithm>
18 #include <cerrno>
19 #include <cstdint>
20 #include <poll.h>
21 #include <unistd.h>
22 #include <sys/eventfd.h>
23 
24 #include "filemgmt_libhilog.h"
25 #include "uv.h"
26 
27 namespace OHOS::FileManagement::ModuleFileIO {
28 using namespace std;
29 
GetNotifyId()30 int32_t FsFileWatcher::GetNotifyId()
31 {
32     return notifyFd_;
33 }
34 
InitNotify()35 bool FsFileWatcher::InitNotify()
36 {
37     notifyFd_ = inotify_init();
38     if (notifyFd_ < 0) {
39         HILOGE("Failed to init notify errCode:%{public}d", errno);
40         return false;
41     }
42     eventFd_ = eventfd(0, EFD_CLOEXEC);
43     if (eventFd_ < 0) {
44         HILOGE("Failed to init eventfd errCode:%{public}d", errno);
45         return false;
46     }
47     return true;
48 }
49 
StartNotify(shared_ptr<WatcherInfo> info)50 int32_t FsFileWatcher::StartNotify(shared_ptr<WatcherInfo> info)
51 {
52     if (!info) {
53         HILOGE("Invalid param: info");
54         return EINVAL;
55     }
56 
57     if (notifyFd_ < 0) {
58         HILOGE("Failed to start notify notifyFd_:%{public}d", notifyFd_);
59         return EIO;
60     }
61 
62     auto [isWatched, wd] = dataCache_.FindWatchedWd(info->fileName, info->events);
63     if (isWatched && wd > 0) {
64         info->wd = wd;
65         return ERRNO_NOERR;
66     }
67 
68     uint32_t watchEvents = 0;
69     if (wd != -1) {
70         watchEvents = dataCache_.GetFileEvents(info->fileName) | info->events;
71     } else {
72         watchEvents = info->events;
73     }
74 
75     int32_t newWd = inotify_add_watch(notifyFd_, info->fileName.c_str(), watchEvents);
76     if (newWd < 0) {
77         HILOGE("Failed to start notify errCode:%{public}d", errno);
78         return errno;
79     }
80 
81     info->wd = newWd;
82     dataCache_.UpdateWatchedEvents(info->fileName, newWd, watchEvents);
83     return ERRNO_NOERR;
84 }
85 
NotifyToWatchNewEvents(const string & fileName,int32_t wd,uint32_t watchEvents)86 int32_t FsFileWatcher::NotifyToWatchNewEvents(const string &fileName, int32_t wd, uint32_t watchEvents)
87 {
88     int32_t newWd = inotify_add_watch(notifyFd_, fileName.c_str(), watchEvents);
89     if (newWd < 0) {
90         HILOGE("Failed to start new notify errCode:%{public}d", errno);
91         return errno;
92     }
93 
94     if (newWd != wd) {
95         HILOGE("New notify wd is error");
96         return EIO;
97     }
98 
99     dataCache_.UpdateWatchedEvents(fileName, wd, watchEvents);
100     return ERRNO_NOERR;
101 }
102 
CloseNotifyFd()103 int32_t FsFileWatcher::CloseNotifyFd()
104 {
105     int32_t closeRet = ERRNO_NOERR;
106 
107     if (!dataCache_.HasWatcherInfo()) {
108         run_ = false;
109         closeRet = close(notifyFd_);
110         if (closeRet != 0) {
111             HILOGE("Failed to stop notify close fd errCode:%{public}d", errno);
112         }
113         notifyFd_ = -1;
114 
115         closeRet = close(eventFd_);
116         if (closeRet != 0) {
117             HILOGE("Failed to close eventfd errCode:%{public}d", errno);
118         }
119         eventFd_ = -1;
120         DestroyTaskThead();
121     }
122 
123     closed_ = false;
124     return closeRet;
125 }
126 
CloseNotifyFdLocked()127 int32_t FsFileWatcher::CloseNotifyFdLocked()
128 {
129     {
130         lock_guard<mutex> lock(readMutex_);
131         closed_ = true;
132         if (reading_) {
133             HILOGE("close while reading");
134             return ERRNO_NOERR;
135         }
136     }
137     return CloseNotifyFd();
138 }
139 
StopNotify(shared_ptr<WatcherInfo> info)140 int32_t FsFileWatcher::StopNotify(shared_ptr<WatcherInfo> info)
141 {
142     if (!info) {
143         HILOGE("Invalid param: info");
144         return EINVAL;
145     }
146 
147     if (notifyFd_ < 0) {
148         HILOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_);
149         return EIO;
150     }
151 
152     uint32_t remainingEvents = RemoveWatcherInfo(info);
153     if (remainingEvents > 0) {
154         // There are still events remaining to be listened for.
155         if (access(info->fileName.c_str(), F_OK) == 0) {
156             return NotifyToWatchNewEvents(info->fileName, info->wd, remainingEvents);
157         }
158         HILOGE("The Watched file does not exist, and the remaining monitored events will be invalid.");
159         return ERRNO_NOERR;
160     }
161 
162     // No events remain to be listened for, and proceed to the file watch removal process.
163     int32_t oldWd = -1;
164     {
165         lock_guard<mutex> lock(readMutex_);
166         if (!(closed_ && reading_)) {
167             oldWd = inotify_rm_watch(notifyFd_, info->wd);
168         } else {
169             HILOGE("Rm watch fail");
170         }
171     }
172 
173     if (oldWd == -1) {
174         int32_t rmErr = errno;
175         if (access(info->fileName.c_str(), F_OK) == 0) {
176             HILOGE("Failed to stop notify errCode:%{public}d", rmErr);
177             dataCache_.RemoveFileWatcher(info->fileName);
178             CloseNotifyFdLocked();
179             return rmErr;
180         }
181     }
182 
183     dataCache_.RemoveFileWatcher(info->fileName);
184     return CloseNotifyFdLocked();
185 }
186 
ReadNotifyEvent()187 void FsFileWatcher::ReadNotifyEvent()
188 {
189     int32_t len = 0;
190     int32_t index = 0;
191     char buf[BUF_SIZE] = { 0 };
192     struct inotify_event *event = nullptr;
193 
194     do {
195         len = read(notifyFd_, &buf, sizeof(buf));
196         if (len < 0 && errno != EINTR) {
197             HILOGE("Read notify event failed! ret: %d", errno);
198             break;
199         }
200     } while (len < 0);
201 
202     while (index < len) {
203         event = reinterpret_cast<inotify_event *>(buf + index);
204         NotifyEvent(event);
205         index += sizeof(struct inotify_event) + static_cast<int32_t>(event->len);
206     }
207 }
208 
ReadNotifyEventLocked()209 void FsFileWatcher::ReadNotifyEventLocked()
210 {
211     {
212         lock_guard<mutex> lock(readMutex_);
213         if (closed_) {
214             HILOGE("Read after close");
215             return;
216         }
217         reading_ = true;
218     }
219 
220     ReadNotifyEvent();
221 
222     {
223         lock_guard<mutex> lock(readMutex_);
224         reading_ = false;
225         if (closed_) {
226             HILOGE("Close after read");
227             CloseNotifyFd();
228             return;
229         }
230     }
231 }
232 
AsyncGetNotifyEvent()233 void FsFileWatcher::AsyncGetNotifyEvent()
234 {
235     lock_guard<mutex> lock(taskMutex_);
236     if (!taskRunning_) {
237         taskRunning_ = true;
238         taskThead_ = thread(&FsFileWatcher::GetNotifyEvent, this);
239     }
240 }
241 
GetNotifyEvent()242 void FsFileWatcher::GetNotifyEvent()
243 {
244     if (run_) {
245         return;
246     }
247 
248     run_ = true;
249     nfds_t nfds = 2;
250     struct pollfd fds[2] = { { -1 } };
251     fds[0].fd = eventFd_;
252     fds[0].events = POLLIN;
253     fds[1].fd = notifyFd_;
254     fds[1].events = POLLIN;
255     int32_t ret = 0;
256 
257     while (run_) {
258         ret = poll(fds, nfds, pollTimeoutMs);
259         if (ret > 0) {
260             if (static_cast<unsigned short>(fds[0].revents) & POLLNVAL) {
261                 run_ = false;
262                 break;
263             }
264             if (static_cast<unsigned short>(fds[0].revents) & POLLIN) {
265                 run_ = false;
266                 break;
267             }
268             if (static_cast<unsigned short>(fds[1].revents) & POLLIN) {
269                 ReadNotifyEventLocked();
270             }
271         } else if (ret < 0 && errno != EINTR) {
272             HILOGE("Failed to poll NotifyFd, errno=%{public}d", errno);
273             break;
274         }
275         // Ignore cases where poll returns 0 (timeout) or EINTR (interrupted system call)
276     }
277 }
278 
AddWatcherInfo(shared_ptr<WatcherInfo> info)279 bool FsFileWatcher::AddWatcherInfo(shared_ptr<WatcherInfo> info)
280 {
281     if (!info) {
282         HILOGE("Invalid param: info");
283         return false;
284     }
285     return dataCache_.AddWatcherInfo(info);
286 }
287 
RemoveWatcherInfo(shared_ptr<WatcherInfo> info)288 uint32_t FsFileWatcher::RemoveWatcherInfo(shared_ptr<WatcherInfo> info)
289 {
290     if (!info) {
291         HILOGE("Invalid param: info");
292         return EINVAL;
293     }
294     return dataCache_.RemoveWatcherInfo(info);
295 }
296 
NotifyEvent(const struct inotify_event * event)297 void FsFileWatcher::NotifyEvent(const struct inotify_event *event)
298 {
299     if (!event) {
300         HILOGE("Invalid inotify event");
301         return;
302     }
303 
304     auto [matched, fileName, watcherInfos] = dataCache_.FindWatcherInfos(event->wd, event->mask);
305     if (!matched) {
306         HILOGE("Cannot find matched watcherInfos");
307         return;
308     }
309 
310     for (const auto &info : watcherInfos) {
311         if (event->len > 0) {
312             fileName += "/" + string(event->name);
313         }
314         info->TriggerCallback(fileName, event->mask & IN_ALL_EVENTS, event->cookie);
315     }
316 }
317 
CheckEventValid(uint32_t event)318 bool FsFileWatcher::CheckEventValid(uint32_t event)
319 {
320     if ((event & IN_ALL_EVENTS) == event) {
321         return true;
322     } else {
323         HILOGE("Param event:%{public}x is not valid", event);
324         return false;
325     }
326 }
327 
DestroyTaskThead()328 void FsFileWatcher::DestroyTaskThead()
329 {
330     if (taskThead_.joinable()) {
331         if (taskThead_.get_id() != std::this_thread::get_id()) {
332             taskThead_.join();
333         } else {
334             taskThead_.detach();
335         }
336     }
337 
338     {
339         lock_guard<mutex> lock(taskMutex_);
340         if (taskRunning_) {
341             taskRunning_ = false;
342             run_ = false;
343         }
344     }
345     dataCache_.ClearCache();
346 }
347 } // namespace OHOS::FileManagement::ModuleFileIO
348