mirror of
https://github.com/facebook/sapling.git
synced 2024-10-05 14:28:17 +03:00
e3f4a56f6b
Summary: We are unifying C++ APIs for accessing optional and unqualified fields: https://fb.workplace.com/groups/1730279463893632/permalink/2541675446087359/. This diff migrates code from accessing data members generated from unqualified Thrift fields directly to the `field_ref` API, i.e. replacing ``` thrift_obj.field ``` with ``` *thrift_obj.field_ref() ``` The `_ref` suffixes will be removed in the future once data members are private and names can be reclaimed. The output of this codemod has been reviewed in D20039637. The new API is documented in https://our.intern.facebook.com/intern/wiki/Thrift/FieldAccess/. drop-conflicts Reviewed By: yfeldblum Differential Revision: D22631599 fbshipit-source-id: 9bfcaeb636f34a32fd871c7cd6a2db4a7ace30bf
383 lines
12 KiB
C++
383 lines
12 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/takeover/TakeoverData.h"
|
|
|
|
#include <folly/Format.h>
|
|
#include <folly/io/Cursor.h>
|
|
#include <folly/io/IOBuf.h>
|
|
#include <thrift/lib/cpp2/protocol/Serializer.h>
|
|
|
|
#include "eden/fs/utils/Bug.h"
|
|
|
|
using apache::thrift::CompactSerializer;
|
|
using folly::IOBuf;
|
|
using std::string;
|
|
|
|
namespace facebook {
|
|
namespace eden {
|
|
|
|
const std::set<int32_t> kSupportedTakeoverVersions{
|
|
TakeoverData::kTakeoverProtocolVersionOne,
|
|
TakeoverData::kTakeoverProtocolVersionThree,
|
|
TakeoverData::kTakeoverProtocolVersionFour};
|
|
|
|
std::optional<int32_t> TakeoverData::computeCompatibleVersion(
|
|
const std::set<int32_t>& versions,
|
|
const std::set<int32_t>& supported) {
|
|
std::optional<int32_t> best;
|
|
|
|
for (auto& version : versions) {
|
|
if (best.has_value() && best.value() > version) {
|
|
// No better than the current best
|
|
continue;
|
|
}
|
|
if (supported.find(version) == supported.end()) {
|
|
// Not supported
|
|
continue;
|
|
}
|
|
best = version;
|
|
}
|
|
return best;
|
|
}
|
|
|
|
IOBuf TakeoverData::serialize(int32_t protocolVersion) {
|
|
switch (protocolVersion) {
|
|
case kTakeoverProtocolVersionOne:
|
|
return serializeVersion1();
|
|
case kTakeoverProtocolVersionThree:
|
|
case kTakeoverProtocolVersionFour:
|
|
// versions 3 and 4 use the same data serialization
|
|
return serializeVersion3();
|
|
default: {
|
|
EDEN_BUG() << "asked to serialize takeover data in unsupported format "
|
|
<< protocolVersion;
|
|
}
|
|
}
|
|
}
|
|
|
|
folly::IOBuf TakeoverData::serializeError(
|
|
int32_t protocolVersion,
|
|
const folly::exception_wrapper& ew) {
|
|
switch (protocolVersion) {
|
|
// We allow NeverSupported in the error case so that we don't
|
|
// end up EDEN_BUG'ing out in the version mismatch error
|
|
// reporting case.
|
|
case kTakeoverProtocolVersionNeverSupported:
|
|
case kTakeoverProtocolVersionOne:
|
|
return serializeErrorVersion1(ew);
|
|
case kTakeoverProtocolVersionThree:
|
|
case kTakeoverProtocolVersionFour:
|
|
// versions 3 and 4 use the same data serialization
|
|
return serializeErrorVersion3(ew);
|
|
default: {
|
|
EDEN_BUG() << "asked to serialize takeover error in unsupported format "
|
|
<< protocolVersion;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TakeoverData::isPing(const IOBuf* buf) {
|
|
if (buf->length() == sizeof(uint32_t)) {
|
|
folly::io::Cursor cursor(buf);
|
|
auto messageType = cursor.readBE<uint32_t>();
|
|
return messageType == MessageType::PING;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
folly::IOBuf TakeoverData::serializePing() {
|
|
IOBuf buf(IOBuf::CREATE, kHeaderLength);
|
|
folly::io::Appender app(&buf, 0);
|
|
app.writeBE<uint32_t>(MessageType::PING);
|
|
return buf;
|
|
}
|
|
|
|
TakeoverData TakeoverData::deserialize(IOBuf* buf) {
|
|
// We need to probe the data to see which version we have
|
|
folly::io::Cursor cursor(buf);
|
|
|
|
auto messageType = cursor.readBE<uint32_t>();
|
|
switch (messageType) {
|
|
case MessageType::ERROR:
|
|
case MessageType::MOUNTS:
|
|
// A version 1 response. We don't advance the buffer that we pass down
|
|
// because it the messageType is needed to decode the response.
|
|
return deserializeVersion1(buf);
|
|
case kTakeoverProtocolVersionThree:
|
|
// Version 3 (there was no 2 because of how Version 1 used word values
|
|
// 1 and 2) doesn't care about this version byte, so we skip past it
|
|
// and let the underlying code decode the data
|
|
buf->trimStart(sizeof(uint32_t));
|
|
return deserializeVersion3(buf);
|
|
default:
|
|
throw std::runtime_error(folly::sformat(
|
|
"Unrecognized TakeoverData response starting with {:x}",
|
|
messageType));
|
|
}
|
|
}
|
|
|
|
IOBuf TakeoverData::serializeVersion1() {
|
|
// Compute the body data length
|
|
uint64_t bodyLength = sizeof(uint32_t);
|
|
for (const auto& mount : mountPoints) {
|
|
bodyLength += sizeof(uint32_t) + mount.mountPath.stringPiece().size();
|
|
bodyLength += sizeof(uint32_t) + mount.stateDirectory.stringPiece().size();
|
|
bodyLength += sizeof(uint32_t);
|
|
for (const auto& bindMount : mount.bindMounts) {
|
|
bodyLength += sizeof(uint32_t) + bindMount.stringPiece().size();
|
|
}
|
|
bodyLength += sizeof(fuse_init_out);
|
|
|
|
// The fileHandleMap has been removed, so its size will always be 0.
|
|
constexpr size_t fileHandleMapSize = 0;
|
|
bodyLength += sizeof(uint32_t) + fileHandleMapSize;
|
|
|
|
auto serializedInodeMap =
|
|
CompactSerializer::serialize<std::string>(mount.inodeMap);
|
|
bodyLength += sizeof(uint32_t) + serializedInodeMap.size();
|
|
}
|
|
|
|
// Build a buffer with all of the mount paths
|
|
auto fullCapacity = kHeaderLength + bodyLength;
|
|
IOBuf buf(IOBuf::CREATE, fullCapacity);
|
|
folly::io::Appender app(&buf, 0);
|
|
|
|
// Serialize the message type
|
|
app.writeBE<uint32_t>(MessageType::MOUNTS);
|
|
|
|
// Write the number of mount points
|
|
app.writeBE<uint32_t>(mountPoints.size());
|
|
|
|
// Serialize each mount point
|
|
for (const auto& mount : mountPoints) {
|
|
// The mount path
|
|
const auto& pathStr = mount.mountPath.stringPiece();
|
|
app.writeBE<uint32_t>(pathStr.size());
|
|
app(pathStr);
|
|
|
|
// The client configuration dir
|
|
const auto& clientStr = mount.stateDirectory.stringPiece();
|
|
app.writeBE<uint32_t>(clientStr.size());
|
|
app(clientStr);
|
|
|
|
// Number of bind mounts, followed by the bind mount paths
|
|
app.writeBE<uint32_t>(mount.bindMounts.size());
|
|
for (const auto& bindMount : mount.bindMounts) {
|
|
app.writeBE<uint32_t>(bindMount.stringPiece().size());
|
|
app(bindMount.stringPiece());
|
|
}
|
|
|
|
// Stuffing the fuse connection information in as a binary
|
|
// blob because we know that the endianness of the target
|
|
// machine must match the current system for a graceful
|
|
// takeover.
|
|
app.push(folly::StringPiece{reinterpret_cast<const char*>(&mount.connInfo),
|
|
sizeof(mount.connInfo)});
|
|
// SerializedFileHandleMap has been removed so its size is always 0.
|
|
app.writeBE<uint32_t>(0);
|
|
|
|
auto serializedInodeMap =
|
|
CompactSerializer::serialize<std::string>(mount.inodeMap);
|
|
app.writeBE<uint32_t>(serializedInodeMap.size());
|
|
app.push(folly::StringPiece{serializedInodeMap});
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
folly::IOBuf TakeoverData::serializeErrorVersion1(
|
|
const folly::exception_wrapper& ew) {
|
|
// Compute the body data length
|
|
auto exceptionClassName = ew.class_name();
|
|
folly::StringPiece what = ew ? ew.get_exception()->what() : "";
|
|
uint64_t bodyLength = sizeof(uint32_t) + exceptionClassName.size() +
|
|
sizeof(uint32_t) + what.size();
|
|
|
|
// Allocate the buffer
|
|
auto fullCapacity = kHeaderLength + bodyLength;
|
|
IOBuf buf(IOBuf::CREATE, fullCapacity);
|
|
folly::io::Appender app(&buf, 0);
|
|
|
|
// Serialize the message type
|
|
app.writeBE<uint32_t>(MessageType::ERROR);
|
|
|
|
// Write the error type and message
|
|
app.writeBE<uint32_t>(exceptionClassName.size());
|
|
app(exceptionClassName);
|
|
app.writeBE<uint32_t>(what.size());
|
|
app(what);
|
|
|
|
return buf;
|
|
}
|
|
|
|
TakeoverData TakeoverData::deserializeVersion1(IOBuf* buf) {
|
|
folly::io::Cursor cursor(buf);
|
|
|
|
auto messageType = cursor.readBE<uint32_t>();
|
|
if (messageType != MessageType::ERROR && messageType != MessageType::MOUNTS) {
|
|
throw std::runtime_error(
|
|
folly::to<string>("unknown takeover data message type ", messageType));
|
|
}
|
|
|
|
// Check the message type
|
|
if (messageType == MessageType::ERROR) {
|
|
auto errorTypeLength = cursor.readBE<uint32_t>();
|
|
auto errorType = cursor.readFixedString(errorTypeLength);
|
|
auto errorMessageLength = cursor.readBE<uint32_t>();
|
|
auto errorMessage = cursor.readFixedString(errorMessageLength);
|
|
|
|
throw std::runtime_error(errorType + ": " + errorMessage);
|
|
}
|
|
if (messageType != MessageType::MOUNTS) {
|
|
throw std::runtime_error(
|
|
folly::to<string>("unknown takeover data message type ", messageType));
|
|
}
|
|
|
|
TakeoverData data;
|
|
auto numMounts = cursor.readBE<uint32_t>();
|
|
for (uint32_t mountIdx = 0; mountIdx < numMounts; ++mountIdx) {
|
|
auto pathLength = cursor.readBE<uint32_t>();
|
|
auto mountPath = cursor.readFixedString(pathLength);
|
|
|
|
auto clientPathLength = cursor.readBE<uint32_t>();
|
|
auto stateDirectory = cursor.readFixedString(clientPathLength);
|
|
|
|
auto numBindMounts = cursor.readBE<uint32_t>();
|
|
|
|
std::vector<AbsolutePath> bindMounts;
|
|
bindMounts.reserve(numBindMounts);
|
|
for (uint32_t bindIdx = 0; bindIdx < numBindMounts; ++bindIdx) {
|
|
auto bindPathLength = cursor.readBE<uint32_t>();
|
|
auto bindPath = cursor.readFixedString(bindPathLength);
|
|
bindMounts.emplace_back(AbsolutePathPiece{bindPath});
|
|
}
|
|
|
|
fuse_init_out connInfo;
|
|
cursor.pull(&connInfo, sizeof(connInfo));
|
|
|
|
auto fileHandleMapLength = cursor.readBE<uint32_t>();
|
|
cursor.readFixedString(fileHandleMapLength);
|
|
// No need to decode the file handle map.
|
|
|
|
auto inodeMapLength = cursor.readBE<uint32_t>();
|
|
auto inodeMapBuffer = cursor.readFixedString(inodeMapLength);
|
|
auto inodeMap =
|
|
CompactSerializer::deserialize<SerializedInodeMap>(inodeMapBuffer);
|
|
|
|
data.mountPoints.emplace_back(
|
|
AbsolutePath{mountPath},
|
|
AbsolutePath{stateDirectory},
|
|
std::move(bindMounts),
|
|
folly::File{},
|
|
connInfo,
|
|
std::move(inodeMap));
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
IOBuf TakeoverData::serializeVersion3() {
|
|
SerializedTakeoverData serialized;
|
|
|
|
folly::IOBufQueue bufQ;
|
|
folly::io::QueueAppender app(&bufQ, 0);
|
|
|
|
// First word is the protocol version
|
|
app.writeBE<uint32_t>(kTakeoverProtocolVersionThree);
|
|
|
|
std::vector<SerializedMountInfo> serializedMounts;
|
|
for (const auto& mount : mountPoints) {
|
|
SerializedMountInfo serializedMount;
|
|
|
|
*serializedMount.mountPath_ref() = mount.mountPath.stringPiece().str();
|
|
*serializedMount.stateDirectory_ref() =
|
|
mount.stateDirectory.stringPiece().str();
|
|
|
|
for (const auto& bindMount : mount.bindMounts) {
|
|
serializedMount.bindMountPaths_ref()->push_back(
|
|
bindMount.stringPiece().str());
|
|
}
|
|
|
|
// Stuffing the fuse connection information in as a binary
|
|
// blob because we know that the endianness of the target
|
|
// machine must match the current system for a graceful
|
|
// takeover, and it saves us from re-encoding an operating
|
|
// system specific struct into a thrift file.
|
|
*serializedMount.connInfo_ref() = std::string{
|
|
reinterpret_cast<const char*>(&mount.connInfo), sizeof(mount.connInfo)};
|
|
|
|
*serializedMount.inodeMap_ref() = mount.inodeMap;
|
|
|
|
serializedMounts.emplace_back(std::move(serializedMount));
|
|
}
|
|
|
|
serialized.set_mounts(std::move(serializedMounts));
|
|
|
|
CompactSerializer::serialize(serialized, &bufQ);
|
|
return std::move(*bufQ.move());
|
|
}
|
|
|
|
folly::IOBuf TakeoverData::serializeErrorVersion3(
|
|
const folly::exception_wrapper& ew) {
|
|
SerializedTakeoverData serialized;
|
|
auto exceptionClassName = ew.class_name();
|
|
folly::StringPiece what = ew ? ew.get_exception()->what() : "";
|
|
serialized.set_errorReason(
|
|
folly::to<std::string>(exceptionClassName, ": ", what));
|
|
|
|
folly::IOBufQueue bufQ;
|
|
folly::io::QueueAppender app(&bufQ, 0);
|
|
|
|
// First word is the protocol version
|
|
app.writeBE<uint32_t>(kTakeoverProtocolVersionThree);
|
|
|
|
CompactSerializer::serialize(serialized, &bufQ);
|
|
return std::move(*bufQ.move());
|
|
}
|
|
|
|
TakeoverData TakeoverData::deserializeVersion3(IOBuf* buf) {
|
|
auto serialized = CompactSerializer::deserialize<SerializedTakeoverData>(buf);
|
|
|
|
switch (serialized.getType()) {
|
|
case SerializedTakeoverData::Type::errorReason:
|
|
throw std::runtime_error(serialized.get_errorReason());
|
|
|
|
case SerializedTakeoverData::Type::mounts: {
|
|
TakeoverData data;
|
|
for (auto& serializedMount : serialized.mutable_mounts()) {
|
|
const auto* connInfo = reinterpret_cast<const fuse_init_out*>(
|
|
serializedMount.connInfo_ref()->data());
|
|
|
|
std::vector<AbsolutePath> bindMounts;
|
|
for (const auto& path : *serializedMount.bindMountPaths_ref()) {
|
|
bindMounts.emplace_back(AbsolutePathPiece{path});
|
|
}
|
|
|
|
data.mountPoints.emplace_back(
|
|
AbsolutePath{*serializedMount.mountPath_ref()},
|
|
AbsolutePath{*serializedMount.stateDirectory_ref()},
|
|
std::move(bindMounts),
|
|
folly::File{},
|
|
*connInfo,
|
|
std::move(*serializedMount.inodeMap_ref()));
|
|
}
|
|
return data;
|
|
}
|
|
case SerializedTakeoverData::Type::__EMPTY__:
|
|
// This case triggers when there are no mounts to pass between
|
|
// the processes; we allow for it here and return an empty
|
|
// TakeoverData instance.
|
|
return TakeoverData{};
|
|
}
|
|
throw std::runtime_error(
|
|
"impossible enum variant for SerializedTakeoverData");
|
|
}
|
|
|
|
} // namespace eden
|
|
} // namespace facebook
|