aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-11 04:02:27 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-11 04:02:27 -0500
commiteb8c20695643923598d0516e86b1fcef144f65fa (patch)
treed2a4df0c6084bc9f112bd01b3e7ec35e01731aa3 /src
parent983c9c04666bca79033766263094fe1d36588ec9 (diff)
Fix FUSE passthrough: use proper API, drop LD_LIBRARY_PATH, auto-apply cap
- Use fuse_passthrough_open/close API instead of raw fd assignment to fi->backing_id, which caused EIO on every file open through the VFS - Remove LD_LIBRARY_PATH from launcher script — DT_RPATH handles lib resolution, and LD_LIBRARY_PATH triggers AT_SECURE which drops file capabilities (cap_sys_admin) needed for passthrough - Auto-detect missing cap_sys_admin at mount time and prompt via pkexec to re-apply it (handles binary copy/update/extract losing the cap) - Disable passthrough for session if fuse_passthrough_open fails, avoiding "Operation not permitted" spam in terminal - Fix Nix flake: tinyxml2 → tinyxml-2, xorg.lib* → top-level names - CI: eliminate artifact zip round-trip for GitHub releases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src')
-rw-r--r--src/src/organizercore.cpp42
-rw-r--r--src/src/vfs/mo2filesystem.cpp30
-rw-r--r--src/src/vfs/mo2filesystem.h1
3 files changed, 66 insertions, 7 deletions
diff --git a/src/src/organizercore.cpp b/src/src/organizercore.cpp
index 3896039..b42c3ed 100644
--- a/src/src/organizercore.cpp
+++ b/src/src/organizercore.cpp
@@ -663,9 +663,49 @@ void OrganizerCore::prepareVFS()
}
// FUSE passthrough: read the per-instance setting and pass it to the VFS.
+ // The binary needs CAP_SYS_ADMIN for passthrough to work — it can be lost
+ // when the binary is copied, updated, or extracted to a new location.
{
- const bool passthrough =
+ bool passthrough =
QSettings().value("fluorine/fuse_passthrough", false).toBool();
+
+ if (passthrough) {
+ // Check if the binary actually has cap_sys_admin right now.
+ const QString binary = QCoreApplication::applicationFilePath();
+ QProcess getcap;
+ getcap.setProgram("getcap");
+ getcap.setArguments({binary});
+ getcap.start();
+ getcap.waitForFinished(5000);
+ const QString caps = QString::fromUtf8(getcap.readAllStandardOutput());
+ const bool hasCap = caps.contains("cap_sys_admin");
+
+ if (!hasCap) {
+ std::fprintf(stderr,
+ "[VFS] passthrough enabled but binary lacks cap_sys_admin "
+ "— requesting via pkexec...\n");
+ QProcess setcap;
+ setcap.setProgram("pkexec");
+ setcap.setArguments({"setcap", "cap_sys_admin+ep", binary});
+ setcap.start();
+ setcap.waitForFinished(60000);
+
+ if (setcap.exitCode() != 0) {
+ std::fprintf(stderr,
+ "[VFS] failed to set cap_sys_admin — disabling passthrough\n");
+ passthrough = false;
+ } else {
+ // The capability is on the file now, but the running process won't
+ // have it until next exec. Disable passthrough for this session —
+ // it will work automatically on next launch.
+ std::fprintf(stderr,
+ "[VFS] cap_sys_admin applied to binary — passthrough "
+ "will activate on next launch\n");
+ passthrough = false;
+ }
+ }
+ }
+
m_USVFS.setPassthroughEnabled(passthrough);
}
#endif
diff --git a/src/src/vfs/mo2filesystem.cpp b/src/src/vfs/mo2filesystem.cpp
index 42e90c0..6c8cb87 100644
--- a/src/src/vfs/mo2filesystem.cpp
+++ b/src/src/vfs/mo2filesystem.cpp
@@ -1056,12 +1056,25 @@ void mo2_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
ptFd = open(realPath.c_str(), O_RDONLY);
}
if (ptFd >= 0) {
- fi->backing_id = static_cast<uint32_t>(ptFd);
- // Store the fd so we can close it on release
- std::scoped_lock lock(ctx->open_files_mutex);
- auto it = ctx->open_files.find(fh);
- if (it != ctx->open_files.end()) {
- it->second.fd = ptFd;
+ int backingId = fuse_passthrough_open(req, ptFd);
+ if (backingId > 0) {
+ fi->backing_id = backingId;
+ // Store the fd so we can close it on release
+ std::scoped_lock lock(ctx->open_files_mutex);
+ auto it = ctx->open_files.find(fh);
+ if (it != ctx->open_files.end()) {
+ it->second.fd = ptFd;
+ it->second.backing_id = backingId;
+ }
+ } else {
+ // Passthrough registration failed (e.g. missing cap_sys_admin at
+ // runtime). Disable passthrough for the rest of this session to
+ // avoid spamming "Operation not permitted" on every open.
+ close(ptFd);
+ ctx->passthrough_active = false;
+ std::fprintf(stderr,
+ "[VFS] fuse_passthrough_open failed — disabling "
+ "passthrough for this session\n");
}
}
}
@@ -1667,6 +1680,11 @@ void mo2_release(fuse_req_t req, fuse_ino_t /*ino*/, struct fuse_file_info* fi)
std::scoped_lock lock(ctx->open_files_mutex);
auto it = ctx->open_files.find(fi->fh);
if (it != ctx->open_files.end()) {
+ if constexpr (FUSE_CAP_PASSTHROUGH != 0) {
+ if (it->second.backing_id > 0) {
+ fuse_passthrough_close(req, it->second.backing_id);
+ }
+ }
if (it->second.fd >= 0) {
close(it->second.fd);
}
diff --git a/src/src/vfs/mo2filesystem.h b/src/src/vfs/mo2filesystem.h
index 2883a18..9b27d76 100644
--- a/src/src/vfs/mo2filesystem.h
+++ b/src/src/vfs/mo2filesystem.h
@@ -39,6 +39,7 @@ struct Mo2FsContext
struct OpenFile
{
int fd = -1;
+ int backing_id = 0; // FUSE passthrough backing ID (0 = not using passthrough)
std::string real_path;
bool writable = false;
bool is_backing = false;