Skip to content

Fix Open Container file copy crashes in Wine - #1784

Open
Nightwalker743 wants to merge 3 commits into
utkarshdalal:masterfrom
Nightwalker743:fix/wfm-copy-paste-crash
Open

Fix Open Container file copy crashes in Wine#1784
Nightwalker743 wants to merge 3 commits into
utkarshdalal:masterfrom
Nightwalker743:fix/wfm-copy-paste-crash

Conversation

@Nightwalker743

@Nightwalker743 Nightwalker743 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Description

Fix file copy crashes in the Open Container file manager when copying between Wine-mapped drives.

The first file conflict now also offers Replace all, avoiding repeated confirmation prompts when installing mods containing many existing files.

The issue I and another user came across:
Open Container launches Winlator File Manager inside the game's Wine container. WFM delegates copy operations to shell32's SHFileOperation.

On some Wine/Proton configurations, pasting files between mapped drives for example, from D: to A: causes WFM to terminate instead of displaying the expected replacement prompt. This was reproduced with games including Unreal Tournament GOTY and Fields of Mistria.

Recording

VN20260730_215630.mp4

Type of Change

  • Bug fix
  • Performance / stability improvement
  • Compatibility improvements
  • Other (requires prior approval)

Checklist

  • If I have access to #code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by cubic

Fixes Open Container copy crashes under Wine by switching WFM copy to native Win32 and auto-installing the patched wfm.exe. Resolves mapped-drive aliases to prevent copying folders into their own descendants, adds a first-conflict “Replace all,” and improves handling for symlinks, read-only files, long paths, and errors.

  • Bug Fixes

    • Replaced SHFileOperation with native CopyFile for ACTION_COPY to stop crashes between mapped drives.
    • Resolves mapped-drive aliases when checking destinations; blocks copying a folder into itself or a subfolder.
    • Skips and summarizes reparse points; preserves folder timestamps/attributes; clearer system error messages; path length validation.
  • New Features

    • “Replace All” vs “Review Each” on the first conflict; cancel supported.
    • WfmInstaller installs the patched wfm.exe at container boot with SHA-256 verification; Open Container launch is gated and shows a snackbar if installation fails. Includes LICENSE.WFM.txt, NOTICE.txt, and README.md with reproducible build steps.

Written for commit ec11ed8. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features

    • Added a bundled, updated file manager for containers.
    • Improved file copying with conflict resolution, cancellation, collision detection, metadata preservation, and clearer error reporting.
    • Added support for replacing all conflicting files during copy operations.
    • Installs the updated file manager automatically when launching directly into a container.
  • Bug Fixes

    • Container startup is prevented with a clear notification if installation fails.
  • Documentation

    • Added licensing, attribution, and reproducible build documentation for the included file manager updates.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds native Win32 copying to WFM, hardens path and filesystem handling, adds conflict policies, and installs the bundled patched wfm.exe before WFM-based container startup.

Changes

WFM copy deployment

Layer / File(s) Summary
Native copy and conflict behavior
app/src/main/assets/wfm/native-copy.patch, app/src/main/assets/wfm/replace-all-conflicts.patch
ACTION_COPY uses recursive native copying. Copying supports cancellation, self-copy handling, and replace-all, per-conflict, or cancel policies.
Filesystem copy hardening
app/src/main/assets/wfm/copy-hardening.patch
Copy operations validate paths, collisions, reparse points, source attributes, enumeration results, and cancellation. They preserve directory metadata, report Win32 failures, and summarize skipped items.
Container installation and licensing
app/src/main/java/app/gamenative/utils/WfmInstaller.kt, app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt, app/src/main/res/values/strings.xml, app/src/main/assets/wfm/LICENSE.WFM.txt, app/src/main/assets/wfm/NOTICE.txt, app/src/main/assets/wfm/README.md
WfmInstaller verifies and atomically installs the bundled executable. WFM startup paths require successful installation and show an error on failure. Licensing, patch details, and rebuild instructions are documented.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant XServerScreen
  participant WfmInstaller
  participant WfmAsset
  participant ContainerWindows
  XServerScreen->>WfmInstaller: Install WFM before selecting a WFM command
  WfmInstaller->>ContainerWindows: Check installed wfm.exe hash
  WfmInstaller->>WfmAsset: Read bundled wfm.exe
  WfmInstaller->>ContainerWindows: Verify and atomically replace wfm.exe
  WfmInstaller-->>XServerScreen: Installation result
  XServerScreen-->>XServerScreen: Show failure and reject WFM startup when installation fails
