diff options
author | Daichi Hirono | 2017-03-05 23:02:42 -0600 |
---|---|---|
committer | Daichi Hirono | 2017-03-13 19:09:29 -0500 |
commit | 57b780fbc3e2c2442c2f58dcb83818e786246b35 (patch) | |
tree | cf2426a800b3566d8f65b884831bf75e42cd47e1 /libappfuse | |
parent | cc9d94ce04dbb63a745001323213bb667ea2e00f (diff) | |
download | platform-system-core-57b780fbc3e2c2442c2f58dcb83818e786246b35.tar.gz platform-system-core-57b780fbc3e2c2442c2f58dcb83818e786246b35.tar.xz platform-system-core-57b780fbc3e2c2442c2f58dcb83818e786246b35.zip |
Add ReadOrAgain and WriteOrAgain methods to FuseMessage.
These methods return kAgain if operation cannot be done without blocking
the current thread.
The CL also introduecs new helper function SetupMessageSockets so that
FuseMessages are always transfered via sockets that save message
boundaries.
Bug: 34903085
Test: libappfuse_test
Change-Id: I34544372cc1b0c7bc9622e581ae16c018a123fa9
Diffstat (limited to 'libappfuse')
-rw-r--r-- | libappfuse/FuseBuffer.cc | 157 | ||||
-rw-r--r-- | libappfuse/include/libappfuse/FuseBuffer.h | 17 | ||||
-rw-r--r-- | libappfuse/tests/FuseAppLoopTest.cc | 5 | ||||
-rw-r--r-- | libappfuse/tests/FuseBridgeLoopTest.cc | 11 | ||||
-rw-r--r-- | libappfuse/tests/FuseBufferTest.cc | 48 |
5 files changed, 148 insertions, 90 deletions
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc index 8fb2dbcc5..13cfc88ec 100644 --- a/libappfuse/FuseBuffer.cc +++ b/libappfuse/FuseBuffer.cc | |||
@@ -23,77 +23,132 @@ | |||
23 | #include <algorithm> | 23 | #include <algorithm> |
24 | #include <type_traits> | 24 | #include <type_traits> |
25 | 25 | ||
26 | #include <sys/socket.h> | ||
27 | |||
26 | #include <android-base/file.h> | 28 | #include <android-base/file.h> |
27 | #include <android-base/logging.h> | 29 | #include <android-base/logging.h> |
28 | #include <android-base/macros.h> | 30 | #include <android-base/macros.h> |
29 | 31 | ||
30 | namespace android { | 32 | namespace android { |
31 | namespace fuse { | 33 | namespace fuse { |
32 | 34 | namespace { | |
33 | static_assert( | ||
34 | std::is_standard_layout<FuseBuffer>::value, | ||
35 | "FuseBuffer must be standard layout union."); | ||
36 | 35 | ||
37 | template <typename T> | 36 | template <typename T> |
38 | bool FuseMessage<T>::CheckHeaderLength(const char* name) const { | 37 | bool CheckHeaderLength(const FuseMessage<T>* self, const char* name) { |
39 | const auto& header = static_cast<const T*>(this)->header; | 38 | const auto& header = static_cast<const T*>(self)->header; |
40 | if (header.len >= sizeof(header) && header.len <= sizeof(T)) { | 39 | if (header.len >= sizeof(header) && header.len <= sizeof(T)) { |
41 | return true; | 40 | return true; |
42 | } else { | 41 | } else { |
43 | LOG(ERROR) << "Invalid header length is found in " << name << ": " << | 42 | LOG(ERROR) << "Invalid header length is found in " << name << ": " << header.len; |
44 | header.len; | 43 | return false; |
45 | return false; | 44 | } |
46 | } | ||
47 | } | 45 | } |
48 | 46 | ||
49 | template <typename T> | 47 | template <typename T> |
50 | bool FuseMessage<T>::Read(int fd) { | 48 | ResultOrAgain ReadInternal(FuseMessage<T>* self, int fd, int sockflag) { |
51 | char* const buf = reinterpret_cast<char*>(this); | 49 | char* const buf = reinterpret_cast<char*>(self); |
52 | const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, buf, sizeof(T))); | 50 | const ssize_t result = sockflag ? TEMP_FAILURE_RETRY(recv(fd, buf, sizeof(T), sockflag)) |
53 | if (result < 0) { | 51 | : TEMP_FAILURE_RETRY(read(fd, buf, sizeof(T))); |
54 | PLOG(ERROR) << "Failed to read a FUSE message"; | 52 | |
55 | return false; | 53 | switch (result) { |
56 | } | 54 | case 0: |
57 | 55 | // Expected EOF. | |
58 | const auto& header = static_cast<const T*>(this)->header; | 56 | return ResultOrAgain::kFailure; |
59 | if (result < static_cast<ssize_t>(sizeof(header))) { | 57 | case -1: |
60 | LOG(ERROR) << "Read bytes " << result << " are shorter than header size " << | 58 | if (errno == EAGAIN) { |
61 | sizeof(header); | 59 | return ResultOrAgain::kAgain; |
62 | return false; | 60 | } |
63 | } | 61 | PLOG(ERROR) << "Failed to read a FUSE message"; |
62 | return ResultOrAgain::kFailure; | ||
63 | } | ||
64 | |||
65 | const auto& header = static_cast<const T*>(self)->header; | ||
66 | if (result < static_cast<ssize_t>(sizeof(header))) { | ||
67 | LOG(ERROR) << "Read bytes " << result << " are shorter than header size " << sizeof(header); | ||
68 | return ResultOrAgain::kFailure; | ||
69 | } | ||
70 | |||
71 | if (!CheckHeaderLength<T>(self, "Read")) { | ||
72 | return ResultOrAgain::kFailure; | ||
73 | } | ||
74 | |||
75 | if (static_cast<uint32_t>(result) != header.len) { | ||
76 | LOG(ERROR) << "Read bytes " << result << " are different from header.len " << header.len; | ||
77 | return ResultOrAgain::kFailure; | ||
78 | } | ||
79 | |||
80 | return ResultOrAgain::kSuccess; | ||
81 | } | ||
64 | 82 | ||
65 | if (!CheckHeaderLength("Read")) { | 83 | template <typename T> |
66 | return false; | 84 | ResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag) { |
67 | } | 85 | if (!CheckHeaderLength<T>(self, "Write")) { |
86 | return ResultOrAgain::kFailure; | ||
87 | } | ||
88 | |||
89 | const char* const buf = reinterpret_cast<const char*>(self); | ||
90 | const auto& header = static_cast<const T*>(self)->header; | ||
91 | const int result = sockflag ? TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag)) | ||
92 | : TEMP_FAILURE_RETRY(write(fd, buf, header.len)); | ||
93 | |||
94 | if (result == -1) { | ||
95 | if (errno == EAGAIN) { | ||
96 | return ResultOrAgain::kAgain; | ||
97 | } | ||
98 | PLOG(ERROR) << "Failed to write a FUSE message"; | ||
99 | return ResultOrAgain::kFailure; | ||
100 | } | ||
101 | |||
102 | CHECK(static_cast<uint32_t>(result) == header.len); | ||
103 | return ResultOrAgain::kSuccess; | ||
104 | } | ||
105 | } | ||
68 | 106 | ||
69 | if (static_cast<uint32_t>(result) > header.len) { | 107 | static_assert(std::is_standard_layout<FuseBuffer>::value, |
70 | LOG(ERROR) << "Read bytes " << result << " are longer than header.len " << | 108 | "FuseBuffer must be standard layout union."); |
71 | header.len; | 109 | |
72 | return false; | 110 | bool SetupMessageSockets(base::unique_fd (*result)[2]) { |
73 | } | 111 | base::unique_fd fds[2]; |
112 | { | ||
113 | int raw_fds[2]; | ||
114 | if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_fds) == -1) { | ||
115 | PLOG(ERROR) << "Failed to create sockets for proxy"; | ||
116 | return false; | ||
117 | } | ||
118 | fds[0].reset(raw_fds[0]); | ||
119 | fds[1].reset(raw_fds[1]); | ||
120 | } | ||
121 | |||
122 | constexpr int kMaxMessageSize = sizeof(FuseBuffer); | ||
123 | if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0 || | ||
124 | setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0) { | ||
125 | PLOG(ERROR) << "Failed to update buffer size for socket"; | ||
126 | return false; | ||
127 | } | ||
128 | |||
129 | (*result)[0] = std::move(fds[0]); | ||
130 | (*result)[1] = std::move(fds[1]); | ||
131 | return true; | ||
132 | } | ||
74 | 133 | ||
75 | if (!base::ReadFully(fd, buf + result, header.len - result)) { | 134 | template <typename T> |
76 | PLOG(ERROR) << "ReadFully failed"; | 135 | bool FuseMessage<T>::Read(int fd) { |
77 | return false; | 136 | return ReadInternal(this, fd, 0) == ResultOrAgain::kSuccess; |
78 | } | 137 | } |
79 | 138 | ||
80 | return true; | 139 | template <typename T> |
140 | ResultOrAgain FuseMessage<T>::ReadOrAgain(int fd) { | ||
141 | return ReadInternal(this, fd, MSG_DONTWAIT); | ||
81 | } | 142 | } |
82 | 143 | ||
83 | template <typename T> | 144 | template <typename T> |
84 | bool FuseMessage<T>::Write(int fd) const { | 145 | bool FuseMessage<T>::Write(int fd) const { |
85 | if (!CheckHeaderLength("Write")) { | 146 | return WriteInternal(this, fd, 0) == ResultOrAgain::kSuccess; |
86 | return false; | 147 | } |
87 | } | ||
88 | |||
89 | const char* const buf = reinterpret_cast<const char*>(this); | ||
90 | const auto& header = static_cast<const T*>(this)->header; | ||
91 | if (!base::WriteFully(fd, buf, header.len)) { | ||
92 | PLOG(ERROR) << "WriteFully failed"; | ||
93 | return false; | ||
94 | } | ||
95 | 148 | ||
96 | return true; | 149 | template <typename T> |
150 | ResultOrAgain FuseMessage<T>::WriteOrAgain(int fd) const { | ||
151 | return WriteInternal(this, fd, MSG_DONTWAIT); | ||
97 | } | 152 | } |
98 | 153 | ||
99 | template class FuseMessage<FuseRequest>; | 154 | template class FuseMessage<FuseRequest>; |
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h index 7abd2fa40..fbb05d633 100644 --- a/libappfuse/include/libappfuse/FuseBuffer.h +++ b/libappfuse/include/libappfuse/FuseBuffer.h | |||
@@ -17,6 +17,7 @@ | |||
17 | #ifndef ANDROID_LIBAPPFUSE_FUSEBUFFER_H_ | 17 | #ifndef ANDROID_LIBAPPFUSE_FUSEBUFFER_H_ |
18 | #define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_ | 18 | #define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_ |
19 | 19 | ||
20 | #include <android-base/unique_fd.h> | ||
20 | #include <linux/fuse.h> | 21 | #include <linux/fuse.h> |
21 | 22 | ||
22 | namespace android { | 23 | namespace android { |
@@ -28,12 +29,24 @@ constexpr size_t kFuseMaxWrite = 256 * 1024; | |||
28 | constexpr size_t kFuseMaxRead = 128 * 1024; | 29 | constexpr size_t kFuseMaxRead = 128 * 1024; |
29 | constexpr int32_t kFuseSuccess = 0; | 30 | constexpr int32_t kFuseSuccess = 0; |
30 | 31 | ||
32 | // Setup sockets to transfer FuseMessage. | ||
33 | bool SetupMessageSockets(base::unique_fd (*sockets)[2]); | ||
34 | |||
35 | enum class ResultOrAgain { | ||
36 | kSuccess, | ||
37 | kFailure, | ||
38 | kAgain, | ||
39 | }; | ||
40 | |||
31 | template<typename T> | 41 | template<typename T> |
32 | class FuseMessage { | 42 | class FuseMessage { |
33 | public: | 43 | public: |
34 | bool Read(int fd); | 44 | bool Read(int fd); |
35 | bool Write(int fd) const; | 45 | bool Write(int fd) const; |
36 | private: | 46 | ResultOrAgain ReadOrAgain(int fd); |
47 | ResultOrAgain WriteOrAgain(int fd) const; | ||
48 | |||
49 | private: | ||
37 | bool CheckHeaderLength(const char* name) const; | 50 | bool CheckHeaderLength(const char* name) const; |
38 | }; | 51 | }; |
39 | 52 | ||
@@ -54,7 +67,7 @@ struct FuseRequest : public FuseMessage<FuseRequest> { | |||
54 | // for FUSE_READ | 67 | // for FUSE_READ |
55 | fuse_read_in read_in; | 68 | fuse_read_in read_in; |
56 | // for FUSE_LOOKUP | 69 | // for FUSE_LOOKUP |
57 | char lookup_name[0]; | 70 | char lookup_name[kFuseMaxWrite]; |
58 | }; | 71 | }; |
59 | void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique); | 72 | void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique); |
60 | }; | 73 | }; |
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc index 25906cf1c..64dd81330 100644 --- a/libappfuse/tests/FuseAppLoopTest.cc +++ b/libappfuse/tests/FuseAppLoopTest.cc | |||
@@ -109,10 +109,7 @@ class FuseAppLoopTest : public ::testing::Test { | |||
109 | 109 | ||
110 | void SetUp() override { | 110 | void SetUp() override { |
111 | base::SetMinimumLogSeverity(base::VERBOSE); | 111 | base::SetMinimumLogSeverity(base::VERBOSE); |
112 | int sockets[2]; | 112 | ASSERT_TRUE(SetupMessageSockets(&sockets_)); |
113 | ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)); | ||
114 | sockets_[0].reset(sockets[0]); | ||
115 | sockets_[1].reset(sockets[1]); | ||
116 | thread_ = std::thread([this] { | 113 | thread_ = std::thread([this] { |
117 | StartFuseAppLoop(sockets_[1].release(), &callback_); | 114 | StartFuseAppLoop(sockets_[1].release(), &callback_); |
118 | }); | 115 | }); |
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc index e74d9e700..b4c1efb01 100644 --- a/libappfuse/tests/FuseBridgeLoopTest.cc +++ b/libappfuse/tests/FuseBridgeLoopTest.cc | |||
@@ -50,15 +50,8 @@ class FuseBridgeLoopTest : public ::testing::Test { | |||
50 | 50 | ||
51 | void SetUp() override { | 51 | void SetUp() override { |
52 | base::SetMinimumLogSeverity(base::VERBOSE); | 52 | base::SetMinimumLogSeverity(base::VERBOSE); |
53 | int dev_sockets[2]; | 53 | ASSERT_TRUE(SetupMessageSockets(&dev_sockets_)); |
54 | int proxy_sockets[2]; | 54 | ASSERT_TRUE(SetupMessageSockets(&proxy_sockets_)); |
55 | ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets)); | ||
56 | ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets)); | ||
57 | dev_sockets_[0].reset(dev_sockets[0]); | ||
58 | dev_sockets_[1].reset(dev_sockets[1]); | ||
59 | proxy_sockets_[0].reset(proxy_sockets[0]); | ||
60 | proxy_sockets_[1].reset(proxy_sockets[1]); | ||
61 | |||
62 | thread_ = std::thread([this] { | 55 | thread_ = std::thread([this] { |
63 | StartFuseBridgeLoop( | 56 | StartFuseBridgeLoop( |
64 | dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_); | 57 | dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_); |
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc index 1a1abd57e..ade34acc1 100644 --- a/libappfuse/tests/FuseBufferTest.cc +++ b/libappfuse/tests/FuseBufferTest.cc | |||
@@ -112,30 +112,6 @@ TEST(FuseMessageTest, Write_TooShort) { | |||
112 | TestWriteInvalidLength(sizeof(fuse_in_header) - 1); | 112 | TestWriteInvalidLength(sizeof(fuse_in_header) - 1); |
113 | } | 113 | } |
114 | 114 | ||
115 | TEST(FuseMessageTest, ShortWriteAndRead) { | ||
116 | int raw_fds[2]; | ||
117 | ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, raw_fds)); | ||
118 | |||
119 | android::base::unique_fd fds[2]; | ||
120 | fds[0].reset(raw_fds[0]); | ||
121 | fds[1].reset(raw_fds[1]); | ||
122 | |||
123 | const int send_buffer_size = 1024; | ||
124 | ASSERT_EQ(0, setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &send_buffer_size, | ||
125 | sizeof(int))); | ||
126 | |||
127 | bool succeed = false; | ||
128 | const int sender_fd = fds[0].get(); | ||
129 | std::thread thread([sender_fd, &succeed] { | ||
130 | FuseRequest request; | ||
131 | request.header.len = 1024 * 4; | ||
132 | succeed = request.Write(sender_fd); | ||
133 | }); | ||
134 | thread.detach(); | ||
135 | FuseRequest request; | ||
136 | ASSERT_TRUE(request.Read(fds[1])); | ||
137 | } | ||
138 | |||
139 | TEST(FuseResponseTest, Reset) { | 115 | TEST(FuseResponseTest, Reset) { |
140 | FuseResponse response; | 116 | FuseResponse response; |
141 | // Write 1 to the first ten bytes. | 117 | // Write 1 to the first ten bytes. |
@@ -211,5 +187,29 @@ TEST(FuseBufferTest, HandleNotImpl) { | |||
211 | EXPECT_EQ(-ENOSYS, buffer.response.header.error); | 187 | EXPECT_EQ(-ENOSYS, buffer.response.header.error); |
212 | } | 188 | } |
213 | 189 | ||
190 | TEST(SetupMessageSocketsTest, Stress) { | ||
191 | constexpr int kCount = 1000; | ||
192 | |||
193 | FuseRequest request; | ||
194 | request.header.len = sizeof(FuseRequest); | ||
195 | |||
196 | base::unique_fd fds[2]; | ||
197 | SetupMessageSockets(&fds); | ||
198 | |||
199 | std::thread thread([&fds] { | ||
200 | FuseRequest request; | ||
201 | for (int i = 0; i < kCount; ++i) { | ||
202 | ASSERT_TRUE(request.Read(fds[1])); | ||
203 | usleep(1000); | ||
204 | } | ||
205 | }); | ||
206 | |||
207 | for (int i = 0; i < kCount; ++i) { | ||
208 | ASSERT_TRUE(request.Write(fds[0])); | ||
209 | } | ||
210 | |||
211 | thread.join(); | ||
212 | } | ||
213 | |||
214 | } // namespace fuse | 214 | } // namespace fuse |
215 | } // namespace android | 215 | } // namespace android |