aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-06-26 15:48:27 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-06-26 15:48:27 -0500
commite01ec60eb4023485f1e667c7b6a5e1a62dc02164 (patch)
tree4c4c4b7bd829083f907c1442fb4c6731cd6e93b1 /src
parent6292eb18a79dc00e2c5369bf3773cffe929b950d (diff)
Route VFS permissions through COW handlers
Diffstat (limited to 'src')
-rw-r--r--src/src/fuseconnector.cpp11
-rw-r--r--src/src/vfs/mo2filesystem.cpp13
2 files changed, 13 insertions, 11 deletions
diff --git a/src/src/fuseconnector.cpp b/src/src/fuseconnector.cpp
index 8b7d206..238ce13 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_access(fuse_req_t req, fuse_ino_t ino, int mask) noexcept
+{
+ try { mo2_access(req, ino, mask); }
+ MO2_TRY_REPLY(req, "access", ino, EIO)
+}
+
void wrap_statfs(fuse_req_t req, fuse_ino_t ino) noexcept
{
try { mo2_statfs(req, ino); }
@@ -449,6 +455,7 @@ void setupFuseOps(struct fuse_lowlevel_ops* ops)
ops->releasedir = wrap_releasedir;
ops->flush = wrap_flush;
ops->fsync = wrap_fsync;
+ ops->access = wrap_access;
ops->statfs = wrap_statfs;
ops->getlk = wrap_getlk;
ops->setlk = wrap_setlk;
@@ -461,8 +468,6 @@ void setupFuseOps(struct fuse_lowlevel_ops* ops)
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.
}
} // namespace
@@ -662,7 +667,7 @@ bool FuseConnector::mount(
// kernel reads don't fit. 1MB is the safe ceiling.
std::vector<std::string> argvStorage = {
"mo2fuse", "-o", "fsname=mo2linux", "-o", "noatime",
- "-o", "default_permissions", "-o", "max_read=1048576"};
+ "-o", "max_read=1048576"};
std::fprintf(stderr, "[VFS] libfuse=%s headers=%d.%d\n",
fuse_pkgversion(), FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION);
diff --git a/src/src/vfs/mo2filesystem.cpp b/src/src/vfs/mo2filesystem.cpp
index 377ce93..0f6d2a4 100644
--- a/src/src/vfs/mo2filesystem.cpp
+++ b/src/src/vfs/mo2filesystem.cpp
@@ -1082,9 +1082,8 @@ void fillStatForDir(struct stat* st, fuse_ino_t ino, uid_t uid, gid_t gid)
mode_t regularFileVfsMode(mode_t sourceMode)
{
mode_t mode = sourceMode != 0 ? sourceMode : static_cast<mode_t>(0644);
- // The merged VFS is copy-on-write. Existing files from the base game or mods
- // must still pass the kernel's default_permissions write check so mo2_open()
- // can materialize them into staging instead of failing before userspace.
+ // The merged VFS is copy-on-write, so expose source files as writable even
+ // when their physical base-game or mod files are read-only.
mode |= S_IRUSR | S_IWUSR;
return mode;
}
@@ -3655,11 +3654,9 @@ void mo2_access(fuse_req_t req, fuse_ino_t ino, int mask)
return;
}
- // W_OK: only allow for files we can write to (non-backing, non-directory)
- if ((mask & W_OK) != 0 && (node->is_directory || node->file_info.is_backing)) {
- fuse_reply_err(req, EACCES);
- return;
- }
+ // The merged filesystem is writable even when the winning source is a
+ // read-only base-game or mod file: mutations are redirected through COW.
+ // Directories must likewise permit creation of staged children.
}
// X_OK on regular files: check real file permissions