upload android base code part6

This commit is contained in:
August 2018-08-08 17:48:24 +08:00
parent 421e214c7d
commit 4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions

View file

@ -0,0 +1,36 @@
// Copyright 2016 The Android Open Source Project
cc_defaults {
name: "libappfuse_defaults",
local_include_dirs: ["include"],
shared_libs: ["libbase"],
cflags: [
"-Wall",
"-Werror",
],
clang: true
}
cc_library_shared {
name: "libappfuse",
defaults: ["libappfuse_defaults"],
export_include_dirs: ["include"],
srcs: [
"FuseAppLoop.cc",
"FuseBuffer.cc",
"FuseBridgeLoop.cc",
"EpollController.cc",
]
}
cc_test {
name: "libappfuse_test",
test_suites: ["device-tests"],
defaults: ["libappfuse_defaults"],
shared_libs: ["libappfuse"],
srcs: [
"tests/FuseAppLoopTest.cc",
"tests/FuseBridgeLoopTest.cc",
"tests/FuseBufferTest.cc",
]
}

View file

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2017 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 specific language governing permissions and
limitations under the License.
-->
<configuration description="Config for libappfuse_test">
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="libappfuse_test->/data/local/tmp/libappfuse_test" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="libappfuse_test" />
</test>
</configuration>

View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2017 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 <android-base/logging.h>
#include "libappfuse/EpollController.h"
namespace android {
namespace fuse {
EpollController::EpollController(base::unique_fd&& poll_fd) : poll_fd_(std::move(poll_fd)) {
}
bool EpollController::Wait(size_t event_count) {
events_.resize(event_count);
const int result = TEMP_FAILURE_RETRY(epoll_wait(poll_fd_, events_.data(), event_count, -1));
if (result == -1) {
PLOG(ERROR) << "Failed to wait for epoll";
return false;
}
events_.resize(result);
return true;
}
bool EpollController::AddFd(int fd, int events, void* data) {
return InvokeControl(EPOLL_CTL_ADD, fd, events, data);
}
bool EpollController::UpdateFd(int fd, int events, void* data) {
return InvokeControl(EPOLL_CTL_MOD, fd, events, data);
}
bool EpollController::RemoveFd(int fd) {
return InvokeControl(EPOLL_CTL_DEL, fd, /* events */ 0, nullptr);
}
const std::vector<epoll_event>& EpollController::events() const {
return events_;
}
bool EpollController::InvokeControl(int op, int fd, int events, void* data) const {
epoll_event event;
memset(&event, 0, sizeof(event));
event.events = events;
event.data.ptr = data;
if (epoll_ctl(poll_fd_, op, fd, &event) == -1) {
PLOG(ERROR) << "epoll_ctl() error op=" << op;
return false;
}
return true;
}
}
}

View file

