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 }