turbonfs/inc/fs-handler.h (617 lines of code) (raw):
#ifndef __FS_HANDLER_H__
#define __FS_HANDLER_H__
#include "nfs_client.h"
#ifdef ENABLE_NO_FUSE
static inline
int fuse_reply_err(fuse_req_t req, int err)
{
assert(err >= 0);
PXT *pxtask = _FR2PXT(req);
pxtask->res = -err;
return 0;
}
static inline
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
{
PXT *pxtask = _FR2PXT(req);
pxtask->res = 0;
return 0;
}
#else
#define FUSE_REPLY_ERR(req, errno_pos) \
do { \
assert(errno_pos >= 0); \
const int fre = fuse_reply_err(req, errno_pos); \
if (fre != 0) { \
INC_GBL_STATS(fuse_reply_failed, 1); \
AZLogError("fuse_reply_err({}, {}) failed: {}", \
fmt::ptr(req), errno_pos, fre); \
assert(0); \
} else { \
DEC_GBL_STATS(fuse_responses_awaited, 1); \
} \
} while (0)
#endif
/*
* These are FS handlers common between fuse and nofuse mode.
* Keeping them common ensures that the exact same code works in fuse and
* nofuse mode. Obviously fuse_req_t does not have the same sigificance
* in nonfuse mode, instead it's used more as a request context.
*
* TODO: Currently it contains many functions which are not needed by nofuse.
* Move those to main.cpp.
*/
static void aznfsc_ll_lookup(fuse_req_t req,
fuse_ino_t parent_ino,
const char *name)
{
FUSE_STATS_TRACKER(FUSE_LOOKUP);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_lookup(req={}, parent_ino={}, name={})",
fmt::ptr(req), parent_ino, name);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->lookup(req, parent_ino, name);
}
[[maybe_unused]]
static void aznfsc_ll_getattr(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_GETATTR);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_getattr(req={}, ino={}, fi={})",
fmt::ptr(req), ino, fmt::ptr(fi));
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->getattr(req, ino, fi);
}
[[maybe_unused]]
static void aznfsc_ll_setattr(fuse_req_t req,
fuse_ino_t ino,
struct stat *attr,
int to_set /* bitmask indicating the attributes to set */,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_SETATTR);
INC_GBL_STATS(fuse_responses_awaited, 1);
// TODO: Log all to-be-set attributes.
AZLogDebug("aznfsc_ll_setattr(req={}, ino={}, to_set=0x{:x}, fi={})",
fmt::ptr(req), ino, to_set, fmt::ptr(fi));
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->setattr(req, ino, attr, to_set, fi);
}
[[maybe_unused]]
static void aznfsc_ll_readlink(fuse_req_t req,
fuse_ino_t ino)
{
FUSE_STATS_TRACKER(FUSE_READLINK);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_readlink(req={}, ino={})",
fmt::ptr(req), ino);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->readlink(req, ino);
}
[[maybe_unused]]
static void aznfsc_ll_mknod(fuse_req_t req,
fuse_ino_t parent_ino,
const char *name,
mode_t mode,
dev_t rdev)
{
FUSE_STATS_TRACKER(FUSE_MKNOD);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_mknod(req={}, parent_ino={}, name={}, "
"mode=0{:03o})",
fmt::ptr(req), parent_ino, name, mode);
if (S_ISREG(mode)) {
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->mknod(req, parent_ino, name, mode);
} else {
AZLogError("mknod(req={}, parent_ino={}, name={}, "
"mode=0{:03o}) is unsupported for non-regular files.",
fmt::ptr(req), parent_ino, name, mode);
FUSE_REPLY_ERR(req, ENOSYS);
}
}
[[maybe_unused]]
static void aznfsc_ll_mkdir(fuse_req_t req,
fuse_ino_t parent_ino,
const char *name,
mode_t mode)
{
FUSE_STATS_TRACKER(FUSE_MKDIR);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_mkdir(req={}, parent_ino={}, name={}, mode=0{:03o})",
fmt::ptr(req), parent_ino, name, mode);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->mkdir(req, parent_ino, name, mode);
}
[[maybe_unused]]
static void aznfsc_ll_unlink(fuse_req_t req,
fuse_ino_t parent_ino,
const char *name)
{
FUSE_STATS_TRACKER(FUSE_UNLINK);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_unlink(req={}, parent_ino={}, name={})",
fmt::ptr(req), parent_ino, name);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
/*
* Call silly_rename() to see if it wants to silly rename instead of unlink.
* We will perform silly rename if the opencnt of the file is not 0, i.e.,
* some process has the file open. This is for POSIX compliance, where
* open files should be accessible till the last open handle is closed.
* Depending on the silly rename status this will reply to the fuse unlink
* request.
*/
if (client->silly_rename(req, parent_ino, name)) {
return;
}
client->unlink(req, parent_ino, name, false /* for_silly_rename */);
}
[[maybe_unused]]
static void aznfsc_ll_rmdir(fuse_req_t req,
fuse_ino_t parent_ino,
const char *name)
{
FUSE_STATS_TRACKER(FUSE_RMDIR);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_rmdir(req={}, parent_ino={}, name={})",
fmt::ptr(req), parent_ino, name);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->rmdir(req, parent_ino, name);
}
[[maybe_unused]]
static void aznfsc_ll_symlink(fuse_req_t req,
const char *link,
fuse_ino_t parent_ino,
const char *name)
{
FUSE_STATS_TRACKER(FUSE_SYMLINK);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_symlink(req={}, link={}, parent_ino={}, name={})",
fmt::ptr(req), link, parent_ino, name);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->symlink(req, link, parent_ino, name);
}
[[maybe_unused]]
static void aznfsc_ll_rename(fuse_req_t req,
fuse_ino_t parent_ino,
const char *name,
fuse_ino_t newparent_ino,
const char *newname,
unsigned int flags)
{
FUSE_STATS_TRACKER(FUSE_RENAME);
INC_GBL_STATS(fuse_responses_awaited, 1);
/*
* If oldpath and newpath are same then rename() must succeed w/o doing
* anything.
*/
if ((parent_ino == newparent_ino) &&
(::strcmp(name, newname) == 0)) {
AZLogDebug("aznfsc_ll_rename(req={}, parent_ino={}, name={}, "
"newparent_ino={}, newname={}, flags={}) oldpath==newpath",
fmt::ptr(req), parent_ino, name,
newparent_ino, newname, flags);
FUSE_REPLY_ERR(req, 0);
return;
}
/*
* We don't support renameat2() i.e., no support for `RENAME_EXCHANGE` or
* `RENAME_NOREPLACE` flags, as NFS doesn't support these.
*/
if (flags != 0) {
AZLogError("aznfsc_ll_rename(req={}, parent_ino={}, name={}, "
"newparent_ino={}, newname={}, flags={}) not supported",
fmt::ptr(req), parent_ino, name,
newparent_ino, newname, flags);
FUSE_REPLY_ERR(req, EINVAL);
return;
} else {
AZLogDebug("aznfsc_ll_rename(req={}, parent_ino={}, name={}, "
"newparent_ino={}, newname={}, flags={})",
fmt::ptr(req), parent_ino, name,
newparent_ino, newname, flags);
}
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
/*
* Call silly_rename() to see if it wants to silly rename the outgoing file
* (newparent_ino/newname). Silly rename will be done if both of these
* conditions are true:
* 1. It exists and is a file (not a directory).
* 2. It has a non-zero open count.
*
* Note that silly rename is needed for POSIX compliance, where open files
* should be accessible till the last open handle is closed.
*
* If silly_rename() finds out that silly rename is needed as per the above
* conditions, it initiates the silly rename and arranges to call the actual
* rename from the silly rename completion callback. It returns true in that
* case and we don't need to perform the actual rename here. To call the
* actual rename it needs to know not only the to-be-silly-renamed file
* newparent_ino/newname but also the old file parent_ino/name.
*
* If silly_rename() finds out that silly rename is not needed, it returns
* false and in that case we must perform the actual rename here.
*/
if (client->silly_rename(req,
newparent_ino,
newname, /* file to silly rename */
parent_ino,
name /* original to-be-renamed file */)) {
return;
}
// Perform user requested rename.
client->rename(req, parent_ino, name, newparent_ino, newname);
}
[[maybe_unused]]
static void aznfsc_ll_link(fuse_req_t req,
fuse_ino_t ino,
fuse_ino_t newparent_ino,
const char *newname)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_open(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_OPEN);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_open(req={}, ino={}, fi={})",
fmt::ptr(req), ino, fmt::ptr(fi));
/*
* We plan to manage our own file cache for better control over writes.
*
* Note: We don't need to set these explicitly as they default to
* these values, we do it to highlight our intent.
*
* TODO: Explore kernel caching, its benefits and side-effects.
* Update: Kernel caching doesn't perform as well for our large files,
* large IOs use case.
*
* Keep parallel_direct_writes disabled, so that fuse ensures that it
* doesn't send another write before the prev one completes. We depend
* on that.
*/
fi->direct_io = !aznfsc_cfg.cache.data.kernel.enable;
fi->keep_cache = aznfsc_cfg.cache.data.kernel.enable;
fi->nonseekable = 0;
fi->parallel_direct_writes = 0;
fi->noflush = 0;
/*
* TODO: Use this to identify the open file handle for which a given fuse
* request is made.
*/
fi->fh = 12345678;
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
struct nfs_inode *inode = client->get_nfs_inode_from_ino(ino);
// Make sure it's not called for directories.
assert(!inode->is_dir());
/*
* For cto consistency open should force revalidate the inode by making a
* getattr call and if that indicates that file data has changed (mtime
* and/or size has changed from our cached values), then we should
* revalidate the cache before proceeding with open. If the cache is
* already maked invalid then also we should revalidate the cache.
* Cache revalidation should flush all dirty mappings, thus ensuring that
* later read can safely read from the cache w/o worrying about the
* freshness of the cached data.
*/
if (inode->is_regfile() &&
inode->has_filecache() && !inode->is_cache_empty()) {
/*
* If cache is already marked invalid, then we must flush the cache
* now.
*/
bool sync_cache =
inode->get_filecache()->test_and_clear_invalidate_pending();
if (!sync_cache) {
/*
* If not already marked invalid, then we need to force revalidate,
* which will issue a fresh GETATTR and determine "cached data has
* changed" by comparing the mtime and size with the cached values.
*/
AZLogDebug("[{}] Force revalidating inode", ino);
inode->revalidate(true /* force */);
// Did revalidate() mark the cache invalid?
sync_cache =
inode->get_filecache()->test_and_clear_invalidate_pending();
}
/*
* Either the cache was already marked invalid or fresh GETATTR
* confirms file on the server has changed since we cached it.
* In either case, sync the dirty data with the server.
*/
if (sync_cache) {
AZLogDebug("[{}] Sync'ing cache before read", ino);
inode->flush_cache_and_wait();
}
}
/*
* TODO: See comments in readahead.h, ideally readahead state should be
* per file pointer (per open handle) but since fuse doesn't let
* us know the file pointer we maintain readahead state per inode.
* We must reset the readahead state so that this file handle can
* correctly perform readaheads and not confuse this as an access
* using the prev handle. This means if we open more than one handle
* simultaneously it will cause the readahead state to be reset.
*
* This is a hack and needs to be properly addressed!
*/
if (inode->is_regfile() && inode->has_rastate()) {
AZLogDebug("[{}] Resetting readahead state", ino);
inode->get_rastate()->reset();
}
/*
* If file cache is not allocated, allocate now.
* Mostly it'll be allocated in nfs_client::reply_entry(), but for inodes
* conveyed through readdirplus, nfs_client::reply_entry() won't be called
* and filecache_handle won't be allocated when aznfsc_ll_open() is called.
*/
inode->on_fuse_open(FUSE_OPEN);
assert(inode->opencnt > 0);
const int fre = fuse_reply_open(req, fi);
if (fre != 0) {
INC_GBL_STATS(fuse_reply_failed, 1);
AZLogError("[{}] fuse_reply_open({}) failed: {}",
inode->get_fuse_ino(), fmt::ptr(req), fre);
assert(0);
// Drop opencnt incremented in on_fuse_open().
inode->opencnt--;
} else {
DEC_GBL_STATS(fuse_responses_awaited, 1);
}
}
[[maybe_unused]]
static void aznfsc_ll_read(fuse_req_t req,
fuse_ino_t ino,
size_t size,
off_t off,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_READ);
INC_GBL_STATS(fuse_responses_awaited, 1);
INC_GBL_STATS(app_read_reqs, 1);
AZLogDebug("aznfsc_ll_read(req={}, ino={}, size={}, offset={} fi={}, "
"fi->fh={})",
fmt::ptr(req), ino, size, off, fmt::ptr(fi), fi->fh);
/*
* Sanity assert. 1MiB is the max read size fuse will ever issue.
* If fuse sends more we'd like to know.
*
* TODO: Remove this before going to production.
*/
assert(size <= 1048576);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->read(req, ino, size, off, fi);
}
[[maybe_unused]]
static void aznfsc_ll_write(fuse_req_t req,
fuse_ino_t ino,
const char *buf,
size_t size,
off_t off,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_WRITE);
INC_GBL_STATS(fuse_responses_awaited, 1);
/*
* XXX: write will be never called as we implement write_buf.
*/
AZLogError("aznfsc_ll_write(req={}, ino={}, buf={}, size={}, off={}, fi={})",
fmt::ptr(req), ino, fmt::ptr(buf), size, off, fmt::ptr(fi));
FUSE_REPLY_ERR(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_flush(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_FLUSH);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_flush(req={}, ino={}, fi={})",
fmt::ptr(req), ino, fmt::ptr(fi));
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->flush(req, ino);
}
[[maybe_unused]]
static void aznfsc_ll_release(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_RELEASE);
INC_GBL_STATS(fuse_responses_awaited, 1);
/*
* Fuse calls flush() for every fd closed and release() once per file,
* when the last fd to that file is closed.
* Though we shouldn't need the flush here but for safety we put it
* here as fuse doc says flush()) may not be called.
*/
AZLogDebug("aznfsc_ll_release(req={}, ino={}, fi={}, fi->fh={})",
fmt::ptr(req), ino, fmt::ptr(fi), fi->fh);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
struct nfs_inode *inode = client->get_nfs_inode_from_ino(ino);
// Must be called for an open file.
assert(inode->is_open());
/*
* inode release() will drop an opencnt on the inode.
* If this was not the last opencnt, then it doesn't do anything more, else
* it does the following for regular files:
* - If release is called for a silly-renamed file, then it drops the
* cache and unlinks the file.
* - If not a silly-renamed file, then it flushes the cache.
*/
if (inode->release(req)) {
FUSE_REPLY_ERR(req, 0);
}
}
[[maybe_unused]]
static void aznfsc_ll_fsync(fuse_req_t req,
fuse_ino_t ino,
int datasync,
struct fuse_file_info *fi)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_opendir(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_OPENDIR);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_opendir(req={}, ino={}, fi={})",
fmt::ptr(req), ino, fmt::ptr(fi));
/*
* We manage our own readdir cache and we don't want kernel to
* cache directory contents.
*
* Note: We don't need to set these explicitly as they default to
* these values, we do it to highlight our intent.
* TODO: Later explore if kernel cacheing directory content is beneficial
* and what are the side effects, if any.
*/
fi->direct_io = !aznfsc_cfg.cache.readdir.kernel.enable;
fi->keep_cache = 1;
fi->nonseekable = 0;
fi->cache_readdir = aznfsc_cfg.cache.readdir.kernel.enable;
fi->noflush = 0;
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
struct nfs_inode *inode = client->get_nfs_inode_from_ino(ino);
assert(inode->is_dir());
inode->on_fuse_open(FUSE_OPENDIR);
assert(inode->opencnt > 0);
const int fre = fuse_reply_open(req, fi);
if (fre != 0) {
INC_GBL_STATS(fuse_reply_failed, 1);
AZLogError("[{}] fuse_reply_open({}) failed: {}",
inode->get_fuse_ino(), fmt::ptr(req), fre);
assert(0);
// Drop opencnt incremented in on_fuse_open().
inode->opencnt--;
} else {
DEC_GBL_STATS(fuse_responses_awaited, 1);
}
}
[[maybe_unused]]
static void aznfsc_ll_readdir(fuse_req_t req,
fuse_ino_t ino,
size_t size,
off_t off,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_READDIR);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_readdir(req={}, ino={}, size={}, off={}, fi={})",
fmt::ptr(req), ino, size, off, fmt::ptr(fi));
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
[[maybe_unused]] struct nfs_inode *inode = client->get_nfs_inode_from_ino(ino);
// Must be called for an open directory.
assert(inode->is_open());
client->readdir(req, ino, size, off, fi);
}
[[maybe_unused]]
static void aznfsc_ll_releasedir(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_RELEASEDIR);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_releasedir(req={}, ino={}, fi={})",
fmt::ptr(req), ino, fmt::ptr(fi));
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
struct nfs_inode *inode = client->get_nfs_inode_from_ino(ino);
// Must be called for an open directory.
assert(inode->is_open());
/*
* We don't do anything in opendir() so nothing to be done in
* releasedir() than just dropping the opencnt increased in
* aznfsc_ll_opendir().
*
* TODO: See if we want to flush the directory buffer to create
* space. This may be helpful for find(1)workloads which
* traverse a directory just once.
*/
if (inode->release(req)) {
FUSE_REPLY_ERR(req, 0);
return;
}
// For directory inodes, release() must always return true.
assert(0);
}
[[maybe_unused]]
static void aznfsc_ll_fsyncdir(fuse_req_t req,
fuse_ino_t ino,
int datasync,
struct fuse_file_info *fi)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_statfs(fuse_req_t req,
fuse_ino_t ino)
{
FUSE_STATS_TRACKER(FUSE_STATFS);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_statfs(req={}, ino={})", fmt::ptr(req), ino);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->statfs(req, ino);
}
[[maybe_unused]]
static void aznfsc_ll_setxattr(fuse_req_t req,
fuse_ino_t ino,
const char *name,
const char *value,
size_t size,
int flags)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_getxattr(fuse_req_t req,
fuse_ino_t ino,
const char *name,
size_t size)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_listxattr(fuse_req_t req,
fuse_ino_t ino,
size_t size)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_removexattr(fuse_req_t req,
fuse_ino_t ino,
const char *name)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_access(fuse_req_t req,
fuse_ino_t ino,
int mask)
{
FUSE_STATS_TRACKER(FUSE_ACCESS);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_access(req={}, ino={}, mask=0{:03o})",
fmt::ptr(req), ino, mask);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->access(req, ino, mask);
}
[[maybe_unused]]
static void aznfsc_ll_create(fuse_req_t req,
fuse_ino_t parent_ino,
const char *name,
mode_t mode,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_CREATE);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_create(req={}, parent_ino={}, name={}, "
"mode=0{:03o}, fi={})",
fmt::ptr(req), parent_ino, name, mode, fmt::ptr(fi));
/*
* See aznfsc_ll_open().
*/
fi->direct_io = !aznfsc_cfg.cache.data.kernel.enable;
fi->keep_cache = aznfsc_cfg.cache.data.kernel.enable;
fi->nonseekable = 0;
fi->parallel_direct_writes = 0;
fi->noflush = 0;
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->create(req, parent_ino, name, mode, fi);
}
[[maybe_unused]]
static void aznfsc_ll_getlk(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi,
struct flock *lock)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_setlk(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi,
struct flock *lock,
int sleep)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_bmap(fuse_req_t req,
fuse_ino_t ino,
size_t blocksize,
uint64_t idx)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
#if FUSE_USE_VERSION < 35
[[maybe_unused]]
static void aznfsc_ll_ioctl(fuse_req_t req,
fuse_ino_t ino,
int cmd,
void *arg,
struct fuse_file_info *fi,
unsigned flags,
const void *in_buf,
size_t in_bufsz,
size_t out_bufsz)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
#else
[[maybe_unused]]
static void aznfsc_ll_ioctl(fuse_req_t req,
fuse_ino_t ino,
unsigned int cmd,
void *arg,
struct fuse_file_info *fi,
unsigned flags,
const void *in_buf,
size_t in_bufsz,
size_t out_bufsz)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
#endif
[[maybe_unused]]
static void aznfsc_ll_poll(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi,
struct fuse_pollhandle *ph)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_write_buf(fuse_req_t req,
fuse_ino_t ino,
struct fuse_bufvec *bufv,
off_t off,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_WRITE);
INC_GBL_STATS(fuse_responses_awaited, 1);
INC_GBL_STATS(app_write_reqs, 1);
assert(bufv->idx < bufv->count);
const size_t length = bufv->buf[bufv->idx].size - bufv->off;
assert((int) length >= 0);
/*
* XXX We are only handling count=1, assert to know if kernel sends more,
* we would want to handle that.
*/
assert(bufv->count == 1);
AZLogDebug("aznfsc_ll_write_buf(req={}, ino={}, bufv={}, off={}, len={}, "
"fi={} [writepage: {}, flush: {}]",
fmt::ptr(req), ino, fmt::ptr(bufv), off, length, fmt::ptr(fi),
fi->writepage ? 1 : 0, fi->flush ? 1 : 0);
/*
* Sanity assert. 1MiB is the max write size fuse will ever issue.
* If fuse sends more we'd like to know.
*
* TODO: Remove this before going to production.
*/
assert(length <= 1048576);
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->write(req, ino, bufv, length, off);
}
[[maybe_unused]]
static void aznfsc_ll_retrieve_reply(fuse_req_t req,
void *cookie,
fuse_ino_t ino,
off_t offset,
struct fuse_bufvec *bufv)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_flock(fuse_req_t req,
fuse_ino_t ino,
struct fuse_file_info *fi,
int op)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_fallocate(fuse_req_t req,
fuse_ino_t ino,
int mode,
off_t offset,
off_t length,
struct fuse_file_info *fi)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_readdirplus(fuse_req_t req,
fuse_ino_t ino,
size_t size,
off_t off,
struct fuse_file_info *fi)
{
FUSE_STATS_TRACKER(FUSE_READDIRPLUS);
INC_GBL_STATS(fuse_responses_awaited, 1);
AZLogDebug("aznfsc_ll_readdirplus(req={}, ino={}, size={}, off={}, fi={})",
fmt::ptr(req), ino, size, off, fmt::ptr(fi));
struct nfs_client *client = get_nfs_client_from_fuse_req(req);
client->readdirplus(req, ino, size, off, fi);
}
void aznfsc_ll_copy_file_range(fuse_req_t req,
fuse_ino_t ino_in,
off_t off_in,
struct fuse_file_info *fi_in,
fuse_ino_t ino_out,
off_t off_out,
struct fuse_file_info *fi_out,
size_t len,
int flags)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
[[maybe_unused]]
static void aznfsc_ll_lseek(fuse_req_t req,
fuse_ino_t ino,
off_t off,
int whence,
struct fuse_file_info *fi)
{
/*
* TODO: Fill me.
*/
fuse_reply_err(req, ENOSYS);
}
#endif /* __FS_HANDLER_H__ */