@ -0,0 +1,248 @@
/*
* 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/eventfd.h>
#include <sys/stat.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include "libappfuse/EpollController.h"
namespace android {
namespace fuse {
namespace {
bool HandleLookUp(FuseAppLoop* loop, 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.";
return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
}
// 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.";
return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
}
const uint64_t inode = static_cast<uint64_t>(atol(buffer->request.lookup_name));
if (inode == 0 || inode == LONG_MAX) {
LOG(ERROR) << "Invalid filename";
return loop->ReplySimple(buffer->request.header.unique, -ENOENT);
}
callback->OnLookup(buffer->request.header.unique, inode);
return true;
}
bool HandleGetAttr(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
if (buffer->request.header.nodeid == FUSE_ROOT_ID) {
return loop->ReplyGetAttr(buffer->request.header.unique, buffer->request.header.nodeid, 0,
S_IFDIR | 0777);
} else {
callback->OnGetAttr(buffer->request.header.unique, buffer->request.header.nodeid);
return true;
}
}
bool HandleRead(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
if (buffer->request.read_in.size > kFuseMaxRead) {
return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
}
callback->OnRead(buffer->request.header.unique, buffer->request.header.nodeid,
buffer->request.read_in.offset, buffer->request.read_in.size);
return true;
}
bool HandleWrite(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {
if (buffer->request.write_in.size > kFuseMaxWrite) {
return loop->ReplySimple(buffer->request.header.unique, -EINVAL);
}
callback->OnWrite(buffer->request.header.unique, buffer->request.header.nodeid,
buffer->request.write_in.offset, buffer->request.write_in.size,
buffer->request.write_data);
return true;
}
bool HandleMessage(FuseAppLoop* loop, FuseBuffer* buffer, int fd, FuseAppLoopCallback* callback) {
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.
return true;
case FUSE_LOOKUP:
return HandleLookUp(loop, buffer, callback);
case FUSE_GETATTR:
return HandleGetAttr(loop, buffer, callback);
case FUSE_OPEN:
callback->OnOpen(buffer->request.header.unique, buffer->request.header.nodeid);
return true;
case FUSE_READ:
return HandleRead(loop, buffer, callback);
case FUSE_WRITE:
return HandleWrite(loop, buffer, callback);
case FUSE_RELEASE:
callback->OnRelease(buffer->request.header.unique, buffer->request.header.nodeid);
return true;
case FUSE_FSYNC:
callback->OnFsync(buffer->request.header.unique, buffer->request.header.nodeid);
return true;
default:
buffer->HandleNotImpl();
return buffer->response.Write(fd);
}
}
} // namespace
FuseAppLoopCallback::~FuseAppLoopCallback() = default;
FuseAppLoop::FuseAppLoop(base::unique_fd&& fd) : fd_(std::move(fd)) {}
void FuseAppLoop::Break() {
const int64_t value = 1;
if (write(break_fd_, &value, sizeof(value)) == -1) {
PLOG(ERROR) << "Failed to send a break event";
}
}
bool FuseAppLoop::ReplySimple(uint64_t unique, int32_t result) {
if (result == -ENOSYS) {
// We should not return -ENOSYS because the kernel stops delivering FUSE
// command after receiving -ENOSYS as a result for the command.
result = -EBADF;
}
FuseSimpleResponse response;
response.Reset(0, result, unique);
return response.Write(fd_);
}
bool FuseAppLoop::ReplyLookup(uint64_t unique, uint64_t inode, int64_t size) {
FuseSimpleResponse response;
response.Reset(sizeof(fuse_entry_out), 0, unique);
response.entry_out.nodeid = inode;
response.entry_out.attr_valid = 10;
response.entry_out.entry_valid = 10;
response.entry_out.attr.ino = inode;
response.entry_out.attr.mode = S_IFREG | 0777;
response.entry_out.attr.size = size;
return response.Write(fd_);
}
bool FuseAppLoop::ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode) {
CHECK(mode == (S_IFREG | 0777) || mode == (S_IFDIR | 0777));
FuseSimpleResponse response;
response.Reset(sizeof(fuse_attr_out), 0, unique);
response.attr_out.attr_valid = 10;
response.attr_out.attr.ino = inode;
response.attr_out.attr.mode = mode;
response.attr_out.attr.size = size;
return response.Write(fd_);
}
bool FuseAppLoop::ReplyOpen(uint64_t unique, uint64_t fh) {
FuseSimpleResponse response;
response.Reset(sizeof(fuse_open_out), kFuseSuccess, unique);
response.open_out.fh = fh;
return response.Write(fd_);
}
bool FuseAppLoop::ReplyWrite(uint64_t unique, uint32_t size) {
CHECK(size <= kFuseMaxWrite);
FuseSimpleResponse response;
response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
response.write_out.size = size;
return response.Write(fd_);
}
bool FuseAppLoop::ReplyRead(uint64_t unique, uint32_t size, const void* data) {
CHECK(size <= kFuseMaxRead);
FuseSimpleResponse response;
response.ResetHeader(size, kFuseSuccess, unique);
return response.WriteWithBody(fd_, sizeof(FuseResponse), data);
}
void FuseAppLoop::Start(FuseAppLoopCallback* callback) {
break_fd_.reset(eventfd(/* initval */ 0, EFD_CLOEXEC));
if (break_fd_.get() == -1) {
PLOG(ERROR) << "Failed to open FD for break event";
return;
}
base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
if (epoll_fd.get() == -1) {
PLOG(ERROR) << "Failed to open FD for epoll";
return;
}
int last_event;
int break_event;
std::unique_ptr<EpollController> epoll_controller(new EpollController(std::move(epoll_fd)));
if (!epoll_controller->AddFd(fd_, EPOLLIN, &last_event)) {
return;
}
if (!epoll_controller->AddFd(break_fd_, EPOLLIN, &break_event)) {
return;
}
last_event = 0;
break_event = 0;
FuseBuffer buffer;
while (true) {
if (!epoll_controller->Wait(1)) {
break;
}
last_event = 0;
*reinterpret_cast<int*>(epoll_controller->events()[0].data.ptr) =
epoll_controller->events()[0].events;
if (break_event != 0 || (last_event & ~EPOLLIN) != 0) {
break;
}
if (!HandleMessage(this, &buffer, fd_, callback)) {
break;
}
}
LOG(VERBOSE) << "FuseAppLoop exit";
}
} // namespace fuse
} // namespace android

View file

