mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 16:57:49 +03:00
daaeb5012a
Summary: Easier to zero initialize structs with braces, which defines that even padding is zeroed. Reviewed By: wez Differential Revision: D19655675 fbshipit-source-id: 2fd12383324029646707e93008cf9ad34e9f1dce
168 lines
4.6 KiB
C++
168 lines
4.6 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This software may be used and distributed according to the terms of the
|
|
* GNU General Public License version 2.
|
|
*/
|
|
|
|
#include "eden/fs/testharness/FakeFuse.h"
|
|
|
|
#include <folly/Exception.h>
|
|
#include <folly/FileUtil.h>
|
|
#include <folly/chrono/Conv.h>
|
|
#include <folly/logging/xlog.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
|
|
using namespace std::chrono_literals;
|
|
using folly::ByteRange;
|
|
using folly::StringPiece;
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
|
|
FakeFuse::FakeFuse() {}
|
|
|
|
folly::File FakeFuse::start() {
|
|
std::array<int, 2> sockets;
|
|
folly::checkUnixError(
|
|
socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets.data()),
|
|
"socketpair() failed");
|
|
conn_ = folly::File(sockets[0], /* ownsFd */ true);
|
|
auto userConn = folly::File(sockets[1], /* ownsFd */ true);
|
|
|
|
// Set a timeout so the tests will fail quickly if we don't have
|
|
// data ready when we expect to.
|
|
setTimeout(1s);
|
|
|
|
return userConn;
|
|
}
|
|
|
|
void FakeFuse::close() {
|
|
conn_.close();
|
|
}
|
|
|
|
bool FakeFuse::isStarted() const {
|
|
return conn_.fd() != -1;
|
|
}
|
|
|
|
void FakeFuse::setTimeout(std::chrono::milliseconds timeout) {
|
|
auto tv = folly::to<struct timeval>(timeout);
|
|
// recvResponse() and sendRequest() both perform blocking I/O.
|
|
// We simply set to the socket timeout to force the blocking recv/send calls
|
|
// to time out if they do not complete within the specified timeout.
|
|
folly::checkUnixError(
|
|
setsockopt(conn_.fd(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)));
|
|
folly::checkUnixError(
|
|
setsockopt(conn_.fd(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)));
|
|
}
|
|
|
|
uint32_t FakeFuse::sendRequest(uint32_t opcode, uint64_t inode, ByteRange arg) {
|
|
auto requestID = requestID_;
|
|
++requestID_;
|
|
XLOG(DBG5) << "injecting FUSE request ID " << requestID
|
|
<< ": opcode= " << opcode;
|
|
|
|
fuse_in_header header = {};
|
|
header.len = sizeof(struct fuse_in_header) + arg.size();
|
|
header.opcode = opcode;
|
|
header.unique = requestID;
|
|
header.nodeid = inode;
|
|
|
|
std::array<iovec, 2> iov;
|
|
iov[0].iov_base = &header;
|
|
iov[0].iov_len = sizeof(struct fuse_in_header);
|
|
iov[1].iov_base = const_cast<uint8_t*>(arg.data());
|
|
iov[1].iov_len = arg.size();
|
|
|
|
folly::checkUnixError(
|
|
folly::writevFull(conn_.fd(), iov.data(), iov.size()),
|
|
"failed to send FUSE request ");
|
|
return requestID;
|
|
}
|
|
|
|
FakeFuse::Response FakeFuse::recvResponse() {
|
|
Response response;
|
|
|
|
std::array<iovec, 2> iov;
|
|
iov[0].iov_base = &response.header;
|
|
iov[0].iov_len = sizeof(response.header);
|
|
|
|
msghdr message{};
|
|
message.msg_iov = iov.data();
|
|
message.msg_iovlen = 1;
|
|
|
|
ssize_t bytesRead = recvmsg(conn_.fd(), &message, MSG_PEEK);
|
|
folly::checkUnixError(bytesRead, "recvmsg failed on FUSE socket");
|
|
|
|
if (static_cast<size_t>(bytesRead) < sizeof(fuse_out_header)) {
|
|
throw std::runtime_error{folly::to<std::string>(
|
|
"received FUSE response with incomplete header: ",
|
|
bytesRead,
|
|
" is shorter than the response header.")};
|
|
}
|
|
|
|
const auto packetLength = response.header.len;
|
|
if (packetLength < sizeof(fuse_out_header)) {
|
|
throw std::runtime_error{folly::to<std::string>(
|
|
"received FUSE response with invalid length: ",
|
|
packetLength,
|
|
" is shorter than the response header.")};
|
|
}
|
|
|
|
const auto dataLength = packetLength - sizeof(fuse_out_header);
|
|
response.body.resize(dataLength);
|
|
|
|
iov[1].iov_base = response.body.data();
|
|
iov[1].iov_len = dataLength;
|
|
message.msg_iovlen = 2;
|
|
|
|
bytesRead = recvmsg(conn_.fd(), &message, 0);
|
|
folly::checkUnixError(bytesRead, "recvmsg failed on FUSE socket");
|
|
|
|
if (bytesRead != packetLength) {
|
|
throw std::runtime_error{folly::to<std::string>(
|
|
"received FUSE response with incorrect message size: ",
|
|
bytesRead,
|
|
" expected ",
|
|
packetLength)};
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
std::vector<FakeFuse::Response> FakeFuse::getAllResponses() {
|
|
std::vector<FakeFuse::Response> responses;
|
|
try {
|
|
while (true) {
|
|
responses.emplace_back(recvResponse());
|
|
}
|
|
} catch (const std::system_error& e) {
|
|
if (e.code().value() != EAGAIN) {
|
|
throw;
|
|
}
|
|
}
|
|
return responses;
|
|
}
|
|
|
|
uint32_t FakeFuse::sendInitRequest(
|
|
uint32_t majorVersion,
|
|
uint32_t minorVersion,
|
|
uint32_t maxReadahead,
|
|
uint32_t flags) {
|
|
struct fuse_init_in initArg;
|
|
initArg.major = majorVersion;
|
|
initArg.minor = minorVersion;
|
|
initArg.max_readahead = maxReadahead;
|
|
initArg.flags = flags;
|
|
|
|
return sendRequest(FUSE_INIT, FUSE_ROOT_ID, initArg);
|
|
}
|
|
|
|
uint32_t FakeFuse::sendLookup(uint64_t inode, StringPiece pathComponent) {
|
|
return sendRequest(FUSE_LOOKUP, inode, folly::ByteRange(pathComponent));
|
|
}
|
|
|
|
} // namespace eden
|
|
} // namespace facebook
|