aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-09 06:20:10 -0500
committerSulfurNitride <SulfurNitride@users.noreply.github.com>2026-03-09 06:20:10 -0500
commit6a310f34ccf2dde98ca82136b992afd7bebfd3d5 (patch)
treef9bfe6c7ea4e61462204b8b5107302126cf19b64
parentfb5079bb7a44c567200bee0b5c5b5c8abda80f25 (diff)
Remove COW from FUSE VFS — write mod files in-place
Mod .ini/.log files now stay in their original mod folder instead of being copied to Overwrite. All files pass through reads and writes to their real location. No copy-on-write needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
-rw-r--r--src/src/vfs/mo2filesystem.cpp113
-rw-r--r--src/src/vfs/mo2filesystem.h1
2 files changed, 8 insertions, 106 deletions
diff --git a/src/src/vfs/mo2filesystem.cpp b/src/src/vfs/mo2filesystem.cpp
index a81db75..bfbc923 100644
--- a/src/src/vfs/mo2filesystem.cpp
+++ b/src/src/vfs/mo2filesystem.cpp
@@ -889,34 +889,16 @@ void mo2_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
std::string realPath = snap.real_path;
const bool writable = isWritableOpen(fi->flags);
bool isBacking = snap.is_backing;
- bool cowPending = false;
- // Lazy COW: don't copy the file on open — wait until an actual write()
- // occurs. This avoids polluting the overwrite folder when Wine/Proton
- // opens files O_RDWR for metadata ops without actually writing to them
- // (common with SKSE plugin .ini/.log files).
- //
- // Files already in staging/overwrite don't need COW, so open them R/W
- // immediately. For mod/base-game files opened writable, open read-only
- // for now and mark COW as pending.
+ // Mod/staging/overwrite files opened writable → open R/W in-place so
+ // writes go back to the original mod folder (no copying to Overwrite).
int fd = -1;
if (writable) {
- const std::string stagedPath = ctx->overwrite->stagingPath(path);
- const std::string owPath = ctx->overwrite->overwritePath(path);
-
- bool alreadyStaged = (realPath == stagedPath || realPath == owPath);
- if (alreadyStaged) {
- // File is already in staging/overwrite — open read-write directly.
- fd = open(realPath.c_str(), O_RDWR);
+ if (isBacking && ctx->backing_dir_fd >= 0) {
+ fd = openat(ctx->backing_dir_fd, realPath.c_str(), O_RDWR);
} else {
- // Mod or base-game file — open read-only, defer COW to write time.
- if (isBacking && ctx->backing_dir_fd >= 0) {
- fd = openat(ctx->backing_dir_fd, realPath.c_str(), O_RDONLY);
- } else {
- fd = open(realPath.c_str(), O_RDONLY);
- }
- cowPending = true;
+ fd = open(realPath.c_str(), O_RDWR);
}
if (fd < 0) {
fuse_reply_err(req, EIO);
@@ -932,7 +914,6 @@ void mo2_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info* fi)
of.real_path = realPath;
of.writable = writable;
of.is_backing = isBacking;
- of.cow_pending = cowPending;
of.relative_path = path;
ctx->open_files[fh] = std::move(of);
}
@@ -1009,9 +990,7 @@ void mo2_write(fuse_req_t req, fuse_ino_t /*ino*/, const char* buf, size_t size,
int fd = -1;
std::string relativePath;
std::string realPath;
- bool writable = false;
- bool cowPending = false;
- bool isBacking = false;
+ bool writable = false;
{
std::shared_lock lock(ctx->open_files_mutex);
auto it = ctx->open_files.find(fi->fh);
@@ -1021,8 +1000,6 @@ void mo2_write(fuse_req_t req, fuse_ino_t /*ino*/, const char* buf, size_t size,
}
fd = it->second.fd;
writable = it->second.writable;
- cowPending = it->second.cow_pending;
- isBacking = it->second.is_backing;
relativePath = it->second.relative_path;
realPath = it->second.real_path;
}
@@ -1032,45 +1009,6 @@ void mo2_write(fuse_req_t req, fuse_ino_t /*ino*/, const char* buf, size_t size,
return;
}
- // Lazy COW: this is the first actual write — perform the deferred copy
- // now and reopen the file descriptor read-write on the staging copy.
- if (cowPending) {
- try {
- std::string newPath;
- if (isBacking && ctx->backing_dir_fd >= 0) {
- newPath = ctx->overwrite->copyOnWriteFromFd(ctx->backing_dir_fd, relativePath);
- } else {
- newPath = ctx->overwrite->copyOnWrite(realPath, relativePath);
- }
-
- int newFd = open(newPath.c_str(), O_RDWR);
- if (newFd < 0) {
- fuse_reply_err(req, EIO);
- return;
- }
-
- // Close the old read-only fd and update the open file record.
- if (fd >= 0) close(fd);
- fd = newFd;
-
- {
- std::scoped_lock lock(ctx->open_files_mutex);
- auto it = ctx->open_files.find(fi->fh);
- if (it != ctx->open_files.end()) {
- it->second.fd = newFd;
- it->second.real_path = newPath;
- it->second.is_backing = false;
- it->second.cow_pending = false;
- }
- }
- realPath = newPath;
- updateFileNode(ctx, relativePath, newPath, "Staging");
- } catch (...) {
- fuse_reply_err(req, EIO);
- return;
- }
- }
-
if (fd < 0) {
fuse_reply_err(req, EBADF);
return;
@@ -1082,7 +1020,7 @@ void mo2_write(fuse_req_t req, fuse_ino_t /*ino*/, const char* buf, size_t size,
return;
}
- updateFileNode(ctx, relativePath, realPath, "Staging");
+ updateFileNode(ctx, relativePath, realPath, "Mod");
fuse_reply_write(req, static_cast<size_t>(written));
}
@@ -1269,41 +1207,6 @@ void mo2_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set,
targetIsBacking = snap.is_backing;
}
- const std::string stagedPath = ctx->overwrite->stagingPath(path);
- if (fs::path(target).lexically_normal().string() !=
- fs::path(stagedPath).lexically_normal().string()) {
- try {
- if (targetIsBacking && ctx->backing_dir_fd >= 0) {
- target = ctx->overwrite->copyOnWriteFromFd(ctx->backing_dir_fd, path);
- } else {
- target = ctx->overwrite->copyOnWrite(target, path);
- }
- } catch (...) {
- fuse_reply_err(req, EIO);
- return;
- }
-
- if (fi != nullptr) {
- std::scoped_lock lock(ctx->open_files_mutex);
- auto it = ctx->open_files.find(fh);
- if (it != ctx->open_files.end()) {
- const int newFd = open(target.c_str(), O_RDWR);
- if (newFd < 0) {
- fuse_reply_err(req, EIO);
- return;
- }
- if (it->second.fd >= 0) {
- close(it->second.fd);
- }
- it->second.fd = newFd;
- it->second.real_path = target;
- it->second.writable = true;
- it->second.is_backing = false;
- it->second.cow_pending = false;
- }
- }
- }
-
bool resized = false;
if (fi != nullptr) {
std::scoped_lock lock(ctx->open_files_mutex);
@@ -1326,7 +1229,7 @@ void mo2_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set,
}
}
- updateFileNode(ctx, path, target, "Staging");
+ updateFileNode(ctx, path, target, "Mod");
}
// Handle explicit timestamp changes (utimensat / Wine SetFileTime)
diff --git a/src/src/vfs/mo2filesystem.h b/src/src/vfs/mo2filesystem.h
index a1e1508..7ab0206 100644
--- a/src/src/vfs/mo2filesystem.h
+++ b/src/src/vfs/mo2filesystem.h
@@ -33,7 +33,6 @@ struct Mo2FsContext
std::string real_path;
bool writable = false;
bool is_backing = false;
- bool cow_pending = false; // true if opened writable but COW deferred
std::string relative_path;
};