diff options
| author | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-16 21:26:51 -0600 |
|---|---|---|
| committer | SulfurNitride <SulfurNitride@users.noreply.github.com> | 2026-02-16 21:27:03 -0600 |
| commit | 37e13e0f5aa8a400ca3bee77d37cbe73b9919355 (patch) | |
| tree | b816bd96fb044865f0048b4a41e3b91db4c02a5f /src | |
| parent | f59349fdca4dc67dac5871fd8da17d66cabf9dee (diff) | |
Fix FUSE VFS reporting wrong timestamps on save files
Three issues caused all save files to show the same date (the session
start time), breaking Starfield's "Continue" mechanism:
1. setattr ignored FUSE_SET_ATTR_MTIME — Wine's SetFileTime() calls
(utimensat) were silently dropped, so games couldn't set timestamps
2. TTL_SECONDS was 365 days — kernel cached stale attrs and never
re-queried FUSE even after writes updated the real file
3. mo2_rename replaced mtime with now() instead of preserving it
Fixes #16
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/src/vfs/mo2filesystem.cpp | 43 |
1 files changed, 41 insertions, 2 deletions
diff --git a/src/src/vfs/mo2filesystem.cpp b/src/src/vfs/mo2filesystem.cpp index 7842c1a..7b440f9 100644 --- a/src/src/vfs/mo2filesystem.cpp +++ b/src/src/vfs/mo2filesystem.cpp @@ -1,6 +1,7 @@ #include "mo2filesystem.h" #include <fcntl.h> +#include <sys/time.h> #include <unistd.h> #include <algorithm> @@ -13,7 +14,7 @@ namespace { namespace fs = std::filesystem; -constexpr double TTL_SECONDS = 60.0 * 60.0 * 24.0 * 365.0; +constexpr double TTL_SECONDS = 1.0; struct NodeSnapshot { @@ -595,7 +596,7 @@ void mo2_rename(fuse_req_t req, fuse_ino_t parent, const char* name, const std::string over = ctx->overwrite->overwritePath(newRelative); const std::string real = fs::exists(staged) ? staged : over; ctx->tree->root.insertFile(splitPath(newRelative), real, oldSnap.size, - std::chrono::system_clock::now(), "Staging"); + oldSnap.mtime, "Staging"); } } @@ -689,6 +690,44 @@ void mo2_setattr(fuse_req_t req, fuse_ino_t ino, struct stat* attr, int to_set, updateFileNode(ctx, path, target, "Staging"); } + // Handle explicit timestamp changes (utimensat / Wine SetFileTime) + if ((to_set & (FUSE_SET_ATTR_MTIME | FUSE_SET_ATTR_MTIME_NOW | + FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_ATIME_NOW)) != 0 && + attr != nullptr) { + const auto snap = snapshotForPath(ctx, path); + if (snap.found && !snap.is_directory) { + // Apply the timestamp to the real file on disk + struct timespec times[2]; + // atime + if (to_set & FUSE_SET_ATTR_ATIME_NOW) { + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_NOW; + } else if (to_set & FUSE_SET_ATTR_ATIME) { + times[0] = attr->st_atim; + } else { + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + } + // mtime + if (to_set & FUSE_SET_ATTR_MTIME_NOW) { + times[1].tv_sec = 0; + times[1].tv_nsec = UTIME_NOW; + } else if (to_set & FUSE_SET_ATTR_MTIME) { + times[1] = attr->st_mtim; + } else { + times[1].tv_sec = 0; + times[1].tv_nsec = UTIME_OMIT; + } + + utimensat(AT_FDCWD, snap.real_path.c_str(), times, 0); + + // Update the VFS tree node with the new mtime + if (to_set & (FUSE_SET_ATTR_MTIME | FUSE_SET_ATTR_MTIME_NOW)) { + updateFileNode(ctx, path, snap.real_path, "Staging"); + } + } + } + const auto snap = snapshotForPath(ctx, path); if (!snap.found) { fuse_reply_err(req, ENOENT); |
