blob: 2685fd92fc272dc6bd20f310ba657d865408174e [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.
#ifndef FUSEBOX_FUSE_REQUEST_H_
#define FUSEBOX_FUSE_REQUEST_H_
#include <fuse_lowlevel.h>
#include <memory>
#include <string>
#include <vector>
namespace fusebox {
/**
* Kernel FUSE low level request responders: FuseRequest stores a Kernel
* fuse_req_t type in member req_ and replies to that req_ with the FUSE
* operation results (the response).
*
* Derived classes specialize the response for the given req_, and store
* any request parameters needed to complete the operation.
*
* Note: the classes only define the low level FUSE request and response
* API, they do not perform FUSE operations.
*/
class FuseRequest {
protected:
explicit FuseRequest(fuse_req_t req, fuse_file_info* fi = nullptr);
FuseRequest(const FuseRequest&) = delete;
FuseRequest& operator=(const FuseRequest&) = delete;
virtual ~FuseRequest();
public:
int flags() const { return flags_; }
uint64_t fh() const { return fh_; }
bool IsInterrupted() const;
int ReplyError(int error);
protected:
const fuse_req_t req_;
bool replied_ = false;
uint64_t fh_;
int flags_;
};
// FUSE request with an OK response.
class OkRequest : public FuseRequest {
public:
explicit OkRequest(fuse_req_t req, fuse_file_info* fi = nullptr)
: FuseRequest(req, fi) {}
void ReplyOk();
};
// FUSE request with a none response.
class NoneRequest : public FuseRequest {
public:
explicit NoneRequest(fuse_req_t req) : FuseRequest(req) {}
void ReplyNone();
};
// FUSE request with an attribute stat response.
class AttrRequest : public FuseRequest {
public:
AttrRequest(fuse_req_t req, fuse_file_info* fi) : FuseRequest(req, fi) {}
void ReplyAttr(const struct stat& attr, double timeout);
};
// FUSE request with a file-system attribute stat response.
class FsattrRequest : public FuseRequest {
public:
explicit FsattrRequest(fuse_req_t req) : FuseRequest(req) {}
void ReplyFsattr(const struct statvfs& fs_attr);
};
// FUSE request with a fuse_entry_param response.
class EntryRequest : public FuseRequest {
public:
explicit EntryRequest(fuse_req_t req) : FuseRequest(req) {}
void ReplyEntry(const fuse_entry_param& entry);
};
// FUSE request with an open file handle response.
class OpenRequest : public FuseRequest {
public:
OpenRequest(fuse_req_t req, fuse_file_info* fi) : FuseRequest(req, fi) {}
void ReplyOpen(uint64_t fh);
protected:
// Set true, iff |this| is a CreateRequest.
bool create_ = false;
// Entry for fuse_reply_create(3) response.
fuse_entry_param entry_ = {0};
};
// FUSE request with an entry create response.
class CreateRequest : public OpenRequest {
public:
explicit CreateRequest(fuse_req_t req, fuse_file_info* fi)
: OpenRequest(req, fi) {
create_ = true;
}
void ReplyCreate(const fuse_entry_param& entry, uint64_t fh);
// Entry for fuse_reply_create(3) response.
void SetEntry(const fuse_entry_param& entry) { entry_ = entry; }
};
// FUSE request with a data buffer response.
class BufferRequest : public FuseRequest {
public:
BufferRequest(fuse_req_t req, fuse_file_info* fi) : FuseRequest(req, fi) {}
void ReplyBuffer(const void* data, size_t size);
};
// FUSE request with a bytes written count response.
class WriteRequest : public FuseRequest {
public:
explicit WriteRequest(fuse_req_t req, fuse_file_info* fi)
: FuseRequest(req, fi) {}
void ReplyWrite(size_t count);
};
// FUSE request with a DirEntry list response.
class DirEntryRequest : public FuseRequest {
public:
DirEntryRequest(fuse_req_t req,
fuse_file_info* fi,
size_t buf_size,
off_t dir_offset);
// Entry buffer |buf_| size.
size_t buf_size() const { return buf_size_; }
// Add entry to |buf_|. Returns true if the entry was added.
bool AddEntry(const struct DirEntry& entry, off_t dir_offset);
// Space used in |buf_| by the added entries.
size_t buf_used() const { return buf_offset_; }
// Offset to the next entry.
off_t dir_offset() const { return dir_offset_; }
// Reply with the entry buffer result.
void ReplyDone();
private:
const size_t buf_size_; // Measured in bytes.
size_t buf_offset_ = 0; // Measured in bytes.
std::unique_ptr<char[]> buf_;
// FUSE (the protocol) and libfuse (the library) does not mandate (it lets
// the program choose) what units the offset is measured in, other than 0
// means "from the beginning". This fusebox program uses "number of files".
off_t dir_offset_;
};
// Responds to multiple DirEntryRequests, each with the same FUSE handle.
class DirEntryBuffer {
public:
DirEntryBuffer();
// Append |request| to the DirEntryRequest list.
void AppendRequest(std::unique_ptr<DirEntryRequest> request);
// Append |entry| DirEntry to the DirEntry list.
void AppendResponse(std::vector<struct DirEntry> entry, bool end = false);
// Append errno |error| to the DirEntry list. Returns |error|.
int AppendResponse(int error);
private:
// Called by AppendResponse() to respond to DirEntry requests.
void Respond();
// List of DirEntryRequest received from Kernel Fuse.
std::vector<std::unique_ptr<DirEntryRequest>> request_;
// List of DirEntry from the file system: readdir(2).
std::vector<struct DirEntry> entry_;
// Error state of the DirEntry list.
int error_ = 0;
// True when the DirEntry list is complete.
bool end_ = false;
};
struct DirEntry {
ino_t ino;
std::string name;
mode_t mode;
};
} // namespace fusebox
#endif // FUSEBOX_FUSE_REQUEST_H_