@ -0,0 +1,388 @@
/*
* 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/epoll.h>
#include <sys/socket.h>
#include <unordered_map>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include "libappfuse/EpollController.h"
namespace android {
namespace fuse {
namespace {
enum class FuseBridgeState { kWaitToReadEither, kWaitToReadProxy, kWaitToWriteProxy, kClosing };
struct FuseBridgeEntryEvent {
FuseBridgeEntry* entry;
int events;
};
void GetObservedEvents(FuseBridgeState state, int* device_events, int* proxy_events) {
switch (state) {
case FuseBridgeState::kWaitToReadEither:
*device_events = EPOLLIN;
*proxy_events = EPOLLIN;
return;
case FuseBridgeState::kWaitToReadProxy:
*device_events = 0;
*proxy_events = EPOLLIN;
return;
case FuseBridgeState::kWaitToWriteProxy:
*device_events = 0;
*proxy_events = EPOLLOUT;
return;
case FuseBridgeState::kClosing:
*device_events = 0;
*proxy_events = 0;
return;
}
}
void LogResponseError(const std::string& message, const FuseResponse& response) {
LOG(ERROR) << message << ": header.len=" << response.header.len
<< " header.error=" << response.header.error
<< " header.unique=" << response.header.unique;
}
}
class FuseBridgeEntry {
public:
FuseBridgeEntry(int mount_id, base::unique_fd&& dev_fd, base::unique_fd&& proxy_fd)
: mount_id_(mount_id),
device_fd_(std::move(dev_fd)),
proxy_fd_(std::move(proxy_fd)),
state_(FuseBridgeState::kWaitToReadEither),
last_state_(FuseBridgeState::kWaitToReadEither),
last_device_events_({this, 0}),
last_proxy_events_({this, 0}),
open_count_(0) {}
// Transfer bytes depends on availability of FDs and the internal |state_|.
void Transfer(FuseBridgeLoopCallback* callback) {
constexpr int kUnexpectedEventMask = ~(EPOLLIN | EPOLLOUT);
const bool unexpected_event = (last_device_events_.events & kUnexpectedEventMask) ||
(last_proxy_events_.events & kUnexpectedEventMask);
const bool device_read_ready = last_device_events_.events & EPOLLIN;
const bool proxy_read_ready = last_proxy_events_.events & EPOLLIN;
const bool proxy_write_ready = last_proxy_events_.events & EPOLLOUT;
last_device_events_.events = 0;
last_proxy_events_.events = 0;
LOG(VERBOSE) << "Transfer device_read_ready=" << device_read_ready
<< " proxy_read_ready=" << proxy_read_ready
<< " proxy_write_ready=" << proxy_write_ready;
if (unexpected_event) {
LOG(ERROR) << "Invalid epoll event is observed";
state_ = FuseBridgeState::kClosing;
return;
}
switch (state_) {
case FuseBridgeState::kWaitToReadEither:
if (proxy_read_ready) {
state_ = ReadFromProxy();
} else if (device_read_ready) {
state_ = ReadFromDevice(callback);
}
return;
case FuseBridgeState::kWaitToReadProxy:
CHECK(proxy_read_ready);
state_ = ReadFromProxy();
return;
case FuseBridgeState::kWaitToWriteProxy:
CHECK(proxy_write_ready);
state_ = WriteToProxy();
return;
case FuseBridgeState::kClosing:
return;
}
}
bool IsClosing() const { return state_ == FuseBridgeState::kClosing; }
int mount_id() const { return mount_id_; }
private:
friend class BridgeEpollController;
FuseBridgeState ReadFromProxy() {
switch (buffer_.response.ReadOrAgain(proxy_fd_)) {
case ResultOrAgain::kSuccess:
break;
case ResultOrAgain::kFailure:
return FuseBridgeState::kClosing;
case ResultOrAgain::kAgain:
return FuseBridgeState::kWaitToReadProxy;
}
if (!buffer_.response.Write(device_fd_)) {
LogResponseError("Failed to write a reply from proxy to device", buffer_.response);
return FuseBridgeState::kClosing;
}
auto it = opcode_map_.find(buffer_.response.header.unique);
if (it != opcode_map_.end()) {
switch (it->second) {
case FUSE_OPEN:
if (buffer_.response.header.error == fuse::kFuseSuccess) {
open_count_++;
}
break;
case FUSE_RELEASE:
if (open_count_ > 0) {
open_count_--;
} else {
LOG(WARNING) << "Unexpected FUSE_RELEASE before opening a file.";
break;
}
if (open_count_ == 0) {
return FuseBridgeState::kClosing;
}
break;
}
opcode_map_.erase(it);
}
return FuseBridgeState::kWaitToReadEither;
}
FuseBridgeState ReadFromDevice(FuseBridgeLoopCallback* callback) {
LOG(VERBOSE) << "ReadFromDevice";
if (!buffer_.request.Read(device_fd_)) {
return FuseBridgeState::kClosing;
}
const uint32_t opcode = buffer_.request.header.opcode;
const uint64_t unique = buffer_.request.header.unique;
LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode << " unique=" << unique;
if (unique == 0) {
return FuseBridgeState::kWaitToReadEither;
}
switch (opcode) {
case FUSE_FORGET:
// Do not reply to FUSE_FORGET.
return FuseBridgeState::kWaitToReadEither;
case FUSE_LOOKUP:
case FUSE_GETATTR:
case FUSE_OPEN:
case FUSE_READ:
case FUSE_WRITE:
case FUSE_RELEASE:
case FUSE_FSYNC:
if (opcode == FUSE_OPEN || opcode == FUSE_RELEASE) {
opcode_map_.emplace(buffer_.request.header.unique, opcode);
}
return WriteToProxy();
case FUSE_INIT:
buffer_.HandleInit();
break;
default:
buffer_.HandleNotImpl();
break;
}
if (!buffer_.response.Write(device_fd_)) {
LogResponseError("Failed to write a response to device", buffer_.response);
return FuseBridgeState::kClosing;
}
if (opcode == FUSE_INIT) {
callback->OnMount(mount_id_);
}
return FuseBridgeState::kWaitToReadEither;
}
FuseBridgeState WriteToProxy() {
switch (buffer_.request.WriteOrAgain(proxy_fd_)) {
case ResultOrAgain::kSuccess:
return FuseBridgeState::kWaitToReadEither;
case ResultOrAgain::kFailure:
LOG(ERROR) << "Failed to write a request to proxy:"
<< " header.len=" << buffer_.request.header.len
<< " header.opcode=" << buffer_.request.header.opcode
<< " header.unique=" << buffer_.request.header.unique
<< " header.nodeid=" << buffer_.request.header.nodeid;
return FuseBridgeState::kClosing;
case ResultOrAgain::kAgain:
return FuseBridgeState::kWaitToWriteProxy;
}
}
const int mount_id_;
base::unique_fd device_fd_;
base::unique_fd proxy_fd_;
FuseBuffer buffer_;
FuseBridgeState state_;
FuseBridgeState last_state_;
FuseBridgeEntryEvent last_device_events_;
FuseBridgeEntryEvent last_proxy_events_;
// Remember map between unique and opcode in fuse_in_header so that we can
// refer the opcode later.
std::unordered_map<uint64_t, uint32_t> opcode_map_;
int open_count_;
DISALLOW_COPY_AND_ASSIGN(FuseBridgeEntry);
};
class BridgeEpollController : private EpollController {
public:
BridgeEpollController(base::unique_fd&& poll_fd) : EpollController(std::move(poll_fd)) {}
bool AddBridgePoll(FuseBridgeEntry* bridge) const {
return InvokeControl(EPOLL_CTL_ADD, bridge);
}
bool UpdateOrDeleteBridgePoll(FuseBridgeEntry* bridge) const {
return InvokeControl(
bridge->state_ != FuseBridgeState::kClosing ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, bridge);
}
bool Wait(size_t bridge_count, std::unordered_set<FuseBridgeEntry*>* entries_out) {
CHECK(entries_out);
const size_t event_count = std::max<size_t>(bridge_count * 2, 1);
if (!EpollController::Wait(event_count)) {
return false;
}
entries_out->clear();
for (const auto& event : events()) {
FuseBridgeEntryEvent* const entry_event =
reinterpret_cast<FuseBridgeEntryEvent*>(event.data.ptr);
entry_event->events = event.events;
entries_out->insert(entry_event->entry);
}
return true;
}
private:
bool InvokeControl(int op, FuseBridgeEntry* bridge) const {
LOG(VERBOSE) << "InvokeControl op=" << op << " bridge=" << bridge->mount_id_
<< " state=" << static_cast<int>(bridge->state_)
<< " last_state=" << static_cast<int>(bridge->last_state_);
int last_device_events;
int last_proxy_events;
int device_events;
int proxy_events;
GetObservedEvents(bridge->last_state_, &last_device_events, &last_proxy_events);
GetObservedEvents(bridge->state_, &device_events, &proxy_events);
bool result = true;
if (op != EPOLL_CTL_MOD || last_device_events != device_events) {
result &= EpollController::InvokeControl(op, bridge->device_fd_, device_events,
&bridge->last_device_events_);
}
if (op != EPOLL_CTL_MOD || last_proxy_events != proxy_events) {
result &= EpollController::InvokeControl(op, bridge->proxy_fd_, proxy_events,
&bridge->last_proxy_events_);
}
return result;
}
};
FuseBridgeLoop::FuseBridgeLoop() : opened_(true) {
base::unique_fd epoll_fd(epoll_create1(/* no flag */ 0));
if (epoll_fd.get() == -1) {
PLOG(ERROR) << "Failed to open FD for epoll";
opened_ = false;
return;
}
epoll_controller_.reset(new BridgeEpollController(std::move(epoll_fd)));
}
FuseBridgeLoop::~FuseBridgeLoop() { CHECK(bridges_.empty()); }
bool FuseBridgeLoop::AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd) {
LOG(VERBOSE) << "Adding bridge " << mount_id;
std::unique_ptr<FuseBridgeEntry> bridge(
new FuseBridgeEntry(mount_id, std::move(dev_fd), std::move(proxy_fd)));
std::lock_guard<std::mutex> lock(mutex_);
if (!opened_) {
LOG(ERROR) << "Tried to add a mount to a closed bridge";
return false;
}
if (bridges_.count(mount_id)) {
LOG(ERROR) << "Tried to add a mount point that has already been added";
return false;
}
if (!epoll_controller_->AddBridgePoll(bridge.get())) {
return false;
}
bridges_.emplace(mount_id, std::move(bridge));
return true;
}
bool FuseBridgeLoop::ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,
FuseBridgeLoopCallback* callback) {
for (auto entry : entries) {
entry->Transfer(callback);
if (!epoll_controller_->UpdateOrDeleteBridgePoll(entry)) {
return false;
}
if (entry->IsClosing()) {
const int mount_id = entry->mount_id();
callback->OnClosed(mount_id);
bridges_.erase(mount_id);
if (bridges_.size() == 0) {
// All bridges are now closed.
return false;
}
}
}
return true;
}
void FuseBridgeLoop::Start(FuseBridgeLoopCallback* callback) {
LOG(DEBUG) << "Start fuse bridge loop";
std::unordered_set<FuseBridgeEntry*> entries;
while (true) {
const bool wait_result = epoll_controller_->Wait(bridges_.size(), &entries);
LOG(VERBOSE) << "Receive epoll events";
{
std::lock_guard<std::mutex> lock(mutex_);
if (!(wait_result && ProcessEventLocked(entries, callback))) {
for (auto it = bridges_.begin(); it != bridges_.end();) {
callback->OnClosed(it->second->mount_id());
it = bridges_.erase(it);
}
opened_ = false;
return;
}
}
}
}
} // namespace fuse
} // namespace android

