/* * 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 #include #include #include #include #include using namespace std::chrono_literals; using folly::ByteRange; using folly::StringPiece; namespace facebook { namespace eden { FakeFuse::FakeFuse() {} folly::File FakeFuse::start() { std::array 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(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 iov; iov[0].iov_base = &header; iov[0].iov_len = sizeof(struct fuse_in_header); iov[1].iov_base = const_cast(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 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(bytesRead) < sizeof(fuse_out_header)) { throw std::runtime_error{folly::to( "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( "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( "received FUSE response with incorrect message size: ", bytesRead, " expected ", packetLength)}; } return response; } std::vector FakeFuse::getAllResponses() { std::vector 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