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
This commit is contained in:
Daichi Hirono 2016-10-27 14:57:55 +09:00
parent 7f8e819ded
commit c613476297
7 changed files with 336 additions and 8 deletions

View file

@ -15,12 +15,12 @@ cc_library_shared {
name: "libappfuse",
defaults: ["libappfuse_defaults"],
export_include_dirs: ["include"],
srcs: ["AppFuse.cc"]
srcs: ["FuseBuffer.cc", "FuseBridgeLoop.cc"]
}
cc_test {
name: "libappfuse_test",
defaults: ["libappfuse_defaults"],
shared_libs: ["libappfuse"],
srcs: ["tests/AppFuseTest.cc"]
srcs: ["tests/FuseBridgeLoopTest.cc", "tests/FuseBufferTest.cc"]
}

View file

@ -0,0 +1,79 @@
/*
* 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 <android-base/logging.h>
#include <android-base/unique_fd.h>
namespace android {
bool FuseBridgeLoop::Start(
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);
LOG(DEBUG) << "Start fuse loop.";
while (true) {
if (!buffer_.request.Read(dev_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:
case FUSE_GETATTR:
case FUSE_OPEN:
case FUSE_READ:
case FUSE_WRITE:
case FUSE_RELEASE:
case FUSE_FLUSH:
if (!buffer_.request.Write(proxy_fd)) {
LOG(ERROR) << "Failed to write a request to the proxy.";
return false;
}
if (!buffer_.response.Read(proxy_fd)) {
LOG(ERROR) << "Failed to read a response from the proxy.";
return false;
}
break;
case FUSE_INIT:
buffer_.HandleInit();
break;
default:
buffer_.HandleNotImpl();
break;
}
if (!buffer_.response.Write(dev_fd)) {
LOG(ERROR) << "Failed to write a response to the device.";
return false;
}
if (opcode == FUSE_INIT) {
callback->OnMount();
}
}
}
} // namespace android

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "libappfuse/AppFuse.h"
#include "libappfuse/FuseBuffer.h"
#include <inttypes.h>
#include <string.h>

View file

@ -0,0 +1,40 @@
/*
* 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_FUSEBRIDGELOOP_H_
#define ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
#include "libappfuse/FuseBuffer.h"
namespace android {
class FuseBridgeLoop {
public:
class Callback {
public:
virtual void OnMount() = 0;
virtual ~Callback() = default;
};
bool Start(int dev_fd, int proxy_fd, Callback* callback);
private:
FuseBuffer buffer_;
};
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_

View file

@ -14,8 +14,8 @@
* limitations under the License.
*/
#ifndef ANDROID_LIBAPPFUSE_APPFUSE_H_
#define ANDROID_LIBAPPFUSE_APPFUSE_H_
#ifndef ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
#define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
#include <linux/fuse.h>
@ -46,7 +46,7 @@ struct FuseRequest : public FuseMessage<FuseRequest, fuse_in_header> {
fuse_open_in open_in;
fuse_init_in init_in;
fuse_read_in read_in;
char lookup_name[];
char lookup_name[0];
};
};
@ -71,6 +71,19 @@ union FuseBuffer {
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 android
#endif // ANDROID_LIBAPPFUSE_APPFUSE_H_
#endif // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_

View file

@ -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

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "libappfuse/AppFuse.h"
#include "libappfuse/FuseBuffer.h"
#include <fcntl.h>
#include <string.h>