Merge "Add FuseAppLoop to libappfuse." am: e1d24f8465
am: 1eda78472a
Change-Id: If03248dcf5efdc46c735fb50d217aadb8a97cd3c
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index 8b46154..f729faf 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -15,12 +15,20 @@
name: "libappfuse",
defaults: ["libappfuse_defaults"],
export_include_dirs: ["include"],
- srcs: ["FuseBuffer.cc", "FuseBridgeLoop.cc"]
+ srcs: [
+ "FuseAppLoop.cc",
+ "FuseBuffer.cc",
+ "FuseBridgeLoop.cc",
+ ]
}
cc_test {
name: "libappfuse_test",
defaults: ["libappfuse_defaults"],
shared_libs: ["libappfuse"],
- srcs: ["tests/FuseBridgeLoopTest.cc", "tests/FuseBufferTest.cc"]
+ srcs: [
+ "tests/FuseAppLoopTest.cc",
+ "tests/FuseBridgeLoopTest.cc",
+ "tests/FuseBufferTest.cc",
+ ]
}
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
new file mode 100644
index 0000000..a31880e
--- /dev/null
+++ b/libappfuse/FuseAppLoop.cc
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/stat.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fuse {
+
+namespace {
+
+void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ // AppFuse does not support directory structure now.
+ // It can lookup only files under the mount point.
+ if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
+ LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ // Ensure that the filename ends with 0.
+ const size_t filename_length =
+ buffer->request.header.len - sizeof(fuse_in_header);
+ if (buffer->request.lookup_name[filename_length - 1] != 0) {
+ LOG(ERROR) << "File name does not end with 0.";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ const uint64_t inode =
+ static_cast<uint64_t>(atol(buffer->request.lookup_name));
+ if (inode == 0 || inode == LONG_MAX) {
+ LOG(ERROR) << "Invalid filename";
+ buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+ return;
+ }
+
+ const int64_t size = callback->OnGetSize(inode);
+ if (size < 0) {
+ buffer->response.Reset(0, size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.Reset(sizeof(fuse_entry_out), 0,
+ buffer->request.header.unique);
+ buffer->response.entry_out.nodeid = inode;
+ buffer->response.entry_out.attr_valid = 10;
+ buffer->response.entry_out.entry_valid = 10;
+ buffer->response.entry_out.attr.ino = inode;
+ buffer->response.entry_out.attr.mode = S_IFREG | 0777;
+ buffer->response.entry_out.attr.size = size;
+}
+
+void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ int64_t size;
+ uint32_t mode;
+ if (nodeid == FUSE_ROOT_ID) {
+ size = 0;
+ mode = S_IFDIR | 0777;
+ } else {
+ size = callback->OnGetSize(buffer->request.header.nodeid);
+ if (size < 0) {
+ buffer->response.Reset(0, size, buffer->request.header.unique);
+ return;
+ }
+ mode = S_IFREG | 0777;
+ }
+
+ buffer->response.Reset(sizeof(fuse_attr_out), 0,
+ buffer->request.header.unique);
+ buffer->response.attr_out.attr_valid = 10;
+ buffer->response.attr_out.attr.ino = nodeid;
+ buffer->response.attr_out.attr.mode = mode;
+ buffer->response.attr_out.attr.size = size;
+}
+
+void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
+ if (file_handle < 0) {
+ buffer->response.Reset(0, file_handle, buffer->request.header.unique);
+ return;
+ }
+ buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
+ buffer->request.header.unique);
+ buffer->response.open_out.fh = file_handle;
+}
+
+void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
+ buffer->request.header.unique);
+}
+
+void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
+ buffer->request.header.unique);
+}
+
+void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t unique = buffer->request.header.unique;
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ const uint64_t offset = buffer->request.read_in.offset;
+ const uint32_t size = buffer->request.read_in.size;
+
+ if (size > kFuseMaxRead) {
+ buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+ return;
+ }
+
+ const int32_t read_size = callback->OnRead(nodeid, offset, size,
+ buffer->response.read_data);
+ if (read_size < 0) {
+ buffer->response.Reset(0, read_size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
+}
+
+void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+ const uint64_t unique = buffer->request.header.unique;
+ const uint64_t nodeid = buffer->request.header.nodeid;
+ const uint64_t offset = buffer->request.write_in.offset;
+ const uint32_t size = buffer->request.write_in.size;
+
+ if (size > kFuseMaxWrite) {
+ buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+ return;
+ }
+
+ const int32_t write_size = callback->OnWrite(nodeid, offset, size,
+ buffer->request.write_data);
+ if (write_size < 0) {
+ buffer->response.Reset(0, write_size, buffer->request.header.unique);
+ return;
+ }
+
+ buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
+ buffer->response.write_out.size = write_size;
+}
+
+} // namespace
+
+bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
+ base::unique_fd fd(raw_fd);
+ FuseBuffer buffer;
+
+ LOG(DEBUG) << "Start fuse loop.";
+ while (callback->IsActive()) {
+ if (!buffer.request.Read(fd)) {
+ return false;
+ }
+
+ const uint32_t opcode = buffer.request.header.opcode;
+ LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+ switch (opcode) {
+ case FUSE_FORGET:
+ // Do not reply to FUSE_FORGET.
+ continue;
+
+ case FUSE_LOOKUP:
+ HandleLookUp(&buffer, callback);
+ break;
+
+ case FUSE_GETATTR:
+ HandleGetAttr(&buffer, callback);
+ break;
+
+ case FUSE_OPEN:
+ HandleOpen(&buffer, callback);
+ break;
+
+ case FUSE_READ:
+ HandleRead(&buffer, callback);
+ break;
+
+ case FUSE_WRITE:
+ HandleWrite(&buffer, callback);
+ break;
+
+ case FUSE_RELEASE:
+ HandleRelease(&buffer, callback);
+ break;
+
+ case FUSE_FSYNC:
+ HandleFsync(&buffer, callback);
+ break;
+
+ default:
+ buffer.HandleNotImpl();
+ break;
+ }
+
+ if (!buffer.response.Write(fd)) {
+ LOG(ERROR) << "Failed to write a response to the device.";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 332556d..acb963c 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -25,14 +25,15 @@
int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoop::Callback* callback) {
base::unique_fd dev_fd(raw_dev_fd);
base::unique_fd proxy_fd(raw_proxy_fd);
+ fuse::FuseBuffer buffer;
LOG(DEBUG) << "Start fuse loop.";
while (true) {
- if (!buffer_.request.Read(dev_fd)) {
+ if (!buffer.request.Read(dev_fd)) {
return false;
}
- const uint32_t opcode = buffer_.request.header.opcode;
+ const uint32_t opcode = buffer.request.header.opcode;
LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
switch (opcode) {
case FUSE_FORGET:
@@ -45,27 +46,27 @@
case FUSE_READ:
case FUSE_WRITE:
case FUSE_RELEASE:
- case FUSE_FLUSH:
- if (!buffer_.request.Write(proxy_fd)) {
+ case FUSE_FSYNC:
+ if (!buffer.request.Write(proxy_fd)) {
LOG(ERROR) << "Failed to write a request to the proxy.";
return false;
}
- if (!buffer_.response.Read(proxy_fd)) {
+ if (!buffer.response.Read(proxy_fd)) {
LOG(ERROR) << "Failed to read a response from the proxy.";
return false;
}
break;
case FUSE_INIT:
- buffer_.HandleInit();
+ buffer.HandleInit();
break;
default:
- buffer_.HandleNotImpl();
+ buffer.HandleNotImpl();
break;
}
- if (!buffer_.response.Write(dev_fd)) {
+ if (!buffer.response.Write(dev_fd)) {
LOG(ERROR) << "Failed to write a response to the device.";
return false;
}
@@ -76,4 +77,12 @@
}
}
+namespace fuse {
+
+bool StartFuseBridgeLoop(
+ int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoopCallback* callback) {
+ return FuseBridgeLoop().Start(raw_dev_fd, raw_proxy_fd, callback);
+}
+
+} // namespace fuse
} // namespace android
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 45280a5..ca47aa8 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -26,6 +26,7 @@
#include <android-base/macros.h>
namespace android {
+namespace fuse {
template <typename T, typename Header>
bool FuseMessage<T, Header>::CheckHeaderLength() const {
@@ -44,7 +45,7 @@
return true;
} else {
PLOG(ERROR) << "Failed to " << operation_name
- << " a packet from FD. result=" << result << " header.len="
+ << " a packet. result=" << result << " header.len="
<< header.len;
return false;
}
@@ -68,6 +69,14 @@
template struct FuseMessage<FuseRequest, fuse_in_header>;
template struct FuseMessage<FuseResponse, fuse_out_header>;
+void FuseRequest::Reset(
+ uint32_t data_length, uint32_t opcode, uint64_t unique) {
+ memset(this, 0, sizeof(fuse_in_header) + data_length);
+ header.len = sizeof(fuse_in_header) + data_length;
+ header.opcode = opcode;
+ header.unique = unique;
+}
+
void FuseResponse::ResetHeader(
uint32_t data_length, int32_t error, uint64_t unique) {
CHECK_LE(error, 0) << "error should be zero or negative.";
@@ -133,4 +142,5 @@
response.Reset(0, -ENOSYS, unique);
}
+} // namespace fuse
} // namespace android
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
new file mode 100644
index 0000000..c3edfcc
--- /dev/null
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+
+#include "libappfuse/FuseBuffer.h"
+
+namespace android {
+namespace fuse {
+
+class FuseAppLoopCallback {
+ public:
+ virtual bool IsActive() = 0;
+ virtual int64_t OnGetSize(uint64_t inode) = 0;
+ virtual int32_t OnFsync(uint64_t inode) = 0;
+ virtual int32_t OnWrite(
+ uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
+ virtual int32_t OnRead(
+ uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
+ virtual int32_t OnOpen(uint64_t inode) = 0;
+ virtual int32_t OnRelease(uint64_t inode) = 0;
+ virtual ~FuseAppLoopCallback() = default;
+};
+
+bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
+
+} // namespace fuse
+} // namespace android
+
+#endif // ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBridgeLoop.h b/libappfuse/include/libappfuse/FuseBridgeLoop.h
index 2006532..38043bc 100644
--- a/libappfuse/include/libappfuse/FuseBridgeLoop.h
+++ b/libappfuse/include/libappfuse/FuseBridgeLoop.h
@@ -21,7 +21,9 @@
namespace android {
-class FuseBridgeLoop {
+// TODO: Remove the class after switching to StartFuseBridgeLoop in the
+// framework code.
+class FuseBridgeLoop final {
public:
class Callback {
public:
@@ -30,11 +32,15 @@
};
bool Start(int dev_fd, int proxy_fd, Callback* callback);
-
- private:
- FuseBuffer buffer_;
};
+namespace fuse {
+
+class FuseBridgeLoopCallback : public FuseBridgeLoop::Callback {};
+bool StartFuseBridgeLoop(
+ int dev_fd, int proxy_fd, FuseBridgeLoopCallback* callback);
+
+} // namespace fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index 071b777..1464142 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -20,6 +20,7 @@
#include <linux/fuse.h>
namespace android {
+namespace fuse {
// The numbers came from sdcard.c.
// Maximum number of bytes to write/read in one request/one reply.
@@ -37,33 +38,51 @@
bool CheckResult(int result, const char* operation_name) const;
};
-struct FuseRequest : public FuseMessage<FuseRequest, fuse_in_header> {
+// FuseRequest represents file operation requests from /dev/fuse. It starts
+// from fuse_in_header. The body layout depends on the operation code.
+struct FuseRequest final : public FuseMessage<FuseRequest, fuse_in_header> {
union {
+ // for FUSE_WRITE
struct {
fuse_write_in write_in;
char write_data[kFuseMaxWrite];
};
+ // for FUSE_OPEN
fuse_open_in open_in;
+ // for FUSE_INIT
fuse_init_in init_in;
+ // for FUSE_READ
fuse_read_in read_in;
+ // for FUSE_LOOKUP
char lookup_name[0];
};
+ void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
};
-struct FuseResponse : public FuseMessage<FuseResponse, fuse_out_header> {
+// FuseResponse represents file operation responses to /dev/fuse. It starts
+// from fuse_out_header. The body layout depends on the operation code.
+struct FuseResponse final : public FuseMessage<FuseResponse, fuse_out_header> {
union {
+ // for FUSE_INIT
fuse_init_out init_out;
+ // for FUSE_LOOKUP
fuse_entry_out entry_out;
+ // for FUSE_GETATTR
fuse_attr_out attr_out;
+ // for FUSE_OPEN
fuse_open_out open_out;
+ // for FUSE_READ
char read_data[kFuseMaxRead];
+ // for FUSE_WRITE
fuse_write_out write_out;
};
void Reset(uint32_t data_length, int32_t error, uint64_t unique);
void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
};
-union FuseBuffer {
+// To reduce memory usage, FuseBuffer shares the memory region for request and
+// response.
+union FuseBuffer final {
FuseRequest request;
FuseResponse response;
@@ -71,19 +90,7 @@
void HandleNotImpl();
};
-class FuseProxyLoop {
- class IFuseProxyLoopCallback {
- public:
- virtual void OnMount() = 0;
- virtual ~IFuseProxyLoopCallback() = default;
- };
-
- bool Start(int dev_fd, int proxy_fd, IFuseProxyLoopCallback* callback);
-
- private:
- FuseBuffer buffer_;
-};
-
+} // namespace fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
new file mode 100644
index 0000000..25906cf
--- /dev/null
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/socket.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+namespace fuse {
+namespace {
+
+constexpr unsigned int kTestFileSize = 1024;
+
+struct CallbackRequest {
+ uint32_t code;
+ uint64_t inode;
+};
+
+class Callback : public FuseAppLoopCallback {
+ public:
+ std::vector<CallbackRequest> requests;
+
+ bool IsActive() override {
+ return true;
+ }
+
+ int64_t OnGetSize(uint64_t inode) override {
+ if (inode == FUSE_ROOT_ID) {
+ return 0;
+ } else {
+ return kTestFileSize;
+ }
+ }
+
+ int32_t OnFsync(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_FSYNC,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnWrite(uint64_t inode,
+ uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED,
+ const void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({
+ .code = FUSE_WRITE,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnRead(uint64_t inode,
+ uint64_t offset ATTRIBUTE_UNUSED,
+ uint32_t size ATTRIBUTE_UNUSED,
+ void* data ATTRIBUTE_UNUSED) override {
+ requests.push_back({
+ .code = FUSE_READ,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnOpen(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_OPEN,
+ .inode = inode
+ });
+ return 0;
+ }
+
+ int32_t OnRelease(uint64_t inode) override {
+ requests.push_back({
+ .code = FUSE_RELEASE,
+ .inode = inode
+ });
+ return 0;
+ }
+};
+
+class FuseAppLoopTest : public ::testing::Test {
+ private:
+ std::thread thread_;
+
+ protected:
+ base::unique_fd sockets_[2];
+ Callback callback_;
+ FuseRequest request_;
+ FuseResponse response_;
+
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ int sockets[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
+ sockets_[0].reset(sockets[0]);
+ sockets_[1].reset(sockets[1]);
+ thread_ = std::thread([this] {
+ StartFuseAppLoop(sockets_[1].release(), &callback_);
+ });
+ }
+
+ void CheckCallback(
+ size_t data_size, uint32_t code, size_t expected_out_size) {
+ request_.Reset(data_size, code, 1);
+ request_.header.nodeid = 10;
+
+ ASSERT_TRUE(request_.Write(sockets_[0]));
+ ASSERT_TRUE(response_.Read(sockets_[0]));
+
+ Close();
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + expected_out_size,
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ ASSERT_EQ(1u, callback_.requests.size());
+ EXPECT_EQ(code, callback_.requests[0].code);
+ EXPECT_EQ(10u, callback_.requests[0].inode);
+ }
+
+ void Close() {
+ sockets_[0].reset();
+ sockets_[1].reset();
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+ }
+
+ void TearDown() override {
+ Close();
+ }
+};
+
+} // namespace
+
+TEST_F(FuseAppLoopTest, LookUp) {
+ request_.Reset(3u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "10");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_entry_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.entry_out.nodeid);
+ EXPECT_EQ(0u, response_.entry_out.generation);
+ EXPECT_EQ(10u, response_.entry_out.entry_valid);
+ EXPECT_EQ(10u, response_.entry_out.attr_valid);
+ EXPECT_EQ(0u, response_.entry_out.entry_valid_nsec);
+ EXPECT_EQ(0u, response_.entry_out.attr_valid_nsec);
+
+ EXPECT_EQ(10u, response_.entry_out.attr.ino);
+ EXPECT_EQ(kTestFileSize, response_.entry_out.attr.size);
+ EXPECT_EQ(0u, response_.entry_out.attr.blocks);
+ EXPECT_EQ(0u, response_.entry_out.attr.atime);
+ EXPECT_EQ(0u, response_.entry_out.attr.mtime);
+ EXPECT_EQ(0u, response_.entry_out.attr.ctime);
+ EXPECT_EQ(0u, response_.entry_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.entry_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.entry_out.attr.ctimensec);
+ EXPECT_EQ(S_IFREG | 0777u, response_.entry_out.attr.mode);
+ EXPECT_EQ(0u, response_.entry_out.attr.nlink);
+ EXPECT_EQ(0u, response_.entry_out.attr.uid);
+ EXPECT_EQ(0u, response_.entry_out.attr.gid);
+ EXPECT_EQ(0u, response_.entry_out.attr.rdev);
+ EXPECT_EQ(0u, response_.entry_out.attr.blksize);
+ EXPECT_EQ(0u, response_.entry_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
+ request_.Reset(3u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "aa");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+ EXPECT_EQ(-ENOENT, response_.header.error);
+ EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_TooLargeName) {
+ request_.Reset(21u, FUSE_LOOKUP, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+ strcpy(request_.lookup_name, "18446744073709551616");
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+ EXPECT_EQ(-ENOENT, response_.header.error);
+ EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr) {
+ request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+ request_.header.nodeid = 10;
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.attr_out.attr_valid);
+ EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+ EXPECT_EQ(10u, response_.attr_out.attr.ino);
+ EXPECT_EQ(kTestFileSize, response_.attr_out.attr.size);
+ EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+ EXPECT_EQ(0u, response_.attr_out.attr.atime);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+ EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+ EXPECT_EQ(S_IFREG | 0777u, response_.attr_out.attr.mode);
+ EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+ EXPECT_EQ(0u, response_.attr_out.attr.uid);
+ EXPECT_EQ(0u, response_.attr_out.attr.gid);
+ EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+ EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr_Root) {
+ request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+ request_.header.nodeid = FUSE_ROOT_ID;
+
+ ASSERT_TRUE(request_.Write(sockets_[0].get()));
+ ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+ EXPECT_EQ(kFuseSuccess, response_.header.error);
+ EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+ response_.header.len);
+ EXPECT_EQ(1u, response_.header.unique);
+
+ EXPECT_EQ(10u, response_.attr_out.attr_valid);
+ EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+ EXPECT_EQ(static_cast<unsigned>(FUSE_ROOT_ID), response_.attr_out.attr.ino);
+ EXPECT_EQ(0u, response_.attr_out.attr.size);
+ EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+ EXPECT_EQ(0u, response_.attr_out.attr.atime);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+ EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+ EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+ EXPECT_EQ(S_IFDIR | 0777u, response_.attr_out.attr.mode);
+ EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+ EXPECT_EQ(0u, response_.attr_out.attr.uid);
+ EXPECT_EQ(0u, response_.attr_out.attr.gid);
+ EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+ EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+ EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, Open) {
+ CheckCallback(sizeof(fuse_open_in), FUSE_OPEN, sizeof(fuse_open_out));
+}
+
+TEST_F(FuseAppLoopTest, Fsync) {
+ CheckCallback(0u, FUSE_FSYNC, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Release) {
+ CheckCallback(0u, FUSE_RELEASE, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Read) {
+ CheckCallback(sizeof(fuse_read_in), FUSE_READ, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Write) {
+ CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
+}
+
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
index 31e3690..bd503eb 100644
--- a/libappfuse/tests/FuseBridgeLoopTest.cc
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -21,11 +21,15 @@
#include <sstream>
#include <thread>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
namespace android {
+namespace fuse {
+namespace {
-class Callback : public FuseBridgeLoop::Callback {
+class Callback : public FuseBridgeLoopCallback {
public:
bool mounted;
Callback() : mounted(false) {}
@@ -36,20 +40,28 @@
class FuseBridgeLoopTest : public ::testing::Test {
protected:
- int dev_sockets_[2];
- int proxy_sockets_[2];
+ base::unique_fd dev_sockets_[2];
+ base::unique_fd proxy_sockets_[2];
Callback callback_;
std::thread thread_;
FuseRequest request_;
FuseResponse response_;
- void SetUp() {
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets_));
- ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets_));
+ void SetUp() override {
+ base::SetMinimumLogSeverity(base::VERBOSE);
+ int dev_sockets[2];
+ int proxy_sockets[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets));
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets));
+ dev_sockets_[0].reset(dev_sockets[0]);
+ dev_sockets_[1].reset(dev_sockets[1]);
+ proxy_sockets_[0].reset(proxy_sockets[0]);
+ proxy_sockets_[1].reset(proxy_sockets[1]);
+
thread_ = std::thread([this] {
- FuseBridgeLoop loop;
- loop.Start(dev_sockets_[1], proxy_sockets_[0], &callback_);
+ StartFuseBridgeLoop(
+ dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_);
});
}
@@ -103,20 +115,22 @@
}
void Close() {
- close(dev_sockets_[0]);
- close(dev_sockets_[1]);
- close(proxy_sockets_[0]);
- close(proxy_sockets_[1]);
+ dev_sockets_[0].reset();
+ dev_sockets_[1].reset();
+ proxy_sockets_[0].reset();
+ proxy_sockets_[1].reset();
if (thread_.joinable()) {
thread_.join();
}
}
- void TearDown() {
+ void TearDown() override {
Close();
}
};
+} // namespace
+
TEST_F(FuseBridgeLoopTest, FuseInit) {
SendInitRequest(1u);
@@ -156,11 +170,11 @@
CheckNotImpl(FUSE_RENAME);
CheckNotImpl(FUSE_LINK);
CheckNotImpl(FUSE_STATFS);
- CheckNotImpl(FUSE_FSYNC);
CheckNotImpl(FUSE_SETXATTR);
CheckNotImpl(FUSE_GETXATTR);
CheckNotImpl(FUSE_LISTXATTR);
CheckNotImpl(FUSE_REMOVEXATTR);
+ CheckNotImpl(FUSE_FLUSH);
CheckNotImpl(FUSE_OPENDIR);
CheckNotImpl(FUSE_READDIR);
CheckNotImpl(FUSE_RELEASEDIR);
@@ -190,7 +204,8 @@
CheckProxy(FUSE_READ);
CheckProxy(FUSE_WRITE);
CheckProxy(FUSE_RELEASE);
- CheckProxy(FUSE_FLUSH);
+ CheckProxy(FUSE_FSYNC);
}
-} // android
+} // namespace fuse
+} // namespace android
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index 1aacfe3..17f1306 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -24,6 +24,7 @@
#include <gtest/gtest.h>
namespace android {
+namespace fuse {
constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
@@ -183,5 +184,6 @@
ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);
EXPECT_EQ(-ENOSYS, buffer.response.header.error);
}
-}
- // namespace android
+
+} // namespace fuse
+} // namespace android