aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-10 05:27:48 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-04-10 05:27:48 -0500
commitad512b0c8ff94c4f859e93b371ce594b911ac651 (patch)
tree5e484f97af07fb49a8ce65a6372322630cf2ce3a /src
parent01abc6994d9a477904af3fdf326ee2fc7163b3e1 (diff)
Fix VFS rename rejecting RENAME_NOREPLACE flag from Wine
Wine uses renameat2(RENAME_NOREPLACE) when translating MoveFileW() calls where ReplaceIfExists is FALSE (e.g. xEdit saving plugins). The VFS was rejecting all non-zero flags with EINVAL, which a real filesystem like ext4 handles correctly. This caused "Could not rename" errors in xEdit and similar tools. Now properly handles RENAME_NOREPLACE: checks if the destination exists in the VFS tree and returns EEXIST (matching ext4 behavior), otherwise proceeds with the rename. Adds diagnostic logging on EACCES failures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'src')
-rw-r--r--src/src/vfs/mo2filesystem.cpp21
1 files changed, 20 insertions, 1 deletions
diff --git a/src/src/vfs/mo2filesystem.cpp b/src/src/vfs/mo2filesystem.cpp
index 5fe6be5..d5a9201 100644
--- a/src/src/vfs/mo2filesystem.cpp
+++ b/src/src/vfs/mo2filesystem.cpp
@@ -1583,7 +1583,15 @@ void mo2_rename(fuse_req_t req, fuse_ino_t parent, const char* name,
fuse_ino_t newparent, const char* newname, unsigned int flags)
{
Mo2FsContext* ctx = getContext(req);
- if (ctx == nullptr || name == nullptr || newname == nullptr || flags != 0) {
+ if (ctx == nullptr || name == nullptr || newname == nullptr) {
+ fuse_reply_err(req, EINVAL);
+ return;
+ }
+
+ // Reject unsupported flags (only RENAME_NOREPLACE is supported).
+ // Wine uses renameat2(RENAME_NOREPLACE) for MoveFileW() calls where
+ // ReplaceIfExists is FALSE (e.g. xEdit saving plugins).
+ if (flags & ~(unsigned int)RENAME_NOREPLACE) {
fuse_reply_err(req, EINVAL);
return;
}
@@ -1606,7 +1614,18 @@ void mo2_rename(fuse_req_t req, fuse_ino_t parent, const char* name,
return;
}
+ // RENAME_NOREPLACE: fail if destination already exists in the VFS
+ if (flags & RENAME_NOREPLACE) {
+ const auto destSnap = snapshotForPath(ctx, newRelative);
+ if (destSnap.found) {
+ fuse_reply_err(req, EEXIST);
+ return;
+ }
+ }
+
if (!ctx->overwrite->rename(oldRelative, newRelative)) {
+ std::fprintf(stderr, "[VFS] rename EACCES: '%s' -> '%s' (flags=%u, backing=%d)\n",
+ oldRelative.c_str(), newRelative.c_str(), flags, oldSnap.is_backing);
fuse_reply_err(req, EACCES);
return;
}