blob: 4880be2eacf64325d80f34b3e700099016aab763 [file] [log] [blame]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fusebox/fuse_request.h"
#include <utility>
#include <base/check.h>
#include <base/check_op.h>
namespace fusebox {
FuseRequest::FuseRequest(fuse_req_t req, fuse_file_info* fi) : req_(req) {
flags_ = fi ? fi->flags : 0;
fh_ = fi ? fi->fh : 0;
}
bool FuseRequest::IsInterrupted() const { // Kernel FUSE interrupt
return fuse_req_interrupted(req_);
}
FuseRequest::~FuseRequest() {
if (!replied_) {
fuse_reply_err(req_, EINTR); // User-space FUSE interrupt
}
}
int FuseRequest::ReplyError(int error) {
DCHECK(!replied_);
DCHECK_GT(error, 0);
fuse_reply_err(req_, error);
replied_ = true;
return error;
}
void OkRequest::ReplyOk() {
DCHECK(!replied_);
fuse_reply_err(req_, 0);
replied_ = true;
}
void NoneRequest::ReplyNone() {
DCHECK(!replied_);
fuse_reply_none(req_);
replied_ = true;
}
void AttrRequest::ReplyAttr(const struct stat& attr, double timeout) {
DCHECK(!replied_);
fuse_reply_attr(req_, &attr, timeout);
replied_ = true;
}
void FsattrRequest::ReplyFsattr(const struct statvfs& fs_attr) {
DCHECK(!replied_);
fuse_reply_statfs(req_, &fs_attr);
replied_ = true;
}
void EntryRequest::ReplyEntry(const fuse_entry_param& entry) {
DCHECK(!replied_);
fuse_reply_entry(req_, &entry);
replied_ = true;
}
void OpenRequest::ReplyOpen(uint64_t fh) {
DCHECK(!replied_);
replied_ = true;
DCHECK_NE(0, fh);
fuse_file_info fi = {0};
fi.fh = fh;
if (create_) {
DCHECK_GT(entry_.ino, FUSE_ROOT_ID);
fuse_reply_create(req_, &entry_, &fi);
} else {
fuse_reply_open(req_, &fi);
}
}
void CreateRequest::ReplyCreate(const fuse_entry_param& entry, uint64_t fh) {
DCHECK(!replied_);
replied_ = true;
DCHECK_NE(0, fh);
fuse_file_info fi = {0};
fi.fh = fh;
DCHECK(create_);
fuse_reply_create(req_, &entry, &fi);
}
void BufferRequest::ReplyBuffer(const void* data, size_t size) {
DCHECK(!replied_);
replied_ = true;
if (data) {
fuse_reply_buf(req_, static_cast<const char*>(data), size);
} else {
fuse_reply_buf(req_, nullptr, 0);
}
}
void WriteRequest::ReplyWrite(size_t count) {
DCHECK(!replied_);
fuse_reply_write(req_, count);
replied_ = true;
}
DirEntryRequest::DirEntryRequest(fuse_req_t req,
fuse_file_info* fi,
size_t buf_size,
off_t dir_offset)
: FuseRequest(req, fi), buf_size_(buf_size), dir_offset_(dir_offset) {
DCHECK(buf_size_);
}
bool DirEntryRequest::AddEntry(const struct DirEntry& entry, off_t dir_offset) {
DCHECK(!replied_);
const char* name = entry.name.c_str();
struct stat stat = {0};
stat.st_ino = entry.ino;
stat.st_mode = entry.mode;
if (!buf_.get()) {
buf_ = std::make_unique<char[]>(buf_size_);
CHECK(buf_.get());
buf_offset_ = 0;
}
char* data = buf_.get() + buf_offset_;
const size_t size = buf_size_ - buf_offset_;
size_t used = fuse_add_direntry(req_, data, size, name, &stat, dir_offset);
if (used > size) {
return false; // no |buf_| space.
}
buf_offset_ += used;
CHECK_LE(buf_offset_, buf_size_);
dir_offset_ = dir_offset;
return true;
}
void DirEntryRequest::ReplyDone() {
DCHECK(!replied_);
fuse_reply_buf(req_, buf_.get(), buf_offset_);
replied_ = true;
}
DirEntryBuffer::DirEntryBuffer() = default;
void DirEntryBuffer::AppendRequest(std::unique_ptr<DirEntryRequest> request) {
request_.emplace_back(std::move(request));
Respond();
}
void DirEntryBuffer::AppendResponse(std::vector<struct DirEntry> entry,
bool end) {
entry_.insert(entry_.end(), entry.begin(), entry.end());
end_ = end;
Respond();
}
int DirEntryBuffer::AppendResponse(int error) {
error_ = error;
Respond();
return error;
}
void DirEntryBuffer::Respond() {
constexpr size_t kFlushAddedEntries = 25;
const auto process_next_request = [&](auto& request) {
if (request->IsInterrupted()) {
return true;
}
if (error_) {
request->ReplyError(error_);
return true;
}
off_t dir_offset = request->dir_offset();
if (dir_offset < 0) {
request->ReplyError(EINVAL);
return true;
}
size_t added;
for (added = 0; dir_offset < entry_.size(); ++added) {
const off_t next = 1 + dir_offset;
if (request->AddEntry(entry_[dir_offset++], next)) {
continue; // add next entry
}
request->ReplyDone();
return true;
}
DCHECK_GE(dir_offset, entry_.size());
bool done = end_ || added >= kFlushAddedEntries;
if (done) {
request->ReplyDone();
}
return done;
};
while (!request_.empty()) {
if (!process_next_request(*request_.begin())) {
break;
}
request_.erase(request_.begin());
}
}
} // namespace fusebox