2016-05-12 23:43:17 +03:00
|
|
|
/*
|
2017-01-21 09:02:33 +03:00
|
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
2016-05-12 23:43:17 +03:00
|
|
|
* 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 "RequestData.h"
|
|
|
|
#include "Dispatcher.h"
|
|
|
|
|
2017-06-22 23:39:57 +03:00
|
|
|
#include <folly/experimental/logging/xlog.h>
|
2016-06-10 21:26:35 +03:00
|
|
|
|
2016-05-12 23:43:17 +03:00
|
|
|
using namespace folly;
|
2016-06-10 21:26:35 +03:00
|
|
|
using namespace std::chrono;
|
2016-05-12 23:43:17 +03:00
|
|
|
|
|
|
|
namespace facebook {
|
|
|
|
namespace eden {
|
|
|
|
namespace fusell {
|
|
|
|
|
|
|
|
const std::string RequestData::kKey("fusell");
|
|
|
|
|
2017-09-09 05:15:25 +03:00
|
|
|
RequestData::RequestData(fuse_req_t req, Dispatcher* dispatcher)
|
|
|
|
: req_(req),
|
|
|
|
requestContext_(folly::RequestContext::saveContext()),
|
|
|
|
dispatcher_(dispatcher) {
|
2016-05-12 23:43:17 +03:00
|
|
|
fuse_req_interrupt_func(req, RequestData::interrupter, this);
|
2017-09-09 05:15:25 +03:00
|
|
|
dispatcher_->incNumOutstandingRequests();
|
|
|
|
}
|
|
|
|
|
|
|
|
RequestData::~RequestData() {
|
|
|
|
dispatcher_->decNumOutstandingRequests();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
2017-07-22 00:50:24 +03:00
|
|
|
void RequestData::interrupter(fuse_req_t /*req*/, void* data) {
|
2016-05-12 23:43:17 +03:00
|
|
|
auto& request = *reinterpret_cast<RequestData*>(data);
|
|
|
|
|
|
|
|
// Guarantee to preserve the current context
|
|
|
|
auto saved = folly::RequestContext::saveContext();
|
|
|
|
SCOPE_EXIT { folly::RequestContext::setContext(saved); };
|
|
|
|
|
|
|
|
// Adopt the context of the target request
|
|
|
|
folly::RequestContext::setContext(request.requestContext_.lock());
|
|
|
|
|
|
|
|
if (request.interrupter_) {
|
2016-08-01 20:01:56 +03:00
|
|
|
request.interrupter_->fut_.cancel();
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-24 20:37:48 +03:00
|
|
|
bool RequestData::isFuseRequest() {
|
|
|
|
return folly::RequestContext::get()->getContextData(kKey) != nullptr;
|
|
|
|
}
|
|
|
|
|
2016-05-12 23:43:17 +03:00
|
|
|
RequestData& RequestData::get() {
|
|
|
|
auto data = folly::RequestContext::get()->getContextData(kKey);
|
|
|
|
if (UNLIKELY(!data)) {
|
2017-06-22 23:39:57 +03:00
|
|
|
XLOG(FATAL) << "boom for missing RequestData";
|
2016-05-12 23:43:17 +03:00
|
|
|
throw std::runtime_error("no fuse request data set in this context!");
|
|
|
|
}
|
|
|
|
return *dynamic_cast<RequestData*>(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
RequestData& RequestData::create(fuse_req_t req) {
|
|
|
|
folly::RequestContext::create();
|
2017-09-09 05:15:25 +03:00
|
|
|
auto dispatcher = static_cast<Dispatcher*>(fuse_req_userdata(req));
|
2016-05-12 23:43:17 +03:00
|
|
|
folly::RequestContext::get()->setContextData(
|
2017-09-09 05:15:25 +03:00
|
|
|
RequestData::kKey, std::make_unique<RequestData>(req, dispatcher));
|
2016-05-12 23:43:17 +03:00
|
|
|
return get();
|
|
|
|
}
|
|
|
|
|
2017-03-31 21:23:02 +03:00
|
|
|
Future<folly::Unit> RequestData::startRequest(
|
2017-08-18 21:43:55 +03:00
|
|
|
ThreadLocalEdenStats* stats,
|
2017-03-31 21:23:02 +03:00
|
|
|
EdenStats::HistogramPtr histogram) {
|
2016-06-10 21:26:35 +03:00
|
|
|
startTime_ = steady_clock::now();
|
|
|
|
DCHECK(latencyHistogram_ == nullptr);
|
2017-03-31 21:23:02 +03:00
|
|
|
latencyHistogram_ = histogram;
|
|
|
|
stats_ = stats;
|
2016-06-10 21:26:35 +03:00
|
|
|
return folly::Unit{};
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::finishRequest() {
|
2017-10-20 21:32:32 +03:00
|
|
|
auto now = steady_clock::now();
|
|
|
|
auto now_since_epoch = duration_cast<seconds>(now.time_since_epoch());
|
|
|
|
auto diff = duration_cast<microseconds>(now - startTime_);
|
|
|
|
stats_->get()->recordLatency(latencyHistogram_, diff, now_since_epoch);
|
2016-06-10 21:26:35 +03:00
|
|
|
latencyHistogram_ = nullptr;
|
2017-03-31 21:23:02 +03:00
|
|
|
stats_ = nullptr;
|
2016-06-10 21:26:35 +03:00
|
|
|
}
|
|
|
|
|
2016-05-12 23:43:17 +03:00
|
|
|
fuse_req_t RequestData::stealReq() {
|
|
|
|
fuse_req_t res = req_;
|
|
|
|
if (res == nullptr || !req_.compare_exchange_strong(res, nullptr)) {
|
|
|
|
throw std::runtime_error("req_ has been released");
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
fuse_req_t RequestData::getReq() const {
|
|
|
|
if (req_ == nullptr) {
|
|
|
|
throw std::runtime_error("req_ has been released");
|
|
|
|
}
|
|
|
|
return req_;
|
|
|
|
}
|
|
|
|
|
|
|
|
const fuse_ctx& RequestData::getContext() const {
|
|
|
|
auto ctx = fuse_req_ctx(getReq());
|
|
|
|
DCHECK(ctx != nullptr) << "request is missing its context!?";
|
|
|
|
return *ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dispatcher* RequestData::getDispatcher() const {
|
2017-09-09 05:15:25 +03:00
|
|
|
return dispatcher_;
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool RequestData::wasInterrupted() const {
|
|
|
|
return fuse_req_interrupted(getReq());
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<gid_t> RequestData::getGroups() const {
|
|
|
|
std::vector<gid_t> grps;
|
|
|
|
#if FUSE_MINOR_VERSION >= 8
|
|
|
|
grps.resize(64);
|
|
|
|
|
|
|
|
int ngroups = fuse_req_getgroups(getReq(), grps.size(), grps.data());
|
|
|
|
if (ngroups < 0) {
|
|
|
|
// OS doesn't support this operation
|
|
|
|
return grps;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ngroups > grps.size()) {
|
|
|
|
grps.resize(ngroups);
|
|
|
|
ngroups = fuse_req_getgroups(getReq(), grps.size(), grps.data());
|
|
|
|
}
|
|
|
|
|
|
|
|
grps.resize(ngroups);
|
|
|
|
#endif
|
|
|
|
return grps;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyError(int err) {
|
|
|
|
checkKernelError(fuse_reply_err(stealReq(), err));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyNone() {
|
|
|
|
fuse_reply_none(stealReq());
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyEntry(const struct fuse_entry_param& e) {
|
|
|
|
checkKernelError(fuse_reply_entry(stealReq(), &e));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RequestData::replyCreate(const struct fuse_entry_param& e,
|
|
|
|
const struct fuse_file_info& fi) {
|
|
|
|
int err = fuse_reply_create(stealReq(), &e, &fi);
|
|
|
|
if (err == -ENOENT) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
checkKernelError(err);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyAttr(const struct stat& attr, double attr_timeout) {
|
|
|
|
checkKernelError(fuse_reply_attr(stealReq(), &attr, attr_timeout));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyReadLink(const std::string& link) {
|
|
|
|
checkKernelError(fuse_reply_readlink(stealReq(), link.c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool RequestData::replyOpen(const struct fuse_file_info& fi) {
|
|
|
|
int err = fuse_reply_open(stealReq(), &fi);
|
|
|
|
if (err == -ENOENT) {
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
checkKernelError(err);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyWrite(size_t count) {
|
|
|
|
checkKernelError(fuse_reply_write(stealReq(), count));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyBuf(const char* buf, size_t size) {
|
|
|
|
if (size == 0) {
|
|
|
|
buf = nullptr;
|
|
|
|
}
|
|
|
|
checkKernelError(fuse_reply_buf(stealReq(), buf, size));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyIov(const struct iovec* iov, int count) {
|
|
|
|
checkKernelError(fuse_reply_iov(stealReq(), iov, count));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyStatfs(const struct statvfs& st) {
|
|
|
|
checkKernelError(fuse_reply_statfs(stealReq(), &st));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyXattr(size_t count) {
|
|
|
|
checkKernelError(fuse_reply_xattr(stealReq(), count));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyLock(struct flock& lock) {
|
|
|
|
checkKernelError(fuse_reply_lock(stealReq(), &lock));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyBmap(uint64_t idx) {
|
|
|
|
checkKernelError(fuse_reply_bmap(stealReq(), idx));
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyIoctl(int result, const struct iovec* iov, int count) {
|
|
|
|
#ifdef FUSE_IOCTL_UNRESTRICTED
|
|
|
|
checkKernelError(fuse_reply_ioctl(stealReq(), result, iov, count));
|
|
|
|
#else
|
|
|
|
throwSystemErrorExplicit(ENOSYS);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::replyPoll(unsigned revents) {
|
|
|
|
#if FUSE_MINOR_VERSION >= 8
|
|
|
|
checkKernelError(fuse_reply_poll(stealReq(), revents));
|
|
|
|
#else
|
|
|
|
throwSystemErrorExplicit(ENOSYS);
|
|
|
|
#endif
|
|
|
|
}
|
2017-06-22 23:39:57 +03:00
|
|
|
|
|
|
|
void RequestData::systemErrorHandler(const std::system_error& err) {
|
|
|
|
int errnum = EIO;
|
|
|
|
if (err.code().category() == std::system_category()) {
|
|
|
|
errnum = err.code().value();
|
|
|
|
}
|
|
|
|
XLOG(DBG5) << folly::exceptionStr(err);
|
|
|
|
RequestData::get().replyError(errnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RequestData::genericErrorHandler(const std::exception& err) {
|
|
|
|
XLOG(DBG5) << folly::exceptionStr(err);
|
|
|
|
RequestData::get().replyError(EIO);
|
|
|
|
}
|
2016-05-12 23:43:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|