Loading

Possibly related PRs

Suggested reviewers: utkarshdalal

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary change: fixing file-copy crashes in Open Container’s Wine file manager.
Description check ✅ Passed The description includes the required sections, explains the fix and motivation, provides a recording, and completes the type and checklist items.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Nightwalker743
Nightwalker743 marked this pull request as ready for review July 31, 2026 04:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
app/src/main/assets/wfm/native-copy.patch (1)

1-9: 📐 Maintainability & Code Quality | 🔵 Trivial

Document the reproducible build procedure for the bundled wfm.exe.

WfmInstaller.kt pins wfm.exe to the SHA-256 cc7b5b77.... These patch assets record the upstream commit but not the toolchain, compiler flags, or patch application order needed to reproduce that exact binary. Without that, no reviewer can verify the shipped executable matches the patches, and the next hash update becomes guesswork.

Add a short README in app/src/main/assets/wfm/ that lists the patch order (native-copy.patch, replace-all-conflicts.patch, copy-hardening.patch), the git apply flags for each, and the exact build command.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/assets/wfm/native-copy.patch` around lines 1 - 9, Add a short
README in the wfm assets directory documenting reproducible wfm.exe creation:
identify the pinned upstream commit, apply native-copy.patch,
replace-all-conflicts.patch, and copy-hardening.patch in that order with the
required git apply flags for each, and provide the exact toolchain/compiler
build command needed to produce the SHA-256 pinned by WfmInstaller.kt.
app/src/main/assets/wfm/copy-hardening.patch (1)

264-272: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Regenerate this patch with context lines instead of --unidiff-zero.

This hunk closes a reparse-point check and the next hunk appends else createdDestination = true; to the if (!CreateDirectory(...)) block. Neither hunk carries context lines, so git apply --unidiff-zero places them purely by absolute line number. Those line numbers depend on native-copy.patch and replace-all-conflicts.patch applying at exactly the expected offsets.

If any earlier patch shifts by one line, these hunks land at the wrong place and the brace nesting changes without a conflict being reported. The build product is pinned by SHA-256 in WfmInstaller.kt, so a silent misapply would still be shipped.

Regenerate all three patches with standard 3-line context, or vendor the patched file_actions.c directly so the result is reviewable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/assets/wfm/copy-hardening.patch` around lines 264 - 272,
Regenerate copy-hardening.patch and the related native-copy.patch and
replace-all-conflicts.patch hunks with standard three-line context instead of
--unidiff-zero, ensuring the reparse-point check and the createdDestination
assignment remain attached to their intended blocks. Alternatively, vendor the
fully patched file_actions.c directly so patch placement and brace nesting are
reviewable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/assets/wfm/copy-hardening.patch`:
- Around line 361-363: Remove the no-op trailing hunk after
createDesktopShortcuts, including the duplicated unchanged closing brace and its
associated absolute-line dependency; leave the meaningful patch hunks unchanged.
- Around line 342-349: The reparse-point branch in copyPathNative must skip only
the unsupported entry instead of aborting the entire paste. Introduce or reuse a
distinct skipped-result status, propagate it through copyDirectoryNative so
enumeration continues, and ensure fileActionTask continues processing remaining
items while accumulating skipped entries. Report a single summary after the
operation completes, while preserving failure handling for genuine copy errors.
- Around line 68-76: Replace swprintf_s in joinCopyPath with _snwprintf_s using
the _TRUNCATE mode and preserve the existing negative-result failure check.
Apply the same formatting change to the message call at
app/src/main/assets/wfm/copy-hardening.patch lines 47-55; both sites should
treat any negative return as truncation without invoking the invalid parameter
handler.

In `@app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt`:
- Around line 4014-4017: Ensure WfmInstaller.install(context, container) runs
before every wfm.exe launch path, including Custom Games without a detectable
executable and Steam games with no executablePath, rather than only when
bootToContainer is true. Move the installation call to the shared pre-launch
flow or invoke it unconditionally, while preserving the existing failure
logging.

In `@app/src/main/java/app/gamenative/utils/WfmInstaller.kt`:
- Around line 47-86: Update the existing fast-path condition in installPayload
so it also requires destination.canExecute() to be true, alongside
destination.isFile and the matching SHA-256 hash. This ensures matching files
with missing executable permissions proceed through the reinstall and chmod
path.

---

Nitpick comments:
In `@app/src/main/assets/wfm/copy-hardening.patch`:
- Around line 264-272: Regenerate copy-hardening.patch and the related
native-copy.patch and replace-all-conflicts.patch hunks with standard three-line
context instead of --unidiff-zero, ensuring the reparse-point check and the
createdDestination assignment remain attached to their intended blocks.
Alternatively, vendor the fully patched file_actions.c directly so patch
placement and brace nesting are reviewable.

In `@app/src/main/assets/wfm/native-copy.patch`:
- Around line 1-9: Add a short README in the wfm assets directory documenting
reproducible wfm.exe creation: identify the pinned upstream commit, apply
native-copy.patch, replace-all-conflicts.patch, and copy-hardening.patch in that
order with the required git apply flags for each, and provide the exact
toolchain/compiler build command needed to produce the SHA-256 pinned by
WfmInstaller.kt.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55b43aa3-e022-42fd-9a43-01262cdfbd4b

📥 Commits

Reviewing files that changed from the base of the PR and between 155ad56 and 7001c84.

⛔ Files ignored due to path filters (1)
  • app/src/main/assets/wfm/wfm.exe is excluded by !**/*.exe
📒 Files selected for processing (7)
  • app/src/main/assets/wfm/LICENSE.WFM.txt
  • app/src/main/assets/wfm/NOTICE.txt
  • app/src/main/assets/wfm/copy-hardening.patch
  • app/src/main/assets/wfm/native-copy.patch
  • app/src/main/assets/wfm/replace-all-conflicts.patch
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/app/gamenative/utils/WfmInstaller.kt

Comment thread app/src/main/assets/wfm/copy-hardening.patch Outdated
Comment thread app/src/main/assets/wfm/copy-hardening.patch
Comment thread app/src/main/assets/wfm/copy-hardening.patch Outdated
Comment thread app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt Outdated
Comment thread app/src/main/java/app/gamenative/utils/WfmInstaller.kt

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 8 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt Outdated
Comment thread app/src/main/assets/wfm/native-copy.patch
Comment thread app/src/main/assets/wfm/native-copy.patch
Comment thread app/src/main/assets/wfm/replace-all-conflicts.patch
Comment thread app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt Outdated
Comment thread app/src/main/assets/wfm/native-copy.patch
Comment thread app/src/main/assets/wfm/native-copy.patch
Comment thread app/src/main/assets/wfm/native-copy.patch
Comment thread app/src/main/assets/wfm/copy-hardening.patch
Comment thread app/src/main/assets/wfm/copy-hardening.patch Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/assets/wfm/copy-hardening.patch`:
- Around line 99-129: Update isCopyDestinationDescendant to resolve mapped-drive
aliases before comparing paths, using resolved filesystem identities or final
paths for the destination’s nearest existing ancestor. Ensure a destination that
maps inside the source is rejected even when source and destination use
different drive letters, while preserving the existing descendant and
path-validation behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d0f5e887-a205-4efd-98a7-b9d721aa3f2d

