upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
36
android/system/core/libappfuse/Android.bp
Normal file
36
android/system/core/libappfuse/Android.bp
Normal 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",
|
||||
]
|
||||
}
|
26
android/system/core/libappfuse/AndroidTest.xml
Normal file
26
android/system/core/libappfuse/AndroidTest.xml
Normal 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>
|
66
android/system/core/libappfuse/EpollController.cc
Normal file
66
android/system/core/libappfuse/EpollController.cc
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
248
android/system/core/libappfuse/FuseAppLoop.cc
Normal file
248
android/system/core/libappfuse/FuseAppLoop.cc
Normal 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
|
388
android/system/core/libappfuse/FuseBridgeLoop.cc
Normal file
388
android/system/core/libappfuse/FuseBridgeLoop.cc
Normal 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
|
267
android/system/core/libappfuse/FuseBuffer.cc
Normal file
267
android/system/core/libappfuse/FuseBuffer.cc
Normal 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
|
|
@ -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_
|
|
@ -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_
|
|
@ -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_
|
112
android/system/core/libappfuse/include/libappfuse/FuseBuffer.h
Normal file
112
android/system/core/libappfuse/include/libappfuse/FuseBuffer.h
Normal 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_
|
299
android/system/core/libappfuse/tests/FuseAppLoopTest.cc
Normal file
299
android/system/core/libappfuse/tests/FuseAppLoopTest.cc
Normal 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
|
214
android/system/core/libappfuse/tests/FuseBridgeLoopTest.cc
Normal file
214
android/system/core/libappfuse/tests/FuseBridgeLoopTest.cc
Normal 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
|
215
android/system/core/libappfuse/tests/FuseBufferTest.cc
Normal file
215
android/system/core/libappfuse/tests/FuseBufferTest.cc
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue