Merge "Add FuseAppLoop to libappfuse."
am: e1d24f8465
Change-Id: Ic15e0ca892a1432fa82cfd80737cbf95a976fab4
This commit is contained in:
commit
1eda78472a
|
@ -15,12 +15,20 @@ cc_library_shared {
|
|||
name: "libappfuse",
|
||||
defaults: ["libappfuse_defaults"],
|
||||
export_include_dirs: ["include"],
|
||||
srcs: ["FuseBuffer.cc", "FuseBridgeLoop.cc"]
|
||||
srcs: [
|
||||
"FuseAppLoop.cc",
|
||||
"FuseBuffer.cc",
|
||||
"FuseBridgeLoop.cc",
|
||||
]
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "libappfuse_test",
|
||||
defaults: ["libappfuse_defaults"],
|
||||
shared_libs: ["libappfuse"],
|
||||
srcs: ["tests/FuseBridgeLoopTest.cc", "tests/FuseBufferTest.cc"]
|
||||
srcs: [
|
||||
"tests/FuseAppLoopTest.cc",
|
||||
"tests/FuseBridgeLoopTest.cc",
|
||||
"tests/FuseBufferTest.cc",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specic language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "libappfuse/FuseAppLoop.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
namespace android {
|
||||
namespace fuse {
|
||||
|
||||
namespace {
|
||||
|
||||
void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
|
||||
// AppFuse does not support directory structure now.
|
||||
// It can lookup only files under the mount point.
|
||||
if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
|
||||
LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
|
||||
buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that the filename ends with 0.
|
||||
const size_t filename_length =
|
||||
buffer->request.header.len - sizeof(fuse_in_header);
|
||||
if (buffer->request.lookup_name[filename_length - 1] != 0) {
|
||||
LOG(ERROR) << "File name does not end with 0.";
|
||||
buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t inode =
|
||||
static_cast<uint64_t>(atol(buffer->request.lookup_name));
|
||||
if (inode == 0 || inode == LONG_MAX) {
|
||||
LOG(ERROR) << "Invalid filename";
|
||||
buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
const int64_t size = callback->OnGetSize(inode);
|
||||
if (size < 0) {
|
||||
buffer->response.Reset(0, size, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->response.Reset(sizeof(fuse_entry_out), 0,
|
||||
buffer->request.header.unique);
|
||||
buffer->response.entry_out.nodeid = inode;
|
||||
buffer->response.entry_out.attr_valid = 10;
|
||||
buffer->response.entry_out.entry_valid = 10;
|
||||
buffer->response.entry_out.attr.ino = inode;
|
||||
buffer->response.entry_out.attr.mode = S_IFREG | 0777;
|
||||
buffer->response.entry_out.attr.size = size;
|
||||
}
|
||||
|
||||
void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
|
||||
const uint64_t nodeid = buffer->request.header.nodeid;
|
||||
int64_t size;
|
||||
uint32_t mode;
|
||||
if (nodeid == FUSE_ROOT_ID) {
|
||||
size = 0;
|
||||
mode = S_IFDIR | 0777;
|
||||
} else {
|
||||
size = callback->OnGetSize(buffer->request.header.nodeid);
|
||||
if (size < 0) {
|
||||
buffer->response.Reset(0, size, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
mode = S_IFREG | 0777;
|
||||
}
|
||||
|
||||
buffer->response.Reset(sizeof(fuse_attr_out), 0,
|
||||
buffer->request.header.unique);
|
||||
buffer->response.attr_out.attr_valid = 10;
|
||||
buffer->response.attr_out.attr.ino = nodeid;
|
||||
buffer->response.attr_out.attr.mode = mode;
|
||||
buffer->response.attr_out.attr.size = size;
|
||||
}
|
||||
|
||||
void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
|
||||
const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
|
||||
if (file_handle < 0) {
|
||||
buffer->response.Reset(0, file_handle, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
|
||||
buffer->request.header.unique);
|
||||
buffer->response.open_out.fh = file_handle;
|
||||
}
|
||||
|
||||
void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
|
||||
buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
|
||||
buffer->request.header.unique);
|
||||
}
|
||||
|
||||
void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
|
||||
buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
|
||||
buffer->request.header.unique);
|
||||
}
|
||||
|
||||
void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
|
||||
const uint64_t unique = buffer->request.header.unique;
|
||||
const uint64_t nodeid = buffer->request.header.nodeid;
|
||||
const uint64_t offset = buffer->request.read_in.offset;
|
||||
const uint32_t size = buffer->request.read_in.size;
|
||||
|
||||
if (size > kFuseMaxRead) {
|
||||
buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t read_size = callback->OnRead(nodeid, offset, size,
|
||||
buffer->response.read_data);
|
||||
if (read_size < 0) {
|
||||
buffer->response.Reset(0, read_size, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
|
||||
}
|
||||
|
||||
void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
|
||||
const uint64_t unique = buffer->request.header.unique;
|
||||
const uint64_t nodeid = buffer->request.header.nodeid;
|
||||
const uint64_t offset = buffer->request.write_in.offset;
|
||||
const uint32_t size = buffer->request.write_in.size;
|
||||
|
||||
if (size > kFuseMaxWrite) {
|
||||
buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t write_size = callback->OnWrite(nodeid, offset, size,
|
||||
buffer->request.write_data);
|
||||
if (write_size < 0) {
|
||||
buffer->response.Reset(0, write_size, buffer->request.header.unique);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
|
||||
buffer->response.write_out.size = write_size;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
|
||||
base::unique_fd fd(raw_fd);
|
||||
FuseBuffer buffer;
|
||||
|
||||
LOG(DEBUG) << "Start fuse loop.";
|
||||
while (callback->IsActive()) {
|
||||
if (!buffer.request.Read(fd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t opcode = buffer.request.header.opcode;
|
||||
LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
|
||||
switch (opcode) {
|
||||
case FUSE_FORGET:
|
||||
// Do not reply to FUSE_FORGET.
|
||||
continue;
|
||||
|
||||
case FUSE_LOOKUP:
|
||||
HandleLookUp(&buffer, callback);
|
||||
break;
|
||||
|
||||
case FUSE_GETATTR:
|
||||
HandleGetAttr(&buffer, callback);
|
||||
break;
|
||||
|
||||
case FUSE_OPEN:
|
||||
HandleOpen(&buffer, callback);
|
||||
break;
|
||||
|
||||
case FUSE_READ:
|
||||
HandleRead(&buffer, callback);
|
||||
break;
|
||||
|
||||
case FUSE_WRITE:
|
||||
HandleWrite(&buffer, callback);
|
||||
break;
|
||||
|
||||
case FUSE_RELEASE:
|
||||
HandleRelease(&buffer, callback);
|
||||
break;
|
||||
|
||||
case FUSE_FSYNC:
|
||||
HandleFsync(&buffer, callback);
|
||||
break;
|
||||
|
||||
default:
|
||||
buffer.HandleNotImpl();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!buffer.response.Write(fd)) {
|
||||
LOG(ERROR) << "Failed to write a response to the device.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
|
@ -25,14 +25,15 @@ bool FuseBridgeLoop::Start(
|
|||
int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoop::Callback* callback) {
|
||||
base::unique_fd dev_fd(raw_dev_fd);
|
||||
base::unique_fd proxy_fd(raw_proxy_fd);
|
||||
fuse::FuseBuffer buffer;
|
||||
|
||||
LOG(DEBUG) << "Start fuse loop.";
|
||||
while (true) {
|
||||
if (!buffer_.request.Read(dev_fd)) {
|
||||
if (!buffer.request.Read(dev_fd)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32_t opcode = buffer_.request.header.opcode;
|
||||
const uint32_t opcode = buffer.request.header.opcode;
|
||||
LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
|
||||
switch (opcode) {
|
||||
case FUSE_FORGET:
|
||||
|
@ -45,27 +46,27 @@ bool FuseBridgeLoop::Start(
|
|||
case FUSE_READ:
|
||||
case FUSE_WRITE:
|
||||
case FUSE_RELEASE:
|
||||
case FUSE_FLUSH:
|
||||
if (!buffer_.request.Write(proxy_fd)) {
|
||||
case FUSE_FSYNC:
|
||||
if (!buffer.request.Write(proxy_fd)) {
|
||||
LOG(ERROR) << "Failed to write a request to the proxy.";
|
||||
return false;
|
||||
}
|
||||
if (!buffer_.response.Read(proxy_fd)) {
|
||||
if (!buffer.response.Read(proxy_fd)) {
|
||||
LOG(ERROR) << "Failed to read a response from the proxy.";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case FUSE_INIT:
|
||||
buffer_.HandleInit();
|
||||
buffer.HandleInit();
|
||||
break;
|
||||
|
||||
default:
|
||||
buffer_.HandleNotImpl();
|
||||
buffer.HandleNotImpl();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!buffer_.response.Write(dev_fd)) {
|
||||
if (!buffer.response.Write(dev_fd)) {
|
||||
LOG(ERROR) << "Failed to write a response to the device.";
|
||||
return false;
|
||||
}
|
||||
|
@ -76,4 +77,12 @@ bool FuseBridgeLoop::Start(
|
|||
}
|
||||
}
|
||||
|
||||
namespace fuse {
|
||||
|
||||
bool StartFuseBridgeLoop(
|
||||
int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoopCallback* callback) {
|
||||
return FuseBridgeLoop().Start(raw_dev_fd, raw_proxy_fd, callback);
|
||||
}
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include <android-base/macros.h>
|
||||
|
||||
namespace android {
|
||||
namespace fuse {
|
||||
|
||||
template <typename T, typename Header>
|
||||
bool FuseMessage<T, Header>::CheckHeaderLength() const {
|
||||
|
@ -44,7 +45,7 @@ bool FuseMessage<T, Header>::CheckResult(
|
|||
return true;
|
||||
} else {
|
||||
PLOG(ERROR) << "Failed to " << operation_name
|
||||
<< " a packet from FD. result=" << result << " header.len="
|
||||
<< " a packet. result=" << result << " header.len="
|
||||
<< header.len;
|
||||
return false;
|
||||
}
|
||||
|
@ -68,6 +69,14 @@ bool FuseMessage<T, Header>::Write(int fd) const {
|
|||
template struct FuseMessage<FuseRequest, fuse_in_header>;
|
||||
template struct FuseMessage<FuseResponse, fuse_out_header>;
|
||||
|
||||
void FuseRequest::Reset(
|
||||
uint32_t data_length, uint32_t opcode, uint64_t unique) {
|
||||
memset(this, 0, sizeof(fuse_in_header) + data_length);
|
||||
header.len = sizeof(fuse_in_header) + data_length;
|
||||
header.opcode = opcode;
|
||||
header.unique = unique;
|
||||
}
|
||||
|
||||
void FuseResponse::ResetHeader(
|
||||
uint32_t data_length, int32_t error, uint64_t unique) {
|
||||
CHECK_LE(error, 0) << "error should be zero or negative.";
|
||||
|
@ -133,4 +142,5 @@ void FuseBuffer::HandleNotImpl() {
|
|||
response.Reset(0, -ENOSYS, unique);
|
||||
}
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specic language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
|
||||
#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
|
||||
|
||||
#include "libappfuse/FuseBuffer.h"
|
||||
|
||||
namespace android {
|
||||
namespace fuse {
|
||||
|
||||
class FuseAppLoopCallback {
|
||||
public:
|
||||
virtual bool IsActive() = 0;
|
||||
virtual int64_t OnGetSize(uint64_t inode) = 0;
|
||||
virtual int32_t OnFsync(uint64_t inode) = 0;
|
||||
virtual int32_t OnWrite(
|
||||
uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
|
||||
virtual int32_t OnRead(
|
||||
uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
|
||||
virtual int32_t OnOpen(uint64_t inode) = 0;
|
||||
virtual int32_t OnRelease(uint64_t inode) = 0;
|
||||
virtual ~FuseAppLoopCallback() = default;
|
||||
};
|
||||
|
||||
bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
namespace android {
|
||||
|
||||
class FuseBridgeLoop {
|
||||
// TODO: Remove the class after switching to StartFuseBridgeLoop in the
|
||||
// framework code.
|
||||
class FuseBridgeLoop final {
|
||||
public:
|
||||
class Callback {
|
||||
public:
|
||||
|
@ -30,11 +32,15 @@ class FuseBridgeLoop {
|
|||
};
|
||||
|
||||
bool Start(int dev_fd, int proxy_fd, Callback* callback);
|
||||
|
||||
private:
|
||||
FuseBuffer buffer_;
|
||||
};
|
||||
|
||||
namespace fuse {
|
||||
|
||||
class FuseBridgeLoopCallback : public FuseBridgeLoop::Callback {};
|
||||
bool StartFuseBridgeLoop(
|
||||
int dev_fd, int proxy_fd, FuseBridgeLoopCallback* callback);
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/fuse.h>
|
||||
|
||||
namespace android {
|
||||
namespace fuse {
|
||||
|
||||
// The numbers came from sdcard.c.
|
||||
// Maximum number of bytes to write/read in one request/one reply.
|
||||
|
@ -37,33 +38,51 @@ struct FuseMessage {
|
|||
bool CheckResult(int result, const char* operation_name) const;
|
||||
};
|
||||
|
||||
struct FuseRequest : public FuseMessage<FuseRequest, fuse_in_header> {
|
||||
// FuseRequest represents file operation requests from /dev/fuse. It starts
|
||||
// from fuse_in_header. The body layout depends on the operation code.
|
||||
struct FuseRequest final : public FuseMessage<FuseRequest, fuse_in_header> {
|
||||
union {
|
||||
// for FUSE_WRITE
|
||||
struct {
|
||||
fuse_write_in write_in;
|
||||
char write_data[kFuseMaxWrite];
|
||||
};
|
||||
// for FUSE_OPEN
|
||||
fuse_open_in open_in;
|
||||
// for FUSE_INIT
|
||||
fuse_init_in init_in;
|
||||
// for FUSE_READ
|
||||
fuse_read_in read_in;
|
||||
// for FUSE_LOOKUP
|
||||
char lookup_name[0];
|
||||
};
|
||||
void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
|
||||
};
|
||||
|
||||
struct FuseResponse : public FuseMessage<FuseResponse, fuse_out_header> {
|
||||
// FuseResponse represents file operation responses to /dev/fuse. It starts
|
||||
// from fuse_out_header. The body layout depends on the operation code.
|
||||
struct FuseResponse final : public FuseMessage<FuseResponse, fuse_out_header> {
|
||||
union {
|
||||
// for FUSE_INIT
|
||||
fuse_init_out init_out;
|
||||
// for FUSE_LOOKUP
|
||||
fuse_entry_out entry_out;
|
||||
// for FUSE_GETATTR
|
||||
fuse_attr_out attr_out;
|
||||
// for FUSE_OPEN
|
||||
fuse_open_out open_out;
|
||||
// for FUSE_READ
|
||||
char read_data[kFuseMaxRead];
|
||||
// for FUSE_WRITE
|
||||
fuse_write_out write_out;
|
||||
};
|
||||
void Reset(uint32_t data_length, int32_t error, uint64_t unique);
|
||||
void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
|
||||
};
|
||||
|
||||
union FuseBuffer {
|
||||
// To reduce memory usage, FuseBuffer shares the memory region for request and
|
||||
// response.
|
||||
union FuseBuffer final {
|
||||
FuseRequest request;
|
||||
FuseResponse response;
|
||||
|
||||
|
@ -71,19 +90,7 @@ union FuseBuffer {
|
|||
void HandleNotImpl();
|
||||
};
|
||||
|
||||
class FuseProxyLoop {
|
||||
class IFuseProxyLoopCallback {
|
||||
public:
|
||||
virtual void OnMount() = 0;
|
||||
virtual ~IFuseProxyLoopCallback() = default;
|
||||
};
|
||||
|
||||
bool Start(int dev_fd, int proxy_fd, IFuseProxyLoopCallback* callback);
|
||||
|
||||
private:
|
||||
FuseBuffer buffer_;
|
||||
};
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
|
||||
|
|
|
@ -0,0 +1,307 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specic language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "libappfuse/FuseAppLoop.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <thread>
|
||||
|
||||
namespace android {
|
||||
namespace fuse {
|
||||
namespace {
|
||||
|
||||
constexpr unsigned int kTestFileSize = 1024;
|
||||
|
||||
struct CallbackRequest {
|
||||
uint32_t code;
|
||||
uint64_t inode;
|
||||
};
|
||||
|
||||
class Callback : public FuseAppLoopCallback {
|
||||
public:
|
||||
std::vector<CallbackRequest> requests;
|
||||
|
||||
bool IsActive() override {
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t OnGetSize(uint64_t inode) override {
|
||||
if (inode == FUSE_ROOT_ID) {
|
||||
return 0;
|
||||
} else {
|
||||
return kTestFileSize;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t OnFsync(uint64_t inode) override {
|
||||
requests.push_back({
|
||||
.code = FUSE_FSYNC,
|
||||
.inode = inode
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t OnWrite(uint64_t inode,
|
||||
uint64_t offset ATTRIBUTE_UNUSED,
|
||||
uint32_t size ATTRIBUTE_UNUSED,
|
||||
const void* data ATTRIBUTE_UNUSED) override {
|
||||
requests.push_back({
|
||||
.code = FUSE_WRITE,
|
||||
.inode = inode
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t OnRead(uint64_t inode,
|
||||
uint64_t offset ATTRIBUTE_UNUSED,
|
||||
uint32_t size ATTRIBUTE_UNUSED,
|
||||
void* data ATTRIBUTE_UNUSED) override {
|
||||
requests.push_back({
|
||||
.code = FUSE_READ,
|
||||
.inode = inode
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t OnOpen(uint64_t inode) override {
|
||||
requests.push_back({
|
||||
.code = FUSE_OPEN,
|
||||
.inode = inode
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t OnRelease(uint64_t inode) override {
|
||||
requests.push_back({
|
||||
.code = FUSE_RELEASE,
|
||||
.inode = inode
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class FuseAppLoopTest : public ::testing::Test {
|
||||
private:
|
||||
std::thread thread_;
|
||||
|
||||
protected:
|
||||
base::unique_fd sockets_[2];
|
||||
Callback callback_;
|
||||
FuseRequest request_;
|
||||
FuseResponse response_;
|
||||
|
||||
void SetUp() override {
|
||||
base::SetMinimumLogSeverity(base::VERBOSE);
|
||||
int sockets[2];
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
|
||||
sockets_[0].reset(sockets[0]);
|
||||
sockets_[1].reset(sockets[1]);
|
||||
thread_ = std::thread([this] {
|
||||
StartFuseAppLoop(sockets_[1].release(), &callback_);
|
||||
});
|
||||
}
|
||||
|
||||
void CheckCallback(
|
||||
size_t data_size, uint32_t code, size_t expected_out_size) {
|
||||
request_.Reset(data_size, code, 1);
|
||||
request_.header.nodeid = 10;
|
||||
|
||||
ASSERT_TRUE(request_.Write(sockets_[0]));
|
||||
ASSERT_TRUE(response_.Read(sockets_[0]));
|
||||
|
||||
Close();
|
||||
|
||||
EXPECT_EQ(kFuseSuccess, response_.header.error);
|
||||
EXPECT_EQ(sizeof(fuse_out_header) + expected_out_size,
|
||||
response_.header.len);
|
||||
EXPECT_EQ(1u, response_.header.unique);
|
||||
|
||||
ASSERT_EQ(1u, callback_.requests.size());
|
||||
EXPECT_EQ(code, callback_.requests[0].code);
|
||||
EXPECT_EQ(10u, callback_.requests[0].inode);
|
||||
}
|
||||
|
||||
void Close() {
|
||||
sockets_[0].reset();
|
||||
sockets_[1].reset();
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
Close();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(FuseAppLoopTest, LookUp) {
|
||||
request_.Reset(3u, FUSE_LOOKUP, 1);
|
||||
request_.header.nodeid = FUSE_ROOT_ID;
|
||||
strcpy(request_.lookup_name, "10");
|
||||
|
||||
ASSERT_TRUE(request_.Write(sockets_[0].get()));
|
||||
ASSERT_TRUE(response_.Read(sockets_[0].get()));
|
||||
|
||||
EXPECT_EQ(kFuseSuccess, response_.header.error);
|
||||
EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_entry_out),
|
||||
response_.header.len);
|
||||
EXPECT_EQ(1u, response_.header.unique);
|
||||
|
||||
EXPECT_EQ(10u, response_.entry_out.nodeid);
|
||||
EXPECT_EQ(0u, response_.entry_out.generation);
|
||||
EXPECT_EQ(10u, response_.entry_out.entry_valid);
|
||||
EXPECT_EQ(10u, response_.entry_out.attr_valid);
|
||||
EXPECT_EQ(0u, response_.entry_out.entry_valid_nsec);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr_valid_nsec);
|
||||
|
||||
EXPECT_EQ(10u, response_.entry_out.attr.ino);
|
||||
EXPECT_EQ(kTestFileSize, response_.entry_out.attr.size);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.blocks);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.atime);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.mtime);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.ctime);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.atimensec);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.mtimensec);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.ctimensec);
|
||||
EXPECT_EQ(S_IFREG | 0777u, response_.entry_out.attr.mode);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.nlink);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.uid);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.gid);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.rdev);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.blksize);
|
||||
EXPECT_EQ(0u, response_.entry_out.attr.padding);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
|
||||
request_.Reset(3u, FUSE_LOOKUP, 1);
|
||||
request_.header.nodeid = FUSE_ROOT_ID;
|
||||
strcpy(request_.lookup_name, "aa");
|
||||
|
||||
ASSERT_TRUE(request_.Write(sockets_[0].get()));
|
||||
ASSERT_TRUE(response_.Read(sockets_[0].get()));
|
||||
|
||||
EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
|
||||
EXPECT_EQ(-ENOENT, response_.header.error);
|
||||
EXPECT_EQ(1u, response_.header.unique);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, LookUp_TooLargeName) {
|
||||
request_.Reset(21u, FUSE_LOOKUP, 1);
|
||||
request_.header.nodeid = FUSE_ROOT_ID;
|
||||
strcpy(request_.lookup_name, "18446744073709551616");
|
||||
|
||||
ASSERT_TRUE(request_.Write(sockets_[0].get()));
|
||||
ASSERT_TRUE(response_.Read(sockets_[0].get()));
|
||||
|
||||
EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
|
||||
EXPECT_EQ(-ENOENT, response_.header.error);
|
||||
EXPECT_EQ(1u, response_.header.unique);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, GetAttr) {
|
||||
request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
|
||||
request_.header.nodeid = 10;
|
||||
|
||||
ASSERT_TRUE(request_.Write(sockets_[0].get()));
|
||||
ASSERT_TRUE(response_.Read(sockets_[0].get()));
|
||||
|
||||
EXPECT_EQ(kFuseSuccess, response_.header.error);
|
||||
EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
|
||||
response_.header.len);
|
||||
EXPECT_EQ(1u, response_.header.unique);
|
||||
|
||||
EXPECT_EQ(10u, response_.attr_out.attr_valid);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
|
||||
|
||||
EXPECT_EQ(10u, response_.attr_out.attr.ino);
|
||||
EXPECT_EQ(kTestFileSize, response_.attr_out.attr.size);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.blocks);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.atime);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.mtime);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.ctime);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
|
||||
EXPECT_EQ(S_IFREG | 0777u, response_.attr_out.attr.mode);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.nlink);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.uid);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.gid);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.rdev);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.blksize);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.padding);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, GetAttr_Root) {
|
||||
request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
|
||||
request_.header.nodeid = FUSE_ROOT_ID;
|
||||
|
||||
ASSERT_TRUE(request_.Write(sockets_[0].get()));
|
||||
ASSERT_TRUE(response_.Read(sockets_[0].get()));
|
||||
|
||||
EXPECT_EQ(kFuseSuccess, response_.header.error);
|
||||
EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
|
||||
response_.header.len);
|
||||
EXPECT_EQ(1u, response_.header.unique);
|
||||
|
||||
EXPECT_EQ(10u, response_.attr_out.attr_valid);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
|
||||
|
||||
EXPECT_EQ(static_cast<unsigned>(FUSE_ROOT_ID), response_.attr_out.attr.ino);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.size);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.blocks);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.atime);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.mtime);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.ctime);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
|
||||
EXPECT_EQ(S_IFDIR | 0777u, response_.attr_out.attr.mode);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.nlink);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.uid);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.gid);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.rdev);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.blksize);
|
||||
EXPECT_EQ(0u, response_.attr_out.attr.padding);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, Open) {
|
||||
CheckCallback(sizeof(fuse_open_in), FUSE_OPEN, sizeof(fuse_open_out));
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, Fsync) {
|
||||
CheckCallback(0u, FUSE_FSYNC, 0u);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, Release) {
|
||||
CheckCallback(0u, FUSE_RELEASE, 0u);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, Read) {
|
||||
CheckCallback(sizeof(fuse_read_in), FUSE_READ, 0u);
|
||||
}
|
||||
|
||||
TEST_F(FuseAppLoopTest, Write) {
|
||||
CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
|
||||
}
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
|
@ -21,11 +21,15 @@
|
|||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace fuse {
|
||||
namespace {
|
||||
|
||||
class Callback : public FuseBridgeLoop::Callback {
|
||||
class Callback : public FuseBridgeLoopCallback {
|
||||
public:
|
||||
bool mounted;
|
||||
Callback() : mounted(false) {}
|
||||
|
@ -36,20 +40,28 @@ class Callback : public FuseBridgeLoop::Callback {
|
|||
|
||||
class FuseBridgeLoopTest : public ::testing::Test {
|
||||
protected:
|
||||
int dev_sockets_[2];
|
||||
int proxy_sockets_[2];
|
||||
base::unique_fd dev_sockets_[2];
|
||||
base::unique_fd proxy_sockets_[2];
|
||||
Callback callback_;
|
||||
std::thread thread_;
|
||||
|
||||
FuseRequest request_;
|
||||
FuseResponse response_;
|
||||
|
||||
void SetUp() {
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets_));
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets_));
|
||||
void SetUp() override {
|
||||
base::SetMinimumLogSeverity(base::VERBOSE);
|
||||
int dev_sockets[2];
|
||||
int proxy_sockets[2];
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets));
|
||||
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets));
|
||||
dev_sockets_[0].reset(dev_sockets[0]);
|
||||
dev_sockets_[1].reset(dev_sockets[1]);
|
||||
proxy_sockets_[0].reset(proxy_sockets[0]);
|
||||
proxy_sockets_[1].reset(proxy_sockets[1]);
|
||||
|
||||
thread_ = std::thread([this] {
|
||||
FuseBridgeLoop loop;
|
||||
loop.Start(dev_sockets_[1], proxy_sockets_[0], &callback_);
|
||||
StartFuseBridgeLoop(
|
||||
dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -103,20 +115,22 @@ class FuseBridgeLoopTest : public ::testing::Test {
|
|||
}
|
||||
|
||||
void Close() {
|
||||
close(dev_sockets_[0]);
|
||||
close(dev_sockets_[1]);
|
||||
close(proxy_sockets_[0]);
|
||||
close(proxy_sockets_[1]);
|
||||
dev_sockets_[0].reset();
|
||||
dev_sockets_[1].reset();
|
||||
proxy_sockets_[0].reset();
|
||||
proxy_sockets_[1].reset();
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown() {
|
||||
void TearDown() override {
|
||||
Close();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(FuseBridgeLoopTest, FuseInit) {
|
||||
SendInitRequest(1u);
|
||||
|
||||
|
@ -156,11 +170,11 @@ TEST_F(FuseBridgeLoopTest, FuseNotImpl) {
|
|||
CheckNotImpl(FUSE_RENAME);
|
||||
CheckNotImpl(FUSE_LINK);
|
||||
CheckNotImpl(FUSE_STATFS);
|
||||
CheckNotImpl(FUSE_FSYNC);
|
||||
CheckNotImpl(FUSE_SETXATTR);
|
||||
CheckNotImpl(FUSE_GETXATTR);
|
||||
CheckNotImpl(FUSE_LISTXATTR);
|
||||
CheckNotImpl(FUSE_REMOVEXATTR);
|
||||
CheckNotImpl(FUSE_FLUSH);
|
||||
CheckNotImpl(FUSE_OPENDIR);
|
||||
CheckNotImpl(FUSE_READDIR);
|
||||
CheckNotImpl(FUSE_RELEASEDIR);
|
||||
|
@ -190,7 +204,8 @@ TEST_F(FuseBridgeLoopTest, Proxy) {
|
|||
CheckProxy(FUSE_READ);
|
||||
CheckProxy(FUSE_WRITE);
|
||||
CheckProxy(FUSE_RELEASE);
|
||||
CheckProxy(FUSE_FLUSH);
|
||||
CheckProxy(FUSE_FSYNC);
|
||||
}
|
||||
|
||||
} // android
|
||||
} // namespace fuse
|
||||
} // namespace android
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
namespace android {
|
||||
namespace fuse {
|
||||
|
||||
constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
|
||||
|
||||
|
@ -183,5 +184,6 @@ TEST(FuseBufferTest, HandleNotImpl) {
|
|||
ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);
|
||||
EXPECT_EQ(-ENOSYS, buffer.response.header.error);
|
||||
}
|
||||
}
|
||||
// namespace android
|
||||
|
||||
} // namespace fuse
|
||||
} // namespace android
|
||||
|
|
Loading…
Reference in New Issue