📥 Commits

Reviewing files that changed from the base of the PR and between 7001c84 and 35e31e5.

⛔ Files ignored due to path filters (1)
  • app/src/main/assets/wfm/wfm.exe is excluded by !**/*.exe
📒 Files selected for processing (8)
  • app/src/main/assets/wfm/NOTICE.txt
  • app/src/main/assets/wfm/README.md
  • app/src/main/assets/wfm/copy-hardening.patch
  • app/src/main/assets/wfm/native-copy.patch
  • app/src/main/assets/wfm/replace-all-conflicts.patch
  • app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
  • app/src/main/java/app/gamenative/utils/WfmInstaller.kt
  • app/src/main/res/values/strings.xml
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/main/assets/wfm/NOTICE.txt
  • app/src/main/java/app/gamenative/utils/WfmInstaller.kt
  • app/src/main/assets/wfm/replace-all-conflicts.patch

Comment thread app/src/main/assets/wfm/copy-hardening.patch

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 9 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/utils/WfmInstaller.kt Outdated
Comment thread app/src/main/assets/wfm/copy-hardening.patch

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/app/gamenative/utils/WfmInstaller.kt (1)

65-78: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fall back on atomic move failure for an existing target.

With StandardCopyOption.ATOMIC_MOVE, option dependencies like REPLACE_EXISTING are ignored. If destination exists and supports atomic moves, this Files.move can throw IOException without reaching the atomic-move fallback. Catch the atomic attempt’s IOException, then retry with REPLACE_EXISTING.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/app/gamenative/utils/WfmInstaller.kt` around lines 65 - 78,
Update the move logic in WfmInstaller to catch the atomic attempt’s IOException,
not only AtomicMoveNotSupportedException, so existing destinations can fall back
to the non-atomic move with REPLACE_EXISTING. Preserve the current fallback move
options and avoid swallowing failures from the retry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/src/main/java/app/gamenative/utils/WfmInstaller.kt`:
- Around line 65-78: Update the move logic in WfmInstaller to catch the atomic
attempt’s IOException, not only AtomicMoveNotSupportedException, so existing
destinations can fall back to the non-atomic move with REPLACE_EXISTING.
Preserve the current fallback move options and avoid swallowing failures from
the retry.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a4eb1d36-ecd5-4ead-aaa4-939aee87d62c

📥 Commits

Reviewing files that changed from the base of the PR and between 35e31e5 and ec11ed8.

⛔ Files ignored due to path filters (1)
  • app/src/main/assets/wfm/wfm.exe is excluded by !**/*.exe
