From 1d1765af605fb927021ad8f7e27bacb520bcfa85 Mon Sep 17 00:00:00 2001 From: SulfurNitride Date: Sun, 26 Apr 2026 12:55:00 -0500 Subject: Spawn: prune non-{C:,Z:} drive letters from prefix at launch Wine canonicalises paths against the dosdevices map and prefers the most specific (longest-prefix) drive letter. An external symlink such as dosdevices/x: -> /home/ turns every Z:\home\\... binary path into X:\..., which confuses tools and obscures launch logs. PrefixSetupRunner::stepDriveCleanup already enforces the {C:, Z:} policy but only runs as part of the explicit prefix-setup pipeline; drives added later (Faugus, manual edits, modlist installers) survive forever. Mirror that cleanup at launch in spawn.cpp's pruneExtraDrives() so every Proton launch sees a known-good dosdevices layout. --- src/src/spawn.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/src/spawn.cpp b/src/src/spawn.cpp index 8e97ed0..9d4d8f2 100644 --- a/src/src/spawn.cpp +++ b/src/src/spawn.cpp @@ -27,7 +27,10 @@ along with Mod Organizer. If not, see . #include "settingsdialogworkarounds.h" #include "shared/appconfig.h" #include +#include +#include #include +#include #include #include #include @@ -517,6 +520,44 @@ QString firstExistingSetting(const QSettings &settings, return {}; } +// Remove dosdevices/: symlinks for any drive other than C: and Z:. +// External tooling (Faugus, manual edits, modlist installers) can re-add +// drives like X: that map subtrees of the host filesystem; Wine then prefers +// the more specific drive when canonicalising paths, which mangles binary +// paths we passed in as Z:\... into X:\... Keeping this list minimal means +// MO2 can rely on Z: being the only host-mapped drive. +void pruneExtraDrives(const QString &prefixPath) { + static const QSet kept = {QStringLiteral("c:"), QStringLiteral("z:")}; + + const QString dosdevices = QDir(prefixPath).filePath("dosdevices"); + QDir dir(dosdevices); + if (!dir.exists()) { + return; + } + + QStringList removed; + for (const QString &entry : + dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries)) { + const QString lower = entry.toLower(); + if (lower.length() != 2 || !lower.endsWith(':') || + !lower.at(0).isLetter()) { + continue; + } + if (kept.contains(lower)) { + continue; + } + if (QFile::remove(dir.filePath(entry))) { + removed << entry.toUpper(); + } + } + + if (!removed.isEmpty()) { + MOBase::log::info( + "Pruned stale drive letters from prefix '{}': {}", prefixPath, + removed.join(QStringLiteral(", ")).toStdString()); + } +} + QString resolvePrefixPath() { // The Fluorine config is authoritative: it's the prefix the user // explicitly created through Settings > Proton and that Fluorine itself @@ -702,6 +743,7 @@ int spawn(const SpawnParameters &sp, pid_t &processId) { return ENOENT; } else { MOBase::log::info("Using Wine prefix: {}", prefixPath); + pruneExtraDrives(prefixPath); launcher.setPrefix(prefixPath); } -- cgit v1.3.1