diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.cpp index 1500dfcfd4f9c268bbe8370d32799e60c91878da..057c8151f561dc2b81e494a906d1864c30b3f710 100644 --- a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.cpp +++ b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.cpp @@ -15,84 +15,234 @@ #include "watcher_entity.h" #include +#include #include #include "filemgmt_libhilog.h" #include "uv.h" + namespace OHOS::FileManagement::ModuleFileIO { using namespace OHOS::FileManagement::LibN; +using namespace std; + +mutex FileWatcher::watchMutex_; + FileWatcher::FileWatcher() {} FileWatcher::~FileWatcher() {} -bool FileWatcher::InitNotify(int &fd) +int32_t FileWatcher::GetNotifyId() { - fd = inotify_init(); - if (fd < 0) { - HILOGE("Failed to init notify fail errCode:%{public}d", errno); + return notifyFd_; +} + +bool FileWatcher::InitNotify() +{ + notifyFd_ = inotify_init(); + if (notifyFd_ < 0) { + HILOGE("Failed to init notify errCode:%{public}d", errno); return false; } return true; } -bool FileWatcher::StartNotify(WatcherInfoArg &arg) +tuple FileWatcher::CheckEventWatched(const string &fileName, const uint32_t &event) { - int wd = inotify_add_watch(arg.fd, arg.filename.c_str(), arg.events); - if (wd < 0) { - HILOGE("Failed to start notify fail errCode:%{public}d", errno); - return false; + int wd = -1; + auto iter = wdFileNameMap_.find(fileName); + if (iter == wdFileNameMap_.end()) { + return {false, wd}; } - arg.wd = wd; - run_ = true; - return true; + wd = iter->second.first; + if ((iter->second.second & event) == event) { + return {true, wd}; + } + return {false, wd}; } -bool FileWatcher::StopNotify(const WatcherInfoArg &arg) +int FileWatcher::StartNotify(shared_ptr arg) { - run_ = false; - if (arg.events == IN_DELETE_SELF) { - close(arg.fd); - return true; + lock_guard lock(watchMutex_); + if (notifyFd_ < 0) { + HILOGE("Failed to start notify notifyFd_:%{public}d", notifyFd_); + return EIO; } - if (inotify_rm_watch(arg.fd, arg.wd) == -1) { - HILOGE("Failed to stop notify fail errCode:%{public}d", errno); - return false; + + auto [isWatched, wd] = CheckEventWatched(arg->fileName, arg->events); + if (isWatched && wd > 0) { + arg->wd = wd; + return ERRNO_NOERR; } - close(arg.fd); - return true; + uint32_t watchEvents = 0; + if (wd != -1) { + watchEvents = wdFileNameMap_[arg->fileName].second | arg->events; + } else { + watchEvents = arg->events; + } + int newWd = inotify_add_watch(notifyFd_, arg->fileName.c_str(), watchEvents); + if (newWd < 0) { + HILOGE("Failed to start notify errCode:%{public}d", errno); + return errno; + } + arg->wd = newWd; + wdFileNameMap_[arg->fileName].first = newWd; + wdFileNameMap_[arg->fileName].second = watchEvents; + return ERRNO_NOERR; } -void FileWatcher::HandleEvent(WatcherInfoArg &arg, const struct inotify_event *event, WatcherCallback callback) +int FileWatcher::NotifyToWatchNewEvents(const string &fileName, const int &wd, const uint32_t &watchEvents) { - if (event->wd != arg.wd) { - return; + int newWd = inotify_add_watch(notifyFd_, fileName.c_str(), watchEvents); + if (newWd < 0) { + HILOGE("Failed to start new notify errCode:%{public}d", errno); + return errno; } - std::string fileName = arg.filename; - if (event->len > 0) { - fileName.append(std::string("/")); - fileName.append(std::string(event->name)); + + if (newWd != wd) { + HILOGE("New notify wd is error"); + return EIO; } - callback(arg.env, arg.nRef, fileName, event->mask, event->cookie); + wdFileNameMap_[fileName].second = watchEvents; + return ERRNO_NOERR; } -void FileWatcher::GetNotifyEvent(WatcherInfoArg &arg, WatcherCallback callback) +int FileWatcher::CloseNotifyFd() { + int closeRet = ERRNO_NOERR; + if (watcherInfoSet_.size() == 0) { + closeRet = close(notifyFd_); + if (closeRet != 0) { + HILOGE("Failed to stop notify close fd errCode:%{public}d", closeRet); + } + notifyFd_ = -1; + run_ = false; + } + + return closeRet; +} + +int FileWatcher::StopNotify(shared_ptr arg) +{ + unique_lock lock(watchMutex_); + if (notifyFd_ < 0) { + HILOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_); + return EIO; + } + uint32_t newEvents = RemoveWatcherInfo(arg); + if (newEvents > 0) { + return NotifyToWatchNewEvents(arg->fileName, arg->wd, newEvents); + } + if (inotify_rm_watch(notifyFd_, arg->wd) == -1) { + int rmErr = errno; + if (access(arg->fileName.c_str(), F_OK) == 0) { + HILOGE("Failed to stop notify errCode:%{public}d", rmErr); + wdFileNameMap_.erase(arg->fileName); + CloseNotifyFd(); + return rmErr; + } + } + wdFileNameMap_.erase(arg->fileName); + return CloseNotifyFd(); +} + +void FileWatcher::GetNotifyEvent(WatcherCallback callback) +{ + if (run_) { + return; + } + run_ = true; char buf[BUF_SIZE] = {0}; struct inotify_event *event = nullptr; + fd_set fds; + FD_ZERO(&fds); + FD_SET(notifyFd_, &fds); while (run_) { - int fd = arg.fd; - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - if (select(fd + 1, &fds, nullptr, nullptr, nullptr) > 0) { + if (notifyFd_ < 0) { + HILOGE("Failed to run Listener Thread because notifyFd_:%{public}d", notifyFd_); + break; + } + if (select(notifyFd_ + 1, &fds, nullptr, nullptr, 0) > 0) { int len, index = 0; - while (((len = read(fd, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {}; + while (((len = read(notifyFd_, &buf, sizeof(buf))) < 0) && (errno == EINTR)) {}; while (index < len) { - event = reinterpret_cast(buf + index); - HandleEvent(arg, event, callback); + event = reinterpret_cast(buf + index); + NotifyEvent(event, callback); index += sizeof(struct inotify_event) + event->len; } } } } + +bool FileWatcher::AddWatcherInfo(const string &fileName, shared_ptr arg) +{ + for (auto &iter : watcherInfoSet_) { + if (iter->fileName == arg->fileName && iter->events == arg->events) { + bool isSame = false; + napi_strict_equals(iter->env, iter->nRef.Deref(iter->env).val_, arg->nRef.Deref(arg->env).val_, &isSame); + if (isSame) { + HILOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str()); + return false; + } + } + } + watcherInfoSet_.insert(arg); + return true; +} + +uint32_t FileWatcher::RemoveWatcherInfo(shared_ptr arg) +{ + watcherInfoSet_.erase(arg); + uint32_t otherEvents = 0; + for (const auto &iter : watcherInfoSet_) { + if (iter->fileName == arg->fileName && iter->wd > 0) { + otherEvents |= iter->events; + } + } + return otherEvents; +} + +bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event) +{ + if ((mask & event) > 0) { + return true; + } + return false; +} + +void FileWatcher::NotifyEvent(const struct inotify_event *event, WatcherCallback callback) +{ + lock_guard lock(watchMutex_); + string tempFileName; + for (const auto &iter : wdFileNameMap_) { + if (iter.second.first == event->wd) { + tempFileName = iter.first; + break; + } + } + + for (const auto &iter : watcherInfoSet_) { + string fileName = tempFileName; + uint32_t watchEvent = 0; + if ((iter->fileName == fileName) && (iter->wd > 0)) { + watchEvent = iter->events; + } + if (!CheckIncludeEvent(event->mask, watchEvent)) { + continue; + } + if (event->len > 0) { + fileName += "/" + string(event->name); + } + callback(iter->env, iter->nRef, fileName, event->mask, event->cookie); + } +} + +bool FileWatcher::CheckEventValid(const uint32_t &event) +{ + if ((event & IN_ALL_EVENTS) == event) { + return true; + } else { + HILOGE("Param event:%{public}x is not valid", event); + return false; + } +} } // namespace OHOS::FileManagement::ModuleFileIO diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.h b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.h index fad6ec27adcd5a43fb5ed99f44b4ed9b391dde84..37db1b7bf9227d950edbe9ae09202a5ad619a534 100644 --- a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.h +++ b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_entity.h @@ -12,25 +12,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef INTERFACES_KITS_JS_SRC_MOD_FILEIO_CLASS_WATCHER_WATCHER_ENTITY_H -#define INTERFACES_KITS_JS_SRC_MOD_FILEIO_CLASS_WATCHER_WATCHER_ENTITY_H +#ifndef INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_WATCHER_ENTITY_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_WATCHER_ENTITY_H + #include +#include #include #include -#include +#include +#include +#include #include "filemgmt_libn.h" +#include "singleton.h" namespace OHOS::FileManagement::ModuleFileIO { using WatcherCallback = void (*)(napi_env env, LibN::NRef &callback, const std::string &filename, const uint32_t &event, const uint32_t &cookie); + constexpr int BUF_SIZE = 1024; struct WatcherInfoArg { - std::string filename = ""; + std::string fileName = ""; uint32_t events = 0; - int fd = -1; int wd = -1; napi_env env = nullptr; LibN::NRef nRef; @@ -38,24 +43,38 @@ struct WatcherInfoArg { ~WatcherInfoArg() = default; }; -class FileWatcher { +class FileWatcher : public Singleton { public: FileWatcher(); ~FileWatcher(); - bool InitNotify(int &fd); - bool StartNotify(WatcherInfoArg &arg); - bool StopNotify(const WatcherInfoArg &arg); - void GetNotifyEvent(WatcherInfoArg &arg, WatcherCallback callback); + + FileWatcher(FileWatcher const &) = delete; + void operator=(FileWatcher const &) = delete; + + int32_t GetNotifyId(); + bool InitNotify(); + int StartNotify(std::shared_ptr arg); + int StopNotify(std::shared_ptr arg); + void GetNotifyEvent(WatcherCallback callback); + bool AddWatcherInfo(const std::string &fileName, std::shared_ptr arg); + bool CheckEventValid(const uint32_t &event); +private: + uint32_t RemoveWatcherInfo(std::shared_ptr arg); + std::tuple CheckEventWatched(const std::string &fileName, const uint32_t &event); + void NotifyEvent(const struct inotify_event *event, WatcherCallback callback); + int CloseNotifyFd(); + int NotifyToWatchNewEvents(const std::string &fileName, const int &wd, const uint32_t &watchEvents); private: - void HandleEvent(WatcherInfoArg &arg, const struct inotify_event *event, - WatcherCallback callback); + static std::mutex watchMutex_; bool run_ = false; + int32_t notifyFd_ = -1; + std::unordered_set> watcherInfoSet_; + std::unordered_map> wdFileNameMap_; }; struct WatcherEntity { - std::unique_ptr data_; - std::shared_ptr watcherPtr_; + std::shared_ptr data_; }; } // namespace OHOS::FileManagement::ModuleFileIO namespace OHOS #endif diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_n_exporter.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_n_exporter.cpp index a1988cbebe6849b9088c21bfde63a443937417b1..30f49cf5f93fce107ac5201e719c171790864da4 100644 --- a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_n_exporter.cpp +++ b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_n_exporter.cpp @@ -34,6 +34,7 @@ napi_value WatcherNExporter::Constructor(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + HILOGE("Failed to get param."); NError(EINVAL).ThrowErr(env); return nullptr; } @@ -45,6 +46,7 @@ napi_value WatcherNExporter::Constructor(napi_env env, napi_callback_info info) return nullptr; } if (!NClass::SetEntityFor(env, funcArg.GetThisVar(), move(watcherEntity))) { + HILOGE("Failed to set watcherEntity."); NError(EIO).ThrowErr(env); return nullptr; } @@ -55,20 +57,23 @@ napi_value WatcherNExporter::Stop(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + HILOGE("Failed to get param when stop."); NError(EINVAL).ThrowErr(env); return nullptr; } auto watchEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); if (!watchEntity) { + HILOGE("Failed to get watcherEntity when stop."); NError(EINVAL).ThrowErr(env); return nullptr; } - if (!watchEntity->watcherPtr_->StopNotify(*(watchEntity->data_))) { - NError(errno).ThrowErr(env); + int ret = FileWatcher::GetInstance().StopNotify(watchEntity->data_); + if (ret != ERRNO_NOERR) { + HILOGE("Failed to stopNotify errno:%{public}d", errno); + NError(ret).ThrowErr(env); return nullptr; } - return NVal::CreateUndefined(env).val_; } @@ -76,28 +81,33 @@ napi_value WatcherNExporter::Start(napi_env env, napi_callback_info info) { NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::ZERO)) { + HILOGE("Failed to get param when start."); NError(EINVAL).ThrowErr(env); return nullptr; } auto watchEntity = NClass::GetEntityOf(env, funcArg.GetThisVar()); if (!watchEntity) { + HILOGE("Failed to get watcherEntity when start."); NError(EINVAL).ThrowErr(env); return nullptr; } - if (!watchEntity->watcherPtr_->StartNotify(*(watchEntity->data_))) { - NError(errno).ThrowErr(env); + int ret = FileWatcher::GetInstance().StartNotify(watchEntity->data_); + if (ret != ERRNO_NOERR) { + HILOGE("Failed to startNotify."); + NError(ret).ThrowErr(env); return nullptr; } - auto cbExec = [watchEntity]() -> NError { - watchEntity->watcherPtr_->GetNotifyEvent(*(watchEntity->data_), WatcherCallback); + auto cbExec = []() -> NError { + FileWatcher::GetInstance().GetNotifyEvent(WatcherCallback); return NError(ERRNO_NOERR); }; auto cbCompl = [](napi_env env, NError err) -> NVal { if (err) { + HILOGE("Failed to execute complete."); return {env, err.GetNapiErr(env)}; } return {NVal::CreateUndefined(env)}; @@ -169,7 +179,7 @@ void WatcherNExporter::WatcherCallback(napi_env env, NRef &callback, const std:: } if (!callback) { - HILOGE("Failed to pass watcher callback"); + HILOGE("Failed to parse watcher callback"); return; } @@ -200,12 +210,14 @@ bool WatcherNExporter::Export() auto [resDefineClass, classValue] = NClass::DefineClass(exports_.env_, className, WatcherNExporter::Constructor, std::move(props)); if (!resDefineClass) { + HILOGE("Failed to DefineClass"); NError(EIO).ThrowErr(exports_.env_); return false; } bool succ = NClass::SaveClass(exports_.env_, className, classValue); if (!succ) { + HILOGE("Failed to SaveClass"); NError(EIO).ThrowErr(exports_.env_); return false; } diff --git a/interfaces/kits/js/src/mod_fs/properties/watcher.cpp b/interfaces/kits/js/src/mod_fs/properties/watcher.cpp index 8d3fa0341affa6418f068f2b485a6acb606f371c..596e525a0233eca3923a027a64f7bd1cc1d1df78 100644 --- a/interfaces/kits/js/src/mod_fs/properties/watcher.cpp +++ b/interfaces/kits/js/src/mod_fs/properties/watcher.cpp @@ -20,9 +20,10 @@ #include #include #include -#include "ipc_skeleton.h" + #include "file_utils.h" #include "filemgmt_libhilog.h" +#include "ipc_skeleton.h" #include "tokenid_kit.h" #include "../class_watcher/watcher_entity.h" #include "../class_watcher/watcher_n_exporter.h" @@ -31,7 +32,6 @@ using namespace std; using namespace OHOS::FileManagement::LibN; namespace { - const std::string STORAGE_DATA_PATH = "/data"; bool IsSystemApp() { uint64_t fullTokenId = OHOS::IPCSkeleton::GetCallingFullTokenID(); @@ -39,78 +39,98 @@ namespace { } } -static tuple, napi_value, NError> CreateAndCheckForWatcherEntity(napi_env env, int& fd) +static tuple CreateAndCheckForWatcherEntity(napi_env env) { - auto watcherPtr = CreateSharedPtr(); - if (watcherPtr == nullptr) { - HILOGE("Failed to request heap memory."); - return {nullptr, nullptr, NError(ENOMEM)}; - } - if (!watcherPtr->InitNotify(fd)) { - return {watcherPtr, nullptr, NError(errno)}; + if (FileWatcher::GetInstance().GetNotifyId() < 0 && !FileWatcher::GetInstance().InitNotify()) { + HILOGE("Failed to get notifyId or initnotify fail"); + return {nullptr, errno}; } napi_value objWatcher = NClass::InstantiateClass(env, WatcherNExporter::className_, {}); if (!objWatcher) { - return {watcherPtr, nullptr, NError(EINVAL)}; + HILOGE("Failed to instantiate watcher"); + return {nullptr, EIO}; } - return {watcherPtr, objWatcher, NError(ERRNO_NOERR)}; + return {objWatcher, ERRNO_NOERR}; } -napi_value Watcher::CreateWatcher(napi_env env, napi_callback_info info) +shared_ptr ParseParam(const napi_env &env, const napi_callback_info &info, int32_t &errCode) { - if (!IsSystemApp()) { - NError(E_PERMISSION_SYS).ThrowErr(env); - return nullptr; - } - NFuncArg funcArg(env, info); if (!funcArg.InitArgs(NARG_CNT::THREE)) { - NError(EINVAL).ThrowErr(env); + HILOGE("Failed to get param."); + errCode = EINVAL; return nullptr; } auto [succGetPath, filename, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8String(); if (!succGetPath) { - NError(EINVAL).ThrowErr(env); + HILOGE("Failed to get watcher path."); + errCode = EINVAL; return nullptr; } - auto [succGetEvent, events] = NVal(env, funcArg[NARG_POS::SECOND]).ToUint32(); - if (!succGetEvent) { - NError(EINVAL).ThrowErr(env); + auto [succGetEvent, events] = NVal(env, funcArg[NARG_POS::SECOND]).ToInt32(); + if (!succGetEvent || events <= 0 || !FileWatcher::GetInstance().CheckEventValid(events)) { + HILOGE("Failed to get watcher event."); + errCode = EINVAL; return nullptr; } - int fd = -1; - auto [watcherPtr, objWatcher, err] = CreateAndCheckForWatcherEntity(env, fd); - if (watcherPtr == nullptr || !objWatcher) { - err.ThrowErr(env); + if (!NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function)) { + HILOGE("Failed to get callback"); + errCode = EINVAL; return nullptr; } + auto infoArg = CreateSharedPtr(NVal(env, funcArg[NARG_POS::THIRD])); + if (infoArg == nullptr) { + HILOGE("Failed to request heap memory."); + errCode = ENOMEM; + return nullptr; + } + infoArg->events = events; + infoArg->env = env; + infoArg->fileName = string(filename.get()); - auto watcherEntity = NClass::GetEntityOf(env, objWatcher); - if (!watcherEntity) { - NError(EINVAL).ThrowErr(env); + return infoArg; +} + +napi_value Watcher::CreateWatcher(napi_env env, napi_callback_info info) +{ + if (!IsSystemApp()) { + HILOGE("The hap is not system app."); + NError(E_PERMISSION_SYS).ThrowErr(env); return nullptr; } - if (!NVal(env, funcArg[NARG_POS::THIRD]).TypeIs(napi_function)) { - NError(EINVAL).ThrowErr(env); + int errCode = 0; + auto infoArg = ParseParam(env, info, errCode); + if (errCode != 0) { + HILOGE("Failed to parse param"); + NError(errCode).ThrowErr(env); return nullptr; } - watcherEntity->data_ = CreateUniquePtr(NVal(env, funcArg[NARG_POS::THIRD])); - if (watcherEntity->data_ == nullptr) { - HILOGE("Failed to request heap memory."); - NError(ENOMEM).ThrowErr(env); + + auto [objWatcher, err] = CreateAndCheckForWatcherEntity(env); + if (!objWatcher) { + HILOGE("Failed to create watcher entity."); + NError(err).ThrowErr(env); + return nullptr; + } + + auto watcherEntity = NClass::GetEntityOf(env, objWatcher); + if (!watcherEntity) { + HILOGE("Failed to get WatcherEntity."); + NError(EIO).ThrowErr(env); return nullptr; } - watcherEntity->data_->events = events; - watcherEntity->data_->env = env; - watcherEntity->data_->filename = string(filename.get()); - watcherEntity->data_->fd = fd; + watcherEntity->data_ = infoArg; - watcherEntity->watcherPtr_ = watcherPtr; - + bool ret = FileWatcher::GetInstance().AddWatcherInfo(infoArg->fileName, infoArg); + if (!ret) { + HILOGE("Failed to add watcher info."); + NError(EINVAL).ThrowErr(env); + return nullptr; + } return objWatcher; } } // namespace OHOS::FileManagement::ModuleFileIO