Merge "Add FuseAppLoop to libappfuse."

This commit is contained in:
Treehugger Robot 2016-11-15 02:35:42 +00:00 committed by Gerrit Code Review
commit e1d24f8465
10 changed files with 678 additions and 49 deletions

View file

@ -15,12 +15,20 @@ cc_library_shared {
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",
]
}

221
libappfuse/FuseAppLoop.cc Normal file
View file

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

View file

@ -25,14 +25,15 @@ 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);
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 @@ bool FuseBridgeLoop::Start(
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 @@ bool FuseBridgeLoop::Start(
}
}
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

View file

@ -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 @@ bool FuseMessage<T, Header>::CheckResult(
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 @@ bool FuseMessage<T, Header>::Write(int fd) const {
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 @@ void FuseBuffer::HandleNotImpl() {
response.Reset(0, -ENOSYS, unique);
}
} // namespace fuse
} // namespace android

View file

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

View file

@ -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 @@ class FuseBridgeLoop {
};
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_

View file

@ -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 @@ struct FuseMessage {
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 @@ 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 fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_

View file

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

View file

@ -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 Callback : public FuseBridgeLoop::Callback {
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 @@ class FuseBridgeLoopTest : public ::testing::Test {
}
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 @@ TEST_F(FuseBridgeLoopTest, FuseNotImpl) {
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 @@ TEST_F(FuseBridgeLoopTest, Proxy) {
CheckProxy(FUSE_READ);
CheckProxy(FUSE_WRITE);
CheckProxy(FUSE_RELEASE);
CheckProxy(FUSE_FLUSH);
CheckProxy(FUSE_FSYNC);
}
} // android
} // namespace fuse
} // namespace android

View file

@ -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 @@ TEST(FuseBufferTest, HandleNotImpl) {
ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);
EXPECT_EQ(-ENOSYS, buffer.response.header.error);
}
}
// namespace android
} // namespace fuse
} // namespace android