| // Copyright 2021 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "dlp/fanotify_watcher.h" |
| |
| #include <fcntl.h> |
| #include <memory> |
| #include <sys/fanotify.h> |
| #include <utility> |
| |
| #include "base/files/scoped_file.h" |
| #include "base/logging.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "dlp/fanotify_reader_thread.h" |
| #include "dlp/kernel_version_tools.h" |
| |
| namespace dlp { |
| |
| FanotifyWatcher::FanotifyWatcher(Delegate* delegate, |
| int fanotify_perm_fd, |
| int fanotify_notif_fd) |
| : task_runner_(base::SequencedTaskRunner::GetCurrentDefault()), |
| fd_events_thread_(task_runner_, this), |
| fh_events_thread_(task_runner_, this), |
| delegate_(delegate) { |
| DCHECK(delegate); |
| |
| if (fanotify_perm_fd >= 0) { |
| fanotify_fd_events_fd_.reset(fanotify_perm_fd); |
| fd_events_thread_.StartThread(fanotify_perm_fd); |
| } |
| |
| if (GetKernelVersion() >= kMinKernelVersionForFanDeleteEvents && |
| fanotify_notif_fd >= 0) { |
| fanotify_fh_events_fd_.reset(fanotify_notif_fd); |
| fh_events_thread_.StartThread(fanotify_notif_fd); |
| } |
| } |
| |
| FanotifyWatcher::~FanotifyWatcher() = default; |
| |
| void FanotifyWatcher::AddFileDeleteWatch(const base::FilePath& path) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| if (GetKernelVersion() >= kMinKernelVersionForFanDeleteEvents && |
| fanotify_fh_events_fd_.get() >= 0) { |
| int res = fanotify_mark(fanotify_fh_events_fd_.get(), FAN_MARK_ADD, |
| FAN_DELETE_SELF, AT_FDCWD, path.value().c_str()); |
| |
| if (res != 0) { |
| PLOG(ERROR) << "fanotify_mark for DELETE_SELF (" << path << ") failed"; |
| OnFanotifyError(FanotifyError::kMarkError); |
| } else { |
| LOG(INFO) << "Added watch for: " << path; |
| } |
| } |
| } |
| |
| void FanotifyWatcher::SetActive(bool active) { |
| active_ = active; |
| } |
| |
| bool FanotifyWatcher::IsActive() const { |
| return active_; |
| } |
| |
| void FanotifyWatcher::OnFileOpenRequested( |
| ino_t inode, |
| time_t crtime, |
| int pid, |
| base::ScopedFD fd, |
| std::unique_ptr<FanotifyReaderThread::FanotifyReplyWatchdog> watchdog) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| if (!active_) { |
| OnRequestProcessed(std::move(fd), std::move(watchdog), /*allowed=*/true); |
| return; |
| } |
| |
| delegate_->ProcessFileOpenRequest( |
| {inode, crtime}, pid, |
| base::BindOnce(&FanotifyWatcher::OnRequestProcessed, |
| base::Unretained(this), std::move(fd), |
| std::move(watchdog))); |
| } |
| |
| void FanotifyWatcher::OnFileDeleted(ino64_t inode) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| delegate_->OnFileDeleted(inode); |
| } |
| |
| void FanotifyWatcher::OnFanotifyError(FanotifyError error) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| delegate_->OnFanotifyError(error); |
| } |
| |
| void FanotifyWatcher::OnRequestProcessed( |
| base::ScopedFD fd, |
| std::unique_ptr<FanotifyReaderThread::FanotifyReplyWatchdog> watchdog, |
| bool allowed) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| struct fanotify_response response = {}; |
| response.fd = fd.get(); |
| response.response = allowed ? FAN_ALLOW : FAN_DENY; |
| HANDLE_EINTR( |
| write(fanotify_fd_events_fd_.get(), &response, sizeof(response))); |
| watchdog->Disarm(); |
| } |
| |
| } // namespace dlp |