blob: c8a99004489f2f03de31311e3fee2585fb6f6a30 [file] [log] [blame]
Sergey Poromov04887322021-03-17 17:30:54 +01001// Copyright 2021 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "dlp/fanotify_reader_thread.h"
6
7#include <fcntl.h>
8#include <sys/fanotify.h>
9#include <sys/ioctl.h>
10#include <sys/stat.h>
11#include <utility>
12#include <vector>
13
14#include "base/check.h"
15#include "base/memory/scoped_refptr.h"
16#include "base/posix/eintr_wrapper.h"
17#include "base/threading/platform_thread.h"
18#include "base/threading/sequenced_task_runner_handle.h"
19
20namespace dlp {
21
22FanotifyReaderThread::FanotifyReaderThread(
23 scoped_refptr<base::SequencedTaskRunner> parent_task_runner,
24 Delegate* delegate)
25 : parent_task_runner_(std::move(parent_task_runner)), delegate_(delegate) {
26 CHECK(delegate_);
27 CHECK(parent_task_runner_->RunsTasksInCurrentSequence());
28}
29
30FanotifyReaderThread::~FanotifyReaderThread() {
31 base::PlatformThread::Join(handle_);
32}
33
34void FanotifyReaderThread::StartThread(int fanotify_fd) {
35 CHECK(parent_task_runner_->RunsTasksInCurrentSequence());
36 fanotify_fd_ = fanotify_fd;
37
38 CHECK(base::PlatformThread::Create(0, this, &handle_));
39}
40
41void FanotifyReaderThread::ThreadMain() {
42 CHECK(!parent_task_runner_->RunsTasksInCurrentSequence());
43 base::PlatformThread::SetName("fanotify_reader");
44
45 RunLoop();
46
47 // TODO(poromov): Gracefully stop the thread and notify.
48}
49
50void FanotifyReaderThread::RunLoop() {
51 CHECK(!parent_task_runner_->RunsTasksInCurrentSequence());
52
53 CHECK_LE(0, fanotify_fd_);
54 CHECK_GT(FD_SETSIZE, fanotify_fd_);
55
56 std::vector<char> buffer(0);
57 while (true) {
58 fd_set rfds;
59 FD_ZERO(&rfds);
60 FD_SET(fanotify_fd_, &rfds);
61 // Re-check file descriptor every second.
62 struct timeval tv;
63 tv.tv_sec = 1;
64 tv.tv_usec = 0;
65
66 // Wait until some inotify events are available.
67 int select_result =
68 HANDLE_EINTR(select(fanotify_fd_ + 1, &rfds, nullptr, nullptr, &tv));
69 if (select_result < 0) {
70 PLOG(WARNING) << "select failed";
71 return;
72 } else if (select_result == 0) {
73 continue;
74 }
75
76 // Adjust buffer size to current event queue size.
77 int buffer_size;
78 int ioctl_result =
79 HANDLE_EINTR(ioctl(fanotify_fd_, FIONREAD, &buffer_size));
80 if (ioctl_result != 0) {
81 PLOG(WARNING) << "ioctl failed";
82 return;
83 }
84
85 buffer.resize(buffer_size);
86 ssize_t bytes_read =
87 HANDLE_EINTR(read(fanotify_fd_, &buffer[0], buffer_size));
88 if (bytes_read < 0) {
89 PLOG(WARNING) << "read from fanotify fd failed";
90 return;
91 }
92
93 fanotify_event_metadata* metadata =
94 reinterpret_cast<fanotify_event_metadata*>(&buffer[0]);
95 while (FAN_EVENT_OK(metadata, bytes_read)) {
96 if (metadata->vers != FANOTIFY_METADATA_VERSION) {
97 LOG(ERROR) << "mismatch of fanotify metadata version";
98 return;
99 }
100 if (metadata->fd >= 0) {
101 base::ScopedFD fd(metadata->fd);
102 if (metadata->mask & FAN_OPEN_PERM) {
103 struct stat st;
104 if (fstat(fd.get(), &st)) {
105 PLOG(ERROR) << "fstat failed";
106 metadata = FAN_EVENT_NEXT(metadata, bytes_read);
107 continue;
108 }
109
110 parent_task_runner_->PostTask(
111 FROM_HERE, base::BindOnce(&Delegate::OnFileOpenRequested,
112 base::Unretained(delegate_), st.st_ino,
113 metadata->pid, std::move(fd)));
114 } else {
115 LOG(WARNING) << "unexpected fanotify event: " << metadata->mask;
116 }
117 }
118 metadata = FAN_EVENT_NEXT(metadata, bytes_read);
119 }
120 }
121}
122
123} // namespace dlp