* Copyright 2022, Adrien Destugues <pulkomandy@pulkomandy.tk>
* Distributed under terms of the MIT license.
*/
#include "FUSELowLevel.h"
#include <assert.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <Errors.h>
#define ROUNDDOWN(a, b) (((a) / (b)) * (b))
#define ROUNDUP(a, b) ROUNDDOWN((a) + (b) - 1, b)
struct fuse_req {
fuse_req()
: fReplyResult(0),
fReplyBuf(NULL)
{
sem_init(&fSyncSem, 0, 0);
}
~fuse_req() {
sem_destroy(&fSyncSem);
}
void Wait() {
sem_wait(&fSyncSem);
}
void Notify() {
sem_post(&fSyncSem);
}
sem_t fSyncSem;
ssize_t fReplyResult;
ReadDirBufferFiller fRequestFiller;
void* fRequestCookie;
union {
struct stat* fReplyAttr;
struct fuse_entry_param fReplyEntry;
struct fuse_file_info* fReplyOpen;
struct statvfs* fReplyStat;
char* fReplyBuf;
};
};
void
fuse_ll_init(const fuse_lowlevel_ops* ops, void* userdata, struct fuse_conn_info* conn)
{
if (ops->init != NULL) {
ops->init(userdata, conn);
}
}
void
fuse_ll_destroy(const fuse_lowlevel_ops* ops, void* userdata)
{
if (ops->destroy != NULL) {
ops->destroy(userdata);
}
}
int
fuse_ll_lookup(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
struct stat* st)
{
if (ops->lookup == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->lookup(&request, parent, name);
request.Wait();
*st = request.fReplyEntry.attr;
st->st_ino = request.fReplyEntry.ino;
return request.fReplyResult;
}
int
fuse_ll_getattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, struct stat* st)
{
if (ops->getattr == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
request.fReplyAttr = st;
ops->getattr(&request, ino, NULL);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_setattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, const struct stat *attr,
int to_set)
{
if (ops->setattr == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->setattr(&request, ino, const_cast<struct stat*>(attr), to_set, NULL);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_readlink(const fuse_lowlevel_ops* ops, fuse_ino_t ino, char* buffer, size_t size)
{
if (ops->readlink == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
request.fReplyBuf = buffer;
ops->readlink(&request, ino);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_mkdir(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
mode_t mode)
{
if (ops->mkdir == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->mkdir(&request, parent, name, mode);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_unlink(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name)
{
if (ops->unlink == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->unlink(&request, parent, name);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_rmdir(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name)
{
if (ops->rmdir == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->rmdir(&request, parent, name);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_symlink(const fuse_lowlevel_ops* ops, const char* link, fuse_ino_t parent,
const char* name)
{
if (ops->symlink == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->symlink(&request, link, parent, name);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_rename(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname)
{
if (ops->rename == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->rename(&request, parent, name, newparent, newname);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_link(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_ino_t newparent,
const char *newname)
{
if (ops->link == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->link(&request, ino, newparent, newname);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_open(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_file_info* ffi)
{
if (ops->open == NULL)
return 0;
fuse_req request;
request.fReplyOpen = ffi;
ops->open(&request, ino, ffi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_read(const fuse_lowlevel_ops* ops, fuse_ino_t ino, char* buffer, size_t bufferSize,
off_t position, fuse_file_info* ffi)
{
if (ops->read == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
request.fReplyBuf = buffer;
ops->read(&request, ino, bufferSize, position, ffi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_write(const fuse_lowlevel_ops* ops, fuse_ino_t ino, const char *buf,
size_t size, off_t off, struct fuse_file_info *fi)
{
if (ops->write == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->write(&request, ino, buf, size, off, fi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_flush(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_file_info* ffi)
{
if (ops->flush == NULL)
return 0;
fuse_req request;
ops->flush(&request, ino, ffi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_release(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_file_info* ffi)
{
if (ops->release == NULL)
return 0;
fuse_req request;
ops->release(&request, ino, ffi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_fsync(const fuse_lowlevel_ops* ops, fuse_ino_t ino, int datasync, fuse_file_info* ffi)
{
if (ops->fsync == NULL)
return 0;
fuse_req request;
ops->fsync(&request, ino, datasync, ffi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_opendir(const fuse_lowlevel_ops* ops, fuse_ino_t inode, struct fuse_file_info* ffi)
{
if (ops->readdir == NULL)
return B_NOT_SUPPORTED;
if (ops->opendir) {
fuse_req request;
ops->opendir(&request, inode, ffi);
request.Wait();
return request.fReplyResult;
}
return 0;
}
int
fuse_ll_readdir(const fuse_lowlevel_ops* ops, fuse_ino_t ino, void* cookie, char* buffer,
size_t bufferSize, ReadDirBufferFiller filler, off_t pos, fuse_file_info* ffi)
{
if (ops->readdir == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
request.fReplyBuf = buffer;
request.fRequestFiller = filler;
request.fRequestCookie = cookie;
ops->readdir(&request, ino, bufferSize, pos, ffi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_releasedir(const fuse_lowlevel_ops* ops, fuse_ino_t ino, struct fuse_file_info *fi)
{
if (ops->releasedir == NULL)
return 0;
fuse_req request;
ops->releasedir(&request, ino, fi);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_statfs(const fuse_lowlevel_ops* ops, fuse_ino_t inode, struct statvfs* stat)
{
if (ops->statfs == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
request.fReplyStat = stat;
ops->statfs(&request, inode);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_getxattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, const char *name,
char* buffer, size_t size)
{
if (ops->getxattr == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
request.fReplyBuf = buffer;
ops->getxattr(&request, ino, name, size);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_listxattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, char* buffer, size_t size)
{
if (ops->listxattr == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
request.fReplyBuf = (char*)buffer;
ops->listxattr(&request, ino, size);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_access(const fuse_lowlevel_ops* ops, fuse_ino_t ino, int mask)
{
if (ops->access == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->access(&request, ino, mask);
request.Wait();
return request.fReplyResult;
}
int
fuse_ll_create(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
mode_t mode, struct fuse_file_info *fi, fuse_ino_t& ino)
{
if (ops->create == NULL)
return B_NOT_SUPPORTED;
fuse_req request;
ops->create(&request, parent, name, mode, fi);
request.Wait();
ino = request.fReplyEntry.ino;
return request.fReplyResult;
}
int
fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
{
*req->fReplyAttr = *attr;
req->Notify();
return 0;
}
int
fuse_reply_create(fuse_req_t req, const struct fuse_entry_param* e, const struct fuse_file_info* fi)
{
req->fReplyEntry = *e;
req->Notify();
return 0;
}
int
fuse_reply_readlink(fuse_req_t req, const char* link)
{
strlcpy(req->fReplyBuf, link, req->fReplyResult);
req->fReplyResult = strlen(link);
req->Notify();
return 0;
}
int
fuse_reply_open(fuse_req_t req, const struct fuse_file_info* f)
{
*req->fReplyOpen = *f;
req->Notify();
return 0;
}
int
fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
{
if (req->fReplyBuf && req->fReplyBuf != buf)
memcpy(req->fReplyBuf, buf, size);
req->fReplyResult = size;
req->Notify();
return 0;
}
int
fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
{
req->fReplyEntry = *e;
req->Notify();
return 0;
}
int
fuse_reply_err(fuse_req_t req, int err)
{
assert(err >= 0);
req->fReplyResult = -err;
req->Notify();
return 0;
}
int
fuse_reply_statfs(fuse_req_t req, const struct statvfs* stat)
{
*req->fReplyStat = *stat;
req->Notify();
return 0;
}
int
fuse_reply_write(fuse_req_t req, size_t count)
{
req->fReplyResult = count;
req->Notify();
return 0;
}
size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name,
const struct stat *stbuf, off_t off)
{
size_t entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1;
entryLen = ROUNDUP(entryLen, 8);
if (stbuf != NULL) {
req->fRequestFiller(req->fRequestCookie, buf, bufsize, name, stbuf, off);
}
return entryLen;
}
void fuse_session_add_chan(struct fuse_session* se, struct fuse_chan* ch)
{
}
void fuse_session_remove_chan(struct fuse_chan* ch)
{
}
void fuse_session_destroy(struct fuse_session* se)
{
}
void fuse_session_exit(struct fuse_session* se)
{
}