summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'libappfuse')
-rw-r--r--libappfuse/FuseAppLoop.cc345
-rw-r--r--libappfuse/include/libappfuse/FuseAppLoop.h48
-rw-r--r--libappfuse/tests/FuseAppLoopTest.cc111
3 files changed, 277 insertions, 227 deletions
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
index a31880e41..b6bc191b7 100644
--- a/libappfuse/FuseAppLoop.cc
+++ b/libappfuse/FuseAppLoop.cc
@@ -16,205 +16,232 @@
16 16
17#include "libappfuse/FuseAppLoop.h" 17#include "libappfuse/FuseAppLoop.h"
18 18
19#include <sys/eventfd.h>
19#include <sys/stat.h> 20#include <sys/stat.h>
20 21
21#include <android-base/logging.h> 22#include <android-base/logging.h>
22#include <android-base/unique_fd.h> 23#include <android-base/unique_fd.h>
23 24
25#include "libappfuse/EpollController.h"
26
24namespace android { 27namespace android {
25namespace fuse { 28namespace fuse {
26 29
27namespace { 30namespace {
28 31
29void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) { 32bool HandleLookUp(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
30 // AppFuse does not support directory structure now. 33 // AppFuse does not support directory structure now.
31 // It can lookup only files under the mount point. 34 // It can lookup only files under the mount point.
32 if (buffer->request.header.nodeid != FUSE_ROOT_ID) { 35 if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
33 LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID."; 36 LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
34 buffer->response.Reset(0, -ENOENT, buffer->request.header.unique); 37 return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
35 return; 38 }
36 }
37
38 // Ensure that the filename ends with 0.
39 const size_t filename_length =
40 buffer->request.header.len - sizeof(fuse_in_header);
41 if (buffer->request.lookup_name[filename_length - 1] != 0) {
42 LOG(ERROR) << "File name does not end with 0.";
43 buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
44 return;
45 }
46
47 const uint64_t inode =
48 static_cast<uint64_t>(atol(buffer->request.lookup_name));
49 if (inode == 0 || inode == LONG_MAX) {
50 LOG(ERROR) << "Invalid filename";
51 buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
52 return;
53 }
54
55 const int64_t size = callback->OnGetSize(inode);
56 if (size < 0) {
57 buffer->response.Reset(0, size, buffer->request.header.unique);
58 return;
59 }
60
61 buffer->response.Reset(sizeof(fuse_entry_out), 0,
62 buffer->request.header.unique);
63 buffer->response.entry_out.nodeid = inode;
64 buffer->response.entry_out.attr_valid = 10;
65 buffer->response.entry_out.entry_valid = 10;
66 buffer->response.entry_out.attr.ino = inode;
67 buffer->response.entry_out.attr.mode = S_IFREG | 0777;
68 buffer->response.entry_out.attr.size = size;
69}
70 39
71void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) { 40 // Ensure that the filename ends with 0.
72 const uint64_t nodeid = buffer->request.header.nodeid; 41 const size_t filename_length = buffer->request.header.len - sizeof(fuse_in_header);
73 int64_t size; 42 if (buffer->request.lookup_name[filename_length - 1] != 0) {
74 uint32_t mode; 43 LOG(ERROR) << "File name does not end with 0.";
75 if (nodeid == FUSE_ROOT_ID) { 44 return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
76 size = 0; 45 }
77 mode = S_IFDIR | 0777; 46
78 } else { 47 const uint64_t inode = static_cast<uint64_t>(atol(buffer->request.lookup_name));
79 size = callback->OnGetSize(buffer->request.header.nodeid); 48 if (inode == 0 || inode == LONG_MAX) {
80 if (size < 0) { 49 LOG(ERROR) << "Invalid filename";
81 buffer->response.Reset(0, size, buffer->request.header.unique); 50 return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
82 return;
83 } 51 }
84 mode = S_IFREG | 0777;
85 }
86
87 buffer->response.Reset(sizeof(fuse_attr_out), 0,
88 buffer->request.header.unique);
89 buffer->response.attr_out.attr_valid = 10;
90 buffer->response.attr_out.attr.ino = nodeid;
91 buffer->response.attr_out.attr.mode = mode;
92 buffer->response.attr_out.attr.size = size;
93}
94 52
95void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) { 53 callback->OnLookup(buffer->request.header.unique, inode);
96 const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid); 54 return true;
97 if (file_handle < 0) {
98 buffer->response.Reset(0, file_handle, buffer->request.header.unique);
99 return;
100 }
101 buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
102 buffer->request.header.unique);
103 buffer->response.open_out.fh = file_handle;
104} 55}
105 56
106void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) { 57bool HandleGetAttr(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
107 buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid), 58 if (buffer->request.header.nodeid == FUSE_ROOT_ID) {
108 buffer->request.header.unique); 59 return loop->ReplyGetAttr(buffer->request.header.unique, buffer->request.header.nodeid, 0,
60 S_IFDIR | 0777);
61 } else {
62 callback->OnGetAttr(buffer->request.header.unique, buffer->request.header.nodeid);
63 return true;
64 }
109} 65}
110 66
111void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) { 67bool HandleRead(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
112 buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid), 68 if (buffer->request.read_in.size > kFuseMaxRead) {
113 buffer->request.header.unique); 69 return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
70 }
71
72 callback->OnRead(buffer->request.header.unique, buffer->request.header.nodeid,
73 buffer->request.read_in.offset, buffer->request.read_in.size);
74 return true;
114} 75}
115 76
116void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) { 77bool HandleWrite(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
117 const uint64_t unique = buffer->request.header.unique; 78 if (buffer->request.write_in.size > kFuseMaxWrite) {
118 const uint64_t nodeid = buffer->request.header.nodeid; 79 return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
119 const uint64_t offset = buffer->request.read_in.offset; 80 }
120 const uint32_t size = buffer->request.read_in.size; 81
121 82 callback->OnWrite(buffer->request.header.unique, buffer->request.header.nodeid,
122 if (size > kFuseMaxRead) { 83 buffer->request.write_in.offset, buffer->request.write_in.size,
123 buffer->response.Reset(0, -EINVAL, buffer->request.header.unique); 84 buffer->request.write_data);
124 return; 85 return true;
125 }
126
127 const int32_t read_size = callback->OnRead(nodeid, offset, size,
128 buffer->response.read_data);
129 if (read_size < 0) {
130 buffer->response.Reset(0, read_size, buffer->request.header.unique);
131 return;
132 }
133
134 buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
135} 86}
136 87
137void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) { 88bool HandleMessage(FuseAppLoop* loop, FuseBuffer* buffer, int fd, FuseAppLoopCallback* callback) {
138 const uint64_t unique = buffer->request.header.unique; 89 if (!buffer->request.Read(fd)) {
139 const uint64_t nodeid = buffer->request.header.nodeid; 90 return false;
140 const uint64_t offset = buffer->request.write_in.offset; 91 }
141 const uint32_t size = buffer->request.write_in.size; 92
142 93 const uint32_t opcode = buffer->request.header.opcode;
143 if (size > kFuseMaxWrite) { 94 LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
144 buffer->response.Reset(0, -EINVAL, buffer->request.header.unique); 95 switch (opcode) {
145 return; 96 case FUSE_FORGET:
146 } 97 // Do not reply to FUSE_FORGET.
147 98 return true;
148 const int32_t write_size = callback->OnWrite(nodeid, offset, size, 99
149 buffer->request.write_data); 100 case FUSE_LOOKUP:
150 if (write_size < 0) { 101 return HandleLookUp(loop, buffer, callback);
151 buffer->response.Reset(0, write_size, buffer->request.header.unique); 102
152 return; 103 case FUSE_GETATTR:
153 } 104 return HandleGetAttr(loop, buffer, callback);
154 105
155 buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique); 106 case FUSE_OPEN:
156 buffer->response.write_out.size = write_size; 107 callback->OnOpen(buffer->request.header.unique, buffer->request.header.nodeid);
108 return true;
109
110 case FUSE_READ:
111 return HandleRead(loop, buffer, callback);
112
113 case FUSE_WRITE:
114 return HandleWrite(loop, buffer, callback);
115
116 case FUSE_RELEASE:
117 callback->OnRelease(buffer->request.header.unique, buffer->request.header.nodeid);
118 return true;
119
120 case FUSE_FSYNC:
121 callback->OnFsync(buffer->request.header.unique, buffer->request.header.nodeid);
122 return true;
123
124 default:
125 buffer->HandleNotImpl();
126 return buffer->response.Write(fd);
127 }
157} 128}
158 129
159} // namespace 130} // namespace
160 131
161bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) { 132FuseAppLoopCallback::~FuseAppLoopCallback() = default;
162 base::unique_fd fd(raw_fd); 133
163 FuseBuffer buffer; 134FuseAppLoop::FuseAppLoop(base::unique_fd&& fd) : fd_(std::move(fd)) {}
164 135
165 LOG(DEBUG) << "Start fuse loop."; 136void FuseAppLoop::Break() {
166 while (callback->IsActive()) { 137 const int64_t value = 1;
167 if (!buffer.request.Read(fd)) { 138 if (write(break_fd_, &value, sizeof(value)) == -1) {
168 return false; 139 PLOG(ERROR) << "Failed to send a break event";
169 } 140 }
141}
170 142
171 const uint32_t opcode = buffer.request.header.opcode; 143bool FuseAppLoop::ReplySimple(uint64_t unique, int32_t result) {
172 LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode; 144 if (result == -ENOSYS) {
173 switch (opcode) { 145 // We should not return -ENOSYS because the kernel stops delivering FUSE
174 case FUSE_FORGET: 146 // command after receiving -ENOSYS as a result for the command.
175 // Do not reply to FUSE_FORGET. 147 result = -EBADF;
176 continue; 148 }
149 FuseSimpleResponse response;
150 response.Reset(0, result, unique);
151 return response.Write(fd_);
152}
177 153
178 case FUSE_LOOKUP: 154bool FuseAppLoop::ReplyLookup(uint64_t unique, uint64_t inode, int64_t size) {
179 HandleLookUp(&buffer, callback); 155 FuseSimpleResponse response;
180 break; 156 response.Reset(sizeof(fuse_entry_out), 0, unique);
157 response.entry_out.nodeid = inode;
158 response.entry_out.attr_valid = 10;
159 response.entry_out.entry_valid = 10;
160 response.entry_out.attr.ino = inode;
161 response.entry_out.attr.mode = S_IFREG | 0777;
162 response.entry_out.attr.size = size;
163 return response.Write(fd_);
164}
181 165
182 case FUSE_GETATTR: 166bool FuseAppLoop::ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode) {
183 HandleGetAttr(&buffer, callback); 167 CHECK(mode == (S_IFREG | 0777) || mode == (S_IFDIR | 0777));
184 break; 168 FuseSimpleResponse response;
169 response.Reset(sizeof(fuse_attr_out), 0, unique);
170 response.attr_out.attr_valid = 10;
171 response.attr_out.attr.ino = inode;
172 response.attr_out.attr.mode = mode;
173 response.attr_out.attr.size = size;
174 return response.Write(fd_);
175}
185 176
186 case FUSE_OPEN: 177bool FuseAppLoop::ReplyOpen(uint64_t unique, uint64_t fh) {
187 HandleOpen(&buffer, callback); 178 FuseSimpleResponse response;
188 break; 179 response.Reset(sizeof(fuse_open_out), kFuseSuccess, unique);
180 response.open_out.fh = fh;
181 return response.Write(fd_);
182}
189 183
190 case FUSE_READ: 184bool FuseAppLoop::ReplyWrite(uint64_t unique, uint32_t size) {
191 HandleRead(&buffer, callback); 185 CHECK(size <= kFuseMaxWrite);
192 break; 186 FuseSimpleResponse response;
187 response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
188 response.write_out.size = size;
189 return response.Write(fd_);
190}
193 191
194 case FUSE_WRITE: 192bool FuseAppLoop::ReplyRead(uint64_t unique, uint32_t size, const void* data) {
195 HandleWrite(&buffer, callback); 193 CHECK(size <= kFuseMaxRead);
196 break; 194 FuseSimpleResponse response;
195 response.ResetHeader(size, kFuseSuccess, unique);
196 return response.WriteWithBody(fd_, sizeof(FuseResponse), data);
197}
197 198
198 case FUSE_RELEASE: 199void FuseAppLoop::Start(FuseAppLoopCallback* callback) {
199 HandleRelease(&buffer, callback); 200 break_fd_.reset(eventfd(/* initval */ 0, EFD_CLOEXEC));
200 break; 201 if (break_fd_.get() == -1) {
202 PLOG(ERROR) << "Failed to open FD for break event";
203 return;
204 }
205
206 base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
207 if (epoll_fd.get() == -1) {
208 PLOG(ERROR) << "Failed to open FD for epoll";
209 return;
210 }
201 211
202 case FUSE_FSYNC: 212 int last_event;
203 HandleFsync(&buffer, callback); 213 int break_event;
204 break;
205 214
206 default: 215 std::unique_ptr<EpollController> epoll_controller(new EpollController(std::move(epoll_fd)));
207 buffer.HandleNotImpl(); 216 if (!epoll_controller->AddFd(fd_, EPOLLIN, &last_event)) {
208 break; 217 return;
218 }
219 if (!epoll_controller->AddFd(break_fd_, EPOLLIN, &break_event)) {
220 return;
209 } 221 }
210 222
211 if (!buffer.response.Write(fd)) { 223 last_event = 0;
212 LOG(ERROR) << "Failed to write a response to the device."; 224 break_event = 0;
213 return false; 225
226 FuseBuffer buffer;
227 while (true) {
228 if (!epoll_controller->Wait(1)) {
229 break;
230 }
231 last_event = 0;
232 *reinterpret_cast<int*>(epoll_controller->events()[0].data.ptr) =
233 epoll_controller->events()[0].events;
234
235 if (break_event != 0 || (last_event & ~EPOLLIN) != 0) {
236 break;
237 }
238
239 if (!HandleMessage(this, &buffer, fd_, callback)) {
240 break;
241 }
214 } 242 }
215 }
216 243
217 return true; 244 LOG(VERBOSE) << "FuseAppLoop exit";
218} 245}
219 246
220} // namespace fuse 247} // namespace fuse
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
index c3edfcc32..f2ef2b5c5 100644
--- a/libappfuse/include/libappfuse/FuseAppLoop.h
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -17,23 +17,51 @@
17#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_ 17#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
18#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_ 18#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
19 19
20#include <memory>
21#include <mutex>
22
23#include <android-base/unique_fd.h>
24
20#include "libappfuse/FuseBuffer.h" 25#include "libappfuse/FuseBuffer.h"
21 26
22namespace android { 27namespace android {
23namespace fuse { 28namespace fuse {
24 29
30class EpollController;
31
25class FuseAppLoopCallback { 32class FuseAppLoopCallback {
26 public: 33 public:
27 virtual bool IsActive() = 0; 34 virtual void OnLookup(uint64_t unique, uint64_t inode) = 0;
28 virtual int64_t OnGetSize(uint64_t inode) = 0; 35 virtual void OnGetAttr(uint64_t unique, uint64_t inode) = 0;
29 virtual int32_t OnFsync(uint64_t inode) = 0; 36 virtual void OnFsync(uint64_t unique, uint64_t inode) = 0;
30 virtual int32_t OnWrite( 37 virtual void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,
31 uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0; 38 const void* data) = 0;
32 virtual int32_t OnRead( 39 virtual void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) = 0;
33 uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0; 40 virtual void OnOpen(uint64_t unique, uint64_t inode) = 0;
34 virtual int32_t OnOpen(uint64_t inode) = 0; 41 virtual void OnRelease(uint64_t unique, uint64_t inode) = 0;
35 virtual int32_t OnRelease(uint64_t inode) = 0; 42 virtual ~FuseAppLoopCallback();
36 virtual ~FuseAppLoopCallback() = default; 43};
44
45class FuseAppLoop final {
46 public:
47 FuseAppLoop(base::unique_fd&& fd);
48
49 void Start(FuseAppLoopCallback* callback);
50 void Break();
51
52 bool ReplySimple(uint64_t unique, int32_t result);
53 bool ReplyLookup(uint64_t unique, uint64_t inode, int64_t size);
54 bool ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode);
55 bool ReplyOpen(uint64_t unique, uint64_t fh);
56 bool ReplyWrite(uint64_t unique, uint32_t size);
57 bool ReplyRead(uint64_t unique, uint32_t size, const void* data);
58
59 private:
60 base::unique_fd fd_;
61 base::unique_fd break_fd_;
62
63 // Lock for multi-threading.
64 std::mutex mutex_;
37}; 65};
38 66
39bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback); 67bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
index 64dd81330..98e36652a 100644
--- a/libappfuse/tests/FuseAppLoopTest.cc
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -23,6 +23,9 @@
23#include <gtest/gtest.h> 23#include <gtest/gtest.h>
24#include <thread> 24#include <thread>
25 25
26#include "libappfuse/EpollController.h"
27#include "libappfuse/FuseBridgeLoop.h"
28
26namespace android { 29namespace android {
27namespace fuse { 30namespace fuse {
28namespace { 31namespace {
@@ -37,82 +40,61 @@ struct CallbackRequest {
37class Callback : public FuseAppLoopCallback { 40class Callback : public FuseAppLoopCallback {
38 public: 41 public:
39 std::vector<CallbackRequest> requests; 42 std::vector<CallbackRequest> requests;
43 FuseAppLoop* loop;
40 44
41 bool IsActive() override { 45 void OnGetAttr(uint64_t seq, uint64_t inode) override {
42 return true; 46 EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
47 EXPECT_TRUE(loop->ReplyGetAttr(seq, inode, kTestFileSize, S_IFREG | 0777));
43 } 48 }
44 49
45 int64_t OnGetSize(uint64_t inode) override { 50 void OnLookup(uint64_t unique, uint64_t inode) override {
46 if (inode == FUSE_ROOT_ID) { 51 EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
47 return 0; 52 EXPECT_TRUE(loop->ReplyLookup(unique, inode, kTestFileSize));
48 } else {
49 return kTestFileSize;
50 }
51 } 53 }
52 54
53 int32_t OnFsync(uint64_t inode) override { 55 void OnFsync(uint64_t seq, uint64_t inode) override {
54 requests.push_back({ 56 requests.push_back({.code = FUSE_FSYNC, .inode = inode});
55 .code = FUSE_FSYNC, 57 loop->ReplySimple(seq, 0);
56 .inode = inode
57 });
58 return 0;
59 } 58 }
60 59
61 int32_t OnWrite(uint64_t inode, 60 void OnWrite(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
62 uint64_t offset ATTRIBUTE_UNUSED, 61 uint32_t size ATTRIBUTE_UNUSED, const void* data ATTRIBUTE_UNUSED) override {
63 uint32_t size ATTRIBUTE_UNUSED, 62 requests.push_back({.code = FUSE_WRITE, .inode = inode});
64 const void* data ATTRIBUTE_UNUSED) override { 63 loop->ReplyWrite(seq, 0);
65 requests.push_back({
66 .code = FUSE_WRITE,
67 .inode = inode
68 });
69 return 0;
70 } 64 }
71 65
72 int32_t OnRead(uint64_t inode, 66 void OnRead(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
73 uint64_t offset ATTRIBUTE_UNUSED, 67 uint32_t size ATTRIBUTE_UNUSED) override {
74 uint32_t size ATTRIBUTE_UNUSED, 68 requests.push_back({.code = FUSE_READ, .inode = inode});
75 void* data ATTRIBUTE_UNUSED) override { 69 loop->ReplySimple(seq, 0);
76 requests.push_back({
77 .code = FUSE_READ,
78 .inode = inode
79 });
80 return 0;
81 } 70 }
82 71
83 int32_t OnOpen(uint64_t inode) override { 72 void OnOpen(uint64_t seq, uint64_t inode) override {
84 requests.push_back({ 73 requests.push_back({.code = FUSE_OPEN, .inode = inode});
85 .code = FUSE_OPEN, 74 loop->ReplyOpen(seq, inode);
86 .inode = inode
87 });
88 return 0;
89 } 75 }
90 76
91 int32_t OnRelease(uint64_t inode) override { 77 void OnRelease(uint64_t seq, uint64_t inode) override {
92 requests.push_back({ 78 requests.push_back({.code = FUSE_RELEASE, .inode = inode});
93 .code = FUSE_RELEASE, 79 loop->ReplySimple(seq, 0);
94 .inode = inode
95 });
96 return 0;
97 } 80 }
98}; 81};
99 82
100class FuseAppLoopTest : public ::testing::Test { 83class FuseAppLoopTest : public ::testing::Test {
101 private:
102 std::thread thread_;
103
104 protected: 84 protected:
105 base::unique_fd sockets_[2]; 85 std::thread thread_;
106 Callback callback_; 86 base::unique_fd sockets_[2];
107 FuseRequest request_; 87 Callback callback_;
108 FuseResponse response_; 88 FuseRequest request_;
109 89 FuseResponse response_;
110 void SetUp() override { 90 std::unique_ptr<FuseAppLoop> loop_;
111 base::SetMinimumLogSeverity(base::VERBOSE); 91
112 ASSERT_TRUE(SetupMessageSockets(&sockets_)); 92 void SetUp() override {
113 thread_ = std::thread([this] { 93 base::SetMinimumLogSeverity(base::VERBOSE);
114 StartFuseAppLoop(sockets_[1].release(), &callback_); 94 ASSERT_TRUE(SetupMessageSockets(&sockets_));
115 }); 95 loop_.reset(new FuseAppLoop(std::move(sockets_[1])));
96 callback_.loop = loop_.get();
97 thread_ = std::thread([this] { loop_->Start(&callback_); });
116 } 98 }
117 99
118 void CheckCallback( 100 void CheckCallback(
@@ -300,5 +282,18 @@ TEST_F(FuseAppLoopTest, Write) {
300 CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out)); 282 CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
301} 283}
302 284
285TEST_F(FuseAppLoopTest, Break) {
286 // Ensure that the loop started.
287 request_.Reset(sizeof(fuse_open_in), FUSE_OPEN, 1);
288 request_.header.nodeid = 10;
289 ASSERT_TRUE(request_.Write(sockets_[0]));
290 ASSERT_TRUE(response_.Read(sockets_[0]));
291
292 loop_->Break();
293 if (thread_.joinable()) {
294 thread_.join();
295 }
296}
297
303} // namespace fuse 298} // namespace fuse
304} // namespace android 299} // namespace android