View file

@ -0,0 +1,267 @@
/*
* 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/FuseBuffer.h"
#include <inttypes.h>
#include <string.h>
#include <unistd.h>
#include <algorithm>
#include <type_traits>
#include <sys/socket.h>
#include <sys/uio.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
namespace android {
namespace fuse {
namespace {
constexpr useconds_t kRetrySleepForWriting = 1000; // 1 ms
template <typename T>
bool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {
const auto& header = static_cast<const T*>(self)->header;
if (header.len >= sizeof(header) && header.len <= max_size) {
return true;
} else {
LOG(ERROR) << "Invalid header length is found in " << name << ": " << header.len;
return false;
}
}
template <typename T>
ResultOrAgain ReadInternal(FuseMessage<T>* self, int fd, int sockflag) {
char* const buf = reinterpret_cast<char*>(self);
const ssize_t result = sockflag ? TEMP_FAILURE_RETRY(recv(fd, buf, sizeof(T), sockflag))
: TEMP_FAILURE_RETRY(read(fd, buf, sizeof(T)));
switch (result) {
case 0:
// Expected EOF.
return ResultOrAgain::kFailure;
case -1:
if (errno == EAGAIN) {
return ResultOrAgain::kAgain;
}
PLOG(ERROR) << "Failed to read a FUSE message";
return ResultOrAgain::kFailure;
}
const auto& header = static_cast<const T*>(self)->header;
if (result < static_cast<ssize_t>(sizeof(header))) {
LOG(ERROR) << "Read bytes " << result << " are shorter than header size " << sizeof(header);
return ResultOrAgain::kFailure;
}
if (!CheckHeaderLength<T>(self, "Read", sizeof(T))) {
return ResultOrAgain::kFailure;
}
if (static_cast<uint32_t>(result) != header.len) {
LOG(ERROR) << "Read bytes " << result << " are different from header.len " << header.len;
return ResultOrAgain::kFailure;
}
return ResultOrAgain::kSuccess;
}
template <typename T>
ResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag, const void* data,
size_t max_size) {
if (!CheckHeaderLength<T>(self, "Write", max_size)) {
return ResultOrAgain::kFailure;
}
const char* const buf = reinterpret_cast<const char*>(self);
const auto& header = static_cast<const T*>(self)->header;
while (true) {
int result;
if (sockflag) {
CHECK(data == nullptr);
result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));
} else if (data) {
const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},
{const_cast<void*>(data), header.len - sizeof(header)}};
result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));
} else {
result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));
}
if (result == -1) {
switch (errno) {
case ENOBUFS:
// When returning ENOBUFS, epoll still reports the FD is writable. Just usleep
// and retry again.
usleep(kRetrySleepForWriting);
continue;
case EAGAIN:
return ResultOrAgain::kAgain;
default:
PLOG(ERROR) << "Failed to write a FUSE message: "
<< "fd=" << fd << " "
<< "sockflag=" << sockflag << " "
<< "data=" << data;
return ResultOrAgain::kFailure;
}
}
if (static_cast<unsigned int>(result) != header.len) {
LOG(ERROR) << "Written bytes " << result << " is different from length in header "
<< header.len;
return ResultOrAgain::kFailure;
}
return ResultOrAgain::kSuccess;
}
}
}
static_assert(std::is_standard_layout<FuseBuffer>::value,
"FuseBuffer must be standard layout union.");
bool SetupMessageSockets(base::unique_fd (*result)[2]) {
base::unique_fd fds[2];
{
int raw_fds[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_fds) == -1) {
PLOG(ERROR) << "Failed to create sockets for proxy";
return false;
}
fds[0].reset(raw_fds[0]);
fds[1].reset(raw_fds[1]);
}
constexpr int kMaxMessageSize = sizeof(FuseBuffer);
if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0 ||
setsockopt(fds[1], SOL_SOCKET, SO_SNDBUFFORCE, &kMaxMessageSize, sizeof(int)) != 0) {
PLOG(ERROR) << "Failed to update buffer size for socket";
return false;
}
(*result)[0] = std::move(fds[0]);
(*result)[1] = std::move(fds[1]);
return true;
}
template <typename T>
bool FuseMessage<T>::Read(int fd) {
return ReadInternal(this, fd, 0) == ResultOrAgain::kSuccess;
}
template <typename T>
ResultOrAgain FuseMessage<T>::ReadOrAgain(int fd) {
return ReadInternal(this, fd, MSG_DONTWAIT);
}
template <typename T>
bool FuseMessage<T>::Write(int fd) const {
return WriteInternal(this, fd, 0, nullptr, sizeof(T)) == ResultOrAgain::kSuccess;
}
template <typename T>
bool FuseMessage<T>::WriteWithBody(int fd, size_t max_size, const void* data) const {
CHECK(data != nullptr);
return WriteInternal(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;
}
template <typename T>
ResultOrAgain FuseMessage<T>::WriteOrAgain(int fd) const {
return WriteInternal(this, fd, MSG_DONTWAIT, nullptr, sizeof(T));
}
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;
}
template <size_t N>
void FuseResponseBase<N>::ResetHeader(uint32_t data_length, int32_t error, uint64_t unique) {
CHECK_LE(error, 0) << "error should be zero or negative.";
header.len = sizeof(fuse_out_header) + data_length;
header.error = error;
header.unique = unique;
}
template <size_t N>
void FuseResponseBase<N>::Reset(uint32_t data_length, int32_t error, uint64_t unique) {
memset(this, 0, sizeof(fuse_out_header) + data_length);
ResetHeader(data_length, error, unique);
}
void FuseBuffer::HandleInit() {
const fuse_init_in* const in = &request.init_in;
// Before writing |out|, we need to copy data from |in|.
const uint64_t unique = request.header.unique;
const uint32_t minor = in->minor;
const uint32_t max_readahead = in->max_readahead;
// Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
// defined (fuse version 7.6). The structure is the same from 7.6 through
// 7.22. Beginning with 7.23, the structure increased in size and added
// new parameters.
if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) {
LOG(ERROR) << "Fuse kernel version mismatch: Kernel version " << in->major
<< "." << in->minor << " Expected at least " << FUSE_KERNEL_VERSION
<< ".6";
response.Reset(0, -EPERM, unique);
return;
}
// We limit ourselves to minor=15 because we don't handle BATCH_FORGET yet.
// Thus we need to use FUSE_COMPAT_22_INIT_OUT_SIZE.
#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
// FUSE_KERNEL_VERSION >= 23.
const size_t response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
#else
const size_t response_size = sizeof(fuse_init_out);
#endif
response.Reset(response_size, kFuseSuccess, unique);
fuse_init_out* const out = &response.init_out;
out->major = FUSE_KERNEL_VERSION;
out->minor = std::min(minor, 15u);
out->max_readahead = max_readahead;
out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
out->max_background = 32;
out->congestion_threshold = 32;
out->max_write = kFuseMaxWrite;
}
void FuseBuffer::HandleNotImpl() {
LOG(VERBOSE) << "NOTIMPL op=" << request.header.opcode << " uniq="
<< request.header.unique << " nid=" << request.header.nodeid;
// Add volatile as a workaround for compiler issue which removes the temporary
// variable.
const volatile uint64_t unique = request.header.unique;
response.Reset(0, -ENOSYS, unique);
}
template class FuseMessage<FuseRequest>;
template class FuseMessage<FuseResponse>;
template class FuseMessage<FuseSimpleResponse>;
template struct FuseResponseBase<0u>;
template struct FuseResponseBase<kFuseMaxRead>;
} // namespace fuse
} // namespace android

View file

@ -0,0 +1,52 @@
/*
* Copyright (C) 2017 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_EPOLLCONTROLLER_H_
#define ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_
#include <sys/epoll.h>
#include <vector>
#include <android-base/macros.h>
#include <android-base/unique_fd.h>
namespace android {
namespace fuse {
class EpollController {
public:
explicit EpollController(base::unique_fd&& poll_fd);
bool Wait(size_t event_count);
bool AddFd(int fd, int events, void* data);
bool UpdateFd(int fd, int events, void* data);
bool RemoveFd(int fd);
const std::vector<epoll_event>& events() const;
protected:
bool InvokeControl(int op, int fd, int events, void* data) const;
private:
base::unique_fd poll_fd_;
std::vector<epoll_event> events_;
DISALLOW_COPY_AND_ASSIGN(EpollController);
};
}
}
#endif // ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_

View file

@ -0,0 +1,72 @@
/*
* 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 <memory>
#include <mutex>
#include <android-base/unique_fd.h>
#include "libappfuse/FuseBuffer.h"
namespace android {
namespace fuse {
class EpollController;
class FuseAppLoopCallback {
public:
virtual void OnLookup(uint64_t unique, uint64_t inode) = 0;
virtual void OnGetAttr(uint64_t unique, uint64_t inode) = 0;
virtual void OnFsync(uint64_t unique, uint64_t inode) = 0;
virtual void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,
const void* data) = 0;
virtual void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) = 0;
virtual void OnOpen(uint64_t unique, uint64_t inode) = 0;
virtual void OnRelease(uint64_t unique, uint64_t inode) = 0;
virtual ~FuseAppLoopCallback();
};
class FuseAppLoop final {
public:
FuseAppLoop(base::unique_fd&& fd);
void Start(FuseAppLoopCallback* callback);
void Break();
bool ReplySimple(uint64_t unique, int32_t result);
bool ReplyLookup(uint64_t unique, uint64_t inode, int64_t size);
bool ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode);
bool ReplyOpen(uint64_t unique, uint64_t fh);
bool ReplyWrite(uint64_t unique, uint32_t size);
bool ReplyRead(uint64_t unique, uint32_t size, const void* data);
private:
base::unique_fd fd_;
base::unique_fd break_fd_;
// Lock for multi-threading.
std::mutex mutex_;
};
bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
} // namespace fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_

View file

@ -0,0 +1,73 @@
/*
* 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 <map>
#include <mutex>
#include <queue>
#include <unordered_set>
#include <android-base/macros.h>
#include "libappfuse/FuseBuffer.h"
namespace android {
namespace fuse {
class FuseBridgeLoopCallback {
public:
virtual void OnMount(int mount_id) = 0;
virtual void OnClosed(int mount_id) = 0;
virtual ~FuseBridgeLoopCallback() = default;
};
class FuseBridgeEntry;
class BridgeEpollController;
class FuseBridgeLoop final {
public:
FuseBridgeLoop();
~FuseBridgeLoop();
void Start(FuseBridgeLoopCallback* callback);
// Add bridge to the loop. It's OK to invoke the method from a different
// thread from one which invokes |Start|.
bool AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd);
private:
bool ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,
FuseBridgeLoopCallback* callback);
std::unique_ptr<BridgeEpollController> epoll_controller_;
// Map between |mount_id| and bridge entry.
std::map<int, std::unique_ptr<FuseBridgeEntry>> bridges_;
// Lock for multi-threading.
std::mutex mutex_;
bool opened_;
DISALLOW_COPY_AND_ASSIGN(FuseBridgeLoop);
};
} // namespace fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_

View file

@ -0,0 +1,112 @@
/*
* 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_FUSEBUFFER_H_
#define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
#include <android-base/unique_fd.h>
#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.
constexpr size_t kFuseMaxWrite = 256 * 1024;
constexpr size_t kFuseMaxRead = 128 * 1024;
constexpr int32_t kFuseSuccess = 0;
// Setup sockets to transfer FuseMessage.
bool SetupMessageSockets(base::unique_fd (*sockets)[2]);
enum class ResultOrAgain {
kSuccess,
kFailure,
kAgain,
};
template<typename T>
class FuseMessage {
public:
bool Read(int fd);
bool Write(int fd) const;
bool WriteWithBody(int fd, size_t max_size, const void* data) const;
ResultOrAgain ReadOrAgain(int fd);
ResultOrAgain WriteOrAgain(int fd) const;
};
// FuseRequest represents file operation requests from /dev/fuse. It starts
// from fuse_in_header. The body layout depends on the operation code.
struct FuseRequest : public FuseMessage<FuseRequest> {
fuse_in_header 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[kFuseMaxWrite];
};
void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
};
// FuseResponse represents file operation responses to /dev/fuse. It starts
// from fuse_out_header. The body layout depends on the operation code.
template <size_t N>
struct FuseResponseBase : public FuseMessage<FuseResponseBase<N>> {
fuse_out_header 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[N];
// 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);
};
using FuseResponse = FuseResponseBase<kFuseMaxRead>;
using FuseSimpleResponse = FuseResponseBase<0u>;
// To reduce memory usage, FuseBuffer shares the memory region for request and
// response.
union FuseBuffer final {
FuseRequest request;
FuseResponse response;
void HandleInit();
void HandleNotImpl();
};
} // namespace fuse
} // namespace android
#endif // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_

View file

@ -0,0 +1,299 @@
/*
* 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>
#include "libappfuse/EpollController.h"
#include "libappfuse/FuseBridgeLoop.h"
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;
FuseAppLoop* loop;
void OnGetAttr(uint64_t seq, uint64_t inode) override {
EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
EXPECT_TRUE(loop->ReplyGetAttr(seq, inode, kTestFileSize, S_IFREG | 0777));
}
void OnLookup(uint64_t unique, uint64_t inode) override {
EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));
EXPECT_TRUE(loop->ReplyLookup(unique, inode, kTestFileSize));
}
void OnFsync(uint64_t seq, uint64_t inode) override {
requests.push_back({.code = FUSE_FSYNC, .inode = inode});
loop->ReplySimple(seq, 0);
}
void OnWrite(uint64_t seq, 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});
loop->ReplyWrite(seq, 0);
}
void OnRead(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,
uint32_t size ATTRIBUTE_UNUSED) override {
requests.push_back({.code = FUSE_READ, .inode = inode});
loop->ReplySimple(seq, 0);
}
void OnOpen(uint64_t seq, uint64_t inode) override {
requests.push_back({.code = FUSE_OPEN, .inode = inode});
loop->ReplyOpen(seq, inode);
}
void OnRelease(uint64_t seq, uint64_t inode) override {
requests.push_back({.code = FUSE_RELEASE, .inode = inode});
loop->ReplySimple(seq, 0);
}
};
class FuseAppLoopTest : public ::testing::Test {
protected:
std::thread thread_;
base::unique_fd sockets_[2];
Callback callback_;
FuseRequest request_;
FuseResponse response_;
std::unique_ptr<FuseAppLoop> loop_;
void SetUp() override {
base::SetMinimumLogSeverity(base::VERBOSE);
ASSERT_TRUE(SetupMessageSockets(&sockets_));
loop_.reset(new FuseAppLoop(std::move(sockets_[1])));
callback_.loop = loop_.get();
thread_ = std::thread([this] { loop_->Start(&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));
}
TEST_F(FuseAppLoopTest, Break) {
// Ensure that the loop started.
request_.Reset(sizeof(fuse_open_in), FUSE_OPEN, 1);
request_.header.nodeid = 10;
ASSERT_TRUE(request_.Write(sockets_[0]));
ASSERT_TRUE(response_.Read(sockets_[0]));
loop_->Break();
if (thread_.joinable()) {
thread_.join();
}
}
} // namespace fuse
} // namespace android

View file

@ -0,0 +1,214 @@
/*
* 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 <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
namespace android {
namespace fuse {
namespace {
class Callback : public FuseBridgeLoopCallback {
public:
bool mounted;
bool closed;
Callback() : mounted(false), closed(false) {}
void OnMount(int /*mount_id*/) override { mounted = true; }
void OnClosed(int /* mount_id */) override { closed = true; }
};
class FuseBridgeLoopTest : public ::testing::Test {
protected:
base::unique_fd dev_sockets_[2];
base::unique_fd proxy_sockets_[2];
Callback callback_;
std::thread thread_;
FuseRequest request_;
FuseResponse response_;
void SetUp() override {
base::SetMinimumLogSeverity(base::VERBOSE);
ASSERT_TRUE(SetupMessageSockets(&dev_sockets_));
ASSERT_TRUE(SetupMessageSockets(&proxy_sockets_));
thread_ = std::thread([this] {
FuseBridgeLoop loop;
loop.AddBridge(1, std::move(dev_sockets_[1]), std::move(proxy_sockets_[0]));
loop.Start(&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);
request_.header.unique = 1;
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() {
dev_sockets_[0].reset();
dev_sockets_[1].reset();
proxy_sockets_[0].reset();
proxy_sockets_[1].reset();
if (thread_.joinable()) {
thread_.join();
}
ASSERT_TRUE(callback_.closed);
}
void TearDown() override {
Close();
}
};
} // namespace
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_SETXATTR);
CheckNotImpl(FUSE_GETXATTR);
CheckNotImpl(FUSE_LISTXATTR);
CheckNotImpl(FUSE_REMOVEXATTR);
CheckNotImpl(FUSE_FLUSH);
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_READ);
CheckProxy(FUSE_WRITE);
CheckProxy(FUSE_FSYNC);
// Invoke FUSE_OPEN and FUSE_RELEASE at last as the loop will exit when all files are closed.
CheckProxy(FUSE_OPEN);
CheckProxy(FUSE_RELEASE);
// Ensure the loop exits.
Close();
}
} // namespace fuse
} // namespace android

