• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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