Add FuseBridgeLoop to libappfuse.

The CL adds FuseBridgeLoop class to libappfuse, which is used in the
system service to proxy fuse commands to applications.

Bug: 29970149
Test: libappfuse_test
Change-Id: I0708f608b3868721ab16ba4028fd2c17a6735af7
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
new file mode 100644
index 0000000..31e3690
--- /dev/null
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -0,0 +1,196 @@
+/*
+ * 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/FuseBridgeLoop.h"
+
+#include <sys/socket.h>
+
+#include <sstream>
+#include <thread>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+class Callback : public FuseBridgeLoop::Callback {
+ public:
+  bool mounted;
+  Callback() : mounted(false) {}
+  void OnMount() override {
+    mounted = true;
+  }
+};
+
+class FuseBridgeLoopTest : public ::testing::Test {
+ protected:
+  int dev_sockets_[2];
+  int 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_));
+    thread_ = std::thread([this] {
+      FuseBridgeLoop loop;
+      loop.Start(dev_sockets_[1], proxy_sockets_[0], &callback_);
+    });
+  }
+
+  void CheckNotImpl(uint32_t opcode) {
+    SCOPED_TRACE((std::ostringstream() << "opcode: " << opcode).str());
+
+    memset(&request_, 0, sizeof(FuseRequest));
+    request_.header.opcode = opcode;
+    request_.header.len = sizeof(fuse_in_header);
+    ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+
+    memset(&response_, 0, sizeof(FuseResponse));
+    ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+    EXPECT_EQ(-ENOSYS, response_.header.error);
+  }
+
+  void CheckProxy(uint32_t opcode) {
+    SCOPED_TRACE((std::ostringstream() << "opcode: " << opcode).str());
+
+    memset(&request_, 0, sizeof(FuseRequest));
+    request_.header.opcode = opcode;
+    request_.header.unique = opcode; // Use opcode as unique.
+    request_.header.len = sizeof(fuse_in_header);
+    ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+
+    memset(&request_, 0, sizeof(FuseRequest));
+    ASSERT_TRUE(request_.Read(proxy_sockets_[1]));
+    EXPECT_EQ(opcode, request_.header.opcode);
+    EXPECT_EQ(opcode, request_.header.unique);
+
+    memset(&response_, 0, sizeof(FuseResponse));
+    response_.header.len = sizeof(fuse_out_header);
+    response_.header.unique = opcode;  // Use opcode as unique.
+    response_.header.error = kFuseSuccess;
+    ASSERT_TRUE(response_.Write(proxy_sockets_[1]));
+
+    memset(&response_, 0, sizeof(FuseResponse));
+    ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+    EXPECT_EQ(opcode, response_.header.unique);
+    EXPECT_EQ(kFuseSuccess, response_.header.error);
+  }
+
+  void SendInitRequest(uint64_t unique) {
+    memset(&request_, 0, sizeof(FuseRequest));
+    request_.header.opcode = FUSE_INIT;
+    request_.header.unique = unique;
+    request_.header.len = sizeof(fuse_in_header) + sizeof(fuse_init_in);
+    request_.init_in.major = FUSE_KERNEL_VERSION;
+    request_.init_in.minor = FUSE_KERNEL_MINOR_VERSION;
+    ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+  }
+
+  void Close() {
+    close(dev_sockets_[0]);
+    close(dev_sockets_[1]);
+    close(proxy_sockets_[0]);
+    close(proxy_sockets_[1]);
+    if (thread_.joinable()) {
+      thread_.join();
+    }
+  }
+
+  void TearDown() {
+    Close();
+  }
+};
+
+TEST_F(FuseBridgeLoopTest, FuseInit) {
+  SendInitRequest(1u);
+
+  memset(&response_, 0, sizeof(FuseResponse));
+  ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+  EXPECT_EQ(kFuseSuccess, response_.header.error);
+  EXPECT_EQ(1u, response_.header.unique);
+
+  // Unmount.
+  Close();
+  EXPECT_TRUE(callback_.mounted);
+}
+
+TEST_F(FuseBridgeLoopTest, FuseForget) {
+  memset(&request_, 0, sizeof(FuseRequest));
+  request_.header.opcode = FUSE_FORGET;
+  request_.header.unique = 1u;
+  request_.header.len = sizeof(fuse_in_header) + sizeof(fuse_forget_in);
+  ASSERT_TRUE(request_.Write(dev_sockets_[0]));
+
+  SendInitRequest(2u);
+
+  memset(&response_, 0, sizeof(FuseResponse));
+  ASSERT_TRUE(response_.Read(dev_sockets_[0]));
+  EXPECT_EQ(2u, response_.header.unique) <<
+      "The loop must not respond to FUSE_FORGET";
+}
+
+TEST_F(FuseBridgeLoopTest, FuseNotImpl) {
+  CheckNotImpl(FUSE_SETATTR);
+  CheckNotImpl(FUSE_READLINK);
+  CheckNotImpl(FUSE_SYMLINK);
+  CheckNotImpl(FUSE_MKNOD);
+  CheckNotImpl(FUSE_MKDIR);
+  CheckNotImpl(FUSE_UNLINK);
+  CheckNotImpl(FUSE_RMDIR);
+  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_OPENDIR);
+  CheckNotImpl(FUSE_READDIR);
+  CheckNotImpl(FUSE_RELEASEDIR);
+  CheckNotImpl(FUSE_FSYNCDIR);
+  CheckNotImpl(FUSE_GETLK);
+  CheckNotImpl(FUSE_SETLK);
+  CheckNotImpl(FUSE_SETLKW);
+  CheckNotImpl(FUSE_ACCESS);
+  CheckNotImpl(FUSE_CREATE);
+  CheckNotImpl(FUSE_INTERRUPT);
+  CheckNotImpl(FUSE_BMAP);
+  CheckNotImpl(FUSE_DESTROY);
+  CheckNotImpl(FUSE_IOCTL);
+  CheckNotImpl(FUSE_POLL);
+  CheckNotImpl(FUSE_NOTIFY_REPLY);
+  CheckNotImpl(FUSE_BATCH_FORGET);
+  CheckNotImpl(FUSE_FALLOCATE);
+  CheckNotImpl(FUSE_READDIRPLUS);
+  CheckNotImpl(FUSE_RENAME2);
+  CheckNotImpl(FUSE_LSEEK);
+}
+
+TEST_F(FuseBridgeLoopTest, Proxy) {
+  CheckProxy(FUSE_LOOKUP);
+  CheckProxy(FUSE_GETATTR);
+  CheckProxy(FUSE_OPEN);
+  CheckProxy(FUSE_READ);
+  CheckProxy(FUSE_WRITE);
+  CheckProxy(FUSE_RELEASE);
+  CheckProxy(FUSE_FLUSH);
+}
+
+}  // android