sapling/eden/fs/fuse/privhelper/PrivHelperConn.cpp
Adam Simpkins f2b4038361 add a privhelper call to set the log file
Summary:
Add a call to tell the privhelper process to redirect its log messages to a
different file handle.

This call will make it possible for the privhelper processed to be forked
before the main edenfs process has processed the --logPath argument.  The main
edenfs process can now drop privileges before it processes arguments and opens
the log file, and it can then pass the file handle to the privhelper process.

Reviewed By: wez

Differential Revision: D8212776

fbshipit-source-id: 3ec6bfc82ee090216d66c5bfd1b8c2b8819c1f45
2018-06-18 17:22:18 -07:00

267 lines
7.8 KiB
C++

/*
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#include "eden/fs/fuse/privhelper/PrivHelperConn.h"
#include <fcntl.h>
#include <folly/Demangle.h>
#include <folly/Exception.h>
#include <folly/File.h>
#include <folly/FileUtil.h>
#include <folly/ScopeGuard.h>
#include <folly/futures/Future.h>
#include <folly/io/Cursor.h>
#include <folly/io/IOBuf.h>
#include <folly/logging/xlog.h>
#include <gflags/gflags.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "eden/fs/utils/SystemError.h"
using folly::ByteRange;
using folly::checkUnixError;
using folly::IOBuf;
using folly::StringPiece;
using folly::io::Appender;
using folly::io::Cursor;
using std::string;
namespace facebook {
namespace eden {
namespace {
constexpr size_t kDefaultBufferSize = 1024;
UnixSocket::Message serializeHeader(
uint32_t xid,
PrivHelperConn::MsgType type) {
UnixSocket::Message msg;
msg.data = IOBuf(IOBuf::CREATE, kDefaultBufferSize);
Appender appender(&msg.data, kDefaultBufferSize);
appender.writeBE<uint32_t>(xid);
appender.writeBE<uint32_t>(static_cast<uint32_t>(type));
return msg;
}
void serializeString(Appender& a, StringPiece str) {
a.writeBE<uint32_t>(str.size());
a.push(ByteRange(str));
}
std::string deserializeString(Cursor& cursor) {
const auto length = cursor.readBE<uint32_t>();
return cursor.readFixedString(length);
}
} // unnamed namespace
void PrivHelperConn::createConnPair(folly::File& client, folly::File& server) {
std::array<int, 2> sockpair;
checkUnixError(
socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sockpair.data()),
"failed to create socket pair for privhelper");
client = folly::File{sockpair[0], /*ownsFd=*/true};
server = folly::File{sockpair[1], /*ownsFd=*/true};
}
UnixSocket::Message PrivHelperConn::serializeMountRequest(
uint32_t xid,
StringPiece mountPoint) {
auto msg = serializeHeader(xid, REQ_MOUNT_FUSE);
Appender appender(&msg.data, kDefaultBufferSize);
serializeString(appender, mountPoint);
return msg;
}
void PrivHelperConn::parseMountRequest(Cursor& cursor, string& mountPoint) {
mountPoint = deserializeString(cursor);
checkAtEnd(cursor, "mount request");
}
UnixSocket::Message PrivHelperConn::serializeUnmountRequest(
uint32_t xid,
StringPiece mountPoint) {
auto msg = serializeHeader(xid, REQ_UNMOUNT_FUSE);
Appender appender(&msg.data, kDefaultBufferSize);
serializeString(appender, mountPoint);
return msg;
}
void PrivHelperConn::parseUnmountRequest(Cursor& cursor, string& mountPoint) {
mountPoint = deserializeString(cursor);
checkAtEnd(cursor, "unmount request");
}
UnixSocket::Message PrivHelperConn::serializeTakeoverShutdownRequest(
uint32_t xid,
StringPiece mountPoint) {
auto msg = serializeHeader(xid, REQ_TAKEOVER_SHUTDOWN);
Appender appender(&msg.data, kDefaultBufferSize);
serializeString(appender, mountPoint);
return msg;
}
void PrivHelperConn::parseTakeoverShutdownRequest(
Cursor& cursor,
string& mountPoint) {
mountPoint = deserializeString(cursor);
checkAtEnd(cursor, "takeover shutdown request");
}
UnixSocket::Message PrivHelperConn::serializeTakeoverStartupRequest(
uint32_t xid,
folly::StringPiece mountPoint,
const std::vector<std::string>& bindMounts) {
auto msg = serializeHeader(xid, REQ_TAKEOVER_STARTUP);
Appender appender(&msg.data, kDefaultBufferSize);
serializeString(appender, mountPoint);
appender.writeBE<uint32_t>(bindMounts.size());
for (const auto& path : bindMounts) {
serializeString(appender, path);
}
return msg;
}
void PrivHelperConn::parseTakeoverStartupRequest(
Cursor& cursor,
std::string& mountPoint,
std::vector<std::string>& bindMounts) {
mountPoint = deserializeString(cursor);
auto n = cursor.readBE<uint32_t>();
while (n-- != 0) {
bindMounts.push_back(deserializeString(cursor));
}
checkAtEnd(cursor, "takeover startup request");
}
void PrivHelperConn::parseEmptyResponse(
MsgType reqType,
const UnixSocket::Message& msg) {
Cursor cursor(&msg.data);
auto xid = cursor.readBE<uint32_t>();
auto msgType = static_cast<MsgType>(cursor.readBE<uint32_t>());
if (msgType == RESP_ERROR) {
rethrowErrorResponse(cursor);
} else if (msgType != reqType) {
throw std::runtime_error(folly::to<string>(
"unexpected response type ",
msgType,
" for request ",
xid,
" of type ",
reqType));
}
}
UnixSocket::Message PrivHelperConn::serializeBindMountRequest(
uint32_t xid,
folly::StringPiece clientPath,
folly::StringPiece mountPath) {
auto msg = serializeHeader(xid, REQ_MOUNT_BIND);
Appender appender(&msg.data, kDefaultBufferSize);
serializeString(appender, mountPath);
serializeString(appender, clientPath);
return msg;
}
void PrivHelperConn::parseBindMountRequest(
Cursor& cursor,
std::string& clientPath,
std::string& mountPath) {
mountPath = deserializeString(cursor);
clientPath = deserializeString(cursor);
checkAtEnd(cursor, "bind mount request");
}
UnixSocket::Message PrivHelperConn::serializeSetLogFileRequest(
uint32_t xid,
folly::File logFile) {
auto msg = serializeHeader(xid, REQ_SET_LOG_FILE);
msg.files.push_back(std::move(logFile));
return msg;
}
void PrivHelperConn::parseSetLogFileRequest(folly::io::Cursor& cursor) {
// REQ_SET_LOG_FILE has an empty body. The only contents
// are the file descriptor transferred with the request.
checkAtEnd(cursor, "set log file request");
}
void PrivHelperConn::serializeErrorResponse(
Appender& appender,
const std::exception& ex) {
int errnum = 0;
auto* sysEx = dynamic_cast<const std::system_error*>(&ex);
if (sysEx != nullptr && isErrnoError(*sysEx)) {
errnum = sysEx->code().value();
}
const auto exceptionType = folly::demangle(typeid(ex));
serializeErrorResponse(appender, ex.what(), errnum, exceptionType);
}
void PrivHelperConn::serializeErrorResponse(
Appender& appender,
folly::StringPiece message,
int errnum,
folly::StringPiece excType) {
appender.writeBE<uint32_t>(errnum);
serializeString(appender, message);
serializeString(appender, excType);
}
[[noreturn]] void PrivHelperConn::rethrowErrorResponse(Cursor& cursor) {
const int errnum = cursor.readBE<uint32_t>();
const auto errmsg = deserializeString(cursor);
const auto excType = deserializeString(cursor);
if (errnum != 0) {
// If we have an errnum, rethrow the error as a std::system_error
//
// Unfortunately this will generally duplicate the errno message
// in the exception string. (errmsg already includes it from when the
// system_error was first thrown in the privhelper process, and the
// system_error constructor ends up including it again here.)
//
// There doesn't seem to be an easy way to avoid this at the moment,
// so for now we just live with it. (We could explicitly search for the
// error string at the end of errmsg and strip it off if found, but this
// seems more complicated than it's worth at the moment.)
throw std::system_error(errnum, std::generic_category(), errmsg);
}
throw PrivHelperError(excType, errmsg);
}
void PrivHelperConn::checkAtEnd(const Cursor& cursor, StringPiece messageType) {
if (!cursor.isAtEnd()) {
throw std::runtime_error(folly::to<string>(
"unexpected trailing data at end of ",
messageType,
": ",
cursor.totalLength(),
" bytes"));
}
}
PrivHelperError::PrivHelperError(StringPiece remoteExType, StringPiece msg)
: message_(folly::to<string>(remoteExType, ": ", msg)) {}
} // namespace eden
} // namespace facebook