From 387cc6888473398f2a7bf0c7f5a0f10cc31a2b05 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Fri, 26 Jun 2026 14:00:14 -0500 Subject: Add FUSE volume and xattr compatibility --- src/src/fuseconnector.cpp | 37 ++++++++++++++++++++++++++++ src/src/vfs/mo2filesystem.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++ src/src/vfs/mo2filesystem.h | 6 +++++ 3 files changed, 99 insertions(+) diff --git a/src/src/fuseconnector.cpp b/src/src/fuseconnector.cpp index 81a653c..8b7d206 100644 --- a/src/src/fuseconnector.cpp +++ b/src/src/fuseconnector.cpp @@ -329,6 +329,12 @@ void wrap_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, MO2_TRY_REPLY(req, "fsync", ino, EIO) } +void wrap_statfs(fuse_req_t req, fuse_ino_t ino) noexcept +{ + try { mo2_statfs(req, ino); } + MO2_TRY_REPLY(req, "statfs", ino, EIO) +} + void wrap_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, struct flock* lock) noexcept { @@ -376,6 +382,32 @@ void wrap_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, MO2_TRY_REPLY(req, "lseek", ino, EIO) } +void wrap_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, + size_t size) noexcept +{ + try { mo2_getxattr(req, ino, name, size); } + MO2_TRY_REPLY(req, "getxattr", ino, EIO) +} + +void wrap_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) noexcept +{ + try { mo2_listxattr(req, ino, size); } + MO2_TRY_REPLY(req, "listxattr", ino, EIO) +} + +void wrap_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, + const char* value, size_t size, int flags) noexcept +{ + try { mo2_setxattr(req, ino, name, value, size, flags); } + MO2_TRY_REPLY(req, "setxattr", ino, EIO) +} + +void wrap_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name) noexcept +{ + try { mo2_removexattr(req, ino, name); } + MO2_TRY_REPLY(req, "removexattr", ino, EIO) +} + #if FUSE_USE_VERSION < 35 void wrap_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void* arg, struct fuse_file_info* fi, unsigned flags, const void* in_buf, @@ -417,12 +449,17 @@ void setupFuseOps(struct fuse_lowlevel_ops* ops) ops->releasedir = wrap_releasedir; ops->flush = wrap_flush; ops->fsync = wrap_fsync; + ops->statfs = wrap_statfs; ops->getlk = wrap_getlk; ops->setlk = wrap_setlk; ops->flock = wrap_flock; ops->fallocate = wrap_fallocate; ops->copy_file_range = wrap_copy_file_range; ops->lseek = wrap_lseek; + ops->getxattr = wrap_getxattr; + ops->listxattr = wrap_listxattr; + ops->setxattr = wrap_setxattr; + ops->removexattr = wrap_removexattr; ops->ioctl = wrap_ioctl; // access handler removed: default_permissions mount option lets the kernel // handle permission checks in-kernel, eliminating access() round-trips. diff --git a/src/src/vfs/mo2filesystem.cpp b/src/src/vfs/mo2filesystem.cpp index 3ae81de..dd701df 100644 --- a/src/src/vfs/mo2filesystem.cpp +++ b/src/src/vfs/mo2filesystem.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -3079,6 +3080,35 @@ void mo2_fsync(fuse_req_t req, fuse_ino_t /*ino*/, int datasync, fuse_reply_err(req, 0); } +void mo2_statfs(fuse_req_t req, fuse_ino_t /*ino*/) +{ + Mo2FsContext* ctx = getContext(req); + if (ctx == nullptr) { + fuse_reply_err(req, EINVAL); + return; + } + + struct statvfs st {}; + if (ctx->backing_dir_fd >= 0 && fstatvfs(ctx->backing_dir_fd, &st) == 0) { + if (st.f_namemax == 0) { + st.f_namemax = 255; + } + fuse_reply_statfs(req, &st); + return; + } + + st.f_bsize = 4096; + st.f_frsize = 4096; + st.f_blocks = 1024ull * 1024ull; + st.f_bfree = st.f_blocks / 2; + st.f_bavail = st.f_bfree; + st.f_files = 1024ull * 1024ull; + st.f_ffree = st.f_files / 2; + st.f_favail = st.f_ffree; + st.f_namemax = 255; + fuse_reply_statfs(req, &st); +} + void mo2_getlk(fuse_req_t req, fuse_ino_t /*ino*/, struct fuse_file_info* /*fi*/, struct flock* lock) { @@ -3344,6 +3374,32 @@ void mo2_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, } } +void mo2_getxattr(fuse_req_t req, fuse_ino_t /*ino*/, const char* /*name*/, + size_t /*size*/) +{ + fuse_reply_err(req, ENODATA); +} + +void mo2_listxattr(fuse_req_t req, fuse_ino_t /*ino*/, size_t size) +{ + if (size == 0) { + fuse_reply_xattr(req, 0); + return; + } + fuse_reply_buf(req, nullptr, 0); +} + +void mo2_setxattr(fuse_req_t req, fuse_ino_t /*ino*/, const char* /*name*/, + const char* /*value*/, size_t /*size*/, int /*flags*/) +{ + fuse_reply_err(req, 0); +} + +void mo2_removexattr(fuse_req_t req, fuse_ino_t /*ino*/, const char* /*name*/) +{ + fuse_reply_err(req, 0); +} + #if FUSE_USE_VERSION < 35 void mo2_ioctl(fuse_req_t req, fuse_ino_t /*ino*/, int cmd, void* /*arg*/, struct fuse_file_info* /*fi*/, unsigned /*flags*/, diff --git a/src/src/vfs/mo2filesystem.h b/src/src/vfs/mo2filesystem.h index 3efedf9..df2a9c1 100644 --- a/src/src/vfs/mo2filesystem.h +++ b/src/src/vfs/mo2filesystem.h @@ -207,6 +207,7 @@ void mo2_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); void mo2_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi); void mo2_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info* fi); +void mo2_statfs(fuse_req_t req, fuse_ino_t ino); void mo2_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, struct flock* lock); void mo2_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi, @@ -220,6 +221,11 @@ void mo2_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, size_t len, int flags); void mo2_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, struct fuse_file_info* fi); +void mo2_getxattr(fuse_req_t req, fuse_ino_t ino, const char* name, size_t size); +void mo2_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size); +void mo2_setxattr(fuse_req_t req, fuse_ino_t ino, const char* name, + const char* value, size_t size, int flags); +void mo2_removexattr(fuse_req_t req, fuse_ino_t ino, const char* name); #if FUSE_USE_VERSION < 35 void mo2_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void* arg, struct fuse_file_info* fi, unsigned flags, const void* in_buf, -- cgit v1.3.1