// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md #include #include #include #include #include #include #include namespace purefs::fs::internal { namespace { void for_path(std::string_view path, std::function fun) { constexpr auto sep = '/'; for (auto it = (path.back() == sep) ? (path.size() - 1) : (path.size()); (it && it != std::string::npos); it = path.rfind(sep, it - 1)) fun(path.substr(0, it)); } std::string absolute_path(std::string_view path) { using namespace std::string_literals; std::string ret; if (!path.empty() && path[0] != '/') { ret = std::string(internal::get_thread_local_cwd_path()) + "/"s + std::string(path); } else { ret = std::string(path); } if (!ret.empty() && ret.back() == '/') { ret.pop_back(); } return ret; } } // namespace notifier::notifier() : m_lock(std::make_unique()) {} notifier::~notifier() {} auto notifier::register_path(std::string_view path, std::shared_ptr owner, inotify_flags flags) -> std::optional { cpp_freertos::LockGuard _lck(*m_lock); const auto abspath = absolute_path(path); // # Check if it is already registered for same path const auto range = m_events.equal_range(abspath); for (auto i = range.first; i != range.second; ++i) { if (i->second.service.lock() == owner) { return std::nullopt; } } return m_events.emplace(std::make_pair(abspath, service_item(owner, flags))); } auto notifier::unregister_path(item_it item) -> void { cpp_freertos::LockGuard _lck(*m_lock); m_events.erase(item); } auto notifier::notify(int fd, inotify_flags mask) const -> void { cpp_freertos::LockGuard _lck(*m_lock); const auto fname_it = m_fd_map.find(fd); if (fname_it != std::end(m_fd_map)) { notify(fname_it->second.path, mask); } } void notifier::notify(std::string_view path, std::string_view path_prv, inotify_flags mask) const { cpp_freertos::LockGuard _lck(*m_lock); const auto abs_path = absolute_path(path); const auto abs_path_prv = absolute_path(path_prv); for_path(abs_path, [this, abs_path, abs_path_prv, mask](std::string_view path) { const auto range = m_events.equal_range(std::string(path)); for (auto i = range.first; i != range.second; ++i) { if (i->second.subscribed_events && mask) { auto svc = i->second.service.lock(); if (svc) { send_notification(svc, mask, abs_path, abs_path_prv); } } } }); } auto notifier::notify_open(std::string_view path, int fd, bool ro) const -> void { cpp_freertos::LockGuard _lck(*m_lock); m_fd_map.emplace(std::make_pair(fd, path_item(path, ro))); notify(path, inotify_flags::open); } auto notifier::notify_close(int fd) const -> void { cpp_freertos::LockGuard _lck(*m_lock); const auto fname_it = m_fd_map.find(fd); if (fname_it != std::end(m_fd_map)) { notify(fname_it->first, fname_it->second.read_only ? inotify_flags::close_nowrite : inotify_flags::close_write); m_fd_map.erase(fname_it); } } auto notifier::send_notification(std::shared_ptr svc, inotify_flags flags, std::string_view name, std::string_view name_dst) const -> void { if (svc->GetHandle() != cpp_freertos::Thread::GetCurrentThreadHandle()) { auto msg = std::make_shared(flags, name, name_dst); svc->bus.sendUnicast(std::move(msg), svc->GetName()); } else { LOG_WARN("Sent notification to the same thread is forbidded"); } } } // namespace purefs::fs::internal