View file

@ -0,0 +1,215 @@
/*
* 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/FuseBuffer.h"
#include <fcntl.h>
#include <string.h>
#include <sys/socket.h>
#include <thread>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
namespace android {
namespace fuse {
constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
void OpenTempFile(android::base::unique_fd* fd) {
fd->reset(open(kTempFile, O_CREAT | O_RDWR, 0600));
ASSERT_NE(-1, *fd) << strerror(errno);
unlink(kTempFile);
ASSERT_NE(-1, *fd) << strerror(errno);
}
void TestReadInvalidLength(size_t headerSize, size_t write_size) {
android::base::unique_fd fd;
OpenTempFile(&fd);
char buffer[std::max(headerSize, sizeof(FuseRequest))];
FuseRequest* const packet = reinterpret_cast<FuseRequest*>(buffer);
packet->header.len = headerSize;
ASSERT_NE(-1, write(fd, packet, write_size)) << strerror(errno);
lseek(fd, 0, SEEK_SET);
EXPECT_FALSE(packet->Read(fd));
}
void TestWriteInvalidLength(size_t size) {
android::base::unique_fd fd;
OpenTempFile(&fd);
char buffer[std::max(size, sizeof(FuseRequest))];
FuseRequest* const packet = reinterpret_cast<FuseRequest*>(buffer);
packet->header.len = size;
EXPECT_FALSE(packet->Write(fd));
}
// Use FuseRequest as a template instance of FuseMessage.
TEST(FuseMessageTest, ReadAndWrite) {
android::base::unique_fd fd;
OpenTempFile(&fd);
FuseRequest request;
request.header.len = sizeof(FuseRequest);
request.header.opcode = 1;
request.header.unique = 2;
request.header.nodeid = 3;
request.header.uid = 4;
request.header.gid = 5;
request.header.pid = 6;
strcpy(request.lookup_name, "test");
ASSERT_TRUE(request.Write(fd));
memset(&request, 0, sizeof(FuseRequest));
lseek(fd, 0, SEEK_SET);
ASSERT_TRUE(request.Read(fd));
EXPECT_EQ(sizeof(FuseRequest), request.header.len);
EXPECT_EQ(1u, request.header.opcode);
EXPECT_EQ(2u, request.header.unique);
EXPECT_EQ(3u, request.header.nodeid);
EXPECT_EQ(4u, request.header.uid);
EXPECT_EQ(5u, request.header.gid);
EXPECT_EQ(6u, request.header.pid);
EXPECT_STREQ("test", request.lookup_name);
}
TEST(FuseMessageTest, Read_InconsistentLength) {
TestReadInvalidLength(sizeof(fuse_in_header), sizeof(fuse_in_header) + 1);
}
TEST(FuseMessageTest, Read_TooLong) {
TestReadInvalidLength(sizeof(FuseRequest) + 1, sizeof(FuseRequest) + 1);
}
TEST(FuseMessageTest, Read_TooShort) {
TestReadInvalidLength(sizeof(fuse_in_header) - 1, sizeof(fuse_in_header) - 1);
}
TEST(FuseMessageTest, Write_TooLong) {
TestWriteInvalidLength(sizeof(FuseRequest) + 1);
}
TEST(FuseMessageTest, Write_TooShort) {
TestWriteInvalidLength(sizeof(fuse_in_header) - 1);
}
TEST(FuseResponseTest, Reset) {
FuseResponse response;
// Write 1 to the first ten bytes.
memset(response.read_data, 'a', 10);
response.Reset(0, -1, 2);
EXPECT_EQ(sizeof(fuse_out_header), response.header.len);
EXPECT_EQ(-1, response.header.error);
EXPECT_EQ(2u, response.header.unique);
EXPECT_EQ('a', response.read_data[0]);
EXPECT_EQ('a', response.read_data[9]);
response.Reset(5, -4, 3);
EXPECT_EQ(sizeof(fuse_out_header) + 5, response.header.len);
EXPECT_EQ(-4, response.header.error);
EXPECT_EQ(3u, response.header.unique);
EXPECT_EQ(0, response.read_data[0]);
EXPECT_EQ(0, response.read_data[1]);
EXPECT_EQ(0, response.read_data[2]);
EXPECT_EQ(0, response.read_data[3]);
EXPECT_EQ(0, response.read_data[4]);
EXPECT_EQ('a', response.read_data[5]);
}
TEST(FuseResponseTest, ResetHeader) {
FuseResponse response;
// Write 1 to the first ten bytes.
memset(response.read_data, 'a', 10);
response.ResetHeader(0, -1, 2);
EXPECT_EQ(sizeof(fuse_out_header), response.header.len);
EXPECT_EQ(-1, response.header.error);
EXPECT_EQ(2u, response.header.unique);
EXPECT_EQ('a', response.read_data[0]);
EXPECT_EQ('a', response.read_data[9]);
response.ResetHeader(5, -4, 3);
EXPECT_EQ(sizeof(fuse_out_header) + 5, response.header.len);
EXPECT_EQ(-4, response.header.error);
EXPECT_EQ(3u, response.header.unique);
EXPECT_EQ('a', response.read_data[0]);
EXPECT_EQ('a', response.read_data[9]);
}
TEST(FuseBufferTest, HandleInit) {
FuseBuffer buffer;
memset(&buffer, 0, sizeof(FuseBuffer));
buffer.request.header.opcode = FUSE_INIT;
buffer.request.init_in.major = FUSE_KERNEL_VERSION;
buffer.request.init_in.minor = FUSE_KERNEL_MINOR_VERSION;
buffer.HandleInit();
ASSERT_EQ(sizeof(fuse_out_header) + FUSE_COMPAT_22_INIT_OUT_SIZE,
buffer.response.header.len);
EXPECT_EQ(kFuseSuccess, buffer.response.header.error);
EXPECT_EQ(static_cast<unsigned int>(FUSE_KERNEL_VERSION),
buffer.response.init_out.major);
EXPECT_EQ(15u, buffer.response.init_out.minor);
EXPECT_EQ(static_cast<unsigned int>(FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES),
buffer.response.init_out.flags);
EXPECT_EQ(kFuseMaxWrite, buffer.response.init_out.max_write);
}
TEST(FuseBufferTest, HandleNotImpl) {
FuseBuffer buffer;
memset(&buffer, 0, sizeof(FuseBuffer));
buffer.HandleNotImpl();
ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);
EXPECT_EQ(-ENOSYS, buffer.response.header.error);
}
TEST(SetupMessageSocketsTest, Stress) {
constexpr int kCount = 1000;
FuseRequest request;
request.header.len = sizeof(FuseRequest);
base::unique_fd fds[2];
SetupMessageSockets(&fds);
std::thread thread([&fds] {
FuseRequest request;
for (int i = 0; i < kCount; ++i) {
ASSERT_TRUE(request.Read(fds[1]));
usleep(1000);
}
});
for (int i = 0; i < kCount; ++i) {
ASSERT_TRUE(request.Write(fds[0]));
}
thread.join();
}
} // namespace fuse
} // namespace android