diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-06-26 15:48:27 -0500 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-06-26 15:48:27 -0500 |
| commit | e01ec60eb4023485f1e667c7b6a5e1a62dc02164 (patch) | |
| tree | 4c4c4b7bd829083f907c1442fb4c6731cd6e93b1 | |
| parent | 6292eb18a79dc00e2c5369bf3773cffe929b950d (diff) | |
Route VFS permissions through COW handlers
| -rw-r--r-- | src/src/fuseconnector.cpp | 11 | ||||
| -rw-r--r-- | src/src/vfs/mo2filesystem.cpp | 13 |
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 |