📒 Files selected for processing (6)
  • app/src/main/assets/wfm/NOTICE.txt
  • app/src/main/assets/wfm/README.md
  • app/src/main/assets/wfm/copy-hardening.patch
  • app/src/main/assets/wfm/native-copy.patch
  • app/src/main/assets/wfm/replace-all-conflicts.patch
  • app/src/main/java/app/gamenative/utils/WfmInstaller.kt
🚧 Files skipped from review as they are similar to previous changes (5)
  • app/src/main/assets/wfm/native-copy.patch
  • app/src/main/assets/wfm/replace-all-conflicts.patch
  • app/src/main/assets/wfm/NOTICE.txt
  • app/src/main/assets/wfm/README.md
  • app/src/main/assets/wfm/copy-hardening.patch

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 7 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/assets/wfm/copy-hardening.patch">

<violation number="1" location="app/src/main/assets/wfm/copy-hardening.patch:99">
P2: Copies through a long resolved junction/mapped-drive path can bypass the self-descendant check and recurse into the source tree. Size the final-path buffer from `GetFinalPathNameByHandle`’s required length (or fail closed) instead of treating paths over 1024 characters as unrelated.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

+ return length >= 0 && (size_t)length < destinationSize;
+}
+
+#define COPY_RESOLVED_PATH_SIZE 1024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Copies through a long resolved junction/mapped-drive path can bypass the self-descendant check and recurse into the source tree. Size the final-path buffer from GetFinalPathNameByHandle’s required length (or fail closed) instead of treating paths over 1024 characters as unrelated.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/assets/wfm/copy-hardening.patch, line 99:

<comment>Copies through a long resolved junction/mapped-drive path can bypass the self-descendant check and recurse into the source tree. Size the final-path buffer from `GetFinalPathNameByHandle`’s required length (or fail closed) instead of treating paths over 1024 characters as unrelated.</comment>

<file context>
@@ -96,6 +96,82 @@ index 45a2c60..d657bd8 100644
 +    return length >= 0 && (size_t)length < destinationSize;
 +}
 +
++#define COPY_RESOLVED_PATH_SIZE 1024
++
++static bool isCopyPathSeparator(wchar_t value) {
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant